]> Cypherpunks repositories - gostls13.git/commitdiff
exp/template: vars as arguments to functions and methods.
authorRob Pike <r@golang.org>
Sat, 9 Jul 2011 07:11:35 +0000 (17:11 +1000)
committerRob Pike <r@golang.org>
Sat, 9 Jul 2011 07:11:35 +0000 (17:11 +1000)
That should be it, bugs aside.

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/4671055

src/pkg/exp/template/exec.go
src/pkg/exp/template/exec_test.go

index 15c382147a04d95e1b36e34c1c4b3b6814896357..b5d4a1d8e72368931c95d2aab50a2b74bc12459f 100644 (file)
@@ -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")
index 5b9b469dd7a5b658f21056ec703411b7d696e68a..8e50f17dcab8fc8d7179a8fad19931947a08fd8c 100644 (file)
@@ -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.