blob: 2f7af91c17c8ac81ccdff75b32a908cea1892848 [file] [log] [blame]
Fumitoshi Ukai119dc912015-03-30 16:52:41 +09001package main
2
3import (
4 "bufio"
Shinichiro Hamajie1841582015-03-30 17:20:33 +09005 "bytes"
Fumitoshi Ukai119dc912015-03-30 16:52:41 +09006 "errors"
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +09007 "fmt"
Fumitoshi Ukai119dc912015-03-30 16:52:41 +09008 "io"
9 "os"
Shinichiro Hamajid7bef602015-03-30 19:55:32 +090010 "strings"
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090011)
12
13type Makefile struct {
14 stmts []AST
15}
16
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090017type ifState struct {
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +090018 ast *IfAST
19 inElse bool
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090020}
21
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090022type parser struct {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090023 rd *bufio.Reader
24 mk Makefile
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +090025 filename string
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090026 lineno int
27 elineno int // lineno == elineno unless there is trailing '\'.
28 unBuf []byte
29 hasUnBuf bool
30 done bool
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090031 outStmts *[]AST
32 ifStack []ifState
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090033}
34
35func exists(filename string) bool {
36 f, err := os.Open(filename)
37 if err != nil {
38 return false
39 }
40 f.Close()
41 return true
42}
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 Hamaji685fecf2015-03-30 18:28:12 +090046 rd: bufio.NewReader(rd),
47 filename: filename,
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090048 }
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
63 p.lineno = p.elineno
64 line, err := p.rd.ReadBytes('\n')
65 p.lineno++
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +090066 p.elineno = p.lineno
Shinichiro Hamajie1841582015-03-30 17:20:33 +090067 if err == io.EOF {
68 p.done = true
69 } else if err != nil {
70 panic(err)
71 }
72
73 if len(line) > 0 {
74 line = line[0 : len(line)-1]
75 }
76
77 // TODO: Handle \\ at the end of the line?
78 for len(line) > 0 && line[len(line)-1] == '\\' {
79 line = line[:len(line)-1]
80 nline := p.readLine()
81 p.elineno++
82 line = append(line, nline...)
83 }
84
85 index := bytes.IndexByte(line, '#')
86 if index >= 0 {
87 line = line[:index]
88 }
89
90 return line
91}
92
93func (p *parser) unreadLine(line []byte) {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090094 if p.hasUnBuf {
Shinichiro Hamajie1841582015-03-30 17:20:33 +090095 panic("unreadLine twice!")
96 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090097 p.unBuf = line
98 p.hasUnBuf = true
Shinichiro Hamajie1841582015-03-30 17:20:33 +090099}
100
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900101func (p *parser) parseAssign(line []byte, sep, esep int) AST {
102 Log("parseAssign %s %s", line, line[sep:esep])
103 ast := &AssignAST{
104 lhs: string(bytes.TrimSpace(line[:sep])),
105 rhs: string(bytes.TrimLeft(line[esep:], " \t")),
106 op: string(line[sep:esep]),
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900107 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900108 ast.filename = p.filename
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900109 ast.lineno = p.lineno
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900110 return ast
111}
112
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900113func (p *parser) parseRule(line []byte, sep int) AST {
114 lhs := string(bytes.TrimSpace(line[:sep]))
115 rhs := string(bytes.TrimSpace(line[sep+1:]))
116 ast := &RuleAST{
117 lhs: lhs,
118 rhs: rhs,
119 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900120 ast.filename = p.filename
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900121 ast.lineno = p.lineno
Shinichiro Hamaji7c4e3252015-03-30 23:04:25 +0900122 ast.cmdLineno = p.elineno + 1
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900123 for {
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900124 line := p.readLine()
125 if len(line) == 0 {
126 break
127 } else if line[0] == '\t' {
128 ast.cmds = append(ast.cmds, string(bytes.TrimSpace(line)))
129 } else {
130 p.unreadLine(line)
131 break
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900132 }
133 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900134 return ast
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900135}
136
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900137func (p *parser) parseInclude(line string, oplen int) AST {
138 ast := &IncludeAST{
139 expr: line[oplen+1:],
140 op: line[:oplen],
141 }
142 ast.filename = p.filename
143 ast.lineno = p.lineno
144 return ast
145}
146
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900147func (p *parser) parseIfdef(line string, oplen int) AST {
148 ast := &IfAST{
Shinichiro Hamajiae32b782015-03-31 14:41:19 +0900149 op: line[:oplen],
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900150 lhs: strings.TrimSpace(line[oplen+1:]),
151 }
152 ast.filename = p.filename
153 ast.lineno = p.lineno
154 p.addStatement(ast)
155 p.ifStack = append(p.ifStack, ifState{ast: ast})
156 p.outStmts = &ast.trueStmts
157 return ast
158}
159
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900160func closeParen(ch byte) (byte, error) {
161 switch ch {
162 case '(':
163 return ')', nil
164 case '{':
165 return '}', nil
166 default:
167 return 0, fmt.Errorf("unexpected paren %c", ch)
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900168 }
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900169}
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900170
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900171// parseExpr parses s as expr.
172// The expr should starts with '(' or '{' and returns strings
173// separeted by ',' before ')' or '}' respectively, and an index for the rest.
174func parseExpr(s string) ([]string, int, error) {
175 if len(s) == 0 {
176 return nil, 0, errors.New("empty expr")
177 }
178 paren, err := closeParen(s[0])
179 if err != nil {
180 return nil, 0, err
181 }
182 parenCnt := make(map[byte]int)
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900183 i := 0
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900184 ia := 1
185 var args []string
186Loop:
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900187 for {
188 i++
189 if i == len(s) {
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900190 return nil, 0, errors.New("unexpected end of expr")
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900191 }
192 ch := s[i]
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900193 switch ch {
194 case '(', '{':
195 cch, err := closeParen(ch)
196 if err != nil {
197 return nil, 0, err
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900198 }
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900199 parenCnt[cch]++
200 case ')', '}':
201 parenCnt[ch]--
202 if ch == paren && parenCnt[ch] < 0 {
203 break Loop
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900204 }
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900205 case ',':
206 if parenCnt[')'] == 0 && parenCnt['}'] == 0 {
207 args = append(args, s[ia:i])
208 ia = i + 1
209 }
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900210 }
211 }
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900212 args = append(args, s[ia:i])
213 return args, i + 1, nil
214}
215
216func parseEq(s string) (string, string, bool) {
217 args, _, err := parseExpr(s)
218 if err != nil {
219 return "", "", false
220 }
221 if len(args) != 2 {
222 return "", "", false
223 }
224 // TODO: check rest?
225 return args[0], args[1], true
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900226}
227
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900228func (p *parser) parseIfeq(line string, oplen int) AST {
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900229 lhs, rhs, ok := parseEq(strings.TrimSpace(line[oplen+1:]))
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900230 if !ok {
231 Error(p.filename, p.lineno, `*** invalid syntax in conditional.`)
232 }
233
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900234 ast := &IfAST{
Shinichiro Hamajiae32b782015-03-31 14:41:19 +0900235 op: line[:oplen],
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900236 lhs: lhs,
237 rhs: rhs,
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900238 }
239 ast.filename = p.filename
240 ast.lineno = p.lineno
241 p.addStatement(ast)
242 p.ifStack = append(p.ifStack, ifState{ast: ast})
243 p.outStmts = &ast.trueStmts
244 return ast
245}
246
247func (p *parser) checkIfStack(curKeyword string) {
248 if len(p.ifStack) == 0 {
249 Error(p.filename, p.lineno, `*** extraneous %q.`, curKeyword)
250 }
251}
252
253func (p *parser) parseElse(line string) {
254 p.checkIfStack("else")
255 state := &p.ifStack[len(p.ifStack)-1]
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +0900256 if state.inElse {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900257 Error(p.filename, p.lineno, `*** only one "else" per conditional.`)
258 }
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +0900259 state.inElse = true
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900260 p.outStmts = &state.ast.falseStmts
261}
262
263func (p *parser) parseEndif(line string) {
264 p.checkIfStack("endif")
Shinichiro Hamajiae32b782015-03-31 14:41:19 +0900265 p.ifStack = p.ifStack[0 : len(p.ifStack)-1]
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900266 if len(p.ifStack) == 0 {
267 p.outStmts = &p.mk.stmts
268 } else {
269 state := p.ifStack[len(p.ifStack)-1]
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +0900270 if state.inElse {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900271 p.outStmts = &state.ast.falseStmts
272 } else {
273 p.outStmts = &state.ast.trueStmts
274 }
275 }
276}
277
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900278func (p *parser) parseKeywords(line string) bool {
Shinichiro Hamajie3a94632015-03-31 01:12:52 +0900279 stripped := strings.TrimLeft(line, " \t")
280 if strings.HasPrefix(stripped, "include ") {
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900281 p.addStatement(p.parseInclude(stripped, len("include")))
282 return true
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900283 }
Shinichiro Hamajie3a94632015-03-31 01:12:52 +0900284 if strings.HasPrefix(stripped, "-include ") {
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900285 p.addStatement(p.parseInclude(stripped, len("-include")))
286 return true
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900287 }
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900288 if strings.HasPrefix(stripped, "ifdef ") {
289 p.parseIfdef(stripped, len("ifdef"))
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900290 return true
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900291 }
292 if strings.HasPrefix(stripped, "ifndef ") {
293 p.parseIfdef(stripped, len("ifndef"))
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900294 return true
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900295 }
296 if strings.HasPrefix(stripped, "ifeq ") {
297 p.parseIfeq(stripped, len("ifeq"))
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900298 return true
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900299 }
300 if strings.HasPrefix(stripped, "ifneq ") {
301 p.parseIfeq(stripped, len("ifneq"))
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900302 return true
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900303 }
304 if strings.HasPrefix(stripped, "else") {
305 p.parseElse(stripped)
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900306 return true
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900307 }
308 if strings.HasPrefix(stripped, "endif") {
309 p.parseEndif(stripped)
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900310 return true
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900311 }
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900312 return false
313}
314
315func (p *parser) parseLine(line string) AST {
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900316 ast := &RawExprAST{expr: line}
317 ast.filename = p.filename
318 ast.lineno = p.lineno
319 return ast
320}
321
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900322func (p *parser) parse() (mk Makefile, err error) {
323 defer func() {
324 if r := recover(); r != nil {
325 err = fmt.Errorf("panic: %v", r)
326 }
327 }()
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900328 for !p.done {
329 line := p.readLine()
330
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900331 if p.parseKeywords(string(line)) {
332 continue
333 }
334
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900335 var ast AST
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900336 for i, ch := range line {
337 switch ch {
338 case ':':
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900339 if i+1 < len(line) && line[i+1] == '=' {
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900340 ast = p.parseAssign(line, i, i+2)
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900341 } else {
342 ast = p.parseRule(line, i)
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900343 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900344 case '=':
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900345 ast = p.parseAssign(line, i, i+1)
Shinichiro Hamaji69b7f652015-03-31 01:01:59 +0900346 case '?', '+':
347 if i+1 < len(line) && line[i+1] == '=' {
348 ast = p.parseAssign(line, i, i+2)
349 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900350 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900351 if ast != nil {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900352 p.addStatement(ast)
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900353 break
354 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900355 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900356 if ast == nil && len(bytes.TrimSpace(line)) > 0 {
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900357 ast = p.parseLine(string(line))
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900358 p.addStatement(ast)
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900359 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900360 }
361 return p.mk, nil
362}
363
364func ParseMakefile(filename string) (Makefile, error) {
365 f, err := os.Open(filename)
366 if err != nil {
367 return Makefile{}, err
368 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900369 defer f.Close()
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900370 parser := newParser(f, filename)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900371 return parser.parse()
372}
373
374func ParseDefaultMakefile() (Makefile, error) {
375 candidates := []string{"GNUmakefile", "makefile", "Makefile"}
376 for _, filename := range candidates {
377 if exists(filename) {
378 return ParseMakefile(filename)
379 }
380 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900381 return Makefile{}, errors.New("no targets specified and no makefile found.")
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900382}