[C++] Introduce Symbol
diff --git a/Makefile b/Makefile
index bb91bf2..6e4b9fb 100644
--- a/Makefile
+++ b/Makefile
@@ -32,6 +32,7 @@
 	string_pool.cc \
 	stringprintf.cc \
 	strutil.cc \
+	symtab.cc \
 	time.cc \
 	value.cc \
 	var.cc
diff --git a/command.cc b/command.cc
index 39aa9fc..f696cd5 100644
--- a/command.cc
+++ b/command.cc
@@ -94,33 +94,33 @@
 };
 
 void AutoAtVar::Eval(Evaluator*, string* s) const {
-  AppendString(ce_->current_dep_node()->output, s);
+  *s += ce_->current_dep_node()->output.str();
 }
 
 void AutoLessVar::Eval(Evaluator*, string* s) const {
   auto& ai = ce_->current_dep_node()->actual_inputs;
   if (!ai.empty())
-    AppendString(ai[0], s);
+    *s += ai[0].str();
 }
 
 void AutoHatVar::Eval(Evaluator*, string* s) const {
   unordered_set<StringPiece> seen;
   WordWriter ww(s);
-  for (StringPiece ai : ce_->current_dep_node()->actual_inputs) {
-    if (seen.insert(ai).second)
-      ww.Write(ai);
+  for (Symbol ai : ce_->current_dep_node()->actual_inputs) {
+    if (seen.insert(ai.str()).second)
+      ww.Write(ai.str());
   }
 }
 
 void AutoPlusVar::Eval(Evaluator*, string* s) const {
   WordWriter ww(s);
-  for (StringPiece ai : ce_->current_dep_node()->actual_inputs) {
-    ww.Write(ai);
+  for (Symbol ai : ce_->current_dep_node()->actual_inputs) {
+    ww.Write(ai.str());
   }
 }
 
 void AutoStarVar::Eval(Evaluator*, string* s) const {
-  AppendString(StripExt(ce_->current_dep_node()->output), s);
+  AppendString(StripExt(ce_->current_dep_node()->output.str()), s);
 }
 
 void AutoSuffixDVar::Eval(Evaluator* ev, string* s) const {
@@ -163,9 +163,9 @@
   Vars* vars = ev_->mutable_vars();
 #define INSERT_AUTO_VAR(name, sym) do {                                 \
     Var* v = new name(this, sym);                                       \
-    (*vars)[STRING_PIECE(sym)] = v;                                     \
-    (*vars)[STRING_PIECE(sym"D")] = new AutoSuffixDVar(this, sym"D", v); \
-    (*vars)[STRING_PIECE(sym"F")] = new AutoSuffixFVar(this, sym"F", v); \
+    (*vars)[Intern(sym)] = v;                                           \
+    (*vars)[Intern(sym"D")] = new AutoSuffixDVar(this, sym"D", v);      \
+    (*vars)[Intern(sym"F")] = new AutoSuffixFVar(this, sym"F", v);      \
   } while (0)
   INSERT_AUTO_VAR(AutoAtVar, "@");
   INSERT_AUTO_VAR(AutoLessVar, "<");
@@ -198,8 +198,7 @@
       ParseCommandPrefixes(&cmd, &echo, &ignore_error);
 
       if (!cmd.empty()) {
-        Command* command = new Command;
-        command->output = n->output;
+        Command* command = new Command(n->output);
         command->cmd = make_shared<string>(cmd.as_string());
         command->echo = echo;
         command->ignore_error = ignore_error;
diff --git a/command.h b/command.h
index f06533f..e93de34 100644
--- a/command.h
+++ b/command.h
@@ -18,7 +18,7 @@
 #include <memory>
 #include <vector>
 
-#include "string_piece.h"
+#include "symtab.h"
 
 using namespace std;
 
@@ -26,10 +26,10 @@
 class Evaluator;
 
 struct Command {
-  Command()
-      : echo(true), ignore_error(false) {
+  explicit Command(Symbol o)
+      : output(o), echo(true), ignore_error(false) {
   }
-  StringPiece output;
+  Symbol output;
   shared_ptr<string> cmd;
   bool echo;
   bool ignore_error;
diff --git a/dep.cc b/dep.cc
index d8980b7..7416774 100644
--- a/dep.cc
+++ b/dep.cc
@@ -27,17 +27,18 @@
 #include "log.h"
 #include "rule.h"
 #include "strutil.h"
+#include "symtab.h"
 #include "var.h"
 
 namespace {
 
 static vector<DepNode*>* g_dep_node_pool;
 
-static StringPiece ReplaceSuffix(StringPiece s, StringPiece newsuf) {
+static Symbol ReplaceSuffix(Symbol s, Symbol newsuf) {
   string r;
-  AppendString(StripExt(s), &r);
+  AppendString(StripExt(s.str()), &r);
   r += '.';
-  AppendString(newsuf, &r);
+  AppendString(newsuf.str(), &r);
   return Intern(r);
 }
 
@@ -99,7 +100,7 @@
 
 }  // namespace
 
-DepNode::DepNode(StringPiece o, bool p)
+DepNode::DepNode(Symbol o, bool p)
     : output(o),
       has_rule(false),
       is_order_only(false),
@@ -112,7 +113,7 @@
  public:
   DepBuilder(Evaluator* ev,
              const vector<shared_ptr<Rule>>& rules,
-             const unordered_map<StringPiece, Vars*>& rule_vars)
+             const unordered_map<Symbol, Vars*>& rule_vars)
       : ev_(ev),
         rule_vars_(rule_vars),
         implicit_rules_(new RuleTrie()),
@@ -123,9 +124,9 @@
     LOG_STAT("%zu implicit rules", implicit_rules_->size());
     LOG_STAT("%zu suffix rules", suffix_rules_.size());
 
-    auto found = rules_.find(".PHONY");
+    auto found = rules_.find(Intern(".PHONY"));
     if (found != rules_.end()) {
-      for (StringPiece input : found->second->inputs) {
+      for (Symbol input : found->second->inputs) {
         phony_.insert(input);
       }
     }
@@ -134,7 +135,7 @@
   ~DepBuilder() {
   }
 
-  void Build(vector<StringPiece> targets,
+  void Build(vector<Symbol> targets,
              vector<DepNode*>* nodes) {
     if (targets.empty()) {
       if (!first_rule_) {
@@ -146,10 +147,10 @@
 
     // TODO: LogStats?
 
-    for (StringPiece target : targets) {
+    for (Symbol target : targets) {
       cur_rule_vars_.reset(new Vars);
       ev_->set_current_scope(cur_rule_vars_.get());
-      DepNode* n = BuildPlan(target, "");
+      DepNode* n = BuildPlan(target, Intern(""));
       nodes->push_back(n);
       ev_->set_current_scope(NULL);
       cur_rule_vars_.reset(NULL);
@@ -157,13 +158,13 @@
   }
 
  private:
-  bool Exists(StringPiece target) {
+  bool Exists(Symbol target) {
     auto found = rules_.find(target);
     if (found != rules_.end())
       return true;
     if (phony_.count(target))
       return true;
-    return ::Exists(target);
+    return ::Exists(target.str());
   }
 
   void PopulateRules(const vector<shared_ptr<Rule>>& rules) {
@@ -179,11 +180,11 @@
     }
   }
 
-  bool PopulateSuffixRule(shared_ptr<Rule> rule, StringPiece output) {
-    if (output.empty() || output[0] != '.')
+  bool PopulateSuffixRule(shared_ptr<Rule> rule, Symbol output) {
+    if (output.empty() || output.str()[0] != '.')
       return false;
 
-    StringPiece rest = output.substr(1);
+    const StringPiece rest = StringPiece(output.str()).substr(1);
     size_t dot_index = rest.find('.');
     // If there is only a single dot or the third dot, this is not a
     // suffix rule.
@@ -196,7 +197,7 @@
     StringPiece output_suffix = rest.substr(dot_index+1);
     shared_ptr<Rule> r = make_shared<Rule>(*rule);
     r->inputs.clear();
-    r->inputs.push_back(input_suffix);
+    r->inputs.push_back(Intern(input_suffix));
     r->is_suffix_rule = true;
     suffix_rules_[output_suffix].push_back(r);
     return true;
@@ -204,18 +205,18 @@
 
   shared_ptr<Rule> MergeRules(const Rule& old_rule,
                               const Rule& rule,
-                              StringPiece output,
+                              Symbol output,
                               bool is_suffix_rule) {
     if (old_rule.is_double_colon != rule.is_double_colon) {
       ERROR("%s:%d: *** target file `%s' has both : and :: entries.",
-            LOCF(rule.loc), output.as_string().c_str());
+            LOCF(rule.loc), output.str().c_str());
     }
     if (!old_rule.cmds.empty() && !rule.cmds.empty() &&
         !is_suffix_rule && !rule.is_double_colon) {
       WARN("%s:%d: warning: overriding commands for target `%s'",
-           LOCF(rule.cmd_loc()), output.as_string().c_str());
+           LOCF(rule.cmd_loc()), output.str().c_str());
       WARN("%s:%d: warning: ignoring old commands for target `%s'",
-           LOCF(old_rule.cmd_loc()), output.as_string().c_str());
+           LOCF(old_rule.cmd_loc()), output.str().c_str());
     }
 
     shared_ptr<Rule> r = make_shared<Rule>(rule);
@@ -245,14 +246,14 @@
            old_rule.order_only_inputs.end(),
            back_inserter(r->inputs));
     }
-    for (StringPiece p : old_rule.output_patterns) {
+    for (Symbol p : old_rule.output_patterns) {
       r->output_patterns.push_back(p);
     }
     return r;
   }
 
   void PopulateExplicitRule(shared_ptr<Rule> rule) {
-    for (StringPiece output : rule->outputs) {
+    for (Symbol output : rule->outputs) {
       const bool is_suffix_rule = PopulateSuffixRule(rule, output);
 
       rule = make_shared<Rule>(*rule);
@@ -271,44 +272,44 @@
   }
 
   void PopulateImplicitRule(shared_ptr<Rule> rule) {
-    for (StringPiece output_pattern : rule->output_patterns) {
+    for (Symbol output_pattern : rule->output_patterns) {
       shared_ptr<Rule> r = make_shared<Rule>(*rule);
       r->output_patterns.clear();
       r->output_patterns.push_back(output_pattern);
-      implicit_rules_->Add(output_pattern, r);
+      implicit_rules_->Add(output_pattern.str(), r);
     }
   }
 
-  shared_ptr<Rule> LookupRule(StringPiece o) {
+  shared_ptr<Rule> LookupRule(Symbol o) {
     auto found = rules_.find(o);
     if (found != rules_.end())
       return found->second;
     return NULL;
   }
 
-  Vars* LookupRuleVars(StringPiece o) {
+  Vars* LookupRuleVars(Symbol o) {
     auto found = rule_vars_.find(o);
     if (found != rule_vars_.end())
       return found->second;
     return NULL;
   }
 
-  bool CanPickImplicitRule(shared_ptr<Rule> rule, StringPiece output) {
+  bool CanPickImplicitRule(shared_ptr<Rule> rule, Symbol output) {
     CHECK(rule->output_patterns.size() == 1);
-    Pattern pat(rule->output_patterns[0]);
-    if (!pat.Match(output)) {
+    Pattern pat(rule->output_patterns[0].str());
+    if (!pat.Match(output.str())) {
       return false;
     }
-    for (StringPiece input : rule->inputs) {
+    for (Symbol input : rule->inputs) {
       string buf;
-      pat.AppendSubst(output, input, &buf);
-      if (!Exists(buf))
+      pat.AppendSubst(output.str(), input.str(), &buf);
+      if (!Exists(Intern(buf)))
         return false;
     }
     return true;
   }
 
-  Vars* MergeImplicitRuleVars(StringPiece output, Vars* vars) {
+  Vars* MergeImplicitRuleVars(Symbol output, Vars* vars) {
     auto found = rule_vars_.find(output);
     if (found == rule_vars_.end())
       return vars;
@@ -322,7 +323,7 @@
     return r;
   }
 
-  bool PickRule(StringPiece output,
+  bool PickRule(Symbol output,
                 shared_ptr<Rule>* out_rule, Vars** out_var) {
     shared_ptr<Rule> rule = LookupRule(output);
     Vars* vars = LookupRuleVars(output);
@@ -335,7 +336,7 @@
     }
 
     vector<shared_ptr<Rule>> irules;
-    implicit_rules_->Get(output, &irules);
+    implicit_rules_->Get(output.str(), &irules);
     for (auto iter = irules.rbegin(); iter != irules.rend(); ++iter) {
       shared_ptr<Rule> irule = *iter;
       if (!CanPickImplicitRule(irule, output))
@@ -362,7 +363,7 @@
       return true;
     }
 
-    StringPiece output_suffix = GetExt(output);
+    StringPiece output_suffix = GetExt(output.str());
     if (output_suffix.get(0) != '.')
       return rule.get();
     output_suffix = output_suffix.substr(1);
@@ -373,7 +374,7 @@
 
     for (shared_ptr<Rule> irule : found->second) {
       CHECK(irule->inputs.size() == 1);
-      StringPiece input = ReplaceSuffix(output, irule->inputs[0]);
+      Symbol input = ReplaceSuffix(output, irule->inputs[0]);
       if (!Exists(input))
         continue;
 
@@ -398,10 +399,10 @@
     return rule.get();
   }
 
-  DepNode* BuildPlan(StringPiece output, StringPiece needed_by) {
+  DepNode* BuildPlan(Symbol output, Symbol needed_by) {
     LOG("BuildPlan: %s for %s",
-        output.as_string().c_str(),
-        needed_by.as_string().c_str());
+        output.c_str(),
+        needed_by.c_str());
 
     auto found = done_.find(output);
     if (found != done_.end()) {
@@ -420,7 +421,7 @@
     vector<unique_ptr<ScopedVar>> sv;
     if (vars) {
       for (const auto& p : *vars) {
-        StringPiece name = p.first;
+        Symbol name = p.first;
         RuleVar* var = reinterpret_cast<RuleVar*>(p.second);
         CHECK(var);
         Var* new_var = var->v();
@@ -446,16 +447,17 @@
       }
     }
 
-    for (StringPiece input : rule->inputs) {
+    for (Symbol input : rule->inputs) {
       if (rule->output_patterns.size() > 0) {
         if (rule->output_patterns.size() > 1) {
           ERROR("TODO: multiple output pattern is not supported yet");
         }
         string o;
-        Pattern(rule->output_patterns[0]).AppendSubst(output, input, &o);
+        Pattern(rule->output_patterns[0].str()).AppendSubst(
+            output.str(), input.str(), &o);
         input = Intern(o);
       } else if (rule->is_suffix_rule) {
-        input = Intern(ReplaceSuffix(output, input));
+        input = ReplaceSuffix(output, input);
       }
 
       n->actual_inputs.push_back(input);
@@ -463,7 +465,7 @@
       n->deps.push_back(c);
     }
 
-    for (StringPiece input : rule->order_only_inputs) {
+    for (Symbol input : rule->order_only_inputs) {
       DepNode* c = BuildPlan(input, output);
       c->is_order_only = true;
       n->deps.push_back(c);
@@ -487,8 +489,8 @@
   }
 
   Evaluator* ev_;
-  unordered_map<StringPiece, shared_ptr<Rule>> rules_;
-  const unordered_map<StringPiece, Vars*>& rule_vars_;
+  unordered_map<Symbol, shared_ptr<Rule>> rules_;
+  const unordered_map<Symbol, Vars*>& rule_vars_;
   unique_ptr<Vars> cur_rule_vars_;
 
   unique_ptr<RuleTrie> implicit_rules_;
@@ -496,14 +498,14 @@
   SuffixRuleMap suffix_rules_;
 
   shared_ptr<Rule> first_rule_;
-  unordered_map<StringPiece, DepNode*> done_;
-  unordered_set<StringPiece> phony_;
+  unordered_map<Symbol, DepNode*> done_;
+  unordered_set<Symbol> phony_;
 };
 
 void MakeDep(Evaluator* ev,
              const vector<shared_ptr<Rule>>& rules,
-             const unordered_map<StringPiece, Vars*>& rule_vars,
-             const vector<StringPiece>& targets,
+             const unordered_map<Symbol, Vars*>& rule_vars,
+             const vector<Symbol>& targets,
              vector<DepNode*>* nodes) {
   DepBuilder db(ev, rules, rule_vars);
   db.Build(targets, nodes);
diff --git a/dep.h b/dep.h
index 934d4fe..22434a3 100644
--- a/dep.h
+++ b/dep.h
@@ -22,6 +22,7 @@
 
 #include "loc.h"
 #include "string_piece.h"
+#include "symtab.h"
 
 class Evaluator;
 class Rule;
@@ -29,16 +30,16 @@
 class Vars;
 
 struct DepNode {
-  DepNode(StringPiece output, bool is_phony);
+  DepNode(Symbol output, bool is_phony);
 
-  StringPiece output;
+  Symbol output;
   vector<Value*> cmds;
   vector<DepNode*> deps;
   vector<DepNode*> parents;
   bool has_rule;
   bool is_order_only;
   bool is_phony;
-  vector<StringPiece> actual_inputs;
+  vector<Symbol> actual_inputs;
   Vars* rule_vars;
   Loc loc;
 };
@@ -48,8 +49,8 @@
 
 void MakeDep(Evaluator* ev,
              const vector<shared_ptr<Rule>>& rules,
-             const unordered_map<StringPiece, Vars*>& rule_vars,
-             const vector<StringPiece>& targets,
+             const unordered_map<Symbol, Vars*>& rule_vars,
+             const vector<Symbol>& targets,
              vector<DepNode*>* nodes);
 
 #endif  // DEP_H_
diff --git a/eval.cc b/eval.cc
index 0710082..489d342 100644
--- a/eval.cc
+++ b/eval.cc
@@ -26,6 +26,7 @@
 #include "parser.h"
 #include "rule.h"
 #include "strutil.h"
+#include "symtab.h"
 #include "value.h"
 #include "var.h"
 
@@ -49,7 +50,7 @@
   // }
 }
 
-Var* Evaluator::EvalRHS(StringPiece lhs, Value* rhs_v, StringPiece orig_rhs,
+Var* Evaluator::EvalRHS(Symbol lhs, Value* rhs_v, StringPiece orig_rhs,
                         AssignOp op, bool is_override) {
   VarOrigin origin = (
       (is_bootstrap_ ? VarOrigin::DEFAULT :
@@ -87,7 +88,7 @@
     }
   }
 
-  LOG("Assign: %.*s=%s", SPF(lhs), rhs->DebugString().c_str());
+  LOG("Assign: %s=%s", lhs.c_str(), rhs->DebugString().c_str());
   if (needs_assign) {
     return rhs;
   }
@@ -97,7 +98,7 @@
 void Evaluator::EvalAssign(const AssignAST* ast) {
   loc_ = ast->loc();
   last_rule_ = NULL;
-  StringPiece lhs = Intern(*ast->lhs->Eval(this));
+  Symbol lhs = Intern(*ast->lhs->Eval(this));
   if (lhs.empty())
     Error("*** empty variable name.");
   Var* rhs = EvalRHS(lhs, ast->rhs, ast->orig_rhs, ast->op,
@@ -130,7 +131,7 @@
     return;
   }
 
-  for (StringPiece output : rule_var.outputs) {
+  for (Symbol output : rule_var.outputs) {
     auto p = rule_vars_.emplace(output, nullptr);
     if (p.second) {
       p.first->second = new Vars;
@@ -153,7 +154,7 @@
     }
 
     current_scope_ = p.first->second;
-    StringPiece lhs = Intern(rule_var.lhs);
+    Symbol lhs = Intern(rule_var.lhs);
     Var* rhs_var = EvalRHS(lhs, rhs, STRING_PIECE("*TODO*"), rule_var.op);
     if (rhs_var)
       current_scope_->Assign(lhs, new RuleVar(rhs_var, rule_var.op));
@@ -182,7 +183,7 @@
   switch (ast->op) {
     case CondOp::IFDEF:
     case CondOp::IFNDEF: {
-      StringPiece lhs = Intern(*ast->lhs->Eval(this));
+      Symbol lhs = Intern(*ast->lhs->Eval(this));
       Var* v = LookupVarInCurrentScope(lhs);
       shared_ptr<string> s = v->Eval(this);
       is_true = (s->empty() == (ast->op == CondOp::IFNDEF));
@@ -228,8 +229,8 @@
     return;
   }
 
-  Var* var_list = LookupVar("MAKEFILE_LIST");
-  var_list->AppendVar(this, NewLiteral(Intern(TrimLeadingCurdir(fname))));
+  Var* var_list = LookupVar(Intern("MAKEFILE_LIST"));
+  var_list->AppendVar(this, NewLiteral(Intern(TrimLeadingCurdir(fname)).str()));
   for (AST* ast : mk->asts()) {
     LOG("%s", ast->DebugString().c_str());
     ast->Eval(this);
@@ -279,7 +280,7 @@
   }
 }
 
-Var* Evaluator::LookupVar(StringPiece name) {
+Var* Evaluator::LookupVar(Symbol name) {
   if (current_scope_) {
     Var* v = current_scope_->Lookup(name);
     if (v->IsDefined())
@@ -291,7 +292,7 @@
   return in_vars_->Lookup(name);
 }
 
-Var* Evaluator::LookupVarInCurrentScope(StringPiece name) {
+Var* Evaluator::LookupVarInCurrentScope(Symbol name) {
   if (current_scope_) {
     return current_scope_->Lookup(name);
   }
diff --git a/eval.h b/eval.h
index 179b825..dcdb87f 100644
--- a/eval.h
+++ b/eval.h
@@ -22,6 +22,7 @@
 #include "ast.h"
 #include "loc.h"
 #include "string_piece.h"
+#include "symtab.h"
 
 using namespace std;
 
@@ -51,18 +52,18 @@
   void EvalInclude(const IncludeAST* ast);
   void EvalExport(const ExportAST* ast);
 
-  Var* LookupVar(StringPiece name);
+  Var* LookupVar(Symbol name);
   // For target specific variables.
-  Var* LookupVarInCurrentScope(StringPiece name);
+  Var* LookupVarInCurrentScope(Symbol name);
 
   const Loc& loc() const { return loc_; }
 
   const vector<shared_ptr<Rule>>& rules() const { return rules_; }
-  const unordered_map<StringPiece, Vars*>& rule_vars() const {
+  const unordered_map<Symbol, Vars*>& rule_vars() const {
     return rule_vars_;
   }
   Vars* mutable_vars() { return vars_; }
-  const unordered_map<StringPiece, bool>& exports() const { return exports_; }
+  const unordered_map<Symbol, bool>& exports() const { return exports_; }
 
   void Error(const string& msg);
 
@@ -74,15 +75,15 @@
   void set_avoid_io(bool a) { avoid_io_ = a; }
 
  private:
-  Var* EvalRHS(StringPiece lhs, Value* rhs, StringPiece orig_rhs, AssignOp op,
+  Var* EvalRHS(Symbol lhs, Value* rhs, StringPiece orig_rhs, AssignOp op,
                bool is_override = false);
   void DoInclude(const char* fname, bool should_exist);
 
   const Vars* in_vars_;
   Vars* vars_;
-  unordered_map<StringPiece, Vars*> rule_vars_;
+  unordered_map<Symbol, Vars*> rule_vars_;
   vector<shared_ptr<Rule>> rules_;
-  unordered_map<StringPiece, bool> exports_;
+  unordered_map<Symbol, bool> exports_;
 
   Rule* last_rule_;
   Vars* current_scope_;
diff --git a/exec.cc b/exec.cc
index 69cf4ea..056318e 100644
--- a/exec.cc
+++ b/exec.cc
@@ -32,6 +32,7 @@
 #include "log.h"
 #include "string_piece.h"
 #include "strutil.h"
+#include "symtab.h"
 #include "value.h"
 #include "var.h"
 
@@ -49,15 +50,15 @@
     done_[n->output] = true;
 
     LOG("ExecNode: %s for %s",
-        n->output.as_string().c_str(),
-        needed_by ? needed_by->output.as_string().c_str() : "(null)");
+        n->output.c_str(),
+        needed_by ? needed_by->output.c_str() : "(null)");
 
     for (DepNode* d : n->deps) {
-      if (d->is_order_only && Exists(d->output)) {
+      if (d->is_order_only && Exists(d->output.str())) {
         continue;
       }
       // TODO: Check the timestamp.
-      if (Exists(d->output)) {
+      if (Exists(d->output.str())) {
         continue;
       }
       ExecNode(d, n);
@@ -74,11 +75,11 @@
         int result = system(command->cmd->c_str());
         if (result != 0) {
           if (command->ignore_error) {
-            fprintf(stderr, "[%.*s] Error %d (ignored)\n",
-                    SPF(command->output), WEXITSTATUS(result));
+            fprintf(stderr, "[%s] Error %d (ignored)\n",
+                    command->output.c_str(), WEXITSTATUS(result));
           } else {
-            fprintf(stderr, "*** [%.*s] Error %d\n",
-                    SPF(command->output), WEXITSTATUS(result));
+            fprintf(stderr, "*** [%s] Error %d\n",
+                    command->output.c_str(), WEXITSTATUS(result));
             exit(1);
           }
         }
@@ -89,7 +90,7 @@
 
  private:
   CommandEvaluator ce_;
-  unordered_map<StringPiece, bool> done_;
+  unordered_map<Symbol, bool> done_;
 };
 
 }  // namespace
diff --git a/func.cc b/func.cc
index dfafe30..306de80 100644
--- a/func.cc
+++ b/func.cc
@@ -31,6 +31,7 @@
 #include "log.h"
 #include "parser.h"
 #include "strutil.h"
+#include "symtab.h"
 #include "var.h"
 
 namespace {
@@ -391,7 +392,7 @@
 
 void ValueFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
   shared_ptr<string> var_name = args[0]->Eval(ev);
-  Var* var = ev->LookupVar(*var_name);
+  Var* var = ev->LookupVar(Intern(*var_name));
   AppendString(var->String().as_string(), s);
 }
 
@@ -447,7 +448,7 @@
   };
 
   shared_ptr<string> func_name = args[0]->Eval(ev);
-  Var* func = ev->LookupVar(*func_name);
+  Var* func = ev->LookupVar(Intern(*func_name));
   vector<unique_ptr<SimpleVar>> av;
   for (size_t i = 1; i < args.size(); i++) {
     unique_ptr<SimpleVar> s(
@@ -457,7 +458,8 @@
   vector<unique_ptr<ScopedVar>> sv;
   for (size_t i = 1; i < args.size(); i++) {
     sv.push_back(move(unique_ptr<ScopedVar>(
-        new ScopedVar(ev->mutable_vars(), tmpvar_names[i], av[i-1].get()))));
+        new ScopedVar(ev->mutable_vars(),
+                      Intern(tmpvar_names[i]), av[i-1].get()))));
   }
   func->Eval(ev, s);
 }
@@ -469,7 +471,7 @@
   for (StringPiece tok : WordScanner(*list)) {
     unique_ptr<SimpleVar> v(new SimpleVar(
         make_shared<string>(tok.data(), tok.size()), VarOrigin::AUTOMATIC));
-    ScopedVar sv(ev->mutable_vars(), *varname, v.get());
+    ScopedVar sv(ev->mutable_vars(), Intern(*varname), v.get());
     ww.MaybeAddWhitespace();
     args[2]->Eval(ev, s);
   }
@@ -477,13 +479,13 @@
 
 void OriginFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
   shared_ptr<string> var_name = args[0]->Eval(ev);
-  Var* var = ev->LookupVar(*var_name);
+  Var* var = ev->LookupVar(Intern(*var_name));
   *s += GetOriginStr(var->Origin());
 }
 
 void FlavorFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
   shared_ptr<string> var_name = args[0]->Eval(ev);
-  Var* var = ev->LookupVar(*var_name);
+  Var* var = ev->LookupVar(Intern(*var_name));
   *s += var->Flavor();
 }
 
@@ -567,7 +569,7 @@
   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;
+    bool ok = g_func_info_map->emplace(fi->name, fi).second;
     CHECK(ok);
   }
 }
diff --git a/main.cc b/main.cc
index b99a035..aa09bed 100644
--- a/main.cc
+++ b/main.cc
@@ -34,6 +34,7 @@
 #include "string_piece.h"
 #include "stringprintf.h"
 #include "strutil.h"
+#include "symtab.h"
 #include "time.h"
 #include "var.h"
 
@@ -61,7 +62,7 @@
 }
 
 static void ParseCommandLine(int argc, char* argv[],
-                             vector<StringPiece>* targets,
+                             vector<Symbol>* targets,
                              vector<StringPiece>* cl_vars) {
   for (int i = 1; i < argc; i++) {
     const char* arg = argv[i];
@@ -116,7 +117,7 @@
   QuitSymtab();
 }
 
-static void ReadBootstrapMakefile(const vector<StringPiece>& targets,
+static void ReadBootstrapMakefile(const vector<Symbol>& targets,
                                   vector<AST*>* asts) {
   string bootstrap = (
       "CC:=cc\n"
@@ -138,7 +139,7 @@
       // TODO: Add more builtin rules.
                       );
   bootstrap += StringPrintf("MAKECMDGOALS:=%s\n",
-                            JoinStrings(targets, " ").c_str());
+                            JoinSymbols(targets, " ").c_str());
 
   char cwd[PATH_MAX];
   if (!getcwd(cwd, PATH_MAX)) {
@@ -146,13 +147,13 @@
     CHECK(false);
   }
   bootstrap += StringPrintf("CURDIR:=%s\n", cwd);
-  Parse(Intern(bootstrap), Loc("*bootstrap*", 0), asts);
+  Parse(Intern(bootstrap).str(), Loc("*bootstrap*", 0), asts);
 }
 
 static void SetVar(StringPiece l, VarOrigin origin, Vars* vars) {
   size_t found = l.find('=');
   CHECK(found != string::npos);
-  StringPiece lhs = Intern(l.substr(0, found));
+  Symbol lhs = Intern(l.substr(0, found));
   StringPiece rhs = l.substr(found + 1);
   vars->Assign(lhs,
                new RecursiveVar(NewLiteral(rhs.data()), origin, rhs.data()));
@@ -168,7 +169,7 @@
   }
 }
 
-static int Run(const vector<StringPiece>& targets,
+static int Run(const vector<Symbol>& targets,
                const vector<StringPiece>& cl_vars) {
   MakefileCacheManager* cache_mgr = NewMakefileCacheManager();
 
@@ -185,7 +186,7 @@
   }
   ev->set_is_bootstrap(false);
 
-  vars->Assign("MAKEFILE_LIST",
+  vars->Assign(Intern("MAKEFILE_LIST"),
                new SimpleVar(make_shared<string>(
                    StringPrintf(" %s", g_makefile)), VarOrigin::FILE));
 
@@ -205,7 +206,7 @@
   }
 
   for (const auto& p : ev->exports()) {
-    const string& name = p.first.as_string();
+    const Symbol name = p.first;
     if (p.second) {
       Var* v = ev->LookupVar(name);
       shared_ptr<string> value = v->Eval(ev);
@@ -242,7 +243,7 @@
 
 int main(int argc, char* argv[]) {
   Init();
-  vector<StringPiece> targets;
+  vector<Symbol> targets;
   vector<StringPiece> cl_vars;
   ParseCommandLine(argc, argv, &targets, &cl_vars);
   int r = Run(targets, cl_vars);
diff --git a/ninja.cc b/ninja.cc
index 27eccfb..418c36a 100644
--- a/ninja.cc
+++ b/ninja.cc
@@ -197,19 +197,19 @@
   }
 
   void EmitBuild(DepNode* node, const string& rule_name) {
-    fprintf(fp_, "build %.*s: %s", SPF(node->output), rule_name.c_str());
-    vector<StringPiece> order_onlys;
+    fprintf(fp_, "build %s: %s", node->output.c_str(), rule_name.c_str());
+    vector<Symbol> order_onlys;
     for (DepNode* d : node->deps) {
       if (d->is_order_only) {
         order_onlys.push_back(d->output);
       } else {
-        fprintf(fp_, " %.*s", SPF(d->output));
+        fprintf(fp_, " %s", d->output.c_str());
       }
     }
     if (!order_onlys.empty()) {
       fprintf(fp_, " ||");
-      for (StringPiece oo : order_onlys) {
-        fprintf(fp_, " %.*s", SPF(oo));
+      for (Symbol oo : order_onlys) {
+        fprintf(fp_, " %s", oo.c_str());
       }
     }
     fprintf(fp_, "\n");
@@ -242,7 +242,7 @@
   CommandEvaluator ce_;
   Evaluator* ev_;
   FILE* fp_;
-  unordered_set<StringPiece> done_;
+  unordered_set<Symbol> done_;
   int rule_id_;
   string cmd_buf_;
 };
diff --git a/rule.cc b/rule.cc
index 7c882a3..0a81c69 100644
--- a/rule.cc
+++ b/rule.cc
@@ -20,6 +20,7 @@
 #include "parser.h"
 #include "stringprintf.h"
 #include "strutil.h"
+#include "symtab.h"
 #include "value.h"
 
 namespace {
@@ -31,11 +32,11 @@
       is_order_only = true;
       continue;
     }
-    input = Intern(TrimLeadingCurdir(input));
+    Symbol input_sym = Intern(TrimLeadingCurdir(input));
     if (is_order_only) {
-      r->order_only_inputs.push_back(input);
+      r->order_only_inputs.push_back(input_sym);
     } else {
-      r->inputs.push_back(input);
+      r->inputs.push_back(input_sym);
     }
   }
 }
@@ -60,12 +61,13 @@
   }
 
   StringPiece first = line.substr(0, index);
-  vector<StringPiece> outputs;
+  vector<Symbol> outputs;
   for (StringPiece tok : WordScanner(first)) {
     outputs.push_back(Intern(TrimLeadingCurdir(tok)));
   }
 
-  const bool is_first_pattern = !outputs.empty() && IsPatternRule(outputs[0]);
+  const bool is_first_pattern = (
+      !outputs.empty() && IsPatternRule(outputs[0].str()));
   if (is_first_pattern) {
     if (outputs.size() > 1) {
       // TODO: Multiple output patterns are not supported yet.
@@ -107,7 +109,7 @@
     CHECK(rest[term_index] == ';');
     // TODO: Maybe better to avoid Intern here?
     rule->cmds.push_back(
-        NewLiteral(Intern(TrimLeftSpace(rest.substr(term_index + 1)))));
+        NewLiteral(Intern(TrimLeftSpace(rest.substr(term_index + 1))).str()));
     rest = rest.substr(0, term_index);
   }
 
@@ -135,7 +137,7 @@
   if (rule->output_patterns.size() > 1) {
     ERROR("%s:%d: *** multiple target patterns.", LOCF(loc));
   }
-  if (!IsPatternRule(rule->output_patterns[0])) {
+  if (!IsPatternRule(rule->output_patterns[0].str())) {
     ERROR("%s:%d: *** target pattern contains no '%%'.", LOCF(loc));
   }
   ParseInputs(rule, third);
@@ -143,15 +145,15 @@
 
 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()));
+  v.push_back(StringPrintf("outputs=[%s]", JoinSymbols(outputs, ",").c_str()));
+  v.push_back(StringPrintf("inputs=[%s]", JoinSymbols(inputs, ",").c_str()));
   if (!order_only_inputs.empty()) {
     v.push_back(StringPrintf("order_only_inputs=[%s]",
-                             JoinStrings(order_only_inputs, ",").c_str()));
+                             JoinSymbols(order_only_inputs, ",").c_str()));
   }
   if (!output_patterns.empty()) {
     v.push_back(StringPrintf("output_patterns=[%s]",
-                             JoinStrings(output_patterns, ",").c_str()));
+                             JoinSymbols(output_patterns, ",").c_str()));
   }
   if (is_double_colon)
     v.push_back("is_double_colon");
diff --git a/rule.h b/rule.h
index d839685..c6fd72c 100644
--- a/rule.h
+++ b/rule.h
@@ -21,6 +21,7 @@
 #include "loc.h"
 #include "log.h"
 #include "string_piece.h"
+#include "symtab.h"
 
 using namespace std;
 
@@ -34,10 +35,10 @@
 
   string DebugString() const;
 
-  vector<StringPiece> outputs;
-  vector<StringPiece> inputs;
-  vector<StringPiece> order_only_inputs;
-  vector<StringPiece> output_patterns;
+  vector<Symbol> outputs;
+  vector<Symbol> inputs;
+  vector<Symbol> order_only_inputs;
+  vector<Symbol> output_patterns;
   bool is_double_colon;
   bool is_suffix_rule;
   vector<Value*> cmds;
@@ -51,7 +52,7 @@
 };
 
 struct RuleVarAssignment {
-  vector<StringPiece> outputs;
+  vector<Symbol> outputs;
   StringPiece lhs;
   StringPiece rhs;
   AssignOp op;
diff --git a/strutil.cc b/strutil.cc
index 99eb163..7042b3a 100644
--- a/strutil.cc
+++ b/strutil.cc
@@ -18,11 +18,9 @@
 
 #include <ctype.h>
 #include <limits.h>
-#include <string.h>
 #include <unistd.h>
 
 #include <stack>
-#include <unordered_map>
 #include <utility>
 
 #include "log.h"
@@ -103,31 +101,6 @@
   const_cast<char*>(s_.data())[s_.size()] = c_;
 }
 
-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;
-}
-
 void AppendString(StringPiece str, string* out) {
   out->append(str.begin(), str.end());
 }
diff --git a/strutil.h b/strutil.h
index 368ee23..6a51ee7 100644
--- a/strutil.h
+++ b/strutil.h
@@ -69,10 +69,6 @@
   char c_;
 };
 
-void InitSymtab();
-void QuitSymtab();
-StringPiece Intern(StringPiece s);
-
 template <class String>
 inline string JoinStrings(vector<String> v, const char* sep) {
   string r;
diff --git a/symtab.cc b/symtab.cc
new file mode 100644
index 0000000..342d222
--- /dev/null
+++ b/symtab.cc
@@ -0,0 +1,79 @@
+// Copyright 2015 Google Inc. All rights reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build ignore
+
+#include "symtab.h"
+
+#include <string.h>
+
+#include <unordered_map>
+
+#include "log.h"
+#include "strutil.h"
+
+vector<string>* g_symbols;
+
+Symbol::Symbol(int v)
+    : v_(v) {
+}
+
+class Symtab {
+ public:
+  Symtab() {
+    CHECK(g_symbols == NULL);
+    g_symbols = &symbols_;
+    Symbol s = Intern("");
+    CHECK(s.v_ == 0);
+    CHECK(Intern("") == s);
+  }
+
+  Symbol Intern(StringPiece s) {
+    auto found = symtab_.find(s);
+    if (found != symtab_.end()) {
+      return found->second;
+    }
+    symbols_.push_back(s.as_string());
+    Symbol sym = Symbol(symtab_.size());
+    bool ok = symtab_.emplace(symbols_.back(), sym).second;
+    CHECK(ok);
+    return sym;
+  }
+
+ private:
+  unordered_map<StringPiece, Symbol> symtab_;
+  vector<string> symbols_;
+};
+
+static Symtab* g_symtab;
+
+void InitSymtab() {
+  g_symtab = new Symtab;
+}
+
+void QuitSymtab() {
+  delete g_symtab;
+}
+
+Symbol Intern(StringPiece s) {
+  return g_symtab->Intern(s);
+}
+
+string JoinSymbols(const vector<Symbol>& syms, const char* sep) {
+  vector<string> strs;
+  for (Symbol s : syms) {
+    strs.push_back(s.str());
+  }
+  return JoinStrings(strs, sep);
+}
diff --git a/symtab.h b/symtab.h
new file mode 100644
index 0000000..55e1ce3
--- /dev/null
+++ b/symtab.h
@@ -0,0 +1,76 @@
+// Copyright 2015 Google Inc. All rights reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SYMTAB_H_
+#define SYMTAB_H_
+
+#include <string>
+#include <vector>
+
+#include "string_piece.h"
+
+using namespace std;
+
+extern vector<string>* g_symbols;
+
+class Symtab;
+
+class Symbol {
+ public:
+  const string& str() const {
+    return (*g_symbols)[v_];
+  }
+
+  const char* c_str() const {
+    return str().c_str();
+  }
+
+  bool empty() const { return !v_; }
+
+  int val() const { return v_; }
+
+  char get(size_t i) const {
+    const string& s = str();
+    if (i >= s.size())
+      return 0;
+    return s[i];
+  }
+
+ private:
+  explicit Symbol(int v);
+
+  int v_;
+
+  friend class Symtab;
+};
+
+inline bool operator==(const Symbol& x, const Symbol& y) {
+  return x.val() == y.val();
+}
+
+namespace std {
+template<> struct hash<Symbol> {
+  size_t operator()(const Symbol& s) const {
+    return s.val();
+  }
+};
+}
+
+void InitSymtab();
+void QuitSymtab();
+Symbol Intern(StringPiece s);
+
+string JoinSymbols(const vector<Symbol>& syms, const char* sep);
+
+#endif  // SYMTAB_H_
diff --git a/value.cc b/value.cc
index 5337e4f..873111e 100644
--- a/value.cc
+++ b/value.cc
@@ -131,13 +131,13 @@
 
   virtual shared_ptr<string> Eval(Evaluator* ev) const override {
     shared_ptr<string> name = name_->Eval(ev);
-    Var* v = ev->LookupVar(*name);
+    Var* v = ev->LookupVar(Intern(*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);
+    Var* v = ev->LookupVar(Intern(*name));
     v->Eval(ev, s);
   }
 
@@ -162,7 +162,7 @@
 
   virtual void Eval(Evaluator* ev, string* s) const override {
     shared_ptr<string> name = name_->Eval(ev);
-    Var* v = ev->LookupVar(*name);
+    Var* v = ev->LookupVar(Intern(*name));
     shared_ptr<string> value = v->Eval(ev);
     shared_ptr<string> pat_str = pat_->Eval(ev);
     shared_ptr<string> subst = subst_->Eval(ev);
diff --git a/var.cc b/var.cc
index 64385c0..c0926c6 100644
--- a/var.cc
+++ b/var.cc
@@ -110,14 +110,14 @@
   }
 }
 
-Var* Vars::Lookup(StringPiece name) const {
+Var* Vars::Lookup(Symbol name) const {
   auto found = find(name);
   if (found == end())
     return kUndefined;
   return found->second;
 }
 
-void Vars::Assign(StringPiece name, Var* v) {
+void Vars::Assign(Symbol name, Var* v) {
   auto p = insert(make_pair(name, v));
   if (!p.second) {
     Var* orig = p.first->second;
@@ -134,7 +134,7 @@
   }
 }
 
-ScopedVar::ScopedVar(Vars* vars, StringPiece name, Var* var)
+ScopedVar::ScopedVar(Vars* vars, Symbol name, Var* var)
     : vars_(vars), orig_(NULL) {
   auto p = vars->insert(make_pair(name, var));
   iter_ = p.first;
diff --git a/var.h b/var.h
index 85f3490..eac1f45 100644
--- a/var.h
+++ b/var.h
@@ -21,6 +21,7 @@
 
 #include "ast.h"
 #include "string_piece.h"
+#include "symtab.h"
 #include "value.h"
 
 using namespace std;
@@ -170,19 +171,19 @@
   AssignOp op_;
 };
 
-class Vars : public unordered_map<StringPiece, Var*> {
+class Vars : public unordered_map<Symbol, Var*> {
  public:
   ~Vars();
 
-  Var* Lookup(StringPiece name) const;
+  Var* Lookup(Symbol name) const;
 
-  void Assign(StringPiece name, Var* v);
+  void Assign(Symbol name, Var* v);
 };
 
 class ScopedVar {
  public:
   // Does not take ownerships of arguments.
-  ScopedVar(Vars* vars, StringPiece name, Var* var);
+  ScopedVar(Vars* vars, Symbol name, Var* var);
   ~ScopedVar();
 
  private: