// Walk functions step through the major pieces of the template structure,
// generating output as they go.
-func (s *state) walk(data reflect.Value, n node) {
+func (s *state) walk(dot reflect.Value, n node) {
switch n := n.(type) {
case *actionNode:
s.line = n.line
defer s.pop(s.mark())
- s.printValue(n, s.evalPipeline(data, n.pipe))
+ s.printValue(n, s.evalPipeline(dot, n.pipe))
case *ifNode:
s.line = n.line
- s.walkIfOrWith(nodeIf, data, n.pipe, n.list, n.elseList)
+ s.walkIfOrWith(nodeIf, dot, n.pipe, n.list, n.elseList)
case *listNode:
for _, node := range n.nodes {
- s.walk(data, node)
+ s.walk(dot, node)
}
case *rangeNode:
s.line = n.line
- s.walkRange(data, n)
+ s.walkRange(dot, n)
case *templateNode:
s.line = n.line
- s.walkTemplate(data, n)
+ s.walkTemplate(dot, n)
case *textNode:
if _, err := s.wr.Write(n.text); err != nil {
s.error(err)
}
case *withNode:
s.line = n.line
- s.walkIfOrWith(nodeWith, data, n.pipe, n.list, n.elseList)
+ s.walkIfOrWith(nodeWith, dot, n.pipe, n.list, n.elseList)
default:
s.errorf("unknown node: %s", n)
}
// walkIfOrWith walks an 'if' or 'with' node. The two control structures
// are identical in behavior except that 'with' sets dot.
-func (s *state) walkIfOrWith(typ nodeType, data reflect.Value, pipe *pipeNode, list, elseList *listNode) {
+func (s *state) walkIfOrWith(typ nodeType, dot reflect.Value, pipe *pipeNode, list, elseList *listNode) {
defer s.pop(s.mark())
- val := s.evalPipeline(data, pipe)
+ val := s.evalPipeline(dot, pipe)
truth, ok := isTrue(val)
if !ok {
s.errorf("if/with can't use value of type %T", val.Interface())
if typ == nodeWith {
s.walk(val, list)
} else {
- s.walk(data, list)
+ s.walk(dot, list)
}
} else if elseList != nil {
- s.walk(data, elseList)
+ s.walk(dot, elseList)
}
}
return truth, true
}
-func (s *state) walkRange(data reflect.Value, r *rangeNode) {
+func (s *state) walkRange(dot reflect.Value, r *rangeNode) {
defer s.pop(s.mark())
- val, _ := indirect(s.evalPipeline(data, r.pipe))
+ val, _ := indirect(s.evalPipeline(dot, r.pipe))
switch val.Kind() {
case reflect.Array, reflect.Slice:
if val.Len() == 0 {
s.errorf("range can't iterate over value of type %T", val.Interface())
}
if r.elseList != nil {
- s.walk(data, r.elseList)
+ s.walk(dot, r.elseList)
}
}
-func (s *state) walkTemplate(data reflect.Value, t *templateNode) {
+func (s *state) walkTemplate(dot reflect.Value, t *templateNode) {
// Can't use evalArg because there are two types we expect.
- arg := s.evalEmptyInterface(data, t.name)
+ arg := s.evalEmptyInterface(dot, t.name)
if !arg.IsValid() {
s.errorf("invalid value in template invocation; expected string or *Template")
}
}
}
defer s.pop(s.mark())
- data = s.evalPipeline(data, t.pipe)
+ dot = s.evalPipeline(dot, t.pipe)
newState := *s
newState.tmpl = tmpl
// No dynamic scoping: template invocations inherit no variables.
- newState.vars = []variable{{"$", data}}
- newState.walk(data, tmpl.root)
+ newState.vars = []variable{{"$", dot}}
+ newState.walk(dot, tmpl.root)
}
// Eval functions evaluate pipelines, commands, and their elements and extract
// pipeline has a variable declaration, the variable will be pushed on the
// stack. Callers should therefore pop the stack after they are finished
// executing commands depending on the pipeline value.
-func (s *state) evalPipeline(data reflect.Value, pipe *pipeNode) (value reflect.Value) {
+func (s *state) evalPipeline(dot reflect.Value, pipe *pipeNode) (value reflect.Value) {
if pipe == nil {
return
}
for _, cmd := range pipe.cmds {
- value = s.evalCommand(data, cmd, value) // previous value is this one's final arg.
+ value = s.evalCommand(dot, cmd, value) // previous value is this one's final arg.
// If the object has type interface{}, dig down one level to the thing inside.
if value.Kind() == reflect.Interface && value.Type().NumMethod() == 0 {
value = reflect.ValueOf(value.Interface()) // lovely!
}
}
-func (s *state) evalCommand(data reflect.Value, cmd *commandNode, final reflect.Value) reflect.Value {
+func (s *state) evalCommand(dot reflect.Value, cmd *commandNode, final reflect.Value) reflect.Value {
firstWord := cmd.args[0]
switch n := firstWord.(type) {
case *fieldNode:
- return s.evalFieldNode(data, n, cmd.args, final)
+ return s.evalFieldNode(dot, n, cmd.args, final)
case *identifierNode:
// Must be a function.
- return s.evalFunction(data, n.ident, cmd.args, final)
+ return s.evalFunction(dot, n.ident, cmd.args, final)
case *variableNode:
- return s.evalVariableNode(n, cmd.args, final)
+ return s.evalVariableNode(dot, n, cmd.args, final)
}
s.notAFunction(cmd.args, final)
switch word := firstWord.(type) {
case *boolNode:
return reflect.ValueOf(word.true)
case *dotNode:
- return data
+ return dot
case *numberNode:
// These are ideal constants but we don't know the type
// and we have no context. (If it was a method argument,
panic("not reached")
}
-func (s *state) evalFieldNode(data reflect.Value, field *fieldNode, args []node, final reflect.Value) reflect.Value {
- return s.evalFieldChain(data, field.ident, args, final)
+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)
}
-func (s *state) evalVariableNode(v *variableNode, args []node, final reflect.Value) reflect.Value {
+func (s *state) evalVariableNode(dot reflect.Value, v *variableNode, args []node, final reflect.Value) reflect.Value {
// $x.Field has $x as the first ident, Field as the second. Eval the var, then the fields.
- data := s.varValue(v.ident[0])
+ value := s.varValue(v.ident[0])
if len(v.ident) == 1 {
- return data
+ return value
}
- return s.evalFieldChain(data, v.ident[1:], args, final)
+ return s.evalFieldChain(dot, value, v.ident[1:], args, final)
}
-func (s *state) evalFieldChain(data reflect.Value, ident []string, args []node, final reflect.Value) reflect.Value {
+func (s *state) evalFieldChain(dot, receiver reflect.Value, ident []string, args []node, final reflect.Value) reflect.Value {
// Up to the last entry, it must be a field.
n := len(ident)
for i := 0; i < n-1; i++ {
- data = s.evalField(data, ident[i], nil, zero, false)
+ dot = s.evalField(dot, ident[i], nil, zero, zero)
}
// Now it can be a field or method and if a method, gets arguments.
- return s.evalField(data, ident[n-1], args, final, true)
+ return s.evalField(dot, ident[n-1], args, final, receiver)
}
-func (s *state) evalFunction(data reflect.Value, name string, args []node, final reflect.Value) reflect.Value {
+func (s *state) evalFunction(dot reflect.Value, name string, args []node, final reflect.Value) reflect.Value {
function, ok := findFunction(name, s.tmpl, s.set)
if !ok {
s.errorf("%q is not a defined function", name)
}
- return s.evalCall(data, function, name, false, args, final)
+ return s.evalCall(dot, zero, function, name, args, final)
}
// Is this an exported - upper case - name?
// value of the pipeline, if any.
// If we're in a chain, such as (.X.Y.Z), .X and .Y cannot be methods;
// canBeMethod will be true only for the last element of such chains (here .Z).
-func (s *state) evalField(data reflect.Value, fieldName string, args []node, final reflect.Value,
-canBeMethod bool) reflect.Value {
- typ := data.Type()
- var isNil bool
- data, isNil = indirect(data)
- if canBeMethod {
+func (s *state) evalField(dot reflect.Value, fieldName string, args []node, final reflect.Value,
+receiver reflect.Value) reflect.Value {
+ typ := dot.Type()
+ if receiver.IsValid() {
+ receiver, _ = indirect(receiver)
// Need to get to a value of type *T to guarantee we see all
// methods of T and *T.
- ptr := data
+ ptr := receiver
if ptr.CanAddr() {
ptr = ptr.Addr()
}
if method, ok := methodByName(ptr.Type(), fieldName); ok {
- return s.evalCall(ptr, method.Func, fieldName, true, args, final)
+ return s.evalCall(dot, ptr, method.Func, fieldName, args, final)
}
}
// It's not a method; is it a field of a struct?
- if data.Kind() == reflect.Struct {
- field := data.FieldByName(fieldName)
+ dot, isNil := indirect(dot)
+ if dot.Kind() == reflect.Struct {
+ field := dot.FieldByName(fieldName)
if field.IsValid() {
if len(args) > 1 || final.IsValid() {
s.errorf("%s is not a method but has arguments", fieldName)
osErrorType = reflect.TypeOf(new(os.Error)).Elem()
)
-func (s *state) evalCall(v, fun reflect.Value, name string, isMethod bool, args []node, final reflect.Value) reflect.Value {
+func (s *state) evalCall(dot, receiver, fun reflect.Value, name string, args []node, final reflect.Value) reflect.Value {
typ := fun.Type()
+ isMethod := receiver.IsValid()
if !isMethod && len(args) > 0 { // Args will be nil if it's a niladic call in an argument list
args = args[1:] // first arg is name of function; not used in call.
}
// First arg is the receiver.
i := 0
if isMethod {
- argv[0] = v
+ argv[0] = receiver
i++
}
// Others must be evaluated. Fixed args first.
for ; i < numFixed; i++ {
- argv[i] = s.evalArg(v, typ.In(i), args[i])
+ argv[i] = s.evalArg(dot, typ.In(i), args[i])
}
// And now the ... args.
if typ.IsVariadic() {
argType := typ.In(typ.NumIn() - 1).Elem() // Argument is a slice.
for ; i < len(args); i++ {
- argv[i] = s.evalArg(v, argType, args[i])
+ argv[i] = s.evalArg(dot, argType, args[i])
}
}
// Add final value if necessary.
return value
}
-func (s *state) evalArg(data reflect.Value, typ reflect.Type, n node) reflect.Value {
+func (s *state) evalArg(dot reflect.Value, typ reflect.Type, n node) reflect.Value {
switch arg := n.(type) {
case *dotNode:
- return s.validateType(data, typ)
+ return s.validateType(dot, typ)
case *fieldNode:
- return s.validateType(s.evalFieldNode(data, arg, []node{n}, zero), typ)
+ return s.validateType(s.evalFieldNode(dot, arg, []node{n}, zero), typ)
case *variableNode:
- return s.validateType(s.evalVariableNode(arg, nil, zero), typ)
+ return s.validateType(s.evalVariableNode(dot, arg, nil, zero), typ)
}
switch typ.Kind() {
case reflect.Bool:
return s.evalInteger(typ, n)
case reflect.Interface:
if typ.NumMethod() == 0 {
- return s.evalEmptyInterface(data, n)
+ return s.evalEmptyInterface(dot, n)
}
case reflect.String:
return s.evalString(typ, n)
panic("not reached")
}
-func (s *state) evalEmptyInterface(data reflect.Value, n node) reflect.Value {
+func (s *state) evalEmptyInterface(dot reflect.Value, n node) reflect.Value {
switch n := n.(type) {
case *boolNode:
return reflect.ValueOf(n.true)
case *dotNode:
- return data
+ return dot
case *fieldNode:
- return s.evalFieldNode(data, n, nil, zero)
+ return s.evalFieldNode(dot, n, nil, zero)
case *identifierNode:
- return s.evalFunction(data, n.ident, nil, zero)
+ return s.evalFunction(dot, n.ident, nil, zero)
case *numberNode:
if n.isComplex {
return reflect.ValueOf(n.complex128)
case *stringNode:
return reflect.ValueOf(n.text)
case *variableNode:
- return s.evalVariableNode(n, nil, zero)
+ return s.evalVariableNode(dot, n, nil, zero)
}
s.errorf("can't handle assignment of %s to empty interface argument", n)
panic("not reached")