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