blob: 6688537e0e245bf86cfbb2871c5aaf2a64178ad3 [file] [log] [blame]
Fumitoshi Ukai119dc912015-03-30 16:52:41 +09001package main
2
Fumitoshi Ukai0aa4fc42015-04-10 17:00:19 +09003//go:generate go run testcase/gen_testcase_parse_benchmark.go
4//
5// $ go generate
6// $ go test -bench .
7
Fumitoshi Ukai119dc912015-03-30 16:52:41 +09008import (
9 "bufio"
Shinichiro Hamajie1841582015-03-30 17:20:33 +090010 "bytes"
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090011 "errors"
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +090012 "fmt"
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090013 "io"
14 "os"
Shinichiro Hamajid7bef602015-03-30 19:55:32 +090015 "strings"
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090016)
17
18type Makefile struct {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +090019 filename string
20 stmts []AST
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090021}
22
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090023type ifState struct {
Shinichiro Hamajia06760f2015-04-07 13:13:45 +090024 ast *IfAST
25 inElse bool
26 numNest int
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090027}
28
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090029type parser struct {
Shinichiro Hamaji370be722015-04-10 14:55:23 +090030 rd *bufio.Reader
31 mk Makefile
32 lineno int
33 elineno int // lineno == elineno unless there is trailing '\'.
34 linenoFixed bool
35 unBuf []byte
36 hasUnBuf bool
37 done bool
38 outStmts *[]AST
39 ifStack []ifState
40 inDef []string
41 numIfNest int
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090042}
43
44func exists(filename string) bool {
45 f, err := os.Open(filename)
46 if err != nil {
47 return false
48 }
49 f.Close()
50 return true
51}
52
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +090053func newParser(rd io.Reader, filename string) *parser {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090054 p := &parser{
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +090055 rd: bufio.NewReader(rd),
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090056 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +090057 p.mk.filename = filename
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090058 p.outStmts = &p.mk.stmts
59 return p
60}
61
Shinichiro Hamajiae32b782015-03-31 14:41:19 +090062func (p *parser) addStatement(ast AST) {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090063 *p.outStmts = append(*p.outStmts, ast)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090064}
65
Shinichiro Hamajie1841582015-03-30 17:20:33 +090066func (p *parser) readLine() []byte {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090067 if p.hasUnBuf {
68 p.hasUnBuf = false
69 return p.unBuf
Shinichiro Hamajie1841582015-03-30 17:20:33 +090070 }
71
Shinichiro Hamaji370be722015-04-10 14:55:23 +090072 if !p.linenoFixed {
73 p.lineno = p.elineno
74 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +090075 line, err := p.rd.ReadBytes('\n')
Shinichiro Hamaji370be722015-04-10 14:55:23 +090076 if !p.linenoFixed {
77 p.lineno++
78 p.elineno = p.lineno
79 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +090080 if err == io.EOF {
81 p.done = true
82 } else if err != nil {
Fumitoshi Ukaia9e51362015-04-17 10:26:00 +090083 panic(fmt.Errorf("readline %s:%d: %v", p.mk.filename, p.lineno, err))
Shinichiro Hamajie1841582015-03-30 17:20:33 +090084 }
85
Shinichiro Hamaji56c868c2015-04-09 10:17:10 +090086 line = bytes.TrimRight(line, "\r\n")
Shinichiro Hamajie1841582015-03-30 17:20:33 +090087
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +090088 return line
89}
90
91func removeComment(line []byte) []byte {
92 var parenStack []byte
Shinichiro Hamajif4d3ee52015-04-22 16:19:33 +090093 // Do not use range as we may modify |line| and |i|.
94 for i := 0; i < len(line); i++ {
95 ch := line[i]
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +090096 switch ch {
97 case '(', '{':
98 parenStack = append(parenStack, ch)
99 case ')', '}':
100 if len(parenStack) > 0 {
101 parenStack = parenStack[:len(parenStack)-1]
102 }
103 case '#':
104 if len(parenStack) == 0 {
Shinichiro Hamajif4d3ee52015-04-22 16:19:33 +0900105 if i == 0 || line[i-1] != '\\' {
106 return line[:i]
107 }
108 // Drop the backslash before '#'.
109 line = append(line[:i-1], line[i:]...)
110 i--
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900111 }
112 }
113 }
114 return line
115}
116
Shinichiro Hamajie2f6e902015-04-22 16:08:47 +0900117func hasTrailingBackslash(line []byte) bool {
118 if len(line) == 0 {
119 return false
120 }
121 if line[len(line)-1] != '\\' {
122 return false
123 }
124 return len(line) <= 1 || line[len(line)-2] != '\\'
125}
126
Shinichiro Hamajie52c16c2015-04-11 23:41:39 +0900127func (p *parser) processDefineLine(line []byte) []byte {
Shinichiro Hamajie2f6e902015-04-22 16:08:47 +0900128 for hasTrailingBackslash(line) {
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900129 line = line[:len(line)-1]
Shinichiro Hamajic4c98102015-04-12 03:56:00 +0900130 line = bytes.TrimRight(line, "\t ")
Shinichiro Hamaji3e4533d2015-04-06 14:03:58 +0900131 lineno := p.lineno
Shinichiro Hamajic4c98102015-04-12 03:56:00 +0900132 nline := trimLeftSpaceBytes(p.readLine())
Shinichiro Hamaji3e4533d2015-04-06 14:03:58 +0900133 p.lineno = lineno
Shinichiro Hamajic4c98102015-04-12 03:56:00 +0900134 line = append(line, ' ')
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900135 line = append(line, nline...)
136 }
Shinichiro Hamajie52c16c2015-04-11 23:41:39 +0900137 return line
138}
139
140func (p *parser) processMakefileLine(line []byte) []byte {
141 return removeComment(p.processDefineLine(line))
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900142}
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900143
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900144func (p *parser) processRecipeLine(line []byte) []byte {
Shinichiro Hamajie2f6e902015-04-22 16:08:47 +0900145 for hasTrailingBackslash(line) {
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900146 line = append(line, '\n')
147 lineno := p.lineno
148 nline := p.readLine()
149 p.lineno = lineno
150 line = append(line, nline...)
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900151 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900152 return line
153}
154
155func (p *parser) unreadLine(line []byte) {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900156 if p.hasUnBuf {
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900157 panic("unreadLine twice!")
158 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900159 p.unBuf = line
160 p.hasUnBuf = true
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900161}
162
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900163func (p *parser) parseAssign(line []byte, sep, esep int) AST {
Fumitoshi Ukai8773e5e2015-04-01 11:23:18 +0900164 Log("parseAssign %q op:%q", line, line[sep:esep])
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900165 // TODO(ukai): parse expr here.
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900166 ast := &AssignAST{
167 lhs: string(bytes.TrimSpace(line[:sep])),
Shinichiro Hamajic88618f2015-04-11 19:58:06 +0900168 rhs: trimLeftSpace(string(line[esep:])),
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900169 op: string(line[sep:esep]),
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900170 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900171 ast.filename = p.mk.filename
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900172 ast.lineno = p.lineno
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900173 return ast
174}
175
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900176func (p *parser) parseMaybeRule(line string, equalIndex, semicolonIndex int) AST {
Shinichiro Hamajide829712015-03-31 18:26:56 +0900177 if len(strings.TrimSpace(line)) == 0 {
178 return nil
179 }
Shinichiro Hamajide829712015-03-31 18:26:56 +0900180
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900181 // Either '=' or ';' is used.
182 if equalIndex >= 0 && semicolonIndex >= 0 {
183 if equalIndex < semicolonIndex {
184 semicolonIndex = -1
185 } else {
186 equalIndex = -1
187 }
188 }
189
Shinichiro Hamaji486e9de2015-04-09 15:20:32 +0900190 ast := &MaybeRuleAST{
191 expr: line,
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900192 equalIndex: equalIndex,
Shinichiro Hamaji486e9de2015-04-09 15:20:32 +0900193 semicolonIndex: semicolonIndex,
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900194 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900195 ast.filename = p.mk.filename
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900196 ast.lineno = p.lineno
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900197 return ast
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900198}
199
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900200func (p *parser) parseInclude(line string, oplen int) AST {
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900201 // TODO(ukai): parse expr here
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900202 ast := &IncludeAST{
203 expr: line[oplen+1:],
204 op: line[:oplen],
205 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900206 ast.filename = p.mk.filename
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900207 ast.lineno = p.lineno
208 return ast
209}
210
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900211func (p *parser) parseIfdef(line string, oplen int) AST {
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900212 // TODO(ukai): parse expr here.
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900213 ast := &IfAST{
Shinichiro Hamajiae32b782015-03-31 14:41:19 +0900214 op: line[:oplen],
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900215 lhs: line[oplen+1:],
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900216 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900217 ast.filename = p.mk.filename
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900218 ast.lineno = p.lineno
219 p.addStatement(ast)
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900220 p.ifStack = append(p.ifStack, ifState{ast: ast, numNest: p.numIfNest})
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900221 p.outStmts = &ast.trueStmts
222 return ast
223}
224
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900225func (p *parser) parseTwoQuotes(s string, op string) ([]string, bool) {
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900226 var args []string
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900227 for i := 0; i < 2; i++ {
228 s = strings.TrimSpace(s)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900229 if s == "" {
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900230 return nil, false
231 }
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900232 quote := s[0]
233 if quote != '\'' && quote != '"' {
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900234 return nil, false
235 }
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900236 end := strings.IndexByte(s[1:], quote) + 1
237 if end < 0 {
238 return nil, false
239 }
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900240 args = append(args, s[1:end])
241 s = s[end+1:]
242 }
243 if len(s) > 0 {
244 Error(p.mk.filename, p.lineno, `extraneous text after %q directive`, op)
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900245 }
246 return args, true
247}
248
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900249// parse
250// "(lhs, rhs)"
251// "lhs, rhs"
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900252func (p *parser) parseEq(s string, op string) (string, string, bool) {
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900253 if s[0] == '(' && s[len(s)-1] == ')' {
254 s = s[1 : len(s)-1]
255 term := []byte{','}
256 in := []byte(s)
257 v, n, err := parseExpr(in, term)
258 if err != nil {
259 return "", "", false
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900260 }
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900261 lhs := v.String()
262 n++
263 n += skipSpaces(in[n:], nil)
264 v, n, err = parseExpr(in[n:], nil)
265 if err != nil {
266 return "", "", false
267 }
268 rhs := v.String()
269 return lhs, rhs, true
270 }
271 args, ok := p.parseTwoQuotes(s, op)
272 if !ok {
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900273 return "", "", false
274 }
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900275 return args[0], args[1], true
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900276}
277
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900278func (p *parser) parseIfeq(line string, oplen int) AST {
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900279 op := line[:oplen]
280 lhs, rhs, ok := p.parseEq(strings.TrimSpace(line[oplen+1:]), op)
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900281 if !ok {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900282 Error(p.mk.filename, p.lineno, `*** invalid syntax in conditional.`)
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900283 }
284
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900285 ast := &IfAST{
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900286 op: op,
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900287 lhs: lhs,
288 rhs: rhs,
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900289 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900290 ast.filename = p.mk.filename
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900291 ast.lineno = p.lineno
292 p.addStatement(ast)
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900293 p.ifStack = append(p.ifStack, ifState{ast: ast, numNest: p.numIfNest})
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900294 p.outStmts = &ast.trueStmts
295 return ast
296}
297
298func (p *parser) checkIfStack(curKeyword string) {
299 if len(p.ifStack) == 0 {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900300 Error(p.mk.filename, p.lineno, `*** extraneous %q.`, curKeyword)
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900301 }
302}
303
304func (p *parser) parseElse(line string) {
305 p.checkIfStack("else")
306 state := &p.ifStack[len(p.ifStack)-1]
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +0900307 if state.inElse {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900308 Error(p.mk.filename, p.lineno, `*** only one "else" per conditional.`)
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900309 }
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +0900310 state.inElse = true
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900311 p.outStmts = &state.ast.falseStmts
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900312
313 nextIf := strings.TrimSpace(line[len("else"):])
314 if len(nextIf) == 0 {
315 return
316 }
317 var ifDirectives = map[string]func(*parser, string){
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900318 "ifdef ": ifdefDirective,
319 "ifndef ": ifndefDirective,
320 "ifeq ": ifeqDirective,
321 "ifneq ": ifneqDirective,
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900322 }
323 p.numIfNest = state.numNest + 1
324 if p.parseKeywords(nextIf, ifDirectives) {
325 p.numIfNest = 0
326 return
327 }
328 p.numIfNest = 0
329 WarnNoPrefix(p.mk.filename, p.lineno, "extraneous text after `else` directive")
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900330}
331
332func (p *parser) parseEndif(line string) {
333 p.checkIfStack("endif")
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900334 state := p.ifStack[len(p.ifStack)-1]
335 for t := 0; t <= state.numNest; t++ {
336 p.ifStack = p.ifStack[0 : len(p.ifStack)-1]
337 if len(p.ifStack) == 0 {
338 p.outStmts = &p.mk.stmts
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900339 } else {
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900340 state := p.ifStack[len(p.ifStack)-1]
341 if state.inElse {
342 p.outStmts = &state.ast.falseStmts
343 } else {
344 p.outStmts = &state.ast.trueStmts
345 }
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900346 }
347 }
348}
349
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900350var makeDirectives = map[string]func(*parser, string){
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900351 "include ": includeDirective,
352 "-include ": sincludeDirective,
353 "sinclude": sincludeDirective,
354 "ifdef ": ifdefDirective,
355 "ifndef ": ifndefDirective,
356 "ifeq ": ifeqDirective,
357 "ifneq ": ifneqDirective,
358 "else": elseDirective,
359 "endif": endifDirective,
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900360 "define ": defineDirective,
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900361}
362
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900363func (p *parser) parseKeywords(line string, directives map[string]func(*parser, string)) bool {
Shinichiro Hamajic88618f2015-04-11 19:58:06 +0900364 stripped := trimLeftSpace(line)
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900365 for prefix, f := range directives {
366 if strings.HasPrefix(stripped, prefix) {
367 f(p, stripped)
368 return true
369 }
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900370 }
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900371 return false
372}
373
Shinichiro Hamaji74b8cb52015-04-08 19:47:43 +0900374func (p *parser) isDirective(line string, directives map[string]func(*parser, string)) bool {
Shinichiro Hamajic88618f2015-04-11 19:58:06 +0900375 stripped := trimLeftSpace(line)
Shinichiro Hamajie103f652015-04-11 19:49:51 +0900376 // Fast paths.
377 // TODO: Consider using a trie.
378 if len(stripped) == 0 {
379 return false
380 }
381 if ch := stripped[0]; ch != 'i' && ch != '-' && ch != 's' && ch != 'e' && ch != 'd' {
382 return false
383 }
384
Shinichiro Hamaji74b8cb52015-04-08 19:47:43 +0900385 for prefix, _ := range directives {
386 if strings.HasPrefix(stripped, prefix) {
387 return true
388 }
389 }
390 return false
391}
392
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900393func includeDirective(p *parser, line string) {
394 p.addStatement(p.parseInclude(line, len("include")))
395}
396
397func sincludeDirective(p *parser, line string) {
398 p.addStatement(p.parseInclude(line, len("-include")))
399}
400
401func ifdefDirective(p *parser, line string) {
402 p.parseIfdef(line, len("ifdef"))
403}
404
405func ifndefDirective(p *parser, line string) {
406 p.parseIfdef(line, len("ifndef"))
407}
408
409func ifeqDirective(p *parser, line string) {
410 p.parseIfeq(line, len("ifeq"))
411}
412
413func ifneqDirective(p *parser, line string) {
414 p.parseIfeq(line, len("ifneq"))
415}
416
417func elseDirective(p *parser, line string) {
418 p.parseElse(line)
419}
420
421func endifDirective(p *parser, line string) {
422 p.parseEndif(line)
423}
424
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900425func defineDirective(p *parser, line string) {
Shinichiro Hamajic88618f2015-04-11 19:58:06 +0900426 p.inDef = []string{trimLeftSpace(line[len("define "):])}
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900427}
428
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900429func (p *parser) parse() (mk Makefile, err error) {
430 defer func() {
431 if r := recover(); r != nil {
Fumitoshi Ukaia9e51362015-04-17 10:26:00 +0900432 err = fmt.Errorf("panic in parse %s: %v", mk.filename, r)
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900433 }
434 }()
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900435 for !p.done {
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900436 line := p.readLine()
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900437
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900438 if len(p.inDef) > 0 {
Shinichiro Hamajie52c16c2015-04-11 23:41:39 +0900439 line = p.processDefineLine(line)
Shinichiro Hamajic88618f2015-04-11 19:58:06 +0900440 if trimLeftSpace(string(line)) == "endef" {
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900441 Log("multilineAssign %q", p.inDef)
442 ast := &AssignAST{
443 lhs: p.inDef[0],
444 rhs: strings.Join(p.inDef[1:], "\n"),
445 op: "=",
446 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900447 ast.filename = p.mk.filename
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900448 ast.lineno = p.lineno - len(p.inDef)
449 p.addStatement(ast)
450 p.inDef = nil
451 continue
452 }
453 p.inDef = append(p.inDef, string(line))
454 continue
455 }
456
Shinichiro Hamaji960161f2015-04-13 17:10:10 +0900457 if len(bytes.TrimSpace(line)) == 0 {
458 continue
459 }
460
Shinichiro Hamaji74b8cb52015-04-08 19:47:43 +0900461 if p.isDirective(string(line), makeDirectives) {
462 line = p.processMakefileLine(line)
463 p.parseKeywords(string(line), makeDirectives)
464 continue
465 }
466
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900467 if line[0] == '\t' {
468 ast := &CommandAST{cmd: string(p.processRecipeLine(line[1:]))}
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900469 ast.filename = p.mk.filename
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900470 ast.lineno = p.lineno
471 p.addStatement(ast)
472 continue
473 }
474
475 line = p.processMakefileLine(line)
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900476
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900477 var ast AST
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900478 var parenStack []byte
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900479 equalIndex := -1
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900480 semicolonIndex := -1
481 isRule := false
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900482 for i, ch := range line {
483 switch ch {
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900484 case '(', '{':
485 parenStack = append(parenStack, ch)
486 case ')', '}':
487 if len(parenStack) == 0 {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900488 Warn(p.mk.filename, p.lineno, "Unmatched parens: %s", line)
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900489 } else {
490 parenStack = parenStack[:len(parenStack)-1]
491 }
492 }
493 if len(parenStack) > 0 {
494 continue
495 }
496
497 switch ch {
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900498 case ':':
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900499 if i+1 < len(line) && line[i+1] == '=' {
Shinichiro Hamaji156ef3e2015-04-11 22:44:41 +0900500 if !isRule {
501 ast = p.parseAssign(line, i, i+2)
502 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900503 } else {
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900504 isRule = true
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900505 }
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900506 case ';':
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900507 if semicolonIndex < 0 {
508 semicolonIndex = i
509 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900510 case '=':
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900511 if !isRule {
512 ast = p.parseAssign(line, i, i+1)
513 }
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900514 if equalIndex < 0 {
515 equalIndex = i
516 }
Shinichiro Hamaji69b7f652015-03-31 01:01:59 +0900517 case '?', '+':
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900518 if !isRule && i+1 < len(line) && line[i+1] == '=' {
Shinichiro Hamaji69b7f652015-03-31 01:01:59 +0900519 ast = p.parseAssign(line, i, i+2)
520 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900521 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900522 if ast != nil {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900523 p.addStatement(ast)
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900524 break
525 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900526 }
Shinichiro Hamajide829712015-03-31 18:26:56 +0900527 if ast == nil {
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900528 ast = p.parseMaybeRule(string(line), equalIndex, semicolonIndex)
Shinichiro Hamajide829712015-03-31 18:26:56 +0900529 if ast != nil {
530 p.addStatement(ast)
531 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900532 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900533 }
534 return p.mk, nil
535}
536
537func ParseMakefile(filename string) (Makefile, error) {
Fumitoshi Ukaia9e51362015-04-17 10:26:00 +0900538 Log("ParseMakefile %q", filename)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900539 f, err := os.Open(filename)
540 if err != nil {
541 return Makefile{}, err
542 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900543 defer f.Close()
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900544 parser := newParser(f, filename)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900545 return parser.parse()
546}
547
548func ParseDefaultMakefile() (Makefile, error) {
549 candidates := []string{"GNUmakefile", "makefile", "Makefile"}
550 for _, filename := range candidates {
551 if exists(filename) {
552 return ParseMakefile(filename)
553 }
554 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900555 return Makefile{}, errors.New("no targets specified and no makefile found.")
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900556}
Shinichiro Hamajic3840812015-04-02 02:10:20 +0900557
Shinichiro Hamaji28ea5bc2015-04-11 12:41:08 +0900558func parseMakefileReader(rd io.Reader, name string, lineno int) (Makefile, error) {
Shinichiro Hamajia5dee372015-04-03 16:41:30 +0900559 parser := newParser(rd, name)
Shinichiro Hamaji370be722015-04-10 14:55:23 +0900560 parser.lineno = lineno
Shinichiro Hamaji29ffc972015-04-06 16:13:27 +0900561 parser.elineno = lineno
Shinichiro Hamaji370be722015-04-10 14:55:23 +0900562 parser.linenoFixed = true
Shinichiro Hamajia5dee372015-04-03 16:41:30 +0900563 return parser.parse()
Shinichiro Hamajic3840812015-04-02 02:10:20 +0900564}
Shinichiro Hamaji28ea5bc2015-04-11 12:41:08 +0900565
566func ParseMakefileString(s string, name string, lineno int) (Makefile, error) {
567 return parseMakefileReader(strings.NewReader(s), name, lineno)
568}
569
570func ParseMakefileBytes(s []byte, name string, lineno int) (Makefile, error) {
571 return parseMakefileReader(bytes.NewReader(s), name, lineno)
572}