1
2
3
4
5
6
7 package parse
8
9 import (
10 "fmt"
11 "strconv"
12 "strings"
13 )
14
15 var textFormat = "%s"
16
17
18
19
20 type Node interface {
21 Type() NodeType
22 String() string
23
24
25
26 Copy() Node
27 Position() Pos
28
29
30 tree() *Tree
31
32 writeTo(*strings.Builder)
33 }
34
35
36 type NodeType int
37
38
39
40 type Pos int
41
42 func (p Pos) Position() Pos {
43 return p
44 }
45
46
47
48 func (t NodeType) Type() NodeType {
49 return t
50 }
51
52 const (
53 NodeText NodeType = iota
54 NodeAction
55 NodeBool
56 NodeChain
57 NodeCommand
58 NodeDot
59 nodeElse
60 nodeEnd
61 NodeField
62 NodeIdentifier
63 NodeIf
64 NodeList
65 NodeNil
66 NodeNumber
67 NodePipe
68 NodeRange
69 NodeString
70 NodeTemplate
71 NodeVariable
72 NodeWith
73 NodeComment
74 )
75
76
77
78
79 type ListNode struct {
80 NodeType
81 Pos
82 tr *Tree
83 Nodes []Node
84 }
85
86 func (t *Tree) newList(pos Pos) *ListNode {
87 return &ListNode{tr: t, NodeType: NodeList, Pos: pos}
88 }
89
90 func (l *ListNode) append(n Node) {
91 l.Nodes = append(l.Nodes, n)
92 }
93
94 func (l *ListNode) tree() *Tree {
95 return l.tr
96 }
97
98 func (l *ListNode) String() string {
99 var sb strings.Builder
100 l.writeTo(&sb)
101 return sb.String()
102 }
103
104 func (l *ListNode) writeTo(sb *strings.Builder) {
105 for _, n := range l.Nodes {
106 n.writeTo(sb)
107 }
108 }
109
110 func (l *ListNode) CopyList() *ListNode {
111 if l == nil {
112 return l
113 }
114 n := l.tr.newList(l.Pos)
115 for _, elem := range l.Nodes {
116 n.append(elem.Copy())
117 }
118 return n
119 }
120
121 func (l *ListNode) Copy() Node {
122 return l.CopyList()
123 }
124
125
126 type TextNode struct {
127 NodeType
128 Pos
129 tr *Tree
130 Text []byte
131 }
132
133 func (t *Tree) newText(pos Pos, text string) *TextNode {
134 return &TextNode{tr: t, NodeType: NodeText, Pos: pos, Text: []byte(text)}
135 }
136
137 func (t *TextNode) String() string {
138 return fmt.Sprintf(textFormat, t.Text)
139 }
140
141 func (t *TextNode) writeTo(sb *strings.Builder) {
142 sb.WriteString(t.String())
143 }
144
145 func (t *TextNode) tree() *Tree {
146 return t.tr
147 }
148
149 func (t *TextNode) Copy() Node {
150 return &TextNode{tr: t.tr, NodeType: NodeText, Pos: t.Pos, Text: append([]byte{}, t.Text...)}
151 }
152
153
154 type CommentNode struct {
155 NodeType
156 Pos
157 tr *Tree
158 Text string
159 }
160
161 func (t *Tree) newComment(pos Pos, text string) *CommentNode {
162 return &CommentNode{tr: t, NodeType: NodeComment, Pos: pos, Text: text}
163 }
164
165 func (c *CommentNode) String() string {
166 var sb strings.Builder
167 c.writeTo(&sb)
168 return sb.String()
169 }
170
171 func (c *CommentNode) writeTo(sb *strings.Builder) {
172 sb.WriteString("{{")
173 sb.WriteString(c.Text)
174 sb.WriteString("}}")
175 }
176
177 func (c *CommentNode) tree() *Tree {
178 return c.tr
179 }
180
181 func (c *CommentNode) Copy() Node {
182 return &CommentNode{tr: c.tr, NodeType: NodeComment, Pos: c.Pos, Text: c.Text}
183 }
184
185
186 type PipeNode struct {
187 NodeType
188 Pos
189 tr *Tree
190 Line int
191 IsAssign bool
192 Decl []*VariableNode
193 Cmds []*CommandNode
194 }
195
196 func (t *Tree) newPipeline(pos Pos, line int, vars []*VariableNode) *PipeNode {
197 return &PipeNode{tr: t, NodeType: NodePipe, Pos: pos, Line: line, Decl: vars}
198 }
199
200 func (p *PipeNode) append(command *CommandNode) {
201 p.Cmds = append(p.Cmds, command)
202 }
203
204 func (p *PipeNode) String() string {
205 var sb strings.Builder
206 p.writeTo(&sb)
207 return sb.String()
208 }
209
210 func (p *PipeNode) writeTo(sb *strings.Builder) {
211 if len(p.Decl) > 0 {
212 for i, v := range p.Decl {
213 if i > 0 {
214 sb.WriteString(", ")
215 }
216 v.writeTo(sb)
217 }
218 sb.WriteString(" := ")
219 }
220 for i, c := range p.Cmds {
221 if i > 0 {
222 sb.WriteString(" | ")
223 }
224 c.writeTo(sb)
225 }
226 }
227
228 func (p *PipeNode) tree() *Tree {
229 return p.tr
230 }
231
232 func (p *PipeNode) CopyPipe() *PipeNode {
233 if p == nil {
234 return p
235 }
236 vars := make([]*VariableNode, len(p.Decl))
237 for i, d := range p.Decl {
238 vars[i] = d.Copy().(*VariableNode)
239 }
240 n := p.tr.newPipeline(p.Pos, p.Line, vars)
241 n.IsAssign = p.IsAssign
242 for _, c := range p.Cmds {
243 n.append(c.Copy().(*CommandNode))
244 }
245 return n
246 }
247
248 func (p *PipeNode) Copy() Node {
249 return p.CopyPipe()
250 }
251
252
253
254
255 type ActionNode struct {
256 NodeType
257 Pos
258 tr *Tree
259 Line int
260 Pipe *PipeNode
261 }
262
263 func (t *Tree) newAction(pos Pos, line int, pipe *PipeNode) *ActionNode {
264 return &ActionNode{tr: t, NodeType: NodeAction, Pos: pos, Line: line, Pipe: pipe}
265 }
266
267 func (a *ActionNode) String() string {
268 var sb strings.Builder
269 a.writeTo(&sb)
270 return sb.String()
271 }
272
273 func (a *ActionNode) writeTo(sb *strings.Builder) {
274 sb.WriteString("{{")
275 a.Pipe.writeTo(sb)
276 sb.WriteString("}}")
277 }
278
279 func (a *ActionNode) tree() *Tree {
280 return a.tr
281 }
282
283 func (a *ActionNode) Copy() Node {
284 return a.tr.newAction(a.Pos, a.Line, a.Pipe.CopyPipe())
285
286 }
287
288
289 type CommandNode struct {
290 NodeType
291 Pos
292 tr *Tree
293 Args []Node
294 }
295
296 func (t *Tree) newCommand(pos Pos) *CommandNode {
297 return &CommandNode{tr: t, NodeType: NodeCommand, Pos: pos}
298 }
299
300 func (c *CommandNode) append(arg Node) {
301 c.Args = append(c.Args, arg)
302 }
303
304 func (c *CommandNode) String() string {
305 var sb strings.Builder
306 c.writeTo(&sb)
307 return sb.String()
308 }
309
310 func (c *CommandNode) writeTo(sb *strings.Builder) {
311 for i, arg := range c.Args {
312 if i > 0 {
313 sb.WriteByte(' ')
314 }
315 if arg, ok := arg.(*PipeNode); ok {
316 sb.WriteByte('(')
317 arg.writeTo(sb)
318 sb.WriteByte(')')
319 continue
320 }
321 arg.writeTo(sb)
322 }
323 }
324
325 func (c *CommandNode) tree() *Tree {
326 return c.tr
327 }
328
329 func (c *CommandNode) Copy() Node {
330 if c == nil {
331 return c
332 }
333 n := c.tr.newCommand(c.Pos)
334 for _, c := range c.Args {
335 n.append(c.Copy())
336 }
337 return n
338 }
339
340
341 type IdentifierNode struct {
342 NodeType
343 Pos
344 tr *Tree
345 Ident string
346 }
347
348
349 func NewIdentifier(ident string) *IdentifierNode {
350 return &IdentifierNode{NodeType: NodeIdentifier, Ident: ident}
351 }
352
353
354
355
356 func (i *IdentifierNode) SetPos(pos Pos) *IdentifierNode {
357 i.Pos = pos
358 return i
359 }
360
361
362
363
364 func (i *IdentifierNode) SetTree(t *Tree) *IdentifierNode {
365 i.tr = t
366 return i
367 }
368
369 func (i *IdentifierNode) String() string {
370 return i.Ident
371 }
372
373 func (i *IdentifierNode) writeTo(sb *strings.Builder) {
374 sb.WriteString(i.String())
375 }
376
377 func (i *IdentifierNode) tree() *Tree {
378 return i.tr
379 }
380
381 func (i *IdentifierNode) Copy() Node {
382 return NewIdentifier(i.Ident).SetTree(i.tr).SetPos(i.Pos)
383 }
384
385
386
387 type VariableNode struct {
388 NodeType
389 Pos
390 tr *Tree
391 Ident []string
392 }
393
394 func (t *Tree) newVariable(pos Pos, ident string) *VariableNode {
395 return &VariableNode{tr: t, NodeType: NodeVariable, Pos: pos, Ident: strings.Split(ident, ".")}
396 }
397
398 func (v *VariableNode) String() string {
399 var sb strings.Builder
400 v.writeTo(&sb)
401 return sb.String()
402 }
403
404 func (v *VariableNode) writeTo(sb *strings.Builder) {
405 for i, id := range v.Ident {
406 if i > 0 {
407 sb.WriteByte('.')
408 }
409 sb.WriteString(id)
410 }
411 }
412
413 func (v *VariableNode) tree() *Tree {
414 return v.tr
415 }
416
417 func (v *VariableNode) Copy() Node {
418 return &VariableNode{tr: v.tr, NodeType: NodeVariable, Pos: v.Pos, Ident: append([]string{}, v.Ident...)}
419 }
420
421
422 type DotNode struct {
423 NodeType
424 Pos
425 tr *Tree
426 }
427
428 func (t *Tree) newDot(pos Pos) *DotNode {
429 return &DotNode{tr: t, NodeType: NodeDot, Pos: pos}
430 }
431
432 func (d *DotNode) Type() NodeType {
433
434
435
436 return NodeDot
437 }
438
439 func (d *DotNode) String() string {
440 return "."
441 }
442
443 func (d *DotNode) writeTo(sb *strings.Builder) {
444 sb.WriteString(d.String())
445 }
446
447 func (d *DotNode) tree() *Tree {
448 return d.tr
449 }
450
451 func (d *DotNode) Copy() Node {
452 return d.tr.newDot(d.Pos)
453 }
454
455
456 type NilNode struct {
457 NodeType
458 Pos
459 tr *Tree
460 }
461
462 func (t *Tree) newNil(pos Pos) *NilNode {
463 return &NilNode{tr: t, NodeType: NodeNil, Pos: pos}
464 }
465
466 func (n *NilNode) Type() NodeType {
467
468
469
470 return NodeNil
471 }
472
473 func (n *NilNode) String() string {
474 return "nil"
475 }
476
477 func (n *NilNode) writeTo(sb *strings.Builder) {
478 sb.WriteString(n.String())
479 }
480
481 func (n *NilNode) tree() *Tree {
482 return n.tr
483 }
484
485 func (n *NilNode) Copy() Node {
486 return n.tr.newNil(n.Pos)
487 }
488
489
490
491
492 type FieldNode struct {
493 NodeType
494 Pos
495 tr *Tree
496 Ident []string
497 }
498
499 func (t *Tree) newField(pos Pos, ident string) *FieldNode {
500 return &FieldNode{tr: t, NodeType: NodeField, Pos: pos, Ident: strings.Split(ident[1:], ".")}
501 }
502
503 func (f *FieldNode) String() string {
504 var sb strings.Builder
505 f.writeTo(&sb)
506 return sb.String()
507 }
508
509 func (f *FieldNode) writeTo(sb *strings.Builder) {
510 for _, id := range f.Ident {
511 sb.WriteByte('.')
512 sb.WriteString(id)
513 }
514 }
515
516 func (f *FieldNode) tree() *Tree {
517 return f.tr
518 }
519
520 func (f *FieldNode) Copy() Node {
521 return &FieldNode{tr: f.tr, NodeType: NodeField, Pos: f.Pos, Ident: append([]string{}, f.Ident...)}
522 }
523
524
525
526
527 type ChainNode struct {
528 NodeType
529 Pos
530 tr *Tree
531 Node Node
532 Field []string
533 }
534
535 func (t *Tree) newChain(pos Pos, node Node) *ChainNode {
536 return &ChainNode{tr: t, NodeType: NodeChain, Pos: pos, Node: node}
537 }
538
539
540 func (c *ChainNode) Add(field string) {
541 if len(field) == 0 || field[0] != '.' {
542 panic("no dot in field")
543 }
544 field = field[1:]
545 if field == "" {
546 panic("empty field")
547 }
548 c.Field = append(c.Field, field)
549 }
550
551 func (c *ChainNode) String() string {
552 var sb strings.Builder
553 c.writeTo(&sb)
554 return sb.String()
555 }
556
557 func (c *ChainNode) writeTo(sb *strings.Builder) {
558 if _, ok := c.Node.(*PipeNode); ok {
559 sb.WriteByte('(')
560 c.Node.writeTo(sb)
561 sb.WriteByte(')')
562 } else {
563 c.Node.writeTo(sb)
564 }
565 for _, field := range c.Field {
566 sb.WriteByte('.')
567 sb.WriteString(field)
568 }
569 }
570
571 func (c *ChainNode) tree() *Tree {
572 return c.tr
573 }
574
575 func (c *ChainNode) Copy() Node {
576 return &ChainNode{tr: c.tr, NodeType: NodeChain, Pos: c.Pos, Node: c.Node, Field: append([]string{}, c.Field...)}
577 }
578
579
580 type BoolNode struct {
581 NodeType
582 Pos
583 tr *Tree
584 True bool
585 }
586
587 func (t *Tree) newBool(pos Pos, true bool) *BoolNode {
588 return &BoolNode{tr: t, NodeType: NodeBool, Pos: pos, True: true}
589 }
590
591 func (b *BoolNode) String() string {
592 if b.True {
593 return "true"
594 }
595 return "false"
596 }
597
598 func (b *BoolNode) writeTo(sb *strings.Builder) {
599 sb.WriteString(b.String())
600 }
601
602 func (b *BoolNode) tree() *Tree {
603 return b.tr
604 }
605
606 func (b *BoolNode) Copy() Node {
607 return b.tr.newBool(b.Pos, b.True)
608 }
609
610
611
612
613 type NumberNode struct {
614 NodeType
615 Pos
616 tr *Tree
617 IsInt bool
618 IsUint bool
619 IsFloat bool
620 IsComplex bool
621 Int64 int64
622 Uint64 uint64
623 Float64 float64
624 Complex128 complex128
625 Text string
626 }
627
628 func (t *Tree) newNumber(pos Pos, text string, typ itemType) (*NumberNode, error) {
629 n := &NumberNode{tr: t, NodeType: NodeNumber, Pos: pos, Text: text}
630 switch typ {
631 case itemCharConstant:
632 rune, _, tail, err := strconv.UnquoteChar(text[1:], text[0])
633 if err != nil {
634 return nil, err
635 }
636 if tail != "'" {
637 return nil, fmt.Errorf("malformed character constant: %s", text)
638 }
639 n.Int64 = int64(rune)
640 n.IsInt = true
641 n.Uint64 = uint64(rune)
642 n.IsUint = true
643 n.Float64 = float64(rune)
644 n.IsFloat = true
645 return n, nil
646 case itemComplex:
647
648 if _, err := fmt.Sscan(text, &n.Complex128); err != nil {
649 return nil, err
650 }
651 n.IsComplex = true
652 n.simplifyComplex()
653 return n, nil
654 }
655
656 if len(text) > 0 && text[len(text)-1] == 'i' {
657 f, err := strconv.ParseFloat(text[:len(text)-1], 64)
658 if err == nil {
659 n.IsComplex = true
660 n.Complex128 = complex(0, f)
661 n.simplifyComplex()
662 return n, nil
663 }
664 }
665
666 u, err := strconv.ParseUint(text, 0, 64)
667 if err == nil {
668 n.IsUint = true
669 n.Uint64 = u
670 }
671 i, err := strconv.ParseInt(text, 0, 64)
672 if err == nil {
673 n.IsInt = true
674 n.Int64 = i
675 if i == 0 {
676 n.IsUint = true
677 n.Uint64 = u
678 }
679 }
680
681 if n.IsInt {
682 n.IsFloat = true
683 n.Float64 = float64(n.Int64)
684 } else if n.IsUint {
685 n.IsFloat = true
686 n.Float64 = float64(n.Uint64)
687 } else {
688 f, err := strconv.ParseFloat(text, 64)
689 if err == nil {
690
691
692 if !strings.ContainsAny(text, ".eEpP") {
693 return nil, fmt.Errorf("integer overflow: %q", text)
694 }
695 n.IsFloat = true
696 n.Float64 = f
697
698 if !n.IsInt && float64(int64(f)) == f {
699 n.IsInt = true
700 n.Int64 = int64(f)
701 }
702 if !n.IsUint && float64(uint64(f)) == f {
703 n.IsUint = true
704 n.Uint64 = uint64(f)
705 }
706 }
707 }
708 if !n.IsInt && !n.IsUint && !n.IsFloat {
709 return nil, fmt.Errorf("illegal number syntax: %q", text)
710 }
711 return n, nil
712 }
713
714
715
716 func (n *NumberNode) simplifyComplex() {
717 n.IsFloat = imag(n.Complex128) == 0
718 if n.IsFloat {
719 n.Float64 = real(n.Complex128)
720 n.IsInt = float64(int64(n.Float64)) == n.Float64
721 if n.IsInt {
722 n.Int64 = int64(n.Float64)
723 }
724 n.IsUint = float64(uint64(n.Float64)) == n.Float64
725 if n.IsUint {
726 n.Uint64 = uint64(n.Float64)
727 }
728 }
729 }
730
731 func (n *NumberNode) String() string {
732 return n.Text
733 }
734
735 func (n *NumberNode) writeTo(sb *strings.Builder) {
736 sb.WriteString(n.String())
737 }
738
739 func (n *NumberNode) tree() *Tree {
740 return n.tr
741 }
742
743 func (n *NumberNode) Copy() Node {
744 nn := new(NumberNode)
745 *nn = *n
746 return nn
747 }
748
749
750 type StringNode struct {
751 NodeType
752 Pos
753 tr *Tree
754 Quoted string
755 Text string
756 }
757
758 func (t *Tree) newString(pos Pos, orig, text string) *StringNode {
759 return &StringNode{tr: t, NodeType: NodeString, Pos: pos, Quoted: orig, Text: text}
760 }
761
762 func (s *StringNode) String() string {
763 return s.Quoted
764 }
765
766 func (s *StringNode) writeTo(sb *strings.Builder) {
767 sb.WriteString(s.String())
768 }
769
770 func (s *StringNode) tree() *Tree {
771 return s.tr
772 }
773
774 func (s *StringNode) Copy() Node {
775 return s.tr.newString(s.Pos, s.Quoted, s.Text)
776 }
777
778
779
780 type endNode struct {
781 NodeType
782 Pos
783 tr *Tree
784 }
785
786 func (t *Tree) newEnd(pos Pos) *endNode {
787 return &endNode{tr: t, NodeType: nodeEnd, Pos: pos}
788 }
789
790 func (e *endNode) String() string {
791 return "{{end}}"
792 }
793
794 func (e *endNode) writeTo(sb *strings.Builder) {
795 sb.WriteString(e.String())
796 }
797
798 func (e *endNode) tree() *Tree {
799 return e.tr
800 }
801
802 func (e *endNode) Copy() Node {
803 return e.tr.newEnd(e.Pos)
804 }
805
806
807 type elseNode struct {
808 NodeType
809 Pos
810 tr *Tree
811 Line int
812 }
813
814 func (t *Tree) newElse(pos Pos, line int) *elseNode {
815 return &elseNode{tr: t, NodeType: nodeElse, Pos: pos, Line: line}
816 }
817
818 func (e *elseNode) Type() NodeType {
819 return nodeElse
820 }
821
822 func (e *elseNode) String() string {
823 return "{{else}}"
824 }
825
826 func (e *elseNode) writeTo(sb *strings.Builder) {
827 sb.WriteString(e.String())
828 }
829
830 func (e *elseNode) tree() *Tree {
831 return e.tr
832 }
833
834 func (e *elseNode) Copy() Node {
835 return e.tr.newElse(e.Pos, e.Line)
836 }
837
838
839 type BranchNode struct {
840 NodeType
841 Pos
842 tr *Tree
843 Line int
844 Pipe *PipeNode
845 List *ListNode
846 ElseList *ListNode
847 }
848
849 func (b *BranchNode) String() string {
850 var sb strings.Builder
851 b.writeTo(&sb)
852 return sb.String()
853 }
854
855 func (b *BranchNode) writeTo(sb *strings.Builder) {
856 name := ""
857 switch b.NodeType {
858 case NodeIf:
859 name = "if"
860 case NodeRange:
861 name = "range"
862 case NodeWith:
863 name = "with"
864 default:
865 panic("unknown branch type")
866 }
867 sb.WriteString("{{")
868 sb.WriteString(name)
869 sb.WriteByte(' ')
870 b.Pipe.writeTo(sb)
871 sb.WriteString("}}")
872 b.List.writeTo(sb)
873 if b.ElseList != nil {
874 sb.WriteString("{{else}}")
875 b.ElseList.writeTo(sb)
876 }
877 sb.WriteString("{{end}}")
878 }
879
880 func (b *BranchNode) tree() *Tree {
881 return b.tr
882 }
883
884 func (b *BranchNode) Copy() Node {
885 switch b.NodeType {
886 case NodeIf:
887 return b.tr.newIf(b.Pos, b.Line, b.Pipe, b.List, b.ElseList)
888 case NodeRange:
889 return b.tr.newRange(b.Pos, b.Line, b.Pipe, b.List, b.ElseList)
890 case NodeWith:
891 return b.tr.newWith(b.Pos, b.Line, b.Pipe, b.List, b.ElseList)
892 default:
893 panic("unknown branch type")
894 }
895 }
896
897
898 type IfNode struct {
899 BranchNode
900 }
901
902 func (t *Tree) newIf(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *IfNode {
903 return &IfNode{BranchNode{tr: t, NodeType: NodeIf, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
904 }
905
906 func (i *IfNode) Copy() Node {
907 return i.tr.newIf(i.Pos, i.Line, i.Pipe.CopyPipe(), i.List.CopyList(), i.ElseList.CopyList())
908 }
909
910
911 type RangeNode struct {
912 BranchNode
913 }
914
915 func (t *Tree) newRange(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *RangeNode {
916 return &RangeNode{BranchNode{tr: t, NodeType: NodeRange, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
917 }
918
919 func (r *RangeNode) Copy() Node {
920 return r.tr.newRange(r.Pos, r.Line, r.Pipe.CopyPipe(), r.List.CopyList(), r.ElseList.CopyList())
921 }
922
923
924 type WithNode struct {
925 BranchNode
926 }
927
928 func (t *Tree) newWith(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *WithNode {
929 return &WithNode{BranchNode{tr: t, NodeType: NodeWith, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
930 }
931
932 func (w *WithNode) Copy() Node {
933 return w.tr.newWith(w.Pos, w.Line, w.Pipe.CopyPipe(), w.List.CopyList(), w.ElseList.CopyList())
934 }
935
936
937 type TemplateNode struct {
938 NodeType
939 Pos
940 tr *Tree
941 Line int
942 Name string
943 Pipe *PipeNode
944 }
945
946 func (t *Tree) newTemplate(pos Pos, line int, name string, pipe *PipeNode) *TemplateNode {
947 return &TemplateNode{tr: t, NodeType: NodeTemplate, Pos: pos, Line: line, Name: name, Pipe: pipe}
948 }
949
950 func (t *TemplateNode) String() string {
951 var sb strings.Builder
952 t.writeTo(&sb)
953 return sb.String()
954 }
955
956 func (t *TemplateNode) writeTo(sb *strings.Builder) {
957 sb.WriteString("{{template ")
958 sb.WriteString(strconv.Quote(t.Name))
959 if t.Pipe != nil {
960 sb.WriteByte(' ')
961 t.Pipe.writeTo(sb)
962 }
963 sb.WriteString("}}")
964 }
965
966 func (t *TemplateNode) tree() *Tree {
967 return t.tr
968 }
969
970 func (t *TemplateNode) Copy() Node {
971 return t.tr.newTemplate(t.Pos, t.Line, t.Name, t.Pipe.CopyPipe())
972 }
973
View as plain text