blob: 336e0292cc72c12f151c106cfba90dcc0393f7c5 [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 {
18 ast *IfAST
19 in_else bool
20}
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
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900160func (p *parser) parseEq(s string) (string, string, bool) {
161 if len(s) == 0 || s[0] != '(' {
162 return "", "", false
163 }
164
165 // TODO: Double-quotes will not be handled properly.
166 i := 0
167 paren_cnt := 0
168 in_rhs := false
169 var lhs []byte
170 var rhs []byte
171 for {
172 i++
173 if i == len(s) {
174 return "", "", false
175 }
176 ch := s[i]
177 if ch == '(' {
178 paren_cnt++
179 } else if ch == ')' {
180 paren_cnt--
181 if paren_cnt < 0 {
182 if in_rhs {
183 break
184 } else {
185 return "", "", false
186 }
187 }
188 } else if ch == ',' {
189 if in_rhs {
190 return "", "", false
191 } else {
192 in_rhs = true
193 continue
194 }
195 }
196 if in_rhs {
197 rhs = append(rhs, ch)
198 } else {
199 lhs = append(lhs, ch)
200 }
201 }
202 return string(lhs), string(rhs), true
203}
204
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900205func (p *parser) parseIfeq(line string, oplen int) AST {
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900206 lhs, rhs, ok := p.parseEq(strings.TrimSpace(line[oplen+1:]))
207 if !ok {
208 Error(p.filename, p.lineno, `*** invalid syntax in conditional.`)
209 }
210
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900211 ast := &IfAST{
Shinichiro Hamajiae32b782015-03-31 14:41:19 +0900212 op: line[:oplen],
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900213 lhs: lhs,
214 rhs: rhs,
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900215 }
216 ast.filename = p.filename
217 ast.lineno = p.lineno
218 p.addStatement(ast)
219 p.ifStack = append(p.ifStack, ifState{ast: ast})
220 p.outStmts = &ast.trueStmts
221 return ast
222}
223
224func (p *parser) checkIfStack(curKeyword string) {
225 if len(p.ifStack) == 0 {
226 Error(p.filename, p.lineno, `*** extraneous %q.`, curKeyword)
227 }
228}
229
230func (p *parser) parseElse(line string) {
231 p.checkIfStack("else")
232 state := &p.ifStack[len(p.ifStack)-1]
233 if state.in_else {
234 Error(p.filename, p.lineno, `*** only one "else" per conditional.`)
235 }
236 state.in_else = true
237 p.outStmts = &state.ast.falseStmts
238}
239
240func (p *parser) parseEndif(line string) {
241 p.checkIfStack("endif")
Shinichiro Hamajiae32b782015-03-31 14:41:19 +0900242 p.ifStack = p.ifStack[0 : len(p.ifStack)-1]
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900243 if len(p.ifStack) == 0 {
244 p.outStmts = &p.mk.stmts
245 } else {
246 state := p.ifStack[len(p.ifStack)-1]
247 if state.in_else {
248 p.outStmts = &state.ast.falseStmts
249 } else {
250 p.outStmts = &state.ast.trueStmts
251 }
252 }
253}
254
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900255func (p *parser) parseLine(line string) AST {
Shinichiro Hamajie3a94632015-03-31 01:12:52 +0900256 stripped := strings.TrimLeft(line, " \t")
257 if strings.HasPrefix(stripped, "include ") {
258 return p.parseInclude(stripped, len("include"))
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900259 }
Shinichiro Hamajie3a94632015-03-31 01:12:52 +0900260 if strings.HasPrefix(stripped, "-include ") {
261 return p.parseInclude(stripped, len("-include"))
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900262 }
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900263 if strings.HasPrefix(stripped, "ifdef ") {
264 p.parseIfdef(stripped, len("ifdef"))
265 return nil
266 }
267 if strings.HasPrefix(stripped, "ifndef ") {
268 p.parseIfdef(stripped, len("ifndef"))
269 return nil
270 }
271 if strings.HasPrefix(stripped, "ifeq ") {
272 p.parseIfeq(stripped, len("ifeq"))
273 return nil
274 }
275 if strings.HasPrefix(stripped, "ifneq ") {
276 p.parseIfeq(stripped, len("ifneq"))
277 return nil
278 }
279 if strings.HasPrefix(stripped, "else") {
280 p.parseElse(stripped)
281 return nil
282 }
283 if strings.HasPrefix(stripped, "endif") {
284 p.parseEndif(stripped)
285 return nil
286 }
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900287 ast := &RawExprAST{expr: line}
288 ast.filename = p.filename
289 ast.lineno = p.lineno
290 return ast
291}
292
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900293func (p *parser) parse() (mk Makefile, err error) {
294 defer func() {
295 if r := recover(); r != nil {
296 err = fmt.Errorf("panic: %v", r)
297 }
298 }()
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900299 for !p.done {
300 line := p.readLine()
301
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900302 var ast AST
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900303 for i, ch := range line {
304 switch ch {
305 case ':':
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900306 if i+1 < len(line) && line[i+1] == '=' {
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900307 ast = p.parseAssign(line, i, i+2)
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900308 } else {
309 ast = p.parseRule(line, i)
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900310 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900311 case '=':
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900312 ast = p.parseAssign(line, i, i+1)
Shinichiro Hamaji69b7f652015-03-31 01:01:59 +0900313 case '?', '+':
314 if i+1 < len(line) && line[i+1] == '=' {
315 ast = p.parseAssign(line, i, i+2)
316 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900317 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900318 if ast != nil {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900319 p.addStatement(ast)
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900320 break
321 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900322 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900323 if ast == nil && len(bytes.TrimSpace(line)) > 0 {
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900324 ast = p.parseLine(string(line))
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900325 if ast != nil {
326 p.addStatement(ast)
327 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900328 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900329 }
330 return p.mk, nil
331}
332
333func ParseMakefile(filename string) (Makefile, error) {
334 f, err := os.Open(filename)
335 if err != nil {
336 return Makefile{}, err
337 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900338 defer f.Close()
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900339 parser := newParser(f, filename)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900340 return parser.parse()
341}
342
343func ParseDefaultMakefile() (Makefile, error) {
344 candidates := []string{"GNUmakefile", "makefile", "Makefile"}
345 for _, filename := range candidates {
346 if exists(filename) {
347 return ParseMakefile(filename)
348 }
349 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900350 return Makefile{}, errors.New("no targets specified and no makefile found.")
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900351}