[C++] The first commit for C++ version

16 tests out of 169 are passing.
diff --git a/Makefile b/Makefile
index 68007db..57e82c5 100644
--- a/Makefile
+++ b/Makefile
@@ -12,24 +12,59 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-GOSRC = $(wildcard *.go)
+GO_SRCS:=$(wildcard *.go)
+CXX_SRCS:= \
+	ast.cc \
+	dep.cc \
+	eval.cc \
+	exec.cc \
+	file.cc \
+	file_cache.cc \
+	fileutil.cc \
+	func.cc \
+	main.cc \
+	parser.cc \
+	rule.cc \
+	string_piece.cc \
+	string_pool.cc \
+	stringprintf.cc \
+	strutil.cc \
+	value.cc \
+	var.cc
+CXX_TEST_SRCS:= \
+	$(wildcard *_test.cc)
+CXX_OBJS:=$(CXX_SRCS:.cc=.o)
+CXX_TEST_OBJS:=$(CXX_TEST_SRCS:.cc=.o)
+CXX_ALL_OBJS:=$(CXX_SRCS:.cc=.o) $(CXX_TEST_SRCS:.cc=.o)
+CXX_TEST_EXES:=$(CXX_TEST_OBJS:.o=)
+CXXFLAGS:=-g -W -Wall -MMD # -O
 
-all: kati go_test para
+all: kati para ckati $(CXX_TEST_EXES)
 
-kati: $(GOSRC)
+kati: $(GO_SRCS)
 	env $(shell go env) go build -o $@ *.go
 
-go_test: $(GOSRC) para
+ckati: $(CXX_OBJS)
+	$(CXX) -std=c++11 $(CXXFLAGS) -o $@ $(CXX_OBJS)
+
+$(CXX_ALL_OBJS): %.o: %.cc
+	$(CXX) -c -std=c++11 $(CXXFLAGS) -o $@ $<
+
+$(CXX_TEST_EXES): $(filter-out main.o,$(CXX_OBJS))
+$(CXX_TEST_EXES): %: %.o
+	$(CXX) $^ -o $@
+
+go_test: $(GO_SRCS) para
 	env $(shell go env) go test *.go
 
 para: para.cc
 	$(CXX) -std=c++11 -g -O -W -Wall -MMD -o $@ $<
 
-test: all
+test: all go_test
 	ruby runtest.rb
 
 clean:
-	rm -rf out kati
+	rm -rf out kati ckati *.o *.d
 
 .PHONY: test
 
