blob: b20acab575e023d16ae9452ab22e144bf9324a44 [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 Ukaif8efa0a2015-03-30 18:10:11 +090011 "fmt"
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090012 "io"
13 "os"
Shinichiro Hamajid7bef602015-03-30 19:55:32 +090014 "strings"
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090015)
16
17type Makefile struct {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +090018 filename string
19 stmts []AST
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090020}
21
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090022type ifState struct {
Shinichiro Hamajia06760f2015-04-07 13:13:45 +090023 ast *IfAST
24 inElse bool
25 numNest int
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090026}
27
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090028type parser struct {
Shinichiro Hamaji370be722015-04-10 14:55:23 +090029 rd *bufio.Reader
30 mk Makefile
31 lineno int
32 elineno int // lineno == elineno unless there is trailing '\'.
33 linenoFixed bool
34 unBuf []byte
35 hasUnBuf bool
36 done bool
37 outStmts *[]AST
38 ifStack []ifState
39 inDef []string
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +090040 defOpt string
Shinichiro Hamaji370be722015-04-10 14:55:23 +090041 numIfNest int
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090042}
43
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +090044func newParser(rd io.Reader, filename string) *parser {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090045 p := &parser{
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +090046 rd: bufio.NewReader(rd),
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090047 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +090048 p.mk.filename = filename
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090049 p.outStmts = &p.mk.stmts
50 return p
51}
52
Shinichiro Hamajiae32b782015-03-31 14:41:19 +090053func (p *parser) addStatement(ast AST) {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090054 *p.outStmts = append(*p.outStmts, ast)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090055}
56
Shinichiro Hamajie1841582015-03-30 17:20:33 +090057func (p *parser) readLine() []byte {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090058 if p.hasUnBuf {
59 p.hasUnBuf = false
60 return p.unBuf
Shinichiro Hamajie1841582015-03-30 17:20:33 +090061 }
62
Shinichiro Hamaji370be722015-04-10 14:55:23 +090063 if !p.linenoFixed {
64 p.lineno = p.elineno
65 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +090066 line, err := p.rd.ReadBytes('\n')
Shinichiro Hamaji370be722015-04-10 14:55:23 +090067 if !p.linenoFixed {
68 p.lineno++
69 p.elineno = p.lineno
70 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +090071 if err == io.EOF {
72 p.done = true
73 } else if err != nil {
Fumitoshi Ukaia9e51362015-04-17 10:26:00 +090074 panic(fmt.Errorf("readline %s:%d: %v", p.mk.filename, p.lineno, err))
Shinichiro Hamajie1841582015-03-30 17:20:33 +090075 }
76
Shinichiro Hamaji56c868c2015-04-09 10:17:10 +090077 line = bytes.TrimRight(line, "\r\n")
Shinichiro Hamajie1841582015-03-30 17:20:33 +090078
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +090079 return line
80}
81
82func removeComment(line []byte) []byte {
83 var parenStack []byte
Shinichiro Hamajif4d3ee52015-04-22 16:19:33 +090084 // Do not use range as we may modify |line| and |i|.
85 for i := 0; i < len(line); i++ {
86 ch := line[i]
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +090087 switch ch {
88 case '(', '{':
89 parenStack = append(parenStack, ch)
90 case ')', '}':
91 if len(parenStack) > 0 {
92 parenStack = parenStack[:len(parenStack)-1]
93 }
94 case '#':
95 if len(parenStack) == 0 {
Shinichiro Hamajif4d3ee52015-04-22 16:19:33 +090096 if i == 0 || line[i-1] != '\\' {
97 return line[:i]
98 }
99 // Drop the backslash before '#'.
100 line = append(line[:i-1], line[i:]...)
101 i--
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900102 }
103 }
104 }
105 return line
106}
107
Shinichiro Hamajie2f6e902015-04-22 16:08:47 +0900108func hasTrailingBackslash(line []byte) bool {
109 if len(line) == 0 {
110 return false
111 }
112 if line[len(line)-1] != '\\' {
113 return false
114 }
115 return len(line) <= 1 || line[len(line)-2] != '\\'
116}
117
Shinichiro Hamajie52c16c2015-04-11 23:41:39 +0900118func (p *parser) processDefineLine(line []byte) []byte {
Shinichiro Hamajie2f6e902015-04-22 16:08:47 +0900119 for hasTrailingBackslash(line) {
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900120 line = line[:len(line)-1]
Shinichiro Hamajic4c98102015-04-12 03:56:00 +0900121 line = bytes.TrimRight(line, "\t ")
Shinichiro Hamaji3e4533d2015-04-06 14:03:58 +0900122 lineno := p.lineno
Shinichiro Hamajic4c98102015-04-12 03:56:00 +0900123 nline := trimLeftSpaceBytes(p.readLine())
Shinichiro Hamaji3e4533d2015-04-06 14:03:58 +0900124 p.lineno = lineno
Shinichiro Hamajic4c98102015-04-12 03:56:00 +0900125 line = append(line, ' ')
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900126 line = append(line, nline...)
127 }
Shinichiro Hamajie52c16c2015-04-11 23:41:39 +0900128 return line
129}
130
131func (p *parser) processMakefileLine(line []byte) []byte {
132 return removeComment(p.processDefineLine(line))
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900133}
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900134
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900135func (p *parser) processRecipeLine(line []byte) []byte {
Shinichiro Hamajie2f6e902015-04-22 16:08:47 +0900136 for hasTrailingBackslash(line) {
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900137 line = append(line, '\n')
138 lineno := p.lineno
139 nline := p.readLine()
140 p.lineno = lineno
141 line = append(line, nline...)
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900142 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900143 return line
144}
145
146func (p *parser) unreadLine(line []byte) {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900147 if p.hasUnBuf {
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900148 panic("unreadLine twice!")
149 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900150 p.unBuf = line
151 p.hasUnBuf = true
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900152}
153
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900154func (p *parser) parseAssign(line []byte, sep, esep int) AST {
Fumitoshi Ukai8773e5e2015-04-01 11:23:18 +0900155 Log("parseAssign %q op:%q", line, line[sep:esep])
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900156 // TODO(ukai): parse expr here.
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900157 ast := &AssignAST{
158 lhs: string(bytes.TrimSpace(line[:sep])),
Shinichiro Hamajic88618f2015-04-11 19:58:06 +0900159 rhs: trimLeftSpace(string(line[esep:])),
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900160 op: string(line[sep:esep]),
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900161 opt: p.defOpt,
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900162 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900163 ast.filename = p.mk.filename
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900164 ast.lineno = p.lineno
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900165 return ast
166}
167
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900168func (p *parser) parseMaybeRule(line string, equalIndex, semicolonIndex int) AST {
Shinichiro Hamajide829712015-03-31 18:26:56 +0900169 if len(strings.TrimSpace(line)) == 0 {
170 return nil
171 }
Shinichiro Hamajide829712015-03-31 18:26:56 +0900172
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900173 // Either '=' or ';' is used.
174 if equalIndex >= 0 && semicolonIndex >= 0 {
175 if equalIndex < semicolonIndex {
176 semicolonIndex = -1
177 } else {
178 equalIndex = -1
179 }
180 }
181
Shinichiro Hamaji486e9de2015-04-09 15:20:32 +0900182 ast := &MaybeRuleAST{
183 expr: line,
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900184 equalIndex: equalIndex,
Shinichiro Hamaji486e9de2015-04-09 15:20:32 +0900185 semicolonIndex: semicolonIndex,
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900186 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900187 ast.filename = p.mk.filename
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900188 ast.lineno = p.lineno
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900189 return ast
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900190}
191
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900192func (p *parser) parseInclude(line string, oplen int) AST {
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900193 // TODO(ukai): parse expr here
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900194 ast := &IncludeAST{
195 expr: line[oplen+1:],
196 op: line[:oplen],
197 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900198 ast.filename = p.mk.filename
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900199 ast.lineno = p.lineno
200 return ast
201}
202
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900203func (p *parser) parseIfdef(line string, oplen int) AST {
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900204 // TODO(ukai): parse expr here.
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900205 ast := &IfAST{
Shinichiro Hamajiae32b782015-03-31 14:41:19 +0900206 op: line[:oplen],
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900207 lhs: line[oplen+1:],
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900208 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900209 ast.filename = p.mk.filename
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900210 ast.lineno = p.lineno
211 p.addStatement(ast)
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900212 p.ifStack = append(p.ifStack, ifState{ast: ast, numNest: p.numIfNest})
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900213 p.outStmts = &ast.trueStmts
214 return ast
215}
216
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900217func (p *parser) parseTwoQuotes(s string, op string) ([]string, bool) {
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900218 var args []string
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900219 for i := 0; i < 2; i++ {
220 s = strings.TrimSpace(s)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900221 if s == "" {
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900222 return nil, false
223 }
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900224 quote := s[0]
225 if quote != '\'' && quote != '"' {
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900226 return nil, false
227 }
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900228 end := strings.IndexByte(s[1:], quote) + 1
229 if end < 0 {
230 return nil, false
231 }
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900232 args = append(args, s[1:end])
233 s = s[end+1:]
234 }
235 if len(s) > 0 {
236 Error(p.mk.filename, p.lineno, `extraneous text after %q directive`, op)
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900237 }
238 return args, true
239}
240
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900241// parse
242// "(lhs, rhs)"
243// "lhs, rhs"
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900244func (p *parser) parseEq(s string, op string) (string, string, bool) {
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900245 if s[0] == '(' && s[len(s)-1] == ')' {
246 s = s[1 : len(s)-1]
247 term := []byte{','}
248 in := []byte(s)
249 v, n, err := parseExpr(in, term)
250 if err != nil {
251 return "", "", false
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900252 }
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900253 lhs := v.String()
254 n++
255 n += skipSpaces(in[n:], nil)
256 v, n, err = parseExpr(in[n:], nil)
257 if err != nil {
258 return "", "", false
259 }
260 rhs := v.String()
261 return lhs, rhs, true
262 }
263 args, ok := p.parseTwoQuotes(s, op)
264 if !ok {
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900265 return "", "", false
266 }
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900267 return args[0], args[1], true
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900268}
269
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900270func (p *parser) parseIfeq(line string, oplen int) AST {
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900271 op := line[:oplen]
272 lhs, rhs, ok := p.parseEq(strings.TrimSpace(line[oplen+1:]), op)
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900273 if !ok {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900274 Error(p.mk.filename, p.lineno, `*** invalid syntax in conditional.`)
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900275 }
276
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900277 ast := &IfAST{
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900278 op: op,
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900279 lhs: lhs,
280 rhs: rhs,
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900281 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900282 ast.filename = p.mk.filename
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900283 ast.lineno = p.lineno
284 p.addStatement(ast)
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900285 p.ifStack = append(p.ifStack, ifState{ast: ast, numNest: p.numIfNest})
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900286 p.outStmts = &ast.trueStmts
287 return ast
288}
289
290func (p *parser) checkIfStack(curKeyword string) {
291 if len(p.ifStack) == 0 {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900292 Error(p.mk.filename, p.lineno, `*** extraneous %q.`, curKeyword)
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900293 }
294}
295
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900296func (p *parser) parseElse(line []byte) {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900297 p.checkIfStack("else")
298 state := &p.ifStack[len(p.ifStack)-1]
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +0900299 if state.inElse {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900300 Error(p.mk.filename, p.lineno, `*** only one "else" per conditional.`)
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900301 }
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +0900302 state.inElse = true
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900303 p.outStmts = &state.ast.falseStmts
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900304
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900305 nextIf := trimLeftSpaceBytes(line[len("else"):])
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900306 if len(nextIf) == 0 {
307 return
308 }
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900309 var ifDirectives = map[string]directiveFunc{
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900310 "ifdef ": ifdefDirective,
311 "ifndef ": ifndefDirective,
312 "ifeq ": ifeqDirective,
313 "ifneq ": ifneqDirective,
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900314 }
315 p.numIfNest = state.numNest + 1
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900316 if f, ok := p.isDirective(nextIf, ifDirectives); ok {
317 f(p, nextIf)
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900318 p.numIfNest = 0
319 return
320 }
321 p.numIfNest = 0
322 WarnNoPrefix(p.mk.filename, p.lineno, "extraneous text after `else` directive")
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900323}
324
325func (p *parser) parseEndif(line string) {
326 p.checkIfStack("endif")
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900327 state := p.ifStack[len(p.ifStack)-1]
328 for t := 0; t <= state.numNest; t++ {
329 p.ifStack = p.ifStack[0 : len(p.ifStack)-1]
330 if len(p.ifStack) == 0 {
331 p.outStmts = &p.mk.stmts
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900332 } else {
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900333 state := p.ifStack[len(p.ifStack)-1]
334 if state.inElse {
335 p.outStmts = &state.ast.falseStmts
336 } else {
337 p.outStmts = &state.ast.trueStmts
338 }
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900339 }
340 }
341}
342
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900343type directiveFunc func(*parser, []byte) []byte
344
345var makeDirectives = map[string]directiveFunc{
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900346 "include ": includeDirective,
347 "-include ": sincludeDirective,
348 "sinclude": sincludeDirective,
349 "ifdef ": ifdefDirective,
350 "ifndef ": ifndefDirective,
351 "ifeq ": ifeqDirective,
352 "ifneq ": ifneqDirective,
353 "else": elseDirective,
354 "endif": endifDirective,
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900355 "define ": defineDirective,
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900356 "override ": overrideDirective,
357 "export ": exportDirective,
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900358 "unexport ": unexportDirective,
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900359}
360
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900361// TODO(ukai): use []byte
362func (p *parser) isDirective(line []byte, directives map[string]directiveFunc) (directiveFunc, bool) {
363 stripped := trimLeftSpaceBytes(line)
Shinichiro Hamajie103f652015-04-11 19:49:51 +0900364 // Fast paths.
365 // TODO: Consider using a trie.
366 if len(stripped) == 0 {
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900367 return nil, false
Shinichiro Hamajie103f652015-04-11 19:49:51 +0900368 }
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900369 if ch := stripped[0]; ch != 'i' && ch != '-' && ch != 's' && ch != 'e' && ch != 'd' && ch != 'o' && ch != 'u' {
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900370 return nil, false
Shinichiro Hamajie103f652015-04-11 19:49:51 +0900371 }
372
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900373 for prefix, f := range directives {
374 if bytes.HasPrefix(stripped, []byte(prefix)) {
375 return f, true
376 }
377 if prefix[len(prefix)-1] == ' ' && bytes.HasPrefix(stripped, []byte(prefix[:len(prefix)-1])) && stripped[len(prefix)-1] == '\t' {
378 return f, true
Shinichiro Hamaji74b8cb52015-04-08 19:47:43 +0900379 }
380 }
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900381 return nil, false
Shinichiro Hamaji74b8cb52015-04-08 19:47:43 +0900382}
383
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900384func includeDirective(p *parser, line []byte) []byte {
385 p.addStatement(p.parseInclude(string(line), len("include")))
386 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900387}
388
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900389func sincludeDirective(p *parser, line []byte) []byte {
390 p.addStatement(p.parseInclude(string(line), len("-include")))
391 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900392}
393
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900394func ifdefDirective(p *parser, line []byte) []byte {
395 p.parseIfdef(string(line), len("ifdef"))
396 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900397}
398
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900399func ifndefDirective(p *parser, line []byte) []byte {
400 p.parseIfdef(string(line), len("ifndef"))
401 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900402}
403
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900404func ifeqDirective(p *parser, line []byte) []byte {
405 p.parseIfeq(string(line), len("ifeq"))
406 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900407}
408
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900409func ifneqDirective(p *parser, line []byte) []byte {
410 p.parseIfeq(string(line), len("ifneq"))
411 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900412}
413
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900414func elseDirective(p *parser, line []byte) []byte {
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900415 p.parseElse(line)
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900416 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900417}
418
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900419func endifDirective(p *parser, line []byte) []byte {
420 p.parseEndif(string(line))
421 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900422}
423
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900424func defineDirective(p *parser, line []byte) []byte {
425 lhs := trimLeftSpaceBytes(line[len("define "):])
426 p.inDef = []string{string(lhs)}
427 return nil
428}
429
430func overrideDirective(p *parser, line []byte) []byte {
431 p.defOpt = "override"
432 line = trimLeftSpaceBytes(line[len("override "):])
433 defineDirective := map[string]directiveFunc{
434 "define": defineDirective,
435 }
436 if f, ok := p.isDirective(line, defineDirective); ok {
437 f(p, line)
438 return nil
439 }
440 // e.g. overrider foo := bar
441 // line will be "foo := bar".
442 return line
443}
444
Shinichiro Hamajiea553f32015-05-29 17:03:33 +0900445func handleExport(p *parser, line []byte, export bool) (hasEqual bool) {
446 equalIndex := bytes.IndexByte(line, '=')
447 if equalIndex > 0 {
448 hasEqual = true
449 switch line[equalIndex-1] {
450 case ':', '+', '?':
451 equalIndex--
452 }
453 line = line[:equalIndex]
454 }
455
Shinichiro Hamajif61033d2015-05-29 15:01:48 +0900456 ast := &ExportAST{
457 expr: line,
458 export: export,
Shinichiro Hamaji7e521422015-05-29 14:23:30 +0900459 }
Shinichiro Hamajif61033d2015-05-29 15:01:48 +0900460 ast.filename = p.mk.filename
461 ast.lineno = p.lineno
462 p.addStatement(ast)
Shinichiro Hamajiea553f32015-05-29 17:03:33 +0900463 return hasEqual
Shinichiro Hamaji7e521422015-05-29 14:23:30 +0900464}
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900465
Shinichiro Hamaji7e521422015-05-29 14:23:30 +0900466func exportDirective(p *parser, line []byte) []byte {
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900467 p.defOpt = "export"
468 line = trimLeftSpaceBytes(line[len("export "):])
469 defineDirective := map[string]directiveFunc{
470 "define": defineDirective,
471 }
472 if f, ok := p.isDirective(line, defineDirective); ok {
473 f(p, line)
474 return nil
475 }
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900476
Shinichiro Hamajiea553f32015-05-29 17:03:33 +0900477 if !handleExport(p, line, true) {
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900478 return nil
479 }
480
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900481 // e.g. export foo := bar
482 // line will be "foo := bar".
483 return line
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900484}
485
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900486func unexportDirective(p *parser, line []byte) []byte {
Shinichiro Hamaji7e521422015-05-29 14:23:30 +0900487 handleExport(p, line[len("unexport "):], false)
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900488 return nil
489}
490
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900491func (p *parser) parse() (mk Makefile, err error) {
492 defer func() {
493 if r := recover(); r != nil {
Fumitoshi Ukaia9e51362015-04-17 10:26:00 +0900494 err = fmt.Errorf("panic in parse %s: %v", mk.filename, r)
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900495 }
496 }()
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900497 for !p.done {
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900498 line := p.readLine()
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900499
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900500 if len(p.inDef) > 0 {
Shinichiro Hamajie52c16c2015-04-11 23:41:39 +0900501 line = p.processDefineLine(line)
Shinichiro Hamajic88618f2015-04-11 19:58:06 +0900502 if trimLeftSpace(string(line)) == "endef" {
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900503 Log("multilineAssign %q", p.inDef)
504 ast := &AssignAST{
505 lhs: p.inDef[0],
506 rhs: strings.Join(p.inDef[1:], "\n"),
507 op: "=",
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900508 opt: p.defOpt,
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900509 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900510 ast.filename = p.mk.filename
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900511 ast.lineno = p.lineno - len(p.inDef)
512 p.addStatement(ast)
513 p.inDef = nil
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900514 p.defOpt = ""
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900515 continue
516 }
517 p.inDef = append(p.inDef, string(line))
518 continue
519 }
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900520 p.defOpt = ""
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900521
Shinichiro Hamaji960161f2015-04-13 17:10:10 +0900522 if len(bytes.TrimSpace(line)) == 0 {
523 continue
524 }
525
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900526 if f, ok := p.isDirective(line, makeDirectives); ok {
527 line = p.processMakefileLine(trimLeftSpaceBytes(line))
528 line = f(p, line)
529 if len(line) == 0 {
530 continue
531 }
Shinichiro Hamaji74b8cb52015-04-08 19:47:43 +0900532 }
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900533 if line[0] == '\t' {
534 ast := &CommandAST{cmd: string(p.processRecipeLine(line[1:]))}
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900535 ast.filename = p.mk.filename
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900536 ast.lineno = p.lineno
537 p.addStatement(ast)
538 continue
539 }
540
541 line = p.processMakefileLine(line)
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900542
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900543 var ast AST
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900544 var parenStack []byte
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900545 equalIndex := -1
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900546 semicolonIndex := -1
547 isRule := false
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900548 for i, ch := range line {
549 switch ch {
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900550 case '(', '{':
551 parenStack = append(parenStack, ch)
552 case ')', '}':
553 if len(parenStack) == 0 {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900554 Warn(p.mk.filename, p.lineno, "Unmatched parens: %s", line)
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900555 } else {
556 parenStack = parenStack[:len(parenStack)-1]
557 }
558 }
559 if len(parenStack) > 0 {
560 continue
561 }
562
563 switch ch {
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900564 case ':':
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900565 if i+1 < len(line) && line[i+1] == '=' {
Shinichiro Hamaji156ef3e2015-04-11 22:44:41 +0900566 if !isRule {
567 ast = p.parseAssign(line, i, i+2)
568 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900569 } else {
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900570 isRule = true
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900571 }
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900572 case ';':
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900573 if semicolonIndex < 0 {
574 semicolonIndex = i
575 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900576 case '=':
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900577 if !isRule {
578 ast = p.parseAssign(line, i, i+1)
579 }
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900580 if equalIndex < 0 {
581 equalIndex = i
582 }
Shinichiro Hamaji69b7f652015-03-31 01:01:59 +0900583 case '?', '+':
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900584 if !isRule && i+1 < len(line) && line[i+1] == '=' {
Shinichiro Hamaji69b7f652015-03-31 01:01:59 +0900585 ast = p.parseAssign(line, i, i+2)
586 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900587 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900588 if ast != nil {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900589 p.addStatement(ast)
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900590 break
591 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900592 }
Shinichiro Hamajide829712015-03-31 18:26:56 +0900593 if ast == nil {
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900594 ast = p.parseMaybeRule(string(line), equalIndex, semicolonIndex)
Shinichiro Hamajide829712015-03-31 18:26:56 +0900595 if ast != nil {
596 p.addStatement(ast)
597 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900598 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900599 }
600 return p.mk, nil
601}
602
Shinichiro Hamaji290eb252015-05-19 18:03:19 +0900603func ParseMakefileFd(filename string, f *os.File) (Makefile, error) {
604 parser := newParser(f, filename)
605 return parser.parse()
606}
607
Shinichiro Hamaji45cde1d2015-05-25 18:21:23 +0900608/*
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900609func ParseMakefile(filename string) (Makefile, error) {
Fumitoshi Ukaia9e51362015-04-17 10:26:00 +0900610 Log("ParseMakefile %q", filename)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900611 f, err := os.Open(filename)
612 if err != nil {
613 return Makefile{}, err
614 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900615 defer f.Close()
Shinichiro Hamaji290eb252015-05-19 18:03:19 +0900616 return ParseMakefileFd(filename, f)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900617}
618
Shinichiro Hamaji750ae2e2015-05-20 12:59:15 +0900619func ParseDefaultMakefile() (Makefile, string, error) {
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900620 candidates := []string{"GNUmakefile", "makefile", "Makefile"}
621 for _, filename := range candidates {
622 if exists(filename) {
Shinichiro Hamaji750ae2e2015-05-20 12:59:15 +0900623 mk, err := ParseMakefile(filename)
624 return mk, filename, err
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900625 }
626 }
Shinichiro Hamaji750ae2e2015-05-20 12:59:15 +0900627 return Makefile{}, "", errors.New("no targets specified and no makefile found.")
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900628}
Shinichiro Hamaji45cde1d2015-05-25 18:21:23 +0900629*/
Shinichiro Hamajic3840812015-04-02 02:10:20 +0900630
Shinichiro Hamajib0d2e2f2015-05-20 16:42:59 +0900631func GetDefaultMakefile() string {
632 candidates := []string{"GNUmakefile", "makefile", "Makefile"}
633 for _, filename := range candidates {
634 if exists(filename) {
635 return filename
636 }
637 }
638 ErrorNoLocation("no targets specified and no makefile found.")
639 panic("") // Cannot be reached.
640}
641
Shinichiro Hamaji28ea5bc2015-04-11 12:41:08 +0900642func parseMakefileReader(rd io.Reader, name string, lineno int) (Makefile, error) {
Shinichiro Hamajia5dee372015-04-03 16:41:30 +0900643 parser := newParser(rd, name)
Shinichiro Hamaji370be722015-04-10 14:55:23 +0900644 parser.lineno = lineno
Shinichiro Hamaji29ffc972015-04-06 16:13:27 +0900645 parser.elineno = lineno
Shinichiro Hamaji370be722015-04-10 14:55:23 +0900646 parser.linenoFixed = true
Shinichiro Hamajia5dee372015-04-03 16:41:30 +0900647 return parser.parse()
Shinichiro Hamajic3840812015-04-02 02:10:20 +0900648}
Shinichiro Hamaji28ea5bc2015-04-11 12:41:08 +0900649
650func ParseMakefileString(s string, name string, lineno int) (Makefile, error) {
651 return parseMakefileReader(strings.NewReader(s), name, lineno)
652}
653
654func ParseMakefileBytes(s []byte, name string, lineno int) (Makefile, error) {
655 return parseMakefileReader(bytes.NewReader(s), name, lineno)
656}
Shinichiro Hamaji45cde1d2015-05-25 18:21:23 +0900657
658func ParseMakefile(s []byte, filename string) (Makefile, error) {
659 Log("ParseMakefile %q", filename)
660 parser := newParser(bytes.NewReader(s), filename)
661 return parser.parse()
662}