blob: 73fe77d045f03ecfba8f7e0e7592354adfb2ac2d [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
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +090041 defOpt string
Shinichiro Hamaji370be722015-04-10 14:55:23 +090042 numIfNest int
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090043}
44
45func exists(filename string) bool {
46 f, err := os.Open(filename)
47 if err != nil {
48 return false
49 }
50 f.Close()
51 return true
52}
53
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +090054func newParser(rd io.Reader, filename string) *parser {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090055 p := &parser{
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +090056 rd: bufio.NewReader(rd),
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090057 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +090058 p.mk.filename = filename
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090059 p.outStmts = &p.mk.stmts
60 return p
61}
62
Shinichiro Hamajiae32b782015-03-31 14:41:19 +090063func (p *parser) addStatement(ast AST) {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090064 *p.outStmts = append(*p.outStmts, ast)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090065}
66
Shinichiro Hamajie1841582015-03-30 17:20:33 +090067func (p *parser) readLine() []byte {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090068 if p.hasUnBuf {
69 p.hasUnBuf = false
70 return p.unBuf
Shinichiro Hamajie1841582015-03-30 17:20:33 +090071 }
72
Shinichiro Hamaji370be722015-04-10 14:55:23 +090073 if !p.linenoFixed {
74 p.lineno = p.elineno
75 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +090076 line, err := p.rd.ReadBytes('\n')
Shinichiro Hamaji370be722015-04-10 14:55:23 +090077 if !p.linenoFixed {
78 p.lineno++
79 p.elineno = p.lineno
80 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +090081 if err == io.EOF {
82 p.done = true
83 } else if err != nil {
Fumitoshi Ukaia9e51362015-04-17 10:26:00 +090084 panic(fmt.Errorf("readline %s:%d: %v", p.mk.filename, p.lineno, err))
Shinichiro Hamajie1841582015-03-30 17:20:33 +090085 }
86
Shinichiro Hamaji56c868c2015-04-09 10:17:10 +090087 line = bytes.TrimRight(line, "\r\n")
Shinichiro Hamajie1841582015-03-30 17:20:33 +090088
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +090089 return line
90}
91
92func removeComment(line []byte) []byte {
93 var parenStack []byte
Shinichiro Hamajif4d3ee52015-04-22 16:19:33 +090094 // Do not use range as we may modify |line| and |i|.
95 for i := 0; i < len(line); i++ {
96 ch := line[i]
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +090097 switch ch {
98 case '(', '{':
99 parenStack = append(parenStack, ch)
100 case ')', '}':
101 if len(parenStack) > 0 {
102 parenStack = parenStack[:len(parenStack)-1]
103 }
104 case '#':
105 if len(parenStack) == 0 {
Shinichiro Hamajif4d3ee52015-04-22 16:19:33 +0900106 if i == 0 || line[i-1] != '\\' {
107 return line[:i]
108 }
109 // Drop the backslash before '#'.
110 line = append(line[:i-1], line[i:]...)
111 i--
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900112 }
113 }
114 }
115 return line
116}
117
Shinichiro Hamajie2f6e902015-04-22 16:08:47 +0900118func hasTrailingBackslash(line []byte) bool {
119 if len(line) == 0 {
120 return false
121 }
122 if line[len(line)-1] != '\\' {
123 return false
124 }
125 return len(line) <= 1 || line[len(line)-2] != '\\'
126}
127
Shinichiro Hamajie52c16c2015-04-11 23:41:39 +0900128func (p *parser) processDefineLine(line []byte) []byte {
Shinichiro Hamajie2f6e902015-04-22 16:08:47 +0900129 for hasTrailingBackslash(line) {
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900130 line = line[:len(line)-1]
Shinichiro Hamajic4c98102015-04-12 03:56:00 +0900131 line = bytes.TrimRight(line, "\t ")
Shinichiro Hamaji3e4533d2015-04-06 14:03:58 +0900132 lineno := p.lineno
Shinichiro Hamajic4c98102015-04-12 03:56:00 +0900133 nline := trimLeftSpaceBytes(p.readLine())
Shinichiro Hamaji3e4533d2015-04-06 14:03:58 +0900134 p.lineno = lineno
Shinichiro Hamajic4c98102015-04-12 03:56:00 +0900135 line = append(line, ' ')
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900136 line = append(line, nline...)
137 }
Shinichiro Hamajie52c16c2015-04-11 23:41:39 +0900138 return line
139}
140
141func (p *parser) processMakefileLine(line []byte) []byte {
142 return removeComment(p.processDefineLine(line))
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900143}
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900144
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900145func (p *parser) processRecipeLine(line []byte) []byte {
Shinichiro Hamajie2f6e902015-04-22 16:08:47 +0900146 for hasTrailingBackslash(line) {
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900147 line = append(line, '\n')
148 lineno := p.lineno
149 nline := p.readLine()
150 p.lineno = lineno
151 line = append(line, nline...)
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900152 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900153 return line
154}
155
156func (p *parser) unreadLine(line []byte) {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900157 if p.hasUnBuf {
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900158 panic("unreadLine twice!")
159 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900160 p.unBuf = line
161 p.hasUnBuf = true
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900162}
163
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900164func (p *parser) parseAssign(line []byte, sep, esep int) AST {
Fumitoshi Ukai8773e5e2015-04-01 11:23:18 +0900165 Log("parseAssign %q op:%q", line, line[sep:esep])
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900166 // TODO(ukai): parse expr here.
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900167 ast := &AssignAST{
168 lhs: string(bytes.TrimSpace(line[:sep])),
Shinichiro Hamajic88618f2015-04-11 19:58:06 +0900169 rhs: trimLeftSpace(string(line[esep:])),
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900170 op: string(line[sep:esep]),
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900171 opt: p.defOpt,
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900172 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900173 ast.filename = p.mk.filename
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900174 ast.lineno = p.lineno
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900175 return ast
176}
177
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900178func (p *parser) parseMaybeRule(line string, equalIndex, semicolonIndex int) AST {
Shinichiro Hamajide829712015-03-31 18:26:56 +0900179 if len(strings.TrimSpace(line)) == 0 {
180 return nil
181 }
Shinichiro Hamajide829712015-03-31 18:26:56 +0900182
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900183 // Either '=' or ';' is used.
184 if equalIndex >= 0 && semicolonIndex >= 0 {
185 if equalIndex < semicolonIndex {
186 semicolonIndex = -1
187 } else {
188 equalIndex = -1
189 }
190 }
191
Shinichiro Hamaji486e9de2015-04-09 15:20:32 +0900192 ast := &MaybeRuleAST{
193 expr: line,
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900194 equalIndex: equalIndex,
Shinichiro Hamaji486e9de2015-04-09 15:20:32 +0900195 semicolonIndex: semicolonIndex,
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900196 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900197 ast.filename = p.mk.filename
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900198 ast.lineno = p.lineno
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900199 return ast
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900200}
201
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900202func (p *parser) parseInclude(line string, oplen int) AST {
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900203 // TODO(ukai): parse expr here
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900204 ast := &IncludeAST{
205 expr: line[oplen+1:],
206 op: line[:oplen],
207 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900208 ast.filename = p.mk.filename
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900209 ast.lineno = p.lineno
210 return ast
211}
212
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900213func (p *parser) parseIfdef(line string, oplen int) AST {
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900214 // TODO(ukai): parse expr here.
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900215 ast := &IfAST{
Shinichiro Hamajiae32b782015-03-31 14:41:19 +0900216 op: line[:oplen],
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900217 lhs: line[oplen+1:],
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900218 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900219 ast.filename = p.mk.filename
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900220 ast.lineno = p.lineno
221 p.addStatement(ast)
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900222 p.ifStack = append(p.ifStack, ifState{ast: ast, numNest: p.numIfNest})
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900223 p.outStmts = &ast.trueStmts
224 return ast
225}
226
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900227func (p *parser) parseTwoQuotes(s string, op string) ([]string, bool) {
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900228 var args []string
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900229 for i := 0; i < 2; i++ {
230 s = strings.TrimSpace(s)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900231 if s == "" {
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900232 return nil, false
233 }
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900234 quote := s[0]
235 if quote != '\'' && quote != '"' {
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900236 return nil, false
237 }
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900238 end := strings.IndexByte(s[1:], quote) + 1
239 if end < 0 {
240 return nil, false
241 }
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900242 args = append(args, s[1:end])
243 s = s[end+1:]
244 }
245 if len(s) > 0 {
246 Error(p.mk.filename, p.lineno, `extraneous text after %q directive`, op)
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900247 }
248 return args, true
249}
250
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900251// parse
252// "(lhs, rhs)"
253// "lhs, rhs"
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900254func (p *parser) parseEq(s string, op string) (string, string, bool) {
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900255 if s[0] == '(' && s[len(s)-1] == ')' {
256 s = s[1 : len(s)-1]
257 term := []byte{','}
258 in := []byte(s)
259 v, n, err := parseExpr(in, term)
260 if err != nil {
261 return "", "", false
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900262 }
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900263 lhs := v.String()
264 n++
265 n += skipSpaces(in[n:], nil)
266 v, n, err = parseExpr(in[n:], nil)
267 if err != nil {
268 return "", "", false
269 }
270 rhs := v.String()
271 return lhs, rhs, true
272 }
273 args, ok := p.parseTwoQuotes(s, op)
274 if !ok {
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900275 return "", "", false
276 }
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900277 return args[0], args[1], true
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900278}
279
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900280func (p *parser) parseIfeq(line string, oplen int) AST {
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900281 op := line[:oplen]
282 lhs, rhs, ok := p.parseEq(strings.TrimSpace(line[oplen+1:]), op)
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900283 if !ok {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900284 Error(p.mk.filename, p.lineno, `*** invalid syntax in conditional.`)
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900285 }
286
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900287 ast := &IfAST{
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900288 op: op,
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900289 lhs: lhs,
290 rhs: rhs,
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900291 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900292 ast.filename = p.mk.filename
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900293 ast.lineno = p.lineno
294 p.addStatement(ast)
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900295 p.ifStack = append(p.ifStack, ifState{ast: ast, numNest: p.numIfNest})
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900296 p.outStmts = &ast.trueStmts
297 return ast
298}
299
300func (p *parser) checkIfStack(curKeyword string) {
301 if len(p.ifStack) == 0 {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900302 Error(p.mk.filename, p.lineno, `*** extraneous %q.`, curKeyword)
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900303 }
304}
305
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900306func (p *parser) parseElse(line []byte) {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900307 p.checkIfStack("else")
308 state := &p.ifStack[len(p.ifStack)-1]
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +0900309 if state.inElse {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900310 Error(p.mk.filename, p.lineno, `*** only one "else" per conditional.`)
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900311 }
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +0900312 state.inElse = true
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900313 p.outStmts = &state.ast.falseStmts
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900314
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900315 nextIf := trimLeftSpaceBytes(line[len("else"):])
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900316 if len(nextIf) == 0 {
317 return
318 }
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900319 var ifDirectives = map[string]directiveFunc{
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900320 "ifdef ": ifdefDirective,
321 "ifndef ": ifndefDirective,
322 "ifeq ": ifeqDirective,
323 "ifneq ": ifneqDirective,
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900324 }
325 p.numIfNest = state.numNest + 1
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900326 if f, ok := p.isDirective(nextIf, ifDirectives); ok {
327 f(p, nextIf)
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900328 p.numIfNest = 0
329 return
330 }
331 p.numIfNest = 0
332 WarnNoPrefix(p.mk.filename, p.lineno, "extraneous text after `else` directive")
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900333}
334
335func (p *parser) parseEndif(line string) {
336 p.checkIfStack("endif")
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900337 state := p.ifStack[len(p.ifStack)-1]
338 for t := 0; t <= state.numNest; t++ {
339 p.ifStack = p.ifStack[0 : len(p.ifStack)-1]
340 if len(p.ifStack) == 0 {
341 p.outStmts = &p.mk.stmts
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900342 } else {
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900343 state := p.ifStack[len(p.ifStack)-1]
344 if state.inElse {
345 p.outStmts = &state.ast.falseStmts
346 } else {
347 p.outStmts = &state.ast.trueStmts
348 }
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900349 }
350 }
351}
352
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900353type directiveFunc func(*parser, []byte) []byte
354
355var makeDirectives = map[string]directiveFunc{
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900356 "include ": includeDirective,
357 "-include ": sincludeDirective,
358 "sinclude": sincludeDirective,
359 "ifdef ": ifdefDirective,
360 "ifndef ": ifndefDirective,
361 "ifeq ": ifeqDirective,
362 "ifneq ": ifneqDirective,
363 "else": elseDirective,
364 "endif": endifDirective,
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900365 "define ": defineDirective,
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900366 "override ": overrideDirective,
367 "export ": exportDirective,
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900368}
369
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900370// TODO(ukai): use []byte
371func (p *parser) isDirective(line []byte, directives map[string]directiveFunc) (directiveFunc, bool) {
372 stripped := trimLeftSpaceBytes(line)
Shinichiro Hamajie103f652015-04-11 19:49:51 +0900373 // Fast paths.
374 // TODO: Consider using a trie.
375 if len(stripped) == 0 {
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900376 return nil, false
Shinichiro Hamajie103f652015-04-11 19:49:51 +0900377 }
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900378 if ch := stripped[0]; ch != 'i' && ch != '-' && ch != 's' && ch != 'e' && ch != 'd' && ch != 'o' {
379 return nil, false
Shinichiro Hamajie103f652015-04-11 19:49:51 +0900380 }
381
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900382 for prefix, f := range directives {
383 if bytes.HasPrefix(stripped, []byte(prefix)) {
384 return f, true
385 }
386 if prefix[len(prefix)-1] == ' ' && bytes.HasPrefix(stripped, []byte(prefix[:len(prefix)-1])) && stripped[len(prefix)-1] == '\t' {
387 return f, true
Shinichiro Hamaji74b8cb52015-04-08 19:47:43 +0900388 }
389 }
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900390 return nil, false
Shinichiro Hamaji74b8cb52015-04-08 19:47:43 +0900391}
392
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900393func includeDirective(p *parser, line []byte) []byte {
394 p.addStatement(p.parseInclude(string(line), len("include")))
395 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900396}
397
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900398func sincludeDirective(p *parser, line []byte) []byte {
399 p.addStatement(p.parseInclude(string(line), len("-include")))
400 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900401}
402
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900403func ifdefDirective(p *parser, line []byte) []byte {
404 p.parseIfdef(string(line), len("ifdef"))
405 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900406}
407
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900408func ifndefDirective(p *parser, line []byte) []byte {
409 p.parseIfdef(string(line), len("ifndef"))
410 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900411}
412
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900413func ifeqDirective(p *parser, line []byte) []byte {
414 p.parseIfeq(string(line), len("ifeq"))
415 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900416}
417
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900418func ifneqDirective(p *parser, line []byte) []byte {
419 p.parseIfeq(string(line), len("ifneq"))
420 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900421}
422
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900423func elseDirective(p *parser, line []byte) []byte {
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900424 p.parseElse(line)
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900425 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900426}
427
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900428func endifDirective(p *parser, line []byte) []byte {
429 p.parseEndif(string(line))
430 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900431}
432
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900433func defineDirective(p *parser, line []byte) []byte {
434 lhs := trimLeftSpaceBytes(line[len("define "):])
435 p.inDef = []string{string(lhs)}
436 return nil
437}
438
439func overrideDirective(p *parser, line []byte) []byte {
440 p.defOpt = "override"
441 line = trimLeftSpaceBytes(line[len("override "):])
442 defineDirective := map[string]directiveFunc{
443 "define": defineDirective,
444 }
445 if f, ok := p.isDirective(line, defineDirective); ok {
446 f(p, line)
447 return nil
448 }
449 // e.g. overrider foo := bar
450 // line will be "foo := bar".
451 return line
452}
453
454func exportDirective(p *parser, line []byte) []byte {
455 p.defOpt = "export"
456 line = trimLeftSpaceBytes(line[len("export "):])
457 defineDirective := map[string]directiveFunc{
458 "define": defineDirective,
459 }
460 if f, ok := p.isDirective(line, defineDirective); ok {
461 f(p, line)
462 return nil
463 }
464 // e.g. export foo := bar
465 // line will be "foo := bar".
466 return line
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900467}
468
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900469func (p *parser) parse() (mk Makefile, err error) {
470 defer func() {
471 if r := recover(); r != nil {
Fumitoshi Ukaia9e51362015-04-17 10:26:00 +0900472 err = fmt.Errorf("panic in parse %s: %v", mk.filename, r)
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900473 }
474 }()
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900475 for !p.done {
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900476 line := p.readLine()
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900477
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900478 if len(p.inDef) > 0 {
Shinichiro Hamajie52c16c2015-04-11 23:41:39 +0900479 line = p.processDefineLine(line)
Shinichiro Hamajic88618f2015-04-11 19:58:06 +0900480 if trimLeftSpace(string(line)) == "endef" {
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900481 Log("multilineAssign %q", p.inDef)
482 ast := &AssignAST{
483 lhs: p.inDef[0],
484 rhs: strings.Join(p.inDef[1:], "\n"),
485 op: "=",
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900486 opt: p.defOpt,
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900487 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900488 ast.filename = p.mk.filename
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900489 ast.lineno = p.lineno - len(p.inDef)
490 p.addStatement(ast)
491 p.inDef = nil
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900492 p.defOpt = ""
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900493 continue
494 }
495 p.inDef = append(p.inDef, string(line))
496 continue
497 }
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900498 p.defOpt = ""
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900499
Shinichiro Hamaji960161f2015-04-13 17:10:10 +0900500 if len(bytes.TrimSpace(line)) == 0 {
501 continue
502 }
503
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900504 if f, ok := p.isDirective(line, makeDirectives); ok {
505 line = p.processMakefileLine(trimLeftSpaceBytes(line))
506 line = f(p, line)
507 if len(line) == 0 {
508 continue
509 }
Shinichiro Hamaji74b8cb52015-04-08 19:47:43 +0900510 }
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900511 if line[0] == '\t' {
512 ast := &CommandAST{cmd: string(p.processRecipeLine(line[1:]))}
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900513 ast.filename = p.mk.filename
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900514 ast.lineno = p.lineno
515 p.addStatement(ast)
516 continue
517 }
518
519 line = p.processMakefileLine(line)
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900520
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900521 var ast AST
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900522 var parenStack []byte
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900523 equalIndex := -1
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900524 semicolonIndex := -1
525 isRule := false
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900526 for i, ch := range line {
527 switch ch {
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900528 case '(', '{':
529 parenStack = append(parenStack, ch)
530 case ')', '}':
531 if len(parenStack) == 0 {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900532 Warn(p.mk.filename, p.lineno, "Unmatched parens: %s", line)
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900533 } else {
534 parenStack = parenStack[:len(parenStack)-1]
535 }
536 }
537 if len(parenStack) > 0 {
538 continue
539 }
540
541 switch ch {
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900542 case ':':
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900543 if i+1 < len(line) && line[i+1] == '=' {
Shinichiro Hamaji156ef3e2015-04-11 22:44:41 +0900544 if !isRule {
545 ast = p.parseAssign(line, i, i+2)
546 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900547 } else {
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900548 isRule = true
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900549 }
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900550 case ';':
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900551 if semicolonIndex < 0 {
552 semicolonIndex = i
553 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900554 case '=':
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900555 if !isRule {
556 ast = p.parseAssign(line, i, i+1)
557 }
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900558 if equalIndex < 0 {
559 equalIndex = i
560 }
Shinichiro Hamaji69b7f652015-03-31 01:01:59 +0900561 case '?', '+':
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900562 if !isRule && i+1 < len(line) && line[i+1] == '=' {
Shinichiro Hamaji69b7f652015-03-31 01:01:59 +0900563 ast = p.parseAssign(line, i, i+2)
564 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900565 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900566 if ast != nil {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900567 p.addStatement(ast)
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900568 break
569 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900570 }
Shinichiro Hamajide829712015-03-31 18:26:56 +0900571 if ast == nil {
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900572 ast = p.parseMaybeRule(string(line), equalIndex, semicolonIndex)
Shinichiro Hamajide829712015-03-31 18:26:56 +0900573 if ast != nil {
574 p.addStatement(ast)
575 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900576 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900577 }
578 return p.mk, nil
579}
580
581func ParseMakefile(filename string) (Makefile, error) {
Fumitoshi Ukaia9e51362015-04-17 10:26:00 +0900582 Log("ParseMakefile %q", filename)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900583 f, err := os.Open(filename)
584 if err != nil {
585 return Makefile{}, err
586 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900587 defer f.Close()
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900588 parser := newParser(f, filename)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900589 return parser.parse()
590}
591
592func ParseDefaultMakefile() (Makefile, error) {
593 candidates := []string{"GNUmakefile", "makefile", "Makefile"}
594 for _, filename := range candidates {
595 if exists(filename) {
596 return ParseMakefile(filename)
597 }
598 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900599 return Makefile{}, errors.New("no targets specified and no makefile found.")
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900600}
Shinichiro Hamajic3840812015-04-02 02:10:20 +0900601
Shinichiro Hamaji28ea5bc2015-04-11 12:41:08 +0900602func parseMakefileReader(rd io.Reader, name string, lineno int) (Makefile, error) {
Shinichiro Hamajia5dee372015-04-03 16:41:30 +0900603 parser := newParser(rd, name)
Shinichiro Hamaji370be722015-04-10 14:55:23 +0900604 parser.lineno = lineno
Shinichiro Hamaji29ffc972015-04-06 16:13:27 +0900605 parser.elineno = lineno
Shinichiro Hamaji370be722015-04-10 14:55:23 +0900606 parser.linenoFixed = true
Shinichiro Hamajia5dee372015-04-03 16:41:30 +0900607 return parser.parse()
Shinichiro Hamajic3840812015-04-02 02:10:20 +0900608}
Shinichiro Hamaji28ea5bc2015-04-11 12:41:08 +0900609
610func ParseMakefileString(s string, name string, lineno int) (Makefile, error) {
611 return parseMakefileReader(strings.NewReader(s), name, lineno)
612}
613
614func ParseMakefileBytes(s []byte, name string, lineno int) (Makefile, error) {
615 return parseMakefileReader(bytes.NewReader(s), name, lineno)
616}