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 // If we parsed it as a float but it looks like an integer, 596 // it's a huge number too large to fit in an int. Reject it. 597 if !strings.ContainsAny(text, ".eE") { 598 return nil, fmt.Errorf("integer overflow: %q", text) 599 } 600 n.IsFloat = true 601 n.Float64 = f 602 // If a floating-point extraction succeeded, extract the int if needed. 603 if !n.IsInt && float64(int64(f)) == f { 604 n.IsInt = true 605 n.Int64 = int64(f) 606 } 607 if !n.IsUint && float64(uint64(f)) == f { 608 n.IsUint = true 609 n.Uint64 = uint64(f) 610 } 611 } 612 } 613 if !n.IsInt && !n.IsUint && !n.IsFloat { 614 return nil, fmt.Errorf("illegal number syntax: %q", text) 615 } 616 return n, nil 617} 618 619// simplifyComplex pulls out any other types that are represented by the complex number. 620// These all require that the imaginary part be zero. 621func (n *NumberNode) simplifyComplex() { 622 n.IsFloat = imag(n.Complex128) == 0 623 if n.IsFloat { 624 n.Float64 = real(n.Complex128) 625 n.IsInt = float64(int64(n.Float64)) == n.Float64 626 if n.IsInt { 627 n.Int64 = int64(n.Float64) 628 } 629 n.IsUint = float64(uint64(n.Float64)) == n.Float64 630 if n.IsUint { 631 n.Uint64 = uint64(n.Float64) 632 } 633 } 634} 635 636func (n *NumberNode) String() string { 637 return n.Text 638} 639 640func (n *NumberNode) tree() *Tree { 641 return n.tr 642} 643 644func (n *NumberNode) Copy() Node { 645 nn := new(NumberNode) 646 *nn = *n // Easy, fast, correct. 647 return nn 648} 649 650// StringNode holds a string constant. The value has been "unquoted". 651type StringNode struct { 652 NodeType 653 Pos 654 tr *Tree 655 Quoted string // The original text of the string, with quotes. 656 Text string // The string, after quote processing. 657} 658 659func (t *Tree) newString(pos Pos, orig, text string) *StringNode { 660 return &StringNode{tr: t, NodeType: NodeString, Pos: pos, Quoted: orig, Text: text} 661} 662 663func (s *StringNode) String() string { 664 return s.Quoted 665} 666 667func (s *StringNode) tree() *Tree { 668 return s.tr 669} 670 671func (s *StringNode) Copy() Node { 672 return s.tr.newString(s.Pos, s.Quoted, s.Text) 673} 674 675// endNode represents an {{end}} action. 676// It does not appear in the final parse tree. 677type endNode struct { 678 NodeType 679 Pos 680 tr *Tree 681} 682 683func (t *Tree) newEnd(pos Pos) *endNode { 684 return &endNode{tr: t, NodeType: nodeEnd, Pos: pos} 685} 686 687func (e *endNode) String() string { 688 return "{{end}}" 689} 690 691func (e *endNode) tree() *Tree { 692 return e.tr 693} 694 695func (e *endNode) Copy() Node { 696 return e.tr.newEnd(e.Pos) 697} 698 699// elseNode represents an {{else}} action. Does not appear in the final tree. 700type elseNode struct { 701 NodeType 702 Pos 703 tr *Tree 704 Line int // The line number in the input. Deprecated: Kept for compatibility. 705} 706 707func (t *Tree) newElse(pos Pos, line int) *elseNode { 708 return &elseNode{tr: t, NodeType: nodeElse, Pos: pos, Line: line} 709} 710 711func (e *elseNode) Type() NodeType { 712 return nodeElse 713} 714 715func (e *elseNode) String() string { 716 return "{{else}}" 717} 718 719func (e *elseNode) tree() *Tree { 720 return e.tr 721} 722 723func (e *elseNode) Copy() Node { 724 return e.tr.newElse(e.Pos, e.Line) 725} 726 727// BranchNode is the common representation of if, range, and with. 728type BranchNode struct { 729 NodeType 730 Pos 731 tr *Tree 732 Line int // The line number in the input. Deprecated: Kept for compatibility. 733 Pipe *PipeNode // The pipeline to be evaluated. 734 List *ListNode // What to execute if the value is non-empty. 735 ElseList *ListNode // What to execute if the value is empty (nil if absent). 736} 737 738func (b *BranchNode) String() string { 739 name := "" 740 switch b.NodeType { 741 case NodeIf: 742 name = "if" 743 case NodeRange: 744 name = "range" 745 case NodeWith: 746 name = "with" 747 default: 748 panic("unknown branch type") 749 } 750 if b.ElseList != nil { 751 return fmt.Sprintf("{{%s %s}}%s{{else}}%s{{end}}", name, b.Pipe, b.List, b.ElseList) 752 } 753 return fmt.Sprintf("{{%s %s}}%s{{end}}", name, b.Pipe, b.List) 754} 755 756func (b *BranchNode) tree() *Tree { 757 return b.tr 758} 759 760func (b *BranchNode) Copy() Node { 761 switch b.NodeType { 762 case NodeIf: 763 return b.tr.newIf(b.Pos, b.Line, b.Pipe, b.List, b.ElseList) 764 case NodeRange: 765 return b.tr.newRange(b.Pos, b.Line, b.Pipe, b.List, b.ElseList) 766 case NodeWith: 767 return b.tr.newWith(b.Pos, b.Line, b.Pipe, b.List, b.ElseList) 768 default: 769 panic("unknown branch type") 770 } 771} 772 773// IfNode represents an {{if}} action and its commands. 774type IfNode struct { 775 BranchNode 776} 777 778func (t *Tree) newIf(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *IfNode { 779 return &IfNode{BranchNode{tr: t, NodeType: NodeIf, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}} 780} 781 782func (i *IfNode) Copy() Node { 783 return i.tr.newIf(i.Pos, i.Line, i.Pipe.CopyPipe(), i.List.CopyList(), i.ElseList.CopyList()) 784} 785 786// RangeNode represents a {{range}} action and its commands. 787type RangeNode struct { 788 BranchNode 789} 790 791func (t *Tree) newRange(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *RangeNode { 792 return &RangeNode{BranchNode{tr: t, NodeType: NodeRange, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}} 793} 794 795func (r *RangeNode) Copy() Node { 796 return r.tr.newRange(r.Pos, r.Line, r.Pipe.CopyPipe(), r.List.CopyList(), r.ElseList.CopyList()) 797} 798 799// WithNode represents a {{with}} action and its commands. 800type WithNode struct { 801 BranchNode 802} 803 804func (t *Tree) newWith(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *WithNode { 805 return &WithNode{BranchNode{tr: t, NodeType: NodeWith, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}} 806} 807 808func (w *WithNode) Copy() Node { 809 return w.tr.newWith(w.Pos, w.Line, w.Pipe.CopyPipe(), w.List.CopyList(), w.ElseList.CopyList()) 810} 811 812// TemplateNode represents a {{template}} action. 813type TemplateNode struct { 814 NodeType 815 Pos 816 tr *Tree 817 Line int // The line number in the input. Deprecated: Kept for compatibility. 818 Name string // The name of the template (unquoted). 819 Pipe *PipeNode // The command to evaluate as dot for the template. 820} 821 822func (t *Tree) newTemplate(pos Pos, line int, name string, pipe *PipeNode) *TemplateNode { 823 return &TemplateNode{tr: t, NodeType: NodeTemplate, Pos: pos, Line: line, Name: name, Pipe: pipe} 824} 825 826func (t *TemplateNode) String() string { 827 if t.Pipe == nil { 828 return fmt.Sprintf("{{template %q}}", t.Name) 829 } 830 return fmt.Sprintf("{{template %q %s}}", t.Name, t.Pipe) 831} 832 833func (t *TemplateNode) tree() *Tree { 834 return t.tr 835} 836 837func (t *TemplateNode) Copy() Node { 838 return t.tr.newTemplate(t.Pos, t.Line, t.Name, t.Pipe.CopyPipe()) 839} 840