is copied to the output.
{{if pipeline}} T1 {{end}}
- If the value of the pipeline is the "zero value" (see below) for
- its type, no output is generated; otherwise, T1 is executed.
+ If the value of the pipeline is empty, no output is generated;
+ otherwise, T1 is executed. The empty values are false, 0, any
+ nil pointer or interface value, and any array, slice, map, or
+ string of length zero.
Dot is unaffected.
{{if pipeline}} T1 {{else}} T0 {{end}}
- If the value of the pipeline is the zero value for its type, T0
- is executed; otherwise, T1 is executed. Dot is unaffected.
+ If the value of the pipeline is empty, T0 is executed;
+ otherwise, T1 is executed. Dot is unaffected.
{{range pipeline}} T1 {{end}}
The value of the pipeline must be an array, slice, or map. If
T0 is executed; otherwise, dot is set to the successive elements
of the array, slice, or map and T1 is executed.
- {{template argument}}
- If the value of the argument is a string, the template with that
- name is executed with nil data. If the value of arg is of type
- *Template, that template is executed.
+ {{template "name"}}
+ The template with the specified name is executed with nil data.
- {{template argument pipeline}}
- If the value of the argument is a string, the template with that
- name is executed with data set to the value of the pipeline. If
- the value of arg is of type *Template, that template is
- executed.
+ {{template "name" pipeline}}
+ The template with the specified name is executed with dot set
+ to the value of the pipeline.
{{with pipeline}} T1 {{end}}
- If the value of the pipeline is the zero value for its type, no
- output is generated; otherwise, dot is set to the value of the
- pipeline and T1 is executed.
+ If the value of the pipeline is empty, no output is generated;
+ otherwise, dot is set to the value of the pipeline and T1 is
+ executed.
{{with pipeline}} T1 {{else}} T0 {{end}}
- If the value of the pipeline is the zero value for its type, dot
- is unaffected and T0 is executed; otherwise, dot is set to the
- value of the pipeline and T1 is executed.
-
-"Zero value" means the true zero value in Go terms. Also, for arrays, slices,
-maps, and strings, any value v with len(v)==0 counts as a zero value.
+ If the value of the pipeline is empty, dot is unaffected and T0
+ is executed; otherwise, dot is set to the value of the pipeline
+ and T1 is executed.
Arguments
such as
.Method
The result is the value of invoking the method with dot as the
- receiver, dot.Method(). Such methods must have one return value (of
+ receiver, dot.Method(). Such a method must have one return value (of
any type) or two return values, the second of which is an os.Error.
If it has two and the returned error is non-nil, execution terminates
- and that error is returned to the caller as the value of Execute.
+ and an error is returned to the caller as the value of Execute.
Method invocations may be chained, but only the last element of
- the chain may be a method; other others must be struct fields:
+ the chain may be a method; others must be struct fields:
.Field1.Field2.Method
Methods can also be evaluated on variables, including chaining:
$x.Field1.Method
A string constant.
{{`"output"`}}
A raw string constant.
- {{printf "%q" output}}
+ {{printf "%q" "output"}}
A function call.
{{"output" | printf "%q"}}
A function call whose final argument comes from the previous
A more elaborate call.
{{"output" | printf "%s" | printf "%q"}}
A longer chain.
- {{$x := "output" | printf "%s" | printf "%q"}}
- An unused variables captures the output.
{{with "output"}}{{printf "%q" .}}{{end}}
A with action using dot.
{{with $x := "output" | printf "%q"}}{{$x}}{{end}}
- A with action creates and uses a variable.
+ A with action that creates and uses a variable.
{{with $x := "output"}}{{printf "%q" $x}}{{end}}
- A with action uses the variable in another action.
+ A with action that uses the variable in another action.
{{with $x := "output"}}{{$x | printf "%q"}}{{end}}
The same, but pipelined.
Template sets
-All templates are named by a string specified when they are created. A template
-may use a template invocation to instantiate another template directly or by its
-name; see the explanation of the template action above. The name of a template
-is looked up in the template set active during the invocation.
+Each template is named by a string specified when it is created. A template may
+use a template invocation to instantiate another template directly or by its
+name; see the explanation of the template action above. The name is looked up
+in the template set active during the invocation.
If no template invocation actions occur in the template, the issue of template
sets can be ignored. If it does contain invocations, though, a set must be
There are two ways to construct template sets.
-The first is to use the Parse method of Set to create a set of named templates
-by reading a single string defining multiple templates. The syntax of the
-definitions is to surround each template declaration with a define and end
-action; those actions are discarded after parsing.
+The first is to use a Set's Parse method to create a set of named templates from
+a single input defining multiple templates. The syntax of the definitions is to
+surround each template declaration with a define and end action.
The define action names the template being created by providing a string
constant. Here is a simple example of input to Set.Parse:
This defines two templates, T1 and T2, and a third T3 that invokes the other two
when it is executed.
-The second way to build a template set is to use the Add method of Set to bind
+The second way to build a template set is to use Set's Add method to add
a template to a set. A template may be bound to multiple sets.
Set.Parse may be called multiple times on different inputs to construct the set.
Two sets may therefore be constructed with a common base set of templates plus,
through a second Parse call each, specializations for some elements.
-When templates are executed via Template.Execute, no set is defined and so no
+When a template is executed via Template.Execute, no set is defined and so no
template invocations are possible. The method Template.ExecuteInSet provides a
way to specify a template set when executing a template directly.
}
func (s *state) walkTemplate(dot reflect.Value, t *templateNode) {
- // Can't use evalArg because there are two types we expect.
- arg := s.evalEmptyInterface(dot, 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)
- }
+ if s.set == nil {
+ s.errorf("no set defined in which to invoke template named %q", t.name)
+ }
+ tmpl := s.set.tmpl[t.name]
+ if tmpl == nil {
+ s.errorf("template %q not in set", t.name)
}
defer s.pop(s.mark())
dot = s.evalPipeline(dot, t.pipe)
},
},
}
- set := NewSet()
+ set := new(Set)
err := set.Parse(treeTemplate)
if err != nil {
t.Fatal("parse error:", err)
}
var b bytes.Buffer
- err = set.Execute("tree", &b, tree)
+ err = set.Execute(&b, "tree", tree)
if err != nil {
t.Fatal("exec error:", err)
}
// FuncMap is the type of the map defining the mapping from names to functions.
// Each function must have either a single return value, or two return values of
// which the second has type os.Error. If the second argument evaluates to non-nil
-// during execution, execution terminates and the error is returned by Execute.
+// during execution, execution terminates and Execute returns an error.
type FuncMap map[string]interface{}
var funcs = map[string]reflect.Value{
"println": reflect.ValueOf(fmt.Sprintln),
}
-// Funcs adds to the global function map the elements of the
-// argument map. It panics if a value in the map is not a function
-// with appropriate return type.
-func Funcs(funcMap FuncMap) {
- addFuncs(funcs, funcMap)
-}
-
// addFuncs adds to values the functions in funcs, converting them to reflect.Values.
func addFuncs(values map[string]reflect.Value, funcMap FuncMap) {
for name, fn := range funcMap {
return t
}
-// ParseFile is a helper function that creates a new Template and parses
-// the template definition from the named file.
-// The template name is the base name of the file.
+// ParseFile creates a new Template and parses the template definition from
+// the named file. The template name is the base name of the file.
func ParseFile(filename string) (*Template, os.Error) {
t := New(filepath.Base(filename))
return t, t.ParseFile(filename)
}
-// MustParseFile is a helper function that creates a new Template and parses
-// the template definition from the named file.
-// The template name is the base name of the file.
+// MustParseFile creates a new Template and parses the template definition
+// from the named file. The template name is the base name of the file.
// It panics if the file cannot be read or the template cannot be parsed.
func MustParseFile(filename string) *Template {
return New(filepath.Base(filename)).MustParseFile(filename)
return s
}
-// ParseSetFile is a helper function that creates a new Set and parses
-// the set definition from the named file.
+// ParseSetFile creates a new Set and parses the set definition from the
+// named file.
func ParseSetFile(filename string) (*Set, os.Error) {
- s := NewSet()
+ s := new(Set)
return s, s.ParseFile(filename)
}
-// MustParseSetFile is a helper function that creates a new Set and parses
-// the set definition from the named file.
+// MustParseSetFile creates a new Set and parses the set definition from the
+// named file.
// It panics if the file cannot be read or the set cannot be parsed.
func MustParseSetFile(filename string) *Set {
- return NewSet().MustParseFile(filename)
+ return new(Set).MustParseFile(filename)
}
type templateNode struct {
nodeType
line int
- name node
+ name string
pipe *pipeNode
}
-func newTemplate(line int, name node, pipe *pipeNode) *templateNode {
+func newTemplate(line int, name string, pipe *pipeNode) *templateNode {
return &templateNode{nodeType: nodeTemplate, line: line, name: name, pipe: pipe}
}
func (t *templateNode) String() string {
if t.pipe == nil {
- return fmt.Sprintf("{{template %s}}", t.name)
+ return fmt.Sprintf("{{template %q}}", t.name)
}
- return fmt.Sprintf("{{template %s %s}}", t.name, t.pipe)
+ return fmt.Sprintf("{{template %q %s}}", t.name, t.pipe)
}
// withNode represents a {{with}} action and its commands.
}
}
-// Funcs adds to the template's function map the elements of the
-// argument map. It panics if a value in the map is not a function
-// with appropriate return type.
+// Funcs adds the elements of the argument map to the template's function
+// map. It panics if a value in the map is not a function with appropriate
+// return type.
// The return value is the template, so calls can be chained.
func (t *Template) Funcs(funcMap FuncMap) *Template {
addFuncs(t.funcs, funcMap)
// Template keyword is past. The name must be something that can evaluate
// to a string.
func (t *Template) templateControl() node {
- var name node
+ var name string
switch token := t.next(); token.typ {
- case itemIdentifier:
- if _, ok := findFunction(token.val, t, t.set); !ok {
- t.errorf("function %q not defined", token.val)
- }
- name = newIdentifier(token.val)
- case itemDot:
- name = newDot()
- case itemVariable:
- name = t.useVar(token.val)
- case itemField:
- name = newField(token.val)
case itemString, itemRawString:
s, err := strconv.Unquote(token.val)
if err != nil {
t.error(err)
}
- name = newString(s)
+ name = s
default:
t.unexpected(token, "template invocation")
}
{"constants", "{{range .SI 1 -3.2i true false 'a'}}{{end}}", noError,
`[({{range [(command: [F=[SI] N=1 N=-3.2i B=true B=false N='a'])]}} [])]`},
{"template", "{{template `x`}}", noError,
- "[{{template S=`x`}}]"},
- {"template", "{{template `x` .Y}}", noError,
- "[{{template S=`x` [(command: [F=[Y]])]}}]"},
+ `[{{template "x"}}]`},
+ {"template with arg", "{{template `x` .Y}}", noError,
+ `[{{template "x" [(command: [F=[Y]])]}}]`},
{"with", "{{with .X}}hello{{end}}", noError,
`[({{with [(command: [F=[X]])]}} [(text: "hello")])]`},
{"with with else", "{{with .X}}hello{{else}}goodbye{{end}}", noError,
`[({{with [(command: [F=[X]])]}} [(text: "hello")] {{else}} [(text: "goodbye")])]`},
- {"variable in template", "{{with $v := `hi`}}{{template $v}}{{end}}", noError,
- "[({{with [$v] := [(command: [S=`hi`])]}} [{{template V=[$v]}}])]"},
// Errors.
{"unclosed action", "hello{{range", hasError, ""},
{"unmatched end", "{{end}}", hasError, ""},
{"variable undefined after end", "{{with $x := 4}}{{end}}{{$x}}", hasError, ""},
{"variable undefined in template", "{{template $v}}", hasError, ""},
{"declare with field", "{{with $x.Y := 4}}{{end}}", hasError, ""},
+ {"template with field ref", "{{template .X}}", hasError, ""},
+ {"template with var", "{{template $v}}", hasError, ""},
}
func TestParse(t *testing.T) {
)
// Set holds a set of related templates that can refer to one another by name.
+// The zero value represents an empty set.
// A template may be a member of multiple sets.
type Set struct {
tmpl map[string]*Template
funcs map[string]reflect.Value
}
-// NewSet allocates a new, empty template set.
-func NewSet() *Set {
- return &Set{
- tmpl: make(map[string]*Template),
- funcs: make(map[string]reflect.Value),
+func (s *Set) init() {
+ if s.tmpl == nil {
+ s.tmpl = make(map[string]*Template)
+ s.funcs = make(map[string]reflect.Value)
}
}
-// Funcs adds to the set's function map the elements of the
-// argument map. It panics if a value in the map is not a function
-// with appropriate return type.
+// Funcs adds the elements of the argument map to the set's function map. It
+// panics if a value in the map is not a function with appropriate return
+// type.
// The return value is the set, so calls can be chained.
func (s *Set) Funcs(funcMap FuncMap) *Set {
+ s.init()
addFuncs(s.funcs, funcMap)
return s
}
-// Add adds the argument templates to the set. It panics if the call
-// attempts to reuse a name defined in the set.
+// Add adds the argument templates to the set. It panics if two templates
+// with the same name are added.
// The return value is the set, so calls can be chained.
func (s *Set) Add(templates ...*Template) *Set {
+ s.init()
for _, t := range templates {
if _, ok := s.tmpl[t.name]; ok {
panic(fmt.Errorf("template: %q already defined in set", t.name))
// It panics if the call attempts to reuse a name defined in the set.
// The return value is the set, so calls can be chained.
func (s *Set) AddSet(set *Set) *Set {
+ s.init()
for _, t := range set.tmpl {
if _, ok := s.tmpl[t.name]; ok {
panic(fmt.Errorf("template: %q already defined in set", t.name))
// template is replaced.
// The return value is the set, so calls can be chained.
func (s *Set) Union(set *Set) *Set {
+ s.init()
for _, t := range set.tmpl {
s.tmpl[t.name] = t
}
return s.tmpl[name]
}
-// Execute looks for the named template in the set and then applies that
-// template to the specified data object, writing the output to wr. Nested
-// template invocations will be resolved from the set.
-func (s *Set) Execute(name string, wr io.Writer, data interface{}) os.Error {
+// Execute applies the named template to the specified data object, writing
+// the output to wr. Nested template invocations will be resolved from the set.
+func (s *Set) Execute(wr io.Writer, name string, data interface{}) os.Error {
tmpl := s.tmpl[name]
if tmpl == nil {
return fmt.Errorf("template: no template %q in set", name)
// to the set. If a template is redefined, the element in the set is
// overwritten with the new definition.
func (s *Set) Parse(text string) (err os.Error) {
+ s.init()
defer s.recover(&err)
lex := lex("set", text)
const context = "define clause"
func TestSetParse(t *testing.T) {
for _, test := range setParseTests {
- set := NewSet()
+ set := new(Set)
err := set.Parse(test.input)
switch {
case err == nil && !test.ok:
{"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},
- {"invoke template by variable", `{{with $t := "x"}}{{template $t}}{{end}}`, "TEXT", 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},
func TestSetExecute(t *testing.T) {
// Declare a set with a couple of templates first.
- set := NewSet()
+ set := new(Set)
err := set.Parse(setText1)
if err != nil {
t.Fatalf("error parsing set: %s", err)