xref: /aosp_15_r20/build/blueprint/parser/parser.go (revision 1fa6dee971e1612fa5cc0aa5ca2d35a22e2c34a3)
1*1fa6dee9SAndroid Build Coastguard Worker// Copyright 2014 Google Inc. All rights reserved.
2*1fa6dee9SAndroid Build Coastguard Worker//
3*1fa6dee9SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*1fa6dee9SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*1fa6dee9SAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*1fa6dee9SAndroid Build Coastguard Worker//
7*1fa6dee9SAndroid Build Coastguard Worker//     http://www.apache.org/licenses/LICENSE-2.0
8*1fa6dee9SAndroid Build Coastguard Worker//
9*1fa6dee9SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*1fa6dee9SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*1fa6dee9SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*1fa6dee9SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*1fa6dee9SAndroid Build Coastguard Worker// limitations under the License.
14*1fa6dee9SAndroid Build Coastguard Worker
15*1fa6dee9SAndroid Build Coastguard Workerpackage parser
16*1fa6dee9SAndroid Build Coastguard Worker
17*1fa6dee9SAndroid Build Coastguard Workerimport (
18*1fa6dee9SAndroid Build Coastguard Worker	"errors"
19*1fa6dee9SAndroid Build Coastguard Worker	"fmt"
20*1fa6dee9SAndroid Build Coastguard Worker	"io"
21*1fa6dee9SAndroid Build Coastguard Worker	"sort"
22*1fa6dee9SAndroid Build Coastguard Worker	"strconv"
23*1fa6dee9SAndroid Build Coastguard Worker	"strings"
24*1fa6dee9SAndroid Build Coastguard Worker	"text/scanner"
25*1fa6dee9SAndroid Build Coastguard Worker)
26*1fa6dee9SAndroid Build Coastguard Worker
27*1fa6dee9SAndroid Build Coastguard Workervar errTooManyErrors = errors.New("too many errors")
28*1fa6dee9SAndroid Build Coastguard Worker
29*1fa6dee9SAndroid Build Coastguard Workerconst maxErrors = 1
30*1fa6dee9SAndroid Build Coastguard Worker
31*1fa6dee9SAndroid Build Coastguard Workerconst default_select_branch_name = "__soong_conditions_default__"
32*1fa6dee9SAndroid Build Coastguard Workerconst any_select_branch_name = "__soong_conditions_any__"
33*1fa6dee9SAndroid Build Coastguard Worker
34*1fa6dee9SAndroid Build Coastguard Workertype ParseError struct {
35*1fa6dee9SAndroid Build Coastguard Worker	Err error
36*1fa6dee9SAndroid Build Coastguard Worker	Pos scanner.Position
37*1fa6dee9SAndroid Build Coastguard Worker}
38*1fa6dee9SAndroid Build Coastguard Worker
39*1fa6dee9SAndroid Build Coastguard Workerfunc (e *ParseError) Error() string {
40*1fa6dee9SAndroid Build Coastguard Worker	return fmt.Sprintf("%s: %s", e.Pos, e.Err)
41*1fa6dee9SAndroid Build Coastguard Worker}
42*1fa6dee9SAndroid Build Coastguard Worker
43*1fa6dee9SAndroid Build Coastguard Workertype File struct {
44*1fa6dee9SAndroid Build Coastguard Worker	Name     string
45*1fa6dee9SAndroid Build Coastguard Worker	Defs     []Definition
46*1fa6dee9SAndroid Build Coastguard Worker	Comments []*CommentGroup
47*1fa6dee9SAndroid Build Coastguard Worker}
48*1fa6dee9SAndroid Build Coastguard Worker
49*1fa6dee9SAndroid Build Coastguard Workerfunc parse(p *parser) (file *File, errs []error) {
50*1fa6dee9SAndroid Build Coastguard Worker	defer func() {
51*1fa6dee9SAndroid Build Coastguard Worker		if r := recover(); r != nil {
52*1fa6dee9SAndroid Build Coastguard Worker			if r == errTooManyErrors {
53*1fa6dee9SAndroid Build Coastguard Worker				errs = p.errors
54*1fa6dee9SAndroid Build Coastguard Worker				return
55*1fa6dee9SAndroid Build Coastguard Worker			}
56*1fa6dee9SAndroid Build Coastguard Worker			panic(r)
57*1fa6dee9SAndroid Build Coastguard Worker		}
58*1fa6dee9SAndroid Build Coastguard Worker	}()
59*1fa6dee9SAndroid Build Coastguard Worker
60*1fa6dee9SAndroid Build Coastguard Worker	p.next()
61*1fa6dee9SAndroid Build Coastguard Worker	defs := p.parseDefinitions()
62*1fa6dee9SAndroid Build Coastguard Worker	p.accept(scanner.EOF)
63*1fa6dee9SAndroid Build Coastguard Worker	errs = p.errors
64*1fa6dee9SAndroid Build Coastguard Worker	comments := p.comments
65*1fa6dee9SAndroid Build Coastguard Worker
66*1fa6dee9SAndroid Build Coastguard Worker	return &File{
67*1fa6dee9SAndroid Build Coastguard Worker		Name:     p.scanner.Filename,
68*1fa6dee9SAndroid Build Coastguard Worker		Defs:     defs,
69*1fa6dee9SAndroid Build Coastguard Worker		Comments: comments,
70*1fa6dee9SAndroid Build Coastguard Worker	}, errs
71*1fa6dee9SAndroid Build Coastguard Worker
72*1fa6dee9SAndroid Build Coastguard Worker}
73*1fa6dee9SAndroid Build Coastguard Worker
74*1fa6dee9SAndroid Build Coastguard Workerfunc ParseAndEval(filename string, r io.Reader, scope *Scope) (file *File, errs []error) {
75*1fa6dee9SAndroid Build Coastguard Worker	file, errs = Parse(filename, r)
76*1fa6dee9SAndroid Build Coastguard Worker	if len(errs) > 0 {
77*1fa6dee9SAndroid Build Coastguard Worker		return nil, errs
78*1fa6dee9SAndroid Build Coastguard Worker	}
79*1fa6dee9SAndroid Build Coastguard Worker
80*1fa6dee9SAndroid Build Coastguard Worker	// evaluate all module properties
81*1fa6dee9SAndroid Build Coastguard Worker	var newDefs []Definition
82*1fa6dee9SAndroid Build Coastguard Worker	for _, def := range file.Defs {
83*1fa6dee9SAndroid Build Coastguard Worker		switch d := def.(type) {
84*1fa6dee9SAndroid Build Coastguard Worker		case *Module:
85*1fa6dee9SAndroid Build Coastguard Worker			for _, prop := range d.Map.Properties {
86*1fa6dee9SAndroid Build Coastguard Worker				newval, err := prop.Value.Eval(scope)
87*1fa6dee9SAndroid Build Coastguard Worker				if err != nil {
88*1fa6dee9SAndroid Build Coastguard Worker					return nil, []error{err}
89*1fa6dee9SAndroid Build Coastguard Worker				}
90*1fa6dee9SAndroid Build Coastguard Worker				switch newval.(type) {
91*1fa6dee9SAndroid Build Coastguard Worker				case *String, *Bool, *Int64, *Select, *Map, *List:
92*1fa6dee9SAndroid Build Coastguard Worker					// ok
93*1fa6dee9SAndroid Build Coastguard Worker				default:
94*1fa6dee9SAndroid Build Coastguard Worker					panic(fmt.Sprintf("Evaled but got %#v\n", newval))
95*1fa6dee9SAndroid Build Coastguard Worker				}
96*1fa6dee9SAndroid Build Coastguard Worker				prop.Value = newval
97*1fa6dee9SAndroid Build Coastguard Worker			}
98*1fa6dee9SAndroid Build Coastguard Worker			newDefs = append(newDefs, d)
99*1fa6dee9SAndroid Build Coastguard Worker		case *Assignment:
100*1fa6dee9SAndroid Build Coastguard Worker			if err := scope.HandleAssignment(d); err != nil {
101*1fa6dee9SAndroid Build Coastguard Worker				return nil, []error{err}
102*1fa6dee9SAndroid Build Coastguard Worker			}
103*1fa6dee9SAndroid Build Coastguard Worker		}
104*1fa6dee9SAndroid Build Coastguard Worker	}
105*1fa6dee9SAndroid Build Coastguard Worker
106*1fa6dee9SAndroid Build Coastguard Worker	// This is not strictly necessary, but removing the assignments from
107*1fa6dee9SAndroid Build Coastguard Worker	// the result makes it clearer that this is an evaluated file.
108*1fa6dee9SAndroid Build Coastguard Worker	// We could also consider adding a "EvaluatedFile" type to return.
109*1fa6dee9SAndroid Build Coastguard Worker	file.Defs = newDefs
110*1fa6dee9SAndroid Build Coastguard Worker
111*1fa6dee9SAndroid Build Coastguard Worker	return file, nil
112*1fa6dee9SAndroid Build Coastguard Worker}
113*1fa6dee9SAndroid Build Coastguard Worker
114*1fa6dee9SAndroid Build Coastguard Workerfunc Parse(filename string, r io.Reader) (file *File, errs []error) {
115*1fa6dee9SAndroid Build Coastguard Worker	p := newParser(r)
116*1fa6dee9SAndroid Build Coastguard Worker	p.scanner.Filename = filename
117*1fa6dee9SAndroid Build Coastguard Worker
118*1fa6dee9SAndroid Build Coastguard Worker	return parse(p)
119*1fa6dee9SAndroid Build Coastguard Worker}
120*1fa6dee9SAndroid Build Coastguard Worker
121*1fa6dee9SAndroid Build Coastguard Workerfunc ParseExpression(r io.Reader) (value Expression, errs []error) {
122*1fa6dee9SAndroid Build Coastguard Worker	p := newParser(r)
123*1fa6dee9SAndroid Build Coastguard Worker	p.next()
124*1fa6dee9SAndroid Build Coastguard Worker	value = p.parseExpression()
125*1fa6dee9SAndroid Build Coastguard Worker	p.accept(scanner.EOF)
126*1fa6dee9SAndroid Build Coastguard Worker	errs = p.errors
127*1fa6dee9SAndroid Build Coastguard Worker	return
128*1fa6dee9SAndroid Build Coastguard Worker}
129*1fa6dee9SAndroid Build Coastguard Worker
130*1fa6dee9SAndroid Build Coastguard Workertype parser struct {
131*1fa6dee9SAndroid Build Coastguard Worker	scanner  scanner.Scanner
132*1fa6dee9SAndroid Build Coastguard Worker	tok      rune
133*1fa6dee9SAndroid Build Coastguard Worker	errors   []error
134*1fa6dee9SAndroid Build Coastguard Worker	comments []*CommentGroup
135*1fa6dee9SAndroid Build Coastguard Worker}
136*1fa6dee9SAndroid Build Coastguard Worker
137*1fa6dee9SAndroid Build Coastguard Workerfunc newParser(r io.Reader) *parser {
138*1fa6dee9SAndroid Build Coastguard Worker	p := &parser{}
139*1fa6dee9SAndroid Build Coastguard Worker	p.scanner.Init(r)
140*1fa6dee9SAndroid Build Coastguard Worker	p.scanner.Error = func(sc *scanner.Scanner, msg string) {
141*1fa6dee9SAndroid Build Coastguard Worker		p.errorf(msg)
142*1fa6dee9SAndroid Build Coastguard Worker	}
143*1fa6dee9SAndroid Build Coastguard Worker	p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanStrings |
144*1fa6dee9SAndroid Build Coastguard Worker		scanner.ScanRawStrings | scanner.ScanComments
145*1fa6dee9SAndroid Build Coastguard Worker	return p
146*1fa6dee9SAndroid Build Coastguard Worker}
147*1fa6dee9SAndroid Build Coastguard Worker
148*1fa6dee9SAndroid Build Coastguard Workerfunc (p *parser) error(err error) {
149*1fa6dee9SAndroid Build Coastguard Worker	pos := p.scanner.Position
150*1fa6dee9SAndroid Build Coastguard Worker	if !pos.IsValid() {
151*1fa6dee9SAndroid Build Coastguard Worker		pos = p.scanner.Pos()
152*1fa6dee9SAndroid Build Coastguard Worker	}
153*1fa6dee9SAndroid Build Coastguard Worker	err = &ParseError{
154*1fa6dee9SAndroid Build Coastguard Worker		Err: err,
155*1fa6dee9SAndroid Build Coastguard Worker		Pos: pos,
156*1fa6dee9SAndroid Build Coastguard Worker	}
157*1fa6dee9SAndroid Build Coastguard Worker	p.errors = append(p.errors, err)
158*1fa6dee9SAndroid Build Coastguard Worker	if len(p.errors) >= maxErrors {
159*1fa6dee9SAndroid Build Coastguard Worker		panic(errTooManyErrors)
160*1fa6dee9SAndroid Build Coastguard Worker	}
161*1fa6dee9SAndroid Build Coastguard Worker}
162*1fa6dee9SAndroid Build Coastguard Worker
163*1fa6dee9SAndroid Build Coastguard Workerfunc (p *parser) errorf(format string, args ...interface{}) {
164*1fa6dee9SAndroid Build Coastguard Worker	p.error(fmt.Errorf(format, args...))
165*1fa6dee9SAndroid Build Coastguard Worker}
166*1fa6dee9SAndroid Build Coastguard Worker
167*1fa6dee9SAndroid Build Coastguard Workerfunc (p *parser) accept(toks ...rune) bool {
168*1fa6dee9SAndroid Build Coastguard Worker	for _, tok := range toks {
169*1fa6dee9SAndroid Build Coastguard Worker		if p.tok != tok {
170*1fa6dee9SAndroid Build Coastguard Worker			p.errorf("expected %s, found %s", scanner.TokenString(tok),
171*1fa6dee9SAndroid Build Coastguard Worker				scanner.TokenString(p.tok))
172*1fa6dee9SAndroid Build Coastguard Worker			return false
173*1fa6dee9SAndroid Build Coastguard Worker		}
174*1fa6dee9SAndroid Build Coastguard Worker		p.next()
175*1fa6dee9SAndroid Build Coastguard Worker	}
176*1fa6dee9SAndroid Build Coastguard Worker	return true
177*1fa6dee9SAndroid Build Coastguard Worker}
178*1fa6dee9SAndroid Build Coastguard Worker
179*1fa6dee9SAndroid Build Coastguard Workerfunc (p *parser) next() {
180*1fa6dee9SAndroid Build Coastguard Worker	if p.tok != scanner.EOF {
181*1fa6dee9SAndroid Build Coastguard Worker		p.tok = p.scanner.Scan()
182*1fa6dee9SAndroid Build Coastguard Worker		if p.tok == scanner.Comment {
183*1fa6dee9SAndroid Build Coastguard Worker			var comments []*Comment
184*1fa6dee9SAndroid Build Coastguard Worker			for p.tok == scanner.Comment {
185*1fa6dee9SAndroid Build Coastguard Worker				lines := strings.Split(p.scanner.TokenText(), "\n")
186*1fa6dee9SAndroid Build Coastguard Worker				if len(comments) > 0 && p.scanner.Position.Line > comments[len(comments)-1].End().Line+1 {
187*1fa6dee9SAndroid Build Coastguard Worker					p.comments = append(p.comments, &CommentGroup{Comments: comments})
188*1fa6dee9SAndroid Build Coastguard Worker					comments = nil
189*1fa6dee9SAndroid Build Coastguard Worker				}
190*1fa6dee9SAndroid Build Coastguard Worker				comments = append(comments, &Comment{lines, p.scanner.Position})
191*1fa6dee9SAndroid Build Coastguard Worker				p.tok = p.scanner.Scan()
192*1fa6dee9SAndroid Build Coastguard Worker			}
193*1fa6dee9SAndroid Build Coastguard Worker			p.comments = append(p.comments, &CommentGroup{Comments: comments})
194*1fa6dee9SAndroid Build Coastguard Worker		}
195*1fa6dee9SAndroid Build Coastguard Worker	}
196*1fa6dee9SAndroid Build Coastguard Worker}
197*1fa6dee9SAndroid Build Coastguard Worker
198*1fa6dee9SAndroid Build Coastguard Workerfunc (p *parser) parseDefinitions() (defs []Definition) {
199*1fa6dee9SAndroid Build Coastguard Worker	for {
200*1fa6dee9SAndroid Build Coastguard Worker		switch p.tok {
201*1fa6dee9SAndroid Build Coastguard Worker		case scanner.Ident:
202*1fa6dee9SAndroid Build Coastguard Worker			ident := p.scanner.TokenText()
203*1fa6dee9SAndroid Build Coastguard Worker			pos := p.scanner.Position
204*1fa6dee9SAndroid Build Coastguard Worker
205*1fa6dee9SAndroid Build Coastguard Worker			p.accept(scanner.Ident)
206*1fa6dee9SAndroid Build Coastguard Worker
207*1fa6dee9SAndroid Build Coastguard Worker			switch p.tok {
208*1fa6dee9SAndroid Build Coastguard Worker			case '+':
209*1fa6dee9SAndroid Build Coastguard Worker				p.accept('+')
210*1fa6dee9SAndroid Build Coastguard Worker				defs = append(defs, p.parseAssignment(ident, pos, "+="))
211*1fa6dee9SAndroid Build Coastguard Worker			case '=':
212*1fa6dee9SAndroid Build Coastguard Worker				defs = append(defs, p.parseAssignment(ident, pos, "="))
213*1fa6dee9SAndroid Build Coastguard Worker			case '{', '(':
214*1fa6dee9SAndroid Build Coastguard Worker				defs = append(defs, p.parseModule(ident, pos))
215*1fa6dee9SAndroid Build Coastguard Worker			default:
216*1fa6dee9SAndroid Build Coastguard Worker				p.errorf("expected \"=\" or \"+=\" or \"{\" or \"(\", found %s",
217*1fa6dee9SAndroid Build Coastguard Worker					scanner.TokenString(p.tok))
218*1fa6dee9SAndroid Build Coastguard Worker			}
219*1fa6dee9SAndroid Build Coastguard Worker		case scanner.EOF:
220*1fa6dee9SAndroid Build Coastguard Worker			return
221*1fa6dee9SAndroid Build Coastguard Worker		default:
222*1fa6dee9SAndroid Build Coastguard Worker			p.errorf("expected assignment or module definition, found %s",
223*1fa6dee9SAndroid Build Coastguard Worker				scanner.TokenString(p.tok))
224*1fa6dee9SAndroid Build Coastguard Worker			return
225*1fa6dee9SAndroid Build Coastguard Worker		}
226*1fa6dee9SAndroid Build Coastguard Worker	}
227*1fa6dee9SAndroid Build Coastguard Worker}
228*1fa6dee9SAndroid Build Coastguard Worker
229*1fa6dee9SAndroid Build Coastguard Workerfunc (p *parser) parseAssignment(name string, namePos scanner.Position,
230*1fa6dee9SAndroid Build Coastguard Worker	assigner string) (assignment *Assignment) {
231*1fa6dee9SAndroid Build Coastguard Worker
232*1fa6dee9SAndroid Build Coastguard Worker	// These are used as keywords in select statements, prevent making variables
233*1fa6dee9SAndroid Build Coastguard Worker	// with the same name to avoid any confusion.
234*1fa6dee9SAndroid Build Coastguard Worker	switch name {
235*1fa6dee9SAndroid Build Coastguard Worker	case "default", "unset":
236*1fa6dee9SAndroid Build Coastguard Worker		p.errorf("'default' and 'unset' are reserved keywords, and cannot be used as variable names")
237*1fa6dee9SAndroid Build Coastguard Worker		return nil
238*1fa6dee9SAndroid Build Coastguard Worker	}
239*1fa6dee9SAndroid Build Coastguard Worker
240*1fa6dee9SAndroid Build Coastguard Worker	assignment = new(Assignment)
241*1fa6dee9SAndroid Build Coastguard Worker
242*1fa6dee9SAndroid Build Coastguard Worker	pos := p.scanner.Position
243*1fa6dee9SAndroid Build Coastguard Worker	if !p.accept('=') {
244*1fa6dee9SAndroid Build Coastguard Worker		return
245*1fa6dee9SAndroid Build Coastguard Worker	}
246*1fa6dee9SAndroid Build Coastguard Worker	value := p.parseExpression()
247*1fa6dee9SAndroid Build Coastguard Worker
248*1fa6dee9SAndroid Build Coastguard Worker	assignment.Name = name
249*1fa6dee9SAndroid Build Coastguard Worker	assignment.NamePos = namePos
250*1fa6dee9SAndroid Build Coastguard Worker	assignment.Value = value
251*1fa6dee9SAndroid Build Coastguard Worker	assignment.EqualsPos = pos
252*1fa6dee9SAndroid Build Coastguard Worker	assignment.Assigner = assigner
253*1fa6dee9SAndroid Build Coastguard Worker
254*1fa6dee9SAndroid Build Coastguard Worker	return
255*1fa6dee9SAndroid Build Coastguard Worker}
256*1fa6dee9SAndroid Build Coastguard Worker
257*1fa6dee9SAndroid Build Coastguard Workerfunc (p *parser) parseModule(typ string, typPos scanner.Position) *Module {
258*1fa6dee9SAndroid Build Coastguard Worker
259*1fa6dee9SAndroid Build Coastguard Worker	compat := false
260*1fa6dee9SAndroid Build Coastguard Worker	lbracePos := p.scanner.Position
261*1fa6dee9SAndroid Build Coastguard Worker	if p.tok == '{' {
262*1fa6dee9SAndroid Build Coastguard Worker		compat = true
263*1fa6dee9SAndroid Build Coastguard Worker	}
264*1fa6dee9SAndroid Build Coastguard Worker
265*1fa6dee9SAndroid Build Coastguard Worker	if !p.accept(p.tok) {
266*1fa6dee9SAndroid Build Coastguard Worker		return nil
267*1fa6dee9SAndroid Build Coastguard Worker	}
268*1fa6dee9SAndroid Build Coastguard Worker	properties := p.parsePropertyList(true, compat)
269*1fa6dee9SAndroid Build Coastguard Worker	rbracePos := p.scanner.Position
270*1fa6dee9SAndroid Build Coastguard Worker	if !compat {
271*1fa6dee9SAndroid Build Coastguard Worker		p.accept(')')
272*1fa6dee9SAndroid Build Coastguard Worker	} else {
273*1fa6dee9SAndroid Build Coastguard Worker		p.accept('}')
274*1fa6dee9SAndroid Build Coastguard Worker	}
275*1fa6dee9SAndroid Build Coastguard Worker
276*1fa6dee9SAndroid Build Coastguard Worker	return &Module{
277*1fa6dee9SAndroid Build Coastguard Worker		Type:    typ,
278*1fa6dee9SAndroid Build Coastguard Worker		TypePos: typPos,
279*1fa6dee9SAndroid Build Coastguard Worker		Map: Map{
280*1fa6dee9SAndroid Build Coastguard Worker			Properties: properties,
281*1fa6dee9SAndroid Build Coastguard Worker			LBracePos:  lbracePos,
282*1fa6dee9SAndroid Build Coastguard Worker			RBracePos:  rbracePos,
283*1fa6dee9SAndroid Build Coastguard Worker		},
284*1fa6dee9SAndroid Build Coastguard Worker	}
285*1fa6dee9SAndroid Build Coastguard Worker}
286*1fa6dee9SAndroid Build Coastguard Worker
287*1fa6dee9SAndroid Build Coastguard Workerfunc (p *parser) parsePropertyList(isModule, compat bool) (properties []*Property) {
288*1fa6dee9SAndroid Build Coastguard Worker	for p.tok == scanner.Ident {
289*1fa6dee9SAndroid Build Coastguard Worker		properties = append(properties, p.parseProperty(isModule, compat))
290*1fa6dee9SAndroid Build Coastguard Worker
291*1fa6dee9SAndroid Build Coastguard Worker		if p.tok != ',' {
292*1fa6dee9SAndroid Build Coastguard Worker			// There was no comma, so the list is done.
293*1fa6dee9SAndroid Build Coastguard Worker			break
294*1fa6dee9SAndroid Build Coastguard Worker		}
295*1fa6dee9SAndroid Build Coastguard Worker
296*1fa6dee9SAndroid Build Coastguard Worker		p.accept(',')
297*1fa6dee9SAndroid Build Coastguard Worker	}
298*1fa6dee9SAndroid Build Coastguard Worker
299*1fa6dee9SAndroid Build Coastguard Worker	return
300*1fa6dee9SAndroid Build Coastguard Worker}
301*1fa6dee9SAndroid Build Coastguard Worker
302*1fa6dee9SAndroid Build Coastguard Workerfunc (p *parser) parseProperty(isModule, compat bool) (property *Property) {
303*1fa6dee9SAndroid Build Coastguard Worker	property = new(Property)
304*1fa6dee9SAndroid Build Coastguard Worker
305*1fa6dee9SAndroid Build Coastguard Worker	name := p.scanner.TokenText()
306*1fa6dee9SAndroid Build Coastguard Worker	namePos := p.scanner.Position
307*1fa6dee9SAndroid Build Coastguard Worker	p.accept(scanner.Ident)
308*1fa6dee9SAndroid Build Coastguard Worker	pos := p.scanner.Position
309*1fa6dee9SAndroid Build Coastguard Worker
310*1fa6dee9SAndroid Build Coastguard Worker	if isModule {
311*1fa6dee9SAndroid Build Coastguard Worker		if compat {
312*1fa6dee9SAndroid Build Coastguard Worker			if !p.accept(':') {
313*1fa6dee9SAndroid Build Coastguard Worker				return
314*1fa6dee9SAndroid Build Coastguard Worker			}
315*1fa6dee9SAndroid Build Coastguard Worker		} else {
316*1fa6dee9SAndroid Build Coastguard Worker			if !p.accept('=') {
317*1fa6dee9SAndroid Build Coastguard Worker				return
318*1fa6dee9SAndroid Build Coastguard Worker			}
319*1fa6dee9SAndroid Build Coastguard Worker		}
320*1fa6dee9SAndroid Build Coastguard Worker	} else {
321*1fa6dee9SAndroid Build Coastguard Worker		if !p.accept(':') {
322*1fa6dee9SAndroid Build Coastguard Worker			return
323*1fa6dee9SAndroid Build Coastguard Worker		}
324*1fa6dee9SAndroid Build Coastguard Worker	}
325*1fa6dee9SAndroid Build Coastguard Worker
326*1fa6dee9SAndroid Build Coastguard Worker	value := p.parseExpression()
327*1fa6dee9SAndroid Build Coastguard Worker
328*1fa6dee9SAndroid Build Coastguard Worker	property.Name = name
329*1fa6dee9SAndroid Build Coastguard Worker	property.NamePos = namePos
330*1fa6dee9SAndroid Build Coastguard Worker	property.Value = value
331*1fa6dee9SAndroid Build Coastguard Worker	property.ColonPos = pos
332*1fa6dee9SAndroid Build Coastguard Worker
333*1fa6dee9SAndroid Build Coastguard Worker	return
334*1fa6dee9SAndroid Build Coastguard Worker}
335*1fa6dee9SAndroid Build Coastguard Worker
336*1fa6dee9SAndroid Build Coastguard Workerfunc (p *parser) parseExpression() (value Expression) {
337*1fa6dee9SAndroid Build Coastguard Worker	value = p.parseValue()
338*1fa6dee9SAndroid Build Coastguard Worker	switch p.tok {
339*1fa6dee9SAndroid Build Coastguard Worker	case '+':
340*1fa6dee9SAndroid Build Coastguard Worker		return p.parseOperator(value)
341*1fa6dee9SAndroid Build Coastguard Worker	case '-':
342*1fa6dee9SAndroid Build Coastguard Worker		p.errorf("subtraction not supported: %s", p.scanner.String())
343*1fa6dee9SAndroid Build Coastguard Worker		return value
344*1fa6dee9SAndroid Build Coastguard Worker	default:
345*1fa6dee9SAndroid Build Coastguard Worker		return value
346*1fa6dee9SAndroid Build Coastguard Worker	}
347*1fa6dee9SAndroid Build Coastguard Worker}
348*1fa6dee9SAndroid Build Coastguard Worker
349*1fa6dee9SAndroid Build Coastguard Workerfunc (p *parser) parseOperator(value1 Expression) Expression {
350*1fa6dee9SAndroid Build Coastguard Worker	operator := p.tok
351*1fa6dee9SAndroid Build Coastguard Worker	pos := p.scanner.Position
352*1fa6dee9SAndroid Build Coastguard Worker	p.accept(operator)
353*1fa6dee9SAndroid Build Coastguard Worker
354*1fa6dee9SAndroid Build Coastguard Worker	value2 := p.parseExpression()
355*1fa6dee9SAndroid Build Coastguard Worker
356*1fa6dee9SAndroid Build Coastguard Worker	return &Operator{
357*1fa6dee9SAndroid Build Coastguard Worker		Args:        [2]Expression{value1, value2},
358*1fa6dee9SAndroid Build Coastguard Worker		Operator:    operator,
359*1fa6dee9SAndroid Build Coastguard Worker		OperatorPos: pos,
360*1fa6dee9SAndroid Build Coastguard Worker	}
361*1fa6dee9SAndroid Build Coastguard Worker}
362*1fa6dee9SAndroid Build Coastguard Worker
363*1fa6dee9SAndroid Build Coastguard Workerfunc (p *parser) parseValue() (value Expression) {
364*1fa6dee9SAndroid Build Coastguard Worker	switch p.tok {
365*1fa6dee9SAndroid Build Coastguard Worker	case scanner.Ident:
366*1fa6dee9SAndroid Build Coastguard Worker		switch text := p.scanner.TokenText(); text {
367*1fa6dee9SAndroid Build Coastguard Worker		case "true", "false":
368*1fa6dee9SAndroid Build Coastguard Worker			return p.parseBoolean()
369*1fa6dee9SAndroid Build Coastguard Worker		case "select":
370*1fa6dee9SAndroid Build Coastguard Worker			return p.parseSelect()
371*1fa6dee9SAndroid Build Coastguard Worker		default:
372*1fa6dee9SAndroid Build Coastguard Worker			return p.parseVariable()
373*1fa6dee9SAndroid Build Coastguard Worker		}
374*1fa6dee9SAndroid Build Coastguard Worker	case '-', scanner.Int: // Integer might have '-' sign ahead ('+' is only treated as operator now)
375*1fa6dee9SAndroid Build Coastguard Worker		return p.parseIntValue()
376*1fa6dee9SAndroid Build Coastguard Worker	case scanner.String, scanner.RawString:
377*1fa6dee9SAndroid Build Coastguard Worker		return p.parseStringValue()
378*1fa6dee9SAndroid Build Coastguard Worker	case '[':
379*1fa6dee9SAndroid Build Coastguard Worker		return p.parseListValue()
380*1fa6dee9SAndroid Build Coastguard Worker	case '{':
381*1fa6dee9SAndroid Build Coastguard Worker		return p.parseMapValue()
382*1fa6dee9SAndroid Build Coastguard Worker	default:
383*1fa6dee9SAndroid Build Coastguard Worker		p.errorf("expected bool, list, or string value; found %s",
384*1fa6dee9SAndroid Build Coastguard Worker			scanner.TokenString(p.tok))
385*1fa6dee9SAndroid Build Coastguard Worker		return
386*1fa6dee9SAndroid Build Coastguard Worker	}
387*1fa6dee9SAndroid Build Coastguard Worker}
388*1fa6dee9SAndroid Build Coastguard Worker
389*1fa6dee9SAndroid Build Coastguard Workerfunc (p *parser) parseBoolean() Expression {
390*1fa6dee9SAndroid Build Coastguard Worker	switch text := p.scanner.TokenText(); text {
391*1fa6dee9SAndroid Build Coastguard Worker	case "true", "false":
392*1fa6dee9SAndroid Build Coastguard Worker		result := &Bool{
393*1fa6dee9SAndroid Build Coastguard Worker			LiteralPos: p.scanner.Position,
394*1fa6dee9SAndroid Build Coastguard Worker			Value:      text == "true",
395*1fa6dee9SAndroid Build Coastguard Worker			Token:      text,
396*1fa6dee9SAndroid Build Coastguard Worker		}
397*1fa6dee9SAndroid Build Coastguard Worker		p.accept(scanner.Ident)
398*1fa6dee9SAndroid Build Coastguard Worker		return result
399*1fa6dee9SAndroid Build Coastguard Worker	default:
400*1fa6dee9SAndroid Build Coastguard Worker		p.errorf("Expected true/false, got %q", text)
401*1fa6dee9SAndroid Build Coastguard Worker		return nil
402*1fa6dee9SAndroid Build Coastguard Worker	}
403*1fa6dee9SAndroid Build Coastguard Worker}
404*1fa6dee9SAndroid Build Coastguard Worker
405*1fa6dee9SAndroid Build Coastguard Workerfunc (p *parser) parseVariable() Expression {
406*1fa6dee9SAndroid Build Coastguard Worker	var value Expression
407*1fa6dee9SAndroid Build Coastguard Worker
408*1fa6dee9SAndroid Build Coastguard Worker	text := p.scanner.TokenText()
409*1fa6dee9SAndroid Build Coastguard Worker	value = &Variable{
410*1fa6dee9SAndroid Build Coastguard Worker		Name:    text,
411*1fa6dee9SAndroid Build Coastguard Worker		NamePos: p.scanner.Position,
412*1fa6dee9SAndroid Build Coastguard Worker	}
413*1fa6dee9SAndroid Build Coastguard Worker
414*1fa6dee9SAndroid Build Coastguard Worker	p.accept(scanner.Ident)
415*1fa6dee9SAndroid Build Coastguard Worker	return value
416*1fa6dee9SAndroid Build Coastguard Worker}
417*1fa6dee9SAndroid Build Coastguard Worker
418*1fa6dee9SAndroid Build Coastguard Workerfunc (p *parser) parseSelect() Expression {
419*1fa6dee9SAndroid Build Coastguard Worker	result := &Select{
420*1fa6dee9SAndroid Build Coastguard Worker		KeywordPos: p.scanner.Position,
421*1fa6dee9SAndroid Build Coastguard Worker	}
422*1fa6dee9SAndroid Build Coastguard Worker	// Read the "select("
423*1fa6dee9SAndroid Build Coastguard Worker	p.accept(scanner.Ident)
424*1fa6dee9SAndroid Build Coastguard Worker	if !p.accept('(') {
425*1fa6dee9SAndroid Build Coastguard Worker		return nil
426*1fa6dee9SAndroid Build Coastguard Worker	}
427*1fa6dee9SAndroid Build Coastguard Worker
428*1fa6dee9SAndroid Build Coastguard Worker	// If we see another '(', there's probably multiple conditions and there must
429*1fa6dee9SAndroid Build Coastguard Worker	// be a ')' after. Set the multipleConditions variable to remind us to check for
430*1fa6dee9SAndroid Build Coastguard Worker	// the ')' after.
431*1fa6dee9SAndroid Build Coastguard Worker	multipleConditions := false
432*1fa6dee9SAndroid Build Coastguard Worker	if p.tok == '(' {
433*1fa6dee9SAndroid Build Coastguard Worker		multipleConditions = true
434*1fa6dee9SAndroid Build Coastguard Worker		p.accept('(')
435*1fa6dee9SAndroid Build Coastguard Worker	}
436*1fa6dee9SAndroid Build Coastguard Worker
437*1fa6dee9SAndroid Build Coastguard Worker	// Read all individual conditions
438*1fa6dee9SAndroid Build Coastguard Worker	conditions := []ConfigurableCondition{}
439*1fa6dee9SAndroid Build Coastguard Worker	for first := true; first || multipleConditions; first = false {
440*1fa6dee9SAndroid Build Coastguard Worker		condition := ConfigurableCondition{
441*1fa6dee9SAndroid Build Coastguard Worker			position:     p.scanner.Position,
442*1fa6dee9SAndroid Build Coastguard Worker			FunctionName: p.scanner.TokenText(),
443*1fa6dee9SAndroid Build Coastguard Worker		}
444*1fa6dee9SAndroid Build Coastguard Worker		if !p.accept(scanner.Ident) {
445*1fa6dee9SAndroid Build Coastguard Worker			return nil
446*1fa6dee9SAndroid Build Coastguard Worker		}
447*1fa6dee9SAndroid Build Coastguard Worker		if !p.accept('(') {
448*1fa6dee9SAndroid Build Coastguard Worker			return nil
449*1fa6dee9SAndroid Build Coastguard Worker		}
450*1fa6dee9SAndroid Build Coastguard Worker
451*1fa6dee9SAndroid Build Coastguard Worker		for p.tok != ')' {
452*1fa6dee9SAndroid Build Coastguard Worker			if s := p.parseStringValue(); s != nil {
453*1fa6dee9SAndroid Build Coastguard Worker				condition.Args = append(condition.Args, *s)
454*1fa6dee9SAndroid Build Coastguard Worker			} else {
455*1fa6dee9SAndroid Build Coastguard Worker				return nil
456*1fa6dee9SAndroid Build Coastguard Worker			}
457*1fa6dee9SAndroid Build Coastguard Worker			if p.tok == ')' {
458*1fa6dee9SAndroid Build Coastguard Worker				break
459*1fa6dee9SAndroid Build Coastguard Worker			}
460*1fa6dee9SAndroid Build Coastguard Worker			if !p.accept(',') {
461*1fa6dee9SAndroid Build Coastguard Worker				return nil
462*1fa6dee9SAndroid Build Coastguard Worker			}
463*1fa6dee9SAndroid Build Coastguard Worker		}
464*1fa6dee9SAndroid Build Coastguard Worker		p.accept(')')
465*1fa6dee9SAndroid Build Coastguard Worker
466*1fa6dee9SAndroid Build Coastguard Worker		for _, c := range conditions {
467*1fa6dee9SAndroid Build Coastguard Worker			if c.Equals(condition) {
468*1fa6dee9SAndroid Build Coastguard Worker				p.errorf("Duplicate select condition found: %s", c.String())
469*1fa6dee9SAndroid Build Coastguard Worker			}
470*1fa6dee9SAndroid Build Coastguard Worker		}
471*1fa6dee9SAndroid Build Coastguard Worker
472*1fa6dee9SAndroid Build Coastguard Worker		conditions = append(conditions, condition)
473*1fa6dee9SAndroid Build Coastguard Worker
474*1fa6dee9SAndroid Build Coastguard Worker		if multipleConditions {
475*1fa6dee9SAndroid Build Coastguard Worker			if p.tok == ')' {
476*1fa6dee9SAndroid Build Coastguard Worker				p.next()
477*1fa6dee9SAndroid Build Coastguard Worker				break
478*1fa6dee9SAndroid Build Coastguard Worker			}
479*1fa6dee9SAndroid Build Coastguard Worker			if !p.accept(',') {
480*1fa6dee9SAndroid Build Coastguard Worker				return nil
481*1fa6dee9SAndroid Build Coastguard Worker			}
482*1fa6dee9SAndroid Build Coastguard Worker			// Retry the closing parent to allow for a trailing comma
483*1fa6dee9SAndroid Build Coastguard Worker			if p.tok == ')' {
484*1fa6dee9SAndroid Build Coastguard Worker				p.next()
485*1fa6dee9SAndroid Build Coastguard Worker				break
486*1fa6dee9SAndroid Build Coastguard Worker			}
487*1fa6dee9SAndroid Build Coastguard Worker		}
488*1fa6dee9SAndroid Build Coastguard Worker	}
489*1fa6dee9SAndroid Build Coastguard Worker
490*1fa6dee9SAndroid Build Coastguard Worker	if multipleConditions && len(conditions) < 2 {
491*1fa6dee9SAndroid Build Coastguard Worker		p.errorf("Expected multiple select conditions due to the extra parenthesis, but only found 1. Please remove the extra parenthesis.")
492*1fa6dee9SAndroid Build Coastguard Worker		return nil
493*1fa6dee9SAndroid Build Coastguard Worker	}
494*1fa6dee9SAndroid Build Coastguard Worker
495*1fa6dee9SAndroid Build Coastguard Worker	result.Conditions = conditions
496*1fa6dee9SAndroid Build Coastguard Worker
497*1fa6dee9SAndroid Build Coastguard Worker	if !p.accept(',') {
498*1fa6dee9SAndroid Build Coastguard Worker		return nil
499*1fa6dee9SAndroid Build Coastguard Worker	}
500*1fa6dee9SAndroid Build Coastguard Worker
501*1fa6dee9SAndroid Build Coastguard Worker	result.LBracePos = p.scanner.Position
502*1fa6dee9SAndroid Build Coastguard Worker	if !p.accept('{') {
503*1fa6dee9SAndroid Build Coastguard Worker		return nil
504*1fa6dee9SAndroid Build Coastguard Worker	}
505*1fa6dee9SAndroid Build Coastguard Worker
506*1fa6dee9SAndroid Build Coastguard Worker	maybeParseBinding := func() (Variable, bool) {
507*1fa6dee9SAndroid Build Coastguard Worker		if p.scanner.TokenText() != "@" {
508*1fa6dee9SAndroid Build Coastguard Worker			return Variable{}, false
509*1fa6dee9SAndroid Build Coastguard Worker		}
510*1fa6dee9SAndroid Build Coastguard Worker		p.next()
511*1fa6dee9SAndroid Build Coastguard Worker		value := Variable{
512*1fa6dee9SAndroid Build Coastguard Worker			Name:    p.scanner.TokenText(),
513*1fa6dee9SAndroid Build Coastguard Worker			NamePos: p.scanner.Position,
514*1fa6dee9SAndroid Build Coastguard Worker		}
515*1fa6dee9SAndroid Build Coastguard Worker		p.accept(scanner.Ident)
516*1fa6dee9SAndroid Build Coastguard Worker		return value, true
517*1fa6dee9SAndroid Build Coastguard Worker	}
518*1fa6dee9SAndroid Build Coastguard Worker
519*1fa6dee9SAndroid Build Coastguard Worker	parseOnePattern := func() SelectPattern {
520*1fa6dee9SAndroid Build Coastguard Worker		var result SelectPattern
521*1fa6dee9SAndroid Build Coastguard Worker		switch p.tok {
522*1fa6dee9SAndroid Build Coastguard Worker		case scanner.Ident:
523*1fa6dee9SAndroid Build Coastguard Worker			switch p.scanner.TokenText() {
524*1fa6dee9SAndroid Build Coastguard Worker			case "any":
525*1fa6dee9SAndroid Build Coastguard Worker				result.Value = &String{
526*1fa6dee9SAndroid Build Coastguard Worker					LiteralPos: p.scanner.Position,
527*1fa6dee9SAndroid Build Coastguard Worker					Value:      any_select_branch_name,
528*1fa6dee9SAndroid Build Coastguard Worker				}
529*1fa6dee9SAndroid Build Coastguard Worker				p.next()
530*1fa6dee9SAndroid Build Coastguard Worker				if binding, exists := maybeParseBinding(); exists {
531*1fa6dee9SAndroid Build Coastguard Worker					result.Binding = binding
532*1fa6dee9SAndroid Build Coastguard Worker				}
533*1fa6dee9SAndroid Build Coastguard Worker				return result
534*1fa6dee9SAndroid Build Coastguard Worker			case "default":
535*1fa6dee9SAndroid Build Coastguard Worker				result.Value = &String{
536*1fa6dee9SAndroid Build Coastguard Worker					LiteralPos: p.scanner.Position,
537*1fa6dee9SAndroid Build Coastguard Worker					Value:      default_select_branch_name,
538*1fa6dee9SAndroid Build Coastguard Worker				}
539*1fa6dee9SAndroid Build Coastguard Worker				p.next()
540*1fa6dee9SAndroid Build Coastguard Worker				return result
541*1fa6dee9SAndroid Build Coastguard Worker			case "true":
542*1fa6dee9SAndroid Build Coastguard Worker				result.Value = &Bool{
543*1fa6dee9SAndroid Build Coastguard Worker					LiteralPos: p.scanner.Position,
544*1fa6dee9SAndroid Build Coastguard Worker					Value:      true,
545*1fa6dee9SAndroid Build Coastguard Worker				}
546*1fa6dee9SAndroid Build Coastguard Worker				p.next()
547*1fa6dee9SAndroid Build Coastguard Worker				return result
548*1fa6dee9SAndroid Build Coastguard Worker			case "false":
549*1fa6dee9SAndroid Build Coastguard Worker				result.Value = &Bool{
550*1fa6dee9SAndroid Build Coastguard Worker					LiteralPos: p.scanner.Position,
551*1fa6dee9SAndroid Build Coastguard Worker					Value:      false,
552*1fa6dee9SAndroid Build Coastguard Worker				}
553*1fa6dee9SAndroid Build Coastguard Worker				p.next()
554*1fa6dee9SAndroid Build Coastguard Worker				return result
555*1fa6dee9SAndroid Build Coastguard Worker			default:
556*1fa6dee9SAndroid Build Coastguard Worker				p.errorf("Expected a string, true, false, or default, got %s", p.scanner.TokenText())
557*1fa6dee9SAndroid Build Coastguard Worker			}
558*1fa6dee9SAndroid Build Coastguard Worker		case scanner.String:
559*1fa6dee9SAndroid Build Coastguard Worker			if s := p.parseStringValue(); s != nil {
560*1fa6dee9SAndroid Build Coastguard Worker				if strings.HasPrefix(s.Value, "__soong") {
561*1fa6dee9SAndroid Build Coastguard Worker					p.errorf("select branch patterns starting with __soong are reserved for internal use")
562*1fa6dee9SAndroid Build Coastguard Worker					return result
563*1fa6dee9SAndroid Build Coastguard Worker				}
564*1fa6dee9SAndroid Build Coastguard Worker				result.Value = s
565*1fa6dee9SAndroid Build Coastguard Worker				return result
566*1fa6dee9SAndroid Build Coastguard Worker			}
567*1fa6dee9SAndroid Build Coastguard Worker			fallthrough
568*1fa6dee9SAndroid Build Coastguard Worker		default:
569*1fa6dee9SAndroid Build Coastguard Worker			p.errorf("Expected a string, true, false, or default, got %s", p.scanner.TokenText())
570*1fa6dee9SAndroid Build Coastguard Worker		}
571*1fa6dee9SAndroid Build Coastguard Worker		return result
572*1fa6dee9SAndroid Build Coastguard Worker	}
573*1fa6dee9SAndroid Build Coastguard Worker
574*1fa6dee9SAndroid Build Coastguard Worker	hasNonUnsetValue := false
575*1fa6dee9SAndroid Build Coastguard Worker	for p.tok != '}' {
576*1fa6dee9SAndroid Build Coastguard Worker		c := &SelectCase{}
577*1fa6dee9SAndroid Build Coastguard Worker
578*1fa6dee9SAndroid Build Coastguard Worker		if multipleConditions {
579*1fa6dee9SAndroid Build Coastguard Worker			if !p.accept('(') {
580*1fa6dee9SAndroid Build Coastguard Worker				return nil
581*1fa6dee9SAndroid Build Coastguard Worker			}
582*1fa6dee9SAndroid Build Coastguard Worker			for i := 0; i < len(conditions); i++ {
583*1fa6dee9SAndroid Build Coastguard Worker				c.Patterns = append(c.Patterns, parseOnePattern())
584*1fa6dee9SAndroid Build Coastguard Worker				if i < len(conditions)-1 {
585*1fa6dee9SAndroid Build Coastguard Worker					if !p.accept(',') {
586*1fa6dee9SAndroid Build Coastguard Worker						return nil
587*1fa6dee9SAndroid Build Coastguard Worker					}
588*1fa6dee9SAndroid Build Coastguard Worker				} else if p.tok == ',' {
589*1fa6dee9SAndroid Build Coastguard Worker					// allow optional trailing comma
590*1fa6dee9SAndroid Build Coastguard Worker					p.next()
591*1fa6dee9SAndroid Build Coastguard Worker				}
592*1fa6dee9SAndroid Build Coastguard Worker			}
593*1fa6dee9SAndroid Build Coastguard Worker			if !p.accept(')') {
594*1fa6dee9SAndroid Build Coastguard Worker				return nil
595*1fa6dee9SAndroid Build Coastguard Worker			}
596*1fa6dee9SAndroid Build Coastguard Worker		} else {
597*1fa6dee9SAndroid Build Coastguard Worker			c.Patterns = append(c.Patterns, parseOnePattern())
598*1fa6dee9SAndroid Build Coastguard Worker		}
599*1fa6dee9SAndroid Build Coastguard Worker		c.ColonPos = p.scanner.Position
600*1fa6dee9SAndroid Build Coastguard Worker		if !p.accept(':') {
601*1fa6dee9SAndroid Build Coastguard Worker			return nil
602*1fa6dee9SAndroid Build Coastguard Worker		}
603*1fa6dee9SAndroid Build Coastguard Worker		if p.tok == scanner.Ident && p.scanner.TokenText() == "unset" {
604*1fa6dee9SAndroid Build Coastguard Worker			c.Value = &UnsetProperty{Position: p.scanner.Position}
605*1fa6dee9SAndroid Build Coastguard Worker			p.accept(scanner.Ident)
606*1fa6dee9SAndroid Build Coastguard Worker		} else {
607*1fa6dee9SAndroid Build Coastguard Worker			hasNonUnsetValue = true
608*1fa6dee9SAndroid Build Coastguard Worker			c.Value = p.parseExpression()
609*1fa6dee9SAndroid Build Coastguard Worker		}
610*1fa6dee9SAndroid Build Coastguard Worker		// allow trailing comma, require it if not seeing a }
611*1fa6dee9SAndroid Build Coastguard Worker		if p.tok != '}' {
612*1fa6dee9SAndroid Build Coastguard Worker			if !p.accept(',') {
613*1fa6dee9SAndroid Build Coastguard Worker				return nil
614*1fa6dee9SAndroid Build Coastguard Worker			}
615*1fa6dee9SAndroid Build Coastguard Worker		}
616*1fa6dee9SAndroid Build Coastguard Worker		result.Cases = append(result.Cases, c)
617*1fa6dee9SAndroid Build Coastguard Worker	}
618*1fa6dee9SAndroid Build Coastguard Worker
619*1fa6dee9SAndroid Build Coastguard Worker	// If all branches have the value "unset", then this is equivalent
620*1fa6dee9SAndroid Build Coastguard Worker	// to an empty select.
621*1fa6dee9SAndroid Build Coastguard Worker	if !hasNonUnsetValue {
622*1fa6dee9SAndroid Build Coastguard Worker		p.errorf("This select statement is empty, remove it")
623*1fa6dee9SAndroid Build Coastguard Worker		return nil
624*1fa6dee9SAndroid Build Coastguard Worker	}
625*1fa6dee9SAndroid Build Coastguard Worker
626*1fa6dee9SAndroid Build Coastguard Worker	patternsEqual := func(a, b SelectPattern) bool {
627*1fa6dee9SAndroid Build Coastguard Worker		// We can ignore the bindings, they don't affect which pattern is matched
628*1fa6dee9SAndroid Build Coastguard Worker		switch a2 := a.Value.(type) {
629*1fa6dee9SAndroid Build Coastguard Worker		case *String:
630*1fa6dee9SAndroid Build Coastguard Worker			if b2, ok := b.Value.(*String); ok {
631*1fa6dee9SAndroid Build Coastguard Worker				return a2.Value == b2.Value
632*1fa6dee9SAndroid Build Coastguard Worker			} else {
633*1fa6dee9SAndroid Build Coastguard Worker				return false
634*1fa6dee9SAndroid Build Coastguard Worker			}
635*1fa6dee9SAndroid Build Coastguard Worker		case *Bool:
636*1fa6dee9SAndroid Build Coastguard Worker			if b2, ok := b.Value.(*Bool); ok {
637*1fa6dee9SAndroid Build Coastguard Worker				return a2.Value == b2.Value
638*1fa6dee9SAndroid Build Coastguard Worker			} else {
639*1fa6dee9SAndroid Build Coastguard Worker				return false
640*1fa6dee9SAndroid Build Coastguard Worker			}
641*1fa6dee9SAndroid Build Coastguard Worker		default:
642*1fa6dee9SAndroid Build Coastguard Worker			// true so that we produce an error in this unexpected scenario
643*1fa6dee9SAndroid Build Coastguard Worker			return true
644*1fa6dee9SAndroid Build Coastguard Worker		}
645*1fa6dee9SAndroid Build Coastguard Worker	}
646*1fa6dee9SAndroid Build Coastguard Worker
647*1fa6dee9SAndroid Build Coastguard Worker	patternListsEqual := func(a, b []SelectPattern) bool {
648*1fa6dee9SAndroid Build Coastguard Worker		if len(a) != len(b) {
649*1fa6dee9SAndroid Build Coastguard Worker			return false
650*1fa6dee9SAndroid Build Coastguard Worker		}
651*1fa6dee9SAndroid Build Coastguard Worker		for i := range a {
652*1fa6dee9SAndroid Build Coastguard Worker			if !patternsEqual(a[i], b[i]) {
653*1fa6dee9SAndroid Build Coastguard Worker				return false
654*1fa6dee9SAndroid Build Coastguard Worker			}
655*1fa6dee9SAndroid Build Coastguard Worker		}
656*1fa6dee9SAndroid Build Coastguard Worker		return true
657*1fa6dee9SAndroid Build Coastguard Worker	}
658*1fa6dee9SAndroid Build Coastguard Worker
659*1fa6dee9SAndroid Build Coastguard Worker	for i, c := range result.Cases {
660*1fa6dee9SAndroid Build Coastguard Worker		// Check for duplicate patterns across different branches
661*1fa6dee9SAndroid Build Coastguard Worker		for _, d := range result.Cases[i+1:] {
662*1fa6dee9SAndroid Build Coastguard Worker			if patternListsEqual(c.Patterns, d.Patterns) {
663*1fa6dee9SAndroid Build Coastguard Worker				p.errorf("Found duplicate select patterns: %v", c.Patterns)
664*1fa6dee9SAndroid Build Coastguard Worker				return nil
665*1fa6dee9SAndroid Build Coastguard Worker			}
666*1fa6dee9SAndroid Build Coastguard Worker		}
667*1fa6dee9SAndroid Build Coastguard Worker		// check for duplicate bindings within this branch
668*1fa6dee9SAndroid Build Coastguard Worker		for i := range c.Patterns {
669*1fa6dee9SAndroid Build Coastguard Worker			if c.Patterns[i].Binding.Name != "" {
670*1fa6dee9SAndroid Build Coastguard Worker				for j := i + 1; j < len(c.Patterns); j++ {
671*1fa6dee9SAndroid Build Coastguard Worker					if c.Patterns[i].Binding.Name == c.Patterns[j].Binding.Name {
672*1fa6dee9SAndroid Build Coastguard Worker						p.errorf("Found duplicate select pattern binding: %s", c.Patterns[i].Binding.Name)
673*1fa6dee9SAndroid Build Coastguard Worker						return nil
674*1fa6dee9SAndroid Build Coastguard Worker					}
675*1fa6dee9SAndroid Build Coastguard Worker				}
676*1fa6dee9SAndroid Build Coastguard Worker			}
677*1fa6dee9SAndroid Build Coastguard Worker		}
678*1fa6dee9SAndroid Build Coastguard Worker		// Check that the only all-default cases is the last one
679*1fa6dee9SAndroid Build Coastguard Worker		if i < len(result.Cases)-1 {
680*1fa6dee9SAndroid Build Coastguard Worker			isAllDefault := true
681*1fa6dee9SAndroid Build Coastguard Worker			for _, x := range c.Patterns {
682*1fa6dee9SAndroid Build Coastguard Worker				if x2, ok := x.Value.(*String); !ok || x2.Value != default_select_branch_name {
683*1fa6dee9SAndroid Build Coastguard Worker					isAllDefault = false
684*1fa6dee9SAndroid Build Coastguard Worker					break
685*1fa6dee9SAndroid Build Coastguard Worker				}
686*1fa6dee9SAndroid Build Coastguard Worker			}
687*1fa6dee9SAndroid Build Coastguard Worker			if isAllDefault {
688*1fa6dee9SAndroid Build Coastguard Worker				p.errorf("Found a default select branch at index %d, expected it to be last (index %d)", i, len(result.Cases)-1)
689*1fa6dee9SAndroid Build Coastguard Worker				return nil
690*1fa6dee9SAndroid Build Coastguard Worker			}
691*1fa6dee9SAndroid Build Coastguard Worker		}
692*1fa6dee9SAndroid Build Coastguard Worker	}
693*1fa6dee9SAndroid Build Coastguard Worker
694*1fa6dee9SAndroid Build Coastguard Worker	result.RBracePos = p.scanner.Position
695*1fa6dee9SAndroid Build Coastguard Worker	if !p.accept('}') {
696*1fa6dee9SAndroid Build Coastguard Worker		return nil
697*1fa6dee9SAndroid Build Coastguard Worker	}
698*1fa6dee9SAndroid Build Coastguard Worker	if !p.accept(')') {
699*1fa6dee9SAndroid Build Coastguard Worker		return nil
700*1fa6dee9SAndroid Build Coastguard Worker	}
701*1fa6dee9SAndroid Build Coastguard Worker	return result
702*1fa6dee9SAndroid Build Coastguard Worker}
703*1fa6dee9SAndroid Build Coastguard Worker
704*1fa6dee9SAndroid Build Coastguard Workerfunc (p *parser) parseStringValue() *String {
705*1fa6dee9SAndroid Build Coastguard Worker	str, err := strconv.Unquote(p.scanner.TokenText())
706*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
707*1fa6dee9SAndroid Build Coastguard Worker		p.errorf("couldn't parse string: %s", err)
708*1fa6dee9SAndroid Build Coastguard Worker		return nil
709*1fa6dee9SAndroid Build Coastguard Worker	}
710*1fa6dee9SAndroid Build Coastguard Worker
711*1fa6dee9SAndroid Build Coastguard Worker	value := &String{
712*1fa6dee9SAndroid Build Coastguard Worker		LiteralPos: p.scanner.Position,
713*1fa6dee9SAndroid Build Coastguard Worker		Value:      str,
714*1fa6dee9SAndroid Build Coastguard Worker	}
715*1fa6dee9SAndroid Build Coastguard Worker	p.accept(p.tok)
716*1fa6dee9SAndroid Build Coastguard Worker	return value
717*1fa6dee9SAndroid Build Coastguard Worker}
718*1fa6dee9SAndroid Build Coastguard Worker
719*1fa6dee9SAndroid Build Coastguard Workerfunc (p *parser) parseIntValue() *Int64 {
720*1fa6dee9SAndroid Build Coastguard Worker	var str string
721*1fa6dee9SAndroid Build Coastguard Worker	literalPos := p.scanner.Position
722*1fa6dee9SAndroid Build Coastguard Worker	if p.tok == '-' {
723*1fa6dee9SAndroid Build Coastguard Worker		str += string(p.tok)
724*1fa6dee9SAndroid Build Coastguard Worker		p.accept(p.tok)
725*1fa6dee9SAndroid Build Coastguard Worker		if p.tok != scanner.Int {
726*1fa6dee9SAndroid Build Coastguard Worker			p.errorf("expected int; found %s", scanner.TokenString(p.tok))
727*1fa6dee9SAndroid Build Coastguard Worker			return nil
728*1fa6dee9SAndroid Build Coastguard Worker		}
729*1fa6dee9SAndroid Build Coastguard Worker	}
730*1fa6dee9SAndroid Build Coastguard Worker	str += p.scanner.TokenText()
731*1fa6dee9SAndroid Build Coastguard Worker	i, err := strconv.ParseInt(str, 10, 64)
732*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
733*1fa6dee9SAndroid Build Coastguard Worker		p.errorf("couldn't parse int: %s", err)
734*1fa6dee9SAndroid Build Coastguard Worker		return nil
735*1fa6dee9SAndroid Build Coastguard Worker	}
736*1fa6dee9SAndroid Build Coastguard Worker
737*1fa6dee9SAndroid Build Coastguard Worker	value := &Int64{
738*1fa6dee9SAndroid Build Coastguard Worker		LiteralPos: literalPos,
739*1fa6dee9SAndroid Build Coastguard Worker		Value:      i,
740*1fa6dee9SAndroid Build Coastguard Worker		Token:      str,
741*1fa6dee9SAndroid Build Coastguard Worker	}
742*1fa6dee9SAndroid Build Coastguard Worker	p.accept(scanner.Int)
743*1fa6dee9SAndroid Build Coastguard Worker	return value
744*1fa6dee9SAndroid Build Coastguard Worker}
745*1fa6dee9SAndroid Build Coastguard Worker
746*1fa6dee9SAndroid Build Coastguard Workerfunc (p *parser) parseListValue() *List {
747*1fa6dee9SAndroid Build Coastguard Worker	lBracePos := p.scanner.Position
748*1fa6dee9SAndroid Build Coastguard Worker	if !p.accept('[') {
749*1fa6dee9SAndroid Build Coastguard Worker		return nil
750*1fa6dee9SAndroid Build Coastguard Worker	}
751*1fa6dee9SAndroid Build Coastguard Worker
752*1fa6dee9SAndroid Build Coastguard Worker	var elements []Expression
753*1fa6dee9SAndroid Build Coastguard Worker	for p.tok != ']' {
754*1fa6dee9SAndroid Build Coastguard Worker		element := p.parseExpression()
755*1fa6dee9SAndroid Build Coastguard Worker		elements = append(elements, element)
756*1fa6dee9SAndroid Build Coastguard Worker
757*1fa6dee9SAndroid Build Coastguard Worker		if p.tok != ',' {
758*1fa6dee9SAndroid Build Coastguard Worker			// There was no comma, so the list is done.
759*1fa6dee9SAndroid Build Coastguard Worker			break
760*1fa6dee9SAndroid Build Coastguard Worker		}
761*1fa6dee9SAndroid Build Coastguard Worker
762*1fa6dee9SAndroid Build Coastguard Worker		p.accept(',')
763*1fa6dee9SAndroid Build Coastguard Worker	}
764*1fa6dee9SAndroid Build Coastguard Worker
765*1fa6dee9SAndroid Build Coastguard Worker	rBracePos := p.scanner.Position
766*1fa6dee9SAndroid Build Coastguard Worker	p.accept(']')
767*1fa6dee9SAndroid Build Coastguard Worker
768*1fa6dee9SAndroid Build Coastguard Worker	return &List{
769*1fa6dee9SAndroid Build Coastguard Worker		LBracePos: lBracePos,
770*1fa6dee9SAndroid Build Coastguard Worker		RBracePos: rBracePos,
771*1fa6dee9SAndroid Build Coastguard Worker		Values:    elements,
772*1fa6dee9SAndroid Build Coastguard Worker	}
773*1fa6dee9SAndroid Build Coastguard Worker}
774*1fa6dee9SAndroid Build Coastguard Worker
775*1fa6dee9SAndroid Build Coastguard Workerfunc (p *parser) parseMapValue() *Map {
776*1fa6dee9SAndroid Build Coastguard Worker	lBracePos := p.scanner.Position
777*1fa6dee9SAndroid Build Coastguard Worker	if !p.accept('{') {
778*1fa6dee9SAndroid Build Coastguard Worker		return nil
779*1fa6dee9SAndroid Build Coastguard Worker	}
780*1fa6dee9SAndroid Build Coastguard Worker
781*1fa6dee9SAndroid Build Coastguard Worker	properties := p.parsePropertyList(false, false)
782*1fa6dee9SAndroid Build Coastguard Worker
783*1fa6dee9SAndroid Build Coastguard Worker	rBracePos := p.scanner.Position
784*1fa6dee9SAndroid Build Coastguard Worker	p.accept('}')
785*1fa6dee9SAndroid Build Coastguard Worker
786*1fa6dee9SAndroid Build Coastguard Worker	return &Map{
787*1fa6dee9SAndroid Build Coastguard Worker		LBracePos:  lBracePos,
788*1fa6dee9SAndroid Build Coastguard Worker		RBracePos:  rBracePos,
789*1fa6dee9SAndroid Build Coastguard Worker		Properties: properties,
790*1fa6dee9SAndroid Build Coastguard Worker	}
791*1fa6dee9SAndroid Build Coastguard Worker}
792*1fa6dee9SAndroid Build Coastguard Worker
793*1fa6dee9SAndroid Build Coastguard Workertype Scope struct {
794*1fa6dee9SAndroid Build Coastguard Worker	vars              map[string]*Assignment
795*1fa6dee9SAndroid Build Coastguard Worker	preventInheriting map[string]bool
796*1fa6dee9SAndroid Build Coastguard Worker	parentScope       *Scope
797*1fa6dee9SAndroid Build Coastguard Worker}
798*1fa6dee9SAndroid Build Coastguard Worker
799*1fa6dee9SAndroid Build Coastguard Workerfunc NewScope(s *Scope) *Scope {
800*1fa6dee9SAndroid Build Coastguard Worker	return &Scope{
801*1fa6dee9SAndroid Build Coastguard Worker		vars:              make(map[string]*Assignment),
802*1fa6dee9SAndroid Build Coastguard Worker		preventInheriting: make(map[string]bool),
803*1fa6dee9SAndroid Build Coastguard Worker		parentScope:       s,
804*1fa6dee9SAndroid Build Coastguard Worker	}
805*1fa6dee9SAndroid Build Coastguard Worker}
806*1fa6dee9SAndroid Build Coastguard Worker
807*1fa6dee9SAndroid Build Coastguard Workerfunc (s *Scope) HandleAssignment(assignment *Assignment) error {
808*1fa6dee9SAndroid Build Coastguard Worker	switch assignment.Assigner {
809*1fa6dee9SAndroid Build Coastguard Worker	case "+=":
810*1fa6dee9SAndroid Build Coastguard Worker		if !s.preventInheriting[assignment.Name] && s.parentScope.Get(assignment.Name) != nil {
811*1fa6dee9SAndroid Build Coastguard Worker			return fmt.Errorf("modified non-local variable %q with +=", assignment.Name)
812*1fa6dee9SAndroid Build Coastguard Worker		}
813*1fa6dee9SAndroid Build Coastguard Worker		if old, ok := s.vars[assignment.Name]; !ok {
814*1fa6dee9SAndroid Build Coastguard Worker			return fmt.Errorf("modified non-existent variable %q with +=", assignment.Name)
815*1fa6dee9SAndroid Build Coastguard Worker		} else if old.Referenced {
816*1fa6dee9SAndroid Build Coastguard Worker			return fmt.Errorf("modified variable %q with += after referencing", assignment.Name)
817*1fa6dee9SAndroid Build Coastguard Worker		} else {
818*1fa6dee9SAndroid Build Coastguard Worker			newValue, err := evaluateOperator(s, '+', old.Value, assignment.Value)
819*1fa6dee9SAndroid Build Coastguard Worker			if err != nil {
820*1fa6dee9SAndroid Build Coastguard Worker				return err
821*1fa6dee9SAndroid Build Coastguard Worker			}
822*1fa6dee9SAndroid Build Coastguard Worker			old.Value = newValue
823*1fa6dee9SAndroid Build Coastguard Worker		}
824*1fa6dee9SAndroid Build Coastguard Worker	case "=":
825*1fa6dee9SAndroid Build Coastguard Worker		if old, ok := s.vars[assignment.Name]; ok {
826*1fa6dee9SAndroid Build Coastguard Worker			return fmt.Errorf("variable already set, previous assignment: %s", old)
827*1fa6dee9SAndroid Build Coastguard Worker		}
828*1fa6dee9SAndroid Build Coastguard Worker
829*1fa6dee9SAndroid Build Coastguard Worker		if old := s.parentScope.Get(assignment.Name); old != nil && !s.preventInheriting[assignment.Name] {
830*1fa6dee9SAndroid Build Coastguard Worker			return fmt.Errorf("variable already set in inherited scope, previous assignment: %s", old)
831*1fa6dee9SAndroid Build Coastguard Worker		}
832*1fa6dee9SAndroid Build Coastguard Worker
833*1fa6dee9SAndroid Build Coastguard Worker		if newValue, err := assignment.Value.Eval(s); err != nil {
834*1fa6dee9SAndroid Build Coastguard Worker			return err
835*1fa6dee9SAndroid Build Coastguard Worker		} else {
836*1fa6dee9SAndroid Build Coastguard Worker			assignment.Value = newValue
837*1fa6dee9SAndroid Build Coastguard Worker		}
838*1fa6dee9SAndroid Build Coastguard Worker		s.vars[assignment.Name] = assignment
839*1fa6dee9SAndroid Build Coastguard Worker	default:
840*1fa6dee9SAndroid Build Coastguard Worker		return fmt.Errorf("Unknown assigner '%s'", assignment.Assigner)
841*1fa6dee9SAndroid Build Coastguard Worker	}
842*1fa6dee9SAndroid Build Coastguard Worker	return nil
843*1fa6dee9SAndroid Build Coastguard Worker}
844*1fa6dee9SAndroid Build Coastguard Worker
845*1fa6dee9SAndroid Build Coastguard Workerfunc (s *Scope) Get(name string) *Assignment {
846*1fa6dee9SAndroid Build Coastguard Worker	if s == nil {
847*1fa6dee9SAndroid Build Coastguard Worker		return nil
848*1fa6dee9SAndroid Build Coastguard Worker	}
849*1fa6dee9SAndroid Build Coastguard Worker	if a, ok := s.vars[name]; ok {
850*1fa6dee9SAndroid Build Coastguard Worker		return a
851*1fa6dee9SAndroid Build Coastguard Worker	}
852*1fa6dee9SAndroid Build Coastguard Worker	if s.preventInheriting[name] {
853*1fa6dee9SAndroid Build Coastguard Worker		return nil
854*1fa6dee9SAndroid Build Coastguard Worker	}
855*1fa6dee9SAndroid Build Coastguard Worker	return s.parentScope.Get(name)
856*1fa6dee9SAndroid Build Coastguard Worker}
857*1fa6dee9SAndroid Build Coastguard Worker
858*1fa6dee9SAndroid Build Coastguard Workerfunc (s *Scope) GetLocal(name string) *Assignment {
859*1fa6dee9SAndroid Build Coastguard Worker	if s == nil {
860*1fa6dee9SAndroid Build Coastguard Worker		return nil
861*1fa6dee9SAndroid Build Coastguard Worker	}
862*1fa6dee9SAndroid Build Coastguard Worker	if a, ok := s.vars[name]; ok {
863*1fa6dee9SAndroid Build Coastguard Worker		return a
864*1fa6dee9SAndroid Build Coastguard Worker	}
865*1fa6dee9SAndroid Build Coastguard Worker	return nil
866*1fa6dee9SAndroid Build Coastguard Worker}
867*1fa6dee9SAndroid Build Coastguard Worker
868*1fa6dee9SAndroid Build Coastguard Worker// DontInherit prevents this scope from inheriting the given variable from its
869*1fa6dee9SAndroid Build Coastguard Worker// parent scope.
870*1fa6dee9SAndroid Build Coastguard Workerfunc (s *Scope) DontInherit(name string) {
871*1fa6dee9SAndroid Build Coastguard Worker	s.preventInheriting[name] = true
872*1fa6dee9SAndroid Build Coastguard Worker}
873*1fa6dee9SAndroid Build Coastguard Worker
874*1fa6dee9SAndroid Build Coastguard Workerfunc (s *Scope) String() string {
875*1fa6dee9SAndroid Build Coastguard Worker	var sb strings.Builder
876*1fa6dee9SAndroid Build Coastguard Worker	s.stringInner(&sb)
877*1fa6dee9SAndroid Build Coastguard Worker	return sb.String()
878*1fa6dee9SAndroid Build Coastguard Worker}
879*1fa6dee9SAndroid Build Coastguard Worker
880*1fa6dee9SAndroid Build Coastguard Workerfunc (s *Scope) stringInner(sb *strings.Builder) {
881*1fa6dee9SAndroid Build Coastguard Worker	if s == nil {
882*1fa6dee9SAndroid Build Coastguard Worker		return
883*1fa6dee9SAndroid Build Coastguard Worker	}
884*1fa6dee9SAndroid Build Coastguard Worker	vars := make([]string, 0, len(s.vars))
885*1fa6dee9SAndroid Build Coastguard Worker	for k := range s.vars {
886*1fa6dee9SAndroid Build Coastguard Worker		vars = append(vars, k)
887*1fa6dee9SAndroid Build Coastguard Worker	}
888*1fa6dee9SAndroid Build Coastguard Worker
889*1fa6dee9SAndroid Build Coastguard Worker	sort.Strings(vars)
890*1fa6dee9SAndroid Build Coastguard Worker
891*1fa6dee9SAndroid Build Coastguard Worker	for _, v := range vars {
892*1fa6dee9SAndroid Build Coastguard Worker		sb.WriteString(s.vars[v].String())
893*1fa6dee9SAndroid Build Coastguard Worker		sb.WriteRune('\n')
894*1fa6dee9SAndroid Build Coastguard Worker	}
895*1fa6dee9SAndroid Build Coastguard Worker
896*1fa6dee9SAndroid Build Coastguard Worker	s.parentScope.stringInner(sb)
897*1fa6dee9SAndroid Build Coastguard Worker}
898