Factor out rule parser

This is a preparation to implement complicated rules.
diff --git a/eval.go b/eval.go
index 060763c..c3dfd96 100644
--- a/eval.go
+++ b/eval.go
@@ -3,19 +3,9 @@
 import (
 	"bytes"
 	"fmt"
-	"regexp"
 	"strings"
 )
 
-type Rule struct {
-	output    string
-	inputs    []string
-	cmds      []string
-	filename  string
-	lineno    int
-	cmdLineno int
-}
-
 type EvalResult struct {
 	vars  map[string]string
 	rules []*Rule
@@ -137,6 +127,7 @@
 	ev.lineno = ast.lineno
 
 	line := ev.evalExpr(ast.expr)
+
 	if strings.TrimSpace(line) == "" {
 		if len(ast.cmds) > 0 {
 			Error(ast.filename, ast.cmdLineno, "*** commands commence before first target.")
@@ -149,27 +140,16 @@
 		lineno:    ast.lineno,
 		cmdLineno: ast.cmdLineno,
 	}
+	ev.curRule.parse(line)
 
-	colonIndex := strings.IndexByte(line, ':')
-	if colonIndex < 0 {
-		Error(ast.filename, ast.lineno, "*** missing separator.")
-	}
-
-	lhs := line[:colonIndex]
-	ev.curRule.output = lhs
-	rhs := strings.TrimSpace(line[colonIndex+1:])
-	if rhs != "" {
-		re, err := regexp.Compile(`\s+`)
-		if err != nil {
-			panic(err)
-		}
-		ev.curRule.inputs = re.Split(rhs, -1)
-	}
 	var cmds []string
 	for _, cmd := range ast.cmds {
 		cmds = append(cmds, ev.evalExpr(cmd))
 	}
-	Log("RULE: %s=%s (%d commands)", lhs, rhs, len(cmds))
+
+	// TODO: Pretty print.
+	//Log("RULE: %s=%s (%d commands)", lhs, rhs, len(cmds))
+
 	ev.curRule.cmds = cmds
 	ev.outRules = append(ev.outRules, ev.curRule)
 	ev.curRule = nil
diff --git a/rule_parser.go b/rule_parser.go
new file mode 100644
index 0000000..9c83fd8
--- /dev/null
+++ b/rule_parser.go
@@ -0,0 +1,25 @@
+package main
+
+import (
+	"strings"
+)
+
+type Rule struct {
+	output    string
+	inputs    []string
+	cmds      []string
+	filename  string
+	lineno    int
+	cmdLineno int
+}
+
+func (r *Rule) parse(line string) {
+	colonIndex := strings.IndexByte(line, ':')
+	if colonIndex < 0 {
+		Error(r.filename, r.lineno, "*** missing separator.")
+	}
+
+	lhs := line[:colonIndex]
+	r.output = lhs
+	r.inputs = splitSpaces(line[colonIndex+1:])
+}
diff --git a/strutil.go b/strutil.go
new file mode 100644
index 0000000..2ba69b7
--- /dev/null
+++ b/strutil.go
@@ -0,0 +1,31 @@
+package main
+
+import (
+	"regexp"
+	"strings"
+)
+
+var isStrutilInitialized bool
+var spacesRe *regexp.Regexp
+
+func maybeInitStrutil() {
+	if isStrutilInitialized {
+		return
+	}
+
+	var err error
+	spacesRe, err = regexp.Compile(`\s+`)
+	if err != nil {
+		panic(err)
+	}
+	isStrutilInitialized = true
+}
+
+func splitSpaces(s string) []string {
+	maybeInitStrutil()
+	s = strings.TrimSpace(s)
+	if s == "" {
+		return []string{}
+	}
+	return spacesRe.Split(s, -1)
+}
diff --git a/test/recipe_in_rule.mk b/test/recipe_in_rule.mk
index ec86dfb..9025de1 100644
--- a/test/recipe_in_rule.mk
+++ b/test/recipe_in_rule.mk
@@ -1,3 +1,4 @@
 # TODO(hamaji): Fix.
 
-all: ; echo PASS
+all: ; echo PASS1
+	echo PASS2