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