1
2
3
4
5
6
7
8
9 package parse
10
11 import (
12 "bytes"
13 "fmt"
14 "runtime"
15 "strconv"
16 "strings"
17 )
18
19
20 type Tree struct {
21 Name string
22 ParseName string
23 Root *ListNode
24 Mode Mode
25 text string
26
27 funcs []map[string]any
28 lex *lexer
29 token [3]item
30 peekCount int
31 vars []string
32 treeSet map[string]*Tree
33 actionLine int
34 rangeDepth int
35 }
36
37
38 type Mode uint
39
40 const (
41 ParseComments Mode = 1 << iota
42 SkipFuncCheck
43 )
44
45
46 func (t *Tree) Copy() *Tree {
47 if t == nil {
48 return nil
49 }
50 return &Tree{
51 Name: t.Name,
52 ParseName: t.ParseName,
53 Root: t.Root.CopyList(),
54 text: t.text,
55 }
56 }
57
58
59
60
61
62 func Parse(name, text, leftDelim, rightDelim string, funcs ...map[string]any) (map[string]*Tree, error) {
63 treeSet := make(map[string]*Tree)
64 t := New(name)
65 t.text = text
66 _, err := t.Parse(text, leftDelim, rightDelim, treeSet, funcs...)
67 return treeSet, err
68 }
69
70
71 func (t *Tree) next() item {
72 if t.peekCount > 0 {
73 t.peekCount--
74 } else {
75 t.token[0] = t.lex.nextItem()
76 }
77 return t.token[t.peekCount]
78 }
79
80
81 func (t *Tree) backup() {
82 t.peekCount++
83 }
84
85
86
87 func (t *Tree) backup2(t1 item) {
88 t.token[1] = t1
89 t.peekCount = 2
90 }
91
92
93
94 func (t *Tree) backup3(t2, t1 item) {
95 t.token[1] = t1
96 t.token[2] = t2
97 t.peekCount = 3
98 }
99
100
101 func (t *Tree) peek() item {
102 if t.peekCount > 0 {
103 return t.token[t.peekCount-1]
104 }
105 t.peekCount = 1
106 t.token[0] = t.lex.nextItem()
107 return t.token[0]
108 }
109
110
111 func (t *Tree) nextNonSpace() (token item) {
112 for {
113 token = t.next()
114 if token.typ != itemSpace {
115 break
116 }
117 }
118 return token
119 }
120
121
122 func (t *Tree) peekNonSpace() item {
123 token := t.nextNonSpace()
124 t.backup()
125 return token
126 }
127
128
129
130
131 func New(name string, funcs ...map[string]any) *Tree {
132 return &Tree{
133 Name: name,
134 funcs: funcs,
135 }
136 }
137
138
139
140
141 func (t *Tree) ErrorContext(n Node) (location, context string) {
142 pos := int(n.Position())
143 tree := n.tree()
144 if tree == nil {
145 tree = t
146 }
147 text := tree.text[:pos]
148 byteNum := strings.LastIndex(text, "\n")
149 if byteNum == -1 {
150 byteNum = pos
151 } else {
152 byteNum++
153 byteNum = pos - byteNum
154 }
155 lineNum := 1 + strings.Count(text, "\n")
156 context = n.String()
157 return fmt.Sprintf("%s:%d:%d", tree.ParseName, lineNum, byteNum), context
158 }
159
160
161 func (t *Tree) errorf(format string, args ...any) {
162 t.Root = nil
163 format = fmt.Sprintf("template: %s:%d: %s", t.ParseName, t.token[0].line, format)
164 panic(fmt.Errorf(format, args...))
165 }
166
167
168 func (t *Tree) error(err error) {
169 t.errorf("%s", err)
170 }
171
172
173 func (t *Tree) expect(expected itemType, context string) item {
174 token := t.nextNonSpace()
175 if token.typ != expected {
176 t.unexpected(token, context)
177 }
178 return token
179 }
180
181
182 func (t *Tree) expectOneOf(expected1, expected2 itemType, context string) item {
183 token := t.nextNonSpace()
184 if token.typ != expected1 && token.typ != expected2 {
185 t.unexpected(token, context)
186 }
187 return token
188 }
189
190
191 func (t *Tree) unexpected(token item, context string) {
192 if token.typ == itemError {
193 extra := ""
194 if t.actionLine != 0 && t.actionLine != token.line {
195 extra = fmt.Sprintf(" in action started at %s:%d", t.ParseName, t.actionLine)
196 if strings.HasSuffix(token.val, " action") {
197 extra = extra[len(" in action"):]
198 }
199 }
200 t.errorf("%s%s", token, extra)
201 }
202 t.errorf("unexpected %s in %s", token, context)
203 }
204
205
206 func (t *Tree) recover(errp *error) {
207 e := recover()
208 if e != nil {
209 if _, ok := e.(runtime.Error); ok {
210 panic(e)
211 }
212 if t != nil {
213 t.stopParse()
214 }
215 *errp = e.(error)
216 }
217 }
218
219
220 func (t *Tree) startParse(funcs []map[string]any, lex *lexer, treeSet map[string]*Tree) {
221 t.Root = nil
222 t.lex = lex
223 t.vars = []string{"$"}
224 t.funcs = funcs
225 t.treeSet = treeSet
226 lex.options = lexOptions{
227 emitComment: t.Mode&ParseComments != 0,
228 breakOK: !t.hasFunction("break"),
229 continueOK: !t.hasFunction("continue"),
230 }
231 }
232
233
234 func (t *Tree) stopParse() {
235 t.lex = nil
236 t.vars = nil
237 t.funcs = nil
238 t.treeSet = nil
239 }
240
241
242
243
244
245 func (t *Tree) Parse(text, leftDelim, rightDelim string, treeSet map[string]*Tree, funcs ...map[string]any) (tree *Tree, err error) {
246 defer t.recover(&err)
247 t.ParseName = t.Name
248 lexer := lex(t.Name, text, leftDelim, rightDelim)
249 t.startParse(funcs, lexer, treeSet)
250 t.text = text
251 t.parse()
252 t.add()
253 t.stopParse()
254 return t, nil
255 }
256
257
258 func (t *Tree) add() {
259 tree := t.treeSet[t.Name]
260 if tree == nil || IsEmptyTree(tree.Root) {
261 t.treeSet[t.Name] = t
262 return
263 }
264 if !IsEmptyTree(t.Root) {
265 t.errorf("template: multiple definition of template %q", t.Name)
266 }
267 }
268
269
270 func IsEmptyTree(n Node) bool {
271 switch n := n.(type) {
272 case nil:
273 return true
274 case *ActionNode:
275 case *CommentNode:
276 return true
277 case *IfNode:
278 case *ListNode:
279 for _, node := range n.Nodes {
280 if !IsEmptyTree(node) {
281 return false
282 }
283 }
284 return true
285 case *RangeNode:
286 case *TemplateNode:
287 case *TextNode:
288 return len(bytes.TrimSpace(n.Text)) == 0
289 case *WithNode:
290 default:
291 panic("unknown node: " + n.String())
292 }
293 return false
294 }
295
296
297
298
299 func (t *Tree) parse() {
300 t.Root = t.newList(t.peek().pos)
301 for t.peek().typ != itemEOF {
302 if t.peek().typ == itemLeftDelim {
303 delim := t.next()
304 if t.nextNonSpace().typ == itemDefine {
305 newT := New("definition")
306 newT.text = t.text
307 newT.Mode = t.Mode
308 newT.ParseName = t.ParseName
309 newT.startParse(t.funcs, t.lex, t.treeSet)
310 newT.parseDefinition()
311 continue
312 }
313 t.backup2(delim)
314 }
315 switch n := t.textOrAction(); n.Type() {
316 case nodeEnd, nodeElse:
317 t.errorf("unexpected %s", n)
318 default:
319 t.Root.append(n)
320 }
321 }
322 }
323
324
325
326
327 func (t *Tree) parseDefinition() {
328 const context = "define clause"
329 name := t.expectOneOf(itemString, itemRawString, context)
330 var err error
331 t.Name, err = strconv.Unquote(name.val)
332 if err != nil {
333 t.error(err)
334 }
335 t.expect(itemRightDelim, context)
336 var end Node
337 t.Root, end = t.itemList()
338 if end.Type() != nodeEnd {
339 t.errorf("unexpected %s in %s", end, context)
340 }
341 t.add()
342 t.stopParse()
343 }
344
345
346
347
348
349
350 func (t *Tree) itemList() (list *ListNode, next Node) {
351 list = t.newList(t.peekNonSpace().pos)
352 for t.peekNonSpace().typ != itemEOF {
353 n := t.textOrAction()
354 switch n.Type() {
355 case nodeEnd, nodeElse:
356 return list, n
357 }
358 list.append(n)
359 }
360 t.errorf("unexpected EOF")
361 return
362 }
363
364
365
366
367 func (t *Tree) textOrAction() Node {
368 switch token := t.nextNonSpace(); token.typ {
369 case itemText:
370 return t.newText(token.pos, token.val)
371 case itemLeftDelim:
372 t.actionLine = token.line
373 defer t.clearActionLine()
374 return t.action()
375 case itemComment:
376 return t.newComment(token.pos, token.val)
377 default:
378 t.unexpected(token, "input")
379 }
380 return nil
381 }
382
383 func (t *Tree) clearActionLine() {
384 t.actionLine = 0
385 }
386
387
388
389
390
391
392
393
394 func (t *Tree) action() (n Node) {
395 switch token := t.nextNonSpace(); token.typ {
396 case itemBlock:
397 return t.blockControl()
398 case itemBreak:
399 return t.breakControl(token.pos, token.line)
400 case itemContinue:
401 return t.continueControl(token.pos, token.line)
402 case itemElse:
403 return t.elseControl()
404 case itemEnd:
405 return t.endControl()
406 case itemIf:
407 return t.ifControl()
408 case itemRange:
409 return t.rangeControl()
410 case itemTemplate:
411 return t.templateControl()
412 case itemWith:
413 return t.withControl()
414 }
415 t.backup()
416 token := t.peek()
417
418 return t.newAction(token.pos, token.line, t.pipeline("command", itemRightDelim))
419 }
420
421
422
423
424
425
426 func (t *Tree) breakControl(pos Pos, line int) Node {
427 if token := t.nextNonSpace(); token.typ != itemRightDelim {
428 t.unexpected(token, "{{break}}")
429 }
430 if t.rangeDepth == 0 {
431 t.errorf("{{break}} outside {{range}}")
432 }
433 return t.newBreak(pos, line)
434 }
435
436
437
438
439
440
441 func (t *Tree) continueControl(pos Pos, line int) Node {
442 if token := t.nextNonSpace(); token.typ != itemRightDelim {
443 t.unexpected(token, "{{continue}}")
444 }
445 if t.rangeDepth == 0 {
446 t.errorf("{{continue}} outside {{range}}")
447 }
448 return t.newContinue(pos, line)
449 }
450
451
452
453
454 func (t *Tree) pipeline(context string, end itemType) (pipe *PipeNode) {
455 token := t.peekNonSpace()
456 pipe = t.newPipeline(token.pos, token.line, nil)
457
458 decls:
459 if v := t.peekNonSpace(); v.typ == itemVariable {
460 t.next()
461
462
463
464
465 tokenAfterVariable := t.peek()
466 next := t.peekNonSpace()
467 switch {
468 case next.typ == itemAssign, next.typ == itemDeclare:
469 pipe.IsAssign = next.typ == itemAssign
470 t.nextNonSpace()
471 pipe.Decl = append(pipe.Decl, t.newVariable(v.pos, v.val))
472 t.vars = append(t.vars, v.val)
473 case next.typ == itemChar && next.val == ",":
474 t.nextNonSpace()
475 pipe.Decl = append(pipe.Decl, t.newVariable(v.pos, v.val))
476 t.vars = append(t.vars, v.val)
477 if context == "range" && len(pipe.Decl) < 2 {
478 switch t.peekNonSpace().typ {
479 case itemVariable, itemRightDelim, itemRightParen:
480
481 goto decls
482 default:
483 t.errorf("range can only initialize variables")
484 }
485 }
486 t.errorf("too many declarations in %s", context)
487 case tokenAfterVariable.typ == itemSpace:
488 t.backup3(v, tokenAfterVariable)
489 default:
490 t.backup2(v)
491 }
492 }
493 for {
494 switch token := t.nextNonSpace(); token.typ {
495 case end:
496
497 t.checkPipeline(pipe, context)
498 return
499 case itemBool, itemCharConstant, itemComplex, itemDot, itemField, itemIdentifier,
500 itemNumber, itemNil, itemRawString, itemString, itemVariable, itemLeftParen:
501 t.backup()
502 pipe.append(t.command())
503 default:
504 t.unexpected(token, context)
505 }
506 }
507 }
508
509 func (t *Tree) checkPipeline(pipe *PipeNode, context string) {
510
511 if len(pipe.Cmds) == 0 {
512 t.errorf("missing value for %s", context)
513 }
514
515 for i, c := range pipe.Cmds[1:] {
516 switch c.Args[0].Type() {
517 case NodeBool, NodeDot, NodeNil, NodeNumber, NodeString:
518
519 t.errorf("non executable command in pipeline stage %d", i+2)
520 }
521 }
522 }
523
524 func (t *Tree) parseControl(context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) {
525 defer t.popVars(len(t.vars))
526 pipe = t.pipeline(context, itemRightDelim)
527 if context == "range" {
528 t.rangeDepth++
529 }
530 var next Node
531 list, next = t.itemList()
532 if context == "range" {
533 t.rangeDepth--
534 }
535 switch next.Type() {
536 case nodeEnd:
537 case nodeElse:
538
539
540
541
542
543
544
545
546
547
548 if context == "if" && t.peek().typ == itemIf {
549 t.next()
550 elseList = t.newList(next.Position())
551 elseList.append(t.ifControl())
552 } else if context == "with" && t.peek().typ == itemWith {
553 t.next()
554 elseList = t.newList(next.Position())
555 elseList.append(t.withControl())
556 } else {
557 elseList, next = t.itemList()
558 if next.Type() != nodeEnd {
559 t.errorf("expected end; found %s", next)
560 }
561 }
562 }
563 return pipe.Position(), pipe.Line, pipe, list, elseList
564 }
565
566
567
568
569
570
571
572 func (t *Tree) ifControl() Node {
573 return t.newIf(t.parseControl("if"))
574 }
575
576
577
578
579
580
581
582 func (t *Tree) rangeControl() Node {
583 r := t.newRange(t.parseControl("range"))
584 return r
585 }
586
587
588
589
590
591
592
593 func (t *Tree) withControl() Node {
594 return t.newWith(t.parseControl("with"))
595 }
596
597
598
599
600
601
602 func (t *Tree) endControl() Node {
603 return t.newEnd(t.expect(itemRightDelim, "end").pos)
604 }
605
606
607
608
609
610
611 func (t *Tree) elseControl() Node {
612 peek := t.peekNonSpace()
613
614
615
616 if peek.typ == itemIf || peek.typ == itemWith {
617 return t.newElse(peek.pos, peek.line)
618 }
619 token := t.expect(itemRightDelim, "else")
620 return t.newElse(token.pos, token.line)
621 }
622
623
624
625
626
627
628
629
630 func (t *Tree) blockControl() Node {
631 const context = "block clause"
632
633 token := t.nextNonSpace()
634 name := t.parseTemplateName(token, context)
635 pipe := t.pipeline(context, itemRightDelim)
636
637 block := New(name)
638 block.text = t.text
639 block.Mode = t.Mode
640 block.ParseName = t.ParseName
641 block.startParse(t.funcs, t.lex, t.treeSet)
642 var end Node
643 block.Root, end = block.itemList()
644 if end.Type() != nodeEnd {
645 t.errorf("unexpected %s in %s", end, context)
646 }
647 block.add()
648 block.stopParse()
649
650 return t.newTemplate(token.pos, token.line, name, pipe)
651 }
652
653
654
655
656
657
658
659 func (t *Tree) templateControl() Node {
660 const context = "template clause"
661 token := t.nextNonSpace()
662 name := t.parseTemplateName(token, context)
663 var pipe *PipeNode
664 if t.nextNonSpace().typ != itemRightDelim {
665 t.backup()
666
667 pipe = t.pipeline(context, itemRightDelim)
668 }
669 return t.newTemplate(token.pos, token.line, name, pipe)
670 }
671
672 func (t *Tree) parseTemplateName(token item, context string) (name string) {
673 switch token.typ {
674 case itemString, itemRawString:
675 s, err := strconv.Unquote(token.val)
676 if err != nil {
677 t.error(err)
678 }
679 name = s
680 default:
681 t.unexpected(token, context)
682 }
683 return
684 }
685
686
687
688
689
690
691
692 func (t *Tree) command() *CommandNode {
693 cmd := t.newCommand(t.peekNonSpace().pos)
694 for {
695 t.peekNonSpace()
696 operand := t.operand()
697 if operand != nil {
698 cmd.append(operand)
699 }
700 switch token := t.next(); token.typ {
701 case itemSpace:
702 continue
703 case itemRightDelim, itemRightParen:
704 t.backup()
705 case itemPipe:
706
707 default:
708 t.unexpected(token, "operand")
709 }
710 break
711 }
712 if len(cmd.Args) == 0 {
713 t.errorf("empty command")
714 }
715 return cmd
716 }
717
718
719
720
721
722
723
724
725 func (t *Tree) operand() Node {
726 node := t.term()
727 if node == nil {
728 return nil
729 }
730 if t.peek().typ == itemField {
731 chain := t.newChain(t.peek().pos, node)
732 for t.peek().typ == itemField {
733 chain.Add(t.next().val)
734 }
735
736
737
738
739
740 switch node.Type() {
741 case NodeField:
742 node = t.newField(chain.Position(), chain.String())
743 case NodeVariable:
744 node = t.newVariable(chain.Position(), chain.String())
745 case NodeBool, NodeString, NodeNumber, NodeNil, NodeDot:
746 t.errorf("unexpected . after term %q", node.String())
747 default:
748 node = chain
749 }
750 }
751 return node
752 }
753
754
755
756
757
758
759
760
761
762
763
764
765 func (t *Tree) term() Node {
766 switch token := t.nextNonSpace(); token.typ {
767 case itemIdentifier:
768 checkFunc := t.Mode&SkipFuncCheck == 0
769 if checkFunc && !t.hasFunction(token.val) {
770 t.errorf("function %q not defined", token.val)
771 }
772 return NewIdentifier(token.val).SetTree(t).SetPos(token.pos)
773 case itemDot:
774 return t.newDot(token.pos)
775 case itemNil:
776 return t.newNil(token.pos)
777 case itemVariable:
778 return t.useVar(token.pos, token.val)
779 case itemField:
780 return t.newField(token.pos, token.val)
781 case itemBool:
782 return t.newBool(token.pos, token.val == "true")
783 case itemCharConstant, itemComplex, itemNumber:
784 number, err := t.newNumber(token.pos, token.val, token.typ)
785 if err != nil {
786 t.error(err)
787 }
788 return number
789 case itemLeftParen:
790 return t.pipeline("parenthesized pipeline", itemRightParen)
791 case itemString, itemRawString:
792 s, err := strconv.Unquote(token.val)
793 if err != nil {
794 t.error(err)
795 }
796 return t.newString(token.pos, token.val, s)
797 }
798 t.backup()
799 return nil
800 }
801
802
803 func (t *Tree) hasFunction(name string) bool {
804 for _, funcMap := range t.funcs {
805 if funcMap == nil {
806 continue
807 }
808 if funcMap[name] != nil {
809 return true
810 }
811 }
812 return false
813 }
814
815
816 func (t *Tree) popVars(n int) {
817 t.vars = t.vars[:n]
818 }
819
820
821
822 func (t *Tree) useVar(pos Pos, name string) Node {
823 v := t.newVariable(pos, name)
824 for _, varName := range t.vars {
825 if varName == v.Ident[0] {
826 return v
827 }
828 }
829 t.errorf("undefined variable %q", v.Ident[0])
830 return nil
831 }
832
View as plain text