blob: 44b63e350d532065dee8626f1f18b034189ded3d [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 {
83 panic(err)
84 }
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
93 for i, ch := range line {
94 switch ch {
95 case '(', '{':
96 parenStack = append(parenStack, ch)
97 case ')', '}':
98 if len(parenStack) > 0 {
99 parenStack = parenStack[:len(parenStack)-1]
100 }
101 case '#':
102 if len(parenStack) == 0 {
103 return line[:i]
104 }
105 }
106 }
107 return line
108}
109
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900110func (p *parser) processMakefileLine(line []byte) []byte {
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900111 // TODO: Handle \\ at the end of the line?
112 for len(line) > 0 && line[len(line)-1] == '\\' {
113 line = line[:len(line)-1]
Shinichiro Hamaji3e4533d2015-04-06 14:03:58 +0900114 lineno := p.lineno
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900115 nline := p.readLine()
Shinichiro Hamaji3e4533d2015-04-06 14:03:58 +0900116 p.lineno = lineno
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900117 line = append(line, nline...)
118 }
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900119 return removeComment(line)
120}
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900121
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900122func (p *parser) processRecipeLine(line []byte) []byte {
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900123 // TODO: Handle \\ at the end of the line?
124 for len(line) > 0 && line[len(line)-1] == '\\' {
125 line = append(line, '\n')
126 lineno := p.lineno
127 nline := p.readLine()
128 p.lineno = lineno
129 line = append(line, nline...)
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900130 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900131 return line
132}
133
134func (p *parser) unreadLine(line []byte) {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900135 if p.hasUnBuf {
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900136 panic("unreadLine twice!")
137 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900138 p.unBuf = line
139 p.hasUnBuf = true
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900140}
141
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900142func (p *parser) parseAssign(line []byte, sep, esep int) AST {
Fumitoshi Ukai8773e5e2015-04-01 11:23:18 +0900143 Log("parseAssign %q op:%q", line, line[sep:esep])
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900144 // TODO(ukai): parse expr here.
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900145 ast := &AssignAST{
146 lhs: string(bytes.TrimSpace(line[:sep])),
147 rhs: string(bytes.TrimLeft(line[esep:], " \t")),
148 op: string(line[sep:esep]),
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900149 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900150 ast.filename = p.mk.filename
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900151 ast.lineno = p.lineno
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900152 return ast
153}
154
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900155func (p *parser) parseMaybeRule(line string, semicolonIndex int) AST {
Shinichiro Hamajide829712015-03-31 18:26:56 +0900156 if len(strings.TrimSpace(line)) == 0 {
157 return nil
158 }
Shinichiro Hamajide829712015-03-31 18:26:56 +0900159
Shinichiro Hamaji486e9de2015-04-09 15:20:32 +0900160 ast := &MaybeRuleAST{
161 expr: line,
162 semicolonIndex: semicolonIndex,
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900163 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900164 ast.filename = p.mk.filename
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900165 ast.lineno = p.lineno
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900166 /*
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900167 ast.cmdLineno = p.elineno + 1
168 for {
169 line := p.readRecipeLine()
170 if len(line) == 0 {
171 break
172 } else if line[0] == '\t' {
173 ast.cmds = append(ast.cmds, string(bytes.TrimLeft(line, " \t")))
174 } else {
175 p.unreadLine(line)
176 break
177 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900178 }
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900179 */
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900180 return ast
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900181}
182
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900183func (p *parser) parseInclude(line string, oplen int) AST {
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900184 // TODO(ukai): parse expr here
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900185 ast := &IncludeAST{
186 expr: line[oplen+1:],
187 op: line[:oplen],
188 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900189 ast.filename = p.mk.filename
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900190 ast.lineno = p.lineno
191 return ast
192}
193
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900194func (p *parser) parseIfdef(line string, oplen int) AST {
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900195 // TODO(ukai): parse expr here.
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900196 ast := &IfAST{
Shinichiro Hamajiae32b782015-03-31 14:41:19 +0900197 op: line[:oplen],
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900198 lhs: line[oplen+1:],
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900199 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900200 ast.filename = p.mk.filename
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900201 ast.lineno = p.lineno
202 p.addStatement(ast)
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900203 p.ifStack = append(p.ifStack, ifState{ast: ast, numNest: p.numIfNest})
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900204 p.outStmts = &ast.trueStmts
205 return ast
206}
207
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900208func (p *parser) parseTwoQuotes(s string, op string) ([]string, bool) {
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900209 var args []string
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900210 for i := 0; i < 2; i++ {
211 s = strings.TrimSpace(s)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900212 if s == "" {
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900213 return nil, false
214 }
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900215 quote := s[0]
216 if quote != '\'' && quote != '"' {
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900217 return nil, false
218 }
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900219 end := strings.IndexByte(s[1:], quote) + 1
220 if end < 0 {
221 return nil, false
222 }
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900223 args = append(args, s[1:end])
224 s = s[end+1:]
225 }
226 if len(s) > 0 {
227 Error(p.mk.filename, p.lineno, `extraneous text after %q directive`, op)
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900228 }
229 return args, true
230}
231
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900232// parse
233// "(lhs, rhs)"
234// "lhs, rhs"
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900235func (p *parser) parseEq(s string, op string) (string, string, bool) {
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900236 if s[0] == '(' && s[len(s)-1] == ')' {
237 s = s[1 : len(s)-1]
238 term := []byte{','}
239 in := []byte(s)
240 v, n, err := parseExpr(in, term)
241 if err != nil {
242 return "", "", false
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900243 }
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900244 lhs := v.String()
245 n++
246 n += skipSpaces(in[n:], nil)
247 v, n, err = parseExpr(in[n:], nil)
248 if err != nil {
249 return "", "", false
250 }
251 rhs := v.String()
252 return lhs, rhs, true
253 }
254 args, ok := p.parseTwoQuotes(s, op)
255 if !ok {
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900256 return "", "", false
257 }
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900258 return args[0], args[1], true
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900259}
260
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900261func (p *parser) parseIfeq(line string, oplen int) AST {
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900262 op := line[:oplen]
263 lhs, rhs, ok := p.parseEq(strings.TrimSpace(line[oplen+1:]), op)
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900264 if !ok {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900265 Error(p.mk.filename, p.lineno, `*** invalid syntax in conditional.`)
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900266 }
267
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900268 ast := &IfAST{
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900269 op: op,
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900270 lhs: lhs,
271 rhs: rhs,
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900272 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900273 ast.filename = p.mk.filename
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900274 ast.lineno = p.lineno
275 p.addStatement(ast)
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900276 p.ifStack = append(p.ifStack, ifState{ast: ast, numNest: p.numIfNest})
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900277 p.outStmts = &ast.trueStmts
278 return ast
279}
280
281func (p *parser) checkIfStack(curKeyword string) {
282 if len(p.ifStack) == 0 {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900283 Error(p.mk.filename, p.lineno, `*** extraneous %q.`, curKeyword)
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900284 }
285}
286
287func (p *parser) parseElse(line string) {
288 p.checkIfStack("else")
289 state := &p.ifStack[len(p.ifStack)-1]
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +0900290 if state.inElse {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900291 Error(p.mk.filename, p.lineno, `*** only one "else" per conditional.`)
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900292 }
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +0900293 state.inElse = true
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900294 p.outStmts = &state.ast.falseStmts
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900295
296 nextIf := strings.TrimSpace(line[len("else"):])
297 if len(nextIf) == 0 {
298 return
299 }
300 var ifDirectives = map[string]func(*parser, string){
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900301 "ifdef ": ifdefDirective,
302 "ifndef ": ifndefDirective,
303 "ifeq ": ifeqDirective,
304 "ifneq ": ifneqDirective,
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900305 }
306 p.numIfNest = state.numNest + 1
307 if p.parseKeywords(nextIf, ifDirectives) {
308 p.numIfNest = 0
309 return
310 }
311 p.numIfNest = 0
312 WarnNoPrefix(p.mk.filename, p.lineno, "extraneous text after `else` directive")
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900313}
314
315func (p *parser) parseEndif(line string) {
316 p.checkIfStack("endif")
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900317 state := p.ifStack[len(p.ifStack)-1]
318 for t := 0; t <= state.numNest; t++ {
319 p.ifStack = p.ifStack[0 : len(p.ifStack)-1]
320 if len(p.ifStack) == 0 {
321 p.outStmts = &p.mk.stmts
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900322 } else {
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900323 state := p.ifStack[len(p.ifStack)-1]
324 if state.inElse {
325 p.outStmts = &state.ast.falseStmts
326 } else {
327 p.outStmts = &state.ast.trueStmts
328 }
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900329 }
330 }
331}
332
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900333var makeDirectives = map[string]func(*parser, string){
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900334 "include ": includeDirective,
335 "-include ": sincludeDirective,
336 "sinclude": sincludeDirective,
337 "ifdef ": ifdefDirective,
338 "ifndef ": ifndefDirective,
339 "ifeq ": ifeqDirective,
340 "ifneq ": ifneqDirective,
341 "else": elseDirective,
342 "endif": endifDirective,
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900343 "define ": defineDirective,
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900344}
345
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900346func (p *parser) parseKeywords(line string, directives map[string]func(*parser, string)) bool {
Shinichiro Hamajie3a94632015-03-31 01:12:52 +0900347 stripped := strings.TrimLeft(line, " \t")
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900348 for prefix, f := range directives {
349 if strings.HasPrefix(stripped, prefix) {
350 f(p, stripped)
351 return true
352 }
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900353 }
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900354 return false
355}
356
Shinichiro Hamaji74b8cb52015-04-08 19:47:43 +0900357func (p *parser) isDirective(line string, directives map[string]func(*parser, string)) bool {
358 stripped := strings.TrimLeft(line, " \t")
359 for prefix, _ := range directives {
360 if strings.HasPrefix(stripped, prefix) {
361 return true
362 }
363 }
364 return false
365}
366
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900367func includeDirective(p *parser, line string) {
368 p.addStatement(p.parseInclude(line, len("include")))
369}
370
371func sincludeDirective(p *parser, line string) {
372 p.addStatement(p.parseInclude(line, len("-include")))
373}
374
375func ifdefDirective(p *parser, line string) {
376 p.parseIfdef(line, len("ifdef"))
377}
378
379func ifndefDirective(p *parser, line string) {
380 p.parseIfdef(line, len("ifndef"))
381}
382
383func ifeqDirective(p *parser, line string) {
384 p.parseIfeq(line, len("ifeq"))
385}
386
387func ifneqDirective(p *parser, line string) {
388 p.parseIfeq(line, len("ifneq"))
389}
390
391func elseDirective(p *parser, line string) {
392 p.parseElse(line)
393}
394
395func endifDirective(p *parser, line string) {
396 p.parseEndif(line)
397}
398
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900399func defineDirective(p *parser, line string) {
400 p.inDef = []string{strings.TrimLeft(line[len("define "):], " \t")}
401}
402
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900403func (p *parser) parse() (mk Makefile, err error) {
404 defer func() {
405 if r := recover(); r != nil {
406 err = fmt.Errorf("panic: %v", r)
407 }
408 }()
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900409 for !p.done {
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900410 line := p.readLine()
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900411
Shinichiro Hamaji81372e52015-04-09 11:32:09 +0900412 if len(bytes.TrimSpace(line)) == 0 {
413 continue
414 }
415
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900416 if len(p.inDef) > 0 {
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900417 line = p.processMakefileLine(line)
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900418 if strings.TrimLeft(string(line), " ") == "endef" {
419 Log("multilineAssign %q", p.inDef)
420 ast := &AssignAST{
421 lhs: p.inDef[0],
422 rhs: strings.Join(p.inDef[1:], "\n"),
423 op: "=",
424 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900425 ast.filename = p.mk.filename
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900426 ast.lineno = p.lineno - len(p.inDef)
427 p.addStatement(ast)
428 p.inDef = nil
429 continue
430 }
431 p.inDef = append(p.inDef, string(line))
432 continue
433 }
434
Shinichiro Hamaji74b8cb52015-04-08 19:47:43 +0900435 if p.isDirective(string(line), makeDirectives) {
436 line = p.processMakefileLine(line)
437 p.parseKeywords(string(line), makeDirectives)
438 continue
439 }
440
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900441 if line[0] == '\t' {
442 ast := &CommandAST{cmd: string(p.processRecipeLine(line[1:]))}
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900443 ast.filename = p.mk.filename
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900444 ast.lineno = p.lineno
445 p.addStatement(ast)
446 continue
447 }
448
449 line = p.processMakefileLine(line)
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900450
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900451 var ast AST
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900452 var parenStack []byte
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900453 semicolonIndex := -1
454 isRule := false
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900455 for i, ch := range line {
456 switch ch {
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900457 case '(', '{':
458 parenStack = append(parenStack, ch)
459 case ')', '}':
460 if len(parenStack) == 0 {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900461 Warn(p.mk.filename, p.lineno, "Unmatched parens: %s", line)
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900462 } else {
463 parenStack = parenStack[:len(parenStack)-1]
464 }
465 }
466 if len(parenStack) > 0 {
467 continue
468 }
469
470 switch ch {
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900471 case ':':
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900472 if i+1 < len(line) && line[i+1] == '=' {
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900473 ast = p.parseAssign(line, i, i+2)
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900474 } else {
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900475 isRule = true
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900476 }
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900477 case ';':
478 semicolonIndex = i
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900479 case '=':
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900480 if !isRule {
481 ast = p.parseAssign(line, i, i+1)
482 }
Shinichiro Hamaji69b7f652015-03-31 01:01:59 +0900483 case '?', '+':
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900484 if !isRule && i+1 < len(line) && line[i+1] == '=' {
Shinichiro Hamaji69b7f652015-03-31 01:01:59 +0900485 ast = p.parseAssign(line, i, i+2)
486 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900487 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900488 if ast != nil {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900489 p.addStatement(ast)
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900490 break
491 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900492 }
Shinichiro Hamajide829712015-03-31 18:26:56 +0900493 if ast == nil {
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900494 ast = p.parseMaybeRule(string(line), semicolonIndex)
Shinichiro Hamajide829712015-03-31 18:26:56 +0900495 if ast != nil {
496 p.addStatement(ast)
497 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900498 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900499 }
500 return p.mk, nil
501}
502
503func ParseMakefile(filename string) (Makefile, error) {
504 f, err := os.Open(filename)
505 if err != nil {
506 return Makefile{}, err
507 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900508 defer f.Close()
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900509 parser := newParser(f, filename)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900510 return parser.parse()
511}
512
513func ParseDefaultMakefile() (Makefile, error) {
514 candidates := []string{"GNUmakefile", "makefile", "Makefile"}
515 for _, filename := range candidates {
516 if exists(filename) {
517 return ParseMakefile(filename)
518 }
519 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900520 return Makefile{}, errors.New("no targets specified and no makefile found.")
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900521}
Shinichiro Hamajic3840812015-04-02 02:10:20 +0900522
Shinichiro Hamaji29ffc972015-04-06 16:13:27 +0900523func ParseMakefileString(s string, name string, lineno int) (Makefile, error) {
Shinichiro Hamajia5dee372015-04-03 16:41:30 +0900524 rd := strings.NewReader(s)
525 parser := newParser(rd, name)
Shinichiro Hamaji370be722015-04-10 14:55:23 +0900526 parser.lineno = lineno
Shinichiro Hamaji29ffc972015-04-06 16:13:27 +0900527 parser.elineno = lineno
Shinichiro Hamaji370be722015-04-10 14:55:23 +0900528 parser.linenoFixed = true
Shinichiro Hamajia5dee372015-04-03 16:41:30 +0900529 return parser.parse()
Shinichiro Hamajic3840812015-04-02 02:10:20 +0900530}