mirror of
https://github.com/golang/go.git
synced 2024-10-01 07:17:21 +00:00
exp/template: make numbers adhere to Go's rules for ideal constants.
Without further type informatnion, 1.0 is a float and an integer must fit in an int. R=rsc CC=golang-dev https://golang.org/cl/4696042
This commit is contained in:
parent
05c89edcd3
commit
7c47741811
@ -300,19 +300,7 @@ func (s *state) evalCommand(dot reflect.Value, cmd *commandNode, final reflect.V
|
|||||||
case *dotNode:
|
case *dotNode:
|
||||||
return dot
|
return dot
|
||||||
case *numberNode:
|
case *numberNode:
|
||||||
// These are ideal constants but we don't know the type
|
return s.idealConstant(word)
|
||||||
// and we have no context. (If it was a method argument,
|
|
||||||
// we'd know what we need.) The syntax guides us to some extent.
|
|
||||||
switch {
|
|
||||||
case word.isComplex:
|
|
||||||
return reflect.ValueOf(word.complex128) // incontrovertible.
|
|
||||||
case word.isFloat && strings.IndexAny(word.text, ".eE") >= 0:
|
|
||||||
return reflect.ValueOf(word.float64)
|
|
||||||
case word.isInt:
|
|
||||||
return reflect.ValueOf(word.int64)
|
|
||||||
case word.isUint:
|
|
||||||
return reflect.ValueOf(word.uint64)
|
|
||||||
}
|
|
||||||
case *stringNode:
|
case *stringNode:
|
||||||
return reflect.ValueOf(word.text)
|
return reflect.ValueOf(word.text)
|
||||||
}
|
}
|
||||||
@ -320,6 +308,31 @@ func (s *state) evalCommand(dot reflect.Value, cmd *commandNode, final reflect.V
|
|||||||
panic("not reached")
|
panic("not reached")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// idealConstant is called to return the value of a number in a context where
|
||||||
|
// we don't know the type. In that case, the syntax of the number tells us
|
||||||
|
// its type, and we use Go rules to resolve. Note there is no such thing as
|
||||||
|
// a uint ideal constant in this situation - the value must be of int type.
|
||||||
|
func (s *state) idealConstant(constant *numberNode) reflect.Value {
|
||||||
|
// These are ideal constants but we don't know the type
|
||||||
|
// and we have no context. (If it was a method argument,
|
||||||
|
// we'd know what we need.) The syntax guides us to some extent.
|
||||||
|
switch {
|
||||||
|
case constant.isComplex:
|
||||||
|
return reflect.ValueOf(constant.complex128) // incontrovertible.
|
||||||
|
case constant.isFloat && strings.IndexAny(constant.text, ".eE") >= 0:
|
||||||
|
return reflect.ValueOf(constant.float64)
|
||||||
|
case constant.isInt:
|
||||||
|
n := int(constant.int64)
|
||||||
|
if int64(n) != constant.int64 {
|
||||||
|
s.errorf("%s overflows int", constant.text)
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(n)
|
||||||
|
case constant.isUint:
|
||||||
|
s.errorf("%s overflows int", constant.text)
|
||||||
|
}
|
||||||
|
return zero
|
||||||
|
}
|
||||||
|
|
||||||
func (s *state) evalFieldNode(dot reflect.Value, field *fieldNode, args []node, final reflect.Value) reflect.Value {
|
func (s *state) evalFieldNode(dot reflect.Value, field *fieldNode, args []node, final reflect.Value) reflect.Value {
|
||||||
return s.evalFieldChain(dot, dot, field.ident, args, final)
|
return s.evalFieldChain(dot, dot, field.ident, args, final)
|
||||||
}
|
}
|
||||||
@ -577,18 +590,7 @@ func (s *state) evalEmptyInterface(dot reflect.Value, n node) reflect.Value {
|
|||||||
case *identifierNode:
|
case *identifierNode:
|
||||||
return s.evalFunction(dot, n.ident, nil, zero)
|
return s.evalFunction(dot, n.ident, nil, zero)
|
||||||
case *numberNode:
|
case *numberNode:
|
||||||
if n.isComplex {
|
return s.idealConstant(n)
|
||||||
return reflect.ValueOf(n.complex128)
|
|
||||||
}
|
|
||||||
if n.isInt {
|
|
||||||
return reflect.ValueOf(n.int64)
|
|
||||||
}
|
|
||||||
if n.isUint {
|
|
||||||
return reflect.ValueOf(n.uint64)
|
|
||||||
}
|
|
||||||
if n.isFloat {
|
|
||||||
return reflect.ValueOf(n.float64)
|
|
||||||
}
|
|
||||||
case *stringNode:
|
case *stringNode:
|
||||||
return reflect.ValueOf(n.text)
|
return reflect.ValueOf(n.text)
|
||||||
case *variableNode:
|
case *variableNode:
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@ -127,6 +128,10 @@ func (t *T) EPERM(error bool) (bool, os.Error) {
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func typeOf(arg interface{}) string {
|
||||||
|
return fmt.Sprintf("%T", arg)
|
||||||
|
}
|
||||||
|
|
||||||
type execTest struct {
|
type execTest struct {
|
||||||
name string
|
name string
|
||||||
input string
|
input string
|
||||||
@ -135,11 +140,27 @@ type execTest struct {
|
|||||||
ok bool
|
ok bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bigInt and bigUint are hex string representing numbers either side
|
||||||
|
// of the max int boundary.
|
||||||
|
// We do it this way so the test doesn't depend on ints being 32 bits.
|
||||||
|
var (
|
||||||
|
bigInt = fmt.Sprintf("0x%x", int(1<<uint(reflect.TypeOf(0).Bits()-1)-1))
|
||||||
|
bigUint = fmt.Sprintf("0x%x", uint(1<<uint(reflect.TypeOf(0).Bits()-1)))
|
||||||
|
)
|
||||||
|
|
||||||
var execTests = []execTest{
|
var execTests = []execTest{
|
||||||
// Trivial cases.
|
// Trivial cases.
|
||||||
{"empty", "", "", nil, true},
|
{"empty", "", "", nil, true},
|
||||||
{"text", "some text", "some text", nil, true},
|
{"text", "some text", "some text", nil, true},
|
||||||
|
|
||||||
|
// Ideal constants.
|
||||||
|
{"ideal int", "{{typeOf 3}}", "int", 0, true},
|
||||||
|
{"ideal float", "{{typeOf 1.0}}", "float64", 0, true},
|
||||||
|
{"ideal exp float", "{{typeOf 1e1}}", "float64", 0, true},
|
||||||
|
{"ideal complex", "{{typeOf 1i}}", "complex128", 0, true},
|
||||||
|
{"ideal int", "{{typeOf " + bigInt + "}}", "int", 0, true},
|
||||||
|
{"ideal too big", "{{typeOf " + bigUint + "}}", "", 0, false},
|
||||||
|
|
||||||
// Fields of structs.
|
// Fields of structs.
|
||||||
{".X", "-{{.X}}-", "-x-", tVal, true},
|
{".X", "-{{.X}}-", "-x-", tVal, true},
|
||||||
{".U.V", "-{{.U.V}}-", "-v-", tVal, true},
|
{".U.V", "-{{.U.V}}-", "-v-", tVal, true},
|
||||||
@ -301,7 +322,7 @@ func oneArg(a string) string {
|
|||||||
|
|
||||||
func testExecute(execTests []execTest, set *Set, t *testing.T) {
|
func testExecute(execTests []execTest, set *Set, t *testing.T) {
|
||||||
b := new(bytes.Buffer)
|
b := new(bytes.Buffer)
|
||||||
funcs := FuncMap{"zeroArgs": zeroArgs, "oneArg": oneArg}
|
funcs := FuncMap{"zeroArgs": zeroArgs, "oneArg": oneArg, "typeOf": typeOf}
|
||||||
for _, test := range execTests {
|
for _, test := range execTests {
|
||||||
tmpl := New(test.name).Funcs(funcs)
|
tmpl := New(test.name).Funcs(funcs)
|
||||||
err := tmpl.Parse(test.input)
|
err := tmpl.Parse(test.input)
|
||||||
|
Loading…
Reference in New Issue
Block a user