]>
Commit | Line | Data |
---|---|---|
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 |
9 | package parse |
10 | ||
11 | import ( | |
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 | 21 | type 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. | |
38 | func 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. |
47 | func (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. | |
57 | func (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 |
63 | func (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. | |
70 | func (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. |
77 | func (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. |
87 | func (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. | |
98 | func (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 |
112 | func 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. |
120 | func (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. |
139 | func (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 | 146 | func (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. | |
151 | func (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 | 160 | func (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. |
169 | func (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 | 174 | func (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 |
189 | func (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. | |
197 | func (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. | |
204 | func (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 | 228 | func (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. |
240 | func (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. | |
252 | func 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. | |
279 | func (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. | |
306 | func (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. |
327 | func (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 | |
343 | func (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. | |
360 | func (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 |
382 | func (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 | 435 | func (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. | |
457 | func (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. | |
465 | func (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. | |
473 | func (t *Tree) withControl() Node { | |
474 | return newWith(t.parseControl("with")) | |
475 | } | |
476 | ||
477 | // End: | |
478 | // {{end}} | |
479 | // End keyword is past. | |
480 | func (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. | |
487 | func (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. | |
495 | func (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. | |
521 | func (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. | |
553 | func (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. | |
588 | func (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. |
631 | func (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 | |
644 | func (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 |
650 | func (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 | } |