text/template: new, simpler API
The Set type is gone. Instead, templates are automatically associated by
being parsed together; nested definitions implicitly create associations.
Only associated templates can invoke one another.
This approach dramatically reduces the breadth of the construction API.
For now, html/template is deleted from src/pkg/Makefile, so this can
be checked in. Nothing in the tree depends on it. It will be updated next.
R=dsymonds, adg, rsc, r, gri, mikesamuel, nigeltao
CC=golang-dev
https://golang.org/cl/5415060
diff --git a/src/pkg/text/template/parse/parse.go b/src/pkg/text/template/parse/parse.go
index c0491e5..36c5403 100644
--- a/src/pkg/text/template/parse/parse.go
+++ b/src/pkg/text/template/parse/parse.go
@@ -97,6 +97,15 @@
return token
}
+// expectEither consumes the next token and guarantees it has one of the required types.
+func (t *Tree) expectOneOf(expected1, expected2 itemType, context string) item {
+ token := t.next()
+ if token.typ != expected1 && token.typ != expected2 {
+ t.errorf("expected %s or %s in %s; got %s", expected1, expected2, context, token)
+ }
+ return token
+}
+
// unexpected complains about the token and terminates processing.
func (t *Tree) unexpected(token item, context string) {
t.errorf("unexpected %s in %s", token, context)
@@ -162,9 +171,18 @@
t.startParse(funcs, lex(t.Name, s, leftDelim, rightDelim))
t.parse(treeSet)
t.stopParse()
+ t.add(treeSet)
return t, nil
}
+// add adds tree to the treeSet.
+func (t *Tree) add(treeSet map[string]*Tree) {
+ if _, present := treeSet[t.Name]; present {
+ t.errorf("template: multiple definition of template %q", t.Name)
+ }
+ treeSet[t.Name] = t
+}
+
// parse is the top-level parser for a template, essentially the same
// as itemList except it also parses {{define}} actions.
// It runs to EOF.
@@ -174,7 +192,7 @@
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 := New("definition") // name will be updated once we know it.
newT.startParse(t.funcs, t.lex)
newT.parseDefinition(treeSet)
continue
@@ -194,11 +212,8 @@
// 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)
+ name := t.expectOneOf(itemString, itemRawString, context)
var err error
t.Name, err = strconv.Unquote(name.val)
if err != nil {
@@ -211,10 +226,7 @@
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
+ t.add(treeSet)
}
// itemList:
diff --git a/src/pkg/text/template/parse/parse_test.go b/src/pkg/text/template/parse/parse_test.go
index 5c10086..fc93455 100644
--- a/src/pkg/text/template/parse/parse_test.go
+++ b/src/pkg/text/template/parse/parse_test.go
@@ -236,7 +236,7 @@
func TestParse(t *testing.T) {
for _, test := range parseTests {
- tmpl, err := New(test.name).Parse(test.input, "", "", nil, builtins)
+ tmpl, err := New(test.name).Parse(test.input, "", "", make(map[string]*Tree), builtins)
switch {
case err == nil && !test.ok:
t.Errorf("%q: expected error; got none", test.name)