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