blob: 4a45b6f22e2b178ec790e9a46beec0ceed2bf201 [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"
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090010)
11
12type Makefile struct {
13 stmts []AST
14}
15
16type parser struct {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090017 rd *bufio.Reader
18 mk Makefile
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +090019 filename string
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090020 lineno int
21 elineno int // lineno == elineno unless there is trailing '\'.
22 unBuf []byte
23 hasUnBuf bool
24 done bool
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090025}
26
27func exists(filename string) bool {
28 f, err := os.Open(filename)
29 if err != nil {
30 return false
31 }
32 f.Close()
33 return true
34}
35
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +090036func newParser(rd io.Reader, filename string) *parser {
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090037 return &parser{
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +090038 rd: bufio.NewReader(rd),
39 filename: filename,
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090040 }
41}
42
Shinichiro Hamajie1841582015-03-30 17:20:33 +090043func (p *parser) readLine() []byte {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090044 if p.hasUnBuf {
45 p.hasUnBuf = false
46 return p.unBuf
Shinichiro Hamajie1841582015-03-30 17:20:33 +090047 }
48
49 p.lineno = p.elineno
50 line, err := p.rd.ReadBytes('\n')
51 p.lineno++
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +090052 p.elineno = p.lineno
Shinichiro Hamajie1841582015-03-30 17:20:33 +090053 if err == io.EOF {
54 p.done = true
55 } else if err != nil {
56 panic(err)
57 }
58
59 if len(line) > 0 {
60 line = line[0 : len(line)-1]
61 }
62
63 // TODO: Handle \\ at the end of the line?
64 for len(line) > 0 && line[len(line)-1] == '\\' {
65 line = line[:len(line)-1]
66 nline := p.readLine()
67 p.elineno++
68 line = append(line, nline...)
69 }
70
71 index := bytes.IndexByte(line, '#')
72 if index >= 0 {
73 line = line[:index]
74 }
75
76 return line
77}
78
79func (p *parser) unreadLine(line []byte) {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090080 if p.hasUnBuf {
Shinichiro Hamajie1841582015-03-30 17:20:33 +090081 panic("unreadLine twice!")
82 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090083 p.unBuf = line
84 p.hasUnBuf = true
Shinichiro Hamajie1841582015-03-30 17:20:33 +090085}
86
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +090087func (p *parser) parseAssign(line []byte, sep, esep int) AST {
88 Log("parseAssign %s %s", line, line[sep:esep])
89 ast := &AssignAST{
90 lhs: string(bytes.TrimSpace(line[:sep])),
91 rhs: string(bytes.TrimLeft(line[esep:], " \t")),
92 op: string(line[sep:esep]),
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +090093 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +090094 ast.filename = p.filename
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090095 ast.lineno = p.lineno
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090096 return ast
97}
98
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +090099func (p *parser) parseRule(line []byte, sep int) AST {
100 lhs := string(bytes.TrimSpace(line[:sep]))
101 rhs := string(bytes.TrimSpace(line[sep+1:]))
102 ast := &RuleAST{
103 lhs: lhs,
104 rhs: rhs,
105 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900106 ast.filename = p.filename
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900107 ast.lineno = p.lineno
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900108 for {
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900109 line := p.readLine()
110 if len(line) == 0 {
111 break
112 } else if line[0] == '\t' {
113 ast.cmds = append(ast.cmds, string(bytes.TrimSpace(line)))
114 } else {
115 p.unreadLine(line)
116 break
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900117 }
118 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900119 return ast
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900120}
121
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900122func (p *parser) parse() (mk Makefile, err error) {
123 defer func() {
124 if r := recover(); r != nil {
125 err = fmt.Errorf("panic: %v", r)
126 }
127 }()
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900128 for !p.done {
129 line := p.readLine()
130
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900131 var ast AST
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900132 for i, ch := range line {
133 switch ch {
134 case ':':
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900135 if i+1 < len(line) && line[i+1] == '=' {
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900136 ast = p.parseAssign(line, i, i+2)
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900137 } else {
138 ast = p.parseRule(line, i)
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900139 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900140 case '=':
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900141 ast = p.parseAssign(line, i, i+1)
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900142 case '?':
143 panic("TODO")
144 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900145 if ast != nil {
146 p.mk.stmts = append(p.mk.stmts, ast)
147 break
148 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900149 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900150 if ast == nil && len(bytes.TrimSpace(line)) > 0 {
151 a := &RawExprAST{expr: string(line)}
152 a.filename = p.filename
153 a.lineno = p.lineno
154 p.mk.stmts = append(p.mk.stmts, a)
155 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900156 }
157 return p.mk, nil
158}
159
160func ParseMakefile(filename string) (Makefile, error) {
161 f, err := os.Open(filename)
162 if err != nil {
163 return Makefile{}, err
164 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900165 defer f.Close()
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900166 parser := newParser(f, filename)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900167 return parser.parse()
168}
169
170func ParseDefaultMakefile() (Makefile, error) {
171 candidates := []string{"GNUmakefile", "makefile", "Makefile"}
172 for _, filename := range candidates {
173 if exists(filename) {
174 return ParseMakefile(filename)
175 }
176 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900177 return Makefile{}, errors.New("no targets specified and no makefile found.")
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900178}