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 "bytes" 11 "fmt" 12 "strconv" 13 "strings" 14) 15 16var 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. 21type 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. 35type NodeType int 36 37// Pos represents a byte position in the original input text from which 38// this template was parsed. 39type Pos int 40 41func (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. 47func (t NodeType) Type() NodeType { 48 return t 49} 50 51const ( 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. 77type ListNode struct { 78 NodeType 79 Pos 80 tr *Tree 81 Nodes []Node // The element nodes in lexical order. 82} 83 84func (t *Tree) newList(pos Pos) *ListNode { 85 return &ListNode{tr: t, NodeType: NodeList, Pos: pos} 86} 87 88func (l *ListNode) append(n Node) { 89 l.Nodes = append(l.Nodes, n) 90} 91 92func (l *ListNode) tree() *Tree { 93 return l.tr 94} 95 96func (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 104func (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 115func (l *ListNode) Copy() Node { 116 return l.CopyList() 117} 118 119// TextNode holds plain text. 120type TextNode struct { 121 NodeType 122 Pos 123 tr *Tree 124 Text []byte // The text; may span newlines. 125} 126 127func (t *Tree) newText(pos Pos, text string) *TextNode { 128 return &TextNode{tr: t, NodeType: NodeText, Pos: pos, Text: []byte(text)} 129} 130 131func (t *TextNode) String() string { 132 return fmt.Sprintf(textFormat, t.Text) 133} 134 135func (t *TextNode) tree() *Tree { 136 return t.tr 137} 138 139func (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 144type 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 153func (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 157func (p *PipeNode) append(command *CommandNode) { 158 p.Cmds = append(p.Cmds, command) 159} 160 161func (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 181func (p *PipeNode) tree() *Tree { 182 return p.tr 183} 184 185func (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 200func (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. 207type 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 215func (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 219func (a *ActionNode) String() string { 220 return fmt.Sprintf("{{%s}}", a.Pipe) 221 222} 223 224func (a *ActionNode) tree() *Tree { 225 return a.tr 226} 227 228func (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). 234type CommandNode struct { 235 NodeType 236 Pos 237 tr *Tree 238 Args []Node // Arguments in lexical order: Identifier, field, or constant. 239} 240 241func (t *Tree) newCommand(pos Pos) *CommandNode { 242 return &CommandNode{tr: t, NodeType: NodeCommand, Pos: pos} 243} 244 245func (c *CommandNode) append(arg Node) { 246 c.Args = append(c.Args, arg) 247} 248 249func (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 264func (c *CommandNode) tree() *Tree { 265 return c.tr 266} 267 268func (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. 280type 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. 288func 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? 295func (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? 303func (i *IdentifierNode) SetTree(t *Tree) *IdentifierNode { 304 i.tr = t 305 return i 306} 307 308func (i *IdentifierNode) String() string { 309 return i.Ident 310} 311 312func (i *IdentifierNode) tree() *Tree { 313 return i.tr 314} 315 316func (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. 322type VariableNode struct { 323 NodeType 324 Pos 325 tr *Tree 326 Ident []string // Variable name and fields in lexical order. 327} 328 329func (t *Tree) newVariable(pos Pos, ident string) *VariableNode { 330 return &VariableNode{tr: t, NodeType: NodeVariable, Pos: pos, Ident: strings.Split(ident, ".")} 331} 332 333func (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 344func (v *VariableNode) tree() *Tree { 345 return v.tr 346} 347 348func (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 '.'. 353type DotNode struct { 354 NodeType 355 Pos 356 tr *Tree 357} 358 359func (t *Tree) newDot(pos Pos) *DotNode { 360 return &DotNode{tr: t, NodeType: NodeDot, Pos: pos} 361} 362 363func (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 370func (d *DotNode) String() string { 371 return "." 372} 373 374func (d *DotNode) tree() *Tree { 375 return d.tr 376} 377 378func (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. 383type NilNode struct { 384 NodeType 385 Pos 386 tr *Tree 387} 388 389func (t *Tree) newNil(pos Pos) *NilNode { 390 return &NilNode{tr: t, NodeType: NodeNil, Pos: pos} 391} 392 393func (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 400func (n *NilNode) String() string { 401 return "nil" 402} 403 404func (n *NilNode) tree() *Tree { 405 return n.tr 406} 407 408func (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. 415type FieldNode struct { 416 NodeType 417 Pos 418 tr *Tree 419 Ident []string // The identifiers in lexical order. 420} 421 422func (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 426func (f *FieldNode) String() string { 427 s := "" 428 for _, id := range f.Ident { 429 s += "." + id 430 } 431 return s 432} 433 434func (f *FieldNode) tree() *Tree { 435 return f.tr 436} 437 438func (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. 445type ChainNode struct { 446 NodeType 447 Pos 448 tr *Tree 449 Node Node 450 Field []string // The identifiers in lexical order. 451} 452 453func (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. 458func (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 469func (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 480func (c *ChainNode) tree() *Tree { 481 return c.tr 482} 483 484func (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. 489type BoolNode struct { 490 NodeType 491 Pos 492 tr *Tree 493 True bool // The value of the boolean constant. 494} 495 496func (t *Tree) newBool(pos Pos, true bool) *BoolNode { 497 return &BoolNode{tr: t, NodeType: NodeBool, Pos: pos, True: true} 498} 499 500func (b *BoolNode) String() string { 501 if b.True { 502 return "true" 503 } 504 return "false" 505} 506 507func (b *BoolNode) tree() *Tree { 508 return b.tr 509} 510 511func (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. 518type 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 533func (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. 616func (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 631func (n *NumberNode) String() string { 632 return n.Text 633} 634 635func (n *NumberNode) tree() *Tree { 636 return n.tr 637} 638 639func (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". 646type 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 654func (t *Tree) newString(pos Pos, orig, text string) *StringNode { 655 return &StringNode{tr: t, NodeType: NodeString, Pos: pos, Quoted: orig, Text: text} 656} 657 658func (s *StringNode) String() string { 659 return s.Quoted 660} 661 662func (s *StringNode) tree() *Tree { 663 return s.tr 664} 665 666func (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. 672type endNode struct { 673 NodeType 674 Pos 675 tr *Tree 676} 677 678func (t *Tree) newEnd(pos Pos) *endNode { 679 return &endNode{tr: t, NodeType: nodeEnd, Pos: pos} 680} 681 682func (e *endNode) String() string { 683 return "{{end}}" 684} 685 686func (e *endNode) tree() *Tree { 687 return e.tr 688} 689 690func (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. 695type elseNode struct { 696 NodeType 697 Pos 698 tr *Tree 699 Line int // The line number in the input (deprecated; kept for compatibility) 700} 701 702func (t *Tree) newElse(pos Pos, line int) *elseNode { 703 return &elseNode{tr: t, NodeType: nodeElse, Pos: pos, Line: line} 704} 705 706func (e *elseNode) Type() NodeType { 707 return nodeElse 708} 709 710func (e *elseNode) String() string { 711 return "{{else}}" 712} 713 714func (e *elseNode) tree() *Tree { 715 return e.tr 716} 717 718func (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. 723type 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 733func (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 751func (b *BranchNode) tree() *Tree { 752 return b.tr 753} 754 755func (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. 769type IfNode struct { 770 BranchNode 771} 772 773func (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 777func (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. 782type RangeNode struct { 783 BranchNode 784} 785 786func (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 790func (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. 795type WithNode struct { 796 BranchNode 797} 798 799func (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 803func (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. 808type 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 817func (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 821func (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 828func (t *TemplateNode) tree() *Tree { 829 return t.tr 830} 831 832func (t *TemplateNode) Copy() Node { 833 return t.tr.newTemplate(t.Pos, t.Line, t.Name, t.Pipe.CopyPipe()) 834} 835