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) { ...@@ -51,8 +51,8 @@ func (s *state) setTop(value reflect.Value) {
s.vars[len(s.vars)-1].value = value s.vars[len(s.vars)-1].value = value
} }
// value returns the value of the named variable. // varValue returns the value of the named variable.
func (s *state) value(name string) reflect.Value { func (s *state) varValue(name string) reflect.Value {
for i := s.mark() - 1; i >= 0; i-- { for i := s.mark() - 1; i >= 0; i-- {
if s.vars[i].name == name { if s.vars[i].name == name {
return s.vars[i].value return s.vars[i].value
...@@ -112,23 +112,23 @@ func (s *state) walk(data reflect.Value, n node) { ...@@ -112,23 +112,23 @@ func (s *state) walk(data reflect.Value, n node) {
s.line = n.line s.line = n.line
defer s.pop(s.mark()) defer s.pop(s.mark())
s.printValue(n, s.evalPipeline(data, n.pipe)) 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: case *listNode:
for _, node := range n.nodes { for _, node := range n.nodes {
s.walk(data, node) s.walk(data, node)
} }
case *ifNode:
s.line = n.line
s.walkIfOrWith(nodeIf, data, n.pipe, n.list, n.elseList)
case *rangeNode: case *rangeNode:
s.line = n.line s.line = n.line
s.walkRange(data, n) s.walkRange(data, n)
case *templateNode:
s.line = n.line
s.walkTemplate(data, n)
case *textNode: case *textNode:
if _, err := s.wr.Write(n.text); err != nil { if _, err := s.wr.Write(n.text); err != nil {
s.error(err) s.error(err)
} }
case *templateNode:
s.line = n.line
s.walkTemplate(data, n)
case *withNode: case *withNode:
s.line = n.line s.line = n.line
s.walkIfOrWith(nodeWith, data, n.pipe, n.list, n.elseList) s.walkIfOrWith(nodeWith, data, n.pipe, n.list, n.elseList)
...@@ -165,16 +165,16 @@ func isTrue(val reflect.Value) (truth, ok bool) { ...@@ -165,16 +165,16 @@ func isTrue(val reflect.Value) (truth, ok bool) {
truth = val.Len() > 0 truth = val.Len() > 0
case reflect.Bool: case reflect.Bool:
truth = val.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: case reflect.Complex64, reflect.Complex128:
truth = val.Complex() != 0 truth = val.Complex() != 0
case reflect.Chan, reflect.Func, reflect.Ptr: case reflect.Chan, reflect.Func, reflect.Ptr:
truth = !val.IsNil() 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: default:
return return
} }
...@@ -274,15 +274,13 @@ func (s *state) evalCommand(data reflect.Value, cmd *commandNode, final reflect. ...@@ -274,15 +274,13 @@ func (s *state) evalCommand(data reflect.Value, cmd *commandNode, final reflect.
case *identifierNode: case *identifierNode:
// Must be a function. // Must be a function.
return s.evalFunction(data, n.ident, cmd.args, final) 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) s.notAFunction(cmd.args, final)
switch word := cmd.args[0].(type) { switch word := firstWord.(type) {
case *dotNode:
return data
case *boolNode: case *boolNode:
return reflect.ValueOf(word.true) return reflect.ValueOf(word.true)
case *dotNode:
return data
case *numberNode: case *numberNode:
// These are ideal constants but we don't know the type // These are ideal constants but we don't know the type
// and we have no context. (If it was a method argument, // 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. ...@@ -299,6 +297,8 @@ func (s *state) evalCommand(data reflect.Value, cmd *commandNode, final reflect.
} }
case *stringNode: case *stringNode:
return reflect.ValueOf(word.text) return reflect.ValueOf(word.text)
case *variableNode:
return s.varValue(word.ident)
} }
s.errorf("can't handle command %q", firstWord) s.errorf("can't handle command %q", firstWord)
panic("not reached") panic("not reached")
...@@ -322,11 +322,6 @@ func (s *state) evalFunction(data reflect.Value, name string, args []node, final ...@@ -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) 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? // Is this an exported - upper case - name?
func isExported(name string) bool { func isExported(name string) bool {
rune, _ := utf8.DecodeRuneInString(name) rune, _ := utf8.DecodeRuneInString(name)
...@@ -439,37 +434,40 @@ func (s *state) evalCall(v, fun reflect.Value, name string, isMethod bool, args ...@@ -439,37 +434,40 @@ func (s *state) evalCall(v, fun reflect.Value, name string, isMethod bool, args
return result[0] 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 { func (s *state) evalArg(data reflect.Value, typ reflect.Type, n node) reflect.Value {
switch arg := n.(type) { switch arg := n.(type) {
case *dotNode: case *dotNode:
if !data.Type().AssignableTo(typ) { return s.validateType(data, typ)
s.errorf("wrong type for value; expected %s; got %s", typ, data.Type())
}
return data
case *fieldNode: case *fieldNode:
value := s.evalFieldNode(data, arg, []node{n}, zero) return s.validateType(s.evalFieldNode(data, arg, []node{n}, zero), typ)
if !value.Type().AssignableTo(typ) { case *variableNode:
s.errorf("wrong type for value; expected %s; got %s", typ, value.Type()) return s.validateType(s.varValue(arg.ident), typ)
}
return value
} }
switch typ.Kind() { switch typ.Kind() {
case reflect.Bool: case reflect.Bool:
return s.evalBool(typ, n) 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: case reflect.Complex64, reflect.Complex128:
return s.evalComplex(typ, n) 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: case reflect.Interface:
if typ.NumMethod() == 0 { if typ.NumMethod() == 0 {
return s.evalEmptyInterface(data, typ, n) 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) s.errorf("can't handle %s for arg of type %s", n, typ)
panic("not reached") panic("not reached")
...@@ -560,6 +558,8 @@ func (s *state) evalEmptyInterface(data reflect.Value, typ reflect.Type, n node) ...@@ -560,6 +558,8 @@ func (s *state) evalEmptyInterface(data reflect.Value, typ reflect.Type, n node)
} }
case *stringNode: case *stringNode:
return reflect.ValueOf(n.text) 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) s.errorf("can't handle assignment of %s to empty interface argument", n)
panic("not reached") panic("not reached")
......
...@@ -183,6 +183,7 @@ var execTests = []execTest{ ...@@ -183,6 +183,7 @@ var execTests = []execTest{
{".Method1(.I)", "-{{.Method1 .I}}-", "-17-", tVal, true}, {".Method1(.I)", "-{{.Method1 .I}}-", "-17-", tVal, true},
{".Method2(3, .X)", "-{{.Method2 3 .X}}-", "-Method2: 3 x-", 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, `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. // Pipelines.
{"pipeline", "-{{.Method0 | .Method2 .U16}}-", "-Method2: 16 M0-", tVal, true}, {"pipeline", "-{{.Method0 | .Method2 .U16}}-", "-Method2: 16 M0-", tVal, true},
...@@ -212,6 +213,8 @@ var execTests = []execTest{ ...@@ -212,6 +213,8 @@ var execTests = []execTest{
{"printf function", `{{printf "%#q" zeroArgs}}`, "`zeroArgs`", tVal, true}, {"printf function", `{{printf "%#q" zeroArgs}}`, "`zeroArgs`", tVal, true},
{"printf field", `{{printf "%s" .U.V}}`, "v", tVal, true}, {"printf field", `{{printf "%s" .U.V}}`, "v", tVal, true},
{"printf method", `{{printf "%s" .Method0}}`, "M0", 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}, {"printf lots", `{{printf "%d %s %g %s" 127 "hello" 7-3i .Method0}}`, "127 hello (7-3i) M0", tVal, true},
// HTML. // 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