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