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