From e86d727e6033c04b9610c4aeb7b7eafcd9e950a7 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Sat, 9 Jul 2011 17:11:35 +1000 Subject: [PATCH] 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 --- src/pkg/exp/template/exec.go | 82 +++++++++++++++---------------- src/pkg/exp/template/exec_test.go | 3 ++ 2 files changed, 44 insertions(+), 41 deletions(-) diff --git a/src/pkg/exp/template/exec.go b/src/pkg/exp/template/exec.go index 15c382147a..b5d4a1d8e7 100644 --- a/src/pkg/exp/template/exec.go +++ b/src/pkg/exp/template/exec.go @@ -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") diff --git a/src/pkg/exp/template/exec_test.go b/src/pkg/exp/template/exec_test.go index 5b9b469dd7..8e50f17dca 100644 --- a/src/pkg/exp/template/exec_test.go +++ b/src/pkg/exp/template/exec_test.go @@ -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. -- 2.50.0