}
for _, test := range execTests {
tmpl := New(test.name).Funcs(funcs)
- _, err := tmpl.ParseInSet(test.input, set)
+ theSet := set
+ if theSet == nil {
+ theSet = new(Set)
+ }
+ _, err := tmpl.ParseInSet(test.input, theSet)
if err != nil {
t.Errorf("%s: parse error: %s", test.name, err)
continue
// Parse parses the template definition string to construct an internal
// representation of the template for execution.
func (t *Template) Parse(s string) (tmpl *Template, err error) {
- t.Tree, err = parse.New(t.name).Parse(s, t.leftDelim, t.rightDelim, t.parseFuncs, builtins)
+ t.Tree, err = parse.New(t.name).Parse(s, t.leftDelim, t.rightDelim, nil, t.parseFuncs, builtins)
if err != nil {
return nil, err
}
// ParseInSet parses the template definition string to construct an internal
// representation of the template for execution. It also adds the template
-// to the set. It is an error if s is already defined in the set.
+// to the set, which must not be nil. It is an error if s is already defined in the set.
// Function bindings are checked against those in the set.
func (t *Template) ParseInSet(s string, set *Set) (tmpl *Template, err error) {
- var setFuncs FuncMap
- if set != nil {
- setFuncs = set.parseFuncs
- }
- t.Tree, err = parse.New(t.name).Parse(s, t.leftDelim, t.rightDelim, t.parseFuncs, setFuncs, builtins)
+ t.Tree, err = parse.New(t.name).Parse(s, t.leftDelim, t.rightDelim, set.trees, t.parseFuncs, set.parseFuncs, builtins)
if err != nil {
return nil, err
}
- if set != nil {
- err = set.add(t)
- }
+ err = set.add(t)
return t, err
}
// Parse parses the template definition string to construct an internal
// representation of the template for execution. If either action delimiter
// string is empty, the default ("{{" or "}}") is used.
-func (t *Tree) Parse(s, leftDelim, rightDelim string, funcs ...map[string]interface{}) (tree *Tree, err error) {
+func (t *Tree) Parse(s, leftDelim, rightDelim string, treeSet map[string]*Tree, funcs ...map[string]interface{}) (tree *Tree, err error) {
defer t.recover(&err)
t.startParse(funcs, lex(t.Name, s, leftDelim, rightDelim))
- t.parse(true)
+ t.parse(treeSet)
t.stopParse()
return t, nil
}
-// parse is the helper for Parse.
-// It triggers an error if we expect EOF but don't reach it.
-func (t *Tree) parse(toEOF bool) (next Node) {
- t.Root, next = t.itemList(true)
- if toEOF && next != nil {
- t.errorf("unexpected %s", next)
+// parse is the top-level parser for a template, essentially the same
+// as itemList except it also parses {{define}} actions.
+// It runs to EOF.
+func (t *Tree) parse(treeSet map[string]*Tree) (next Node) {
+ t.Root = newList()
+ for t.peek().typ != itemEOF {
+ if t.peek().typ == itemLeftDelim {
+ delim := t.next()
+ if t.next().typ == itemDefine {
+ newT := New("new definition") // name will be updated once we know it.
+ newT.startParse(t.funcs, t.lex)
+ newT.parseDefinition(treeSet)
+ continue
+ }
+ t.backup2(delim)
+ }
+ n := t.textOrAction()
+ if n.Type() == nodeEnd {
+ t.errorf("unexpected %s", n)
+ }
+ t.Root.append(n)
}
- return next
+ return nil
+}
+
+// parseDefinition parses a {{define}} ... {{end}} template definition and
+// installs the definition in the treeSet map. The "define" keyword has already
+// been scanned.
+func (t *Tree) parseDefinition(treeSet map[string]*Tree) {
+ if treeSet == nil {
+ t.errorf("no set specified for template definition")
+ }
+ const context = "define clause"
+ name := t.expect(itemString, context)
+ var err error
+ t.Name, err = strconv.Unquote(name.val)
+ if err != nil {
+ t.error(err)
+ }
+ t.expect(itemRightDelim, context)
+ var end Node
+ t.Root, end = t.itemList()
+ if end.Type() != nodeEnd {
+ t.errorf("unexpected %s in %s", end, context)
+ }
+ t.stopParse()
+ if _, present := treeSet[t.Name]; present {
+ t.errorf("template: %q multiply defined", name)
+ }
+ treeSet[t.Name] = t
}
// itemList:
// textOrAction*
-// Terminates at EOF and at {{end}} or {{else}}, which is returned separately.
-// The toEOF flag tells whether we expect to reach EOF.
-func (t *Tree) itemList(toEOF bool) (list *ListNode, next Node) {
+// Terminates at {{end}} or {{else}}, returned separately.
+func (t *Tree) itemList() (list *ListNode, next Node) {
list = newList()
for t.peek().typ != itemEOF {
n := t.textOrAction()
}
list.append(n)
}
- if !toEOF {
- t.unexpected(t.next(), "input")
- }
- return list, nil
+ t.errorf("unexpected EOF")
+ return
}
// textOrAction:
defer t.popVars(len(t.vars))
pipe = t.pipeline(context)
var next Node
- list, next = t.itemList(false)
+ list, next = t.itemList()
switch next.Type() {
case nodeEnd: //done
case nodeElse:
- elseList, next = t.itemList(false)
+ elseList, next = t.itemList()
if next.Type() != nodeEnd {
t.errorf("expected end; found %s", next)
}
func TestParse(t *testing.T) {
for _, test := range parseTests {
- tmpl, err := New(test.name).Parse(test.input, "", "", builtins)
+ tmpl, err := New(test.name).Parse(test.input, "", "", nil, builtins)
switch {
case err == nil && !test.ok:
t.Errorf("%q: expected error; got none", test.name)
package parse
-import (
- "fmt"
- "strconv"
-)
-
// Set returns a slice of Trees created by parsing the template set
// definition in the argument string. If an error is encountered,
// parsing stops and an empty slice is returned with the error.
func Set(text, leftDelim, rightDelim string, funcs ...map[string]interface{}) (tree map[string]*Tree, err error) {
tree = make(map[string]*Tree)
- defer (*Tree)(nil).recover(&err)
- lex := lex("set", text, leftDelim, rightDelim)
- const context = "define clause"
- for {
- t := New("set") // name will be updated once we know it.
- t.startParse(funcs, lex)
- // Expect EOF or "{{ define name }}".
- if t.atEOF() {
- break
- }
- t.expect(itemLeftDelim, context)
- t.expect(itemDefine, context)
- name := t.expect(itemString, context)
- t.Name, err = strconv.Unquote(name.val)
- if err != nil {
- t.error(err)
- }
- t.expect(itemRightDelim, context)
- end := t.parse(false)
- if end == nil {
- t.errorf("unexpected EOF in %s", context)
- }
- if end.Type() != nodeEnd {
- t.errorf("unexpected %s in %s", end, context)
- }
- t.stopParse()
- if _, present := tree[t.Name]; present {
- return nil, fmt.Errorf("template: %q multiply defined", name)
- }
- tree[t.Name] = t
- }
+ // Top-level template name is needed but unused. TODO: clean this up.
+ _, err = New("ROOT").Parse(text, leftDelim, rightDelim, tree, funcs...)
return
}
// A template may be a member of multiple sets.
type Set struct {
tmpl map[string]*Template
+ trees map[string]*parse.Tree // maintained by parse package
leftDelim string
rightDelim string
parseFuncs FuncMap