don't panic

return error on api boundary.
diff --git a/eval.go b/eval.go
index 9c8427d..2d1de66 100644
--- a/eval.go
+++ b/eval.go
@@ -2,6 +2,7 @@
 
 import (
 	"bytes"
+	"fmt"
 	"os/exec"
 	"path/filepath"
 	"regexp"
@@ -157,8 +158,13 @@
 	ast.eval(ev)
 }
 
-func Eval(mk Makefile) *EvalResult {
+func Eval(mk Makefile) (er *EvalResult, err error) {
 	ev := newEvaluator()
+	defer func() {
+		if r := recover(); r != nil {
+			err = fmt.Errorf("panic: %v", r)
+		}
+	}()
 	for _, stmt := range mk.stmts {
 		ev.eval(stmt)
 	}
@@ -166,5 +172,5 @@
 		vars:  ev.outVars,
 		rules: ev.outRules,
 		refs:  ev.refs,
-	}
+	}, nil
 }
diff --git a/exec.go b/exec.go
index 158eefb..27bb27c 100644
--- a/exec.go
+++ b/exec.go
@@ -1,6 +1,7 @@
 package main
 
 import (
+	"errors"
 	"fmt"
 	"os"
 	"os/exec"
@@ -26,7 +27,7 @@
 	return st.ModTime().Unix()
 }
 
-func (ex *Executor) runCommands(cmds []string) {
+func (ex *Executor) runCommands(cmds []string) error {
 	for _, cmd := range cmds {
 		fmt.Printf("%s\n", cmd)
 
@@ -37,7 +38,7 @@
 		}
 		out, err := cmd.CombinedOutput()
 		if err != nil {
-			panic(err)
+			return err
 		}
 		success := false
 		if cmd.ProcessState != nil {
@@ -46,60 +47,68 @@
 
 		fmt.Printf("%s", out)
 		if !success {
-			panic("Command failed")
+			return fmt.Errorf("command failed: %q", cmd)
 		}
 	}
+	return nil
 }
 
-func (ex *Executor) build(output string) int64 {
+func (ex *Executor) build(output string) (int64, error) {
 	Log("Building: %s", output)
 	outputTs := getTimestamp(output)
 
 	rule, present := ex.rules[output]
 	if !present {
 		if outputTs >= 0 {
-			return outputTs
+			return outputTs, nil
 		}
-		Error("No rule to make target %q", output)
+		return outputTs, fmt.Errorf("no rule to make target %q", output)
 	}
 
 	latest := int64(-1)
 	for _, input := range rule.inputs {
-		ts := ex.build(input)
+		ts, err := ex.build(input)
+		if err != nil {
+			return outputTs, err
+		}
 		if latest < ts {
 			latest = ts
 		}
 	}
 
 	if outputTs >= latest {
-		return outputTs
+		return outputTs, nil
 	}
 
-	ex.runCommands(rule.cmds)
+	err := ex.runCommands(rule.cmds)
+	if err != nil {
+		return outputTs, err
+	}
 
 	outputTs = getTimestamp(output)
 	if outputTs < 0 {
 		outputTs = time.Now().Unix()
 	}
-	return outputTs
+	return outputTs, nil
 }
 
-func (ex *Executor) exec(er *EvalResult) {
+func (ex *Executor) exec(er *EvalResult) error {
 	if len(er.rules) == 0 {
-		panic("No targets.")
+		return errors.New("no targets.")
 	}
 
 	for _, rule := range er.rules {
 		if _, present := ex.rules[rule.output]; present {
-			Warn("overiding recipie for target '%s'", rule.output)
+			Warn("overiding recipie for target %q", rule.output)
 		}
 		ex.rules[rule.output] = rule
 	}
 
-	ex.build(er.rules[0].output)
+	_, err := ex.build(er.rules[0].output)
+	return err
 }
 
-func Exec(er *EvalResult) {
+func Exec(er *EvalResult) error {
 	ex := newExecutor()
-	ex.exec(er)
+	return ex.exec(er)
 }
diff --git a/main.go b/main.go
index 9f82548..658ac9a 100644
--- a/main.go
+++ b/main.go
@@ -10,6 +10,9 @@
 		stmt.show()
 	}
 
-	er := Eval(mk)
+	er, err := Eval(mk)
+	if err != nil {
+		panic(err)
+	}
 	Exec(er)
 }
diff --git a/parser.go b/parser.go
index 6a0aeb9..43666f8 100644
--- a/parser.go
+++ b/parser.go
@@ -4,6 +4,7 @@
 	"bufio"
 	"bytes"
 	"errors"
+	"fmt"
 	"io"
 	"os"
 )
@@ -115,7 +116,12 @@
 	return ast
 }
 
-func (p *parser) parse() (Makefile, error) {
+func (p *parser) parse() (mk Makefile, err error) {
+	defer func() {
+		if r := recover(); r != nil {
+			err = fmt.Errorf("panic: %v", r)
+		}
+	}()
 	for !p.done {
 		line := p.readLine()