// Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package template import ( "fmt" "io" "os" "reflect" "strings" ) // state represents the state of an execution. It's not part of the // template so that multiple executions of the same template // can execute in parallel. type state struct { tmpl *Template wr io.Writer set *Set line int // line number for errors } // errorf formats the error and terminates processing. func (s *state) errorf(format string, args ...interface{}) { format = fmt.Sprintf("template: %s:%d: %s", s.tmpl.name, s.line, format) panic(fmt.Errorf(format, args...)) } // error terminates processing. func (s *state) error(err os.Error) { s.errorf("%s", err) } // Execute applies a parsed template to the specified data object, // writing the output to wr. func (t *Template) Execute(wr io.Writer, data interface{}) os.Error { return t.ExecuteInSet(wr, data, nil) } // ExecuteInSet applies a parsed template to the specified data object, // writing the output to wr. Nested template invocations will be resolved // from the specified set. func (t *Template) ExecuteInSet(wr io.Writer, data interface{}, set *Set) (err os.Error) { defer t.recover(&err) state := &state{ tmpl: t, wr: wr, set: set, line: 1, } if t.root == nil { state.errorf("must be parsed before execution") } state.walk(reflect.ValueOf(data), t.root) return } // 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) { switch n := n.(type) { case *actionNode: s.line = n.line s.printValue(n, s.evalPipeline(data, n.pipeline)) case *listNode: for _, node := range n.nodes { s.walk(data, node) } case *ifNode: s.walkIfOrWith(nodeIf, data, n.pipeline, n.list, n.elseList) case *rangeNode: s.walkRange(data, n) case *textNode: if _, err := s.wr.Write(n.text); err != nil { s.error(err) } case *templateNode: s.walkTemplate(data, n) case *withNode: s.walkIfOrWith(nodeWith, data, n.pipeline, 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 []*commandNode, list, elseList *listNode) { val := s.evalPipeline(data, pipe) truth := false switch val.Kind() { case reflect.Array, reflect.Map, reflect.Slice, reflect.String: 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() default: s.errorf("if/with can't use value of type %T", val.Interface()) } if truth { if typ == nodeWith { data = val } s.walk(data, list) } else if elseList != nil { s.walk(data, elseList) } } func (s *state) walkRange(data reflect.Value, r *rangeNode) { val := s.evalPipeline(data, r.pipeline) switch val.Kind() { case reflect.Array, reflect.Slice: if val.Len() == 0 { break } for i := 0; i < val.Len(); i++ { s.walk(val.Index(i), r.list) } return case reflect.Map: if val.Len() == 0 { break } for _, key := range val.MapKeys() { s.walk(val.MapIndex(key), r.list) } return default: s.errorf("range can't iterate over value of type %T", val.Interface()) } if r.elseList != nil { s.walk(data, r.elseList) } } func (s *state) walkTemplate(data reflect.Value, t *templateNode) { name := s.evalArg(data, reflect.TypeOf("string"), t.name).String() if s.set == nil { s.errorf("no set defined in which to invoke template named %q", name) } tmpl := s.set.tmpl[name] if tmpl == nil { s.errorf("template %q not in set", name) } data = s.evalPipeline(data, t.pipeline) newState := *s newState.tmpl = tmpl newState.walk(data, tmpl.root) } // Eval functions evaluate pipelines, commands, and their elements and extract // values from the data structure by examining fields, calling methods, and so on. // The printing of those values happens only through walk functions. func (s *state) evalPipeline(data reflect.Value, pipe []*commandNode) reflect.Value { value := reflect.Value{} for _, cmd := range pipe { value = s.evalCommand(data, cmd, value) // previous value is this one's final arg. } return value } func (s *state) evalCommand(data 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) case *identifierNode: return s.evalFieldOrCall(data, n.ident, cmd.args, final) } if len(cmd.args) > 1 || final.IsValid() { // TODO: functions s.errorf("can't give argument to non-method %s", cmd.args[0]) } switch word := cmd.args[0].(type) { case *dotNode: return data case *boolNode: return reflect.ValueOf(word.true) case *numberNode: // These are ideal constants but we don't know the type // and we have no context. (If it was a method argument, // we'd know what we need.) The syntax guides us to some extent. switch { case word.isComplex: return reflect.ValueOf(word.complex128) // incontrovertible. case word.isFloat && strings.IndexAny(word.text, ".eE") >= 0: return reflect.ValueOf(word.float64) case word.isInt: return reflect.ValueOf(word.int64) case word.isUint: return reflect.ValueOf(word.uint64) } case *stringNode: return reflect.ValueOf(word.text) default: s.errorf("can't handle command %q", firstWord) } panic("not reached") } func (s *state) evalFieldNode(data reflect.Value, field *fieldNode, args []node, final reflect.Value) reflect.Value { // Up to the last entry, it must be a field. n := len(field.ident) for i := 0; i < n-1; i++ { data = s.evalField(data, field.ident[i]) } // Now it can be a field or method and if a method, gets arguments. return s.evalFieldOrCall(data, field.ident[n-1], args, final) } func (s *state) evalField(data reflect.Value, fieldName string) reflect.Value { for data.Kind() == reflect.Ptr { data = reflect.Indirect(data) } switch data.Kind() { case reflect.Struct: // Is it a field? field := data.FieldByName(fieldName) // TODO: look higher up the tree if we can't find it here. Also unexported fields // might succeed higher up, as map keys. if field.IsValid() && field.Type().PkgPath() == "" { // valid and exported return field } s.errorf("%s has no field %s", data.Type(), fieldName) default: s.errorf("can't evaluate field %s of type %s", fieldName, data.Type()) } panic("not reached") } func (s *state) evalFieldOrCall(data reflect.Value, fieldName string, args []node, final reflect.Value) reflect.Value { // Is it a function? if function, ok := findFunction(fieldName, s.tmpl, s.set); ok { return s.evalCall(data, function, fieldName, false, args, final) } ptr := data for data.Kind() == reflect.Ptr { ptr, data = data, reflect.Indirect(data) } // Is it a method? We use the pointer because it has value methods too. if method, ok := ptr.Type().MethodByName(fieldName); ok { return s.evalCall(ptr, method.Func, fieldName, true, args, final) } if len(args) > 1 || final.IsValid() { s.errorf("%s is not a method but has arguments", fieldName) } switch data.Kind() { case reflect.Struct: return s.evalField(data, fieldName) default: s.errorf("can't handle evaluation of field %s of type %s", fieldName, data.Type()) } panic("not reached") } var ( 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 { typ := fun.Type() 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. } numIn := len(args) if final.IsValid() { numIn++ } numFixed := len(args) if typ.IsVariadic() { numFixed = typ.NumIn() - 1 // last arg is the variadic one. if numIn < numFixed { s.errorf("wrong number of args for %s: want at least %d got %d", name, typ.NumIn()-1, len(args)) } } else if numIn < typ.NumIn()-1 || !typ.IsVariadic() && numIn != typ.NumIn() { s.errorf("wrong number of args for %s: want %d got %d", name, typ.NumIn(), len(args)) } if !goodFunc(typ) { s.errorf("can't handle multiple results from method/function %q", name) } // Build the arg list. argv := make([]reflect.Value, numIn) // First arg is the receiver. i := 0 if isMethod { argv[0] = v i++ } // Others must be evaluated. Fixed args first. for ; i < numFixed; i++ { argv[i] = s.evalArg(v, 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]) } } // Add final value if necessary. if final.IsValid() { argv[len(args)] = final } result := fun.Call(argv) // If we have an os.Error that is not nil, stop execution and return that error to the caller. if len(result) == 2 && !result[1].IsNil() { s.error(result[1].Interface().(os.Error)) } return result[0] } func (s *state) evalArg(data reflect.Value, typ reflect.Type, n node) reflect.Value { if field, ok := n.(*fieldNode); ok { value := s.evalFieldNode(data, field, []node{n}, reflect.Value{}) if !value.Type().AssignableTo(typ) { s.errorf("wrong type for value; expected %s; got %s", typ, value.Type()) } return value } 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.Interface: if typ.NumMethod() == 0 { return s.evalEmptyInterface(data, typ, n) } } s.errorf("can't handle %s for arg of type %s", n, typ) panic("not reached") } func (s *state) evalBool(typ reflect.Type, n node) reflect.Value { if n, ok := n.(*boolNode); ok { value := reflect.New(typ).Elem() value.SetBool(n.true) return value } s.errorf("expected bool; found %s", n) panic("not reached") } func (s *state) evalString(typ reflect.Type, n node) reflect.Value { if n, ok := n.(*stringNode); ok { value := reflect.New(typ).Elem() value.SetString(n.text) return value } s.errorf("expected string; found %s", n) panic("not reached") } func (s *state) evalInteger(typ reflect.Type, n node) reflect.Value { if n, ok := n.(*numberNode); ok && n.isInt { value := reflect.New(typ).Elem() value.SetInt(n.int64) return value } s.errorf("expected integer; found %s", n) panic("not reached") } func (s *state) evalUnsignedInteger(typ reflect.Type, n node) reflect.Value { if n, ok := n.(*numberNode); ok && n.isUint { value := reflect.New(typ).Elem() value.SetUint(n.uint64) return value } s.errorf("expected unsigned integer; found %s", n) panic("not reached") } func (s *state) evalFloat(typ reflect.Type, n node) reflect.Value { if n, ok := n.(*numberNode); ok && n.isFloat { value := reflect.New(typ).Elem() value.SetFloat(n.float64) return value } s.errorf("expected float; found %s", n) panic("not reached") } func (s *state) evalComplex(typ reflect.Type, n node) reflect.Value { if n, ok := n.(*numberNode); ok && n.isComplex { value := reflect.New(typ).Elem() value.SetComplex(n.complex128) return value } s.errorf("expected complex; found %s", n) panic("not reached") } func (s *state) evalEmptyInterface(data reflect.Value, typ reflect.Type, n node) reflect.Value { switch n := n.(type) { case *boolNode: return reflect.ValueOf(n.true) case *fieldNode: return s.evalFieldNode(data, n, nil, reflect.Value{}) case *identifierNode: return s.evalFieldOrCall(data, n.ident, nil, reflect.Value{}) case *numberNode: if n.isComplex { return reflect.ValueOf(n.complex128) } if n.isInt { return reflect.ValueOf(n.int64) } if n.isUint { return reflect.ValueOf(n.uint64) } if n.isFloat { return reflect.ValueOf(n.float64) } case *stringNode: return reflect.ValueOf(n.text) } s.errorf("can't handle assignment of %s to empty interface argument", n) panic("not reached") } // printValue writes the textual representation of the value to the output of // the template. func (s *state) printValue(n node, v reflect.Value) { if !v.IsValid() { return } switch v.Kind() { case reflect.Ptr: if v.IsNil() { s.errorf("%s: nil value", n) } case reflect.Chan, reflect.Func, reflect.Interface: s.errorf("can't print %s of type %s", n, v.Type()) } fmt.Fprint(s.wr, v.Interface()) }