diff options
Diffstat (limited to 'src/pkg/text/template/parse/node.go')
| -rw-r--r-- | src/pkg/text/template/parse/node.go | 242 | 
1 files changed, 177 insertions, 65 deletions
| diff --git a/src/pkg/text/template/parse/node.go b/src/pkg/text/template/parse/node.go index db645624c..9d0d09eb5 100644 --- a/src/pkg/text/template/parse/node.go +++ b/src/pkg/text/template/parse/node.go @@ -13,7 +13,9 @@ import (  	"strings"  ) -// A node is an element in the parse tree. The interface is trivial. +// A Node is an element in the parse tree. The interface is trivial. +// The interface contains an unexported method so that only +// types local to this package can satisfy it.  type Node interface {  	Type() NodeType  	String() string @@ -21,11 +23,27 @@ type Node interface {  	// To avoid type assertions, some XxxNodes also have specialized  	// CopyXxx methods that return *XxxNode.  	Copy() Node +	Position() Pos // byte position of start of node in full original input string +	// Make sure only functions in this package can create Nodes. +	unexported()  }  // NodeType identifies the type of a parse tree node.  type NodeType int +// Pos represents a byte position in the original input text from which +// this template was parsed. +type Pos int + +func (p Pos) Position() Pos { +	return p +} + +// unexported keeps Node implementations local to the package. +// All implementations embed Pos, so this takes care of it. +func (Pos) unexported() { +} +  // Type returns itself and provides an easy default implementation  // for embedding in a Node. Embedded in all non-trivial Nodes.  func (t NodeType) Type() NodeType { @@ -34,8 +52,9 @@ func (t NodeType) Type() NodeType {  const (  	NodeText       NodeType = iota // Plain text. -	NodeAction                     // A simple action such as field evaluation. +	NodeAction                     // A non-control action such as a field evaluation.  	NodeBool                       // A boolean constant. +	NodeChain                      // A sequence of field accesses.  	NodeCommand                    // An element of a pipeline.  	NodeDot                        // The cursor, dot.  	nodeElse                       // An else action. Not added to tree. @@ -44,6 +63,7 @@ const (  	NodeIdentifier                 // An identifier; always a function name.  	NodeIf                         // An if action.  	NodeList                       // A list of Nodes. +	NodeNil                        // An untyped nil constant.  	NodeNumber                     // A numerical constant.  	NodePipe                       // A pipeline of commands.  	NodeRange                      // A range action. @@ -58,11 +78,12 @@ const (  // ListNode holds a sequence of nodes.  type ListNode struct {  	NodeType +	Pos  	Nodes []Node // The element nodes in lexical order.  } -func newList() *ListNode { -	return &ListNode{NodeType: NodeList} +func newList(pos Pos) *ListNode { +	return &ListNode{NodeType: NodeList, Pos: pos}  }  func (l *ListNode) append(n Node) { @@ -81,7 +102,7 @@ func (l *ListNode) CopyList() *ListNode {  	if l == nil {  		return l  	} -	n := newList() +	n := newList(l.Pos)  	for _, elem := range l.Nodes {  		n.append(elem.Copy())  	} @@ -95,11 +116,12 @@ func (l *ListNode) Copy() Node {  // TextNode holds plain text.  type TextNode struct {  	NodeType +	Pos  	Text []byte // The text; may span newlines.  } -func newText(text string) *TextNode { -	return &TextNode{NodeType: NodeText, Text: []byte(text)} +func newText(pos Pos, text string) *TextNode { +	return &TextNode{NodeType: NodeText, Pos: pos, Text: []byte(text)}  }  func (t *TextNode) String() string { @@ -113,13 +135,14 @@ func (t *TextNode) Copy() Node {  // PipeNode holds a pipeline with optional declaration  type PipeNode struct {  	NodeType -	Line int             // The line number in the input. +	Pos +	Line int             // The line number in the input (deprecated; kept for compatibility)  	Decl []*VariableNode // Variable declarations in lexical order.  	Cmds []*CommandNode  // The commands in lexical order.  } -func newPipeline(line int, decl []*VariableNode) *PipeNode { -	return &PipeNode{NodeType: NodePipe, Line: line, Decl: decl} +func newPipeline(pos Pos, line int, decl []*VariableNode) *PipeNode { +	return &PipeNode{NodeType: NodePipe, Pos: pos, Line: line, Decl: decl}  }  func (p *PipeNode) append(command *CommandNode) { @@ -154,7 +177,7 @@ func (p *PipeNode) CopyPipe() *PipeNode {  	for _, d := range p.Decl {  		decl = append(decl, d.Copy().(*VariableNode))  	} -	n := newPipeline(p.Line, decl) +	n := newPipeline(p.Pos, p.Line, decl)  	for _, c := range p.Cmds {  		n.append(c.Copy().(*CommandNode))  	} @@ -167,15 +190,16 @@ func (p *PipeNode) Copy() Node {  // ActionNode holds an action (something bounded by delimiters).  // Control actions have their own nodes; ActionNode represents simple -// ones such as field evaluations. +// ones such as field evaluations and parenthesized pipelines.  type ActionNode struct {  	NodeType -	Line int       // The line number in the input. +	Pos +	Line int       // The line number in the input (deprecated; kept for compatibility)  	Pipe *PipeNode // The pipeline in the action.  } -func newAction(line int, pipe *PipeNode) *ActionNode { -	return &ActionNode{NodeType: NodeAction, Line: line, Pipe: pipe} +func newAction(pos Pos, line int, pipe *PipeNode) *ActionNode { +	return &ActionNode{NodeType: NodeAction, Pos: pos, Line: line, Pipe: pipe}  }  func (a *ActionNode) String() string { @@ -184,18 +208,19 @@ func (a *ActionNode) String() string {  }  func (a *ActionNode) Copy() Node { -	return newAction(a.Line, a.Pipe.CopyPipe()) +	return newAction(a.Pos, a.Line, a.Pipe.CopyPipe())  }  // CommandNode holds a command (a pipeline inside an evaluating action).  type CommandNode struct {  	NodeType +	Pos  	Args []Node // Arguments in lexical order: Identifier, field, or constant.  } -func newCommand() *CommandNode { -	return &CommandNode{NodeType: NodeCommand} +func newCommand(pos Pos) *CommandNode { +	return &CommandNode{NodeType: NodeCommand, Pos: pos}  }  func (c *CommandNode) append(arg Node) { @@ -208,6 +233,10 @@ func (c *CommandNode) String() string {  		if i > 0 {  			s += " "  		} +		if arg, ok := arg.(*PipeNode); ok { +			s += "(" + arg.String() + ")" +			continue +		}  		s += arg.String()  	}  	return s @@ -217,7 +246,7 @@ func (c *CommandNode) Copy() Node {  	if c == nil {  		return c  	} -	n := newCommand() +	n := newCommand(c.Pos)  	for _, c := range c.Args {  		n.append(c.Copy())  	} @@ -227,6 +256,7 @@ func (c *CommandNode) Copy() Node {  // IdentifierNode holds an identifier.  type IdentifierNode struct {  	NodeType +	Pos  	Ident string // The identifier's name.  } @@ -235,23 +265,32 @@ func NewIdentifier(ident string) *IdentifierNode {  	return &IdentifierNode{NodeType: NodeIdentifier, Ident: ident}  } +// SetPos sets the position. NewIdentifier is a public method so we can't modify its signature. +// Chained for convenience. +// TODO: fix one day? +func (i *IdentifierNode) SetPos(pos Pos) *IdentifierNode { +	i.Pos = pos +	return i +} +  func (i *IdentifierNode) String() string {  	return i.Ident  }  func (i *IdentifierNode) Copy() Node { -	return NewIdentifier(i.Ident) +	return NewIdentifier(i.Ident).SetPos(i.Pos)  } -// VariableNode holds a list of variable names. The dollar sign is -// part of the name. +// VariableNode holds a list of variable names, possibly with chained field +// accesses. The dollar sign is part of the (first) name.  type VariableNode struct {  	NodeType -	Ident []string // Variable names in lexical order. +	Pos +	Ident []string // Variable name and fields in lexical order.  } -func newVariable(ident string) *VariableNode { -	return &VariableNode{NodeType: NodeVariable, Ident: strings.Split(ident, ".")} +func newVariable(pos Pos, ident string) *VariableNode { +	return &VariableNode{NodeType: NodeVariable, Pos: pos, Ident: strings.Split(ident, ".")}  }  func (v *VariableNode) String() string { @@ -266,14 +305,16 @@ func (v *VariableNode) String() string {  }  func (v *VariableNode) Copy() Node { -	return &VariableNode{NodeType: NodeVariable, Ident: append([]string{}, v.Ident...)} +	return &VariableNode{NodeType: NodeVariable, Pos: v.Pos, Ident: append([]string{}, v.Ident...)}  } -// DotNode holds the special identifier '.'. It is represented by a nil pointer. -type DotNode bool +// DotNode holds the special identifier '.'. +type DotNode struct { +	Pos +} -func newDot() *DotNode { -	return nil +func newDot(pos Pos) *DotNode { +	return &DotNode{Pos: pos}  }  func (d *DotNode) Type() NodeType { @@ -285,7 +326,28 @@ func (d *DotNode) String() string {  }  func (d *DotNode) Copy() Node { -	return newDot() +	return newDot(d.Pos) +} + +// NilNode holds the special identifier 'nil' representing an untyped nil constant. +type NilNode struct { +	Pos +} + +func newNil(pos Pos) *NilNode { +	return &NilNode{Pos: pos} +} + +func (n *NilNode) Type() NodeType { +	return NodeNil +} + +func (n *NilNode) String() string { +	return "nil" +} + +func (n *NilNode) Copy() Node { +	return newNil(n.Pos)  }  // FieldNode holds a field (identifier starting with '.'). @@ -293,11 +355,12 @@ func (d *DotNode) Copy() Node {  // The period is dropped from each ident.  type FieldNode struct {  	NodeType +	Pos  	Ident []string // The identifiers in lexical order.  } -func newField(ident string) *FieldNode { -	return &FieldNode{NodeType: NodeField, Ident: strings.Split(ident[1:], ".")} // [1:] to drop leading period +func newField(pos Pos, ident string) *FieldNode { +	return &FieldNode{NodeType: NodeField, Pos: pos, Ident: strings.Split(ident[1:], ".")} // [1:] to drop leading period  }  func (f *FieldNode) String() string { @@ -309,17 +372,59 @@ func (f *FieldNode) String() string {  }  func (f *FieldNode) Copy() Node { -	return &FieldNode{NodeType: NodeField, Ident: append([]string{}, f.Ident...)} +	return &FieldNode{NodeType: NodeField, Pos: f.Pos, Ident: append([]string{}, f.Ident...)} +} + +// ChainNode holds a term followed by a chain of field accesses (identifier starting with '.'). +// The names may be chained ('.x.y'). +// The periods are dropped from each ident. +type ChainNode struct { +	NodeType +	Pos +	Node  Node +	Field []string // The identifiers in lexical order. +} + +func newChain(pos Pos, node Node) *ChainNode { +	return &ChainNode{NodeType: NodeChain, Pos: pos, Node: node} +} + +// Add adds the named field (which should start with a period) to the end of the chain. +func (c *ChainNode) Add(field string) { +	if len(field) == 0 || field[0] != '.' { +		panic("no dot in field") +	} +	field = field[1:] // Remove leading dot. +	if field == "" { +		panic("empty field") +	} +	c.Field = append(c.Field, field) +} + +func (c *ChainNode) String() string { +	s := c.Node.String() +	if _, ok := c.Node.(*PipeNode); ok { +		s = "(" + s + ")" +	} +	for _, field := range c.Field { +		s += "." + field +	} +	return s +} + +func (c *ChainNode) Copy() Node { +	return &ChainNode{NodeType: NodeChain, Pos: c.Pos, Node: c.Node, Field: append([]string{}, c.Field...)}  }  // BoolNode holds a boolean constant.  type BoolNode struct {  	NodeType +	Pos  	True bool // The value of the boolean constant.  } -func newBool(true bool) *BoolNode { -	return &BoolNode{NodeType: NodeBool, True: true} +func newBool(pos Pos, true bool) *BoolNode { +	return &BoolNode{NodeType: NodeBool, Pos: pos, True: true}  }  func (b *BoolNode) String() string { @@ -330,7 +435,7 @@ func (b *BoolNode) String() string {  }  func (b *BoolNode) Copy() Node { -	return newBool(b.True) +	return newBool(b.Pos, b.True)  }  // NumberNode holds a number: signed or unsigned integer, float, or complex. @@ -338,6 +443,7 @@ func (b *BoolNode) Copy() Node {  // This simulates in a small amount of code the behavior of Go's ideal constants.  type NumberNode struct {  	NodeType +	Pos  	IsInt      bool       // Number has an integral value.  	IsUint     bool       // Number has an unsigned integral value.  	IsFloat    bool       // Number has a floating-point value. @@ -349,8 +455,8 @@ type NumberNode struct {  	Text       string     // The original textual representation from the input.  } -func newNumber(text string, typ itemType) (*NumberNode, error) { -	n := &NumberNode{NodeType: NodeNumber, Text: text} +func newNumber(pos Pos, text string, typ itemType) (*NumberNode, error) { +	n := &NumberNode{NodeType: NodeNumber, Pos: pos, Text: text}  	switch typ {  	case itemCharConstant:  		rune, _, tail, err := strconv.UnquoteChar(text[1:], text[0]) @@ -460,12 +566,13 @@ func (n *NumberNode) Copy() Node {  // StringNode holds a string constant. The value has been "unquoted".  type StringNode struct {  	NodeType +	Pos  	Quoted string // The original text of the string, with quotes.  	Text   string // The string, after quote processing.  } -func newString(orig, text string) *StringNode { -	return &StringNode{NodeType: NodeString, Quoted: orig, Text: text} +func newString(pos Pos, orig, text string) *StringNode { +	return &StringNode{NodeType: NodeString, Pos: pos, Quoted: orig, Text: text}  }  func (s *StringNode) String() string { @@ -473,15 +580,17 @@ func (s *StringNode) String() string {  }  func (s *StringNode) Copy() Node { -	return newString(s.Quoted, s.Text) +	return newString(s.Pos, s.Quoted, s.Text)  } -// endNode represents an {{end}} action. It is represented by a nil pointer. +// endNode represents an {{end}} action.  // It does not appear in the final parse tree. -type endNode bool +type endNode struct { +	Pos +} -func newEnd() *endNode { -	return nil +func newEnd(pos Pos) *endNode { +	return &endNode{Pos: pos}  }  func (e *endNode) Type() NodeType { @@ -493,17 +602,18 @@ func (e *endNode) String() string {  }  func (e *endNode) Copy() Node { -	return newEnd() +	return newEnd(e.Pos)  }  // elseNode represents an {{else}} action. Does not appear in the final tree.  type elseNode struct {  	NodeType -	Line int // The line number in the input. +	Pos +	Line int // The line number in the input (deprecated; kept for compatibility)  } -func newElse(line int) *elseNode { -	return &elseNode{NodeType: nodeElse, Line: line} +func newElse(pos Pos, line int) *elseNode { +	return &elseNode{NodeType: nodeElse, Pos: pos, Line: line}  }  func (e *elseNode) Type() NodeType { @@ -515,13 +625,14 @@ func (e *elseNode) String() string {  }  func (e *elseNode) Copy() Node { -	return newElse(e.Line) +	return newElse(e.Pos, e.Line)  }  // BranchNode is the common representation of if, range, and with.  type BranchNode struct {  	NodeType -	Line     int       // The line number in the input. +	Pos +	Line     int       // The line number in the input (deprecated; kept for compatibility)  	Pipe     *PipeNode // The pipeline to be evaluated.  	List     *ListNode // What to execute if the value is non-empty.  	ElseList *ListNode // What to execute if the value is empty (nil if absent). @@ -550,12 +661,12 @@ type IfNode struct {  	BranchNode  } -func newIf(line int, pipe *PipeNode, list, elseList *ListNode) *IfNode { -	return &IfNode{BranchNode{NodeType: NodeIf, Line: line, Pipe: pipe, List: list, ElseList: elseList}} +func newIf(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *IfNode { +	return &IfNode{BranchNode{NodeType: NodeIf, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}  }  func (i *IfNode) Copy() Node { -	return newIf(i.Line, i.Pipe.CopyPipe(), i.List.CopyList(), i.ElseList.CopyList()) +	return newIf(i.Pos, i.Line, i.Pipe.CopyPipe(), i.List.CopyList(), i.ElseList.CopyList())  }  // RangeNode represents a {{range}} action and its commands. @@ -563,12 +674,12 @@ type RangeNode struct {  	BranchNode  } -func newRange(line int, pipe *PipeNode, list, elseList *ListNode) *RangeNode { -	return &RangeNode{BranchNode{NodeType: NodeRange, Line: line, Pipe: pipe, List: list, ElseList: elseList}} +func newRange(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *RangeNode { +	return &RangeNode{BranchNode{NodeType: NodeRange, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}  }  func (r *RangeNode) Copy() Node { -	return newRange(r.Line, r.Pipe.CopyPipe(), r.List.CopyList(), r.ElseList.CopyList()) +	return newRange(r.Pos, r.Line, r.Pipe.CopyPipe(), r.List.CopyList(), r.ElseList.CopyList())  }  // WithNode represents a {{with}} action and its commands. @@ -576,24 +687,25 @@ type WithNode struct {  	BranchNode  } -func newWith(line int, pipe *PipeNode, list, elseList *ListNode) *WithNode { -	return &WithNode{BranchNode{NodeType: NodeWith, Line: line, Pipe: pipe, List: list, ElseList: elseList}} +func newWith(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *WithNode { +	return &WithNode{BranchNode{NodeType: NodeWith, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}  }  func (w *WithNode) Copy() Node { -	return newWith(w.Line, w.Pipe.CopyPipe(), w.List.CopyList(), w.ElseList.CopyList()) +	return newWith(w.Pos, w.Line, w.Pipe.CopyPipe(), w.List.CopyList(), w.ElseList.CopyList())  }  // TemplateNode represents a {{template}} action.  type TemplateNode struct {  	NodeType -	Line int       // The line number in the input. +	Pos +	Line int       // The line number in the input (deprecated; kept for compatibility)  	Name string    // The name of the template (unquoted).  	Pipe *PipeNode // The command to evaluate as dot for the template.  } -func newTemplate(line int, name string, pipe *PipeNode) *TemplateNode { -	return &TemplateNode{NodeType: NodeTemplate, Line: line, Name: name, Pipe: pipe} +func newTemplate(pos Pos, line int, name string, pipe *PipeNode) *TemplateNode { +	return &TemplateNode{NodeType: NodeTemplate, Line: line, Pos: pos, Name: name, Pipe: pipe}  }  func (t *TemplateNode) String() string { @@ -604,5 +716,5 @@ func (t *TemplateNode) String() string {  }  func (t *TemplateNode) Copy() Node { -	return newTemplate(t.Line, t.Name, t.Pipe.CopyPipe()) +	return newTemplate(t.Pos, t.Line, t.Name, t.Pipe.CopyPipe())  } | 
