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) 75 76// Nodes. 77 78// ListNode holds a sequence of nodes. 79type ListNode struct { 80 NodeType 81 Pos 82 tr *Tree 83 Nodes []Node // The element nodes in lexical order. 84} 85 86func (t *Tree) newList(pos Pos) *ListNode { 87 return &ListNode{tr: t, NodeType: NodeList, Pos: pos} 88} 89 90func (l *ListNode) append(n Node) { 91 l.Nodes = append(l.Nodes, n) 92} 93 94func (l *ListNode) tree() *Tree { 95 return l.tr 96} 97 98func (l *ListNode) String() string { 99 var sb strings.Builder 100 l.writeTo(&sb) 101 return sb.String() 102} 103 104func (l *ListNode) writeTo(sb *strings.Builder) { 105 for _, n := range l.Nodes { 106 n.writeTo(sb) 107 } 108} 109 110func (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 121func (l *ListNode) Copy() Node { 122 return l.CopyList() 123} 124 125// TextNode holds plain text. 126type TextNode struct { 127 NodeType 128 Pos 129 tr *Tree 130 Text []byte // The text; may span newlines. 131} 132 133func (t *Tree) newText(pos Pos, text string) *TextNode { 134 return &TextNode{tr: t, NodeType: NodeText, Pos: pos, Text: []byte(text)} 135} 136 137func (t *TextNode) String() string { 138 return fmt.Sprintf(textFormat, t.Text) 139} 140 141func (t *TextNode) writeTo(sb *strings.Builder) { 142 sb.WriteString(t.String()) 143} 144 145func (t *TextNode) tree() *Tree { 146 return t.tr 147} 148 149func (t *TextNode) Copy() Node { 150 return &TextNode{tr: t.tr, NodeType: NodeText, Pos: t.Pos, Text: append([]byte{}, t.Text...)} 151} 152 153// CommentNode holds a comment. 154type CommentNode struct { 155 NodeType 156 Pos 157 tr *Tree 158 Text string // Comment text. 159} 160 161func (t *Tree) newComment(pos Pos, text string) *CommentNode { 162 return &CommentNode{tr: t, NodeType: NodeComment, Pos: pos, Text: text} 163} 164 165func (c *CommentNode) String() string { 166 var sb strings.Builder 167 c.writeTo(&sb) 168 return sb.String() 169} 170 171func (c *CommentNode) writeTo(sb *strings.Builder) { 172 sb.WriteString("{{") 173 sb.WriteString(c.Text) 174 sb.WriteString("}}") 175} 176 177func (c *CommentNode) tree() *Tree { 178 return c.tr 179} 180 181func (c *CommentNode) Copy() Node { 182 return &CommentNode{tr: c.tr, NodeType: NodeComment, Pos: c.Pos, Text: c.Text} 183} 184 185// PipeNode holds a pipeline with optional declaration 186type PipeNode struct { 187 NodeType 188 Pos 189 tr *Tree 190 Line int // The line number in the input. Deprecated: Kept for compatibility. 191 IsAssign bool // The variables are being assigned, not declared. 192 Decl []*VariableNode // Variables in lexical order. 193 Cmds []*CommandNode // The commands in lexical order. 194} 195 196func (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 200func (p *PipeNode) append(command *CommandNode) { 201 p.Cmds = append(p.Cmds, command) 202} 203 204func (p *PipeNode) String() string { 205 var sb strings.Builder 206 p.writeTo(&sb) 207 return sb.String() 208} 209 210func (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 228func (p *PipeNode) tree() *Tree { 229 return p.tr 230} 231 232func (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 248func (p *PipeNode) Copy() Node { 249 return p.CopyPipe() 250} 251 252// ActionNode holds an action (something bounded by delimiters). 253// Control actions have their own nodes; ActionNode represents simple 254// ones such as field evaluations and parenthesized pipelines. 255type ActionNode struct { 256 NodeType 257 Pos 258 tr *Tree 259 Line int // The line number in the input. Deprecated: Kept for compatibility. 260 Pipe *PipeNode // The pipeline in the action. 261} 262 263func (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 267func (a *ActionNode) String() string { 268 var sb strings.Builder 269 a.writeTo(&sb) 270 return sb.String() 271} 272 273func (a *ActionNode) writeTo(sb *strings.Builder) { 274 sb.WriteString("{{") 275 a.Pipe.writeTo(sb) 276 sb.WriteString("}}") 277} 278 279func (a *ActionNode) tree() *Tree { 280 return a.tr 281} 282 283func (a *ActionNode) Copy() Node { 284 return a.tr.newAction(a.Pos, a.Line, a.Pipe.CopyPipe()) 285 286} 287 288// CommandNode holds a command (a pipeline inside an evaluating action). 289type CommandNode struct { 290 NodeType 291 Pos 292 tr *Tree 293 Args []Node // Arguments in lexical order: Identifier, field, or constant. 294} 295 296func (t *Tree) newCommand(pos Pos) *CommandNode { 297 return &CommandNode{tr: t, NodeType: NodeCommand, Pos: pos} 298} 299 300func (c *CommandNode) append(arg Node) { 301 c.Args = append(c.Args, arg) 302} 303 304func (c *CommandNode) String() string { 305 var sb strings.Builder 306 c.writeTo(&sb) 307 return sb.String() 308} 309 310func (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 325func (c *CommandNode) tree() *Tree { 326 return c.tr 327} 328 329func (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// IdentifierNode holds an identifier. 341type IdentifierNode struct { 342 NodeType 343 Pos 344 tr *Tree 345 Ident string // The identifier's name. 346} 347 348// NewIdentifier returns a new IdentifierNode with the given identifier name. 349func NewIdentifier(ident string) *IdentifierNode { 350 return &IdentifierNode{NodeType: NodeIdentifier, Ident: ident} 351} 352 353// SetPos sets the position. NewIdentifier is a public method so we can't modify its signature. 354// Chained for convenience. 355// TODO: fix one day? 356func (i *IdentifierNode) SetPos(pos Pos) *IdentifierNode { 357 i.Pos = pos 358 return i 359} 360 361// SetTree sets the parent tree for the node. NewIdentifier is a public method so we can't modify its signature. 362// Chained for convenience. 363// TODO: fix one day? 364func (i *IdentifierNode) SetTree(t *Tree) *IdentifierNode { 365 i.tr = t 366 return i 367} 368 369func (i *IdentifierNode) String() string { 370 return i.Ident 371} 372 373func (i *IdentifierNode) writeTo(sb *strings.Builder) { 374 sb.WriteString(i.String()) 375} 376 377func (i *IdentifierNode) tree() *Tree { 378 return i.tr 379} 380 381func (i *IdentifierNode) Copy() Node { 382 return NewIdentifier(i.Ident).SetTree(i.tr).SetPos(i.Pos) 383} 384 385// VariableNode holds a list of variable names, possibly with chained field 386// accesses. The dollar sign is part of the (first) name. 387type VariableNode struct { 388 NodeType 389 Pos 390 tr *Tree 391 Ident []string // Variable name and fields in lexical order. 392} 393 394func (t *Tree) newVariable(pos Pos, ident string) *VariableNode { 395 return &VariableNode{tr: t, NodeType: NodeVariable, Pos: pos, Ident: strings.Split(ident, ".")} 396} 397 398func (v *VariableNode) String() string { 399 var sb strings.Builder 400 v.writeTo(&sb) 401 return sb.String() 402} 403 404func (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 413func (v *VariableNode) tree() *Tree { 414 return v.tr 415} 416 417func (v *VariableNode) Copy() Node { 418 return &VariableNode{tr: v.tr, NodeType: NodeVariable, Pos: v.Pos, Ident: append([]string{}, v.Ident...)} 419} 420 421// DotNode holds the special identifier '.'. 422type DotNode struct { 423 NodeType 424 Pos 425 tr *Tree 426} 427 428func (t *Tree) newDot(pos Pos) *DotNode { 429 return &DotNode{tr: t, NodeType: NodeDot, Pos: pos} 430} 431 432func (d *DotNode) Type() NodeType { 433 // Override method on embedded NodeType for API compatibility. 434 // TODO: Not really a problem; could change API without effect but 435 // api tool complains. 436 return NodeDot 437} 438 439func (d *DotNode) String() string { 440 return "." 441} 442 443func (d *DotNode) writeTo(sb *strings.Builder) { 444 sb.WriteString(d.String()) 445} 446 447func (d *DotNode) tree() *Tree { 448 return d.tr 449} 450 451func (d *DotNode) Copy() Node { 452 return d.tr.newDot(d.Pos) 453} 454 455// NilNode holds the special identifier 'nil' representing an untyped nil constant. 456type NilNode struct { 457 NodeType 458 Pos 459 tr *Tree 460} 461 462func (t *Tree) newNil(pos Pos) *NilNode { 463 return &NilNode{tr: t, NodeType: NodeNil, Pos: pos} 464} 465 466func (n *NilNode) Type() NodeType { 467 // Override method on embedded NodeType for API compatibility. 468 // TODO: Not really a problem; could change API without effect but 469 // api tool complains. 470 return NodeNil 471} 472 473func (n *NilNode) String() string { 474 return "nil" 475} 476 477func (n *NilNode) writeTo(sb *strings.Builder) { 478 sb.WriteString(n.String()) 479} 480 481func (n *NilNode) tree() *Tree { 482 return n.tr 483} 484 485func (n *NilNode) Copy() Node { 486 return n.tr.newNil(n.Pos) 487} 488 489// FieldNode holds a field (identifier starting with '.'). 490// The names may be chained ('.x.y'). 491// The period is dropped from each ident. 492type FieldNode struct { 493 NodeType 494 Pos 495 tr *Tree 496 Ident []string // The identifiers in lexical order. 497} 498 499func (t *Tree) newField(pos Pos, ident string) *FieldNode { 500 return &FieldNode{tr: t, NodeType: NodeField, Pos: pos, Ident: strings.Split(ident[1:], ".")} // [1:] to drop leading period 501} 502 503func (f *FieldNode) String() string { 504 var sb strings.Builder 505 f.writeTo(&sb) 506 return sb.String() 507} 508 509func (f *FieldNode) writeTo(sb *strings.Builder) { 510 for _, id := range f.Ident { 511 sb.WriteByte('.') 512 sb.WriteString(id) 513 } 514} 515 516func (f *FieldNode) tree() *Tree { 517 return f.tr 518} 519 520func (f *FieldNode) Copy() Node { 521 return &FieldNode{tr: f.tr, NodeType: NodeField, Pos: f.Pos, Ident: append([]string{}, f.Ident...)} 522} 523 524// ChainNode holds a term followed by a chain of field accesses (identifier starting with '.'). 525// The names may be chained ('.x.y'). 526// The periods are dropped from each ident. 527type ChainNode struct { 528 NodeType 529 Pos 530 tr *Tree 531 Node Node 532 Field []string // The identifiers in lexical order. 533} 534 535func (t *Tree) newChain(pos Pos, node Node) *ChainNode { 536 return &ChainNode{tr: t, NodeType: NodeChain, Pos: pos, Node: node} 537} 538 539// Add adds the named field (which should start with a period) to the end of the chain. 540func (c *ChainNode) Add(field string) { 541 if len(field) == 0 || field[0] != '.' { 542 panic("no dot in field") 543 } 544 field = field[1:] // Remove leading dot. 545 if field == "" { 546 panic("empty field") 547 } 548 c.Field = append(c.Field, field) 549} 550 551func (c *ChainNode) String() string { 552 var sb strings.Builder 553 c.writeTo(&sb) 554 return sb.String() 555} 556 557func (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 571func (c *ChainNode) tree() *Tree { 572 return c.tr 573} 574 575func (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// BoolNode holds a boolean constant. 580type BoolNode struct { 581 NodeType 582 Pos 583 tr *Tree 584 True bool // The value of the boolean constant. 585} 586 587func (t *Tree) newBool(pos Pos, true bool) *BoolNode { 588 return &BoolNode{tr: t, NodeType: NodeBool, Pos: pos, True: true} 589} 590 591func (b *BoolNode) String() string { 592 if b.True { 593 return "true" 594 } 595 return "false" 596} 597 598func (b *BoolNode) writeTo(sb *strings.Builder) { 599 sb.WriteString(b.String()) 600} 601 602func (b *BoolNode) tree() *Tree { 603 return b.tr 604} 605 606func (b *BoolNode) Copy() Node { 607 return b.tr.newBool(b.Pos, b.True) 608} 609 610// NumberNode holds a number: signed or unsigned integer, float, or complex. 611// The value is parsed and stored under all the types that can represent the value. 612// This simulates in a small amount of code the behavior of Go's ideal constants. 613type NumberNode struct { 614 NodeType 615 Pos 616 tr *Tree 617 IsInt bool // Number has an integral value. 618 IsUint bool // Number has an unsigned integral value. 619 IsFloat bool // Number has a floating-point value. 620 IsComplex bool // Number is complex. 621 Int64 int64 // The signed integer value. 622 Uint64 uint64 // The unsigned integer value. 623 Float64 float64 // The floating-point value. 624 Complex128 complex128 // The complex value. 625 Text string // The original textual representation from the input. 626} 627 628func (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) // odd but those are the rules. 644 n.IsFloat = true 645 return n, nil 646 case itemComplex: 647 // fmt.Sscan can parse the pair, so let it do the work. 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 // Imaginary constants can only be complex unless they are zero. 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 // Do integer test first so we get 0x123 etc. 666 u, err := strconv.ParseUint(text, 0, 64) // will fail for -0; fixed below. 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 // in case of -0. 677 n.Uint64 = u 678 } 679 } 680 // If an integer extraction succeeded, promote the float. 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 // If we parsed it as a float but it looks like an integer, 691 // it's a huge number too large to fit in an int. Reject it. 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 // If a floating-point extraction succeeded, extract the int if needed. 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// simplifyComplex pulls out any other types that are represented by the complex number. 715// These all require that the imaginary part be zero. 716func (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 731func (n *NumberNode) String() string { 732 return n.Text 733} 734 735func (n *NumberNode) writeTo(sb *strings.Builder) { 736 sb.WriteString(n.String()) 737} 738 739func (n *NumberNode) tree() *Tree { 740 return n.tr 741} 742 743func (n *NumberNode) Copy() Node { 744 nn := new(NumberNode) 745 *nn = *n // Easy, fast, correct. 746 return nn 747} 748 749// StringNode holds a string constant. The value has been "unquoted". 750type StringNode struct { 751 NodeType 752 Pos 753 tr *Tree 754 Quoted string // The original text of the string, with quotes. 755 Text string // The string, after quote processing. 756} 757 758func (t *Tree) newString(pos Pos, orig, text string) *StringNode { 759 return &StringNode{tr: t, NodeType: NodeString, Pos: pos, Quoted: orig, Text: text} 760} 761 762func (s *StringNode) String() string { 763 return s.Quoted 764} 765 766func (s *StringNode) writeTo(sb *strings.Builder) { 767 sb.WriteString(s.String()) 768} 769 770func (s *StringNode) tree() *Tree { 771 return s.tr 772} 773 774func (s *StringNode) Copy() Node { 775 return s.tr.newString(s.Pos, s.Quoted, s.Text) 776} 777 778// endNode represents an {{end}} action. 779// It does not appear in the final parse tree. 780type endNode struct { 781 NodeType 782 Pos 783 tr *Tree 784} 785 786func (t *Tree) newEnd(pos Pos) *endNode { 787 return &endNode{tr: t, NodeType: nodeEnd, Pos: pos} 788} 789 790func (e *endNode) String() string { 791 return "{{end}}" 792} 793 794func (e *endNode) writeTo(sb *strings.Builder) { 795 sb.WriteString(e.String()) 796} 797 798func (e *endNode) tree() *Tree { 799 return e.tr 800} 801 802func (e *endNode) Copy() Node { 803 return e.tr.newEnd(e.Pos) 804} 805 806// elseNode represents an {{else}} action. Does not appear in the final tree. 807type elseNode struct { 808 NodeType 809 Pos 810 tr *Tree 811 Line int // The line number in the input. Deprecated: Kept for compatibility. 812} 813 814func (t *Tree) newElse(pos Pos, line int) *elseNode { 815 return &elseNode{tr: t, NodeType: nodeElse, Pos: pos, Line: line} 816} 817 818func (e *elseNode) Type() NodeType { 819 return nodeElse 820} 821 822func (e *elseNode) String() string { 823 return "{{else}}" 824} 825 826func (e *elseNode) writeTo(sb *strings.Builder) { 827 sb.WriteString(e.String()) 828} 829 830func (e *elseNode) tree() *Tree { 831 return e.tr 832} 833 834func (e *elseNode) Copy() Node { 835 return e.tr.newElse(e.Pos, e.Line) 836} 837 838// BranchNode is the common representation of if, range, and with. 839type BranchNode struct { 840 NodeType 841 Pos 842 tr *Tree 843 Line int // The line number in the input. Deprecated: Kept for compatibility. 844 Pipe *PipeNode // The pipeline to be evaluated. 845 List *ListNode // What to execute if the value is non-empty. 846 ElseList *ListNode // What to execute if the value is empty (nil if absent). 847} 848 849func (b *BranchNode) String() string { 850 var sb strings.Builder 851 b.writeTo(&sb) 852 return sb.String() 853} 854 855func (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 880func (b *BranchNode) tree() *Tree { 881 return b.tr 882} 883 884func (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// IfNode represents an {{if}} action and its commands. 898type IfNode struct { 899 BranchNode 900} 901 902func (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 906func (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// RangeNode represents a {{range}} action and its commands. 911type RangeNode struct { 912 BranchNode 913} 914 915func (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 919func (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// WithNode represents a {{with}} action and its commands. 924type WithNode struct { 925 BranchNode 926} 927 928func (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 932func (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// TemplateNode represents a {{template}} action. 937type TemplateNode struct { 938 NodeType 939 Pos 940 tr *Tree 941 Line int // The line number in the input. Deprecated: Kept for compatibility. 942 Name string // The name of the template (unquoted). 943 Pipe *PipeNode // The command to evaluate as dot for the template. 944} 945 946func (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 950func (t *TemplateNode) String() string { 951 var sb strings.Builder 952 t.writeTo(&sb) 953 return sb.String() 954} 955 956func (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 966func (t *TemplateNode) tree() *Tree { 967 return t.tr 968} 969 970func (t *TemplateNode) Copy() Node { 971 return t.tr.newTemplate(t.Pos, t.Line, t.Name, t.Pipe.CopyPipe()) 972} 973