xref: /aosp_15_r20/build/soong/androidmk/parser/parser.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1*333d2b36SAndroid Build Coastguard Worker// Copyright 2017 Google Inc. All rights reserved.
2*333d2b36SAndroid Build Coastguard Worker//
3*333d2b36SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*333d2b36SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*333d2b36SAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*333d2b36SAndroid Build Coastguard Worker//
7*333d2b36SAndroid Build Coastguard Worker//     http://www.apache.org/licenses/LICENSE-2.0
8*333d2b36SAndroid Build Coastguard Worker//
9*333d2b36SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*333d2b36SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*333d2b36SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*333d2b36SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*333d2b36SAndroid Build Coastguard Worker// limitations under the License.
14*333d2b36SAndroid Build Coastguard Worker
15*333d2b36SAndroid Build Coastguard Workerpackage parser
16*333d2b36SAndroid Build Coastguard Worker
17*333d2b36SAndroid Build Coastguard Workerimport (
18*333d2b36SAndroid Build Coastguard Worker	"errors"
19*333d2b36SAndroid Build Coastguard Worker	"fmt"
20*333d2b36SAndroid Build Coastguard Worker	"io"
21*333d2b36SAndroid Build Coastguard Worker	"sort"
22*333d2b36SAndroid Build Coastguard Worker	"text/scanner"
23*333d2b36SAndroid Build Coastguard Worker)
24*333d2b36SAndroid Build Coastguard Worker
25*333d2b36SAndroid Build Coastguard Workervar errTooManyErrors = errors.New("too many errors")
26*333d2b36SAndroid Build Coastguard Worker
27*333d2b36SAndroid Build Coastguard Workerconst maxErrors = 100
28*333d2b36SAndroid Build Coastguard Worker
29*333d2b36SAndroid Build Coastguard Workertype ParseError struct {
30*333d2b36SAndroid Build Coastguard Worker	Err error
31*333d2b36SAndroid Build Coastguard Worker	Pos scanner.Position
32*333d2b36SAndroid Build Coastguard Worker}
33*333d2b36SAndroid Build Coastguard Worker
34*333d2b36SAndroid Build Coastguard Workerfunc (e *ParseError) Error() string {
35*333d2b36SAndroid Build Coastguard Worker	return fmt.Sprintf("%s: %s", e.Pos, e.Err)
36*333d2b36SAndroid Build Coastguard Worker}
37*333d2b36SAndroid Build Coastguard Worker
38*333d2b36SAndroid Build Coastguard Workerconst builtinDollar = "__builtin_dollar"
39*333d2b36SAndroid Build Coastguard Worker
40*333d2b36SAndroid Build Coastguard Workervar builtinDollarName = SimpleMakeString(builtinDollar, NoPos)
41*333d2b36SAndroid Build Coastguard Worker
42*333d2b36SAndroid Build Coastguard Workerfunc (p *parser) Parse() ([]Node, []error) {
43*333d2b36SAndroid Build Coastguard Worker	defer func() {
44*333d2b36SAndroid Build Coastguard Worker		if r := recover(); r != nil {
45*333d2b36SAndroid Build Coastguard Worker			if r == errTooManyErrors {
46*333d2b36SAndroid Build Coastguard Worker				return
47*333d2b36SAndroid Build Coastguard Worker			}
48*333d2b36SAndroid Build Coastguard Worker			panic(r)
49*333d2b36SAndroid Build Coastguard Worker		}
50*333d2b36SAndroid Build Coastguard Worker	}()
51*333d2b36SAndroid Build Coastguard Worker
52*333d2b36SAndroid Build Coastguard Worker	p.parseLines()
53*333d2b36SAndroid Build Coastguard Worker	p.accept(scanner.EOF)
54*333d2b36SAndroid Build Coastguard Worker	p.nodes = append(p.nodes, p.comments...)
55*333d2b36SAndroid Build Coastguard Worker	sort.Sort(byPosition(p.nodes))
56*333d2b36SAndroid Build Coastguard Worker
57*333d2b36SAndroid Build Coastguard Worker	return p.nodes, p.errors
58*333d2b36SAndroid Build Coastguard Worker}
59*333d2b36SAndroid Build Coastguard Worker
60*333d2b36SAndroid Build Coastguard Workertype parser struct {
61*333d2b36SAndroid Build Coastguard Worker	scanner  scanner.Scanner
62*333d2b36SAndroid Build Coastguard Worker	tok      rune
63*333d2b36SAndroid Build Coastguard Worker	errors   []error
64*333d2b36SAndroid Build Coastguard Worker	comments []Node
65*333d2b36SAndroid Build Coastguard Worker	nodes    []Node
66*333d2b36SAndroid Build Coastguard Worker	lines    []int
67*333d2b36SAndroid Build Coastguard Worker}
68*333d2b36SAndroid Build Coastguard Worker
69*333d2b36SAndroid Build Coastguard Workerfunc NewParser(filename string, r io.Reader) *parser {
70*333d2b36SAndroid Build Coastguard Worker	p := &parser{}
71*333d2b36SAndroid Build Coastguard Worker	p.lines = []int{0}
72*333d2b36SAndroid Build Coastguard Worker	p.scanner.Init(r)
73*333d2b36SAndroid Build Coastguard Worker	p.scanner.Error = func(sc *scanner.Scanner, msg string) {
74*333d2b36SAndroid Build Coastguard Worker		p.errorf(msg)
75*333d2b36SAndroid Build Coastguard Worker	}
76*333d2b36SAndroid Build Coastguard Worker	p.scanner.Whitespace = 0
77*333d2b36SAndroid Build Coastguard Worker	p.scanner.IsIdentRune = func(ch rune, i int) bool {
78*333d2b36SAndroid Build Coastguard Worker		return ch > 0 && ch != ':' && ch != '#' && ch != '=' && ch != '+' && ch != '$' &&
79*333d2b36SAndroid Build Coastguard Worker			ch != '\\' && ch != '(' && ch != ')' && ch != '{' && ch != '}' && ch != ';' &&
80*333d2b36SAndroid Build Coastguard Worker			ch != '|' && ch != '?' && ch != '\r' && !isWhitespace(ch)
81*333d2b36SAndroid Build Coastguard Worker	}
82*333d2b36SAndroid Build Coastguard Worker	p.scanner.Mode = scanner.ScanIdents
83*333d2b36SAndroid Build Coastguard Worker	p.scanner.Filename = filename
84*333d2b36SAndroid Build Coastguard Worker	p.next()
85*333d2b36SAndroid Build Coastguard Worker	return p
86*333d2b36SAndroid Build Coastguard Worker}
87*333d2b36SAndroid Build Coastguard Worker
88*333d2b36SAndroid Build Coastguard Workerfunc (p *parser) Unpack(pos Pos) scanner.Position {
89*333d2b36SAndroid Build Coastguard Worker	offset := int(pos)
90*333d2b36SAndroid Build Coastguard Worker	line := sort.Search(len(p.lines), func(i int) bool { return p.lines[i] > offset }) - 1
91*333d2b36SAndroid Build Coastguard Worker	return scanner.Position{
92*333d2b36SAndroid Build Coastguard Worker		Filename: p.scanner.Filename,
93*333d2b36SAndroid Build Coastguard Worker		Line:     line + 1,
94*333d2b36SAndroid Build Coastguard Worker		Column:   offset - p.lines[line] + 1,
95*333d2b36SAndroid Build Coastguard Worker		Offset:   offset,
96*333d2b36SAndroid Build Coastguard Worker	}
97*333d2b36SAndroid Build Coastguard Worker}
98*333d2b36SAndroid Build Coastguard Worker
99*333d2b36SAndroid Build Coastguard Workerfunc (p *parser) pos() Pos {
100*333d2b36SAndroid Build Coastguard Worker	pos := p.scanner.Position
101*333d2b36SAndroid Build Coastguard Worker	if !pos.IsValid() {
102*333d2b36SAndroid Build Coastguard Worker		pos = p.scanner.Pos()
103*333d2b36SAndroid Build Coastguard Worker	}
104*333d2b36SAndroid Build Coastguard Worker	return Pos(pos.Offset)
105*333d2b36SAndroid Build Coastguard Worker}
106*333d2b36SAndroid Build Coastguard Worker
107*333d2b36SAndroid Build Coastguard Workerfunc (p *parser) errorf(format string, args ...interface{}) {
108*333d2b36SAndroid Build Coastguard Worker	err := &ParseError{
109*333d2b36SAndroid Build Coastguard Worker		Err: fmt.Errorf(format, args...),
110*333d2b36SAndroid Build Coastguard Worker		Pos: p.scanner.Position,
111*333d2b36SAndroid Build Coastguard Worker	}
112*333d2b36SAndroid Build Coastguard Worker	p.errors = append(p.errors, err)
113*333d2b36SAndroid Build Coastguard Worker	if len(p.errors) >= maxErrors {
114*333d2b36SAndroid Build Coastguard Worker		panic(errTooManyErrors)
115*333d2b36SAndroid Build Coastguard Worker	}
116*333d2b36SAndroid Build Coastguard Worker}
117*333d2b36SAndroid Build Coastguard Worker
118*333d2b36SAndroid Build Coastguard Workerfunc (p *parser) accept(toks ...rune) bool {
119*333d2b36SAndroid Build Coastguard Worker	for _, tok := range toks {
120*333d2b36SAndroid Build Coastguard Worker		if p.tok != tok {
121*333d2b36SAndroid Build Coastguard Worker			p.errorf("expected %s, found %s", scanner.TokenString(tok),
122*333d2b36SAndroid Build Coastguard Worker				scanner.TokenString(p.tok))
123*333d2b36SAndroid Build Coastguard Worker			return false
124*333d2b36SAndroid Build Coastguard Worker		}
125*333d2b36SAndroid Build Coastguard Worker		p.next()
126*333d2b36SAndroid Build Coastguard Worker	}
127*333d2b36SAndroid Build Coastguard Worker	return true
128*333d2b36SAndroid Build Coastguard Worker}
129*333d2b36SAndroid Build Coastguard Worker
130*333d2b36SAndroid Build Coastguard Workerfunc (p *parser) next() {
131*333d2b36SAndroid Build Coastguard Worker	if p.tok != scanner.EOF {
132*333d2b36SAndroid Build Coastguard Worker		p.tok = p.scanner.Scan()
133*333d2b36SAndroid Build Coastguard Worker		for p.tok == '\r' {
134*333d2b36SAndroid Build Coastguard Worker			p.tok = p.scanner.Scan()
135*333d2b36SAndroid Build Coastguard Worker		}
136*333d2b36SAndroid Build Coastguard Worker	}
137*333d2b36SAndroid Build Coastguard Worker	if p.tok == '\n' {
138*333d2b36SAndroid Build Coastguard Worker		p.lines = append(p.lines, p.scanner.Position.Offset+1)
139*333d2b36SAndroid Build Coastguard Worker	}
140*333d2b36SAndroid Build Coastguard Worker}
141*333d2b36SAndroid Build Coastguard Worker
142*333d2b36SAndroid Build Coastguard Workerfunc (p *parser) parseLines() {
143*333d2b36SAndroid Build Coastguard Worker	for {
144*333d2b36SAndroid Build Coastguard Worker		p.ignoreWhitespace()
145*333d2b36SAndroid Build Coastguard Worker
146*333d2b36SAndroid Build Coastguard Worker		if p.parseDirective() {
147*333d2b36SAndroid Build Coastguard Worker			continue
148*333d2b36SAndroid Build Coastguard Worker		}
149*333d2b36SAndroid Build Coastguard Worker
150*333d2b36SAndroid Build Coastguard Worker		ident := p.parseExpression('=', '?', ':', '#', '\n')
151*333d2b36SAndroid Build Coastguard Worker
152*333d2b36SAndroid Build Coastguard Worker		p.ignoreSpaces()
153*333d2b36SAndroid Build Coastguard Worker
154*333d2b36SAndroid Build Coastguard Worker		switch p.tok {
155*333d2b36SAndroid Build Coastguard Worker		case '?':
156*333d2b36SAndroid Build Coastguard Worker			p.accept('?')
157*333d2b36SAndroid Build Coastguard Worker			if p.tok == '=' {
158*333d2b36SAndroid Build Coastguard Worker				p.parseAssignment("?=", nil, ident)
159*333d2b36SAndroid Build Coastguard Worker			} else {
160*333d2b36SAndroid Build Coastguard Worker				p.errorf("expected = after ?")
161*333d2b36SAndroid Build Coastguard Worker			}
162*333d2b36SAndroid Build Coastguard Worker		case '+':
163*333d2b36SAndroid Build Coastguard Worker			p.accept('+')
164*333d2b36SAndroid Build Coastguard Worker			if p.tok == '=' {
165*333d2b36SAndroid Build Coastguard Worker				p.parseAssignment("+=", nil, ident)
166*333d2b36SAndroid Build Coastguard Worker			} else {
167*333d2b36SAndroid Build Coastguard Worker				p.errorf("expected = after +")
168*333d2b36SAndroid Build Coastguard Worker			}
169*333d2b36SAndroid Build Coastguard Worker		case ':':
170*333d2b36SAndroid Build Coastguard Worker			p.accept(':')
171*333d2b36SAndroid Build Coastguard Worker			switch p.tok {
172*333d2b36SAndroid Build Coastguard Worker			case '=':
173*333d2b36SAndroid Build Coastguard Worker				p.parseAssignment(":=", nil, ident)
174*333d2b36SAndroid Build Coastguard Worker			default:
175*333d2b36SAndroid Build Coastguard Worker				p.parseRule(ident)
176*333d2b36SAndroid Build Coastguard Worker			}
177*333d2b36SAndroid Build Coastguard Worker		case '=':
178*333d2b36SAndroid Build Coastguard Worker			p.parseAssignment("=", nil, ident)
179*333d2b36SAndroid Build Coastguard Worker		case '#', '\n', scanner.EOF:
180*333d2b36SAndroid Build Coastguard Worker			ident.TrimRightSpaces()
181*333d2b36SAndroid Build Coastguard Worker			if v, ok := toVariable(ident); ok {
182*333d2b36SAndroid Build Coastguard Worker				p.nodes = append(p.nodes, &v)
183*333d2b36SAndroid Build Coastguard Worker			} else if !ident.Empty() {
184*333d2b36SAndroid Build Coastguard Worker				p.errorf("expected directive, rule, or assignment after ident " + ident.Dump())
185*333d2b36SAndroid Build Coastguard Worker			}
186*333d2b36SAndroid Build Coastguard Worker			switch p.tok {
187*333d2b36SAndroid Build Coastguard Worker			case scanner.EOF:
188*333d2b36SAndroid Build Coastguard Worker				return
189*333d2b36SAndroid Build Coastguard Worker			case '\n':
190*333d2b36SAndroid Build Coastguard Worker				p.accept('\n')
191*333d2b36SAndroid Build Coastguard Worker			case '#':
192*333d2b36SAndroid Build Coastguard Worker				p.parseComment()
193*333d2b36SAndroid Build Coastguard Worker			}
194*333d2b36SAndroid Build Coastguard Worker		default:
195*333d2b36SAndroid Build Coastguard Worker			p.errorf("expected assignment or rule definition, found %s\n",
196*333d2b36SAndroid Build Coastguard Worker				p.scanner.TokenText())
197*333d2b36SAndroid Build Coastguard Worker			return
198*333d2b36SAndroid Build Coastguard Worker		}
199*333d2b36SAndroid Build Coastguard Worker	}
200*333d2b36SAndroid Build Coastguard Worker}
201*333d2b36SAndroid Build Coastguard Worker
202*333d2b36SAndroid Build Coastguard Workerfunc (p *parser) parseDirective() bool {
203*333d2b36SAndroid Build Coastguard Worker	if p.tok != scanner.Ident || !isDirective(p.scanner.TokenText()) {
204*333d2b36SAndroid Build Coastguard Worker		return false
205*333d2b36SAndroid Build Coastguard Worker	}
206*333d2b36SAndroid Build Coastguard Worker
207*333d2b36SAndroid Build Coastguard Worker	d := p.scanner.TokenText()
208*333d2b36SAndroid Build Coastguard Worker	pos := p.pos()
209*333d2b36SAndroid Build Coastguard Worker	p.accept(scanner.Ident)
210*333d2b36SAndroid Build Coastguard Worker	endPos := NoPos
211*333d2b36SAndroid Build Coastguard Worker
212*333d2b36SAndroid Build Coastguard Worker	expression := SimpleMakeString("", pos)
213*333d2b36SAndroid Build Coastguard Worker
214*333d2b36SAndroid Build Coastguard Worker	switch d {
215*333d2b36SAndroid Build Coastguard Worker	case "endif", "endef":
216*333d2b36SAndroid Build Coastguard Worker		// Nothing
217*333d2b36SAndroid Build Coastguard Worker	case "else":
218*333d2b36SAndroid Build Coastguard Worker		p.ignoreSpaces()
219*333d2b36SAndroid Build Coastguard Worker		if p.tok != '\n' && p.tok != '#' {
220*333d2b36SAndroid Build Coastguard Worker			d = p.scanner.TokenText()
221*333d2b36SAndroid Build Coastguard Worker			p.accept(scanner.Ident)
222*333d2b36SAndroid Build Coastguard Worker			if d == "ifdef" || d == "ifndef" || d == "ifeq" || d == "ifneq" {
223*333d2b36SAndroid Build Coastguard Worker				d = "el" + d
224*333d2b36SAndroid Build Coastguard Worker				p.ignoreSpaces()
225*333d2b36SAndroid Build Coastguard Worker				expression = p.parseExpression('#')
226*333d2b36SAndroid Build Coastguard Worker				expression.TrimRightSpaces()
227*333d2b36SAndroid Build Coastguard Worker			} else {
228*333d2b36SAndroid Build Coastguard Worker				p.errorf("expected ifdef/ifndef/ifeq/ifneq, found %s", d)
229*333d2b36SAndroid Build Coastguard Worker			}
230*333d2b36SAndroid Build Coastguard Worker		}
231*333d2b36SAndroid Build Coastguard Worker	case "define":
232*333d2b36SAndroid Build Coastguard Worker		expression, endPos = p.parseDefine()
233*333d2b36SAndroid Build Coastguard Worker	default:
234*333d2b36SAndroid Build Coastguard Worker		p.ignoreSpaces()
235*333d2b36SAndroid Build Coastguard Worker		expression = p.parseExpression('#')
236*333d2b36SAndroid Build Coastguard Worker	}
237*333d2b36SAndroid Build Coastguard Worker
238*333d2b36SAndroid Build Coastguard Worker	p.nodes = append(p.nodes, &Directive{
239*333d2b36SAndroid Build Coastguard Worker		NamePos: pos,
240*333d2b36SAndroid Build Coastguard Worker		Name:    d,
241*333d2b36SAndroid Build Coastguard Worker		Args:    expression,
242*333d2b36SAndroid Build Coastguard Worker		EndPos:  endPos,
243*333d2b36SAndroid Build Coastguard Worker	})
244*333d2b36SAndroid Build Coastguard Worker	return true
245*333d2b36SAndroid Build Coastguard Worker}
246*333d2b36SAndroid Build Coastguard Worker
247*333d2b36SAndroid Build Coastguard Workerfunc (p *parser) parseDefine() (*MakeString, Pos) {
248*333d2b36SAndroid Build Coastguard Worker	value := SimpleMakeString("", p.pos())
249*333d2b36SAndroid Build Coastguard Worker
250*333d2b36SAndroid Build Coastguard Workerloop:
251*333d2b36SAndroid Build Coastguard Worker	for {
252*333d2b36SAndroid Build Coastguard Worker		switch p.tok {
253*333d2b36SAndroid Build Coastguard Worker		case scanner.Ident:
254*333d2b36SAndroid Build Coastguard Worker			value.appendString(p.scanner.TokenText())
255*333d2b36SAndroid Build Coastguard Worker			if p.scanner.TokenText() == "endef" {
256*333d2b36SAndroid Build Coastguard Worker				p.accept(scanner.Ident)
257*333d2b36SAndroid Build Coastguard Worker				break loop
258*333d2b36SAndroid Build Coastguard Worker			}
259*333d2b36SAndroid Build Coastguard Worker			p.accept(scanner.Ident)
260*333d2b36SAndroid Build Coastguard Worker		case '\\':
261*333d2b36SAndroid Build Coastguard Worker			p.parseEscape()
262*333d2b36SAndroid Build Coastguard Worker			switch p.tok {
263*333d2b36SAndroid Build Coastguard Worker			case '\n':
264*333d2b36SAndroid Build Coastguard Worker				value.appendString(" ")
265*333d2b36SAndroid Build Coastguard Worker			case scanner.EOF:
266*333d2b36SAndroid Build Coastguard Worker				p.errorf("expected escaped character, found %s",
267*333d2b36SAndroid Build Coastguard Worker					scanner.TokenString(p.tok))
268*333d2b36SAndroid Build Coastguard Worker				break loop
269*333d2b36SAndroid Build Coastguard Worker			default:
270*333d2b36SAndroid Build Coastguard Worker				value.appendString(`\` + string(p.tok))
271*333d2b36SAndroid Build Coastguard Worker			}
272*333d2b36SAndroid Build Coastguard Worker			p.accept(p.tok)
273*333d2b36SAndroid Build Coastguard Worker		//TODO: handle variables inside defines?  result depends if
274*333d2b36SAndroid Build Coastguard Worker		//define is used in make or rule context
275*333d2b36SAndroid Build Coastguard Worker		//case '$':
276*333d2b36SAndroid Build Coastguard Worker		//	variable := p.parseVariable()
277*333d2b36SAndroid Build Coastguard Worker		//	value.appendVariable(variable)
278*333d2b36SAndroid Build Coastguard Worker		case scanner.EOF:
279*333d2b36SAndroid Build Coastguard Worker			p.errorf("unexpected EOF while looking for endef")
280*333d2b36SAndroid Build Coastguard Worker			break loop
281*333d2b36SAndroid Build Coastguard Worker		default:
282*333d2b36SAndroid Build Coastguard Worker			value.appendString(p.scanner.TokenText())
283*333d2b36SAndroid Build Coastguard Worker			p.accept(p.tok)
284*333d2b36SAndroid Build Coastguard Worker		}
285*333d2b36SAndroid Build Coastguard Worker	}
286*333d2b36SAndroid Build Coastguard Worker
287*333d2b36SAndroid Build Coastguard Worker	return value, p.pos()
288*333d2b36SAndroid Build Coastguard Worker}
289*333d2b36SAndroid Build Coastguard Worker
290*333d2b36SAndroid Build Coastguard Workerfunc (p *parser) parseEscape() {
291*333d2b36SAndroid Build Coastguard Worker	p.scanner.Mode = 0
292*333d2b36SAndroid Build Coastguard Worker	p.accept('\\')
293*333d2b36SAndroid Build Coastguard Worker	p.scanner.Mode = scanner.ScanIdents
294*333d2b36SAndroid Build Coastguard Worker}
295*333d2b36SAndroid Build Coastguard Worker
296*333d2b36SAndroid Build Coastguard Workerfunc (p *parser) parseExpression(end ...rune) *MakeString {
297*333d2b36SAndroid Build Coastguard Worker	value := SimpleMakeString("", p.pos())
298*333d2b36SAndroid Build Coastguard Worker
299*333d2b36SAndroid Build Coastguard Worker	endParen := false
300*333d2b36SAndroid Build Coastguard Worker	for _, r := range end {
301*333d2b36SAndroid Build Coastguard Worker		if r == ')' {
302*333d2b36SAndroid Build Coastguard Worker			endParen = true
303*333d2b36SAndroid Build Coastguard Worker		}
304*333d2b36SAndroid Build Coastguard Worker	}
305*333d2b36SAndroid Build Coastguard Worker	parens := 0
306*333d2b36SAndroid Build Coastguard Worker
307*333d2b36SAndroid Build Coastguard Workerloop:
308*333d2b36SAndroid Build Coastguard Worker	for {
309*333d2b36SAndroid Build Coastguard Worker		if endParen && parens > 0 && p.tok == ')' {
310*333d2b36SAndroid Build Coastguard Worker			parens--
311*333d2b36SAndroid Build Coastguard Worker			value.appendString(")")
312*333d2b36SAndroid Build Coastguard Worker			p.accept(')')
313*333d2b36SAndroid Build Coastguard Worker			continue
314*333d2b36SAndroid Build Coastguard Worker		}
315*333d2b36SAndroid Build Coastguard Worker
316*333d2b36SAndroid Build Coastguard Worker		for _, r := range end {
317*333d2b36SAndroid Build Coastguard Worker			if p.tok == r {
318*333d2b36SAndroid Build Coastguard Worker				break loop
319*333d2b36SAndroid Build Coastguard Worker			}
320*333d2b36SAndroid Build Coastguard Worker		}
321*333d2b36SAndroid Build Coastguard Worker
322*333d2b36SAndroid Build Coastguard Worker		switch p.tok {
323*333d2b36SAndroid Build Coastguard Worker		case '\n':
324*333d2b36SAndroid Build Coastguard Worker			break loop
325*333d2b36SAndroid Build Coastguard Worker		case scanner.Ident:
326*333d2b36SAndroid Build Coastguard Worker			value.appendString(p.scanner.TokenText())
327*333d2b36SAndroid Build Coastguard Worker			p.accept(scanner.Ident)
328*333d2b36SAndroid Build Coastguard Worker		case '\\':
329*333d2b36SAndroid Build Coastguard Worker			p.parseEscape()
330*333d2b36SAndroid Build Coastguard Worker			switch p.tok {
331*333d2b36SAndroid Build Coastguard Worker			case '\n':
332*333d2b36SAndroid Build Coastguard Worker				value.appendString(" ")
333*333d2b36SAndroid Build Coastguard Worker			case scanner.EOF:
334*333d2b36SAndroid Build Coastguard Worker				p.errorf("expected escaped character, found %s",
335*333d2b36SAndroid Build Coastguard Worker					scanner.TokenString(p.tok))
336*333d2b36SAndroid Build Coastguard Worker				return value
337*333d2b36SAndroid Build Coastguard Worker			default:
338*333d2b36SAndroid Build Coastguard Worker				value.appendString(`\` + string(p.tok))
339*333d2b36SAndroid Build Coastguard Worker			}
340*333d2b36SAndroid Build Coastguard Worker			p.accept(p.tok)
341*333d2b36SAndroid Build Coastguard Worker		case '$':
342*333d2b36SAndroid Build Coastguard Worker			var variable Variable
343*333d2b36SAndroid Build Coastguard Worker			variable = p.parseVariable()
344*333d2b36SAndroid Build Coastguard Worker			if variable.Name == builtinDollarName {
345*333d2b36SAndroid Build Coastguard Worker				value.appendString("$")
346*333d2b36SAndroid Build Coastguard Worker			} else {
347*333d2b36SAndroid Build Coastguard Worker				value.appendVariable(variable)
348*333d2b36SAndroid Build Coastguard Worker			}
349*333d2b36SAndroid Build Coastguard Worker		case scanner.EOF:
350*333d2b36SAndroid Build Coastguard Worker			break loop
351*333d2b36SAndroid Build Coastguard Worker		case '(':
352*333d2b36SAndroid Build Coastguard Worker			if endParen {
353*333d2b36SAndroid Build Coastguard Worker				parens++
354*333d2b36SAndroid Build Coastguard Worker			}
355*333d2b36SAndroid Build Coastguard Worker			value.appendString("(")
356*333d2b36SAndroid Build Coastguard Worker			p.accept('(')
357*333d2b36SAndroid Build Coastguard Worker		default:
358*333d2b36SAndroid Build Coastguard Worker			value.appendString(p.scanner.TokenText())
359*333d2b36SAndroid Build Coastguard Worker			p.accept(p.tok)
360*333d2b36SAndroid Build Coastguard Worker		}
361*333d2b36SAndroid Build Coastguard Worker	}
362*333d2b36SAndroid Build Coastguard Worker
363*333d2b36SAndroid Build Coastguard Worker	if parens > 0 {
364*333d2b36SAndroid Build Coastguard Worker		p.errorf("expected closing paren %s", value.Dump())
365*333d2b36SAndroid Build Coastguard Worker	}
366*333d2b36SAndroid Build Coastguard Worker	return value
367*333d2b36SAndroid Build Coastguard Worker}
368*333d2b36SAndroid Build Coastguard Worker
369*333d2b36SAndroid Build Coastguard Workerfunc (p *parser) parseVariable() Variable {
370*333d2b36SAndroid Build Coastguard Worker	pos := p.pos()
371*333d2b36SAndroid Build Coastguard Worker	p.accept('$')
372*333d2b36SAndroid Build Coastguard Worker	var name *MakeString
373*333d2b36SAndroid Build Coastguard Worker	switch p.tok {
374*333d2b36SAndroid Build Coastguard Worker	case '(':
375*333d2b36SAndroid Build Coastguard Worker		return p.parseBracketedVariable('(', ')', pos)
376*333d2b36SAndroid Build Coastguard Worker	case '{':
377*333d2b36SAndroid Build Coastguard Worker		return p.parseBracketedVariable('{', '}', pos)
378*333d2b36SAndroid Build Coastguard Worker	case '$':
379*333d2b36SAndroid Build Coastguard Worker		name = builtinDollarName
380*333d2b36SAndroid Build Coastguard Worker		p.accept(p.tok)
381*333d2b36SAndroid Build Coastguard Worker	case scanner.EOF:
382*333d2b36SAndroid Build Coastguard Worker		p.errorf("expected variable name, found %s",
383*333d2b36SAndroid Build Coastguard Worker			scanner.TokenString(p.tok))
384*333d2b36SAndroid Build Coastguard Worker	default:
385*333d2b36SAndroid Build Coastguard Worker		name = p.parseExpression(variableNameEndRunes...)
386*333d2b36SAndroid Build Coastguard Worker	}
387*333d2b36SAndroid Build Coastguard Worker
388*333d2b36SAndroid Build Coastguard Worker	return p.nameToVariable(name)
389*333d2b36SAndroid Build Coastguard Worker}
390*333d2b36SAndroid Build Coastguard Worker
391*333d2b36SAndroid Build Coastguard Workerfunc (p *parser) parseBracketedVariable(start, end rune, pos Pos) Variable {
392*333d2b36SAndroid Build Coastguard Worker	p.accept(start)
393*333d2b36SAndroid Build Coastguard Worker	name := p.parseExpression(end)
394*333d2b36SAndroid Build Coastguard Worker	p.accept(end)
395*333d2b36SAndroid Build Coastguard Worker	return p.nameToVariable(name)
396*333d2b36SAndroid Build Coastguard Worker}
397*333d2b36SAndroid Build Coastguard Worker
398*333d2b36SAndroid Build Coastguard Workerfunc (p *parser) nameToVariable(name *MakeString) Variable {
399*333d2b36SAndroid Build Coastguard Worker	return Variable{
400*333d2b36SAndroid Build Coastguard Worker		Name: name,
401*333d2b36SAndroid Build Coastguard Worker	}
402*333d2b36SAndroid Build Coastguard Worker}
403*333d2b36SAndroid Build Coastguard Worker
404*333d2b36SAndroid Build Coastguard Workerfunc (p *parser) parseRule(target *MakeString) {
405*333d2b36SAndroid Build Coastguard Worker	prerequisites, newLine := p.parseRulePrerequisites(target)
406*333d2b36SAndroid Build Coastguard Worker
407*333d2b36SAndroid Build Coastguard Worker	recipe := ""
408*333d2b36SAndroid Build Coastguard Worker	recipePos := p.pos()
409*333d2b36SAndroid Build Coastguard Workerloop:
410*333d2b36SAndroid Build Coastguard Worker	for {
411*333d2b36SAndroid Build Coastguard Worker		if newLine {
412*333d2b36SAndroid Build Coastguard Worker			if p.tok == '\t' {
413*333d2b36SAndroid Build Coastguard Worker				p.accept('\t')
414*333d2b36SAndroid Build Coastguard Worker				newLine = false
415*333d2b36SAndroid Build Coastguard Worker				continue loop
416*333d2b36SAndroid Build Coastguard Worker			} else if p.tok == '\n' {
417*333d2b36SAndroid Build Coastguard Worker				p.accept('\n')
418*333d2b36SAndroid Build Coastguard Worker				continue loop
419*333d2b36SAndroid Build Coastguard Worker			} else if p.parseDirective() {
420*333d2b36SAndroid Build Coastguard Worker				newLine = false
421*333d2b36SAndroid Build Coastguard Worker				continue
422*333d2b36SAndroid Build Coastguard Worker			} else {
423*333d2b36SAndroid Build Coastguard Worker				break loop
424*333d2b36SAndroid Build Coastguard Worker			}
425*333d2b36SAndroid Build Coastguard Worker		}
426*333d2b36SAndroid Build Coastguard Worker
427*333d2b36SAndroid Build Coastguard Worker		newLine = false
428*333d2b36SAndroid Build Coastguard Worker		switch p.tok {
429*333d2b36SAndroid Build Coastguard Worker		case '\\':
430*333d2b36SAndroid Build Coastguard Worker			p.parseEscape()
431*333d2b36SAndroid Build Coastguard Worker			recipe += string(p.tok)
432*333d2b36SAndroid Build Coastguard Worker			p.accept(p.tok)
433*333d2b36SAndroid Build Coastguard Worker		case '\n':
434*333d2b36SAndroid Build Coastguard Worker			newLine = true
435*333d2b36SAndroid Build Coastguard Worker			recipe += "\n"
436*333d2b36SAndroid Build Coastguard Worker			p.accept('\n')
437*333d2b36SAndroid Build Coastguard Worker		case scanner.EOF:
438*333d2b36SAndroid Build Coastguard Worker			break loop
439*333d2b36SAndroid Build Coastguard Worker		default:
440*333d2b36SAndroid Build Coastguard Worker			recipe += p.scanner.TokenText()
441*333d2b36SAndroid Build Coastguard Worker			p.accept(p.tok)
442*333d2b36SAndroid Build Coastguard Worker		}
443*333d2b36SAndroid Build Coastguard Worker	}
444*333d2b36SAndroid Build Coastguard Worker
445*333d2b36SAndroid Build Coastguard Worker	if prerequisites != nil {
446*333d2b36SAndroid Build Coastguard Worker		p.nodes = append(p.nodes, &Rule{
447*333d2b36SAndroid Build Coastguard Worker			Target:        target,
448*333d2b36SAndroid Build Coastguard Worker			Prerequisites: prerequisites,
449*333d2b36SAndroid Build Coastguard Worker			Recipe:        recipe,
450*333d2b36SAndroid Build Coastguard Worker			RecipePos:     recipePos,
451*333d2b36SAndroid Build Coastguard Worker			RecipeEndPos:  p.pos(),
452*333d2b36SAndroid Build Coastguard Worker		})
453*333d2b36SAndroid Build Coastguard Worker	}
454*333d2b36SAndroid Build Coastguard Worker}
455*333d2b36SAndroid Build Coastguard Worker
456*333d2b36SAndroid Build Coastguard Workerfunc (p *parser) parseRulePrerequisites(target *MakeString) (*MakeString, bool) {
457*333d2b36SAndroid Build Coastguard Worker	newLine := false
458*333d2b36SAndroid Build Coastguard Worker
459*333d2b36SAndroid Build Coastguard Worker	p.ignoreSpaces()
460*333d2b36SAndroid Build Coastguard Worker
461*333d2b36SAndroid Build Coastguard Worker	prerequisites := p.parseExpression('#', '\n', ';', ':', '=')
462*333d2b36SAndroid Build Coastguard Worker
463*333d2b36SAndroid Build Coastguard Worker	switch p.tok {
464*333d2b36SAndroid Build Coastguard Worker	case '\n':
465*333d2b36SAndroid Build Coastguard Worker		p.accept('\n')
466*333d2b36SAndroid Build Coastguard Worker		newLine = true
467*333d2b36SAndroid Build Coastguard Worker	case '#':
468*333d2b36SAndroid Build Coastguard Worker		p.parseComment()
469*333d2b36SAndroid Build Coastguard Worker		newLine = true
470*333d2b36SAndroid Build Coastguard Worker	case ';':
471*333d2b36SAndroid Build Coastguard Worker		p.accept(';')
472*333d2b36SAndroid Build Coastguard Worker	case ':':
473*333d2b36SAndroid Build Coastguard Worker		p.accept(':')
474*333d2b36SAndroid Build Coastguard Worker		if p.tok == '=' {
475*333d2b36SAndroid Build Coastguard Worker			p.parseAssignment(":=", target, prerequisites)
476*333d2b36SAndroid Build Coastguard Worker			return nil, true
477*333d2b36SAndroid Build Coastguard Worker		} else {
478*333d2b36SAndroid Build Coastguard Worker			more := p.parseExpression('#', '\n', ';')
479*333d2b36SAndroid Build Coastguard Worker			prerequisites.appendMakeString(more)
480*333d2b36SAndroid Build Coastguard Worker		}
481*333d2b36SAndroid Build Coastguard Worker	case '=':
482*333d2b36SAndroid Build Coastguard Worker		p.parseAssignment("=", target, prerequisites)
483*333d2b36SAndroid Build Coastguard Worker		return nil, true
484*333d2b36SAndroid Build Coastguard Worker	case scanner.EOF:
485*333d2b36SAndroid Build Coastguard Worker		// do nothing
486*333d2b36SAndroid Build Coastguard Worker	default:
487*333d2b36SAndroid Build Coastguard Worker		p.errorf("unexpected token %s after rule prerequisites", scanner.TokenString(p.tok))
488*333d2b36SAndroid Build Coastguard Worker	}
489*333d2b36SAndroid Build Coastguard Worker
490*333d2b36SAndroid Build Coastguard Worker	return prerequisites, newLine
491*333d2b36SAndroid Build Coastguard Worker}
492*333d2b36SAndroid Build Coastguard Worker
493*333d2b36SAndroid Build Coastguard Workerfunc (p *parser) parseComment() {
494*333d2b36SAndroid Build Coastguard Worker	pos := p.pos()
495*333d2b36SAndroid Build Coastguard Worker	p.accept('#')
496*333d2b36SAndroid Build Coastguard Worker	comment := ""
497*333d2b36SAndroid Build Coastguard Workerloop:
498*333d2b36SAndroid Build Coastguard Worker	for {
499*333d2b36SAndroid Build Coastguard Worker		switch p.tok {
500*333d2b36SAndroid Build Coastguard Worker		case '\\':
501*333d2b36SAndroid Build Coastguard Worker			p.parseEscape()
502*333d2b36SAndroid Build Coastguard Worker			comment += "\\" + p.scanner.TokenText()
503*333d2b36SAndroid Build Coastguard Worker			p.accept(p.tok)
504*333d2b36SAndroid Build Coastguard Worker		case '\n':
505*333d2b36SAndroid Build Coastguard Worker			p.accept('\n')
506*333d2b36SAndroid Build Coastguard Worker			break loop
507*333d2b36SAndroid Build Coastguard Worker		case scanner.EOF:
508*333d2b36SAndroid Build Coastguard Worker			break loop
509*333d2b36SAndroid Build Coastguard Worker		default:
510*333d2b36SAndroid Build Coastguard Worker			comment += p.scanner.TokenText()
511*333d2b36SAndroid Build Coastguard Worker			p.accept(p.tok)
512*333d2b36SAndroid Build Coastguard Worker		}
513*333d2b36SAndroid Build Coastguard Worker	}
514*333d2b36SAndroid Build Coastguard Worker
515*333d2b36SAndroid Build Coastguard Worker	p.comments = append(p.comments, &Comment{
516*333d2b36SAndroid Build Coastguard Worker		CommentPos: pos,
517*333d2b36SAndroid Build Coastguard Worker		Comment:    comment,
518*333d2b36SAndroid Build Coastguard Worker	})
519*333d2b36SAndroid Build Coastguard Worker}
520*333d2b36SAndroid Build Coastguard Worker
521*333d2b36SAndroid Build Coastguard Workerfunc (p *parser) parseAssignment(t string, target *MakeString, ident *MakeString) {
522*333d2b36SAndroid Build Coastguard Worker	// The value of an assignment is everything including and after the first
523*333d2b36SAndroid Build Coastguard Worker	// non-whitespace character after the = until the end of the logical line,
524*333d2b36SAndroid Build Coastguard Worker	// which may included escaped newlines
525*333d2b36SAndroid Build Coastguard Worker	p.accept('=')
526*333d2b36SAndroid Build Coastguard Worker	value := p.parseExpression('#')
527*333d2b36SAndroid Build Coastguard Worker	value.TrimLeftSpaces()
528*333d2b36SAndroid Build Coastguard Worker	if ident.EndsWith('+') && t == "=" {
529*333d2b36SAndroid Build Coastguard Worker		ident.TrimRightOne()
530*333d2b36SAndroid Build Coastguard Worker		t = "+="
531*333d2b36SAndroid Build Coastguard Worker	}
532*333d2b36SAndroid Build Coastguard Worker
533*333d2b36SAndroid Build Coastguard Worker	ident.TrimRightSpaces()
534*333d2b36SAndroid Build Coastguard Worker
535*333d2b36SAndroid Build Coastguard Worker	p.nodes = append(p.nodes, &Assignment{
536*333d2b36SAndroid Build Coastguard Worker		Name:   ident,
537*333d2b36SAndroid Build Coastguard Worker		Value:  value,
538*333d2b36SAndroid Build Coastguard Worker		Target: target,
539*333d2b36SAndroid Build Coastguard Worker		Type:   t,
540*333d2b36SAndroid Build Coastguard Worker	})
541*333d2b36SAndroid Build Coastguard Worker}
542*333d2b36SAndroid Build Coastguard Worker
543*333d2b36SAndroid Build Coastguard Workertype androidMkModule struct {
544*333d2b36SAndroid Build Coastguard Worker	assignments map[string]string
545*333d2b36SAndroid Build Coastguard Worker}
546*333d2b36SAndroid Build Coastguard Worker
547*333d2b36SAndroid Build Coastguard Workertype androidMkFile struct {
548*333d2b36SAndroid Build Coastguard Worker	assignments map[string]string
549*333d2b36SAndroid Build Coastguard Worker	modules     []androidMkModule
550*333d2b36SAndroid Build Coastguard Worker	includes    []string
551*333d2b36SAndroid Build Coastguard Worker}
552*333d2b36SAndroid Build Coastguard Worker
553*333d2b36SAndroid Build Coastguard Workervar directives = [...]string{
554*333d2b36SAndroid Build Coastguard Worker	"define",
555*333d2b36SAndroid Build Coastguard Worker	"else",
556*333d2b36SAndroid Build Coastguard Worker	"endef",
557*333d2b36SAndroid Build Coastguard Worker	"endif",
558*333d2b36SAndroid Build Coastguard Worker	"export",
559*333d2b36SAndroid Build Coastguard Worker	"ifdef",
560*333d2b36SAndroid Build Coastguard Worker	"ifeq",
561*333d2b36SAndroid Build Coastguard Worker	"ifndef",
562*333d2b36SAndroid Build Coastguard Worker	"ifneq",
563*333d2b36SAndroid Build Coastguard Worker	"include",
564*333d2b36SAndroid Build Coastguard Worker	"-include",
565*333d2b36SAndroid Build Coastguard Worker	"unexport",
566*333d2b36SAndroid Build Coastguard Worker}
567*333d2b36SAndroid Build Coastguard Worker
568*333d2b36SAndroid Build Coastguard Workervar functions = [...]string{
569*333d2b36SAndroid Build Coastguard Worker	"abspath",
570*333d2b36SAndroid Build Coastguard Worker	"addprefix",
571*333d2b36SAndroid Build Coastguard Worker	"addsuffix",
572*333d2b36SAndroid Build Coastguard Worker	"basename",
573*333d2b36SAndroid Build Coastguard Worker	"dir",
574*333d2b36SAndroid Build Coastguard Worker	"notdir",
575*333d2b36SAndroid Build Coastguard Worker	"subst",
576*333d2b36SAndroid Build Coastguard Worker	"suffix",
577*333d2b36SAndroid Build Coastguard Worker	"filter",
578*333d2b36SAndroid Build Coastguard Worker	"filter-out",
579*333d2b36SAndroid Build Coastguard Worker	"findstring",
580*333d2b36SAndroid Build Coastguard Worker	"firstword",
581*333d2b36SAndroid Build Coastguard Worker	"flavor",
582*333d2b36SAndroid Build Coastguard Worker	"join",
583*333d2b36SAndroid Build Coastguard Worker	"lastword",
584*333d2b36SAndroid Build Coastguard Worker	"patsubst",
585*333d2b36SAndroid Build Coastguard Worker	"realpath",
586*333d2b36SAndroid Build Coastguard Worker	"shell",
587*333d2b36SAndroid Build Coastguard Worker	"sort",
588*333d2b36SAndroid Build Coastguard Worker	"strip",
589*333d2b36SAndroid Build Coastguard Worker	"wildcard",
590*333d2b36SAndroid Build Coastguard Worker	"word",
591*333d2b36SAndroid Build Coastguard Worker	"wordlist",
592*333d2b36SAndroid Build Coastguard Worker	"words",
593*333d2b36SAndroid Build Coastguard Worker	"origin",
594*333d2b36SAndroid Build Coastguard Worker	"foreach",
595*333d2b36SAndroid Build Coastguard Worker	"call",
596*333d2b36SAndroid Build Coastguard Worker	"info",
597*333d2b36SAndroid Build Coastguard Worker	"error",
598*333d2b36SAndroid Build Coastguard Worker	"warning",
599*333d2b36SAndroid Build Coastguard Worker	"if",
600*333d2b36SAndroid Build Coastguard Worker	"or",
601*333d2b36SAndroid Build Coastguard Worker	"and",
602*333d2b36SAndroid Build Coastguard Worker	"value",
603*333d2b36SAndroid Build Coastguard Worker	"eval",
604*333d2b36SAndroid Build Coastguard Worker	"file",
605*333d2b36SAndroid Build Coastguard Worker}
606*333d2b36SAndroid Build Coastguard Worker
607*333d2b36SAndroid Build Coastguard Workerfunc init() {
608*333d2b36SAndroid Build Coastguard Worker	sort.Strings(directives[:])
609*333d2b36SAndroid Build Coastguard Worker	sort.Strings(functions[:])
610*333d2b36SAndroid Build Coastguard Worker}
611*333d2b36SAndroid Build Coastguard Worker
612*333d2b36SAndroid Build Coastguard Workerfunc isDirective(s string) bool {
613*333d2b36SAndroid Build Coastguard Worker	for _, d := range directives {
614*333d2b36SAndroid Build Coastguard Worker		if s == d {
615*333d2b36SAndroid Build Coastguard Worker			return true
616*333d2b36SAndroid Build Coastguard Worker		} else if s < d {
617*333d2b36SAndroid Build Coastguard Worker			return false
618*333d2b36SAndroid Build Coastguard Worker		}
619*333d2b36SAndroid Build Coastguard Worker	}
620*333d2b36SAndroid Build Coastguard Worker	return false
621*333d2b36SAndroid Build Coastguard Worker}
622*333d2b36SAndroid Build Coastguard Worker
623*333d2b36SAndroid Build Coastguard Workerfunc isFunctionName(s string) bool {
624*333d2b36SAndroid Build Coastguard Worker	for _, f := range functions {
625*333d2b36SAndroid Build Coastguard Worker		if s == f {
626*333d2b36SAndroid Build Coastguard Worker			return true
627*333d2b36SAndroid Build Coastguard Worker		} else if s < f {
628*333d2b36SAndroid Build Coastguard Worker			return false
629*333d2b36SAndroid Build Coastguard Worker		}
630*333d2b36SAndroid Build Coastguard Worker	}
631*333d2b36SAndroid Build Coastguard Worker	return false
632*333d2b36SAndroid Build Coastguard Worker}
633*333d2b36SAndroid Build Coastguard Worker
634*333d2b36SAndroid Build Coastguard Workerfunc isWhitespace(ch rune) bool {
635*333d2b36SAndroid Build Coastguard Worker	return ch == ' ' || ch == '\t' || ch == '\n'
636*333d2b36SAndroid Build Coastguard Worker}
637*333d2b36SAndroid Build Coastguard Worker
638*333d2b36SAndroid Build Coastguard Workerfunc isValidVariableRune(ch rune) bool {
639*333d2b36SAndroid Build Coastguard Worker	return ch != scanner.Ident && ch != ':' && ch != '=' && ch != '#'
640*333d2b36SAndroid Build Coastguard Worker}
641*333d2b36SAndroid Build Coastguard Worker
642*333d2b36SAndroid Build Coastguard Workervar whitespaceRunes = []rune{' ', '\t', '\n'}
643*333d2b36SAndroid Build Coastguard Workervar variableNameEndRunes = append([]rune{':', '=', '#', ')', '}'}, whitespaceRunes...)
644*333d2b36SAndroid Build Coastguard Worker
645*333d2b36SAndroid Build Coastguard Workerfunc (p *parser) ignoreSpaces() int {
646*333d2b36SAndroid Build Coastguard Worker	skipped := 0
647*333d2b36SAndroid Build Coastguard Worker	for p.tok == ' ' || p.tok == '\t' {
648*333d2b36SAndroid Build Coastguard Worker		p.accept(p.tok)
649*333d2b36SAndroid Build Coastguard Worker		skipped++
650*333d2b36SAndroid Build Coastguard Worker	}
651*333d2b36SAndroid Build Coastguard Worker	return skipped
652*333d2b36SAndroid Build Coastguard Worker}
653*333d2b36SAndroid Build Coastguard Worker
654*333d2b36SAndroid Build Coastguard Workerfunc (p *parser) ignoreWhitespace() {
655*333d2b36SAndroid Build Coastguard Worker	for isWhitespace(p.tok) {
656*333d2b36SAndroid Build Coastguard Worker		p.accept(p.tok)
657*333d2b36SAndroid Build Coastguard Worker	}
658*333d2b36SAndroid Build Coastguard Worker}
659