blob: 3f9c5a21f77e79bde0ad6b95d2ad2450bc070225 [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
Shinichiro Hamaji7c4e3252015-03-30 23:04:25 +0900109 ast.cmdLineno = p.elineno + 1
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900110 for {
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900111 line := p.readLine()
112 if len(line) == 0 {
113 break
114 } else if line[0] == '\t' {
115 ast.cmds = append(ast.cmds, string(bytes.TrimSpace(line)))
116 } else {
117 p.unreadLine(line)
118 break
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900119 }
120 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900121 return ast
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900122}
123
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900124func (p *parser) parseInclude(line string, oplen int) AST {
125 ast := &IncludeAST{
126 expr: line[oplen+1:],
127 op: line[:oplen],
128 }
129 ast.filename = p.filename
130 ast.lineno = p.lineno
131 return ast
132}
133
134func (p *parser) parseLine(line string) AST {
135 if strings.HasPrefix(line, "include ") {
136 return p.parseInclude(line, len("include"))
137 }
138 if strings.HasPrefix(line, "-include ") {
139 return p.parseInclude(line, len("-include"))
140 }
141 ast := &RawExprAST{expr: line}
142 ast.filename = p.filename
143 ast.lineno = p.lineno
144 return ast
145}
146
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900147func (p *parser) parse() (mk Makefile, err error) {
148 defer func() {
149 if r := recover(); r != nil {
150 err = fmt.Errorf("panic: %v", r)
151 }
152 }()
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900153 for !p.done {
154 line := p.readLine()
155
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900156 var ast AST
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900157 for i, ch := range line {
158 switch ch {
159 case ':':
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900160 if i+1 < len(line) && line[i+1] == '=' {
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900161 ast = p.parseAssign(line, i, i+2)
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900162 } else {
163 ast = p.parseRule(line, i)
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900164 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900165 case '=':
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900166 ast = p.parseAssign(line, i, i+1)
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900167 case '?':
168 panic("TODO")
169 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900170 if ast != nil {
171 p.mk.stmts = append(p.mk.stmts, ast)
172 break
173 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900174 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900175 if ast == nil && len(bytes.TrimSpace(line)) > 0 {
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900176 ast = p.parseLine(string(line))
177 p.mk.stmts = append(p.mk.stmts, ast)
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900178 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900179 }
180 return p.mk, nil
181}
182
183func ParseMakefile(filename string) (Makefile, error) {
184 f, err := os.Open(filename)
185 if err != nil {
186 return Makefile{}, err
187 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900188 defer f.Close()
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900189 parser := newParser(f, filename)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900190 return parser.parse()
191}
192
193func ParseDefaultMakefile() (Makefile, error) {
194 candidates := []string{"GNUmakefile", "makefile", "Makefile"}
195 for _, filename := range candidates {
196 if exists(filename) {
197 return ParseMakefile(filename)
198 }
199 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900200 return Makefile{}, errors.New("no targets specified and no makefile found.")
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900201}