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