}
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)
+ // Can't use evalArg because there are two types we expect.
+ arg := s.evalEmptyInterface(data, t.name)
+ if !arg.IsValid() {
+ s.errorf("invalid value in template invocation; expected string or *Template")
+ }
+ var tmpl *Template
+ if arg.Type() == reflect.TypeOf((*Template)(nil)) {
+ tmpl = arg.Interface().(*Template)
+ if tmpl == nil {
+ s.errorf("nil template")
+ }
+ } else {
+ s.validateType(arg, reflect.TypeOf(""))
+ name := arg.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)
+ }
}
defer s.pop(s.mark())
data = s.evalPipeline(data, t.pipe)
// 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) reflect.Value {
- value := zero
+func (s *state) evalPipeline(data 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.
// If the object has type interface{}, dig down one level to the thing inside.
return result[0]
}
-// validateType guarantees that the value is assignable to the type.
+// validateType guarantees that the value is valid and assignable to the type.
func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Value {
+ if !value.IsValid() {
+ s.errorf("invalid value; expected %s", typ)
+ }
if !value.Type().AssignableTo(typ) {
s.errorf("wrong type for value; expected %s; got %s", typ, value.Type())
}
return s.evalInteger(typ, n)
case reflect.Interface:
if typ.NumMethod() == 0 {
- return s.evalEmptyInterface(data, typ, n)
+ return s.evalEmptyInterface(data, n)
}
case reflect.String:
return s.evalString(typ, n)
panic("not reached")
}
-func (s *state) evalEmptyInterface(data reflect.Value, typ reflect.Type, n node) reflect.Value {
+func (s *state) evalEmptyInterface(data reflect.Value, n node) reflect.Value {
switch n := n.(type) {
case *boolNode:
return reflect.ValueOf(n.true)
PI *int
PSI *[]int
NIL *int
+ // Template to test evaluation of templates.
+ Tmpl *Template
}
type U struct {
Empty4: &U{"v"},
PI: newInt(23),
PSI: newIntSlice(21, 22, 23),
+ Tmpl: New("x").MustParse("test template"), // "x" is the value of .X
}
// Helpers for creation.
}
func (t *templateNode) String() string {
+ if t.pipe == nil {
+ return fmt.Sprintf("{{template %s}}", t.name)
+ }
return fmt.Sprintf("{{template %s %s}}", t.name, t.pipe)
}
return newWith(t.parseControl("with"))
}
-
// End:
// {{end}}
// End keyword is past.
default:
t.unexpected(token, "template invocation")
}
- return newTemplate(t.lex.lineNumber(), name, t.pipeline("template"))
+ var pipe *pipeNode
+ if t.next().typ != itemRightDelim {
+ t.backup()
+ pipe = t.pipeline("template")
+ }
+ return newTemplate(t.lex.lineNumber(), name, pipe)
}
// command:
`[({{range [(command: [F=[SI]])]}} [(action: [(command: [{{<.>}}])])])]`},
{"constants", "{{range .SI 1 -3.2i true false }}{{end}}", noError,
`[({{range [(command: [F=[SI] N=1 N=-3.2i B=true B=false])]}} [])]`},
+ {"template", "{{template `x`}}", noError,
+ "[{{template S=`x`}}]"},
{"template", "{{template `x` .Y}}", noError,
"[{{template S=`x` [(command: [F=[Y]])]}}]"},
{"with", "{{with .X}}hello{{end}}", noError,
var setExecTests = []execTest{
{"empty", "", "", nil, true},
{"text", "some text", "some text", nil, true},
- {"invoke text", `{{template "text" .SI}}`, "TEXT", tVal, true},
+ {"invoke x", `{{template "x" .SI}}`, "TEXT", tVal, true},
+ {"invoke x no args", `{{template "x"}}`, "TEXT", tVal, true},
{"invoke dot int", `{{template "dot" .I}}`, "17", tVal, true},
{"invoke dot []int", `{{template "dot" .SI}}`, "[3 4 5]", tVal, true},
{"invoke dotV", `{{template "dotV" .U}}`, "v", tVal, true},
{"invoke nested int", `{{template "nested" .I}}`, "17", tVal, true},
+ {"invoke template by field", `{{template .X}}`, "TEXT", tVal, true},
+ {"invoke template by template", `{{template .Tmpl}}`, "test template", tVal, true},
+ {"invalid: invoke template by []int", `{{template .SI}}`, "", tVal, false},
// User-defined function: test argument evaluator.
{"testFunc literal", `{{oneArg "joe"}}`, "oneArg=joe", tVal, true},
}
const setText = `
- {{define "text"}}TEXT{{end}}
+ {{define "x"}}TEXT{{end}}
{{define "dotV"}}{{.V}}{{end}}
{{define "dot"}}{{.}}{{end}}
{{define "nested"}}{{template "dot" .}}{{end}}