]> git.ipfire.org Git - thirdparty/gcc.git/blame - libgo/go/text/template/parse/parse.go
libgo: Update to current sources.
[thirdparty/gcc.git] / libgo / go / text / template / parse / parse.go
CommitLineData
adb0401d
ILT
1// Copyright 2011 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
501699af
ILT
5// Package parse builds parse trees for templates as defined by text/template
6// and html/template. Clients should use those packages to construct templates
7// rather than this one, which provides shared internal data structures not
8// intended for general use.
adb0401d
ILT
9package parse
10
11import (
d5363590 12 "bytes"
adb0401d 13 "fmt"
adb0401d
ILT
14 "runtime"
15 "strconv"
4ccad563 16 "strings"
adb0401d
ILT
17 "unicode"
18)
19
7b1c3dd9 20// Tree is the representation of a single parsed template.
adb0401d 21type Tree struct {
4ccad563
ILT
22 Name string // name of the template represented by the tree.
23 ParseName string // name of the top-level template during parsing, for error messages.
24 Root *ListNode // top-level root of the tree.
25 text string // text parsed to create the template (or its parent)
adb0401d
ILT
26 // Parsing only; cleared after parse.
27 funcs []map[string]interface{}
28 lex *lexer
4ccad563 29 token [3]item // three-token lookahead for parser.
adb0401d
ILT
30 peekCount int
31 vars []string // variables defined at the moment.
32}
33
7b1c3dd9
ILT
34// Parse returns a map from template name to parse.Tree, created by parsing the
35// templates described in the argument string. The top-level template will be
36// given the specified name. If an error is encountered, parsing stops and an
37// empty map is returned with the error.
38func Parse(name, text, leftDelim, rightDelim string, funcs ...map[string]interface{}) (treeSet map[string]*Tree, err error) {
39 treeSet = make(map[string]*Tree)
4ccad563
ILT
40 t := New(name)
41 t.text = text
42 _, err = t.Parse(text, leftDelim, rightDelim, treeSet, funcs...)
7b1c3dd9
ILT
43 return
44}
45
adb0401d
ILT
46// next returns the next token.
47func (t *Tree) next() item {
48 if t.peekCount > 0 {
49 t.peekCount--
50 } else {
51 t.token[0] = t.lex.nextItem()
52 }
53 return t.token[t.peekCount]
54}
55
56// backup backs the input stream up one token.
57func (t *Tree) backup() {
58 t.peekCount++
59}
60
4ccad563
ILT
61// backup2 backs the input stream up two tokens.
62// The zeroth token is already there.
adb0401d
ILT
63func (t *Tree) backup2(t1 item) {
64 t.token[1] = t1
65 t.peekCount = 2
66}
67
4ccad563
ILT
68// backup3 backs the input stream up three tokens
69// The zeroth token is already there.
70func (t *Tree) backup3(t2, t1 item) { // Reverse order: we're pushing back.
71 t.token[1] = t1
72 t.token[2] = t2
73 t.peekCount = 3
74}
75
adb0401d
ILT
76// peek returns but does not consume the next token.
77func (t *Tree) peek() item {
78 if t.peekCount > 0 {
79 return t.token[t.peekCount-1]
80 }
81 t.peekCount = 1
82 t.token[0] = t.lex.nextItem()
83 return t.token[0]
84}
85
4ccad563
ILT
86// nextNonSpace returns the next non-space token.
87func (t *Tree) nextNonSpace() (token item) {
88 for {
89 token = t.next()
90 if token.typ != itemSpace {
91 break
92 }
93 }
94 return token
95}
96
97// peekNonSpace returns but does not consume the next non-space token.
98func (t *Tree) peekNonSpace() (token item) {
99 for {
100 token = t.next()
101 if token.typ != itemSpace {
102 break
103 }
104 }
105 t.backup()
106 return token
107}
108
adb0401d
ILT
109// Parsing.
110
7b1c3dd9 111// New allocates a new parse tree with the given name.
adb0401d
ILT
112func New(name string, funcs ...map[string]interface{}) *Tree {
113 return &Tree{
114 Name: name,
115 funcs: funcs,
116 }
117}
118
4ccad563
ILT
119// ErrorContext returns a textual representation of the location of the node in the input text.
120func (t *Tree) ErrorContext(n Node) (location, context string) {
121 pos := int(n.Position())
122 text := t.text[:pos]
123 byteNum := strings.LastIndex(text, "\n")
124 if byteNum == -1 {
125 byteNum = pos // On first line.
126 } else {
127 byteNum++ // After the newline.
128 byteNum = pos - byteNum
129 }
130 lineNum := 1 + strings.Count(text, "\n")
131 context = n.String()
132 if len(context) > 20 {
133 context = fmt.Sprintf("%.20s...", context)
134 }
135 return fmt.Sprintf("%s:%d:%d", t.ParseName, lineNum, byteNum), context
136}
137
adb0401d
ILT
138// errorf formats the error and terminates processing.
139func (t *Tree) errorf(format string, args ...interface{}) {
140 t.Root = nil
4ccad563 141 format = fmt.Sprintf("template: %s:%d: %s", t.ParseName, t.lex.lineNumber(), format)
adb0401d
ILT
142 panic(fmt.Errorf(format, args...))
143}
144
145// error terminates processing.
2fd401c8 146func (t *Tree) error(err error) {
adb0401d
ILT
147 t.errorf("%s", err)
148}
149
150// expect consumes the next token and guarantees it has the required type.
151func (t *Tree) expect(expected itemType, context string) item {
4ccad563 152 token := t.nextNonSpace()
adb0401d
ILT
153 if token.typ != expected {
154 t.errorf("expected %s in %s; got %s", expected, context, token)
155 }
156 return token
157}
158
4ccad563 159// expectOneOf consumes the next token and guarantees it has one of the required types.
7b1c3dd9 160func (t *Tree) expectOneOf(expected1, expected2 itemType, context string) item {
4ccad563 161 token := t.nextNonSpace()
7b1c3dd9
ILT
162 if token.typ != expected1 && token.typ != expected2 {
163 t.errorf("expected %s or %s in %s; got %s", expected1, expected2, context, token)
164 }
165 return token
166}
167
adb0401d
ILT
168// unexpected complains about the token and terminates processing.
169func (t *Tree) unexpected(token item, context string) {
170 t.errorf("unexpected %s in %s", token, context)
171}
172
173// recover is the handler that turns panics into returns from the top level of Parse.
2fd401c8 174func (t *Tree) recover(errp *error) {
adb0401d
ILT
175 e := recover()
176 if e != nil {
177 if _, ok := e.(runtime.Error); ok {
178 panic(e)
179 }
180 if t != nil {
181 t.stopParse()
182 }
2fd401c8 183 *errp = e.(error)
adb0401d
ILT
184 }
185 return
186}
187
7b1c3dd9 188// startParse initializes the parser, using the lexer.
adb0401d
ILT
189func (t *Tree) startParse(funcs []map[string]interface{}, lex *lexer) {
190 t.Root = nil
191 t.lex = lex
192 t.vars = []string{"$"}
193 t.funcs = funcs
194}
195
196// stopParse terminates parsing.
197func (t *Tree) stopParse() {
198 t.lex = nil
199 t.vars = nil
200 t.funcs = nil
201}
202
203// atEOF returns true if, possibly after spaces, we're at EOF.
204func (t *Tree) atEOF() bool {
205 for {
206 token := t.peek()
207 switch token.typ {
208 case itemEOF:
209 return true
210 case itemText:
211 for _, r := range token.val {
212 if !unicode.IsSpace(r) {
213 return false
214 }
215 }
216 t.next() // skip spaces.
217 continue
218 }
219 break
220 }
221 return false
222}
223
7b1c3dd9
ILT
224// Parse parses the template definition string to construct a representation of
225// the template for execution. If either action delimiter string is empty, the
226// default ("{{" or "}}") is used. Embedded template definitions are added to
227// the treeSet map.
4ccad563 228func (t *Tree) Parse(text, leftDelim, rightDelim string, treeSet map[string]*Tree, funcs ...map[string]interface{}) (tree *Tree, err error) {
adb0401d 229 defer t.recover(&err)
4ccad563
ILT
230 t.ParseName = t.Name
231 t.startParse(funcs, lex(t.Name, text, leftDelim, rightDelim))
232 t.text = text
ab61e9c4 233 t.parse(treeSet)
7b1c3dd9 234 t.add(treeSet)
adb0401d
ILT
235 t.stopParse()
236 return t, nil
237}
238
7b1c3dd9
ILT
239// add adds tree to the treeSet.
240func (t *Tree) add(treeSet map[string]*Tree) {
d5363590
ILT
241 tree := treeSet[t.Name]
242 if tree == nil || IsEmptyTree(tree.Root) {
243 treeSet[t.Name] = t
244 return
245 }
246 if !IsEmptyTree(t.Root) {
7b1c3dd9
ILT
247 t.errorf("template: multiple definition of template %q", t.Name)
248 }
d5363590
ILT
249}
250
251// IsEmptyTree reports whether this tree (node) is empty of everything but space.
252func IsEmptyTree(n Node) bool {
253 switch n := n.(type) {
593f74bb
ILT
254 case nil:
255 return true
d5363590
ILT
256 case *ActionNode:
257 case *IfNode:
258 case *ListNode:
259 for _, node := range n.Nodes {
260 if !IsEmptyTree(node) {
261 return false
262 }
263 }
264 return true
265 case *RangeNode:
266 case *TemplateNode:
267 case *TextNode:
268 return len(bytes.TrimSpace(n.Text)) == 0
269 case *WithNode:
270 default:
271 panic("unknown node: " + n.String())
272 }
273 return false
7b1c3dd9
ILT
274}
275
ab61e9c4
ILT
276// parse is the top-level parser for a template, essentially the same
277// as itemList except it also parses {{define}} actions.
278// It runs to EOF.
279func (t *Tree) parse(treeSet map[string]*Tree) (next Node) {
4ccad563 280 t.Root = newList(t.peek().pos)
ab61e9c4
ILT
281 for t.peek().typ != itemEOF {
282 if t.peek().typ == itemLeftDelim {
283 delim := t.next()
4ccad563 284 if t.nextNonSpace().typ == itemDefine {
7b1c3dd9 285 newT := New("definition") // name will be updated once we know it.
4ccad563
ILT
286 newT.text = t.text
287 newT.ParseName = t.ParseName
ab61e9c4
ILT
288 newT.startParse(t.funcs, t.lex)
289 newT.parseDefinition(treeSet)
290 continue
291 }
292 t.backup2(delim)
293 }
294 n := t.textOrAction()
295 if n.Type() == nodeEnd {
296 t.errorf("unexpected %s", n)
297 }
298 t.Root.append(n)
adb0401d 299 }
ab61e9c4
ILT
300 return nil
301}
302
303// parseDefinition parses a {{define}} ... {{end}} template definition and
304// installs the definition in the treeSet map. The "define" keyword has already
305// been scanned.
306func (t *Tree) parseDefinition(treeSet map[string]*Tree) {
ab61e9c4 307 const context = "define clause"
7b1c3dd9 308 name := t.expectOneOf(itemString, itemRawString, context)
ab61e9c4
ILT
309 var err error
310 t.Name, err = strconv.Unquote(name.val)
311 if err != nil {
312 t.error(err)
313 }
314 t.expect(itemRightDelim, context)
315 var end Node
316 t.Root, end = t.itemList()
317 if end.Type() != nodeEnd {
318 t.errorf("unexpected %s in %s", end, context)
319 }
7b1c3dd9 320 t.add(treeSet)
4ccad563 321 t.stopParse()
adb0401d
ILT
322}
323
324// itemList:
325// textOrAction*
ab61e9c4
ILT
326// Terminates at {{end}} or {{else}}, returned separately.
327func (t *Tree) itemList() (list *ListNode, next Node) {
4ccad563
ILT
328 list = newList(t.peekNonSpace().pos)
329 for t.peekNonSpace().typ != itemEOF {
adb0401d
ILT
330 n := t.textOrAction()
331 switch n.Type() {
332 case nodeEnd, nodeElse:
333 return list, n
334 }
335 list.append(n)
336 }
ab61e9c4
ILT
337 t.errorf("unexpected EOF")
338 return
adb0401d
ILT
339}
340
341// textOrAction:
342// text | action
343func (t *Tree) textOrAction() Node {
4ccad563 344 switch token := t.nextNonSpace(); token.typ {
adb0401d 345 case itemText:
4ccad563 346 return newText(token.pos, token.val)
adb0401d
ILT
347 case itemLeftDelim:
348 return t.action()
349 default:
350 t.unexpected(token, "input")
351 }
352 return nil
353}
354
355// Action:
356// control
357// command ("|" command)*
358// Left delim is past. Now get actions.
359// First word could be a keyword such as range.
360func (t *Tree) action() (n Node) {
4ccad563 361 switch token := t.nextNonSpace(); token.typ {
adb0401d
ILT
362 case itemElse:
363 return t.elseControl()
364 case itemEnd:
365 return t.endControl()
366 case itemIf:
367 return t.ifControl()
368 case itemRange:
369 return t.rangeControl()
370 case itemTemplate:
371 return t.templateControl()
372 case itemWith:
373 return t.withControl()
374 }
375 t.backup()
376 // Do not pop variables; they persist until "end".
4ccad563 377 return newAction(t.peek().pos, t.lex.lineNumber(), t.pipeline("command"))
adb0401d
ILT
378}
379
380// Pipeline:
4ccad563 381// declarations? command ('|' command)*
adb0401d
ILT
382func (t *Tree) pipeline(context string) (pipe *PipeNode) {
383 var decl []*VariableNode
4ccad563 384 pos := t.peekNonSpace().pos
adb0401d
ILT
385 // Are there declarations?
386 for {
4ccad563 387 if v := t.peekNonSpace(); v.typ == itemVariable {
adb0401d 388 t.next()
4ccad563
ILT
389 // Since space is a token, we need 3-token look-ahead here in the worst case:
390 // in "$x foo" we need to read "foo" (as opposed to ":=") to know that $x is an
391 // argument variable rather than a declaration. So remember the token
392 // adjacent to the variable so we can push it back if necessary.
393 tokenAfterVariable := t.peek()
394 if next := t.peekNonSpace(); next.typ == itemColonEquals || (next.typ == itemChar && next.val == ",") {
395 t.nextNonSpace()
396 variable := newVariable(v.pos, v.val)
adb0401d
ILT
397 decl = append(decl, variable)
398 t.vars = append(t.vars, v.val)
399 if next.typ == itemChar && next.val == "," {
400 if context == "range" && len(decl) < 2 {
401 continue
402 }
403 t.errorf("too many declarations in %s", context)
404 }
4ccad563
ILT
405 } else if tokenAfterVariable.typ == itemSpace {
406 t.backup3(v, tokenAfterVariable)
adb0401d
ILT
407 } else {
408 t.backup2(v)
409 }
410 }
411 break
412 }
4ccad563 413 pipe = newPipeline(pos, t.lex.lineNumber(), decl)
adb0401d 414 for {
4ccad563
ILT
415 switch token := t.nextNonSpace(); token.typ {
416 case itemRightDelim, itemRightParen:
adb0401d
ILT
417 if len(pipe.Cmds) == 0 {
418 t.errorf("missing value for %s", context)
419 }
4ccad563
ILT
420 if token.typ == itemRightParen {
421 t.backup()
422 }
adb0401d
ILT
423 return
424 case itemBool, itemCharConstant, itemComplex, itemDot, itemField, itemIdentifier,
4ccad563 425 itemNumber, itemNil, itemRawString, itemString, itemVariable, itemLeftParen:
adb0401d
ILT
426 t.backup()
427 pipe.append(t.command())
428 default:
429 t.unexpected(token, context)
430 }
431 }
432 return
433}
434
4ccad563 435func (t *Tree) parseControl(context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) {
adb0401d 436 defer t.popVars(len(t.vars))
4ccad563 437 line = t.lex.lineNumber()
adb0401d
ILT
438 pipe = t.pipeline(context)
439 var next Node
ab61e9c4 440 list, next = t.itemList()
adb0401d
ILT
441 switch next.Type() {
442 case nodeEnd: //done
443 case nodeElse:
ab61e9c4 444 elseList, next = t.itemList()
adb0401d
ILT
445 if next.Type() != nodeEnd {
446 t.errorf("expected end; found %s", next)
447 }
448 elseList = elseList
449 }
4ccad563 450 return pipe.Position(), line, pipe, list, elseList
adb0401d
ILT
451}
452
453// If:
454// {{if pipeline}} itemList {{end}}
455// {{if pipeline}} itemList {{else}} itemList {{end}}
456// If keyword is past.
457func (t *Tree) ifControl() Node {
458 return newIf(t.parseControl("if"))
459}
460
461// Range:
462// {{range pipeline}} itemList {{end}}
463// {{range pipeline}} itemList {{else}} itemList {{end}}
464// Range keyword is past.
465func (t *Tree) rangeControl() Node {
466 return newRange(t.parseControl("range"))
467}
468
469// With:
470// {{with pipeline}} itemList {{end}}
471// {{with pipeline}} itemList {{else}} itemList {{end}}
472// If keyword is past.
473func (t *Tree) withControl() Node {
474 return newWith(t.parseControl("with"))
475}
476
477// End:
478// {{end}}
479// End keyword is past.
480func (t *Tree) endControl() Node {
4ccad563 481 return newEnd(t.expect(itemRightDelim, "end").pos)
adb0401d
ILT
482}
483
484// Else:
485// {{else}}
486// Else keyword is past.
487func (t *Tree) elseControl() Node {
4ccad563 488 return newElse(t.expect(itemRightDelim, "else").pos, t.lex.lineNumber())
adb0401d
ILT
489}
490
491// Template:
492// {{template stringValue pipeline}}
493// Template keyword is past. The name must be something that can evaluate
494// to a string.
495func (t *Tree) templateControl() Node {
496 var name string
4ccad563
ILT
497 token := t.nextNonSpace()
498 switch token.typ {
adb0401d
ILT
499 case itemString, itemRawString:
500 s, err := strconv.Unquote(token.val)
501 if err != nil {
502 t.error(err)
503 }
504 name = s
505 default:
506 t.unexpected(token, "template invocation")
507 }
508 var pipe *PipeNode
4ccad563 509 if t.nextNonSpace().typ != itemRightDelim {
adb0401d
ILT
510 t.backup()
511 // Do not pop variables; they persist until "end".
512 pipe = t.pipeline("template")
513 }
4ccad563 514 return newTemplate(token.pos, t.lex.lineNumber(), name, pipe)
adb0401d
ILT
515}
516
517// command:
4ccad563 518// operand (space operand)*
adb0401d
ILT
519// space-separated arguments up to a pipeline character or right delimiter.
520// we consume the pipe character but leave the right delim to terminate the action.
521func (t *Tree) command() *CommandNode {
4ccad563 522 cmd := newCommand(t.peekNonSpace().pos)
adb0401d 523 for {
4ccad563
ILT
524 t.peekNonSpace() // skip leading spaces.
525 operand := t.operand()
526 if operand != nil {
527 cmd.append(operand)
528 }
adb0401d 529 switch token := t.next(); token.typ {
4ccad563
ILT
530 case itemSpace:
531 continue
adb0401d
ILT
532 case itemError:
533 t.errorf("%s", token.val)
4ccad563
ILT
534 case itemRightDelim, itemRightParen:
535 t.backup()
536 case itemPipe:
adb0401d 537 default:
4ccad563 538 t.errorf("unexpected %s in operand; missing space?", token)
adb0401d 539 }
4ccad563 540 break
adb0401d
ILT
541 }
542 if len(cmd.Args) == 0 {
543 t.errorf("empty command")
544 }
545 return cmd
546}
547
4ccad563
ILT
548// operand:
549// term .Field*
550// An operand is a space-separated component of a command,
551// a term possibly followed by field accesses.
552// A nil return means the next item is not an operand.
553func (t *Tree) operand() Node {
554 node := t.term()
555 if node == nil {
556 return nil
557 }
558 if t.peek().typ == itemField {
559 chain := newChain(t.peek().pos, node)
560 for t.peek().typ == itemField {
561 chain.Add(t.next().val)
562 }
563 // Compatibility with original API: If the term is of type NodeField
564 // or NodeVariable, just put more fields on the original.
565 // Otherwise, keep the Chain node.
566 // TODO: Switch to Chains always when we can.
567 switch node.Type() {
568 case NodeField:
569 node = newField(chain.Position(), chain.String())
570 case NodeVariable:
571 node = newVariable(chain.Position(), chain.String())
572 default:
573 node = chain
574 }
575 }
576 return node
577}
578
579// term:
580// literal (number, string, nil, boolean)
581// function (identifier)
582// .
583// .Field
584// $
585// '(' pipeline ')'
586// A term is a simple "expression".
587// A nil return means the next item is not a term.
588func (t *Tree) term() Node {
589 switch token := t.nextNonSpace(); token.typ {
590 case itemError:
591 t.errorf("%s", token.val)
592 case itemIdentifier:
593 if !t.hasFunction(token.val) {
594 t.errorf("function %q not defined", token.val)
595 }
596 return NewIdentifier(token.val).SetPos(token.pos)
597 case itemDot:
598 return newDot(token.pos)
599 case itemNil:
600 return newNil(token.pos)
601 case itemVariable:
602 return t.useVar(token.pos, token.val)
603 case itemField:
604 return newField(token.pos, token.val)
605 case itemBool:
606 return newBool(token.pos, token.val == "true")
607 case itemCharConstant, itemComplex, itemNumber:
608 number, err := newNumber(token.pos, token.val, token.typ)
609 if err != nil {
610 t.error(err)
611 }
612 return number
613 case itemLeftParen:
614 pipe := t.pipeline("parenthesized pipeline")
615 if token := t.next(); token.typ != itemRightParen {
616 t.errorf("unclosed right paren: unexpected %s", token)
617 }
618 return pipe
619 case itemString, itemRawString:
620 s, err := strconv.Unquote(token.val)
621 if err != nil {
622 t.error(err)
623 }
624 return newString(token.pos, token.val, s)
625 }
626 t.backup()
627 return nil
628}
629
adb0401d
ILT
630// hasFunction reports if a function name exists in the Tree's maps.
631func (t *Tree) hasFunction(name string) bool {
632 for _, funcMap := range t.funcs {
633 if funcMap == nil {
634 continue
635 }
636 if funcMap[name] != nil {
637 return true
638 }
639 }
640 return false
641}
642
643// popVars trims the variable list to the specified length
644func (t *Tree) popVars(n int) {
645 t.vars = t.vars[:n]
646}
647
648// useVar returns a node for a variable reference. It errors if the
649// variable is not defined.
4ccad563
ILT
650func (t *Tree) useVar(pos Pos, name string) Node {
651 v := newVariable(pos, name)
adb0401d
ILT
652 for _, varName := range t.vars {
653 if varName == v.Ident[0] {
654 return v
655 }
656 }
657 t.errorf("undefined variable %q", v.Ident[0])
658 return nil
659}