node.go - hugo - [fork] hugo port for 9front
(HTM) git clone https://git.drkhsh.at/hugo.git
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) Submodules
(DIR) README
(DIR) LICENSE
---
node.go (24888B)
---
1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // Parse nodes.
6
7 package parse
8
9 import (
10 "fmt"
11 "strconv"
12 "strings"
13 )
14
15 var textFormat = "%s" // Changed to "%q" in tests for better error messages.
16
17 // A Node is an element in the parse tree. The interface is trivial.
18 // The interface contains an unexported method so that only
19 // types local to this package can satisfy it.
20 type Node interface {
21 Type() NodeType
22 String() string
23 // Copy does a deep copy of the Node and all its components.
24 // To avoid type assertions, some XxxNodes also have specialized
25 // CopyXxx methods that return *XxxNode.
26 Copy() Node
27 Position() Pos // byte position of start of node in full original input string
28 // tree returns the containing *Tree.
29 // It is unexported so all implementations of Node are in this package.
30 tree() *Tree
31 // writeTo writes the String output to the builder.
32 writeTo(*strings.Builder)
33 }
34
35 // NodeType identifies the type of a parse tree node.
36 type NodeType int
37
38 // Pos represents a byte position in the original input text from which
39 // this template was parsed.
40 type Pos int
41
42 func (p Pos) Position() Pos {
43 return p
44 }
45
46 // Type returns itself and provides an easy default implementation
47 // for embedding in a Node. Embedded in all non-trivial Nodes.
48 func (t NodeType) Type() NodeType {
49 return t
50 }
51
52 const (
53 NodeText NodeType = iota // Plain text.
54 NodeAction // A non-control action such as a field evaluation.
55 NodeBool // A boolean constant.
56 NodeChain // A sequence of field accesses.
57 NodeCommand // An element of a pipeline.
58 NodeDot // The cursor, dot.
59 nodeElse // An else action. Not added to tree.
60 nodeEnd // An end action. Not added to tree.
61 NodeField // A field or method name.
62 NodeIdentifier // An identifier; always a function name.
63 NodeIf // An if action.
64 NodeList // A list of Nodes.
65 NodeNil // An untyped nil constant.
66 NodeNumber // A numerical constant.
67 NodePipe // A pipeline of commands.
68 NodeRange // A range action.
69 NodeString // A string constant.
70 NodeTemplate // A template invocation action.
71 NodeVariable // A $ variable.
72 NodeWith // A with action.
73 NodeComment // A comment.
74 NodeBreak // A break action.
75 NodeContinue // A continue action.
76 )
77
78 // Nodes.
79
80 // ListNode holds a sequence of nodes.
81 type ListNode struct {
82 NodeType
83 Pos
84 tr *Tree
85 Nodes []Node // The element nodes in lexical order.
86 }
87
88 func (t *Tree) newList(pos Pos) *ListNode {
89 return &ListNode{tr: t, NodeType: NodeList, Pos: pos}
90 }
91
92 func (l *ListNode) append(n Node) {
93 l.Nodes = append(l.Nodes, n)
94 }
95
96 func (l *ListNode) tree() *Tree {
97 return l.tr
98 }
99
100 func (l *ListNode) String() string {
101 var sb strings.Builder
102 l.writeTo(&sb)
103 return sb.String()
104 }
105
106 func (l *ListNode) writeTo(sb *strings.Builder) {
107 for _, n := range l.Nodes {
108 n.writeTo(sb)
109 }
110 }
111
112 func (l *ListNode) CopyList() *ListNode {
113 if l == nil {
114 return l
115 }
116 n := l.tr.newList(l.Pos)
117 for _, elem := range l.Nodes {
118 n.append(elem.Copy())
119 }
120 return n
121 }
122
123 func (l *ListNode) Copy() Node {
124 return l.CopyList()
125 }
126
127 // TextNode holds plain text.
128 type TextNode struct {
129 NodeType
130 Pos
131 tr *Tree
132 Text []byte // The text; may span newlines.
133 }
134
135 func (t *Tree) newText(pos Pos, text string) *TextNode {
136 return &TextNode{tr: t, NodeType: NodeText, Pos: pos, Text: []byte(text)}
137 }
138
139 func (t *TextNode) String() string {
140 return fmt.Sprintf(textFormat, t.Text)
141 }
142
143 func (t *TextNode) writeTo(sb *strings.Builder) {
144 sb.WriteString(t.String())
145 }
146
147 func (t *TextNode) tree() *Tree {
148 return t.tr
149 }
150
151 func (t *TextNode) Copy() Node {
152 return &TextNode{tr: t.tr, NodeType: NodeText, Pos: t.Pos, Text: append([]byte{}, t.Text...)}
153 }
154
155 // CommentNode holds a comment.
156 type CommentNode struct {
157 NodeType
158 Pos
159 tr *Tree
160 Text string // Comment text.
161 }
162
163 func (t *Tree) newComment(pos Pos, text string) *CommentNode {
164 return &CommentNode{tr: t, NodeType: NodeComment, Pos: pos, Text: text}
165 }
166
167 func (c *CommentNode) String() string {
168 var sb strings.Builder
169 c.writeTo(&sb)
170 return sb.String()
171 }
172
173 func (c *CommentNode) writeTo(sb *strings.Builder) {
174 sb.WriteString("{{")
175 sb.WriteString(c.Text)
176 sb.WriteString("}}")
177 }
178
179 func (c *CommentNode) tree() *Tree {
180 return c.tr
181 }
182
183 func (c *CommentNode) Copy() Node {
184 return &CommentNode{tr: c.tr, NodeType: NodeComment, Pos: c.Pos, Text: c.Text}
185 }
186
187 // PipeNode holds a pipeline with optional declaration
188 type PipeNode struct {
189 NodeType
190 Pos
191 tr *Tree
192 Line int // The line number in the input. Deprecated: Kept for compatibility.
193 IsAssign bool // The variables are being assigned, not declared.
194 Decl []*VariableNode // Variables in lexical order.
195 Cmds []*CommandNode // The commands in lexical order.
196 }
197
198 func (t *Tree) newPipeline(pos Pos, line int, vars []*VariableNode) *PipeNode {
199 return &PipeNode{tr: t, NodeType: NodePipe, Pos: pos, Line: line, Decl: vars}
200 }
201
202 func (p *PipeNode) append(command *CommandNode) {
203 p.Cmds = append(p.Cmds, command)
204 }
205
206 func (p *PipeNode) String() string {
207 var sb strings.Builder
208 p.writeTo(&sb)
209 return sb.String()
210 }
211
212 func (p *PipeNode) writeTo(sb *strings.Builder) {
213 if len(p.Decl) > 0 {
214 for i, v := range p.Decl {
215 if i > 0 {
216 sb.WriteString(", ")
217 }
218 v.writeTo(sb)
219 }
220 if p.IsAssign {
221 sb.WriteString(" = ")
222 } else {
223 sb.WriteString(" := ")
224 }
225 }
226 for i, c := range p.Cmds {
227 if i > 0 {
228 sb.WriteString(" | ")
229 }
230 c.writeTo(sb)
231 }
232 }
233
234 func (p *PipeNode) tree() *Tree {
235 return p.tr
236 }
237
238 func (p *PipeNode) CopyPipe() *PipeNode {
239 if p == nil {
240 return p
241 }
242 vars := make([]*VariableNode, len(p.Decl))
243 for i, d := range p.Decl {
244 vars[i] = d.Copy().(*VariableNode)
245 }
246 n := p.tr.newPipeline(p.Pos, p.Line, vars)
247 n.IsAssign = p.IsAssign
248 for _, c := range p.Cmds {
249 n.append(c.Copy().(*CommandNode))
250 }
251 return n
252 }
253
254 func (p *PipeNode) Copy() Node {
255 return p.CopyPipe()
256 }
257
258 // ActionNode holds an action (something bounded by delimiters).
259 // Control actions have their own nodes; ActionNode represents simple
260 // ones such as field evaluations and parenthesized pipelines.
261 type ActionNode struct {
262 NodeType
263 Pos
264 tr *Tree
265 Line int // The line number in the input. Deprecated: Kept for compatibility.
266 Pipe *PipeNode // The pipeline in the action.
267 }
268
269 func (t *Tree) newAction(pos Pos, line int, pipe *PipeNode) *ActionNode {
270 return &ActionNode{tr: t, NodeType: NodeAction, Pos: pos, Line: line, Pipe: pipe}
271 }
272
273 func (a *ActionNode) String() string {
274 var sb strings.Builder
275 a.writeTo(&sb)
276 return sb.String()
277 }
278
279 func (a *ActionNode) writeTo(sb *strings.Builder) {
280 sb.WriteString("{{")
281 a.Pipe.writeTo(sb)
282 sb.WriteString("}}")
283 }
284
285 func (a *ActionNode) tree() *Tree {
286 return a.tr
287 }
288
289 func (a *ActionNode) Copy() Node {
290 return a.tr.newAction(a.Pos, a.Line, a.Pipe.CopyPipe())
291 }
292
293 // CommandNode holds a command (a pipeline inside an evaluating action).
294 type CommandNode struct {
295 NodeType
296 Pos
297 tr *Tree
298 Args []Node // Arguments in lexical order: Identifier, field, or constant.
299 }
300
301 func (t *Tree) newCommand(pos Pos) *CommandNode {
302 return &CommandNode{tr: t, NodeType: NodeCommand, Pos: pos}
303 }
304
305 func (c *CommandNode) append(arg Node) {
306 c.Args = append(c.Args, arg)
307 }
308
309 func (c *CommandNode) String() string {
310 var sb strings.Builder
311 c.writeTo(&sb)
312 return sb.String()
313 }
314
315 func (c *CommandNode) writeTo(sb *strings.Builder) {
316 for i, arg := range c.Args {
317 if i > 0 {
318 sb.WriteByte(' ')
319 }
320 if arg, ok := arg.(*PipeNode); ok {
321 sb.WriteByte('(')
322 arg.writeTo(sb)
323 sb.WriteByte(')')
324 continue
325 }
326 arg.writeTo(sb)
327 }
328 }
329
330 func (c *CommandNode) tree() *Tree {
331 return c.tr
332 }
333
334 func (c *CommandNode) Copy() Node {
335 if c == nil {
336 return c
337 }
338 n := c.tr.newCommand(c.Pos)
339 for _, c := range c.Args {
340 n.append(c.Copy())
341 }
342 return n
343 }
344
345 // IdentifierNode holds an identifier.
346 type IdentifierNode struct {
347 NodeType
348 Pos
349 tr *Tree
350 Ident string // The identifier's name.
351 }
352
353 // NewIdentifier returns a new [IdentifierNode] with the given identifier name.
354 func NewIdentifier(ident string) *IdentifierNode {
355 return &IdentifierNode{NodeType: NodeIdentifier, Ident: ident}
356 }
357
358 // SetPos sets the position. [NewIdentifier] is a public method so we can't modify its signature.
359 // Chained for convenience.
360 // TODO: fix one day?
361 func (i *IdentifierNode) SetPos(pos Pos) *IdentifierNode {
362 i.Pos = pos
363 return i
364 }
365
366 // SetTree sets the parent tree for the node. [NewIdentifier] is a public method so we can't modify its signature.
367 // Chained for convenience.
368 // TODO: fix one day?
369 func (i *IdentifierNode) SetTree(t *Tree) *IdentifierNode {
370 i.tr = t
371 return i
372 }
373
374 func (i *IdentifierNode) String() string {
375 return i.Ident
376 }
377
378 func (i *IdentifierNode) writeTo(sb *strings.Builder) {
379 sb.WriteString(i.String())
380 }
381
382 func (i *IdentifierNode) tree() *Tree {
383 return i.tr
384 }
385
386 func (i *IdentifierNode) Copy() Node {
387 return NewIdentifier(i.Ident).SetTree(i.tr).SetPos(i.Pos)
388 }
389
390 // VariableNode holds a list of variable names, possibly with chained field
391 // accesses. The dollar sign is part of the (first) name.
392 type VariableNode struct {
393 NodeType
394 Pos
395 tr *Tree
396 Ident []string // Variable name and fields in lexical order.
397 }
398
399 func (t *Tree) newVariable(pos Pos, ident string) *VariableNode {
400 return &VariableNode{tr: t, NodeType: NodeVariable, Pos: pos, Ident: strings.Split(ident, ".")}
401 }
402
403 func (v *VariableNode) String() string {
404 var sb strings.Builder
405 v.writeTo(&sb)
406 return sb.String()
407 }
408
409 func (v *VariableNode) writeTo(sb *strings.Builder) {
410 for i, id := range v.Ident {
411 if i > 0 {
412 sb.WriteByte('.')
413 }
414 sb.WriteString(id)
415 }
416 }
417
418 func (v *VariableNode) tree() *Tree {
419 return v.tr
420 }
421
422 func (v *VariableNode) Copy() Node {
423 return &VariableNode{tr: v.tr, NodeType: NodeVariable, Pos: v.Pos, Ident: append([]string{}, v.Ident...)}
424 }
425
426 // DotNode holds the special identifier '.'.
427 type DotNode struct {
428 NodeType
429 Pos
430 tr *Tree
431 }
432
433 func (t *Tree) newDot(pos Pos) *DotNode {
434 return &DotNode{tr: t, NodeType: NodeDot, Pos: pos}
435 }
436
437 func (d *DotNode) Type() NodeType {
438 // Override method on embedded NodeType for API compatibility.
439 // TODO: Not really a problem; could change API without effect but
440 // api tool complains.
441 return NodeDot
442 }
443
444 func (d *DotNode) String() string {
445 return "."
446 }
447
448 func (d *DotNode) writeTo(sb *strings.Builder) {
449 sb.WriteString(d.String())
450 }
451
452 func (d *DotNode) tree() *Tree {
453 return d.tr
454 }
455
456 func (d *DotNode) Copy() Node {
457 return d.tr.newDot(d.Pos)
458 }
459
460 // NilNode holds the special identifier 'nil' representing an untyped nil constant.
461 type NilNode struct {
462 NodeType
463 Pos
464 tr *Tree
465 }
466
467 func (t *Tree) newNil(pos Pos) *NilNode {
468 return &NilNode{tr: t, NodeType: NodeNil, Pos: pos}
469 }
470
471 func (n *NilNode) Type() NodeType {
472 // Override method on embedded NodeType for API compatibility.
473 // TODO: Not really a problem; could change API without effect but
474 // api tool complains.
475 return NodeNil
476 }
477
478 func (n *NilNode) String() string {
479 return "nil"
480 }
481
482 func (n *NilNode) writeTo(sb *strings.Builder) {
483 sb.WriteString(n.String())
484 }
485
486 func (n *NilNode) tree() *Tree {
487 return n.tr
488 }
489
490 func (n *NilNode) Copy() Node {
491 return n.tr.newNil(n.Pos)
492 }
493
494 // FieldNode holds a field (identifier starting with '.').
495 // The names may be chained ('.x.y').
496 // The period is dropped from each ident.
497 type FieldNode struct {
498 NodeType
499 Pos
500 tr *Tree
501 Ident []string // The identifiers in lexical order.
502 }
503
504 func (t *Tree) newField(pos Pos, ident string) *FieldNode {
505 return &FieldNode{tr: t, NodeType: NodeField, Pos: pos, Ident: strings.Split(ident[1:], ".")} // [1:] to drop leading period
506 }
507
508 func (f *FieldNode) String() string {
509 var sb strings.Builder
510 f.writeTo(&sb)
511 return sb.String()
512 }
513
514 func (f *FieldNode) writeTo(sb *strings.Builder) {
515 for _, id := range f.Ident {
516 sb.WriteByte('.')
517 sb.WriteString(id)
518 }
519 }
520
521 func (f *FieldNode) tree() *Tree {
522 return f.tr
523 }
524
525 func (f *FieldNode) Copy() Node {
526 return &FieldNode{tr: f.tr, NodeType: NodeField, Pos: f.Pos, Ident: append([]string{}, f.Ident...)}
527 }
528
529 // ChainNode holds a term followed by a chain of field accesses (identifier starting with '.').
530 // The names may be chained ('.x.y').
531 // The periods are dropped from each ident.
532 type ChainNode struct {
533 NodeType
534 Pos
535 tr *Tree
536 Node Node
537 Field []string // The identifiers in lexical order.
538 }
539
540 func (t *Tree) newChain(pos Pos, node Node) *ChainNode {
541 return &ChainNode{tr: t, NodeType: NodeChain, Pos: pos, Node: node}
542 }
543
544 // Add adds the named field (which should start with a period) to the end of the chain.
545 func (c *ChainNode) Add(field string) {
546 if len(field) == 0 || field[0] != '.' {
547 panic("no dot in field")
548 }
549 field = field[1:] // Remove leading dot.
550 if field == "" {
551 panic("empty field")
552 }
553 c.Field = append(c.Field, field)
554 }
555
556 func (c *ChainNode) String() string {
557 var sb strings.Builder
558 c.writeTo(&sb)
559 return sb.String()
560 }
561
562 func (c *ChainNode) writeTo(sb *strings.Builder) {
563 if _, ok := c.Node.(*PipeNode); ok {
564 sb.WriteByte('(')
565 c.Node.writeTo(sb)
566 sb.WriteByte(')')
567 } else {
568 c.Node.writeTo(sb)
569 }
570 for _, field := range c.Field {
571 sb.WriteByte('.')
572 sb.WriteString(field)
573 }
574 }
575
576 func (c *ChainNode) tree() *Tree {
577 return c.tr
578 }
579
580 func (c *ChainNode) Copy() Node {
581 return &ChainNode{tr: c.tr, NodeType: NodeChain, Pos: c.Pos, Node: c.Node, Field: append([]string{}, c.Field...)}
582 }
583
584 // BoolNode holds a boolean constant.
585 type BoolNode struct {
586 NodeType
587 Pos
588 tr *Tree
589 True bool // The value of the boolean constant.
590 }
591
592 func (t *Tree) newBool(pos Pos, true bool) *BoolNode {
593 return &BoolNode{tr: t, NodeType: NodeBool, Pos: pos, True: true}
594 }
595
596 func (b *BoolNode) String() string {
597 if b.True {
598 return "true"
599 }
600 return "false"
601 }
602
603 func (b *BoolNode) writeTo(sb *strings.Builder) {
604 sb.WriteString(b.String())
605 }
606
607 func (b *BoolNode) tree() *Tree {
608 return b.tr
609 }
610
611 func (b *BoolNode) Copy() Node {
612 return b.tr.newBool(b.Pos, b.True)
613 }
614
615 // NumberNode holds a number: signed or unsigned integer, float, or complex.
616 // The value is parsed and stored under all the types that can represent the value.
617 // This simulates in a small amount of code the behavior of Go's ideal constants.
618 type NumberNode struct {
619 NodeType
620 Pos
621 tr *Tree
622 IsInt bool // Number has an integral value.
623 IsUint bool // Number has an unsigned integral value.
624 IsFloat bool // Number has a floating-point value.
625 IsComplex bool // Number is complex.
626 Int64 int64 // The signed integer value.
627 Uint64 uint64 // The unsigned integer value.
628 Float64 float64 // The floating-point value.
629 Complex128 complex128 // The complex value.
630 Text string // The original textual representation from the input.
631 }
632
633 func (t *Tree) newNumber(pos Pos, text string, typ itemType) (*NumberNode, error) {
634 n := &NumberNode{tr: t, NodeType: NodeNumber, Pos: pos, Text: text}
635 switch typ {
636 case itemCharConstant:
637 rune, _, tail, err := strconv.UnquoteChar(text[1:], text[0])
638 if err != nil {
639 return nil, err
640 }
641 if tail != "'" {
642 return nil, fmt.Errorf("malformed character constant: %s", text)
643 }
644 n.Int64 = int64(rune)
645 n.IsInt = true
646 n.Uint64 = uint64(rune)
647 n.IsUint = true
648 n.Float64 = float64(rune) // odd but those are the rules.
649 n.IsFloat = true
650 return n, nil
651 case itemComplex:
652 // fmt.Sscan can parse the pair, so let it do the work.
653 if _, err := fmt.Sscan(text, &n.Complex128); err != nil {
654 return nil, err
655 }
656 n.IsComplex = true
657 n.simplifyComplex()
658 return n, nil
659 }
660 // Imaginary constants can only be complex unless they are zero.
661 if len(text) > 0 && text[len(text)-1] == 'i' {
662 f, err := strconv.ParseFloat(text[:len(text)-1], 64)
663 if err == nil {
664 n.IsComplex = true
665 n.Complex128 = complex(0, f)
666 n.simplifyComplex()
667 return n, nil
668 }
669 }
670 // Do integer test first so we get 0x123 etc.
671 u, err := strconv.ParseUint(text, 0, 64) // will fail for -0; fixed below.
672 if err == nil {
673 n.IsUint = true
674 n.Uint64 = u
675 }
676 i, err := strconv.ParseInt(text, 0, 64)
677 if err == nil {
678 n.IsInt = true
679 n.Int64 = i
680 if i == 0 {
681 n.IsUint = true // in case of -0.
682 n.Uint64 = u
683 }
684 }
685 // If an integer extraction succeeded, promote the float.
686 if n.IsInt {
687 n.IsFloat = true
688 n.Float64 = float64(n.Int64)
689 } else if n.IsUint {
690 n.IsFloat = true
691 n.Float64 = float64(n.Uint64)
692 } else {
693 f, err := strconv.ParseFloat(text, 64)
694 if err == nil {
695 // If we parsed it as a float but it looks like an integer,
696 // it's a huge number too large to fit in an int. Reject it.
697 if !strings.ContainsAny(text, ".eEpP") {
698 return nil, fmt.Errorf("integer overflow: %q", text)
699 }
700 n.IsFloat = true
701 n.Float64 = f
702 // If a floating-point extraction succeeded, extract the int if needed.
703 if !n.IsInt && float64(int64(f)) == f {
704 n.IsInt = true
705 n.Int64 = int64(f)
706 }
707 if !n.IsUint && float64(uint64(f)) == f {
708 n.IsUint = true
709 n.Uint64 = uint64(f)
710 }
711 }
712 }
713 if !n.IsInt && !n.IsUint && !n.IsFloat {
714 return nil, fmt.Errorf("illegal number syntax: %q", text)
715 }
716 return n, nil
717 }
718
719 // simplifyComplex pulls out any other types that are represented by the complex number.
720 // These all require that the imaginary part be zero.
721 func (n *NumberNode) simplifyComplex() {
722 n.IsFloat = imag(n.Complex128) == 0
723 if n.IsFloat {
724 n.Float64 = real(n.Complex128)
725 n.IsInt = float64(int64(n.Float64)) == n.Float64
726 if n.IsInt {
727 n.Int64 = int64(n.Float64)
728 }
729 n.IsUint = float64(uint64(n.Float64)) == n.Float64
730 if n.IsUint {
731 n.Uint64 = uint64(n.Float64)
732 }
733 }
734 }
735
736 func (n *NumberNode) String() string {
737 return n.Text
738 }
739
740 func (n *NumberNode) writeTo(sb *strings.Builder) {
741 sb.WriteString(n.String())
742 }
743
744 func (n *NumberNode) tree() *Tree {
745 return n.tr
746 }
747
748 func (n *NumberNode) Copy() Node {
749 nn := new(NumberNode)
750 *nn = *n // Easy, fast, correct.
751 return nn
752 }
753
754 // StringNode holds a string constant. The value has been "unquoted".
755 type StringNode struct {
756 NodeType
757 Pos
758 tr *Tree
759 Quoted string // The original text of the string, with quotes.
760 Text string // The string, after quote processing.
761 }
762
763 func (t *Tree) newString(pos Pos, orig, text string) *StringNode {
764 return &StringNode{tr: t, NodeType: NodeString, Pos: pos, Quoted: orig, Text: text}
765 }
766
767 func (s *StringNode) String() string {
768 return s.Quoted
769 }
770
771 func (s *StringNode) writeTo(sb *strings.Builder) {
772 sb.WriteString(s.String())
773 }
774
775 func (s *StringNode) tree() *Tree {
776 return s.tr
777 }
778
779 func (s *StringNode) Copy() Node {
780 return s.tr.newString(s.Pos, s.Quoted, s.Text)
781 }
782
783 // endNode represents an {{end}} action.
784 // It does not appear in the final parse tree.
785 type endNode struct {
786 NodeType
787 Pos
788 tr *Tree
789 }
790
791 func (t *Tree) newEnd(pos Pos) *endNode {
792 return &endNode{tr: t, NodeType: nodeEnd, Pos: pos}
793 }
794
795 func (e *endNode) String() string {
796 return "{{end}}"
797 }
798
799 func (e *endNode) writeTo(sb *strings.Builder) {
800 sb.WriteString(e.String())
801 }
802
803 func (e *endNode) tree() *Tree {
804 return e.tr
805 }
806
807 func (e *endNode) Copy() Node {
808 return e.tr.newEnd(e.Pos)
809 }
810
811 // elseNode represents an {{else}} action. Does not appear in the final tree.
812 type elseNode struct {
813 NodeType
814 Pos
815 tr *Tree
816 Line int // The line number in the input. Deprecated: Kept for compatibility.
817 }
818
819 func (t *Tree) newElse(pos Pos, line int) *elseNode {
820 return &elseNode{tr: t, NodeType: nodeElse, Pos: pos, Line: line}
821 }
822
823 func (e *elseNode) Type() NodeType {
824 return nodeElse
825 }
826
827 func (e *elseNode) String() string {
828 return "{{else}}"
829 }
830
831 func (e *elseNode) writeTo(sb *strings.Builder) {
832 sb.WriteString(e.String())
833 }
834
835 func (e *elseNode) tree() *Tree {
836 return e.tr
837 }
838
839 func (e *elseNode) Copy() Node {
840 return e.tr.newElse(e.Pos, e.Line)
841 }
842
843 // BranchNode is the common representation of if, range, and with.
844 type BranchNode struct {
845 NodeType
846 Pos
847 tr *Tree
848 Line int // The line number in the input. Deprecated: Kept for compatibility.
849 Pipe *PipeNode // The pipeline to be evaluated.
850 List *ListNode // What to execute if the value is non-empty.
851 ElseList *ListNode // What to execute if the value is empty (nil if absent).
852 }
853
854 func (b *BranchNode) String() string {
855 var sb strings.Builder
856 b.writeTo(&sb)
857 return sb.String()
858 }
859
860 func (b *BranchNode) writeTo(sb *strings.Builder) {
861 name := ""
862 switch b.NodeType {
863 case NodeIf:
864 name = "if"
865 case NodeRange:
866 name = "range"
867 case NodeWith:
868 name = "with"
869 default:
870 panic("unknown branch type")
871 }
872 sb.WriteString("{{")
873 sb.WriteString(name)
874 sb.WriteByte(' ')
875 b.Pipe.writeTo(sb)
876 sb.WriteString("}}")
877 b.List.writeTo(sb)
878 if b.ElseList != nil {
879 sb.WriteString("{{else}}")
880 b.ElseList.writeTo(sb)
881 }
882 sb.WriteString("{{end}}")
883 }
884
885 func (b *BranchNode) tree() *Tree {
886 return b.tr
887 }
888
889 func (b *BranchNode) Copy() Node {
890 switch b.NodeType {
891 case NodeIf:
892 return b.tr.newIf(b.Pos, b.Line, b.Pipe, b.List, b.ElseList)
893 case NodeRange:
894 return b.tr.newRange(b.Pos, b.Line, b.Pipe, b.List, b.ElseList)
895 case NodeWith:
896 return b.tr.newWith(b.Pos, b.Line, b.Pipe, b.List, b.ElseList)
897 default:
898 panic("unknown branch type")
899 }
900 }
901
902 // IfNode represents an {{if}} action and its commands.
903 type IfNode struct {
904 BranchNode
905 }
906
907 func (t *Tree) newIf(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *IfNode {
908 return &IfNode{BranchNode{tr: t, NodeType: NodeIf, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
909 }
910
911 func (i *IfNode) Copy() Node {
912 return i.tr.newIf(i.Pos, i.Line, i.Pipe.CopyPipe(), i.List.CopyList(), i.ElseList.CopyList())
913 }
914
915 // BreakNode represents a {{break}} action.
916 type BreakNode struct {
917 tr *Tree
918 NodeType
919 Pos
920 Line int
921 }
922
923 func (t *Tree) newBreak(pos Pos, line int) *BreakNode {
924 return &BreakNode{tr: t, NodeType: NodeBreak, Pos: pos, Line: line}
925 }
926
927 func (b *BreakNode) Copy() Node { return b.tr.newBreak(b.Pos, b.Line) }
928 func (b *BreakNode) String() string { return "{{break}}" }
929 func (b *BreakNode) tree() *Tree { return b.tr }
930 func (b *BreakNode) writeTo(sb *strings.Builder) { sb.WriteString("{{break}}") }
931
932 // ContinueNode represents a {{continue}} action.
933 type ContinueNode struct {
934 tr *Tree
935 NodeType
936 Pos
937 Line int
938 }
939
940 func (t *Tree) newContinue(pos Pos, line int) *ContinueNode {
941 return &ContinueNode{tr: t, NodeType: NodeContinue, Pos: pos, Line: line}
942 }
943
944 func (c *ContinueNode) Copy() Node { return c.tr.newContinue(c.Pos, c.Line) }
945 func (c *ContinueNode) String() string { return "{{continue}}" }
946 func (c *ContinueNode) tree() *Tree { return c.tr }
947 func (c *ContinueNode) writeTo(sb *strings.Builder) { sb.WriteString("{{continue}}") }
948
949 // RangeNode represents a {{range}} action and its commands.
950 type RangeNode struct {
951 BranchNode
952 }
953
954 func (t *Tree) newRange(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *RangeNode {
955 return &RangeNode{BranchNode{tr: t, NodeType: NodeRange, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
956 }
957
958 func (r *RangeNode) Copy() Node {
959 return r.tr.newRange(r.Pos, r.Line, r.Pipe.CopyPipe(), r.List.CopyList(), r.ElseList.CopyList())
960 }
961
962 // WithNode represents a {{with}} action and its commands.
963 type WithNode struct {
964 BranchNode
965 }
966
967 func (t *Tree) newWith(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *WithNode {
968 return &WithNode{BranchNode{tr: t, NodeType: NodeWith, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
969 }
970
971 func (w *WithNode) Copy() Node {
972 return w.tr.newWith(w.Pos, w.Line, w.Pipe.CopyPipe(), w.List.CopyList(), w.ElseList.CopyList())
973 }
974
975 // TemplateNode represents a {{template}} action.
976 type TemplateNode struct {
977 NodeType
978 Pos
979 tr *Tree
980 Line int // The line number in the input. Deprecated: Kept for compatibility.
981 Name string // The name of the template (unquoted).
982 Pipe *PipeNode // The command to evaluate as dot for the template.
983 }
984
985 func (t *Tree) newTemplate(pos Pos, line int, name string, pipe *PipeNode) *TemplateNode {
986 return &TemplateNode{tr: t, NodeType: NodeTemplate, Pos: pos, Line: line, Name: name, Pipe: pipe}
987 }
988
989 func (t *TemplateNode) String() string {
990 var sb strings.Builder
991 t.writeTo(&sb)
992 return sb.String()
993 }
994
995 func (t *TemplateNode) writeTo(sb *strings.Builder) {
996 sb.WriteString("{{template ")
997 sb.WriteString(strconv.Quote(t.Name))
998 if t.Pipe != nil {
999 sb.WriteByte(' ')
1000 t.Pipe.writeTo(sb)
1001 }
1002 sb.WriteString("}}")
1003 }
1004
1005 func (t *TemplateNode) tree() *Tree {
1006 return t.tr
1007 }
1008
1009 func (t *TemplateNode) Copy() Node {
1010 return t.tr.newTemplate(t.Pos, t.Line, t.Name, t.Pipe.CopyPipe())
1011 }