Implement ifdef and ifndef
diff --git a/ast.go b/ast.go
index fe8e21f..7abeeaa 100644
--- a/ast.go
+++ b/ast.go
@@ -92,3 +92,21 @@
func (ast *IncludeAST) show() {
Log("include %s", ast.expr)
}
+
+type IfAST struct {
+ ASTBase
+ op string
+ lhs string
+ rhs string // Empty if |op| is ifdef or ifndef.
+ trueStmts []AST
+ falseStmts []AST
+}
+
+func (ast *IfAST) eval(ev *Evaluator) {
+ ev.evalIf(ast)
+}
+
+func (ast *IfAST) show() {
+ // TODO
+ Log("if")
+}
diff --git a/eval.go b/eval.go
index 6f092cc..4d4c07f 100644
--- a/eval.go
+++ b/eval.go
@@ -221,6 +221,26 @@
}
}
+func (ev *Evaluator) evalIf(ast *IfAST) {
+ var stmts []AST
+ switch ast.op {
+ case "ifdef", "ifndef":
+ value, _ := ev.getVar(ev.evalExpr(ast.lhs))
+ if (value != "") == (ast.op == "ifdef") {
+ stmts = ast.trueStmts
+ } else {
+ stmts = ast.falseStmts
+ }
+ case "ifeq", "ifneq":
+ panic("TODO")
+ default:
+ panic(fmt.Sprintf("unknown if statement: %q", ast.op))
+ }
+ for _, stmt := range stmts {
+ ev.eval(stmt)
+ }
+}
+
func (ev *Evaluator) eval(ast AST) {
ast.eval(ev)
}
diff --git a/parser.go b/parser.go
index d8bb903..513305b 100644
--- a/parser.go
+++ b/parser.go
@@ -14,6 +14,11 @@
stmts []AST
}
+type ifState struct {
+ ast *IfAST
+ in_else bool
+}
+
type parser struct {
rd *bufio.Reader
mk Makefile
@@ -23,6 +28,8 @@
unBuf []byte
hasUnBuf bool
done bool
+ outStmts *[]AST
+ ifStack []ifState
}
func exists(filename string) bool {
@@ -35,10 +42,16 @@
}
func newParser(rd io.Reader, filename string) *parser {
- return &parser{
+ p := &parser{
rd: bufio.NewReader(rd),
filename: filename,
}
+ p.outStmts = &p.mk.stmts
+ return p
+}
+
+func (p* parser) addStatement(ast AST) {
+ *p.outStmts = append(*p.outStmts, ast)
}
func (p *parser) readLine() []byte {
@@ -131,6 +144,63 @@
return ast
}
+func (p *parser) parseIfdef(line string, oplen int) AST {
+ ast := &IfAST{
+ op: line[:oplen],
+ lhs: strings.TrimSpace(line[oplen+1:]),
+ }
+ ast.filename = p.filename
+ ast.lineno = p.lineno
+ p.addStatement(ast)
+ p.ifStack = append(p.ifStack, ifState{ast: ast})
+ p.outStmts = &ast.trueStmts
+ return ast
+}
+
+func (p *parser) parseIfeq(line string, oplen int) AST {
+ ast := &IfAST{
+ op: line[:oplen],
+ lhs: strings.TrimSpace(line[oplen+1:]),
+ }
+ ast.filename = p.filename
+ ast.lineno = p.lineno
+ p.addStatement(ast)
+ p.ifStack = append(p.ifStack, ifState{ast: ast})
+ p.outStmts = &ast.trueStmts
+ return ast
+}
+
+func (p *parser) checkIfStack(curKeyword string) {
+ if len(p.ifStack) == 0 {
+ Error(p.filename, p.lineno, `*** extraneous %q.`, curKeyword)
+ }
+}
+
+func (p *parser) parseElse(line string) {
+ p.checkIfStack("else")
+ state := &p.ifStack[len(p.ifStack)-1]
+ if state.in_else {
+ Error(p.filename, p.lineno, `*** only one "else" per conditional.`)
+ }
+ state.in_else = true
+ p.outStmts = &state.ast.falseStmts
+}
+
+func (p *parser) parseEndif(line string) {
+ p.checkIfStack("endif")
+ p.ifStack = p.ifStack[0:len(p.ifStack)-1]
+ if len(p.ifStack) == 0 {
+ p.outStmts = &p.mk.stmts
+ } else {
+ state := p.ifStack[len(p.ifStack)-1]
+ if state.in_else {
+ p.outStmts = &state.ast.falseStmts
+ } else {
+ p.outStmts = &state.ast.trueStmts
+ }
+ }
+}
+
func (p *parser) parseLine(line string) AST {
stripped := strings.TrimLeft(line, " \t")
if strings.HasPrefix(stripped, "include ") {
@@ -139,6 +209,30 @@
if strings.HasPrefix(stripped, "-include ") {
return p.parseInclude(stripped, len("-include"))
}
+ if strings.HasPrefix(stripped, "ifdef ") {
+ p.parseIfdef(stripped, len("ifdef"))
+ return nil
+ }
+ if strings.HasPrefix(stripped, "ifndef ") {
+ p.parseIfdef(stripped, len("ifndef"))
+ return nil
+ }
+ if strings.HasPrefix(stripped, "ifeq ") {
+ p.parseIfeq(stripped, len("ifeq"))
+ return nil
+ }
+ if strings.HasPrefix(stripped, "ifneq ") {
+ p.parseIfeq(stripped, len("ifneq"))
+ return nil
+ }
+ if strings.HasPrefix(stripped, "else") {
+ p.parseElse(stripped)
+ return nil
+ }
+ if strings.HasPrefix(stripped, "endif") {
+ p.parseEndif(stripped)
+ return nil
+ }
ast := &RawExprAST{expr: line}
ast.filename = p.filename
ast.lineno = p.lineno
@@ -171,13 +265,15 @@
}
}
if ast != nil {
- p.mk.stmts = append(p.mk.stmts, ast)
+ p.addStatement(ast)
break
}
}
if ast == nil && len(bytes.TrimSpace(line)) > 0 {
ast = p.parseLine(string(line))
- p.mk.stmts = append(p.mk.stmts, ast)
+ if ast != nil {
+ p.addStatement(ast)
+ }
}
}
return p.mk, nil
diff --git a/runtest.rb b/runtest.rb
index bb4cbd6..fef0a5a 100755
--- a/runtest.rb
+++ b/runtest.rb
@@ -33,6 +33,9 @@
output = ''
testcases = c.scan(/^test\d*/).sort
+ if testcases.empty?
+ testcases = ['']
+ end
cleanup
testcases.each do |tc|
diff --git a/test/cond_syntax.mk b/test/cond_syntax.mk
new file mode 100644
index 0000000..e5ba2eb
--- /dev/null
+++ b/test/cond_syntax.mk
@@ -0,0 +1,91 @@
+VAR=var
+VARREF=VAR
+EMPTY=
+UNDEFREF=UNDEFINED
+
+RESULT=
+
+ifdef VAR
+RESULT += PASS
+endif
+
+ifdef VAR
+RESULT += PASS
+else
+RESULT += FAIL
+endif
+ifdef $(VARREF)
+RESULT += PASS
+else
+RESULT += FAIL
+endif
+ifdef UNDEFINED
+RESULT += FAIL
+else
+RESULT += PASS
+endif
+ifdef $(UNDEFREF)
+RESULT += FAIL
+else
+RESULT += PASS
+endif
+ifdef EMPTY
+RESULT += FAIL
+else
+RESULT += PASS
+endif
+
+ifndef VAR
+RESULT += FAIL
+else
+RESULT += PASS
+endif
+ifndef $(VARREF)
+RESULT += FAIL
+else
+RESULT += PASS
+endif
+ifndef UNDEFINED
+RESULT += PASS
+else
+RESULT += FAIL
+endif
+ifndef $(UNDEFREF)
+RESULT += PASS
+else
+RESULT += FAIL
+endif
+
+# TODO: Support ifeq and ifneq
+
+# ifeq ($(VAR),var)
+# RESULT += PASS
+# else
+# RESULT += FAIL
+# endif
+# ifneq ($(VAR),var)
+# RESULT += FAIL
+# else
+# RESULT += PASS
+# endif
+
+# ifeq ($(UNDEFINED),)
+# RESULT += PASS
+# else
+# RESULT += FAIL
+# endif
+# ifeq (,$(UNDEFINED))
+# RESULT += PASS
+# else
+# RESULT += FAIL
+# endif
+
+# TODO: Support?
+# ifeq "$(VAR)" "var"
+# RESULT += PASS
+# else
+# RESULT += FAIL
+# endif
+
+test:
+ echo $(RESULT)
diff --git a/test/err_extra_else.mk b/test/err_extra_else.mk
new file mode 100644
index 0000000..82fec45
--- /dev/null
+++ b/test/err_extra_else.mk
@@ -0,0 +1 @@
+else
diff --git a/test/err_extra_endif.mk b/test/err_extra_endif.mk
new file mode 100644
index 0000000..1467f63
--- /dev/null
+++ b/test/err_extra_endif.mk
@@ -0,0 +1 @@
+endif
diff --git a/test/err_two_else.mk b/test/err_two_else.mk
new file mode 100644
index 0000000..c996fa7
--- /dev/null
+++ b/test/err_two_else.mk
@@ -0,0 +1,4 @@
+ifdef VAR
+else
+else
+endif