diff --git a/ast.cc b/ast.cc
new file mode 100644
index 0000000..fdd4588
--- /dev/null
+++ b/ast.cc
@@ -0,0 +1,67 @@
+#include "ast.h"
+
+#include "eval.h"
+#include "stringprintf.h"
+#include "value.h"
+
+AST::AST() {}
+
+AST::~AST() {}
+
+string RuleAST::DebugString() const {
+  return StringPrintf("RuleAST(expr=%s term=%d after_term=%s)",
+                      expr->DebugString().c_str(),
+                      term,
+                      after_term->DebugString().c_str());
+}
+
+string AssignAST::DebugString() const {
+  const char* opstr = "???";
+  switch (op) {
+    case AssignOp::EQ: opstr = "EQ"; break;
+    case AssignOp::COLON_EQ: opstr = "COLON_EQ"; break;
+    case AssignOp::PLUS_EQ: opstr = "PLUS_EQ"; break;
+    case AssignOp::QUESTION_EQ: opstr = "QUESTION_EQ"; break;
+  }
+  const char* dirstr = "???";
+  switch (directive) {
+    case AssignDirective::NONE: dirstr = ""; break;
+    case AssignDirective::OVERRIDE: dirstr = "override"; break;
+    case AssignDirective::EXPORT: dirstr = "export"; break;
+  }
+  return StringPrintf("AssignAST(lhs=%s rhs=%s opstr=%s dir=%s)",
+                      lhs->DebugString().c_str(),
+                      rhs->DebugString().c_str(),
+                      opstr, dirstr);
+}
+
+string CommandAST::DebugString() const {
+  return StringPrintf("CommandAST(%s)",
+                      expr->DebugString().c_str());
+}
+
+RuleAST::~RuleAST() {
+  delete expr;
+  delete after_term;
+}
+
+void RuleAST::Eval(Evaluator* ev) const {
+  ev->EvalRule(this);
+}
+
+AssignAST::~AssignAST() {
+  delete lhs;
+  delete rhs;
+}
+
+void AssignAST::Eval(Evaluator* ev) const {
+  ev->EvalAssign(this);
+}
+
+CommandAST::~CommandAST() {
+  delete expr;
+}
+
+void CommandAST::Eval(Evaluator* ev) const {
+  ev->EvalCommand(this);
+}
diff --git a/ast.h b/ast.h
new file mode 100644
index 0000000..8c0ceea
--- /dev/null
+++ b/ast.h
@@ -0,0 +1,82 @@
+#ifndef AST_H_
+#define AST_H_
+
+#include <string>
+
+#include "loc.h"
+#include "string_piece.h"
+
+using namespace std;
+
+class Evaluator;
+struct Value;
+
+enum struct AssignOp {
+  EQ,
+  COLON_EQ,
+  PLUS_EQ,
+  QUESTION_EQ,
+};
+
+enum struct AssignDirective {
+  NONE,
+  OVERRIDE,
+  EXPORT,
+};
+
+struct AST {
+ public:
+  virtual ~AST();
+
+  Loc loc() const { return loc_; }
+  void set_loc(Loc loc) { loc_ = loc; }
+  StringPiece orig() const { return orig_; }
+
+  virtual void Eval(Evaluator* ev) const = 0;
+
+  virtual string DebugString() const = 0;
+
+ protected:
+  AST();
+
+ private:
+  Loc loc_;
+  StringPiece orig_;
+};
+
+struct RuleAST : public AST {
+  Value* expr;
+  char term;
+  Value* after_term;
+
+  virtual ~RuleAST();
+
+  virtual void Eval(Evaluator* ev) const;
+
+  virtual string DebugString() const;
+};
+
+struct AssignAST : public AST {
+  Value* lhs;
+  Value* rhs;
+  AssignOp op;
+  AssignDirective directive;
+
+  virtual ~AssignAST();
+
+  virtual void Eval(Evaluator* ev) const;
+
+  virtual string DebugString() const;
+};
+
+struct CommandAST : public AST {
+  Value* expr;
+
+  virtual ~CommandAST();
+
+  virtual void Eval(Evaluator* ev) const;
+
+  virtual string DebugString() const;
+};
+
+#endif  // AST_H_
diff --git a/dep.cc b/dep.cc
new file mode 100644
index 0000000..426b1a2
--- /dev/null
+++ b/dep.cc
@@ -0,0 +1,193 @@
+#include "dep.h"
+
+#include <memory>
+
+#include "log.h"
+#include "rule.h"
+#include "var.h"
+
+static vector<DepNode*>* g_dep_node_pool;
+
+DepNode::DepNode(StringPiece o, bool p)
+    : output(o),
+      has_rule(false),
+      is_order_only(false),
+      is_phony(p),
+      target_specific_vars(NULL) {
+  g_dep_node_pool->push_back(this);
+}
+
+class DepBuilder {
+ public:
+  DepBuilder(const vector<Rule*>& rules,
+             const Vars& vars,
+             const unordered_map<StringPiece, Vars*>& rule_vars)
+      : vars_(vars),
+        rule_vars_(rule_vars),
+        first_rule_(NULL) {
+    PopulateRules(rules);
+  }
+
+  void Build(vector<StringPiece> targets,
+             vector<DepNode*>* nodes) {
+    if (targets.empty()) {
+      if (!first_rule_) {
+        ERROR("*** No targets.");
+      }
+      CHECK(!first_rule_->outputs.empty());
+      targets.push_back(first_rule_->outputs[0]);
+    }
+
+    // TODO: LogStats?
+
+    for (StringPiece target : targets) {
+      unique_ptr<Vars> tsvs(new Vars);
+      DepNode* n = BuildPlan(target, "", tsvs.get());
+      nodes->push_back(n);
+    }
+  }
+
+ private:
+  void PopulateRules(const vector<Rule*>& rules) {
+    for (Rule* rule : rules) {
+      if (rule->outputs.empty()) {
+        PopulateImplicitRule(rule);
+      } else {
+        PopulateExplicitRule(rule);
+      }
+    }
+  }
+
+  void PopulateExplicitRule(Rule* rule) {
+    for (StringPiece output : rule->outputs) {
+      // isSuffixRule := db.populateSuffixRule(rule, output)
+
+
+      /*
+          if oldRule, present := db.rules[output]; present {
+     r := mergeRules(oldRule, rule, output, isSuffixRule)
+                                                         db.rules[output] = r
+                                                         } else {
+        db.rules[output] = rule
+            if db.firstRule == nil && !strings.HasPrefix(output, ".") {
+                db.firstRule = rule
+              }
+      }
+      */
+
+      auto p = rules_.insert(make_pair(output, rule));
+      if (p.second) {
+        if (!first_rule_ && output.get(0) != '.') {
+          first_rule_ = rule;
+        }
+      } else {
+        // TODO: merge
+        CHECK(false);
+      }
+    }
+  }
+
+  void PopulateImplicitRule(Rule*) {
+    CHECK(false);
+  }
+
+  Rule* LookupRule(StringPiece o) {
+    auto found = rules_.find(o);
+    if (found != rules_.end())
+      return found->second;
+    return NULL;
+  }
+
+  Vars* LookupRuleVars(StringPiece o) {
+    auto found = rule_vars_.find(o);
+    if (found != rule_vars_.end())
+      return found->second;
+    return NULL;
+  }
+
+  bool PickRule(StringPiece output, Rule** r, Vars** v) {
+    Rule* rule = LookupRule(output);
+    Vars* vars = LookupRuleVars(output);
+    *r = rule;
+    *v = vars;
+    if (rule) {
+      if (!rule->cmds.empty()) {
+        return true;
+      }
+    }
+    return rule;
+  }
+
+  DepNode* BuildPlan(StringPiece output, StringPiece needed_by, Vars* tsvs) {
+    LOG("BuildPlan: %s for %s",
+        output.as_string().c_str(),
+        needed_by.as_string().c_str());
+
+    auto found = done_.find(output);
+    if (found != done_.end()) {
+      return found->second;
+    }
+
+    DepNode* n = new DepNode(output, phony_[output]);
+    done_[output] = n;
+
+    Rule* rule;
+    Vars* vars;
+    if (!PickRule(output, &rule, &vars)) {
+      return n;
+    }
+
+    // TODO: Handle TSVs
+
+    for (StringPiece input : rule->inputs) {
+      if (rule->output_patterns.size() > 0) {
+        if (rule->output_patterns.size() > 1) {
+          ERROR("TODO: multiple output pattern is not supported yet");
+        }
+        ERROR("TODO");
+      }
+
+      n->actual_inputs.push_back(input);
+      DepNode* c = BuildPlan(input, output, tsvs);
+      n->deps.push_back(c);
+    }
+
+    // TODO: order only
+    n->has_rule = true;
+    n->cmds = rule->cmds;
+
+    return n;
+  }
+
+  unordered_map<StringPiece, Rule*> rules_;
+  const Vars& vars_;
+  const unordered_map<StringPiece, Vars*>& rule_vars_;
+
+  vector<Rule*> implicit_rules_;   // pattern=%. no prefix,suffix.
+  //vector<Rule*> iprefix_rules_;   // pattern=prefix%..  may have suffix
+  //vector<Rule*> isuffix_rules_;   // pattern=%suffix  no prefix
+
+  unordered_map<StringPiece, vector<Rule*>> suffix_rules_;
+  Rule* first_rule_;
+  unordered_map<StringPiece, DepNode*> done_;
+  unordered_map<StringPiece, bool> phony_;
+};
+
+void MakeDep(const vector<Rule*>& rules,
+             const Vars& vars,
+             const unordered_map<StringPiece, Vars*>& rule_vars,
+             const vector<StringPiece>& targets,
+             vector<DepNode*>* nodes) {
+  DepBuilder db(rules, vars, rule_vars);
+  db.Build(targets, nodes);
+}
+
+void InitDepNodePool() {
+  g_dep_node_pool = new vector<DepNode*>;
+}
+
+void QuitDepNodePool() {
+  for (DepNode* n : *g_dep_node_pool)
+    delete n;
+  delete g_dep_node_pool;
+}
diff --git a/dep.h b/dep.h
new file mode 100644
index 0000000..d829b10
--- /dev/null
+++ b/dep.h
@@ -0,0 +1,39 @@
+#ifndef DEP_H_
+#define DEP_H_
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "loc.h"
+#include "string_piece.h"
+
+class Rule;
+class Value;
+class Vars;
+
+struct DepNode {
+  DepNode(StringPiece output, bool is_phony);
+
+  StringPiece output;
+  vector<Value*> cmds;
+  vector<DepNode*> deps;
+  vector<DepNode*> parents;
+  bool has_rule;
+  bool is_order_only;
+  bool is_phony;
+  vector<StringPiece> actual_inputs;
+  Vars* target_specific_vars;
+  Loc loc;
+};
+
+void InitDepNodePool();
+void QuitDepNodePool();
+
+void MakeDep(const vector<Rule*>& rules,
+             const Vars& vars,
+             const unordered_map<StringPiece, Vars*>& rule_vars,
+             const vector<StringPiece>& targets,
+             vector<DepNode*>* nodes);
+
+#endif  // DEP_H_
diff --git a/eval.cc b/eval.cc
new file mode 100644
index 0000000..bd2e253
--- /dev/null
+++ b/eval.cc
@@ -0,0 +1,128 @@
+#include "eval.h"
+
+#include "ast.h"
+#include "file.h"
+#include "rule.h"
+#include "strutil.h"
+#include "value.h"
+#include "var.h"
+
+EvalResult::~EvalResult() {
+  for (Rule* r : rules)
+    delete r;
+  for (auto p : rule_vars)
+    delete p.second;
+  delete vars;
+}
+
+Evaluator::Evaluator(const Vars* vars)
+    : in_vars_(vars),
+      vars_(new Vars()),
+      last_rule_(NULL) {
+}
+
+Evaluator::~Evaluator() {
+  for (Rule* r : rules_) {
+    delete r;
+  }
+  delete vars_;
+  // for (auto p : rule_vars) {
+  //   delete p.second;
+  // }
+}
+
+void Evaluator::EvalAssign(const AssignAST* ast) {
+  loc_ = ast->loc();
+  last_rule_ = NULL;
+
+  const char* origin = "file";
+
+  StringPiece lhs = Intern(*ast->lhs->Eval(this));
+  Var* rhs = NULL;
+  switch (ast->op) {
+    case AssignOp::COLON_EQ:
+      rhs = new SimpleVar(ast->rhs->Eval(this), origin);
+      break;
+    case AssignOp::EQ:
+      rhs = new RecursiveVar(ast->rhs, origin);
+      break;
+    case AssignOp::PLUS_EQ: {
+      Var* prev = LookupVarInCurrentScope(lhs);
+      if (!prev->IsDefined()) {
+        rhs = new RecursiveVar(ast->rhs, origin);
+      } else {
+        // TODO
+        abort();
+      }
+      break;
+    }
+    case AssignOp::QUESTION_EQ: {
+      Var* prev = LookupVarInCurrentScope(lhs);
+      if (!prev->IsDefined()) {
+        rhs = new RecursiveVar(ast->rhs, origin);
+      } else {
+        // TODO
+        abort();
+      }
+      break;
+    }
+  }
+
+  LOG("Assign: %.*s=%s", SPF(lhs), rhs->DebugString().c_str());
+  vars_->Assign(lhs, rhs);
+}
+
+void Evaluator::EvalRule(const RuleAST* ast) {
+  loc_ = ast->loc();
+  last_rule_ = NULL;
+
+  shared_ptr<string> expr = ast->expr->Eval(this);
+  // See semicolon.mk.
+  if (expr->find_first_not_of(" \t\n;") == string::npos)
+    return;
+
+  Rule* rule = new Rule;
+  rule->loc = loc_;
+  rule->Parse(*expr);
+
+  LOG("Rule: %s", rule->DebugString().c_str());
+  rules_.push_back(rule);
+  last_rule_ = rule;
+}
+
+void Evaluator::EvalCommand(const CommandAST* ast) {
+  loc_ = ast->loc();
+
+  if (!last_rule_) {
+    // TODO:
+    ERROR("TODO");
+  }
+
+  last_rule_->cmds.push_back(ast->expr);
+  LOG("Command: %s", ast->expr->DebugString().c_str());
+}
+
+Var* Evaluator::LookupVar(StringPiece name) {
+  // TODO: TSV.
+  Var* v = vars_->Lookup(name);
+  if (v->IsDefined())
+    return v;
+  return in_vars_->Lookup(name);
+}
+
+Var* Evaluator::LookupVarInCurrentScope(StringPiece name) {
+  // TODO: TSV.
+  Var* v = vars_->Lookup(name);
+  if (v->IsDefined())
+    return v;
+  return in_vars_->Lookup(name);
+}
+
+EvalResult* Evaluator::GetEvalResult() {
+  EvalResult* er = new EvalResult;
+  er->rules.swap(rules_);
+  er->vars = vars_;
+  vars_ = NULL;
+  er->rule_vars.swap(rule_vars_);
+  return er;
+}
diff --git a/eval.h b/eval.h
new file mode 100644
index 0000000..78cc307
--- /dev/null
+++ b/eval.h
@@ -0,0 +1,62 @@
+#ifndef EVAL_H_
+#define EVAL_H_
+
+#include <unordered_map>
+#include <vector>
+
+#include "loc.h"
+#include "string_piece.h"
+
+using namespace std;
+
+class AssignAST;
+class CommandAST;
+class Makefile;
+class Rule;
+class RuleAST;
+class Var;
+class Vars;
+
+struct EvalResult {
+  ~EvalResult();
+  vector<Rule*> rules;
+  Vars* vars;
+  unordered_map<StringPiece, Vars*> rule_vars;
+  // TODO: read_mks
+  unordered_map<StringPiece, bool> exports;
+};
+
+class Evaluator {
+ public:
+  Evaluator(const Vars* vars);
+  ~Evaluator();
+
+  void EvalAssign(const AssignAST* ast);
+  void EvalRule(const RuleAST* ast);
+  void EvalCommand(const CommandAST* ast);
+
+  Var* LookupVar(StringPiece name);
+  // For target specific variables.
+  Var* LookupVarInCurrentScope(StringPiece name);
+
+  EvalResult* GetEvalResult();
+
+#if 0
+  const vector<Rule*>& rules() const { return rules_; }
+  const Vars* vars() const { return vars_; }
+  const unordered_map<StringPiece, Vars*>& rule_vars() const {
+    return rule_vars_;
+  }
+#endif
+
+ private:
+  const Vars* in_vars_;
+  Vars* vars_;
+  unordered_map<StringPiece, Vars*> rule_vars_;
+  vector<Rule*> rules_;
+  Rule* last_rule_;
+
+  Loc loc_;
+};
+
+#endif  // EVAL_H_
diff --git a/exec.cc b/exec.cc
new file mode 100644
index 0000000..82f49b0
--- /dev/null
+++ b/exec.cc
@@ -0,0 +1,85 @@
+#include "exec.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <memory>
+#include <unordered_map>
+
+#include "dep.h"
+#include "eval.h"
+#include "log.h"
+#include "string_piece.h"
+#include "value.h"
+
+namespace {
+
+struct Runner {
+  Runner()
+      : echo(true), ignore_error(false) {
+  }
+  StringPiece output;
+  shared_ptr<string> cmd;
+  bool echo;
+  bool ignore_error;
+  //StringPiece shell;
+};
+
+}  // namespace
+
+class Executor {
+ public:
+  explicit Executor(const Vars* vars)
+      : vars_(vars) {
+  }
+
+  void ExecNode(DepNode* n, DepNode* needed_by) {
+    if (done_[n->output])
+      return;
+
+    LOG("ExecNode: %s for %s",
+        n->output.as_string().c_str(),
+        needed_by ? needed_by->output.as_string().c_str() : "(null)");
+
+    for (DepNode* d : n->deps) {
+#if 0
+      if (d.is_order_only && exists(d->output)) {
+      }
+#endif
+      ExecNode(d, n);
+    }
+
+    vector<Runner*> runners;
+    CreateRunners(n, &runners);
+    for (Runner* runner : runners) {
+      if (runner->echo) {
+        printf("%s\n", runner->cmd->c_str());
+        fflush(stdout);
+      }
+      system(runner->cmd->c_str());
+      delete runner;
+    }
+  }
+
+  void CreateRunners(DepNode* n, vector<Runner*>* runners) {
+    unique_ptr<Evaluator> ev(new Evaluator(vars_));
+    for (Value* v : n->cmds) {
+      Runner* runner = new Runner;
+      runner->output = n->output;
+      runner->cmd = v->Eval(ev.get());
+      runners->push_back(runner);
+    }
+  }
+
+ private:
+  const Vars* vars_;
+  unordered_map<StringPiece, bool> done_;
+
+};
+
+void Exec(const vector<DepNode*>& roots, const Vars* vars) {
+  unique_ptr<Executor> executor(new Executor(vars));
+  for (DepNode* root : roots) {
+    executor->ExecNode(root, NULL);
+  }
+}
diff --git a/exec.h b/exec.h
new file mode 100644
index 0000000..0f5944a
--- /dev/null
+++ b/exec.h
@@ -0,0 +1,13 @@
+#ifndef EXEC_H_
+#define EXEC_H_
+
+#include <vector>
+
+using namespace std;
+
+class DepNode;
+class Vars;
+
+void Exec(const vector<DepNode*>& roots, const Vars* vars);
+
+#endif  // EXEC_H_
diff --git a/file.cc b/file.cc
new file mode 100644
index 0000000..9f3c147
--- /dev/null
+++ b/file.cc
@@ -0,0 +1,45 @@
+#include "file.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "ast.h"
+#include "log.h"
+#include "parser.h"
+
+Makefile::Makefile(const string& filename)
+    : len_(0), mtime_(0), filename_(filename) {
+  int fd = open(filename.c_str(), O_RDONLY);
+  if (fd < 0) {
+    return;
+  }
+
+  struct stat st;
+  if (fstat(fd, &st) < 0) {
+    PERROR("fstat failed for %s", filename.c_str());
+  }
+
+  len_ = st.st_size;
+  mtime_ = st.st_mtime;
+  buf_ = new char[len_];
+  ssize_t r = read(fd, buf_, len_);
+  if (r != static_cast<ssize_t>(len_)) {
+    if (r < 0)
+      PERROR("read failed for %s", filename.c_str());
+    ERROR("Unexpected read length=%zd expected=%zu", r, len_);
+  }
+
+  if (close(fd) < 0) {
+    PERROR("close failed for %s", filename.c_str());
+  }
+
+  Parse(this);
+}
+
+Makefile::~Makefile() {
+  delete[] buf_;
+  for (AST* ast : asts_)
+    delete ast;
+}
diff --git a/file.h b/file.h
new file mode 100644
index 0000000..a053073
--- /dev/null
+++ b/file.h
@@ -0,0 +1,37 @@
+#ifndef FILE_H_
+#define FILE_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "string_pool.h"
+
+using namespace std;
+
+class AST;
+
+class Makefile {
+ public:
+  explicit Makefile(const string& filename);
+  ~Makefile();
+
+  const char* buf() const { return buf_; }
+  size_t len() const { return len_; }
+  const string& filename() const { return filename_; }
+
+  StringPool* mutable_pool() { return &pool_; }
+  const vector<AST*>& asts() const { return asts_; }
+  vector<AST*>* mutable_asts() { return &asts_; }
+
+ private:
+  char* buf_;
+  size_t len_;
+  uint64_t mtime_;
+  string filename_;
+  StringPool pool_;
+  vector<AST*> asts_;
+};
+
+#endif  // FILE_H_
diff --git a/file_cache.cc b/file_cache.cc
new file mode 100644
index 0000000..eb64328
--- /dev/null
+++ b/file_cache.cc
@@ -0,0 +1,36 @@
+#include "file_cache.h"
+
+#include <unordered_map>
+
+#include "file.h"
+
+MakefileCacheManager::MakefileCacheManager() {}
+
+MakefileCacheManager::~MakefileCacheManager() {}
+
+class MakefileCacheManagerImpl : public MakefileCacheManager {
+ public:
+  virtual ~MakefileCacheManagerImpl() {
+    for (auto p : cache_) {
+      delete p.second;
+    }
+  }
+
+  virtual Makefile* ReadMakefile(const string& filename) override {
+    Makefile* result = NULL;
+    auto p = cache_.insert(make_pair(filename, result));
+    if (p.second) {
+      p.first->second = result = new Makefile(filename);
+    } else {
+      result = p.first->second;
+    }
+    return result;
+  }
+
+private:
+  unordered_map<string, Makefile*> cache_;
+};
+
+MakefileCacheManager* NewMakefileCacheManager() {
+  return new MakefileCacheManagerImpl();
+}
diff --git a/file_cache.h b/file_cache.h
new file mode 100644
index 0000000..7b3b3ff
--- /dev/null
+++ b/file_cache.h
@@ -0,0 +1,23 @@
+#ifndef FILE_CACHE_H_
+#define FILE_CACHE_H_
+
+#include <string>
+
+using namespace std;
+
+class Makefile;
+
+class MakefileCacheManager {
+ public:
+  virtual ~MakefileCacheManager();
+
+  virtual Makefile* ReadMakefile(const string& filename) = 0;
+
+ protected:
+  MakefileCacheManager();
+
+};
+
+MakefileCacheManager* NewMakefileCacheManager();
+
+#endif  // FILE_CACHE_H_
diff --git a/fileutil.cc b/fileutil.cc
new file mode 100644
index 0000000..51f080e
--- /dev/null
+++ b/fileutil.cc
@@ -0,0 +1,20 @@
+#include "fileutil.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "log.h"
+
+bool Exists(StringPiece filename) {
+  CHECK(filename.size() < PATH_MAX);
+  char buf[PATH_MAX+1];
+  memcpy(buf, filename.data(), filename.size());
+  buf[filename.size()] = 0;
+  struct stat st;
+  if (stat(buf, &st) < 0) {
+    return false;
+  }
+  return true;
+}
diff --git a/fileutil.h b/fileutil.h
new file mode 100644
index 0000000..31b7318
--- /dev/null
+++ b/fileutil.h
@@ -0,0 +1,8 @@
+#ifndef FILEUTIL_H_
+#define FILEUTIL_H_
+
+#include "string_piece.h"
+
+bool Exists(StringPiece f);
+
+#endif  // FILEUTIL_H_
diff --git a/func.cc b/func.cc
new file mode 100644
index 0000000..2910193
--- /dev/null
+++ b/func.cc
@@ -0,0 +1,44 @@
+#include "func.h"
+
+#include <stdio.h>
+
+#include <unordered_map>
+
+#include "log.h"
+#include "strutil.h"
+
+namespace {
+
+void BuiltinInfoFunc(const vector<Value*>& args, Evaluator* ev, string*) {
+  shared_ptr<string> a = args[0]->Eval(ev);
+  printf("%s\n", a->c_str());
+  fflush(stdout);
+}
+
+FuncInfo g_func_infos[] = {
+  { "info", &BuiltinInfoFunc, 1 },
+};
+
+unordered_map<StringPiece, FuncInfo*>* g_func_info_map;
+
+}  // namespace
+
+void InitFuncTable() {
+  g_func_info_map = new unordered_map<StringPiece, FuncInfo*>;
+  for (size_t i = 0; i < sizeof(g_func_infos) / sizeof(g_func_infos[0]); i++) {
+    FuncInfo* fi = &g_func_infos[i];
+    bool ok = g_func_info_map->insert(make_pair(Intern(fi->name), fi)).second;
+    CHECK(ok);
+  }
+}
+
+void QuitFuncTable() {
+  delete g_func_info_map;
+}
+
+FuncInfo* GetFuncInfo(StringPiece name) {
+  auto found = g_func_info_map->find(name);
+  if (found == g_func_info_map->end())
+    return NULL;
+  return found->second;
+}
diff --git a/func.h b/func.h
new file mode 100644
index 0000000..cd5f3b3
--- /dev/null
+++ b/func.h
@@ -0,0 +1,21 @@
+#ifndef FUNC_H_
+#define FUNC_H_
+
+#include <vector>
+
+#include "value.h"
+
+using namespace std;
+
+struct FuncInfo {
+  const char* name;
+  void (*func)(const vector<Value*>& args, Evaluator* ev, string* s);
+  int arity;
+};
+
+void InitFuncTable();
+void QuitFuncTable();
+
+FuncInfo* GetFuncInfo(StringPiece name);
+
+#endif  // FUNC_H_
diff --git a/loc.h b/loc.h
new file mode 100644
index 0000000..2884915
--- /dev/null
+++ b/loc.h
@@ -0,0 +1,19 @@
+#ifndef LOC_H_
+#define LOC_H_
+
+#include <string>
+
+#include "stringprintf.h"
+
+struct Loc {
+  Loc()
+      : filename(0), lineno(-1) {}
+  Loc(const char* f, int l)
+      : filename(f), lineno(l) {
+  }
+
+  const char* filename;
+  int lineno;
+};
+
+#endif  // LOC_H_
diff --git a/log.h b/log.h
new file mode 100644
index 0000000..6fb0326
--- /dev/null
+++ b/log.h
@@ -0,0 +1,31 @@
+#ifndef LOG_H_
+#define LOG_H_
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define LOG(fmt, ...) do {                                              \
+    char buf[999];                                                      \
+    sprintf(buf, fmt, __VA_ARGS__);                                     \
+    fprintf(stderr, "*kati*: %s\n", buf);                               \
+  } while(0)
+
+#define PERROR(...) do {                                        \
+    char buf[999];                                              \
+    sprintf(buf, __VA_ARGS__);                                  \
+    fprintf(stderr, "%s: %s\n", buf, strerror(errno));          \
+    exit(1);                                                    \
+  } while (0)
+
+#define ERROR(...) do {                                         \
+    char buf[999];                                              \
+    sprintf(buf, __VA_ARGS__);                                  \
+    fprintf(stderr, "%s\n", buf);                               \
+    exit(1);                                                    \
+  } while (0)
+
+#define CHECK(c) if (!(c)) ERROR("%s:%d: %s", __FILE__, __LINE__, #c)
+
+#endif  // LOG_H_
diff --git a/main.cc b/main.cc
new file mode 100644
index 0000000..cdb06ee
--- /dev/null
+++ b/main.cc
@@ -0,0 +1,96 @@
+#include <stdio.h>
+#include <string.h>
+
+#include "ast.h"
+#include "dep.h"
+#include "eval.h"
+#include "exec.h"
+#include "file.h"
+#include "file_cache.h"
+#include "fileutil.h"
+#include "func.h"
+#include "log.h"
+#include "string_piece.h"
+#include "strutil.h"
+#include "var.h"
+
+static const char* g_makefile;
+
+static void ParseCommandLine(int argc, char* argv[],
+                             vector<StringPiece>* targets) {
+  for (int i = 1; i < argc; i++) {
+    const char* arg = argv[i];
+    if (!strcmp(arg, "-f")) {
+      g_makefile = argv[++i];
+    } else if (arg[0] == '-') {
+      ERROR("Unknown flag: %s", arg);
+    } else {
+      targets->push_back(Intern(arg));
+    }
+  }
+}
+
+static void Init() {
+  InitSymtab();
+  InitFuncTable();
+  InitDepNodePool();
+
+  if (g_makefile == NULL) {
+    if (Exists("GNUmakefile")) {
+      g_makefile = "GNUmakefile";
+    } else if (Exists("makefile")) {
+      g_makefile = "makefile";
+    } else if (Exists("Makefile")) {
+      g_makefile = "Makefile";
+    } else {
+      ERROR("*** No targets specified and no makefile found.");
+    }
+  }
+}
+
+static void Quit() {
+  QuitDepNodePool();
+  QuitFuncTable();
+  QuitSymtab();
+}
+
+static int Run(const vector<StringPiece>& targets) {
+  MakefileCacheManager* cache_mgr = NewMakefileCacheManager();
+  Makefile* mk = cache_mgr->ReadMakefile(g_makefile);
+
+  // TODO: Fill env, etc.
+  Vars* vars = new Vars();
+  Evaluator* ev = new Evaluator(vars);
+  for (AST* ast : mk->asts()) {
+    LOG("%s", ast->DebugString().c_str());
+
+    ast->Eval(ev);
+  }
+
+  EvalResult* er = ev->GetEvalResult();
+  for (auto p : *er->vars) {
+    vars->Assign(p.first, p.second);
+  }
+  er->vars->clear();
+
+  vector<DepNode*> nodes;
+  MakeDep(er->rules, *vars, er->rule_vars, targets, &nodes);
+
+  Exec(nodes, vars);
+
+  delete er;
+  delete ev;
+  delete vars;
+  delete cache_mgr;
+
+  return mk != 0;
+}
+
+int main(int argc, char* argv[]) {
+  Init();
+  vector<StringPiece> targets;
+  ParseCommandLine(argc, argv, &targets);
+  int r = Run(targets);
+  Quit();
+  return r;
+}
diff --git a/parser.cc b/parser.cc
new file mode 100644
index 0000000..526b8fe
--- /dev/null
+++ b/parser.cc
@@ -0,0 +1,164 @@
+#include "parser.h"
+
+#include "ast.h"
+#include "file.h"
+#include "loc.h"
+#include "log.h"
+#include "string_piece.h"
+#include "value.h"
+
+enum struct ParserState {
+  NOT_AFTER_RULE = 0,
+  AFTER_RULE,
+  MAYBE_AFTER_RULE,
+};
+
+class Parser {
+ public:
+  Parser(StringPiece buf, const char* filename, vector<AST*>* asts)
+      : buf_(buf),
+        state_(ParserState::NOT_AFTER_RULE),
+        out_asts_(asts),
+        loc_(filename, 0),
+        fixed_lineno_(false) {
+  }
+
+  ~Parser() {
+  }
+
+  void Parse() {
+    l_ = 0;
+
+    for (l_ = 0; l_ < buf_.size();) {
+      if (!fixed_lineno_)
+        ++loc_.lineno;
+      size_t lf_cnt = 0;
+      size_t e = FindEndOfLine(&lf_cnt);
+      StringPiece line(buf_.data() + l_, e - l_);
+      ParseLine(line);
+      if (e == buf_.size())
+        break;
+
+      l_ = e + 1;
+      loc_.lineno += lf_cnt;
+    }
+  }
+
+ private:
+  void Error(const string& msg) {
+    ERROR("%s:%d: %s", loc_.filename, loc_.lineno, msg.c_str());
+  }
+
+  size_t FindEndOfLine(size_t* lf_cnt) {
+    size_t e = l_;
+    bool prev_backslash = false;
+    for (; e < buf_.size(); e++) {
+      char c = buf_[e];
+      if (c == '\\') {
+        prev_backslash = !prev_backslash;
+      } else if (c == '\n') {
+        ++*lf_cnt;
+        if (!prev_backslash) {
+          return e;
+        }
+      } else if (c != '\r') {
+        prev_backslash = false;
+      }
+    }
+    return e;
+  }
+
+  void ParseLine(StringPiece line) {
+    if (line.empty() || (line.size() == 1 && line[0] == '\r'))
+      return;
+
+    if (line[0] == '\t' && state_ != ParserState::NOT_AFTER_RULE) {
+      CommandAST* ast = new CommandAST();
+      ast->expr = ParseExpr(line.substr(1), true);
+      out_asts_->push_back(ast);
+      return;
+    }
+
+    // TODO: directive.
+
+    size_t sep = line.find_first_of(STRING_PIECE("=:"));
+    if (sep == string::npos) {
+      ParseRuleAST(line, sep);
+    } else if (line[sep] == '=') {
+      ParseAssignAST(line, sep);
+    } else if (line.get(sep+1) == '=') {
+      ParseAssignAST(line, sep+1);
+    } else if (line[sep] == ':') {
+      ParseRuleAST(line, sep);
+    } else {
+      CHECK(false);
+    }
+  }
+
+  void ParseRuleAST(StringPiece line, size_t sep) {
+    const bool is_rule = line.find(':') != string::npos;
+    RuleAST* ast = new RuleAST;
+    ast->set_loc(loc_);
+
+    size_t found = line.substr(sep + 1).find_first_of("=;");
+    if (found != string::npos) {
+      found += sep + 1;
+      ast->term = line[found];
+      ast->after_term = ParseExpr(line.substr(found + 1).StripLeftSpaces(),
+                                  ast->term == ';');
+      ast->expr = ParseExpr(line.substr(0, found).StripSpaces(), false);
+    } else {
+      ast->term = 0;
+      ast->after_term = NULL;
+      ast->expr = ParseExpr(line.StripSpaces(), false);
+    }
+    out_asts_->push_back(ast);
+    state_ = is_rule ? ParserState::AFTER_RULE : ParserState::MAYBE_AFTER_RULE;
+  }
+
+  void ParseAssignAST(StringPiece line, size_t sep) {
+    if (sep == 0)
+      Error("*** empty variable name ***");
+    AssignOp op = AssignOp::EQ;
+    size_t lhs_end = sep;
+    switch (line[sep-1]) {
+      case ':':
+        lhs_end--;
+        op = AssignOp::COLON_EQ;
+        break;
+      case '+':
+        lhs_end--;
+        op = AssignOp::PLUS_EQ;
+        break;
+      case '?':
+        lhs_end--;
+        op = AssignOp::QUESTION_EQ;
+        break;
+    }
+
+    AssignAST* ast = new AssignAST;
+    ast->set_loc(loc_);
+    ast->lhs = ParseExpr(line.substr(0, lhs_end).StripSpaces(), false);
+    ast->rhs = ParseExpr(line.substr(sep + 1).StripLeftSpaces(), false);
+    ast->op = op;
+    ast->directive = AssignDirective::NONE;
+    out_asts_->push_back(ast);
+    state_ = ParserState::NOT_AFTER_RULE;
+  }
+
+  StringPiece buf_;
+  size_t l_;
+  ParserState state_;
+
+  vector<AST*>* out_asts_;
+
+  Loc loc_;
+  bool fixed_lineno_;
+};
+
+void Parse(Makefile* mk) {
+  Parser parser(StringPiece(mk->buf(), mk->len()),
+                mk->filename().c_str(),
+                mk->mutable_asts());
+  parser.Parse();
+}
diff --git a/parser.h b/parser.h
new file mode 100644
index 0000000..b82741b
--- /dev/null
+++ b/parser.h
@@ -0,0 +1,8 @@
+#ifndef PARSER_H_
+#define PARSER_H_
+
+class Makefile;
+
+void Parse(Makefile* mk);
+
+#endif  // PARSER_H_
diff --git a/rule.cc b/rule.cc
new file mode 100644
index 0000000..54280a3
--- /dev/null
+++ b/rule.cc
@@ -0,0 +1,88 @@
+#include "rule.h"
+
+#include "log.h"
+#include "stringprintf.h"
+#include "strutil.h"
+#include "value.h"
+
+// Strip leading sequences of './' from file names, so that ./file
+// and file are considered to be the same file.
+// From http://www.gnu.org/software/make/manual/make.html#Features
+StringPiece TrimLeadingCurdir(StringPiece s) {
+  if (s.substr(0, 2) != "./")
+    return s;
+  return s.substr(2);
+}
+
+static void ParseInputs(Rule* r, StringPiece s) {
+  bool is_order_only = false;
+  for (StringPiece input : WordScanner(s)) {
+    if (input == "|") {
+      is_order_only = true;
+      continue;
+    }
+    input = Intern(TrimLeadingCurdir(input));
+    if (is_order_only) {
+      r->order_only_inputs.push_back(input);
+    } else {
+      r->inputs.push_back(input);
+    }
+  }
+}
+
+Rule::Rule()
+    : is_double_colon(false),
+      is_suffix_rule(false),
+      cmd_lineno(0) {
+}
+
+void Rule::Parse(StringPiece line) {
+  size_t index = line.find(':');
+  if (index == string::npos) {
+    Error("*** missing separator.");
+  }
+
+  StringPiece first = line.substr(0, index);
+  // TODO: isPattern?
+  if (false) {
+  } else {
+    for (StringPiece tok : WordScanner(first)) {
+      outputs.push_back(Intern(TrimLeadingCurdir(tok)));
+    }
+  }
+
+  index++;
+  if (line.get(index) == ':') {
+    is_double_colon = true;
+    index++;
+  }
+
+  StringPiece rest = line.substr(index);
+
+  // TODO: TSV
+  //if (
+
+  index = rest.find(':');
+  if (index == string::npos) {
+    ParseInputs(this, rest);
+    return;
+  }
+}
+
+string Rule::DebugString() const {
+  vector<string> v;
+  v.push_back(StringPrintf("outputs=[%s]", JoinStrings(outputs, ",").c_str()));
+  v.push_back(StringPrintf("inputs=[%s]", JoinStrings(inputs, ",").c_str()));
+  if (!order_only_inputs.empty()) {
+    v.push_back(StringPrintf("order_only_inputs=[%s]",
+                             JoinStrings(order_only_inputs, ",").c_str()));
+  }
+  if (is_double_colon)
+    v.push_back("is_double_colon");
+  if (is_suffix_rule)
+    v.push_back("is_suffix_rule");
+  if (!cmds.empty()) {
+    v.push_back(StringPrintf("cmds=[%s]", JoinValues(cmds, ",").c_str()));
+  }
+  return JoinStrings(v, " ");
+}
diff --git a/rule.h b/rule.h
new file mode 100644
index 0000000..071ad48
--- /dev/null
+++ b/rule.h
@@ -0,0 +1,37 @@
+#ifndef RULE_H_
+#define RULE_H_
+
+#include <vector>
+
+#include "loc.h"
+#include "log.h"
+#include "string_piece.h"
+
+using namespace std;
+
+class Value;
+
+class Rule {
+ public:
+  Rule();
+  void Parse(StringPiece line);
+
+  string DebugString() const;
+
+  vector<StringPiece> outputs;
+  vector<StringPiece> inputs;
+  vector<StringPiece> order_only_inputs;
+  vector<string> output_patterns;
+  bool is_double_colon;
+  bool is_suffix_rule;
+  vector<Value*> cmds;
+  Loc loc;
+  int cmd_lineno;
+
+ private:
+  void Error(const string& msg) {
+    ERROR("%s:%d: %s", loc.filename, loc.lineno, msg.c_str());
+  }
+};
+
+#endif  // RULE_H_
diff --git a/runtest.rb b/runtest.rb
index 2313a10..e3f4e6c 100755
--- a/runtest.rb
+++ b/runtest.rb
@@ -18,6 +18,9 @@
 
 if ARGV[0] == '-s'
   test_serialization = true
