blob: 4c9232d20a271655848af62b7383fd69a6b22553 [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"
7 "io"
8 "os"
9 "strings"
10)
11
12type Makefile struct {
13 stmts []AST
14}
15
16type parser struct {
Shinichiro Hamajie1841582015-03-30 17:20:33 +090017 rd *bufio.Reader
18 mk Makefile
19 lineno int
20 elineno int // lineno == elineno unless there is trailing '\'.
21 un_buf []byte
22 has_un_buf bool
23 done bool
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090024}
25
26func exists(filename string) bool {
27 f, err := os.Open(filename)
28 if err != nil {
29 return false
30 }
31 f.Close()
32 return true
33}
34
35func isdigit(ch byte) bool {
36 return ch >= '0' && ch <= '9'
37}
38
39func isident(ch byte) bool {
40 return (ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z' || ch == '_' || ch == '.')
41}
42
43func newParser(rd io.Reader) *parser {
44 return &parser{
45 rd: bufio.NewReader(rd),
46 }
47}
48
Shinichiro Hamajie1841582015-03-30 17:20:33 +090049func (p *parser) readLine() []byte {
50 if p.has_un_buf {
51 p.has_un_buf = false
52 return p.un_buf
53 }
54
55 p.lineno = p.elineno
56 line, err := p.rd.ReadBytes('\n')
57 p.lineno++
58 if err == io.EOF {
59 p.done = true
60 } else if err != nil {
61 panic(err)
62 }
63
64 if len(line) > 0 {
65 line = line[0 : len(line)-1]
66 }
67
68 // TODO: Handle \\ at the end of the line?
69 for len(line) > 0 && line[len(line)-1] == '\\' {
70 line = line[:len(line)-1]
71 nline := p.readLine()
72 p.elineno++
73 line = append(line, nline...)
74 }
75
76 index := bytes.IndexByte(line, '#')
77 if index >= 0 {
78 line = line[:index]
79 }
80
81 return line
82}
83
84func (p *parser) unreadLine(line []byte) {
85 if p.has_un_buf {
86 panic("unreadLine twice!")
87 }
88 p.un_buf = line
89 p.has_un_buf = true
90}
91
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090092func (p *parser) readByte() (byte, error) {
93 ch, err := p.rd.ReadByte()
94 if err != nil {
95 p.done = true
96 }
97 return ch, err
98}
99
100func (p *parser) unreadByte() {
101 p.rd.UnreadByte()
102}
103
104func (p *parser) skipWhiteSpaces() error {
105 for {
106 ch, err := p.readByte()
107 if err != nil {
108 return err
109 }
110 switch ch {
111 case '\n':
112 p.lineno++
113 fallthrough
114 case '\r', ' ':
115 continue
116 default:
117 p.unreadByte()
118 return nil
119 }
120 }
121}
122
123func (p *parser) getNextToken() (string, error) {
124 if err := p.skipWhiteSpaces(); err != nil {
125 return "", err
126 }
127 ch, err := p.readByte()
128 if err != nil {
129 return "", errors.New("TODO")
130 }
131 switch ch {
132 case '$', '=':
133 return string(ch), nil
134 case ':':
135 var s []byte
136 s = append(s, ch)
137 ch, err := p.readByte()
138 if ch == ':' {
139 ch, err = p.readByte()
140 }
141 if err != nil {
142 return string(s), err
143 }
144 if ch == '=' {
145 s = append(s, ch)
146 } else {
147 p.unreadByte()
148 }
149 return string(s), nil
150 default:
151 if isident(ch) {
152 var s []byte
153 s = append(s, ch)
154 for {
155 ch, err := p.readByte()
156 if err != nil {
157 return string(s), err
158 }
159 if isident(ch) || isdigit(ch) {
160 s = append(s, ch)
161 } else {
162 p.unreadByte()
163 return string(s), nil
164 }
165 }
166 }
167 }
168
169 return "", errors.New("foobar")
170}
171
172func (p *parser) readUntilEol() string {
173 var r []byte
174 for {
175 ch, err := p.readByte()
176 if err != nil || ch == '\n' {
177 return string(r)
178 }
179 r = append(r, ch)
180 }
181}
182
183func (p *parser) parseAssign(lhs string) AST {
184 ast := &AssignAST{lhs: lhs}
185 ast.lineno = p.lineno
186 ast.rhs = strings.TrimSpace(p.readUntilEol())
187 return ast
188}
189
190func (p *parser) parseRule(lhs string) AST {
191 ast := &RuleAST{lhs: lhs}
192 ast.lineno = p.lineno
193 ast.rhs = strings.TrimSpace(p.readUntilEol())
194 for {
195 ch, err := p.readByte()
196 if err != nil {
197 return ast
198 }
199 switch ch {
200 case '\n':
201 continue
202 case '\t':
203 ast.cmds = append(ast.cmds, strings.TrimSpace(p.readUntilEol()))
204 continue
205 default:
206 p.unreadByte()
207 return ast
208 }
209 }
210}
211
212func (p *parser) parse() (Makefile, error) {
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900213 for !p.done {
214 line := p.readLine()
215
216 for i, ch := range line {
217 switch ch {
218 case ':':
219 // TODO: Handle := and ::=.
220 lhs := string(bytes.TrimSpace(line[:i]))
221 rhs := string(bytes.TrimSpace(line[i+1:]))
222 ast := &RuleAST{
223 lhs: lhs,
224 rhs: rhs,
225 }
226 ast.lineno = p.lineno
227 for {
228 line := p.readLine()
229 if len(line) == 0 {
230 break
231 } else if line[0] == '\t' {
232 ast.cmds = append(ast.cmds, string(bytes.TrimSpace(line)))
233 } else {
234 p.unreadLine(line)
235 break
236 }
237 }
238 p.mk.stmts = append(p.mk.stmts, ast)
239 case '=':
240 lhs := string(bytes.TrimSpace(line[:i]))
241 rhs := string(bytes.TrimSpace(line[i+1:]))
242 ast := &AssignAST{
243 lhs: lhs,
244 rhs: rhs,
245 }
246 ast.lineno = p.lineno
247 p.mk.stmts = append(p.mk.stmts, ast)
248 case '?':
249 panic("TODO")
250 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900251 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900252
253 /*
254 tok, err := p.getNextToken()
255 Log("tok=%s", tok)
256 if err == io.EOF {
257 return p.mk, nil
258 } else if err != nil {
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900259 return p.mk, err
260 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900261 switch tok {
262 default:
263 ntok, err := p.getNextToken()
264 if err != nil {
265 return p.mk, err
266 }
267 switch ntok {
268 case "=":
269 ast := p.parseAssign(tok)
270 p.mk.stmts = append(p.mk.stmts, ast)
271 case ":":
272 ast := p.parseRule(tok)
273 p.mk.stmts = append(p.mk.stmts, ast)
274 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900275 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900276 */
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900277 }
278 return p.mk, nil
279}
280
281func ParseMakefile(filename string) (Makefile, error) {
282 f, err := os.Open(filename)
283 if err != nil {
284 return Makefile{}, err
285 }
286 parser := newParser(f)
287 return parser.parse()
288}
289
290func ParseDefaultMakefile() (Makefile, error) {
291 candidates := []string{"GNUmakefile", "makefile", "Makefile"}
292 for _, filename := range candidates {
293 if exists(filename) {
294 return ParseMakefile(filename)
295 }
296 }
297 return Makefile{}, errors.New("No targets specified and no makefile found.")
298}