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
7package parse
8
9import (
10	"fmt"
11	"strconv"
12	"strings"
13)
14
15var 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.
20type 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.
36type NodeType int
37
38// Pos represents a byte position in the original input text from which
39// this template was parsed.
40type Pos int
41
42func (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.
48func (t NodeType) Type() NodeType {
49	return t
50}
51
52const (
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.
81type ListNode struct {
82	NodeType
83	Pos
84	tr    *Tree
85	Nodes []Node // The element nodes in lexical order.
86}
87
88func (t *Tree) newList(pos Pos) *ListNode {
89	return &ListNode{tr: t, NodeType: NodeList, Pos: pos}
90}
91
92func (l *ListNode) append(n Node) {
93	l.Nodes = append(l.Nodes, n)
94}
95
96func (l *ListNode) tree() *Tree {
97	return l.tr
98}
99
100func (l *ListNode) String() string {
101	var sb strings.Builder
102	l.writeTo(&sb)
103	return sb.String()
104}
105
106func (l *ListNode) writeTo(sb *strings.Builder) {
107	for _, n := range l.Nodes {
108		n.writeTo(sb)
109	}
110}
111
112func (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
123func (l *ListNode) Copy() Node {
124	return l.CopyList()
125}
126
127// TextNode holds plain text.
128type TextNode struct {
129	NodeType
130	Pos
131	tr   *Tree
132	Text []byte // The text; may span newlines.
133}
134
135func (t *Tree) newText(pos Pos, text string) *TextNode {
136	return &TextNode{tr: t, NodeType: NodeText, Pos: pos, Text: []byte(text)}
137}
138
139func (t *TextNode) String() string {
140	return fmt.Sprintf(textFormat, t.Text)
141}
142
143func (t *TextNode) writeTo(sb *strings.Builder) {
144	sb.WriteString(t.String())
145}
146
147func (t *TextNode) tree() *Tree {
148	return t.tr
149}
150
151func (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.
156type CommentNode struct {
157	NodeType
158	Pos
159	tr   *Tree
160	Text string // Comment text.
161}
162
163func (t *Tree) newComment(pos Pos, text string) *CommentNode {
164	return &CommentNode{tr: t, NodeType: NodeComment, Pos: pos, Text: text}
165}
166
167func (c *CommentNode) String() string {
168	var sb strings.Builder
169	c.writeTo(&sb)
170	return sb.String()
171}
172
173func (c *CommentNode) writeTo(sb *strings.Builder) {
174	sb.WriteString("{{")
175	sb.WriteString(c.Text)
176	sb.WriteString("}}")
177}
178
179func (c *CommentNode) tree() *Tree {
180	return c.tr
181}
182
183func (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
188type 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
198func (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
202func (p *PipeNode) append(command *CommandNode) {
203	p.Cmds = append(p.Cmds, command)
204}
205
206func (p *PipeNode) String() string {
207	var sb strings.Builder
208	p.writeTo(&sb)
209	return sb.String()
210}
211
212func (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
234func (p *PipeNode) tree() *Tree {
235	return p.tr
236}
237
238func (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
254func (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.
261type 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
269func (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
273func (a *ActionNode) String() string {
274	var sb strings.Builder
275	a.writeTo(&sb)
276	return sb.String()
277}
278
279func (a *ActionNode) writeTo(sb *strings.Builder) {
280	sb.WriteString("{{")
281	a.Pipe.writeTo(sb)
282	sb.WriteString("}}")
283}
284
285func (a *ActionNode) tree() *Tree {
286	return a.tr
287}
288
289func (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).
294type CommandNode struct {
295	NodeType
296	Pos
297	tr   *Tree
298	Args []Node // Arguments in lexical order: Identifier, field, or constant.
299}
300
301func (t *Tree) newCommand(pos Pos) *CommandNode {
302	return &CommandNode{tr: t, NodeType: NodeCommand, Pos: pos}
303}
304
305func (c *CommandNode) append(arg Node) {
306	c.Args = append(c.Args, arg)
307}
308
309func (c *CommandNode) String() string {
310	var sb strings.Builder
311	c.writeTo(&sb)
312	return sb.String()
313}
314
315func (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
330func (c *CommandNode) tree() *Tree {
331	return c.tr
332}
333
334func (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.
346type 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.
354func 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?
361func (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?
369func (i *IdentifierNode) SetTree(t *Tree) *IdentifierNode {
370	i.tr = t
371	return i
372}
373
374func (i *IdentifierNode) String() string {
375	return i.Ident
376}
377
378func (i *IdentifierNode) writeTo(sb *strings.Builder) {
379	sb.WriteString(i.String())
380}
381
382func (i *IdentifierNode) tree() *Tree {
383	return i.tr
384}
385
386func (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.
392type VariableNode struct {
393	NodeType
394	Pos
395	tr    *Tree
396	Ident []string // Variable name and fields in lexical order.
397}
398
399func (t *Tree) newVariable(pos Pos, ident string) *VariableNode {
400	return &VariableNode{tr: t, NodeType: NodeVariable, Pos: pos, Ident: strings.Split(ident, ".")}
401}
402
403func (v *VariableNode) String() string {
404	var sb strings.Builder
405	v.writeTo(&sb)
406	return sb.String()
407}
408
409func (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
418func (v *VariableNode) tree() *Tree {
419	return v.tr
420}
421
422func (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 '.'.
427type DotNode struct {
428	NodeType
429	Pos
430	tr *Tree
431}
432
433func (t *Tree) newDot(pos Pos) *DotNode {
434	return &DotNode{tr: t, NodeType: NodeDot, Pos: pos}
435}
436
437func (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
444func (d *DotNode) String() string {
445	return "."
446}
447
448func (d *DotNode) writeTo(sb *strings.Builder) {
449	sb.WriteString(d.String())
450}
451
452func (d *DotNode) tree() *Tree {
453	return d.tr
454}
455
456func (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.
461type NilNode struct {
462	NodeType
463	Pos
464	tr *Tree
465}
466
467func (t *Tree) newNil(pos Pos) *NilNode {
468	return &NilNode{tr: t, NodeType: NodeNil, Pos: pos}
469}
470
471func (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
478func (n *NilNode) String() string {
479	return "nil"
480}
481
482func (n *NilNode) writeTo(sb *strings.Builder) {
483	sb.WriteString(n.String())
484}
485
486func (n *NilNode) tree() *Tree {
487	return n.tr
488}
489
490func (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.
497type FieldNode struct {
498	NodeType
499	Pos
500	tr    *Tree
501	Ident []string // The identifiers in lexical order.
502}
503
504func (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
508func (f *FieldNode) String() string {
509	var sb strings.Builder
510	f.writeTo(&sb)
511	return sb.String()
512}
513
514func (f *FieldNode) writeTo(sb *strings.Builder) {
515	for _, id := range f.Ident {
516		sb.WriteByte('.')
517		sb.WriteString(id)
518	}
519}
520
521func (f *FieldNode) tree() *Tree {
522	return f.tr
523}
524
525func (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.
532type ChainNode struct {
533	NodeType
534	Pos
535	tr    *Tree
536	Node  Node
537	Field []string // The identifiers in lexical order.
538}
539
540func (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.
545func (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
556func (c *ChainNode) String() string {
557	var sb strings.Builder
558	c.writeTo(&sb)
559	return sb.String()
560}
561
562func (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
576func (c *ChainNode) tree() *Tree {
577	return c.tr
578}
579
580func (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.
585type BoolNode struct {
586	NodeType
587	Pos
588	tr   *Tree
589	True bool // The value of the boolean constant.
590}
591
592func (t *Tree) newBool(pos Pos, true bool) *BoolNode {
593	return &BoolNode{tr: t, NodeType: NodeBool, Pos: pos, True: true}
594}
595
596func (b *BoolNode) String() string {
597	if b.True {
598		return "true"
599	}
600	return "false"
601}
602
603func (b *BoolNode) writeTo(sb *strings.Builder) {
604	sb.WriteString(b.String())
605}
606
607func (b *BoolNode) tree() *Tree {
608	return b.tr
609}
610
611func (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.
618type 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
633func (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.
721func (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
736func (n *NumberNode) String() string {
737	return n.Text
738}
739
740func (n *NumberNode) writeTo(sb *strings.Builder) {
741	sb.WriteString(n.String())
742}
743
744func (n *NumberNode) tree() *Tree {
745	return n.tr
746}
747
748func (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".
755type 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
763func (t *Tree) newString(pos Pos, orig, text string) *StringNode {
764	return &StringNode{tr: t, NodeType: NodeString, Pos: pos, Quoted: orig, Text: text}
765}
766
767func (s *StringNode) String() string {
768	return s.Quoted
769}
770
771func (s *StringNode) writeTo(sb *strings.Builder) {
772	sb.WriteString(s.String())
773}
774
775func (s *StringNode) tree() *Tree {
776	return s.tr
777}
778
779func (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.
785type endNode struct {
786	NodeType
787	Pos
788	tr *Tree
789}
790
791func (t *Tree) newEnd(pos Pos) *endNode {
792	return &endNode{tr: t, NodeType: nodeEnd, Pos: pos}
793}
794
795func (e *endNode) String() string {
796	return "{{end}}"
797}
798
799func (e *endNode) writeTo(sb *strings.Builder) {
800	sb.WriteString(e.String())
801}
802
803func (e *endNode) tree() *Tree {
804	return e.tr
805}
806
807func (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.
812type elseNode struct {
813	NodeType
814	Pos
815	tr   *Tree
816	Line int // The line number in the input. Deprecated: Kept for compatibility.
817}
818
819func (t *Tree) newElse(pos Pos, line int) *elseNode {
820	return &elseNode{tr: t, NodeType: nodeElse, Pos: pos, Line: line}
821}
822
823func (e *elseNode) Type() NodeType {
824	return nodeElse
825}
826
827func (e *elseNode) String() string {
828	return "{{else}}"
829}
830
831func (e *elseNode) writeTo(sb *strings.Builder) {
832	sb.WriteString(e.String())
833}
834
835func (e *elseNode) tree() *Tree {
836	return e.tr
837}
838
839func (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.
844type 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
854func (b *BranchNode) String() string {
855	var sb strings.Builder
856	b.writeTo(&sb)
857	return sb.String()
858}
859
860func (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
885func (b *BranchNode) tree() *Tree {
886	return b.tr
887}
888
889func (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.
903type IfNode struct {
904	BranchNode
905}
906
907func (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
911func (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.
916type BreakNode struct {
917	tr *Tree
918	NodeType
919	Pos
920	Line int
921}
922
923func (t *Tree) newBreak(pos Pos, line int) *BreakNode {
924	return &BreakNode{tr: t, NodeType: NodeBreak, Pos: pos, Line: line}
925}
926
927func (b *BreakNode) Copy() Node                  { return b.tr.newBreak(b.Pos, b.Line) }
928func (b *BreakNode) String() string              { return "{{break}}" }
929func (b *BreakNode) tree() *Tree                 { return b.tr }
930func (b *BreakNode) writeTo(sb *strings.Builder) { sb.WriteString("{{break}}") }
931
932// ContinueNode represents a {{continue}} action.
933type ContinueNode struct {
934	tr *Tree
935	NodeType
936	Pos
937	Line int
938}
939
940func (t *Tree) newContinue(pos Pos, line int) *ContinueNode {
941	return &ContinueNode{tr: t, NodeType: NodeContinue, Pos: pos, Line: line}
942}
943
944func (c *ContinueNode) Copy() Node                  { return c.tr.newContinue(c.Pos, c.Line) }
945func (c *ContinueNode) String() string              { return "{{continue}}" }
946func (c *ContinueNode) tree() *Tree                 { return c.tr }
947func (c *ContinueNode) writeTo(sb *strings.Builder) { sb.WriteString("{{continue}}") }
948
949// RangeNode represents a {{range}} action and its commands.
950type RangeNode struct {
951	BranchNode
952}
953
954func (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
958func (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.
963type WithNode struct {
964	BranchNode
965}
966
967func (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
971func (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.
976type 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
985func (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
989func (t *TemplateNode) String() string {
990	var sb strings.Builder
991	t.writeTo(&sb)
992	return sb.String()
993}
994
995func (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
1005func (t *TemplateNode) tree() *Tree {
1006	return t.tr
1007}
1008
1009func (t *TemplateNode) Copy() Node {
1010	return t.tr.newTemplate(t.Pos, t.Line, t.Name, t.Pipe.CopyPipe())
1011}
1012