+elsif ARGV[0] == '-c'
+  ckati = true
+  ARGV.shift
 end
 
 def get_output_filenames
@@ -110,6 +113,11 @@
   expected_failure = c =~ /\A# TODO/
 
   run_in_testdir(mk) do |name|
+    # TODO: Fix
+    if name =~ /eval_assign/ && ckati
+      next
+    end
+
     File.open("Makefile", 'w') do |ofile|
       ofile.print(c)
     end
@@ -135,6 +143,9 @@
     testcases.each do |tc|
       json = "#{tc.empty? ? 'test' : tc}"
       cmd = "../../kati -save_json=#{json}.json -kati_log #{tc} 2>&1"
+      if ckati
+        cmd = "../../ckati #{tc} 2>&1"
+      end
       res = IO.popen(cmd, 'r:binary', &:read)
       res = normalize_kati_log(res)
       output += "=== #{tc} ===\n" + res
@@ -251,7 +262,7 @@
 puts
 
 if !unexpected_passes.empty? || !failures.empty?
-  puts 'FAIL!'
+  puts "FAIL! (#{failures.size + unexpected_passes.size} fails #{passes.size} passes)"
 else
   puts 'PASS!'
 end
diff --git a/string_piece.cc b/string_piece.cc
new file mode 100644
index 0000000..074acf3
--- /dev/null
+++ b/string_piece.cc
@@ -0,0 +1,231 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// Copied from strings/stringpiece.cc with modifications
+
+#include <ctype.h>
+#include <limits.h>
+
+#include <algorithm>
+#include <ostream>
+
+#include "string_piece.h"
+
+typedef StringPiece::size_type size_type;
+
+bool operator==(const StringPiece& x, const StringPiece& y) {
+  if (x.size() != y.size())
+    return false;
+
+  return StringPiece::wordmemcmp(x.data(), y.data(), x.size()) == 0;
+}
+
+void StringPiece::CopyToString(std::string* target) const {
+  target->assign(!empty() ? data() : "", size());
+}
+
+void StringPiece::AppendToString(std::string* target) const {
+  if (!empty())
+    target->append(data(), size());
+}
+
+size_type StringPiece::copy(char* buf, size_type n, size_type pos) const {
+  size_type ret = std::min(length_ - pos, n);
+  memcpy(buf, ptr_ + pos, ret);
+  return ret;
+}
+
+size_type StringPiece::find(const StringPiece& s, size_type pos) const {
+  if (pos > length_)
+    return npos;
+
+  const char* result = std::search(ptr_ + pos, ptr_ + length_,
+                                   s.ptr_, s.ptr_ + s.length_);
+  const size_type xpos = result - ptr_;
+  return xpos + s.length_ <= length_ ? xpos : npos;
+}
+
+size_type StringPiece::find(char c, size_type pos) const {
+  if (pos >= length_)
+    return npos;
+
+  const char* result = std::find(ptr_ + pos, ptr_ + length_, c);
+  return result != ptr_ + length_ ? static_cast<size_t>(result - ptr_) : npos;
+}
+
+size_type StringPiece::rfind(const StringPiece& s, size_type pos) const {
+  if (length_ < s.length_)
+    return npos;
+
+  if (s.empty())
+    return std::min(length_, pos);
+
+  const char* last = ptr_ + std::min(length_ - s.length_, pos) + s.length_;
+  const char* result = std::find_end(ptr_, last, s.ptr_, s.ptr_ + s.length_);
+  return result != last ? static_cast<size_t>(result - ptr_) : npos;
+}
+
+size_type StringPiece::rfind(char c, size_type pos) const {
+  if (length_ == 0)
+    return npos;
+
+  for (size_type i = std::min(pos, length_ - 1); ; --i) {
+    if (ptr_[i] == c)
+      return i;
+    if (i == 0)
+      break;
+  }
+  return npos;
+}
+
+// For each character in characters_wanted, sets the index corresponding
+// to the ASCII code of that character to 1 in table.  This is used by
+// the find_.*_of methods below to tell whether or not a character is in
+// the lookup table in constant time.
+// The argument `table' must be an array that is large enough to hold all
+// the possible values of an unsigned char.  Thus it should be be declared
+// as follows:
+//   bool table[UCHAR_MAX + 1]
+static inline void BuildLookupTable(const StringPiece& characters_wanted,
+                                    bool* table) {
+  const size_type length = characters_wanted.length();
+  const char* const data = characters_wanted.data();
+  for (size_type i = 0; i < length; ++i) {
+    table[static_cast<unsigned char>(data[i])] = true;
+  }
+}
+
+size_type StringPiece::find_first_of(const StringPiece& s,
+                                     size_type pos) const {
+  if (length_ == 0 || s.length_ == 0)
+    return npos;
+
+  // Avoid the cost of BuildLookupTable() for a single-character search.
+  if (s.length_ == 1)
+    return find_first_of(s.ptr_[0], pos);
+
+  bool lookup[UCHAR_MAX + 1] = { false };
+  BuildLookupTable(s, lookup);
+  for (size_type i = pos; i < length_; ++i) {
+    if (lookup[static_cast<unsigned char>(ptr_[i])]) {
+      return i;
+    }
+  }
+  return npos;
+}
+
+size_type StringPiece::find_first_not_of(const StringPiece& s,
+                                         size_type pos) const {
+  if (length_ == 0)
+    return npos;
+
+  if (s.length_ == 0)
+    return 0;
+
+  // Avoid the cost of BuildLookupTable() for a single-character search.
+  if (s.length_ == 1)
+    return find_first_not_of(s.ptr_[0], pos);
+
+  bool lookup[UCHAR_MAX + 1] = { false };
+  BuildLookupTable(s, lookup);
+  for (size_type i = pos; i < length_; ++i) {
+    if (!lookup[static_cast<unsigned char>(ptr_[i])]) {
+      return i;
+    }
+  }
+  return npos;
+}
+
+size_type StringPiece::find_first_not_of(char c, size_type pos) const {
+  if (length_ == 0)
+    return npos;
+
+  for (; pos < length_; ++pos) {
+    if (ptr_[pos] != c) {
+      return pos;
+    }
+  }
+  return npos;
+}
+
+size_type StringPiece::find_last_of(const StringPiece& s, size_type pos) const {
+  if (length_ == 0 || s.length_ == 0)
+    return npos;
+
+  // Avoid the cost of BuildLookupTable() for a single-character search.
+  if (s.length_ == 1)
+    return find_last_of(s.ptr_[0], pos);
+
+  bool lookup[UCHAR_MAX + 1] = { false };
+  BuildLookupTable(s, lookup);
+  for (size_type i = std::min(pos, length_ - 1); ; --i) {
+    if (lookup[static_cast<unsigned char>(ptr_[i])])
+      return i;
+    if (i == 0)
+      break;
+  }
+  return npos;
+}
+
+size_type StringPiece::find_last_not_of(const StringPiece& s,
+                                        size_type pos) const {
+  if (length_ == 0)
+    return npos;
+
+  size_type i = std::min(pos, length_ - 1);
+  if (s.length_ == 0)
+    return i;
+
+  // Avoid the cost of BuildLookupTable() for a single-character search.
+  if (s.length_ == 1)
+    return find_last_not_of(s.ptr_[0], pos);
+
+  bool lookup[UCHAR_MAX + 1] = { false };
+  BuildLookupTable(s, lookup);
+  for (; ; --i) {
+    if (!lookup[static_cast<unsigned char>(ptr_[i])])
+      return i;
+    if (i == 0)
+      break;
+  }
+  return npos;
+}
+
+size_type StringPiece::find_last_not_of(char c, size_type pos) const {
+  if (length_ == 0)
+    return npos;
+
+  for (size_type i = std::min(pos, length_ - 1); ; --i) {
+    if (ptr_[i] != c)
+      return i;
+    if (i == 0)
+      break;
+  }
+  return npos;
+}
+
+StringPiece StringPiece::substr(size_type pos, size_type n) const {
+  if (pos > length_) pos = length_;
+  if (n > length_ - pos) n = length_ - pos;
+  return StringPiece(ptr_ + pos, n);
+}
+
+const StringPiece::size_type StringPiece::npos = size_type(-1);
+
+StringPiece StringPiece::StripLeftSpaces() const {
+  size_t i = 0;
+  while (i < size() && isspace(ptr_[i]))
+    i++;
+  return StringPiece(ptr_ + i, size() - i);
+}
+
+StringPiece StringPiece::StripRightSpaces() const {
+  size_t i = 0;
+  while (i < size() && isspace(ptr_[size() - 1 - i]))
+    i++;
+  return StringPiece(ptr_, size() - i);
+}
+
+StringPiece StringPiece::StripSpaces() const {
+  return StripLeftSpaces().StripRightSpaces();
+}
diff --git a/string_piece.h b/string_piece.h
new file mode 100644
index 0000000..7e20e13
--- /dev/null
+++ b/string_piece.h
@@ -0,0 +1,217 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// Copied from strings/stringpiece.h with modifications
+//
+// A string-like object that points to a sized piece of memory.
+//
+// Functions or methods may use const StringPiece& parameters to accept either
+// a "const char*" or a "string" value that will be implicitly converted to
+// a StringPiece.  The implicit conversion means that it is often appropriate
+// to include this .h file in other files rather than forward-declaring
+// StringPiece as would be appropriate for most other Google classes.
+//
+// Systematic usage of StringPiece is encouraged as it will reduce unnecessary
+// conversions from "const char*" to "string" and back again.
+//
+
+#ifndef BASE_STRING_PIECE_H_
+#define BASE_STRING_PIECE_H_
+#pragma once
+
+#include <stddef.h>
+#include <string.h>
+
+#include <string>
+
+//#include "base/base_api.h"
+//#include "base/basictypes.h"
+
+#define STRING_PIECE(s) StringPiece(s, sizeof(s) - 1)
+
+class StringPiece {
+ public:
+  // standard STL container boilerplate
+  typedef size_t size_type;
+  typedef char value_type;
+  typedef const char* pointer;
+  typedef const char& reference;
+  typedef const char& const_reference;
+  typedef ptrdiff_t difference_type;
+  typedef const char* const_iterator;
+  typedef const char* iterator;
+  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+  typedef std::reverse_iterator<iterator> reverse_iterator;
+
+  static const size_type npos;
+
+ public:
+  // We provide non-explicit singleton constructors so users can pass
+  // in a "const char*" or a "string" wherever a "StringPiece" is
+  // expected.
+  StringPiece() : ptr_(NULL), length_(0) { }
+  StringPiece(const char* str)
+    : ptr_(str), length_((str == NULL) ? 0 : strlen(str)) { }
+  StringPiece(const std::string& str)
+    : ptr_(str.data()), length_(str.size()) { }
+  StringPiece(const char* offset, size_type len)
+    : ptr_(offset), length_(len) { }
+
+  // data() may return a pointer to a buffer with embedded NULs, and the
+  // returned buffer may or may not be null terminated.  Therefore it is
+  // typically a mistake to pass data() to a routine that expects a NUL
+  // terminated string.
+  const char* data() const { return ptr_; }
+  size_type size() const { return length_; }
+  size_type length() const { return length_; }
+  bool empty() const { return length_ == 0; }
+
+  void clear() {
+    ptr_ = NULL;
+    length_ = 0;
+  }
+  void set(const char* data, size_type len) {
+    ptr_ = data;
+    length_ = len;
+  }
+  void set(const char* str) {
+    ptr_ = str;
+    length_ = str ? strlen(str) : 0;
+  }
+  void set(const void* data, size_type len) {
+    ptr_ = reinterpret_cast<const char*>(data);
+    length_ = len;
+  }
+
+  char operator[](size_type i) const { return ptr_[i]; }
+
+  void remove_prefix(size_type n) {
+    ptr_ += n;
+    length_ -= n;
+  }
+
+  void remove_suffix(size_type n) {
+    length_ -= n;
+  }
+
+  int compare(const StringPiece& x) const {
+    int r = wordmemcmp(
+        ptr_, x.ptr_, (length_ < x.length_ ? length_ : x.length_));
+    if (r == 0) {
+      if (length_ < x.length_) r = -1;
+      else if (length_ > x.length_) r = +1;
+    }
+    return r;
+  }
+
+  std::string as_string() const {
+    // std::string doesn't like to take a NULL pointer even with a 0 size.
+    return std::string(!empty() ? data() : "", size());
+  }
+
+  void CopyToString(std::string* target) const;
+  void AppendToString(std::string* target) const;
+
+  // Does "this" start with "x"
+  bool starts_with(const StringPiece& x) const {
+    return ((length_ >= x.length_) &&
+            (wordmemcmp(ptr_, x.ptr_, x.length_) == 0));
+  }
+
+  // Does "this" end with "x"
+  bool ends_with(const StringPiece& x) const {
+    return ((length_ >= x.length_) &&
+            (wordmemcmp(ptr_ + (length_-x.length_), x.ptr_, x.length_) == 0));
+  }
+
+  iterator begin() const { return ptr_; }
+  iterator end() const { return ptr_ + length_; }
+  const_reverse_iterator rbegin() const {
+    return const_reverse_iterator(ptr_ + length_);
+  }
+  const_reverse_iterator rend() const {
+    return const_reverse_iterator(ptr_);
+  }
+
+  size_type max_size() const { return length_; }
+  size_type capacity() const { return length_; }
+
+  size_type copy(char* buf, size_type n, size_type pos = 0) const;
+
+  size_type find(const StringPiece& s, size_type pos = 0) const;
+  size_type find(char c, size_type pos = 0) const;
+  size_type rfind(const StringPiece& s, size_type pos = npos) const;
+  size_type rfind(char c, size_type pos = npos) const;
+
+  size_type find_first_of(const StringPiece& s, size_type pos = 0) const;
+  size_type find_first_of(char c, size_type pos = 0) const {
+    return find(c, pos);
+  }
+  size_type find_first_not_of(const StringPiece& s, size_type pos = 0) const;
+  size_type find_first_not_of(char c, size_type pos = 0) const;
+  size_type find_last_of(const StringPiece& s, size_type pos = npos) const;
+  size_type find_last_of(char c, size_type pos = npos) const {
+    return rfind(c, pos);
+  }
+  size_type find_last_not_of(const StringPiece& s, size_type pos = npos) const;
+  size_type find_last_not_of(char c, size_type pos = npos) const;
+
+  StringPiece substr(size_type pos, size_type n = npos) const;
+
+  static int wordmemcmp(const char* p, const char* p2, size_type N) {
+    return memcmp(p, p2, N);
+  }
+
+  // kati specific functions will follow.
+
+  char get(size_type i) const { return i < length_ ? ptr_[i] : 0; }
+
+  StringPiece StripLeftSpaces() const;
+  StringPiece StripRightSpaces() const;
+  StringPiece StripSpaces() const;
+
+ private:
+  const char*   ptr_;
+  size_type     length_;
+};
+
+bool operator==(const StringPiece& x, const StringPiece& y);
+
+inline bool operator!=(const StringPiece& x, const StringPiece& y) {
+  return !(x == y);
+}
+
+inline bool operator<(const StringPiece& x, const StringPiece& y) {
+  const int r = StringPiece::wordmemcmp(
+      x.data(), y.data(), (x.size() < y.size() ? x.size() : y.size()));
+  return ((r < 0) || ((r == 0) && (x.size() < y.size())));
+}
+
+inline bool operator>(const StringPiece& x, const StringPiece& y) {
+  return y < x;
+}
+
+inline bool operator<=(const StringPiece& x, const StringPiece& y) {
+  return !(x > y);
+}
+
+inline bool operator>=(const StringPiece& x, const StringPiece& y) {
+  return !(x < y);
+}
+
+namespace std {
+template<> struct hash<StringPiece> {
+  size_t operator()(const StringPiece& s) const {
+    size_t result = 0;
+    for (char c : s) {
+      result = (result * 131) + c;
+    }
+    return result;
+  }
+};
+
+}
+
+#define SPF(s) static_cast<int>((s).size()), (s).data()
+
+#endif  // BASE_STRING_PIECE_H_
diff --git a/string_piece_test.cc b/string_piece_test.cc
new file mode 100644
index 0000000..8e16731
--- /dev/null
+++ b/string_piece_test.cc
@@ -0,0 +1,17 @@
+#include "string_piece.h"
+
+#include <assert.h>
+
+#include <unordered_set>
+
+using namespace std;
+
+int main() {
+  unordered_set<StringPiece> sps;
+  sps.insert(STRING_PIECE("foo"));
+  sps.insert(STRING_PIECE("foo"));
+  sps.insert(STRING_PIECE("bar"));
+  assert(sps.size() == 2);
+  assert(sps.count(STRING_PIECE("foo")) == 1);
+  assert(sps.count(STRING_PIECE("bar")) == 1);
+}
diff --git a/string_pool.cc b/string_pool.cc
new file mode 100644
index 0000000..a71a4d4
--- /dev/null
+++ b/string_pool.cc
@@ -0,0 +1,19 @@
+#include "string_pool.h"
+
+#include <stdlib.h>
+
+StringPool::StringPool() {
+}
+
+StringPool::~StringPool() {
+  for (char* b : pool_) {
+    free(b);
+  }
+}
+
+StringPiece StringPool::Add(StringPiece s) {
+  char* b = static_cast<char*>(malloc(s.size()));
+  memcpy(b, s.data(), s.size());
+  pool_.push_back(b);
+  return StringPiece(b, s.size());
+}
diff --git a/string_pool.h b/string_pool.h
new file mode 100644
index 0000000..c084b16
--- /dev/null
+++ b/string_pool.h
@@ -0,0 +1,22 @@
+#ifndef STRING_POOL_H_
+#define STRING_POOL_H_
+
+#include <string>
+#include <vector>
+
+#include "string_piece.h"
+
+using namespace std;
+
+class StringPool {
+ public:
+  StringPool();
+  ~StringPool();
+
+  StringPiece Add(StringPiece s);
+
+ private:
+  vector<char*> pool_;
+};
+
+#endif  // STRING_POOL_H_
diff --git a/stringprintf.cc b/stringprintf.cc
new file mode 100644
index 0000000..d4fe74f
--- /dev/null
+++ b/stringprintf.cc
@@ -0,0 +1,22 @@
+#include "stringprintf.h"
+
+#include <assert.h>
+#include <stdarg.h>
+
+string StringPrintf(const char* format, ...) {
+  string str;
+  str.resize(128);
+  for (int i = 0; i < 2; i++) {
+    va_list args;
+    va_start(args, format);
+    int ret = vsnprintf(&str[0], str.size(), format, args);
+    va_end(args);
+    assert(ret >= 0);
+    if (static_cast<size_t>(ret) < str.size()) {
+      str.resize(ret);
+      return str;
+    }
+    str.resize(ret + 1);
+  }
+  assert(false);
+}
diff --git a/stringprintf.h b/stringprintf.h
new file mode 100644
index 0000000..bdeaec4
--- /dev/null
+++ b/stringprintf.h
@@ -0,0 +1,10 @@
+#ifndef STRINGPRINTF_H_
+#define STRINGPRINTF_H_
+
+#include <string>
+
+using namespace std;
+
+string StringPrintf(const char* fmt, ...);
+
+#endif  // STRINGPRINTF_H_
diff --git a/strutil.cc b/strutil.cc
new file mode 100644
index 0000000..7747c01
--- /dev/null
+++ b/strutil.cc
@@ -0,0 +1,76 @@
+#include "strutil.h"
+
+#include <ctype.h>
+#include <string.h>
+
+#include <unordered_map>
+#include <utility>
+
+WordScanner::Iterator& WordScanner::Iterator::operator++() {
+  int len = static_cast<int>(in->size());
+  for (s = i; s < len; s++) {
+    if (!isspace((*in)[s]))
+      break;
+  }
+  if (s == len) {
+    in = NULL;
+    s = 0;
+    i = 0;
+    return *this;
+  }
+  for (i = s; i < len; i++) {
+    if (isspace((*in)[s]))
+      break;
+  }
+  return *this;
+}
+
+StringPiece WordScanner::Iterator::operator*() const {
+  return in->substr(s, i);
+}
+
+WordScanner::WordScanner(StringPiece in)
+    : in_(in) {
+}
+
+WordScanner::Iterator WordScanner::begin() const {
+  Iterator iter;
+  iter.in = &in_;
+  iter.s = 0;
+  iter.i = 0;
+  ++iter;
+  return iter;
+}
+
+WordScanner::Iterator WordScanner::end() const {
+  Iterator iter;
+  iter.in = NULL;
+  iter.s = 0;
+  iter.i = 0;
+  return iter;
+}
+
+static unordered_map<StringPiece, char*>* g_symtab;
+
+void InitSymtab() {
+  g_symtab = new unordered_map<StringPiece, char*>;
+}
+
+void QuitSymtab() {
+  for (auto p : *g_symtab) {
+    free(p.second);
+  }
+  delete g_symtab;
+}
+
+StringPiece Intern(StringPiece s) {
+  auto found = g_symtab->find(s);
+  if (found != g_symtab->end())
+    return found->first;
+
+  char* b = static_cast<char*>(malloc(s.size()+1));
+  memcpy(b, s.data(), s.size());
+  s = StringPiece(b, s.size());
+  (*g_symtab)[s] = b;
+  return s;
+}
diff --git a/strutil.h b/strutil.h
new file mode 100644
index 0000000..a4e5ae5
--- /dev/null
+++ b/strutil.h
@@ -0,0 +1,50 @@
+#ifndef STRUTIL_H_
+#define STRUTIL_H_
+
+#include <string>
+#include <vector>
+
+#include "string_piece.h"
+
+using namespace std;
+
+class WordScanner {
+ public:
+  struct Iterator {
+    Iterator& operator++();
+    StringPiece operator*() const;
+    bool operator!=(const Iterator& r) const {
+      return in != r.in || s != r.s || i != r.i;
+    }
+
+    const StringPiece* in;
+    int s;
+    int i;
+  };
+
+  explicit WordScanner(StringPiece in);
+
+  Iterator begin() const;
+  Iterator end() const;
+
+ private:
+  StringPiece in_;
+};
+
+void InitSymtab();
+void QuitSymtab();
+StringPiece Intern(StringPiece s);
+
+template <class String>
+inline string JoinStrings(vector<String> v, const char* sep) {
+  string r;
+  for (StringPiece s : v) {
+    if (!r.empty()) {
+      r += sep;
+    }
+    r.append(s.begin(), s.end());
+  }
+  return r;
+}
+
+#endif  // STRUTIL_H_
diff --git a/value.cc b/value.cc
new file mode 100644
index 0000000..ef84cb4
--- /dev/null
+++ b/value.cc
@@ -0,0 +1,389 @@
+#include "value.h"
+
+#include <vector>
+
+#include "eval.h"
+#include "func.h"
+#include "log.h"
+#include "stringprintf.h"
+#include "strutil.h"
+#include "var.h"
+
+Evaluable::Evaluable() {
+}
+
+Evaluable::~Evaluable() {
+}
+
+shared_ptr<string> Evaluable::Eval(Evaluator* ev) const {
+  shared_ptr<string> s = make_shared<string>();
+  Eval(ev, s.get());
+  return s;
+}
+
+Value::Value() {
+}
+
+Value::~Value() {
+}
+
+string Value::DebugString() const {
+  if (this) {
+    return DebugString_();
+  }
+  return "(null)";
+}
+
+class Literal : public Value {
+ public:
+  explicit Literal(StringPiece s)
+      : s_(s) {
+  }
+
+  StringPiece val() const { return s_; }
+
+  virtual void Eval(Evaluator*, string* s) const override {
+    s->append(s_.begin(), s_.end());
+  }
+
+  virtual string DebugString_() const override {
+    return s_.as_string();
+  }
+
+ private:
+  StringPiece s_;
+};
+
+class Expr : public Value {
+ public:
+  Expr() {
+  }
+
+  virtual ~Expr() {
+    for (Value* v : vals_) {
+      delete v;
+    }
+  }
+
+  // Takes the ownership of |v|.
+  void AddValue(Value* v) {
+    vals_.push_back(v);
+  }
+
+  virtual void Eval(Evaluator* ev, string* s) const override {
+    for (Value* v : vals_) {
+      v->Eval(ev, s);
+    }
+  }
+
+  virtual string DebugString_() const override {
+    string r;
+    for (Value* v : vals_) {
+      if (r.empty()) {
+        r += "Expr(";
+      } else {
+        r += ", ";
+      }
+      r += v->DebugString();
+    }
+    r += ")";
+    return r;
+  }
+
+  virtual Value* Compact() {
+    if (vals_.size() != 1) {
+      return this;
+    }
+    Value* r = vals_[0];
+    vals_.clear();
+    delete this;
+    return r;
+  }
+
+ private:
+  vector<Value*> vals_;
+};
+
+class VarRef : public Value {
+ public:
+  explicit VarRef(Value* n)
+      : name_(n) {
+  }
+  virtual ~VarRef() {
+    delete name_;
+  }
+
+  virtual shared_ptr<string> Eval(Evaluator* ev) const override {
+    shared_ptr<string> name = name_->Eval(ev);
+    Var* v = ev->LookupVar(*name);
+    return v->Eval(ev);
+  }
+
+  virtual void Eval(Evaluator* ev, string* s) const override {
+    shared_ptr<string> name = name_->Eval(ev);
+    Var* v = ev->LookupVar(*name);
+    v->Eval(ev, s);
+  }
+
+  virtual string DebugString_() const override {
+    return StringPrintf("VarRef(%s)", name_->DebugString().c_str());
+  }
+
+ private:
+  Value* name_;
+};
+
+class Func : public Value {
+ public:
+  explicit Func(FuncInfo* fi)
+      : fi_(fi) {
+  }
+
+  virtual void Eval(Evaluator* ev, string* s) const override {
+    LOG("Invoke func %s(%s)", name(), JoinValues(args_, ",").c_str());
+    fi_->func(args_, ev, s);
+  }
+
+  virtual string DebugString_() const override {
+    return StringPrintf("Func(%s %s)",
+                        fi_->name,
+                        JoinValues(args_, ",").c_str());
+  }
+
+  void AddArg(Value* v) {
+    args_.push_back(v);
+  }
+
+  const char* name() const { return fi_->name; }
+  int arity() const { return fi_->arity; }
+
+ private:
+  FuncInfo* fi_;
+  vector<Value*> args_;
+};
+
+static char CloseParen(char c) {
+  switch (c) {
+    case '(':
+      return ')';
+    case '{':
+      return '}';
+  }
+  return 0;
+}
+
+static size_t SkipSpaces(StringPiece s, const char* terms) {
+  for (size_t i = 0; i < s.size(); i++) {
+    char c = s[i];
+    if ((c != ' ' && c != '\t') || strchr(terms, c))
+      return i;
+  }
+  return s.size();
+}
+
+static Value* ParseExprImpl(StringPiece s, const char* terms, bool is_command,
+                            size_t* index_out);
+
+Value* ParseFunc(Func* f, StringPiece s, size_t i, char* terms,
+                 size_t* index_out) {
+  terms[1] = ',';
+  terms[2] = '\0';
+  i += SkipSpaces(s.substr(i), terms);
+  if (i == s.size()) {
+    *index_out = i;
+    return f;
+  }
+
+  int nargs = 1;
+  while (true) {
+    if (f->arity() && nargs >= f->arity()) {
+      terms[1] = '\0';  // Drop ','.
+    }
+
+    size_t n;
+    Value* v = ParseExprImpl(s.substr(i), terms, false, &n);
+    // TODO: concatLine???
+    // TODO: trimLiteralSpace for conditional functions.
+    f->AddArg(v);
+    i += n;
+    nargs++;
+    if (s[i] == terms[0]) {
+      i++;
+      break;
+    }
+    i++;  // Should be ','.
+    if (i == s.size())
+      break;
+  }
+
+  *index_out = i;
+  return f;
+}
+
+Value* ParseDollar(StringPiece s, size_t* index_out) {
+  CHECK(s.size() >= 2);
+  CHECK(s[0] == '$');
+  CHECK(s[1] != '$');
+
+  char cp = CloseParen(s[1]);
+  if (cp == 0) {
+    *index_out = 2;
+    return new VarRef(new Literal(s.substr(1, 1)));
+  }
+
+  char terms[] = {cp, ':', ' ', 0};
+  for (size_t i = 2;;) {
+    size_t n;
+    Value* vname = ParseExprImpl(s.substr(i), terms, false, &n);
+    i += n;
+    if (s[i] == cp) {
+      *index_out = i + 1;
+      return new VarRef(vname);
+    }
+
+    if (s[i] == ' ') {
+      // ${func ...}
+      if (Literal* lit = reinterpret_cast<Literal*>(vname)) {
+        if (FuncInfo* fi = GetFuncInfo(lit->val())) {
+          Func* func = new Func(fi);
+          return ParseFunc(func, s, i+1, terms, index_out);
+        }
+      }
+
+      // Not a function. Drop ' ' from |terms| and parse it
+      // again. This is inefficient, but this code path should be
+      // rarely used.
+      delete vname;
+      terms[2] = 0;
+      i = 2;
+      continue;
+    }
+
+    if (s[i] == ':') {
+      // Implement substr ref.
+      CHECK(false);
+    }
+
+    CHECK(false);
+  }
+}
+
+static Value* ParseExprImpl(StringPiece s, const char* terms, bool is_command,
+                            size_t* index_out) {
+  // TODO: A faulty optimization.
+#if 0
+  char specials[] = "$(){}\\\n";
+  size_t found = s.find_first_of(specials);
+  if (found == string::npos) {
+    *index_out = s.size();
+    return new Literal(s);
+  }
+  if (terms && strchr(terms, s[found])) {
+    *index_out = found;
+    return new Literal(s.substr(0, found));
+  }
+#endif
+
+  Expr* r = new Expr;
+  size_t b = 0;
+  char save_paren = 0;
+  int paren_depth = 0;
+  size_t i;
+  for (i = 0; i < s.size(); i++) {
+    char c = s[i];
+    if (terms && strchr(terms, c)) {
+      break;
+    }
+
+    // Handle a comment.
+    if (!terms && c == '#' && !is_command) {
+      if (i > b)
+        r->AddValue(new Literal(s.substr(b, i-b)));
+      bool was_backslash = false;
+      for (; i < s.size() && !(s[i] == '\n' && !was_backslash); i++) {
+        was_backslash = !was_backslash && s[i] == '\\';
+      }
+      *index_out = i;
+      return r->Compact();
+    }
+
+    if (c == '$') {
+      if (i + 1 >= s.size()) {
+        break;
+      }
+
+      if (i > b)
+        r->AddValue(new Literal(s.substr(b, i-b)));
+
+      if (s[i+1] == '$') {
+        r->AddValue(new Literal(STRING_PIECE("$")));
+        i += 2;
+        b = i;
+        continue;
+      }
+
+      if (terms && strchr(terms, s[i+1])) {
+        *index_out = i + 1;
+        return r->Compact();
+      }
+
+      size_t n;
+      Value* v = ParseDollar(s.substr(i), &n);
+      i += n;
+      b = i;
+      i--;
+      r->AddValue(v);
+      continue;
+    }
+
+    if (c == '(' || c == '{') {
+      char cp = CloseParen(c);
+      if (terms && terms[0] == cp) {
+        paren_depth++;
+        save_paren = cp;
+        terms++;
+      } else if (cp == save_paren) {
+        paren_depth++;
+      }
+      continue;
+    }
+
+    if (c == save_paren) {
+      paren_depth--;
+      if (paren_depth == 0) {
+        terms--;
+        save_paren = 0;
+      }
+    }
+
+    if (c == '\\' && i + 1 < s.size() && !is_command) {
+      char n = s[i+1];
+      if (n == '\\') {
+        i++;
+        continue;
+      }
+      if (n == '\r' || n == '\n') {
+        // TODO
+        CHECK(false);
+      }
+    }
+  }
+
+  if (i > b)
+    r->AddValue(new Literal(s.substr(b, i-b)));
+  *index_out = i;
+  return r->Compact();
+}
+
+Value* ParseExpr(StringPiece s, bool is_command) {
+  size_t n;
+  return ParseExprImpl(s, NULL, is_command, &n);
+}
+
+string JoinValues(const vector<Value*> vals, const char* sep) {
+  vector<string> val_strs;
+  for (Value* v : vals) {
+    val_strs.push_back(v->DebugString());
+  }
+  return JoinStrings(val_strs, sep);
+}
diff --git a/value.h b/value.h
new file mode 100644
index 0000000..0236b42
--- /dev/null
+++ b/value.h
@@ -0,0 +1,41 @@
+#ifndef VALUE_H_
+#define VALUE_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "string_piece.h"
+
+using namespace std;
+
+class Evaluator;
+
+class Evaluable {
+ public:
+  virtual void Eval(Evaluator* ev, string* s) const = 0;
+  virtual shared_ptr<string> Eval(Evaluator*) const;
+
+ protected:
+  Evaluable();
+  virtual ~Evaluable();
+};
+
+class Value : public Evaluable {
+ public:
+  virtual ~Value();
+
+  virtual Value* Compact() { return this; }
+
+  string DebugString() const;
+
+ protected:
+  Value();
+  virtual string DebugString_() const = 0;
+};
+
+Value* ParseExpr(StringPiece s, bool is_command);
+
+string JoinValues(const vector<Value*> vals, const char* sep);
+
+#endif  // VALUE_H_
diff --git a/var.cc b/var.cc
new file mode 100644
index 0000000..9bac8a2
--- /dev/null
+++ b/var.cc
@@ -0,0 +1,68 @@
+#include "var.h"
+
+#include "value.h"
+
+UndefinedVar kUndefinedBuf;
+UndefinedVar* kUndefined = &kUndefinedBuf;
+
+Var::Var() {
+}
+
+Var::~Var() {
+}
+
+SimpleVar::SimpleVar(shared_ptr<string> v, const char* origin)
+    : v_(v), origin_(origin) {
+}
+
+void SimpleVar::Eval(Evaluator*, string* s) const {
+  *s += *v_;
+}
+
+string SimpleVar::DebugString() const {
+  return *v_;
+}
+
+RecursiveVar::RecursiveVar(Value* v, const char* origin)
+    : v_(v), origin_(origin) {
+}
+
+void RecursiveVar::Eval(Evaluator* ev, string* s) const {
+  v_->Eval(ev, s);
+}
+
+string RecursiveVar::DebugString() const {
+  return v_->DebugString();
+}
+
+UndefinedVar::UndefinedVar() {}
+
+void UndefinedVar::Eval(Evaluator*, string*) const {
+  // Nothing to do.
+}
+
+string UndefinedVar::DebugString() const {
+  return "*undefined*";
+}
+
+Vars::~Vars() {
+  for (auto p : *this) {
+    delete p.second;
+  }
+}
+
+Var* Vars::Lookup(StringPiece name) const {
+  auto found = find(name);
+  if (found == end())
+    return kUndefined;
+  return found->second;
+}
+
+void Vars::Assign(StringPiece name, Var* v) {
+  auto p = insert(make_pair(name, v));
+  if (!p.second) {
+    if (p.first->second->IsDefined())
+      delete p.first->second;
+    p.first->second = v;
+  }
+}
diff --git a/var.h b/var.h
new file mode 100644
index 0000000..bb7ab31
--- /dev/null
+++ b/var.h
@@ -0,0 +1,101 @@
+#ifndef VAR_H_
+#define VAR_H_
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "string_piece.h"
+#include "value.h"
+
+using namespace std;
+
+class Evaluator;
+class Value;
+
+class Var : public Evaluable {
+ public:
+  virtual ~Var();
+
+  virtual const char* Flavor() const = 0;
+  virtual const char* Origin() const = 0;
+  virtual bool IsDefined() const { return true; }
+
+  virtual string DebugString() const = 0;
+
+ protected:
+  Var();
+};
+
+class SimpleVar : public Var {
+ public:
+  SimpleVar(shared_ptr<string> v, const char* origin);
+
+  virtual const char* Flavor() const {
+    return "simple";
+  }
+  virtual const char* Origin() const {
+    return origin_;
+  }
+
+  virtual shared_ptr<string> Eval(Evaluator*) const override {
+    return v_;
+  }
+  virtual void Eval(Evaluator* ev, string* s) const override;
+
+  string DebugString() const override;
+
+ private:
+  shared_ptr<string> v_;
+  const char* origin_;
+};
+
+class RecursiveVar : public Var {
+ public:
+  RecursiveVar(Value* v, const char* origin);
+
+  virtual const char* Flavor() const {
+    return "recursive";
+  }
+  virtual const char* Origin() const {
+    return origin_;
+  }
+
+  virtual void Eval(Evaluator* ev, string* s) const override;
+
+  string DebugString() const override;
+
+ private:
+  Value* v_;
+  const char* origin_;
+};
+
+class UndefinedVar : public Var {
+ public:
+  UndefinedVar();
+
+  virtual const char* Flavor() const {
+    return "undefined";
+  }
+  virtual const char* Origin() const {
+    return "undefined";
+  }
+  virtual bool IsDefined() const { return false; }
+
+  virtual void Eval(Evaluator* ev, string* s) const override;
+
+  string DebugString() const override;
+};
+
+extern UndefinedVar* kUndefined;
+
+class Vars : public unordered_map<StringPiece, Var*> {
+ public:
+  ~Vars();
+
+  Var* Lookup(StringPiece name) const;
+
+  void Assign(StringPiece name, Var* v);
+};
+
+#endif  // VAR_H_