Commit e86d727e authored by Rob Pike's avatar Rob Pike

exp/template: vars as arguments to functions and methods.

That should be it, bugs aside.

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/4671055
parent 58baf648
......@@ -51,8 +51,8 @@ func (s *state) setTop(value reflect.Value) {
s.vars[len(s.vars)-1].value = value
}
// value returns the value of the named variable.
func (s *state) value(name string) reflect.Value {
// varValue returns the value of the named variable.
func (s *state) varValue(name string) reflect.Value {
for i := s.mark() - 1; i >= 0; i-- {
if s.vars[i].name == name {
return s.vars[i].value
......@@ -112,23 +112,23 @@ func (s *state) walk(data reflect.Value, n node) {
s.line = n.line
defer s.pop(s.mark())
s.printValue(n, s.evalPipeline(data, n.pipe))
case *ifNode:
s.line = n.line
s.walkIfOrWith(nodeIf, data, n.pipe, n.list, n.elseList)
case *listNode:
for _, node := range n.nodes {
s.walk(data, node)
}
case *ifNode:
s.line = n.line
s.walkIfOrWith(nodeIf, data, n.pipe, n.list, n.elseList)
case *rangeNode:
s.line = n.line
s.walkRange(data, n)
case *templateNode:
s.line = n.line
s.walkTemplate(data, n)
case *textNode:
if _, err := s.wr.Write(n.text); err != nil {
s.error(err)
}
case *templateNode:
s.line = n.line
s.walkTemplate(data, n)
case *withNode:
s.line = n.line
s.walkIfOrWith(nodeWith, data, n.pipe, n.list, n.elseList)
......@@ -165,16 +165,16 @@ func isTrue(val reflect.Value) (truth, ok bool) {
truth = val.Len() > 0
case reflect.Bool:
truth = val.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
truth = val.Int() != 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
truth = val.Uint() != 0
case reflect.Float32, reflect.Float64:
truth = val.Float() != 0
case reflect.Complex64, reflect.Complex128:
truth = val.Complex() != 0
case reflect.Chan, reflect.Func, reflect.Ptr:
truth = !val.IsNil()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
truth = val.Int() != 0
case reflect.Float32, reflect.Float64:
truth = val.Float() != 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
truth = val.Uint() != 0
default:
return
}
......@@ -274,15 +274,13 @@ func (s *state) evalCommand(data reflect.Value, cmd *commandNode, final reflect.
case *identifierNode:
// Must be a function.
return s.evalFunction(data, n.ident, cmd.args, final)
case *variableNode:
return s.evalVariable(data, n.ident, cmd.args, final)
}
s.notAFunction(cmd.args, final)
switch word := cmd.args[0].(type) {
case *dotNode:
return data
switch word := firstWord.(type) {
case *boolNode:
return reflect.ValueOf(word.true)
case *dotNode:
return data
case *numberNode:
// These are ideal constants but we don't know the type
// and we have no context. (If it was a method argument,
......@@ -299,6 +297,8 @@ func (s *state) evalCommand(data reflect.Value, cmd *commandNode, final reflect.
}
case *stringNode:
return reflect.ValueOf(word.text)
case *variableNode:
return s.varValue(word.ident)
}
s.errorf("can't handle command %q", firstWord)
panic("not reached")
......@@ -322,11 +322,6 @@ func (s *state) evalFunction(data reflect.Value, name string, args []node, final
return s.evalCall(data, function, name, false, args, final)
}
func (s *state) evalVariable(data reflect.Value, name string, args []node, final reflect.Value) reflect.Value {
s.notAFunction(args, final) // Can't invoke function-valued variables - too confusing.
return s.value(name)
}
// Is this an exported - upper case - name?
func isExported(name string) bool {
rune, _ := utf8.DecodeRuneInString(name)
......@@ -439,37 +434,40 @@ func (s *state) evalCall(v, fun reflect.Value, name string, isMethod bool, args
return result[0]
}
// validateType guarantees that the value is assignable to the type.
func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Value {
if !value.Type().AssignableTo(typ) {
s.errorf("wrong type for value; expected %s; got %s", typ, value.Type())
}
return value
}
func (s *state) evalArg(data reflect.Value, typ reflect.Type, n node) reflect.Value {
switch arg := n.(type) {
case *dotNode:
if !data.Type().AssignableTo(typ) {
s.errorf("wrong type for value; expected %s; got %s", typ, data.Type())
}
return data
return s.validateType(data, typ)
case *fieldNode:
value := s.evalFieldNode(data, arg, []node{n}, zero)
if !value.Type().AssignableTo(typ) {
s.errorf("wrong type for value; expected %s; got %s", typ, value.Type())
}
return value
return s.validateType(s.evalFieldNode(data, arg, []node{n}, zero), typ)
case *variableNode:
return s.validateType(s.varValue(arg.ident), typ)
}
switch typ.Kind() {
case reflect.Bool:
return s.evalBool(typ, n)
case reflect.String:
return s.evalString(typ, n)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return s.evalInteger(typ, n)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return s.evalUnsignedInteger(typ, n)
case reflect.Float32, reflect.Float64:
return s.evalFloat(typ, n)
case reflect.Complex64, reflect.Complex128:
return s.evalComplex(typ, n)
case reflect.Float32, reflect.Float64:
return s.evalFloat(typ, n)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return s.evalInteger(typ, n)
case reflect.Interface:
if typ.NumMethod() == 0 {
return s.evalEmptyInterface(data, typ, n)
}
case reflect.String:
return s.evalString(typ, n)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return s.evalUnsignedInteger(typ, n)
}
s.errorf("can't handle %s for arg of type %s", n, typ)
panic("not reached")
......@@ -560,6 +558,8 @@ func (s *state) evalEmptyInterface(data reflect.Value, typ reflect.Type, n node)
}
case *stringNode:
return reflect.ValueOf(n.text)
case *variableNode:
return s.varValue(n.ident)
}
s.errorf("can't handle assignment of %s to empty interface argument", n)
panic("not reached")
......
......@@ -183,6 +183,7 @@ var execTests = []execTest{
{".Method1(.I)", "-{{.Method1 .I}}-", "-17-", tVal, true},
{".Method2(3, .X)", "-{{.Method2 3 .X}}-", "-Method2: 3 x-", tVal, true},
{".Method2(.U16, `str`)", "-{{.Method2 .U16 `str`}}-", "-Method2: 16 str-", tVal, true},
{".Method2(.U16, $x)", "{{if $x := .X}}-{{.Method2 .U16 $x}}{{end}}-", "-Method2: 16 x-", tVal, true},
// Pipelines.
{"pipeline", "-{{.Method0 | .Method2 .U16}}-", "-Method2: 16 M0-", tVal, true},
......@@ -212,6 +213,8 @@ var execTests = []execTest{
{"printf function", `{{printf "%#q" zeroArgs}}`, "`zeroArgs`", tVal, true},
{"printf field", `{{printf "%s" .U.V}}`, "v", tVal, true},
{"printf method", `{{printf "%s" .Method0}}`, "M0", tVal, true},
{"printf dot", `{{with .I}}{{printf "%d" .}}{{end}}`, "17", tVal, true},
{"printf var", `{{with $x := .I}}{{printf "%d" $x}}{{end}}`, "17", tVal, true},
{"printf lots", `{{printf "%d %s %g %s" 127 "hello" 7-3i .Method0}}`, "127 hello (7-3i) M0", tVal, true},
// HTML.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment