blob: e991499aba029c954e70486fe18576aa88030030 [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"
Fumitoshi Ukai119dc912015-03-30 16:52:41 +09009)
10
11type Makefile struct {
12 stmts []AST
13}
14
15type parser struct {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090016 rd *bufio.Reader
17 mk Makefile
18 lineno int
19 elineno int // lineno == elineno unless there is trailing '\'.
20 unBuf []byte
21 hasUnBuf bool
22 done bool
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090023}
24
25func exists(filename string) bool {
26 f, err := os.Open(filename)
27 if err != nil {
28 return false
29 }
30 f.Close()
31 return true
32}
33
34func isdigit(ch byte) bool {
35 return ch >= '0' && ch <= '9'
36}
37
38func isident(ch byte) bool {
39 return (ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z' || ch == '_' || ch == '.')
40}
41
42func newParser(rd io.Reader) *parser {
43 return &parser{
44 rd: bufio.NewReader(rd),
45 }
46}
47
Shinichiro Hamajie1841582015-03-30 17:20:33 +090048func (p *parser) readLine() []byte {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090049 if p.hasUnBuf {
50 p.hasUnBuf = false
51 return p.unBuf
Shinichiro Hamajie1841582015-03-30 17:20:33 +090052 }
53
54 p.lineno = p.elineno
55 line, err := p.rd.ReadBytes('\n')
56 p.lineno++
57 if err == io.EOF {
58 p.done = true
59 } else if err != nil {
60 panic(err)
61 }
62
63 if len(line) > 0 {
64 line = line[0 : len(line)-1]
65 }
66
67 // TODO: Handle \\ at the end of the line?
68 for len(line) > 0 && line[len(line)-1] == '\\' {
69 line = line[:len(line)-1]
70 nline := p.readLine()
71 p.elineno++
72 line = append(line, nline...)
73 }
74
75 index := bytes.IndexByte(line, '#')
76 if index >= 0 {
77 line = line[:index]
78 }
79
80 return line
81}
82
83func (p *parser) unreadLine(line []byte) {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090084 if p.hasUnBuf {
Shinichiro Hamajie1841582015-03-30 17:20:33 +090085 panic("unreadLine twice!")
86 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090087 p.unBuf = line
88 p.hasUnBuf = true
Shinichiro Hamajie1841582015-03-30 17:20:33 +090089}
90
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090091func (p *parser) readByte() (byte, error) {
92 ch, err := p.rd.ReadByte()
93 if err != nil {
94 p.done = true
95 }
96 return ch, err
97}
98
99func (p *parser) unreadByte() {
100 p.rd.UnreadByte()
101}
102
103func (p *parser) skipWhiteSpaces() error {
104 for {
105 ch, err := p.readByte()
106 if err != nil {
107 return err
108 }
109 switch ch {
110 case '\n':
111 p.lineno++
112 fallthrough
113 case '\r', ' ':
114 continue
115 default:
116 p.unreadByte()
117 return nil
118 }
119 }
120}
121
122func (p *parser) getNextToken() (string, error) {
123 if err := p.skipWhiteSpaces(); err != nil {
124 return "", err
125 }
126 ch, err := p.readByte()
127 if err != nil {
128 return "", errors.New("TODO")
129 }
130 switch ch {
131 case '$', '=':
132 return string(ch), nil
133 case ':':
134 var s []byte
135 s = append(s, ch)
136 ch, err := p.readByte()
137 if ch == ':' {
138 ch, err = p.readByte()
139 }
140 if err != nil {
141 return string(s), err
142 }
143 if ch == '=' {
144 s = append(s, ch)
145 } else {
146 p.unreadByte()
147 }
148 return string(s), nil
149 default:
150 if isident(ch) {
151 var s []byte
152 s = append(s, ch)
153 for {
154 ch, err := p.readByte()
155 if err != nil {
156 return string(s), err
157 }
158 if isident(ch) || isdigit(ch) {
159 s = append(s, ch)
160 } else {
161 p.unreadByte()
162 return string(s), nil
163 }
164 }
165 }
166 }
167
168 return "", errors.New("foobar")
169}
170
171func (p *parser) readUntilEol() string {
172 var r []byte
173 for {
174 ch, err := p.readByte()
175 if err != nil || ch == '\n' {
176 return string(r)
177 }
178 r = append(r, ch)
179 }
180}
181
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900182func (p *parser) parseAssign(line []byte, sep int, typ int) AST {
183 Log("parseAssign %s %d", line, sep)
184 esep := sep + 1
185 if typ != ASSIGN_RECURSIVE {
186 esep++
187 }
188 lhs := string(bytes.TrimSpace(line[:sep]))
189 rhs := string(bytes.TrimLeft(line[esep:], " \t"))
190 ast := &AssignAST{lhs: lhs, rhs: rhs, assign_type: typ}
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900191 ast.lineno = p.lineno
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900192 return ast
193}
194
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900195func (p *parser) parseRule(line []byte, sep int) AST {
196 lhs := string(bytes.TrimSpace(line[:sep]))
197 rhs := string(bytes.TrimSpace(line[sep+1:]))
198 ast := &RuleAST{
199 lhs: lhs,
200 rhs: rhs,
201 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900202 ast.lineno = p.lineno
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900203 for {
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900204 line := p.readLine()
205 if len(line) == 0 {
206 break
207 } else if line[0] == '\t' {
208 ast.cmds = append(ast.cmds, string(bytes.TrimSpace(line)))
209 } else {
210 p.unreadLine(line)
211 break
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900212 }
213 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900214 return ast
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900215}
216
217func (p *parser) parse() (Makefile, error) {
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900218 for !p.done {
219 line := p.readLine()
220
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900221 var ast AST
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900222 for i, ch := range line {
223 switch ch {
224 case ':':
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900225 if i+1 < len(line) && line[i+1] == '=' {
226 ast = p.parseAssign(line, i, ASSIGN_SIMPLE)
227 } else {
228 ast = p.parseRule(line, i)
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900229 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900230 case '=':
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900231 ast = p.parseAssign(line, i, ASSIGN_RECURSIVE)
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900232 case '?':
233 panic("TODO")
234 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900235 if ast != nil {
236 p.mk.stmts = append(p.mk.stmts, ast)
237 break
238 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900239 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900240 }
241 return p.mk, nil
242}
243
244func ParseMakefile(filename string) (Makefile, error) {
245 f, err := os.Open(filename)
246 if err != nil {
247 return Makefile{}, err
248 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900249 defer f.Close()
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900250 parser := newParser(f)
251 return parser.parse()
252}
253
254func ParseDefaultMakefile() (Makefile, error) {
255 candidates := []string{"GNUmakefile", "makefile", "Makefile"}
256 for _, filename := range candidates {
257 if exists(filename) {
258 return ParseMakefile(filename)
259 }
260 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900261 return Makefile{}, errors.New("no targets specified and no makefile found.")
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900262}