aboutsummaryrefslogtreecommitdiff
path: root/src/text/template/parse/parse.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/text/template/parse/parse.go')
-rw-r--r--src/text/template/parse/parse.go59
1 files changed, 33 insertions, 26 deletions
diff --git a/src/text/template/parse/parse.go b/src/text/template/parse/parse.go
index 496d8bfa1d..5e6e512eb4 100644
--- a/src/text/template/parse/parse.go
+++ b/src/text/template/parse/parse.go
@@ -24,13 +24,14 @@ type Tree struct {
Mode Mode // parsing mode.
text string // text parsed to create the template (or its parent)
// Parsing only; cleared after parse.
- funcs []map[string]interface{}
- lex *lexer
- token [3]item // three-token lookahead for parser.
- peekCount int
- vars []string // variables defined at the moment.
- treeSet map[string]*Tree
- mode Mode
+ funcs []map[string]interface{}
+ lex *lexer
+ token [3]item // three-token lookahead for parser.
+ peekCount int
+ vars []string // variables defined at the moment.
+ treeSet map[string]*Tree
+ actionLine int // line of left delim starting action
+ mode Mode
}
// A mode value is a set of flags (or 0). Modes control parser behavior.
@@ -187,6 +188,16 @@ func (t *Tree) expectOneOf(expected1, expected2 itemType, context string) item {
// unexpected complains about the token and terminates processing.
func (t *Tree) unexpected(token item, context string) {
+ if token.typ == itemError {
+ extra := ""
+ if t.actionLine != 0 && t.actionLine != token.line {
+ extra = fmt.Sprintf(" in action started at %s:%d", t.ParseName, t.actionLine)
+ if strings.HasSuffix(token.val, " action") {
+ extra = extra[len(" in action"):] // avoid "action in action"
+ }
+ }
+ t.errorf("%s%s", token, extra)
+ }
t.errorf("unexpected %s in %s", token, context)
}
@@ -350,6 +361,8 @@ func (t *Tree) textOrAction() Node {
case itemText:
return t.newText(token.pos, token.val)
case itemLeftDelim:
+ t.actionLine = token.line
+ defer t.clearActionLine()
return t.action()
case itemComment:
return t.newComment(token.pos, token.val)
@@ -359,6 +372,10 @@ func (t *Tree) textOrAction() Node {
return nil
}
+func (t *Tree) clearActionLine() {
+ t.actionLine = 0
+}
+
// Action:
// control
// command ("|" command)*
@@ -384,12 +401,12 @@ func (t *Tree) action() (n Node) {
t.backup()
token := t.peek()
// Do not pop variables; they persist until "end".
- return t.newAction(token.pos, token.line, t.pipeline("command"))
+ return t.newAction(token.pos, token.line, t.pipeline("command", itemRightDelim))
}
// Pipeline:
// declarations? command ('|' command)*
-func (t *Tree) pipeline(context string) (pipe *PipeNode) {
+func (t *Tree) pipeline(context string, end itemType) (pipe *PipeNode) {
token := t.peekNonSpace()
pipe = t.newPipeline(token.pos, token.line, nil)
// Are there declarations or assignments?
@@ -430,12 +447,9 @@ decls:
}
for {
switch token := t.nextNonSpace(); token.typ {
- case itemRightDelim, itemRightParen:
+ case end:
// At this point, the pipeline is complete
t.checkPipeline(pipe, context)
- if token.typ == itemRightParen {
- t.backup()
- }
return
case itemBool, itemCharConstant, itemComplex, itemDot, itemField, itemIdentifier,
itemNumber, itemNil, itemRawString, itemString, itemVariable, itemLeftParen:
@@ -464,7 +478,7 @@ func (t *Tree) checkPipeline(pipe *PipeNode, context string) {
func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) {
defer t.popVars(len(t.vars))
- pipe = t.pipeline(context)
+ pipe = t.pipeline(context, itemRightDelim)
var next Node
list, next = t.itemList()
switch next.Type() {
@@ -550,7 +564,7 @@ func (t *Tree) blockControl() Node {
token := t.nextNonSpace()
name := t.parseTemplateName(token, context)
- pipe := t.pipeline(context)
+ pipe := t.pipeline(context, itemRightDelim)
block := New(name) // name will be updated once we know it.
block.text = t.text
@@ -580,7 +594,7 @@ func (t *Tree) templateControl() Node {
if t.nextNonSpace().typ != itemRightDelim {
t.backup()
// Do not pop variables; they persist until "end".
- pipe = t.pipeline(context)
+ pipe = t.pipeline(context, itemRightDelim)
}
return t.newTemplate(token.pos, token.line, name, pipe)
}
@@ -614,13 +628,12 @@ func (t *Tree) command() *CommandNode {
switch token := t.next(); token.typ {
case itemSpace:
continue
- case itemError:
- t.errorf("%s", token.val)
case itemRightDelim, itemRightParen:
t.backup()
case itemPipe:
+ // nothing here; break loop below
default:
- t.errorf("unexpected %s in operand", token)
+ t.unexpected(token, "operand")
}
break
}
@@ -675,8 +688,6 @@ func (t *Tree) operand() Node {
// A nil return means the next item is not a term.
func (t *Tree) term() Node {
switch token := t.nextNonSpace(); token.typ {
- case itemError:
- t.errorf("%s", token.val)
case itemIdentifier:
if !t.hasFunction(token.val) {
t.errorf("function %q not defined", token.val)
@@ -699,11 +710,7 @@ func (t *Tree) term() Node {
}
return number
case itemLeftParen:
- pipe := t.pipeline("parenthesized pipeline")
- if token := t.next(); token.typ != itemRightParen {
- t.errorf("unclosed right paren: unexpected %s", token)
- }
- return pipe
+ return t.pipeline("parenthesized pipeline", itemRightParen)
case itemString, itemRawString:
s, err := strconv.Unquote(token.val)
if err != nil {