...

Source file src/text/template/parse/parse.go

     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	// Package parse builds parse trees for templates as defined by text/template
     6	// and html/template. Clients should use those packages to construct templates
     7	// rather than this one, which provides shared internal data structures not
     8	// intended for general use.
     9	package parse
    10	
    11	import (
    12		"bytes"
    13		"fmt"
    14		"runtime"
    15		"strconv"
    16		"strings"
    17	)
    18	
    19	// Tree is the representation of a single parsed template.
    20	type Tree struct {
    21		Name      string    // name of the template represented by the tree.
    22		ParseName string    // name of the top-level template during parsing, for error messages.
    23		Root      *ListNode // top-level root of the tree.
    24		text      string    // text parsed to create the template (or its parent)
    25		// Parsing only; cleared after parse.
    26		funcs     []map[string]interface{}
    27		lex       *lexer
    28		token     [3]item // three-token lookahead for parser.
    29		peekCount int
    30		vars      []string // variables defined at the moment.
    31		treeSet   map[string]*Tree
    32	}
    33	
    34	// Copy returns a copy of the Tree. Any parsing state is discarded.
    35	func (t *Tree) Copy() *Tree {
    36		if t == nil {
    37			return nil
    38		}
    39		return &Tree{
    40			Name:      t.Name,
    41			ParseName: t.ParseName,
    42			Root:      t.Root.CopyList(),
    43			text:      t.text,
    44		}
    45	}
    46	
    47	// Parse returns a map from template name to parse.Tree, created by parsing the
    48	// templates described in the argument string. The top-level template will be
    49	// given the specified name. If an error is encountered, parsing stops and an
    50	// empty map is returned with the error.
    51	func Parse(name, text, leftDelim, rightDelim string, funcs ...map[string]interface{}) (map[string]*Tree, error) {
    52		treeSet := make(map[string]*Tree)
    53		t := New(name)
    54		t.text = text
    55		_, err := t.Parse(text, leftDelim, rightDelim, treeSet, funcs...)
    56		return treeSet, err
    57	}
    58	
    59	// next returns the next token.
    60	func (t *Tree) next() item {
    61		if t.peekCount > 0 {
    62			t.peekCount--
    63		} else {
    64			t.token[0] = t.lex.nextItem()
    65		}
    66		return t.token[t.peekCount]
    67	}
    68	
    69	// backup backs the input stream up one token.
    70	func (t *Tree) backup() {
    71		t.peekCount++
    72	}
    73	
    74	// backup2 backs the input stream up two tokens.
    75	// The zeroth token is already there.
    76	func (t *Tree) backup2(t1 item) {
    77		t.token[1] = t1
    78		t.peekCount = 2
    79	}
    80	
    81	// backup3 backs the input stream up three tokens
    82	// The zeroth token is already there.
    83	func (t *Tree) backup3(t2, t1 item) { // Reverse order: we're pushing back.
    84		t.token[1] = t1
    85		t.token[2] = t2
    86		t.peekCount = 3
    87	}
    88	
    89	// peek returns but does not consume the next token.
    90	func (t *Tree) peek() item {
    91		if t.peekCount > 0 {
    92			return t.token[t.peekCount-1]
    93		}
    94		t.peekCount = 1
    95		t.token[0] = t.lex.nextItem()
    96		return t.token[0]
    97	}
    98	
    99	// nextNonSpace returns the next non-space token.
   100	func (t *Tree) nextNonSpace() (token item) {
   101		for {
   102			token = t.next()
   103			if token.typ != itemSpace {
   104				break
   105			}
   106		}
   107		return token
   108	}
   109	
   110	// peekNonSpace returns but does not consume the next non-space token.
   111	func (t *Tree) peekNonSpace() (token item) {
   112		for {
   113			token = t.next()
   114			if token.typ != itemSpace {
   115				break
   116			}
   117		}
   118		t.backup()
   119		return token
   120	}
   121	
   122	// Parsing.
   123	
   124	// New allocates a new parse tree with the given name.
   125	func New(name string, funcs ...map[string]interface{}) *Tree {
   126		return &Tree{
   127			Name:  name,
   128			funcs: funcs,
   129		}
   130	}
   131	
   132	// ErrorContext returns a textual representation of the location of the node in the input text.
   133	// The receiver is only used when the node does not have a pointer to the tree inside,
   134	// which can occur in old code.
   135	func (t *Tree) ErrorContext(n Node) (location, context string) {
   136		pos := int(n.Position())
   137		tree := n.tree()
   138		if tree == nil {
   139			tree = t
   140		}
   141		text := tree.text[:pos]
   142		byteNum := strings.LastIndex(text, "\n")
   143		if byteNum == -1 {
   144			byteNum = pos // On first line.
   145		} else {
   146			byteNum++ // After the newline.
   147			byteNum = pos - byteNum
   148		}
   149		lineNum := 1 + strings.Count(text, "\n")
   150		context = n.String()
   151		return fmt.Sprintf("%s:%d:%d", tree.ParseName, lineNum, byteNum), context
   152	}
   153	
   154	// errorf formats the error and terminates processing.
   155	func (t *Tree) errorf(format string, args ...interface{}) {
   156		t.Root = nil
   157		format = fmt.Sprintf("template: %s:%d: %s", t.ParseName, t.token[0].line, format)
   158		panic(fmt.Errorf(format, args...))
   159	}
   160	
   161	// error terminates processing.
   162	func (t *Tree) error(err error) {
   163		t.errorf("%s", err)
   164	}
   165	
   166	// expect consumes the next token and guarantees it has the required type.
   167	func (t *Tree) expect(expected itemType, context string) item {
   168		token := t.nextNonSpace()
   169		if token.typ != expected {
   170			t.unexpected(token, context)
   171		}
   172		return token
   173	}
   174	
   175	// expectOneOf consumes the next token and guarantees it has one of the required types.
   176	func (t *Tree) expectOneOf(expected1, expected2 itemType, context string) item {
   177		token := t.nextNonSpace()
   178		if token.typ != expected1 && token.typ != expected2 {
   179			t.unexpected(token, context)
   180		}
   181		return token
   182	}
   183	
   184	// unexpected complains about the token and terminates processing.
   185	func (t *Tree) unexpected(token item, context string) {
   186		t.errorf("unexpected %s in %s", token, context)
   187	}
   188	
   189	// recover is the handler that turns panics into returns from the top level of Parse.
   190	func (t *Tree) recover(errp *error) {
   191		e := recover()
   192		if e != nil {
   193			if _, ok := e.(runtime.Error); ok {
   194				panic(e)
   195			}
   196			if t != nil {
   197				t.lex.drain()
   198				t.stopParse()
   199			}
   200			*errp = e.(error)
   201		}
   202	}
   203	
   204	// startParse initializes the parser, using the lexer.
   205	func (t *Tree) startParse(funcs []map[string]interface{}, lex *lexer, treeSet map[string]*Tree) {
   206		t.Root = nil
   207		t.lex = lex
   208		t.vars = []string{"$"}
   209		t.funcs = funcs
   210		t.treeSet = treeSet
   211	}
   212	
   213	// stopParse terminates parsing.
   214	func (t *Tree) stopParse() {
   215		t.lex = nil
   216		t.vars = nil
   217		t.funcs = nil
   218		t.treeSet = nil
   219	}
   220	
   221	// Parse parses the template definition string to construct a representation of
   222	// the template for execution. If either action delimiter string is empty, the
   223	// default ("{{" or "}}") is used. Embedded template definitions are added to
   224	// the treeSet map.
   225	func (t *Tree) Parse(text, leftDelim, rightDelim string, treeSet map[string]*Tree, funcs ...map[string]interface{}) (tree *Tree, err error) {
   226		defer t.recover(&err)
   227		t.ParseName = t.Name
   228		t.startParse(funcs, lex(t.Name, text, leftDelim, rightDelim), treeSet)
   229		t.text = text
   230		t.parse()
   231		t.add()
   232		t.stopParse()
   233		return t, nil
   234	}
   235	
   236	// add adds tree to t.treeSet.
   237	func (t *Tree) add() {
   238		tree := t.treeSet[t.Name]
   239		if tree == nil || IsEmptyTree(tree.Root) {
   240			t.treeSet[t.Name] = t
   241			return
   242		}
   243		if !IsEmptyTree(t.Root) {
   244			t.errorf("template: multiple definition of template %q", t.Name)
   245		}
   246	}
   247	
   248	// IsEmptyTree reports whether this tree (node) is empty of everything but space.
   249	func IsEmptyTree(n Node) bool {
   250		switch n := n.(type) {
   251		case nil:
   252			return true
   253		case *ActionNode:
   254		case *IfNode:
   255		case *ListNode:
   256			for _, node := range n.Nodes {
   257				if !IsEmptyTree(node) {
   258					return false
   259				}
   260			}
   261			return true
   262		case *RangeNode:
   263		case *TemplateNode:
   264		case *TextNode:
   265			return len(bytes.TrimSpace(n.Text)) == 0
   266		case *WithNode:
   267		default:
   268			panic("unknown node: " + n.String())
   269		}
   270		return false
   271	}
   272	
   273	// parse is the top-level parser for a template, essentially the same
   274	// as itemList except it also parses {{define}} actions.
   275	// It runs to EOF.
   276	func (t *Tree) parse() {
   277		t.Root = t.newList(t.peek().pos)
   278		for t.peek().typ != itemEOF {
   279			if t.peek().typ == itemLeftDelim {
   280				delim := t.next()
   281				if t.nextNonSpace().typ == itemDefine {
   282					newT := New("definition") // name will be updated once we know it.
   283					newT.text = t.text
   284					newT.ParseName = t.ParseName
   285					newT.startParse(t.funcs, t.lex, t.treeSet)
   286					newT.parseDefinition()
   287					continue
   288				}
   289				t.backup2(delim)
   290			}
   291			switch n := t.textOrAction(); n.Type() {
   292			case nodeEnd, nodeElse:
   293				t.errorf("unexpected %s", n)
   294			default:
   295				t.Root.append(n)
   296			}
   297		}
   298	}
   299	
   300	// parseDefinition parses a {{define}} ...  {{end}} template definition and
   301	// installs the definition in t.treeSet. The "define" keyword has already
   302	// been scanned.
   303	func (t *Tree) parseDefinition() {
   304		const context = "define clause"
   305		name := t.expectOneOf(itemString, itemRawString, context)
   306		var err error
   307		t.Name, err = strconv.Unquote(name.val)
   308		if err != nil {
   309			t.error(err)
   310		}
   311		t.expect(itemRightDelim, context)
   312		var end Node
   313		t.Root, end = t.itemList()
   314		if end.Type() != nodeEnd {
   315			t.errorf("unexpected %s in %s", end, context)
   316		}
   317		t.add()
   318		t.stopParse()
   319	}
   320	
   321	// itemList:
   322	//	textOrAction*
   323	// Terminates at {{end}} or {{else}}, returned separately.
   324	func (t *Tree) itemList() (list *ListNode, next Node) {
   325		list = t.newList(t.peekNonSpace().pos)
   326		for t.peekNonSpace().typ != itemEOF {
   327			n := t.textOrAction()
   328			switch n.Type() {
   329			case nodeEnd, nodeElse:
   330				return list, n
   331			}
   332			list.append(n)
   333		}
   334		t.errorf("unexpected EOF")
   335		return
   336	}
   337	
   338	// textOrAction:
   339	//	text | action
   340	func (t *Tree) textOrAction() Node {
   341		switch token := t.nextNonSpace(); token.typ {
   342		case itemText:
   343			return t.newText(token.pos, token.val)
   344		case itemLeftDelim:
   345			return t.action()
   346		default:
   347			t.unexpected(token, "input")
   348		}
   349		return nil
   350	}
   351	
   352	// Action:
   353	//	control
   354	//	command ("|" command)*
   355	// Left delim is past. Now get actions.
   356	// First word could be a keyword such as range.
   357	func (t *Tree) action() (n Node) {
   358		switch token := t.nextNonSpace(); token.typ {
   359		case itemBlock:
   360			return t.blockControl()
   361		case itemElse:
   362			return t.elseControl()
   363		case itemEnd:
   364			return t.endControl()
   365		case itemIf:
   366			return t.ifControl()
   367		case itemRange:
   368			return t.rangeControl()
   369		case itemTemplate:
   370			return t.templateControl()
   371		case itemWith:
   372			return t.withControl()
   373		}
   374		t.backup()
   375		token := t.peek()
   376		// Do not pop variables; they persist until "end".
   377		return t.newAction(token.pos, token.line, t.pipeline("command"))
   378	}
   379	
   380	// Pipeline:
   381	//	declarations? command ('|' command)*
   382	func (t *Tree) pipeline(context string) (pipe *PipeNode) {
   383		token := t.peekNonSpace()
   384		pipe = t.newPipeline(token.pos, token.line, nil)
   385		// Are there declarations or assignments?
   386	decls:
   387		if v := t.peekNonSpace(); v.typ == itemVariable {
   388			t.next()
   389			// Since space is a token, we need 3-token look-ahead here in the worst case:
   390			// in "$x foo" we need to read "foo" (as opposed to ":=") to know that $x is an
   391			// argument variable rather than a declaration. So remember the token
   392			// adjacent to the variable so we can push it back if necessary.
   393			tokenAfterVariable := t.peek()
   394			next := t.peekNonSpace()
   395			switch {
   396			case next.typ == itemAssign, next.typ == itemDeclare:
   397				pipe.IsAssign = next.typ == itemAssign
   398				t.nextNonSpace()
   399				pipe.Decl = append(pipe.Decl, t.newVariable(v.pos, v.val))
   400				t.vars = append(t.vars, v.val)
   401			case next.typ == itemChar && next.val == ",":
   402				t.nextNonSpace()
   403				pipe.Decl = append(pipe.Decl, t.newVariable(v.pos, v.val))
   404				t.vars = append(t.vars, v.val)
   405				if context == "range" && len(pipe.Decl) < 2 {
   406					switch t.peekNonSpace().typ {
   407					case itemVariable, itemRightDelim, itemRightParen:
   408						// second initialized variable in a range pipeline
   409						goto decls
   410					default:
   411						t.errorf("range can only initialize variables")
   412					}
   413				}
   414				t.errorf("too many declarations in %s", context)
   415			case tokenAfterVariable.typ == itemSpace:
   416				t.backup3(v, tokenAfterVariable)
   417			default:
   418				t.backup2(v)
   419			}
   420		}
   421		for {
   422			switch token := t.nextNonSpace(); token.typ {
   423			case itemRightDelim, itemRightParen:
   424				// At this point, the pipeline is complete
   425				t.checkPipeline(pipe, context)
   426				if token.typ == itemRightParen {
   427					t.backup()
   428				}
   429				return
   430			case itemBool, itemCharConstant, itemComplex, itemDot, itemField, itemIdentifier,
   431				itemNumber, itemNil, itemRawString, itemString, itemVariable, itemLeftParen:
   432				t.backup()
   433				pipe.append(t.command())
   434			default:
   435				t.unexpected(token, context)
   436			}
   437		}
   438	}
   439	
   440	func (t *Tree) checkPipeline(pipe *PipeNode, context string) {
   441		// Reject empty pipelines
   442		if len(pipe.Cmds) == 0 {
   443			t.errorf("missing value for %s", context)
   444		}
   445		// Only the first command of a pipeline can start with a non executable operand
   446		for i, c := range pipe.Cmds[1:] {
   447			switch c.Args[0].Type() {
   448			case NodeBool, NodeDot, NodeNil, NodeNumber, NodeString:
   449				// With A|B|C, pipeline stage 2 is B
   450				t.errorf("non executable command in pipeline stage %d", i+2)
   451			}
   452		}
   453	}
   454	
   455	func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) {
   456		defer t.popVars(len(t.vars))
   457		pipe = t.pipeline(context)
   458		var next Node
   459		list, next = t.itemList()
   460		switch next.Type() {
   461		case nodeEnd: //done
   462		case nodeElse:
   463			if allowElseIf {
   464				// Special case for "else if". If the "else" is followed immediately by an "if",
   465				// the elseControl will have left the "if" token pending. Treat
   466				//	{{if a}}_{{else if b}}_{{end}}
   467				// as
   468				//	{{if a}}_{{else}}{{if b}}_{{end}}{{end}}.
   469				// To do this, parse the if as usual and stop at it {{end}}; the subsequent{{end}}
   470				// is assumed. This technique works even for long if-else-if chains.
   471				// TODO: Should we allow else-if in with and range?
   472				if t.peek().typ == itemIf {
   473					t.next() // Consume the "if" token.
   474					elseList = t.newList(next.Position())
   475					elseList.append(t.ifControl())
   476					// Do not consume the next item - only one {{end}} required.
   477					break
   478				}
   479			}
   480			elseList, next = t.itemList()
   481			if next.Type() != nodeEnd {
   482				t.errorf("expected end; found %s", next)
   483			}
   484		}
   485		return pipe.Position(), pipe.Line, pipe, list, elseList
   486	}
   487	
   488	// If:
   489	//	{{if pipeline}} itemList {{end}}
   490	//	{{if pipeline}} itemList {{else}} itemList {{end}}
   491	// If keyword is past.
   492	func (t *Tree) ifControl() Node {
   493		return t.newIf(t.parseControl(true, "if"))
   494	}
   495	
   496	// Range:
   497	//	{{range pipeline}} itemList {{end}}
   498	//	{{range pipeline}} itemList {{else}} itemList {{end}}
   499	// Range keyword is past.
   500	func (t *Tree) rangeControl() Node {
   501		return t.newRange(t.parseControl(false, "range"))
   502	}
   503	
   504	// With:
   505	//	{{with pipeline}} itemList {{end}}
   506	//	{{with pipeline}} itemList {{else}} itemList {{end}}
   507	// If keyword is past.
   508	func (t *Tree) withControl() Node {
   509		return t.newWith(t.parseControl(false, "with"))
   510	}
   511	
   512	// End:
   513	//	{{end}}
   514	// End keyword is past.
   515	func (t *Tree) endControl() Node {
   516		return t.newEnd(t.expect(itemRightDelim, "end").pos)
   517	}
   518	
   519	// Else:
   520	//	{{else}}
   521	// Else keyword is past.
   522	func (t *Tree) elseControl() Node {
   523		// Special case for "else if".
   524		peek := t.peekNonSpace()
   525		if peek.typ == itemIf {
   526			// We see "{{else if ... " but in effect rewrite it to {{else}}{{if ... ".
   527			return t.newElse(peek.pos, peek.line)
   528		}
   529		token := t.expect(itemRightDelim, "else")
   530		return t.newElse(token.pos, token.line)
   531	}
   532	
   533	// Block:
   534	//	{{block stringValue pipeline}}
   535	// Block keyword is past.
   536	// The name must be something that can evaluate to a string.
   537	// The pipeline is mandatory.
   538	func (t *Tree) blockControl() Node {
   539		const context = "block clause"
   540	
   541		token := t.nextNonSpace()
   542		name := t.parseTemplateName(token, context)
   543		pipe := t.pipeline(context)
   544	
   545		block := New(name) // name will be updated once we know it.
   546		block.text = t.text
   547		block.ParseName = t.ParseName
   548		block.startParse(t.funcs, t.lex, t.treeSet)
   549		var end Node
   550		block.Root, end = block.itemList()
   551		if end.Type() != nodeEnd {
   552			t.errorf("unexpected %s in %s", end, context)
   553		}
   554		block.add()
   555		block.stopParse()
   556	
   557		return t.newTemplate(token.pos, token.line, name, pipe)
   558	}
   559	
   560	// Template:
   561	//	{{template stringValue pipeline}}
   562	// Template keyword is past. The name must be something that can evaluate
   563	// to a string.
   564	func (t *Tree) templateControl() Node {
   565		const context = "template clause"
   566		token := t.nextNonSpace()
   567		name := t.parseTemplateName(token, context)
   568		var pipe *PipeNode
   569		if t.nextNonSpace().typ != itemRightDelim {
   570			t.backup()
   571			// Do not pop variables; they persist until "end".
   572			pipe = t.pipeline(context)
   573		}
   574		return t.newTemplate(token.pos, token.line, name, pipe)
   575	}
   576	
   577	func (t *Tree) parseTemplateName(token item, context string) (name string) {
   578		switch token.typ {
   579		case itemString, itemRawString:
   580			s, err := strconv.Unquote(token.val)
   581			if err != nil {
   582				t.error(err)
   583			}
   584			name = s
   585		default:
   586			t.unexpected(token, context)
   587		}
   588		return
   589	}
   590	
   591	// command:
   592	//	operand (space operand)*
   593	// space-separated arguments up to a pipeline character or right delimiter.
   594	// we consume the pipe character but leave the right delim to terminate the action.
   595	func (t *Tree) command() *CommandNode {
   596		cmd := t.newCommand(t.peekNonSpace().pos)
   597		for {
   598			t.peekNonSpace() // skip leading spaces.
   599			operand := t.operand()
   600			if operand != nil {
   601				cmd.append(operand)
   602			}
   603			switch token := t.next(); token.typ {
   604			case itemSpace:
   605				continue
   606			case itemError:
   607				t.errorf("%s", token.val)
   608			case itemRightDelim, itemRightParen:
   609				t.backup()
   610			case itemPipe:
   611			default:
   612				t.errorf("unexpected %s in operand", token)
   613			}
   614			break
   615		}
   616		if len(cmd.Args) == 0 {
   617			t.errorf("empty command")
   618		}
   619		return cmd
   620	}
   621	
   622	// operand:
   623	//	term .Field*
   624	// An operand is a space-separated component of a command,
   625	// a term possibly followed by field accesses.
   626	// A nil return means the next item is not an operand.
   627	func (t *Tree) operand() Node {
   628		node := t.term()
   629		if node == nil {
   630			return nil
   631		}
   632		if t.peek().typ == itemField {
   633			chain := t.newChain(t.peek().pos, node)
   634			for t.peek().typ == itemField {
   635				chain.Add(t.next().val)
   636			}
   637			// Compatibility with original API: If the term is of type NodeField
   638			// or NodeVariable, just put more fields on the original.
   639			// Otherwise, keep the Chain node.
   640			// Obvious parsing errors involving literal values are detected here.
   641			// More complex error cases will have to be handled at execution time.
   642			switch node.Type() {
   643			case NodeField:
   644				node = t.newField(chain.Position(), chain.String())
   645			case NodeVariable:
   646				node = t.newVariable(chain.Position(), chain.String())
   647			case NodeBool, NodeString, NodeNumber, NodeNil, NodeDot:
   648				t.errorf("unexpected . after term %q", node.String())
   649			default:
   650				node = chain
   651			}
   652		}
   653		return node
   654	}
   655	
   656	// term:
   657	//	literal (number, string, nil, boolean)
   658	//	function (identifier)
   659	//	.
   660	//	.Field
   661	//	$
   662	//	'(' pipeline ')'
   663	// A term is a simple "expression".
   664	// A nil return means the next item is not a term.
   665	func (t *Tree) term() Node {
   666		switch token := t.nextNonSpace(); token.typ {
   667		case itemError:
   668			t.errorf("%s", token.val)
   669		case itemIdentifier:
   670			if !t.hasFunction(token.val) {
   671				t.errorf("function %q not defined", token.val)
   672			}
   673			return NewIdentifier(token.val).SetTree(t).SetPos(token.pos)
   674		case itemDot:
   675			return t.newDot(token.pos)
   676		case itemNil:
   677			return t.newNil(token.pos)
   678		case itemVariable:
   679			return t.useVar(token.pos, token.val)
   680		case itemField:
   681			return t.newField(token.pos, token.val)
   682		case itemBool:
   683			return t.newBool(token.pos, token.val == "true")
   684		case itemCharConstant, itemComplex, itemNumber:
   685			number, err := t.newNumber(token.pos, token.val, token.typ)
   686			if err != nil {
   687				t.error(err)
   688			}
   689			return number
   690		case itemLeftParen:
   691			pipe := t.pipeline("parenthesized pipeline")
   692			if token := t.next(); token.typ != itemRightParen {
   693				t.errorf("unclosed right paren: unexpected %s", token)
   694			}
   695			return pipe
   696		case itemString, itemRawString:
   697			s, err := strconv.Unquote(token.val)
   698			if err != nil {
   699				t.error(err)
   700			}
   701			return t.newString(token.pos, token.val, s)
   702		}
   703		t.backup()
   704		return nil
   705	}
   706	
   707	// hasFunction reports if a function name exists in the Tree's maps.
   708	func (t *Tree) hasFunction(name string) bool {
   709		for _, funcMap := range t.funcs {
   710			if funcMap == nil {
   711				continue
   712			}
   713			if funcMap[name] != nil {
   714				return true
   715			}
   716		}
   717		return false
   718	}
   719	
   720	// popVars trims the variable list to the specified length
   721	func (t *Tree) popVars(n int) {
   722		t.vars = t.vars[:n]
   723	}
   724	
   725	// useVar returns a node for a variable reference. It errors if the
   726	// variable is not defined.
   727	func (t *Tree) useVar(pos Pos, name string) Node {
   728		v := t.newVariable(pos, name)
   729		for _, varName := range t.vars {
   730			if varName == v.Ident[0] {
   731				return v
   732			}
   733		}
   734		t.errorf("undefined variable %q", v.Ident[0])
   735		return nil
   736	}
   737	

View as plain text