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.lex.drain()
214 t.stopParse()
215 }
216 *errp = e.(error)
217 }
218 }
219
220
221 func (t *Tree) startParse(funcs []map[string]any, lex *lexer, treeSet map[string]*Tree) {
222 t.Root = nil
223 t.lex = lex
224 t.vars = []string{"$"}
225 t.funcs = funcs
226 t.treeSet = treeSet
227 }
228
229
230 func (t *Tree) stopParse() {
231 t.lex = nil
232 t.vars = nil
233 t.funcs = nil
234 t.treeSet = nil
235 }
236
237
238
239
240
241 func (t *Tree) Parse(text, leftDelim, rightDelim string, treeSet map[string]*Tree, funcs ...map[string]any) (tree *Tree, err error) {
242 defer t.recover(&err)
243 t.ParseName = t.Name
244 emitComment := t.Mode&ParseComments != 0
245 breakOK := !t.hasFunction("break")
246 continueOK := !t.hasFunction("continue")
247 lexer := lex(t.Name, text, leftDelim, rightDelim, emitComment, breakOK, continueOK)
248 t.startParse(funcs, lexer, treeSet)
249 t.text = text
250 t.parse()
251 t.add()
252 t.stopParse()
253 return t, nil
254 }
255
256
257 func (t *Tree) add() {
258 tree := t.treeSet[t.Name]
259 if tree == nil || IsEmptyTree(tree.Root) {
260 t.treeSet[t.Name] = t
261 return
262 }
263 if !IsEmptyTree(t.Root) {
264 t.errorf("template: multiple definition of template %q", t.Name)
265 }
266 }
267
268
269 func IsEmptyTree(n Node) bool {
270 switch n := n.(type) {
271 case nil:
272 return true
273 case *ActionNode:
274 case *CommentNode:
275 return true
276 case *IfNode:
277 case *ListNode:
278 for _, node := range n.Nodes {
279 if !IsEmptyTree(node) {
280 return false
281 }
282 }
283 return true
284 case *RangeNode:
285 case *TemplateNode:
286 case *TextNode:
287 return len(bytes.TrimSpace(n.Text)) == 0
288 case *WithNode:
289 default:
290 panic("unknown node: " + n.String())
291 }
292 return false
293 }
294
295
296
297
298 func (t *Tree) parse() {
299 t.Root = t.newList(t.peek().pos)
300 for t.peek().typ != itemEOF {
301 if t.peek().typ == itemLeftDelim {
302 delim := t.next()
303 if t.nextNonSpace().typ == itemDefine {
304 newT := New("definition")
305 newT.text = t.text
306 newT.Mode = t.Mode
307 newT.ParseName = t.ParseName
308 newT.startParse(t.funcs, t.lex, t.treeSet)
309 newT.parseDefinition()
310 continue
311 }
312 t.backup2(delim)
313 }
314 switch n := t.textOrAction(); n.Type() {
315 case nodeEnd, nodeElse:
316 t.errorf("unexpected %s", n)
317 default:
318 t.Root.append(n)
319 }
320 }
321 }
322
323
324
325
326 func (t *Tree) parseDefinition() {
327 const context = "define clause"
328 name := t.expectOneOf(itemString, itemRawString, context)
329 var err error
330 t.Name, err = strconv.Unquote(name.val)
331 if err != nil {
332 t.error(err)
333 }
334 t.expect(itemRightDelim, context)
335 var end Node
336 t.Root, end = t.itemList()
337 if end.Type() != nodeEnd {
338 t.errorf("unexpected %s in %s", end, context)
339 }
340 t.add()
341 t.stopParse()
342 }
343
344
345
346
347
348
349 func (t *Tree) itemList() (list *ListNode, next Node) {
350 list = t.newList(t.peekNonSpace().pos)
351 for t.peekNonSpace().typ != itemEOF {
352 n := t.textOrAction()
353 switch n.Type() {
354 case nodeEnd, nodeElse:
355 return list, n
356 }
357 list.append(n)
358 }
359 t.errorf("unexpected EOF")
360 return
361 }
362
363
364
365
366 func (t *Tree) textOrAction() Node {
367 switch token := t.nextNonSpace(); token.typ {
368 case itemText:
369 return t.newText(token.pos, token.val)
370 case itemLeftDelim:
371 t.actionLine = token.line
372 defer t.clearActionLine()
373 return t.action()
374 case itemComment:
375 return t.newComment(token.pos, token.val)
376 default:
377 t.unexpected(token, "input")
378 }
379 return nil
380 }
381
382 func (t *Tree) clearActionLine() {
383 t.actionLine = 0
384 }
385
386
387
388
389
390
391
392
393 func (t *Tree) action() (n Node) {
394 switch token := t.nextNonSpace(); token.typ {
395 case itemBlock:
396 return t.blockControl()
397 case itemBreak:
398 return t.breakControl(token.pos, token.line)
399 case itemContinue:
400 return t.continueControl(token.pos, token.line)
401 case itemElse:
402 return t.elseControl()
403 case itemEnd:
404 return t.endControl()
405 case itemIf:
406 return t.ifControl()
407 case itemRange:
408 return t.rangeControl()
409 case itemTemplate:
410 return t.templateControl()
411 case itemWith:
412 return t.withControl()
413 }
414 t.backup()
415 token := t.peek()
416
417 return t.newAction(token.pos, token.line, t.pipeline("command", itemRightDelim))
418 }
419
420
421
422
423
424
425 func (t *Tree) breakControl(pos Pos, line int) Node {
426 if token := t.nextNonSpace(); token.typ != itemRightDelim {
427 t.unexpected(token, "{{break}}")
428 }
429 if t.rangeDepth == 0 {
430 t.errorf("{{break}} outside {{range}}")
431 }
432 return t.newBreak(pos, line)
433 }
434
435
436
437
438
439
440 func (t *Tree) continueControl(pos Pos, line int) Node {
441 if token := t.nextNonSpace(); token.typ != itemRightDelim {
442 t.unexpected(token, "{{continue}}")
443 }
444 if t.rangeDepth == 0 {
445 t.errorf("{{continue}} outside {{range}}")
446 }
447 return t.newContinue(pos, line)
448 }
449
450
451
452
453 func (t *Tree) pipeline(context string, end itemType) (pipe *PipeNode) {
454 token := t.peekNonSpace()
455 pipe = t.newPipeline(token.pos, token.line, nil)
456
457 decls:
458 if v := t.peekNonSpace(); v.typ == itemVariable {
459 t.next()
460
461
462
463
464 tokenAfterVariable := t.peek()
465 next := t.peekNonSpace()
466 switch {
467 case next.typ == itemAssign, next.typ == itemDeclare:
468 pipe.IsAssign = next.typ == itemAssign
469 t.nextNonSpace()
470 pipe.Decl = append(pipe.Decl, t.newVariable(v.pos, v.val))
471 t.vars = append(t.vars, v.val)
472 case next.typ == itemChar && next.val == ",":
473 t.nextNonSpace()
474 pipe.Decl = append(pipe.Decl, t.newVariable(v.pos, v.val))
475 t.vars = append(t.vars, v.val)
476 if context == "range" && len(pipe.Decl) < 2 {
477 switch t.peekNonSpace().typ {
478 case itemVariable, itemRightDelim, itemRightParen:
479
480 goto decls
481 default:
482 t.errorf("range can only initialize variables")
483 }
484 }
485 t.errorf("too many declarations in %s", context)
486 case tokenAfterVariable.typ == itemSpace:
487 t.backup3(v, tokenAfterVariable)
488 default:
489 t.backup2(v)
490 }
491 }
492 for {
493 switch token := t.nextNonSpace(); token.typ {
494 case end:
495
496 t.checkPipeline(pipe, context)
497 return
498 case itemBool, itemCharConstant, itemComplex, itemDot, itemField, itemIdentifier,
499 itemNumber, itemNil, itemRawString, itemString, itemVariable, itemLeftParen:
500 t.backup()
501 pipe.append(t.command())
502 default:
503 t.unexpected(token, context)
504 }
505 }
506 }
507
508 func (t *Tree) checkPipeline(pipe *PipeNode, context string) {
509
510 if len(pipe.Cmds) == 0 {
511 t.errorf("missing value for %s", context)
512 }
513
514 for i, c := range pipe.Cmds[1:] {
515 switch c.Args[0].Type() {
516 case NodeBool, NodeDot, NodeNil, NodeNumber, NodeString:
517
518 t.errorf("non executable command in pipeline stage %d", i+2)
519 }
520 }
521 }
522
523 func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) {
524 defer t.popVars(len(t.vars))
525 pipe = t.pipeline(context, itemRightDelim)
526 if context == "range" {
527 t.rangeDepth++
528 }
529 var next Node
530 list, next = t.itemList()
531 if context == "range" {
532 t.rangeDepth--
533 }
534 switch next.Type() {
535 case nodeEnd:
536 case nodeElse:
537 if allowElseIf {
538
539
540
541
542
543
544
545
546 if t.peek().typ == itemIf {
547 t.next()
548 elseList = t.newList(next.Position())
549 elseList.append(t.ifControl())
550
551 break
552 }
553 }
554 elseList, next = t.itemList()
555 if next.Type() != nodeEnd {
556 t.errorf("expected end; found %s", next)
557 }
558 }
559 return pipe.Position(), pipe.Line, pipe, list, elseList
560 }
561
562
563
564
565
566
567
568 func (t *Tree) ifControl() Node {
569 return t.newIf(t.parseControl(true, "if"))
570 }
571
572
573
574
575
576
577
578 func (t *Tree) rangeControl() Node {
579 r := t.newRange(t.parseControl(false, "range"))
580 return r
581 }
582
583
584
585
586
587
588
589 func (t *Tree) withControl() Node {
590 return t.newWith(t.parseControl(false, "with"))
591 }
592
593
594
595
596
597
598 func (t *Tree) endControl() Node {
599 return t.newEnd(t.expect(itemRightDelim, "end").pos)
600 }
601
602
603
604
605
606
607 func (t *Tree) elseControl() Node {
608
609 peek := t.peekNonSpace()
610 if peek.typ == itemIf {
611
612 return t.newElse(peek.pos, peek.line)
613 }
614 token := t.expect(itemRightDelim, "else")
615 return t.newElse(token.pos, token.line)
616 }
617
618
619
620
621
622
623
624
625 func (t *Tree) blockControl() Node {
626 const context = "block clause"
627
628 token := t.nextNonSpace()
629 name := t.parseTemplateName(token, context)
630 pipe := t.pipeline(context, itemRightDelim)
631
632 block := New(name)
633 block.text = t.text
634 block.Mode = t.Mode
635 block.ParseName = t.ParseName
636 block.startParse(t.funcs, t.lex, t.treeSet)
637 var end Node
638 block.Root, end = block.itemList()
639 if end.Type() != nodeEnd {
640 t.errorf("unexpected %s in %s", end, context)
641 }
642 block.add()
643 block.stopParse()
644
645 return t.newTemplate(token.pos, token.line, name, pipe)
646 }
647
648
649
650
651
652
653
654 func (t *Tree) templateControl() Node {
655 const context = "template clause"
656 token := t.nextNonSpace()
657 name := t.parseTemplateName(token, context)
658 var pipe *PipeNode
659 if t.nextNonSpace().typ != itemRightDelim {
660 t.backup()
661
662 pipe = t.pipeline(context, itemRightDelim)
663 }
664 return t.newTemplate(token.pos, token.line, name, pipe)
665 }
666
667 func (t *Tree) parseTemplateName(token item, context string) (name string) {
668 switch token.typ {
669 case itemString, itemRawString:
670 s, err := strconv.Unquote(token.val)
671 if err != nil {
672 t.error(err)
673 }
674 name = s
675 default:
676 t.unexpected(token, context)
677 }
678 return
679 }
680
681
682
683
684
685
686
687 func (t *Tree) command() *CommandNode {
688 cmd := t.newCommand(t.peekNonSpace().pos)
689 for {
690 t.peekNonSpace()
691 operand := t.operand()
692 if operand != nil {
693 cmd.append(operand)
694 }
695 switch token := t.next(); token.typ {
696 case itemSpace:
697 continue
698 case itemRightDelim, itemRightParen:
699 t.backup()
700 case itemPipe:
701
702 default:
703 t.unexpected(token, "operand")
704 }
705 break
706 }
707 if len(cmd.Args) == 0 {
708 t.errorf("empty command")
709 }
710 return cmd
711 }
712
713
714
715
716
717
718
719
720 func (t *Tree) operand() Node {
721 node := t.term()
722 if node == nil {
723 return nil
724 }
725 if t.peek().typ == itemField {
726 chain := t.newChain(t.peek().pos, node)
727 for t.peek().typ == itemField {
728 chain.Add(t.next().val)
729 }
730
731
732
733
734
735 switch node.Type() {
736 case NodeField:
737 node = t.newField(chain.Position(), chain.String())
738 case NodeVariable:
739 node = t.newVariable(chain.Position(), chain.String())
740 case NodeBool, NodeString, NodeNumber, NodeNil, NodeDot:
741 t.errorf("unexpected . after term %q", node.String())
742 default:
743 node = chain
744 }
745 }
746 return node
747 }
748
749
750
751
752
753
754
755
756
757
758
759
760 func (t *Tree) term() Node {
761 switch token := t.nextNonSpace(); token.typ {
762 case itemIdentifier:
763 checkFunc := t.Mode&SkipFuncCheck == 0
764 if checkFunc && !t.hasFunction(token.val) {
765 t.errorf("function %q not defined", token.val)
766 }
767 return NewIdentifier(token.val).SetTree(t).SetPos(token.pos)
768 case itemDot:
769 return t.newDot(token.pos)
770 case itemNil:
771 return t.newNil(token.pos)
772 case itemVariable:
773 return t.useVar(token.pos, token.val)
774 case itemField:
775 return t.newField(token.pos, token.val)
776 case itemBool:
777 return t.newBool(token.pos, token.val == "true")
778 case itemCharConstant, itemComplex, itemNumber:
779 number, err := t.newNumber(token.pos, token.val, token.typ)
780 if err != nil {
781 t.error(err)
782 }
783 return number
784 case itemLeftParen:
785 return t.pipeline("parenthesized pipeline", itemRightParen)
786 case itemString, itemRawString:
787 s, err := strconv.Unquote(token.val)
788 if err != nil {
789 t.error(err)
790 }
791 return t.newString(token.pos, token.val, s)
792 }
793 t.backup()
794 return nil
795 }
796
797
798 func (t *Tree) hasFunction(name string) bool {
799 for _, funcMap := range t.funcs {
800 if funcMap == nil {
801 continue
802 }
803 if funcMap[name] != nil {
804 return true
805 }
806 }
807 return false
808 }
809
810
811 func (t *Tree) popVars(n int) {
812 t.vars = t.vars[:n]
813 }
814
815
816
817 func (t *Tree) useVar(pos Pos, name string) Node {
818 v := t.newVariable(pos, name)
819 for _, varName := range t.vars {
820 if varName == v.Ident[0] {
821 return v
822 }
823 }
824 t.errorf("undefined variable %q", v.Ident[0])
825 return nil
826 }
827
View as plain text