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