blob: 5fff8aa464afef94acc8c23ec31461d3cdcb50d6 [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
17type parser struct {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090018 rd *bufio.Reader
19 mk Makefile
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +090020 filename string
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090021 lineno int
22 elineno int // lineno == elineno unless there is trailing '\'.
23 unBuf []byte
24 hasUnBuf bool
25 done bool
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090026}
27
28func exists(filename string) bool {
29 f, err := os.Open(filename)
30 if err != nil {
31 return false
32 }
33 f.Close()
34 return true
35}
36
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +090037func newParser(rd io.Reader, filename string) *parser {
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090038 return &parser{
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +090039 rd: bufio.NewReader(rd),
40 filename: filename,
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090041 }
42}
43
Shinichiro Hamajie1841582015-03-30 17:20:33 +090044func (p *parser) readLine() []byte {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090045 if p.hasUnBuf {
46 p.hasUnBuf = false
47 return p.unBuf
Shinichiro Hamajie1841582015-03-30 17:20:33 +090048 }
49
50 p.lineno = p.elineno
51 line, err := p.rd.ReadBytes('\n')
52 p.lineno++
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +090053 p.elineno = p.lineno
Shinichiro Hamajie1841582015-03-30 17:20:33 +090054 if err == io.EOF {
55 p.done = true
56 } else if err != nil {
57 panic(err)
58 }
59
60 if len(line) > 0 {
61 line = line[0 : len(line)-1]
62 }
63
64 // TODO: Handle \\ at the end of the line?
65 for len(line) > 0 && line[len(line)-1] == '\\' {
66 line = line[:len(line)-1]
67 nline := p.readLine()
68 p.elineno++
69 line = append(line, nline...)
70 }
71
72 index := bytes.IndexByte(line, '#')
73 if index >= 0 {
74 line = line[:index]
75 }
76
77 return line
78}
79
80func (p *parser) unreadLine(line []byte) {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090081 if p.hasUnBuf {
Shinichiro Hamajie1841582015-03-30 17:20:33 +090082 panic("unreadLine twice!")
83 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090084 p.unBuf = line
85 p.hasUnBuf = true
Shinichiro Hamajie1841582015-03-30 17:20:33 +090086}
87
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +090088func (p *parser) parseAssign(line []byte, sep, esep int) AST {
89 Log("parseAssign %s %s", line, line[sep:esep])
90 ast := &AssignAST{
91 lhs: string(bytes.TrimSpace(line[:sep])),
92 rhs: string(bytes.TrimLeft(line[esep:], " \t")),
93 op: string(line[sep:esep]),
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +090094 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +090095 ast.filename = p.filename
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090096 ast.lineno = p.lineno
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090097 return ast
98}
99
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900100func (p *parser) parseRule(line []byte, sep int) AST {
101 lhs := string(bytes.TrimSpace(line[:sep]))
102 rhs := string(bytes.TrimSpace(line[sep+1:]))
103 ast := &RuleAST{
104 lhs: lhs,
105 rhs: rhs,
106 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900107 ast.filename = p.filename
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900108 ast.lineno = p.lineno
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900109 for {
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900110 line := p.readLine()
111 if len(line) == 0 {
112 break
113 } else if line[0] == '\t' {
114 ast.cmds = append(ast.cmds, string(bytes.TrimSpace(line)))
115 } else {
116 p.unreadLine(line)
117 break
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900118 }
119 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900120 return ast
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900121}
122
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900123func (p *parser) parseInclude(line string, oplen int) AST {
124 ast := &IncludeAST{
125 expr: line[oplen+1:],
126 op: line[:oplen],
127 }
128 ast.filename = p.filename
129 ast.lineno = p.lineno
130 return ast
131}
132
133func (p *parser) parseLine(line string) AST {
134 if strings.HasPrefix(line, "include ") {
135 return p.parseInclude(line, len("include"))
136 }
137 if strings.HasPrefix(line, "-include ") {
138 return p.parseInclude(line, len("-include"))
139 }
140 ast := &RawExprAST{expr: line}
141 ast.filename = p.filename
142 ast.lineno = p.lineno
143 return ast
144}
145
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900146func (p *parser) parse() (mk Makefile, err error) {
147 defer func() {
148 if r := recover(); r != nil {
149 err = fmt.Errorf("panic: %v", r)
150 }
151 }()
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900152 for !p.done {
153 line := p.readLine()
154
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900155 var ast AST
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900156 for i, ch := range line {
157 switch ch {
158 case ':':
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900159 if i+1 < len(line) && line[i+1] == '=' {
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900160 ast = p.parseAssign(line, i, i+2)
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900161 } else {
162 ast = p.parseRule(line, i)
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900163 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900164 case '=':
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900165 ast = p.parseAssign(line, i, i+1)
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900166 case '?':
167 panic("TODO")
168 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900169 if ast != nil {
170 p.mk.stmts = append(p.mk.stmts, ast)
171 break
172 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900173 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900174 if ast == nil && len(bytes.TrimSpace(line)) > 0 {
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900175 ast = p.parseLine(string(line))
176 p.mk.stmts = append(p.mk.stmts, ast)
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900177 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900178 }
179 return p.mk, nil
180}
181
182func ParseMakefile(filename string) (Makefile, error) {
183 f, err := os.Open(filename)
184 if err != nil {
185 return Makefile{}, err
186 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900187 defer f.Close()
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900188 parser := newParser(f, filename)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900189 return parser.parse()
190}
191
192func ParseDefaultMakefile() (Makefile, error) {
193 candidates := []string{"GNUmakefile", "makefile", "Makefile"}
194 for _, filename := range candidates {
195 if exists(filename) {
196 return ParseMakefile(filename)
197 }
198 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900199 return Makefile{}, errors.New("no targets specified and no makefile found.")
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900200}