[debugger] Move ExprTokenType to a separate file.

The ExprToken needs to have more information associated with it to
support future extensions. As a first step, this moves the enum out of
the ExprToken class and into its own toplevel enum class.

Adds a helper function to ExprParser to convert a token to a parser
dispatch info record.

There should be no functionality change, this is mostly
search-and-replace.

There are some collateral chang-format updates.

Change-Id: I549aec08117ae3a025a0fec1a19ae6eaba7e2ac6
diff --git a/garnet/bin/zxdb/expr/BUILD.gn b/garnet/bin/zxdb/expr/BUILD.gn
index 214e6f6..805ca6c 100644
--- a/garnet/bin/zxdb/expr/BUILD.gn
+++ b/garnet/bin/zxdb/expr/BUILD.gn
@@ -18,6 +18,7 @@
     "expr_parser.cc",
     "expr_parser.h",
     "expr_token.h",
+    "expr_token_type.h",
     "expr_tokenizer.cc",
     "expr_tokenizer.h",
     "expr_value.cc",
diff --git a/garnet/bin/zxdb/expr/eval_operators.cc b/garnet/bin/zxdb/expr/eval_operators.cc
index c41102f..24b38d5 100644
--- a/garnet/bin/zxdb/expr/eval_operators.cc
+++ b/garnet/bin/zxdb/expr/eval_operators.cc
@@ -46,7 +46,7 @@
   // the coerced value.
   context->GetDataProvider()->WriteMemory(
       dest.address(), std::move(data),
-      [ coerced = std::move(coerced), cb = std::move(cb) ](const Err& err) {
+      [coerced = std::move(coerced), cb = std::move(cb)](const Err& err) {
         if (err.has_error())
           cb(err, ExprValue());
         else
@@ -58,15 +58,15 @@
                       const ExprValue& left_value, const ExprToken& op,
                       const ExprValue& right_value, EvalCallback cb) {
   switch (op.type()) {
-    case ExprToken::kEquals:
+    case ExprTokenType::kEquals:
       DoAssignment(std::move(context), left_value, right_value, std::move(cb));
       break;
 
-    case ExprToken::kEquality:
-    case ExprToken::kAmpersand:
-    case ExprToken::kDoubleAnd:
-    case ExprToken::kBitwiseOr:
-    case ExprToken::kLogicalOr:
+    case ExprTokenType::kEquality:
+    case ExprTokenType::kAmpersand:
+    case ExprTokenType::kDoubleAnd:
+    case ExprTokenType::kBitwiseOr:
+    case ExprTokenType::kLogicalOr:
     default:
       cb(Err("Unsupported binary operator '%s', sorry!", op.value().c_str()),
          ExprValue());
@@ -79,7 +79,7 @@
 void EvalBinaryOperator(fxl::RefPtr<ExprEvalContext> context,
                         const fxl::RefPtr<ExprNode>& left, const ExprToken& op,
                         const fxl::RefPtr<ExprNode>& right, EvalCallback cb) {
-  left->Eval(context, [ context, op, right, cb = std::move(cb) ](
+  left->Eval(context, [context, op, right, cb = std::move(cb)](
                           const Err& err, ExprValue left_value) {
     if (err.has_error()) {
       cb(err, ExprValue());
@@ -88,12 +88,12 @@
 
     // Note: if we implement ||, need to special-case here so evaluation
     // short-circuits if the "left" is true.
-    right->Eval(context, [
-      context, left_value = std::move(left_value), op, cb = std::move(cb)
-    ](const Err& err, ExprValue right_value) {
-      DoBinaryOperator(std::move(context), left_value, op, right_value,
-                       std::move(cb));
-    });
+    right->Eval(context,
+                [context, left_value = std::move(left_value), op,
+                 cb = std::move(cb)](const Err& err, ExprValue right_value) {
+                  DoBinaryOperator(std::move(context), left_value, op,
+                                   right_value, std::move(cb));
+                });
   });
 }
 
diff --git a/garnet/bin/zxdb/expr/eval_operators_unittest.cc b/garnet/bin/zxdb/expr/eval_operators_unittest.cc
index f185eac..545d26c 100644
--- a/garnet/bin/zxdb/expr/eval_operators_unittest.cc
+++ b/garnet/bin/zxdb/expr/eval_operators_unittest.cc
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "garnet/bin/zxdb/expr/eval_operators.h"
 #include "garnet/bin/zxdb/common/err.h"
 #include "garnet/bin/zxdb/common/test_with_loop.h"
-#include "garnet/bin/zxdb/expr/eval_operators.h"
 #include "garnet/bin/zxdb/expr/expr_value.h"
 #include "garnet/bin/zxdb/expr/mock_expr_eval_context.h"
 #include "garnet/bin/zxdb/expr/mock_expr_node.h"
@@ -18,8 +18,7 @@
 
 class EvalOperators : public TestWithLoop {
  public:
-  EvalOperators() : eval_context_(fxl::MakeRefCounted<MockExprEvalContext>()) {
-  }
+  EvalOperators() : eval_context_(fxl::MakeRefCounted<MockExprEvalContext>()) {}
   ~EvalOperators() = default;
 
   fxl::RefPtr<MockExprEvalContext>& eval_context() { return eval_context_; }
@@ -28,9 +27,7 @@
   fxl::RefPtr<MockExprEvalContext> eval_context_;
 };
 
-void QuitNow() {
-  debug_ipc::MessageLoop::Current()->QuitNow();
-}
+void QuitNow() { debug_ipc::MessageLoop::Current()->QuitNow(); }
 
 }  // namespace
 
@@ -43,22 +40,23 @@
   ExprValue dest(int32_type, {0, 0, 0, 0}, ExprValueSource(kAddress));
   auto dest_node = fxl::MakeRefCounted<MockExprNode>(false, dest);
 
-  ExprToken assign(ExprToken::kEquals, "=", 0);
+  ExprToken assign(ExprTokenType::kEquals, "=", 0);
 
-  std::vector<uint8_t> data { 0x12, 0x34, 0x56, 0x78 };
+  std::vector<uint8_t> data{0x12, 0x34, 0x56, 0x78};
   ExprValue source(int32_type, data, ExprValueSource());
   auto source_node = fxl::MakeRefCounted<MockExprNode>(false, source);
 
   bool called = false;
   Err out_err;
   ExprValue out_value;
-  EvalBinaryOperator(eval_context(), dest_node, assign, source_node,
+  EvalBinaryOperator(
+      eval_context(), dest_node, assign, source_node,
       [&called, &out_err, &out_value](const Err& err, ExprValue value) {
-      called = true;
-    out_err = err;
-    out_value = value;
-    QuitNow();
-  });
+        called = true;
+        out_err = err;
+        out_value = value;
+        QuitNow();
+      });
 
   EXPECT_FALSE(called);
   loop().Run();
diff --git a/garnet/bin/zxdb/expr/expr.cc b/garnet/bin/zxdb/expr/expr.cc
index 141bbdd5..4fc023e 100644
--- a/garnet/bin/zxdb/expr/expr.cc
+++ b/garnet/bin/zxdb/expr/expr.cc
@@ -27,7 +27,7 @@
     // Add context information since we have the original input string (the
     // parser doesn't have this).
     ExprToken error_token = parser.error_token();
-    if (error_token.type() != ExprToken::Type::kInvalid) {
+    if (error_token.type() != ExprTokenType::kInvalid) {
       Err context_err(
           parser.err().type(),
           parser.err().msg() + "\n" +
diff --git a/garnet/bin/zxdb/expr/expr_node.cc b/garnet/bin/zxdb/expr/expr_node.cc
index 0c41864..287318b 100644
--- a/garnet/bin/zxdb/expr/expr_node.cc
+++ b/garnet/bin/zxdb/expr/expr_node.cc
@@ -47,7 +47,7 @@
   //
   // TODO(brettw) when we add more mathematical operations we'll want a
   // more flexible system for getting the results out.
-  if (op_token.type() == ExprToken::kMinus) {
+  if (op_token.type() == ExprTokenType::kMinus) {
     // Currently "-" is the only unary operator.  Since this is a debugger
     // primarily for C-like languages, use the C rules for negating values: the
     // result type is the same as the input, and negating an unsigned value
@@ -338,17 +338,17 @@
 void LiteralExprNode::Eval(fxl::RefPtr<ExprEvalContext> context,
                            EvalCallback cb) const {
   switch (token_.type()) {
-    case ExprToken::kInteger: {
+    case ExprTokenType::kInteger: {
       ExprValue value;
       Err err = StringToNumber(token_.value(), &value);
       cb(err, std::move(value));
       break;
     }
-    case ExprToken::kTrue: {
+    case ExprTokenType::kTrue: {
       cb(Err(), ExprValue(true));
       break;
     }
-    case ExprToken::kFalse: {
+    case ExprTokenType::kFalse: {
       cb(Err(), ExprValue(false));
       break;
     }
@@ -363,7 +363,7 @@
 
 void MemberAccessExprNode::Eval(fxl::RefPtr<ExprEvalContext> context,
                                 EvalCallback cb) const {
-  bool is_arrow = accessor_.type() == ExprToken::kArrow;
+  bool is_arrow = accessor_.type() == ExprTokenType::kArrow;
   left_->EvalFollowReferences(
       context, [context, is_arrow, member = member_, cb = std::move(cb)](
                    const Err& err, ExprValue base) {
diff --git a/garnet/bin/zxdb/expr/expr_node_unittest.cc b/garnet/bin/zxdb/expr/expr_node_unittest.cc
index 33a8441..abc5600 100644
--- a/garnet/bin/zxdb/expr/expr_node_unittest.cc
+++ b/garnet/bin/zxdb/expr/expr_node_unittest.cc
@@ -39,7 +39,7 @@
 
   // This identifier should be found synchronously and returned.
   auto good_identifier = fxl::MakeRefCounted<IdentifierExprNode>(
-      ExprToken(ExprToken::Type::kName, "foo", 0));
+      ExprToken(ExprTokenType::kName, "foo", 0));
   bool called = false;
   Err out_err;
   ExprValue out_value;
@@ -57,7 +57,7 @@
 
   // This identifier should be not found.
   auto bad_identifier = fxl::MakeRefCounted<IdentifierExprNode>(
-      ExprToken(ExprToken::Type::kName, "bar", 0));
+      ExprToken(ExprTokenType::kName, "bar", 0));
   called = false;
   out_value = ExprValue();
   bad_identifier->Eval(context, [&called, &out_err, &out_value](
@@ -80,7 +80,7 @@
   context->AddVariable("foo", foo_expected);
 
   auto identifier = fxl::MakeRefCounted<IdentifierExprNode>(
-      ExprToken(ExprToken::kName, "foo", 0));
+      ExprToken(ExprTokenType::kName, "foo", 0));
 
   // Validate the value by itself. This also has the effect of checking the
   // ExprValue type-specific constructor.
@@ -102,7 +102,7 @@
 
   // Apply a unary '-' to that value.
   auto unary = fxl::MakeRefCounted<UnaryOpExprNode>(
-      ExprToken(ExprToken::kMinus, "-", 0), std::move(identifier));
+      ExprToken(ExprTokenType::kMinus, "-", 0), std::move(identifier));
   unary->Eval(context,
               [&called, &out_err, &out_value](const Err& err, ExprValue value) {
                 called = true;
@@ -154,9 +154,9 @@
   context->AddVariable("foo", expected);
 
   auto identifier = fxl::MakeRefCounted<IdentifierExprNode>(
-      ExprToken(ExprToken::kName, "foo", 0));
+      ExprToken(ExprTokenType::kName, "foo", 0));
   auto unary = fxl::MakeRefCounted<UnaryOpExprNode>(
-      ExprToken(ExprToken::kMinus, "-", 0), std::move(identifier));
+      ExprToken(ExprTokenType::kMinus, "-", 0), std::move(identifier));
 
   bool called = false;
   Err out_err;
@@ -363,7 +363,7 @@
   // Parameter for the call.
   std::vector<fxl::RefPtr<ExprNode>> args;
   args.push_back(fxl::MakeRefCounted<LiteralExprNode>(
-      ExprToken(ExprToken::Type::kInteger, "255", 0)));
+      ExprToken(ExprTokenType::kInteger, "255", 0)));
 
   auto call_node =
       fxl::MakeRefCounted<FunctionCallExprNode>(reinterpret, std::move(args));
@@ -394,8 +394,8 @@
   auto struct_node = fxl::MakeRefCounted<MockExprNode>(
       true, ExprValue(sc, {0x78, 0x56, 0x34, 0x12}));
   auto access_node = fxl::MakeRefCounted<MemberAccessExprNode>(
-      struct_node, ExprToken(ExprToken::Type::kDot, ".", 0),
-      Identifier(ExprToken(ExprToken::Type::kName, "a", 0)));
+      struct_node, ExprToken(ExprTokenType::kDot, ".", 0),
+      Identifier(ExprToken(ExprTokenType::kName, "a", 0)));
 
   // Do the call.
   bool called = false;
@@ -429,8 +429,8 @@
       false, ExprValue(foo_ptr_type,
                        {0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
   auto access_ptr_node = fxl::MakeRefCounted<MemberAccessExprNode>(
-      struct_ptr_node, ExprToken(ExprToken::Type::kArrow, "->", 0),
-      Identifier(ExprToken(ExprToken::Type::kName, "b", 0)));
+      struct_ptr_node, ExprToken(ExprTokenType::kArrow, "->", 0),
+      Identifier(ExprToken(ExprTokenType::kName, "b", 0)));
 
   // Do the call.
   called = false;
diff --git a/garnet/bin/zxdb/expr/expr_parser.cc b/garnet/bin/zxdb/expr/expr_parser.cc
index f325a97e..ff7e011 100644
--- a/garnet/bin/zxdb/expr/expr_parser.cc
+++ b/garnet/bin/zxdb/expr/expr_parser.cc
@@ -126,8 +126,9 @@
                        NameLookupCallback name_lookup)
     : name_lookup_callback_(std::move(name_lookup)),
       tokens_(std::move(tokens)) {
-  static_assert(arraysize(ExprParser::kDispatchInfo) == ExprToken::kNumTypes,
-                "kDispatchInfo needs updating to match ExprToken::Type");
+  static_assert(arraysize(ExprParser::kDispatchInfo) ==
+                    static_cast<int>(ExprTokenType::kNumTypes),
+                "kDispatchInfo needs updating to match ExprTokenType");
 }
 
 fxl::RefPtr<ExprNode> ExprParser::Parse() {
@@ -153,7 +154,7 @@
     return nullptr;
 
   const ExprToken& token = Consume();
-  PrefixFunc prefix = kDispatchInfo[token.type()].prefix;
+  PrefixFunc prefix = DispatchForToken(token).prefix;
 
   if (!prefix) {
     SetError(token, fxl::StringPrintf("Unexpected token '%s'.",
@@ -165,10 +166,9 @@
   if (has_error())
     return left;
 
-  while (!at_end() &&
-         precedence < kDispatchInfo[cur_token().type()].precedence) {
+  while (!at_end() && precedence < DispatchForToken(cur_token()).precedence) {
     const ExprToken& next_token = Consume();
-    InfixFunc infix = kDispatchInfo[next_token.type()].infix;
+    InfixFunc infix = DispatchForToken(next_token).infix;
     if (!infix) {
       SetError(next_token, fxl::StringPrintf("Unexpected token '%s'.",
                                              next_token.value().c_str()));
@@ -227,7 +227,7 @@
   while (!at_end()) {
     const ExprToken& token = cur_token();
     switch (token.type()) {
-      case ExprToken::kColonColon: {
+      case ExprTokenType::kColonColon: {
         // "::" can only follow nothing, a namespace or type name.
         if (mode != kBegin && mode != kNamespace && mode != kType &&
             mode != kAnything) {
@@ -243,7 +243,7 @@
         break;
       }
 
-      case ExprToken::kLess: {
+      case ExprTokenType::kLess: {
         // "<" can only come after a template name.
         if (mode == kNamespace || mode == kType) {
           // Generate a nicer error for these cases.
@@ -267,13 +267,14 @@
         prev_token = &Consume();  // Eat the "<".
 
         // Extract the contents of the template.
-        std::vector<std::string> list = ParseTemplateList(ExprToken::kGreater);
+        std::vector<std::string> list =
+            ParseTemplateList(ExprTokenType::kGreater);
         if (has_error())
           return ParseNameResult();
 
         // Ending ">".
         const ExprToken& template_end =
-            Consume(ExprToken::kGreater, token, "Expected '>' to match.");
+            Consume(ExprTokenType::kGreater, token, "Expected '>' to match.");
         if (has_error())
           return ParseNameResult();
 
@@ -307,7 +308,7 @@
         continue;  // Don't Consume() since we already ate the token.
       }
 
-      case ExprToken::kName: {
+      case ExprTokenType::kName: {
         // Names can only follow nothing or "::".
         if (mode == kType) {
           // Normally in C++ a name can follow a type, so make a special error
@@ -447,13 +448,13 @@
   while (!at_end()) {
     // Read the operator.
     const ExprToken& token = cur_token();
-    if (token.type() == ExprToken::kStar) {
+    if (token.type() == ExprTokenType::kStar) {
       type = fxl::MakeRefCounted<ModifiedType>(DwarfTag::kPointerType,
                                                LazySymbol(std::move(type)));
-    } else if (token.type() == ExprToken::kAmpersand) {
+    } else if (token.type() == ExprTokenType::kAmpersand) {
       type = fxl::MakeRefCounted<ModifiedType>(DwarfTag::kReferenceType,
                                                LazySymbol(std::move(type)));
-    } else if (token.type() == ExprToken::kDoubleAnd) {
+    } else if (token.type() == ExprTokenType::kDoubleAnd) {
       type = fxl::MakeRefCounted<ModifiedType>(DwarfTag::kRvalueReferenceType,
                                                LazySymbol(std::move(type)));
     } else {
@@ -476,7 +477,7 @@
 // A list is any sequence of comma-separated types. We don't parse the types
 // (this is hard) but instead skip over them.
 std::vector<std::string> ExprParser::ParseTemplateList(
-    ExprToken::Type stop_before) {
+    ExprTokenType stop_before) {
   std::vector<std::string> result;
 
   bool first_time = true;
@@ -485,7 +486,7 @@
       first_time = false;
     } else {
       // Expect comma to separate items.
-      if (LookAhead(ExprToken::kComma)) {
+      if (LookAhead(ExprTokenType::kComma)) {
         Consume();
       } else {
         SetError(cur_token(), "Expected ',' separating expressions.");
@@ -515,7 +516,7 @@
 // do. A more general approach would implement a comma infix which constructs a
 // new type of ExprNode.
 std::vector<fxl::RefPtr<ExprNode>> ExprParser::ParseExpressionList(
-    ExprToken::Type stop_before) {
+    ExprTokenType stop_before) {
   std::vector<fxl::RefPtr<ExprNode>> result;
 
   bool first_time = true;
@@ -524,7 +525,7 @@
       first_time = false;
     } else {
       // Expect comma to separate items.
-      if (LookAhead(ExprToken::kComma)) {
+      if (LookAhead(ExprTokenType::kComma)) {
         Consume();
       } else {
         SetError(cur_token(), "Expected ',' separating expressions.");
@@ -552,7 +553,7 @@
 
 fxl::RefPtr<ExprNode> ExprParser::BinaryOpInfix(fxl::RefPtr<ExprNode> left,
                                                 const ExprToken& token) {
-  const DispatchInfo& dispatch = kDispatchInfo[token.type()];
+  const DispatchInfo& dispatch = DispatchForToken(token);
   fxl::RefPtr<ExprNode> right = ParseExpression(dispatch.precedence);
   if (!has_error() && !right) {
     SetError(token, fxl::StringPrintf("Expected expression after '%s'.",
@@ -593,7 +594,7 @@
   if (!has_error() && !expr)
     SetError(token, "Expected expression inside '('.");
   if (!has_error())
-    Consume(ExprToken::kRightParen, token, "Expected ')' to match.");
+    Consume(ExprTokenType::kRightParen, token, "Expected ')' to match.");
   if (has_error())
     return nullptr;
 
@@ -627,10 +628,10 @@
 
   // Read the function parameters.
   std::vector<fxl::RefPtr<ExprNode>> args =
-      ParseExpressionList(ExprToken::Type::kRightParen);
+      ParseExpressionList(ExprTokenType::kRightParen);
   if (has_error())
     return nullptr;
-  Consume(ExprToken::kRightParen, token, "Expected ')' to match.");
+  Consume(ExprTokenType::kRightParen, token, "Expected ')' to match.");
 
   return fxl::MakeRefCounted<FunctionCallExprNode>(std::move(name),
                                                    std::move(args));
@@ -642,7 +643,7 @@
   if (!has_error() && !inner)
     SetError(token, "Expected expression inside '['.");
   if (!has_error())
-    Consume(ExprToken::kRightSquare, token, "Expected ']' to match.");
+    Consume(ExprTokenType::kRightSquare, token, "Expected ']' to match.");
   if (has_error())
     return nullptr;
   return fxl::MakeRefCounted<ArrayAccessExprNode>(std::move(left),
@@ -687,9 +688,9 @@
   FXL_DCHECK(cur_ > 0);
   cur_--;
 
-  if (token.type() == ExprToken::kConst ||
-      token.type() == ExprToken::kVolatile ||
-      token.type() == ExprToken::kRestrict) {
+  if (token.type() == ExprTokenType::kConst ||
+      token.type() == ExprTokenType::kVolatile ||
+      token.type() == ExprTokenType::kRestrict) {
     // These start a type name, force type parsing mode.
     fxl::RefPtr<Type> type = ParseType(fxl::RefPtr<Type>());
     if (has_error())
@@ -715,7 +716,7 @@
   return fxl::MakeRefCounted<DereferenceExprNode>(std::move(right));
 }
 
-bool ExprParser::LookAhead(ExprToken::Type type) const {
+bool ExprParser::LookAhead(ExprTokenType type) const {
   if (at_end())
     return false;
   return cur_token().type() == type;
@@ -727,7 +728,7 @@
   return tokens_[cur_++];
 }
 
-const ExprToken& ExprParser::Consume(ExprToken::Type type,
+const ExprToken& ExprParser::Consume(ExprTokenType type,
                                      const ExprToken& error_token,
                                      const char* error_msg) {
   FXL_DCHECK(!has_error());  // Should have error-checked before calling.
@@ -749,11 +750,11 @@
     const ExprToken& token = cur_token();
 
     DwarfTag tag = DwarfTag::kNone;
-    if (token.type() == ExprToken::kConst) {
+    if (token.type() == ExprTokenType::kConst) {
       tag = DwarfTag::kConstType;
-    } else if (token.type() == ExprToken::kVolatile) {
+    } else if (token.type() == ExprTokenType::kVolatile) {
       tag = DwarfTag::kVolatileType;
-    } else if (token.type() == ExprToken::kRestrict) {
+    } else if (token.type() == ExprTokenType::kRestrict) {
       tag = DwarfTag::kRestrictType;
     } else {
       // Not a qualification token, done.
@@ -790,4 +791,12 @@
   error_token_ = token;
 }
 
+// static
+const ExprParser::DispatchInfo& ExprParser::DispatchForToken(
+    const ExprToken& token) {
+  size_t index = static_cast<size_t>(token.type());
+  FXL_DCHECK(index < arraysize(kDispatchInfo));
+  return kDispatchInfo[index];
+}
+
 }  // namespace zxdb
diff --git a/garnet/bin/zxdb/expr/expr_parser.h b/garnet/bin/zxdb/expr/expr_parser.h
index 9d0a4c6..cc849e6 100644
--- a/garnet/bin/zxdb/expr/expr_parser.h
+++ b/garnet/bin/zxdb/expr/expr_parser.h
@@ -89,12 +89,12 @@
 
   // Parses template parameter lists. The "stop_before" parameter indicates how
   // the list is expected to end (i.e. ">"). Sets the error on failure.
-  std::vector<std::string> ParseTemplateList(ExprToken::Type stop_before);
+  std::vector<std::string> ParseTemplateList(ExprTokenType stop_before);
 
   // Parses comma-separated lists of expressions. Runs until the given ending
   // token is found (normally a ')' for a function call).
   std::vector<fxl::RefPtr<ExprNode>> ParseExpressionList(
-      ExprToken::Type stop_before);
+      ExprTokenType stop_before);
 
   fxl::RefPtr<ExprNode> AmpersandPrefix(const ExprToken& token);
   fxl::RefPtr<ExprNode> BinaryOpInfix(fxl::RefPtr<ExprNode> left,
@@ -116,7 +116,7 @@
   fxl::RefPtr<ExprNode> StarPrefix(const ExprToken& token);
 
   // Returns true if the next token is the given type.
-  bool LookAhead(ExprToken::Type type) const;
+  bool LookAhead(ExprTokenType type) const;
 
   // Returns the next token or the invalid token if nothing is left. Advances
   // to the next token.
@@ -126,7 +126,7 @@
   // available and the type matches. Otherwise, sets the error condition using
   // the given error_token and message, and returns a reference to an invalid
   // token. It will advance to the next token.
-  const ExprToken& Consume(ExprToken::Type type, const ExprToken& error_token,
+  const ExprToken& Consume(ExprTokenType type, const ExprToken& error_token,
                            const char* error_msg);
 
   // Reads a sequence of cv-qualifiers (plus "restrict" for C) and appends to
@@ -141,9 +141,8 @@
 
   // Applies the given type modifier tags to the given input in order and
   // returns the newly qualified type.
-  fxl::RefPtr<Type> ApplyQualifiers(
-      fxl::RefPtr<Type> input,
-      const std::vector<DwarfTag>& qual);
+  fxl::RefPtr<Type> ApplyQualifiers(fxl::RefPtr<Type> input,
+                                    const std::vector<DwarfTag>& qual);
 
   void SetError(const ExprToken& token, std::string msg);
 
@@ -153,6 +152,7 @@
   bool has_error() const { return err_.has_error(); }
   bool at_end() const { return cur_ == tokens_.size(); }
 
+  static const DispatchInfo& DispatchForToken(const ExprToken& token);
   static DispatchInfo kDispatchInfo[];
 
   // Possibly null, see constructor.
diff --git a/garnet/bin/zxdb/expr/expr_parser_unittest.cc b/garnet/bin/zxdb/expr/expr_parser_unittest.cc
index f2195fc..38e7dcf 100644
--- a/garnet/bin/zxdb/expr/expr_parser_unittest.cc
+++ b/garnet/bin/zxdb/expr/expr_parser_unittest.cc
@@ -112,7 +112,7 @@
 
   const MemberAccessExprNode* access = result->AsMemberAccess();
   ASSERT_TRUE(access);
-  EXPECT_EQ(ExprToken::kDot, access->accessor().type());
+  EXPECT_EQ(ExprTokenType::kDot, access->accessor().type());
   EXPECT_EQ(".", access->accessor().value());
 
   // Left side is the "base" identifier.
@@ -154,7 +154,7 @@
 
   const MemberAccessExprNode* access = result->AsMemberAccess();
   ASSERT_TRUE(access);
-  EXPECT_EQ(ExprToken::kArrow, access->accessor().type());
+  EXPECT_EQ(ExprTokenType::kArrow, access->accessor().type());
   EXPECT_EQ("->", access->accessor().value());
 
   // Left side is the "base" identifier.
diff --git a/garnet/bin/zxdb/expr/expr_token.h b/garnet/bin/zxdb/expr/expr_token.h
index 9b6044f..d347c196 100644
--- a/garnet/bin/zxdb/expr/expr_token.h
+++ b/garnet/bin/zxdb/expr/expr_token.h
@@ -6,6 +6,8 @@
 
 #include <string>
 
+#include "garnet/bin/zxdb/expr/expr_token_type.h"
+
 namespace zxdb {
 
 // A parsed token. This token does not own the strings, it's intended to be
@@ -13,55 +15,18 @@
 // parsed.
 class ExprToken {
  public:
-  // This type must start at 0 and increment monotonically since it is used
-  // as an index into the parser lookup table.
-  enum Type : int {
-    kInvalid = 0,
-    kName,         // random_text
-    kInteger,      // 123, 0x89ab
-    kEquals,       // =
-    kEquality,     // ==
-    kDot,          // .
-    kComma,        // ,
-    kStar,         // *
-    kAmpersand,    // &
-    kDoubleAnd,    // && (logical "and" or rvalue reference)
-    kBitwiseOr,    // |
-    kLogicalOr,    // ||
-    kArrow,        // ->
-    kLeftSquare,   // [
-    kRightSquare,  // ]
-    kLeftParen,    // (
-    kRightParen,   // )
-    kLess,         // <
-    kGreater,      // >
-    kMinus,        // - (by itself, not part of "->")
-    kPlus,         // +
-    kColonColon,   // ::
-
-    // Special keywords.
-    kTrue,         // true
-    kFalse,        // false
-    kConst,        // const
-    kVolatile,     // volatile
-    kRestrict,     // restrict
-
-    // Keep last. Not a token, but the count of tokens.
-    kNumTypes
-  };
-
   ExprToken() = default;
-  ExprToken(Type type, const std::string& value, size_t byte_offset)
+  ExprToken(ExprTokenType type, const std::string& value, size_t byte_offset)
       : type_(type), value_(value), byte_offset_(byte_offset) {}
 
-  Type type() const { return type_; }
+  ExprTokenType type() const { return type_; }
   const std::string& value() const { return value_; }
 
   // Offset into the input string where this token begins.
   size_t byte_offset() const { return byte_offset_; }
 
  private:
-  Type type_ = kInvalid;
+  ExprTokenType type_ = ExprTokenType::kInvalid;
   std::string value_;
   size_t byte_offset_ = 0;
 };
diff --git a/garnet/bin/zxdb/expr/expr_token_type.h b/garnet/bin/zxdb/expr/expr_token_type.h
new file mode 100644
index 0000000..12a95c0
--- /dev/null
+++ b/garnet/bin/zxdb/expr/expr_token_type.h
@@ -0,0 +1,46 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+namespace zxdb {
+
+// This type must start at 0 and increment monotonically since it is used
+// as an index into the parser lookup table.
+enum class ExprTokenType : size_t {
+  kInvalid = 0,
+  kName,         // random_text
+  kInteger,      // 123, 0x89ab
+  kEquals,       // =
+  kEquality,     // ==
+  kDot,          // .
+  kComma,        // ,
+  kStar,         // *
+  kAmpersand,    // &
+  kDoubleAnd,    // && (logical "and" or rvalue reference)
+  kBitwiseOr,    // |
+  kLogicalOr,    // ||
+  kArrow,        // ->
+  kLeftSquare,   // [
+  kRightSquare,  // ]
+  kLeftParen,    // (
+  kRightParen,   // )
+  kLess,         // <
+  kGreater,      // >
+  kMinus,        // - (by itself, not part of "->")
+  kPlus,         // +
+  kColonColon,   // ::
+
+  // Special keywords.
+  kTrue,         // true
+  kFalse,        // false
+  kConst,        // const
+  kVolatile,     // volatile
+  kRestrict,     // restrict
+
+  // Keep last. Not a token, but the count of tokens.
+  kNumTypes
+};
+
+}  // namespace zxdb
diff --git a/garnet/bin/zxdb/expr/expr_tokenizer.cc b/garnet/bin/zxdb/expr/expr_tokenizer.cc
index 35dcecf..5de582f 100644
--- a/garnet/bin/zxdb/expr/expr_tokenizer.cc
+++ b/garnet/bin/zxdb/expr/expr_tokenizer.cc
@@ -38,7 +38,7 @@
     if (done())
       break;
 
-    ExprToken::Type type = ClassifyCurrent();
+    ExprTokenType type = ClassifyCurrent();
     if (has_error())
       break;
 
@@ -83,68 +83,68 @@
     AdvanceOneChar();
 }
 
-void ExprTokenizer::AdvanceToEndOfToken(ExprToken::Type type) {
+void ExprTokenizer::AdvanceToEndOfToken(ExprTokenType type) {
   switch (type) {
-    case ExprToken::kInteger:
+    case ExprTokenType::kInteger:
       do {
         AdvanceOneChar();
       } while (!at_end() && IsIntegerContinuingChar(cur_char()));
       break;
 
-    case ExprToken::kName:
+    case ExprTokenType::kName:
       do {
         AdvanceOneChar();
       } while (!at_end() && IsNameContinuingChar(cur_char()));
       break;
 
-    case ExprToken::kArrow:
-    case ExprToken::kColonColon:
-    case ExprToken::kEquality:
-    case ExprToken::kDoubleAnd:
-    case ExprToken::kLogicalOr:
+    case ExprTokenType::kArrow:
+    case ExprTokenType::kColonColon:
+    case ExprTokenType::kEquality:
+    case ExprTokenType::kDoubleAnd:
+    case ExprTokenType::kLogicalOr:
       // The classification code should already have validated there were two
       // characters available.
       AdvanceOneChar();
       AdvanceOneChar();
       break;
 
-    case ExprToken::kEquals:
-    case ExprToken::kDot:
-    case ExprToken::kComma:
-    case ExprToken::kStar:
-    case ExprToken::kAmpersand:
-    case ExprToken::kBitwiseOr:
-    case ExprToken::kLeftSquare:
-    case ExprToken::kRightSquare:
-    case ExprToken::kLeftParen:
-    case ExprToken::kRightParen:
-    case ExprToken::kLess:
-    case ExprToken::kGreater:
-    case ExprToken::kMinus:
-    case ExprToken::kPlus:
+    case ExprTokenType::kEquals:
+    case ExprTokenType::kDot:
+    case ExprTokenType::kComma:
+    case ExprTokenType::kStar:
+    case ExprTokenType::kAmpersand:
+    case ExprTokenType::kBitwiseOr:
+    case ExprTokenType::kLeftSquare:
+    case ExprTokenType::kRightSquare:
+    case ExprTokenType::kLeftParen:
+    case ExprTokenType::kRightParen:
+    case ExprTokenType::kLess:
+    case ExprTokenType::kGreater:
+    case ExprTokenType::kMinus:
+    case ExprTokenType::kPlus:
       AdvanceOneChar();  // All are one char.
       break;
 
     // If we add too many more keywords we should have a more flexible system
     // rather than hardcoding all lengths here.
-    case ExprToken::kTrue:
+    case ExprTokenType::kTrue:
       AdvanceChars(4);
       break;
-    case ExprToken::kFalse:
+    case ExprTokenType::kFalse:
       AdvanceChars(5);
       break;
-    case ExprToken::kConst:
+    case ExprTokenType::kConst:
       AdvanceChars(5);
       break;
-    case ExprToken::kVolatile:
+    case ExprTokenType::kVolatile:
       AdvanceChars(8);
       break;
-    case ExprToken::kRestrict:
+    case ExprTokenType::kRestrict:
       AdvanceChars(8);
       break;
 
-    case ExprToken::kInvalid:
-    case ExprToken::kNumTypes:
+    case ExprTokenType::kInvalid:
+    case ExprTokenType::kNumTypes:
       FXL_NOTREACHED();
       err_ = Err("Internal parser error.");
       error_location_ = cur_;
@@ -165,9 +165,8 @@
 bool ExprTokenizer::IsCurrentName(std::string_view s) const {
   if (!IsCurrentString(s))
     return false;
-  return input_.size() == cur_ + s.size() ||  // End of buffer.
+  return input_.size() == cur_ + s.size() ||              // End of buffer.
          !IsNameContinuingChar(input_[cur_ + s.size()]);  // Non-name char.
-
 }
 
 bool ExprTokenizer::IsCurrentWhitespace() const {
@@ -176,30 +175,30 @@
   return c == 0x0A || c == 0x0D || c == 0x20;
 }
 
-ExprToken::Type ExprTokenizer::ClassifyCurrent() {
+ExprTokenType ExprTokenizer::ClassifyCurrent() {
   FXL_DCHECK(!at_end());
   char cur = cur_char();
 
   // Numbers.
   if (IsIntegerFirstChar(cur))
-    return ExprToken::kInteger;
+    return ExprTokenType::kInteger;
 
   // Words.
   if (IsNameFirstChar(cur)) {
     // Check for special keywords.
     if (IsCurrentName("true"))
-      return ExprToken::kTrue;
+      return ExprTokenType::kTrue;
     else if (IsCurrentName("false"))
-      return ExprToken::kFalse;
+      return ExprTokenType::kFalse;
     else if (IsCurrentName("const"))
-      return ExprToken::kConst;
+      return ExprTokenType::kConst;
     else if (IsCurrentName("volatile"))
-      return ExprToken::kVolatile;
+      return ExprTokenType::kVolatile;
     else if (IsCurrentName("restrict"))
-      return ExprToken::kRestrict;
+      return ExprTokenType::kRestrict;
 
     // Everything else is a general name.
-    return ExprToken::kName;
+    return ExprTokenType::kName;
   }
 
   // Punctuation.
@@ -208,66 +207,66 @@
       // Hyphen could be itself or an arrow, look ahead.
       if (can_advance()) {
         if (input_[cur_ + 1] == '>')
-          return ExprToken::kArrow;
+          return ExprTokenType::kArrow;
       }
       // Anything else is a standalone hyphen.
-      return ExprToken::kMinus;
+      return ExprTokenType::kMinus;
     case '=':
       // Check for "==".
       if (can_advance()) {
         if (input_[cur_ + 1] == '=')
-          return ExprToken::kEquality;
+          return ExprTokenType::kEquality;
       }
-      return ExprToken::kEquals;
+      return ExprTokenType::kEquals;
     case '.':
-      return ExprToken::kDot;
+      return ExprTokenType::kDot;
     case ',':
-      return ExprToken::kComma;
+      return ExprTokenType::kComma;
     case '*':
-      return ExprToken::kStar;
+      return ExprTokenType::kStar;
     case '&':
       // Check for "&&".
       if (can_advance()) {
         if (input_[cur_ + 1] == '&')
-          return ExprToken::kDoubleAnd;
+          return ExprTokenType::kDoubleAnd;
       }
-      return ExprToken::kAmpersand;
+      return ExprTokenType::kAmpersand;
     case '|':
       // Check for "||".
       if (can_advance()) {
         if (input_[cur_ + 1] == '|')
-          return ExprToken::kLogicalOr;
+          return ExprTokenType::kLogicalOr;
       }
-      return ExprToken::kBitwiseOr;
+      return ExprTokenType::kBitwiseOr;
     case '[':
-      return ExprToken::kLeftSquare;
+      return ExprTokenType::kLeftSquare;
     case ']':
-      return ExprToken::kRightSquare;
+      return ExprTokenType::kRightSquare;
     case '(':
-      return ExprToken::kLeftParen;
+      return ExprTokenType::kLeftParen;
     case ')':
-      return ExprToken::kRightParen;
+      return ExprTokenType::kRightParen;
     case '<':
-      return ExprToken::kLess;
+      return ExprTokenType::kLess;
     case '>':
-      return ExprToken::kGreater;
+      return ExprTokenType::kGreater;
     case ':':
       // Currently only support colons as part of "::", look ahead.
       if (can_advance()) {
         if (input_[cur_ + 1] == ':')
-          return ExprToken::kColonColon;
+          return ExprTokenType::kColonColon;
       }
       // Any other use of colon is an error.
       error_location_ = cur_;
       err_ = Err("Invalid standalone ':' in expression.\n" +
                  GetErrorContext(input_, cur_));
-      return ExprToken::kInvalid;
+      return ExprTokenType::kInvalid;
     default:
       error_location_ = cur_;
       err_ = Err(
           fxl::StringPrintf("Invalid character '%c' in expression.\n", cur) +
           GetErrorContext(input_, cur_));
-      return ExprToken::kInvalid;
+      return ExprTokenType::kInvalid;
   }
 }
 
diff --git a/garnet/bin/zxdb/expr/expr_tokenizer.h b/garnet/bin/zxdb/expr/expr_tokenizer.h
index e18a24e..1c7809f 100644
--- a/garnet/bin/zxdb/expr/expr_tokenizer.h
+++ b/garnet/bin/zxdb/expr/expr_tokenizer.h
@@ -47,7 +47,7 @@
   void AdvanceChars(int n);
   void AdvanceOneChar();
   void AdvanceToNextToken();
-  void AdvanceToEndOfToken(ExprToken::Type type);
+  void AdvanceToEndOfToken(ExprTokenType type);
 
   bool IsCurrentWhitespace() const;
 
@@ -60,7 +60,7 @@
   // the one after that is a name boundary (punctuation, end of buffer, etc.).
   bool IsCurrentName(std::string_view s) const;
 
-  ExprToken::Type ClassifyCurrent();
+  ExprTokenType ClassifyCurrent();
 
   bool done() const { return at_end() || has_error(); }
   bool has_error() const { return err_.has_error(); }
diff --git a/garnet/bin/zxdb/expr/expr_tokenizer_unittest.cc b/garnet/bin/zxdb/expr/expr_tokenizer_unittest.cc
index 2c15f65..91546de 100644
--- a/garnet/bin/zxdb/expr/expr_tokenizer_unittest.cc
+++ b/garnet/bin/zxdb/expr/expr_tokenizer_unittest.cc
@@ -39,59 +39,59 @@
   const auto& tokens = t.tokens();
   ASSERT_EQ(14u, tokens.size());
 
-  EXPECT_EQ(ExprToken::kDot, tokens[0].type());
+  EXPECT_EQ(ExprTokenType::kDot, tokens[0].type());
   EXPECT_EQ(".", tokens[0].value());
   EXPECT_EQ(1u, tokens[0].byte_offset());
 
-  EXPECT_EQ(ExprToken::kStar, tokens[1].type());
+  EXPECT_EQ(ExprTokenType::kStar, tokens[1].type());
   EXPECT_EQ("*", tokens[1].value());
   EXPECT_EQ(3u, tokens[1].byte_offset());
 
-  EXPECT_EQ(ExprToken::kArrow, tokens[2].type());
+  EXPECT_EQ(ExprTokenType::kArrow, tokens[2].type());
   EXPECT_EQ("->", tokens[2].value());
   EXPECT_EQ(5u, tokens[2].byte_offset());
 
-  EXPECT_EQ(ExprToken::kAmpersand, tokens[3].type());
+  EXPECT_EQ(ExprTokenType::kAmpersand, tokens[3].type());
   EXPECT_EQ("&", tokens[3].value());
   EXPECT_EQ(8u, tokens[3].byte_offset());
 
-  EXPECT_EQ(ExprToken::kLeftParen, tokens[4].type());
+  EXPECT_EQ(ExprTokenType::kLeftParen, tokens[4].type());
   EXPECT_EQ("(", tokens[4].value());
   EXPECT_EQ(10u, tokens[4].byte_offset());
 
-  EXPECT_EQ(ExprToken::kRightParen, tokens[5].type());
+  EXPECT_EQ(ExprTokenType::kRightParen, tokens[5].type());
   EXPECT_EQ(")", tokens[5].value());
   EXPECT_EQ(11u, tokens[5].byte_offset());
 
-  EXPECT_EQ(ExprToken::kLeftSquare, tokens[6].type());
+  EXPECT_EQ(ExprTokenType::kLeftSquare, tokens[6].type());
   EXPECT_EQ("[", tokens[6].value());
   EXPECT_EQ(13u, tokens[6].byte_offset());
 
-  EXPECT_EQ(ExprToken::kRightSquare, tokens[7].type());
+  EXPECT_EQ(ExprTokenType::kRightSquare, tokens[7].type());
   EXPECT_EQ("]", tokens[7].value());
   EXPECT_EQ(14u, tokens[7].byte_offset());
 
-  EXPECT_EQ(ExprToken::kMinus, tokens[8].type());
+  EXPECT_EQ(ExprTokenType::kMinus, tokens[8].type());
   EXPECT_EQ("-", tokens[8].value());
   EXPECT_EQ(16u, tokens[8].byte_offset());
 
-  EXPECT_EQ(ExprToken::kColonColon, tokens[9].type());
+  EXPECT_EQ(ExprTokenType::kColonColon, tokens[9].type());
   EXPECT_EQ("::", tokens[9].value());
   EXPECT_EQ(18u, tokens[9].byte_offset());
 
-  EXPECT_EQ(ExprToken::kLess, tokens[10].type());
+  EXPECT_EQ(ExprTokenType::kLess, tokens[10].type());
   EXPECT_EQ("<", tokens[10].value());
   EXPECT_EQ(21u, tokens[10].byte_offset());
 
-  EXPECT_EQ(ExprToken::kGreater, tokens[11].type());
+  EXPECT_EQ(ExprTokenType::kGreater, tokens[11].type());
   EXPECT_EQ(">", tokens[11].value());
   EXPECT_EQ(23u, tokens[11].byte_offset());
 
-  EXPECT_EQ(ExprToken::kEquality, tokens[12].type());
+  EXPECT_EQ(ExprTokenType::kEquality, tokens[12].type());
   EXPECT_EQ("==", tokens[12].value());
   EXPECT_EQ(25u, tokens[12].byte_offset());
 
-  EXPECT_EQ(ExprToken::kEquals, tokens[13].type());
+  EXPECT_EQ(ExprTokenType::kEquals, tokens[13].type());
   EXPECT_EQ("=", tokens[13].value());
   EXPECT_EQ(28u, tokens[13].byte_offset());
 }
@@ -110,35 +110,35 @@
   const auto& tokens = t.tokens();
   ASSERT_EQ(8u, tokens.size());
 
-  EXPECT_EQ(ExprToken::kInteger, tokens[0].type());
+  EXPECT_EQ(ExprTokenType::kInteger, tokens[0].type());
   EXPECT_EQ("1234", tokens[0].value());
   EXPECT_EQ(0u, tokens[0].byte_offset());
 
-  EXPECT_EQ(ExprToken::kMinus, tokens[1].type());
+  EXPECT_EQ(ExprTokenType::kMinus, tokens[1].type());
   EXPECT_EQ("-", tokens[1].value());
   EXPECT_EQ(5u, tokens[1].byte_offset());
 
-  EXPECT_EQ(ExprToken::kInteger, tokens[2].type());
+  EXPECT_EQ(ExprTokenType::kInteger, tokens[2].type());
   EXPECT_EQ("56", tokens[2].value());
   EXPECT_EQ(6u, tokens[2].byte_offset());
 
-  EXPECT_EQ(ExprToken::kMinus, tokens[3].type());
+  EXPECT_EQ(ExprTokenType::kMinus, tokens[3].type());
   EXPECT_EQ("-", tokens[3].value());
   EXPECT_EQ(8u, tokens[3].byte_offset());
 
-  EXPECT_EQ(ExprToken::kInteger, tokens[4].type());
+  EXPECT_EQ(ExprTokenType::kInteger, tokens[4].type());
   EXPECT_EQ("1", tokens[4].value());
   EXPECT_EQ(9u, tokens[4].byte_offset());
 
-  EXPECT_EQ(ExprToken::kInteger, tokens[5].type());
+  EXPECT_EQ(ExprTokenType::kInteger, tokens[5].type());
   EXPECT_EQ("0x5a4bcdef", tokens[5].value());
   EXPECT_EQ(11u, tokens[5].byte_offset());
 
-  EXPECT_EQ(ExprToken::kInteger, tokens[6].type());
+  EXPECT_EQ(ExprTokenType::kInteger, tokens[6].type());
   EXPECT_EQ("0o123llu", tokens[6].value());
   EXPECT_EQ(22u, tokens[6].byte_offset());
 
-  EXPECT_EQ(ExprToken::kInteger, tokens[7].type());
+  EXPECT_EQ(ExprTokenType::kInteger, tokens[7].type());
   EXPECT_EQ("7hello", tokens[7].value());
   EXPECT_EQ(31u, tokens[7].byte_offset());
 }
@@ -153,39 +153,39 @@
   const auto& tokens = t.tokens();
   ASSERT_EQ(9u, tokens.size());
 
-  EXPECT_EQ(ExprToken::kTrue, tokens[0].type());
+  EXPECT_EQ(ExprTokenType::kTrue, tokens[0].type());
   EXPECT_EQ("true", tokens[0].value());
   EXPECT_EQ(0u, tokens[0].byte_offset());
 
-  EXPECT_EQ(ExprToken::kName, tokens[1].type());
+  EXPECT_EQ(ExprTokenType::kName, tokens[1].type());
   EXPECT_EQ("True", tokens[1].value());
   EXPECT_EQ(5u, tokens[1].byte_offset());
 
-  EXPECT_EQ(ExprToken::kTrue, tokens[2].type());
+  EXPECT_EQ(ExprTokenType::kTrue, tokens[2].type());
   EXPECT_EQ("true", tokens[2].value());
   EXPECT_EQ(10u, tokens[2].byte_offset());
 
-  EXPECT_EQ(ExprToken::kRightParen, tokens[3].type());
+  EXPECT_EQ(ExprTokenType::kRightParen, tokens[3].type());
   EXPECT_EQ(")", tokens[3].value());
   EXPECT_EQ(14u, tokens[3].byte_offset());
 
-  EXPECT_EQ(ExprToken::kFalse, tokens[4].type());
+  EXPECT_EQ(ExprTokenType::kFalse, tokens[4].type());
   EXPECT_EQ("false", tokens[4].value());
   EXPECT_EQ(15u, tokens[4].byte_offset());
 
-  EXPECT_EQ(ExprToken::kName, tokens[5].type());
+  EXPECT_EQ(ExprTokenType::kName, tokens[5].type());
   EXPECT_EQ("falsey", tokens[5].value());
   EXPECT_EQ(21u, tokens[5].byte_offset());
 
-  EXPECT_EQ(ExprToken::kConst, tokens[6].type());
+  EXPECT_EQ(ExprTokenType::kConst, tokens[6].type());
   EXPECT_EQ("const", tokens[6].value());
   EXPECT_EQ(28u, tokens[6].byte_offset());
 
-  EXPECT_EQ(ExprToken::kVolatile, tokens[7].type());
+  EXPECT_EQ(ExprTokenType::kVolatile, tokens[7].type());
   EXPECT_EQ("volatile", tokens[7].value());
   EXPECT_EQ(34u, tokens[7].byte_offset());
 
-  EXPECT_EQ(ExprToken::kRestrict, tokens[8].type());
+  EXPECT_EQ(ExprTokenType::kRestrict, tokens[8].type());
   EXPECT_EQ("restrict", tokens[8].value());
   EXPECT_EQ(43u, tokens[8].byte_offset());
 }
@@ -200,27 +200,27 @@
   const auto& tokens = t.tokens();
   ASSERT_EQ(6u, tokens.size());
 
-  EXPECT_EQ(ExprToken::kName, tokens[0].type());
+  EXPECT_EQ(ExprTokenType::kName, tokens[0].type());
   EXPECT_EQ("name", tokens[0].value());
   EXPECT_EQ(1u, tokens[0].byte_offset());
 
-  EXPECT_EQ(ExprToken::kLeftParen, tokens[1].type());
+  EXPECT_EQ(ExprTokenType::kLeftParen, tokens[1].type());
   EXPECT_EQ("(", tokens[1].value());
   EXPECT_EQ(5u, tokens[1].byte_offset());
 
-  EXPECT_EQ(ExprToken::kName, tokens[2].type());
+  EXPECT_EQ(ExprTokenType::kName, tokens[2].type());
   EXPECT_EQ("hello", tokens[2].value());
   EXPECT_EQ(6u, tokens[2].byte_offset());
 
-  EXPECT_EQ(ExprToken::kRightSquare, tokens[3].type());
+  EXPECT_EQ(ExprTokenType::kRightSquare, tokens[3].type());
   EXPECT_EQ("]", tokens[3].value());
   EXPECT_EQ(11u, tokens[3].byte_offset());
 
-  EXPECT_EQ(ExprToken::kName, tokens[4].type());
+  EXPECT_EQ(ExprTokenType::kName, tokens[4].type());
   EXPECT_EQ("goodbye", tokens[4].value());
   EXPECT_EQ(13u, tokens[4].byte_offset());
 
-  EXPECT_EQ(ExprToken::kName, tokens[5].type());
+  EXPECT_EQ(ExprTokenType::kName, tokens[5].type());
   EXPECT_EQ("a", tokens[5].value());
   EXPECT_EQ(21u, tokens[5].byte_offset());
 }
diff --git a/garnet/bin/zxdb/expr/find_variable_unittest.cc b/garnet/bin/zxdb/expr/find_variable_unittest.cc
index d18e9cc..f438d26 100644
--- a/garnet/bin/zxdb/expr/find_variable_unittest.cc
+++ b/garnet/bin/zxdb/expr/find_variable_unittest.cc
@@ -144,7 +144,7 @@
 
   // Find "value" in the nested block should give the block's one.
   Identifier value_ident(
-      ExprToken(ExprToken::kName, var_value->GetAssignedName(), 0));
+      ExprToken(ExprTokenType::kName, var_value->GetAssignedName(), 0));
   auto found = FindVariable(nullptr, block.get(), &symbol_context, value_ident);
   EXPECT_TRUE(found);
   EXPECT_EQ(block_value.get(), found->variable());
@@ -156,8 +156,8 @@
 
   // Find "::value" should match nothing.
   Identifier value_global_ident(Identifier::Component(
-      ExprToken(ExprToken::kColonColon, "::", 0),
-      ExprToken(ExprToken::kName, var_value->GetAssignedName(), 0)));
+      ExprToken(ExprTokenType::kColonColon, "::", 0),
+      ExprToken(ExprTokenType::kName, var_value->GetAssignedName(), 0)));
   found = FindVariable(nullptr, function.get(), &symbol_context,
                        value_global_ident);
   EXPECT_FALSE(found);
@@ -165,7 +165,7 @@
   // Find "block_local" in the block should be found, but in the function it
   // should not be.
   Identifier block_local_ident(
-      ExprToken(ExprToken::kName, block_other->GetAssignedName(), 0));
+      ExprToken(ExprTokenType::kName, block_other->GetAssignedName(), 0));
   found =
       FindVariable(nullptr, block.get(), &symbol_context, block_local_ident);
   EXPECT_TRUE(found);
@@ -176,7 +176,7 @@
 
   // Finding the other function parameter in the block should work.
   Identifier other_param_ident(
-      ExprToken(ExprToken::kName, param_other->GetAssignedName(), 0));
+      ExprToken(ExprTokenType::kName, param_other->GetAssignedName(), 0));
   found =
       FindVariable(nullptr, block.get(), &symbol_context, other_param_ident);
   EXPECT_TRUE(found);
@@ -185,7 +185,7 @@
   // Look up the variable "ns::ns_value" using the name "ns_value" (no
   // namespace) from within the context of the "ns::function()" function.
   // The namespace of the function should be implicitly picked up.
-  Identifier ns_value_ident(ExprToken(ExprToken::kName, kNsVarName, 0));
+  Identifier ns_value_ident(ExprToken(ExprTokenType::kName, kNsVarName, 0));
   found = FindVariable(&setup.process(), block.get(), &symbol_context,
                        ns_value_ident);
   EXPECT_TRUE(found);
@@ -211,10 +211,10 @@
   const char kVar2Name[] = "var2";      // Only in module 2
   const char kNotFoundName[] = "notfound";
 
-  Identifier global_ident(ExprToken(ExprToken::kName, kGlobalName, 0));
-  Identifier var1_ident(ExprToken(ExprToken::kName, kVar1Name, 0));
-  Identifier var2_ident(ExprToken(ExprToken::kName, kVar2Name, 0));
-  Identifier notfound_ident(ExprToken(ExprToken::kName, kNotFoundName, 0));
+  Identifier global_ident(ExprToken(ExprTokenType::kName, kGlobalName, 0));
+  Identifier var1_ident(ExprToken(ExprTokenType::kName, kVar1Name, 0));
+  Identifier var2_ident(ExprToken(ExprTokenType::kName, kVar2Name, 0));
+  Identifier notfound_ident(ExprToken(ExprTokenType::kName, kNotFoundName, 0));
 
   // Module 1.
   auto mod1 = std::make_unique<MockModuleSymbols>("mod1.so");
@@ -273,14 +273,14 @@
   // Make a global variable in the toplevel namespace.
   TestGlobalVariable global(&mod_sym, &root, kVarName);
 
-  Identifier var_ident(ExprToken(ExprToken::kName, kVarName, 0));
+  Identifier var_ident(ExprToken(ExprTokenType::kName, kVarName, 0));
   auto found = FindGlobalVariableInModule(&mod_sym, Identifier(), var_ident);
   ASSERT_TRUE(found);
   EXPECT_EQ(global.var.get(), found->variable());
 
   // Say we're in some nested namespace and search for the same name. It should
   // find the variable in the upper namespace.
-  Identifier nested_ns(ExprToken(ExprToken::kName, kNsName, 0));
+  Identifier nested_ns(ExprToken(ExprTokenType::kName, kNsName, 0));
   found = FindGlobalVariableInModule(&mod_sym, nested_ns, var_ident);
   ASSERT_TRUE(found);
   EXPECT_EQ(global.var.get(), found->variable());
@@ -298,8 +298,8 @@
   // Now do the same search but globally qualify the input "::var" which should
   // match only the toplevel one.
   Identifier var_global_ident(
-      Identifier::Component(ExprToken(ExprToken::kColonColon, "::", 0),
-                            ExprToken(ExprToken::kName, kVarName, 0)));
+      Identifier::Component(ExprToken(ExprTokenType::kColonColon, "::", 0),
+                            ExprToken(ExprTokenType::kName, kVarName, 0)));
   found = FindGlobalVariableInModule(&mod_sym, nested_ns, var_global_ident);
   ASSERT_TRUE(found);
   EXPECT_EQ(global.var.get(), found->variable());
diff --git a/garnet/bin/zxdb/expr/identifier.h b/garnet/bin/zxdb/expr/identifier.h
index c1bb5d8..34c9be9 100644
--- a/garnet/bin/zxdb/expr/identifier.h
+++ b/garnet/bin/zxdb/expr/identifier.h
@@ -48,9 +48,9 @@
     // Constructor for names without templates for use by tests that hard-code
     // values.
     Component(bool has_separator, const std::string& name)
-        : name_(ExprToken::kName, name, 0) {
+        : name_(ExprTokenType::kName, name, 0) {
       if (has_separator)
-        separator_ = ExprToken(ExprToken::kColonColon, "::", 0);
+        separator_ = ExprToken(ExprTokenType::kColonColon, "::", 0);
     }
 
     // Constructor for names with templates. The contents will be a
@@ -65,10 +65,10 @@
           template_end_(std::move(template_end)) {}
 
     bool has_separator() const {
-      return separator_.type() != ExprToken::kInvalid;
+      return separator_.type() != ExprTokenType::kInvalid;
     }
     bool has_template() const {
-      return template_begin_.type() != ExprToken::kInvalid;
+      return template_begin_.type() != ExprTokenType::kInvalid;
     }
 
     const ExprToken& separator() const { return separator_; }
diff --git a/garnet/bin/zxdb/expr/identifier_unittest.cc b/garnet/bin/zxdb/expr/identifier_unittest.cc
index e692dd1..500f8b8 100644
--- a/garnet/bin/zxdb/expr/identifier_unittest.cc
+++ b/garnet/bin/zxdb/expr/identifier_unittest.cc
@@ -15,29 +15,29 @@
 
   // Single name with no "::" at the beginning.
   unqualified.AppendComponent(ExprToken(),
-                              ExprToken(ExprToken::kName, "First", 2));
+                              ExprToken(ExprTokenType::kName, "First", 2));
   EXPECT_EQ("First", unqualified.GetFullName());
 
   // Single name with a "::" at the beginning.
   Identifier qualified;
-  qualified.AppendComponent(ExprToken(ExprToken::kColonColon, "::", 0),
-                            ExprToken(ExprToken::kName, "First", 2));
+  qualified.AppendComponent(ExprToken(ExprTokenType::kColonColon, "::", 0),
+                            ExprToken(ExprTokenType::kName, "First", 2));
   EXPECT_EQ("::First", qualified.GetFullName());
 
   // Append some template stuff.
-  qualified.AppendComponent(ExprToken(ExprToken::kColonColon, "::", 7),
-                            ExprToken(ExprToken::kName, "Second", 9),
-                            ExprToken(ExprToken::kLess, "<", 15),
+  qualified.AppendComponent(ExprToken(ExprTokenType::kColonColon, "::", 7),
+                            ExprToken(ExprTokenType::kName, "Second", 9),
+                            ExprToken(ExprTokenType::kLess, "<", 15),
                             {"int", "Foo"},
-                            ExprToken(ExprToken::kGreater, ">", 24));
+                            ExprToken(ExprTokenType::kGreater, ">", 24));
   EXPECT_EQ("::First::Second<int, Foo>", qualified.GetFullName());
 }
 
 TEST(Identifier, GetScope) {
-  ExprToken colon_colon(ExprToken::kColonColon, "::", 0);
-  ExprToken name1(ExprToken::kName, "Name1", 100);
-  ExprToken name2(ExprToken::kName, "Name2", 100);
-  ExprToken name3(ExprToken::kName, "Name3", 100);
+  ExprToken colon_colon(ExprTokenType::kColonColon, "::", 0);
+  ExprToken name1(ExprTokenType::kName, "Name1", 100);
+  ExprToken name2(ExprTokenType::kName, "Name2", 100);
+  ExprToken name3(ExprTokenType::kName, "Name3", 100);
 
   // "" -> "".
   Identifier empty;
@@ -69,7 +69,8 @@
   Identifier three_scoped_names(Identifier::Component(ExprToken(), name1));
   three_scoped_names.AppendComponent(Identifier::Component(colon_colon, name2));
   three_scoped_names.AppendComponent(Identifier::Component(colon_colon, name3));
-  EXPECT_EQ("\"Name1\"; ::,\"Name2\"", three_scoped_names.GetScope().GetDebugName());
+  EXPECT_EQ("\"Name1\"; ::,\"Name2\"",
+            three_scoped_names.GetScope().GetDebugName());
 }
 
 TEST(Identifier, InGlobalNamespace) {
@@ -78,30 +79,30 @@
 
   Identifier non_global;
   non_global.AppendComponent(Identifier::Component(
-      ExprToken(), ExprToken(ExprToken::kName, "Foo", 0)));
+      ExprToken(), ExprToken(ExprTokenType::kName, "Foo", 0)));
   EXPECT_FALSE(non_global.InGlobalNamespace());
 
   Identifier global;
   global.AppendComponent(
-      Identifier::Component(ExprToken(ExprToken::kColonColon, "::", 0),
-                            ExprToken(ExprToken::kName, "Foo", 0)));
+      Identifier::Component(ExprToken(ExprTokenType::kColonColon, "::", 0),
+                            ExprToken(ExprTokenType::kName, "Foo", 0)));
   EXPECT_TRUE(global.InGlobalNamespace());
 }
 
 TEST(Identifier, FromString) {
   // Empty input.
-  auto[empty_err, empty_ident] = Identifier::FromString("");
+  auto [empty_err, empty_ident] = Identifier::FromString("");
   EXPECT_TRUE(empty_err.has_error());
   EXPECT_EQ("No input to parse.", empty_err.msg());
   EXPECT_EQ("", empty_ident.GetDebugName());
 
   // Normal word.
-  auto[word_err, word_ident] = Identifier::FromString("foo");
+  auto [word_err, word_ident] = Identifier::FromString("foo");
   EXPECT_FALSE(word_err.has_error()) << word_err.msg();
   EXPECT_EQ("\"foo\"", word_ident.GetDebugName());
 
   // Complicated identifier (copied from STL).
-  auto[complex_err, complex_ident] = Identifier::FromString(
+  auto [complex_err, complex_ident] = Identifier::FromString(
       "std::unordered_map<"
       "std::__2::basic_string<char>, "
       "unsigned long, "
@@ -125,7 +126,7 @@
 
 TEST(Identifier, FromStringError) {
   // Error from input.
-  auto[bad_err, bad_ident] = Identifier::FromString("Foo<Bar");
+  auto [bad_err, bad_ident] = Identifier::FromString("Foo<Bar");
   EXPECT_TRUE(bad_err.has_error());
   EXPECT_EQ("Expected '>' to match. Hit the end of input instead.",
             bad_err.msg());
diff --git a/garnet/bin/zxdb/expr/index_walker_unittest.cc b/garnet/bin/zxdb/expr/index_walker_unittest.cc
index e762190..d6ddace 100644
--- a/garnet/bin/zxdb/expr/index_walker_unittest.cc
+++ b/garnet/bin/zxdb/expr/index_walker_unittest.cc
@@ -10,11 +10,11 @@
 
 TEST(IndexWalker, ComponentMatchesNameOnly) {
   Identifier::Component foo_comp(ExprToken(),
-                                 ExprToken(ExprToken::kName, "Foo", 0));
+                                 ExprToken(ExprTokenType::kName, "Foo", 0));
   Identifier::Component foo_template_comp(
-      ExprToken(), ExprToken(ExprToken::kName, "Foo", 0),
-      ExprToken(ExprToken::kLess, "<", 10), {"A", "b"},
-      ExprToken(ExprToken::kGreater, ">", 100));
+      ExprToken(), ExprToken(ExprTokenType::kName, "Foo", 0),
+      ExprToken(ExprTokenType::kLess, "<", 10), {"A", "b"},
+      ExprToken(ExprTokenType::kGreater, ">", 100));
 
   // Simple name-only comparisons.
   EXPECT_TRUE(IndexWalker::ComponentMatchesNameOnly("Foo", foo_comp));
@@ -31,15 +31,15 @@
 
 TEST(IndexWalker, ComponentMatchesTemplateOnly) {
   Identifier::Component foo_comp(ExprToken(),
-                                 ExprToken(ExprToken::kName, "Foo", 0));
+                                 ExprToken(ExprTokenType::kName, "Foo", 0));
   Identifier::Component foo_template_comp(
-      ExprToken(), ExprToken(ExprToken::kName, "Foo", 0),
-      ExprToken(ExprToken::kLess, "<", 10), {"A", "b"},
-      ExprToken(ExprToken::kGreater, ">", 100));
+      ExprToken(), ExprToken(ExprTokenType::kName, "Foo", 0),
+      ExprToken(ExprTokenType::kLess, "<", 10), {"A", "b"},
+      ExprToken(ExprTokenType::kGreater, ">", 100));
   Identifier::Component foo_empty_template_comp(
-      ExprToken(), ExprToken(ExprToken::kName, "Foo", 0),
-      ExprToken(ExprToken::kLess, "<", 10), {},
-      ExprToken(ExprToken::kGreater, ">", 100));
+      ExprToken(), ExprToken(ExprTokenType::kName, "Foo", 0),
+      ExprToken(ExprTokenType::kLess, "<", 10), {},
+      ExprToken(ExprTokenType::kGreater, ">", 100));
 
   // Neither inputs have templates (should be a match).
   EXPECT_TRUE(IndexWalker::ComponentMatchesTemplateOnly("Foo", foo_comp));
@@ -62,11 +62,11 @@
 // Most cases are tested by ComponentMatchesNameOnly and ...TemplateOnly above.
 TEST(IndexWalker, ComponentMatches) {
   Identifier::Component foo_comp(ExprToken(),
-                                 ExprToken(ExprToken::kName, "Foo", 0));
+                                 ExprToken(ExprTokenType::kName, "Foo", 0));
   Identifier::Component foo_template_comp(
-      ExprToken(), ExprToken(ExprToken::kName, "Foo", 0),
-      ExprToken(ExprToken::kLess, "<", 10), {"A", "b"},
-      ExprToken(ExprToken::kGreater, ">", 100));
+      ExprToken(), ExprToken(ExprTokenType::kName, "Foo", 0),
+      ExprToken(ExprTokenType::kLess, "<", 10), {"A", "b"},
+      ExprToken(ExprTokenType::kGreater, ">", 100));
 
   EXPECT_TRUE(IndexWalker::ComponentMatches("Foo", foo_comp));
   EXPECT_FALSE(IndexWalker::ComponentMatches("Foo<>", foo_comp));
@@ -127,7 +127,7 @@
   EXPECT_EQ(foo_node, walker.current());
 
   // Walk to the "Bar<int,char>" identifier.
-  auto[err1, bar_int_char] = Identifier::FromString("Bar < int , char >");
+  auto [err1, bar_int_char] = Identifier::FromString("Bar < int , char >");
   EXPECT_FALSE(err1.has_error()) << err1.msg();
   EXPECT_TRUE(walker.WalkInto(bar_int_char));
   EXPECT_EQ(bar_int_char_node, walker.current());
@@ -147,13 +147,13 @@
 
   // Parse the Barf identifier for the following two tests. This one has a
   // toplevel scope.
-  auto[err2, barf] = Identifier::FromString("::Foo::Barf<int>");
+  auto [err2, barf] = Identifier::FromString("::Foo::Barf<int>");
   EXPECT_FALSE(err2.has_error()) << err2.msg();
 
   // Walk to the "Foo::Bar9<int>" with copying the walker.
   {
     IndexWalker nested_walker(walker);
-    auto[err2, bar9] = Identifier::FromString("Foo :: Bar9 < int >");
+    auto [err2, bar9] = Identifier::FromString("Foo :: Bar9 < int >");
     EXPECT_FALSE(err2.has_error()) << err2.msg();
     EXPECT_TRUE(nested_walker.WalkInto(bar9));
     EXPECT_EQ(bar9_node, nested_walker.current());
diff --git a/garnet/bin/zxdb/expr/symbol_eval_context_unittest.cc b/garnet/bin/zxdb/expr/symbol_eval_context_unittest.cc
index 72b3413..c0650a4 100644
--- a/garnet/bin/zxdb/expr/symbol_eval_context_unittest.cc
+++ b/garnet/bin/zxdb/expr/symbol_eval_context_unittest.cc
@@ -276,7 +276,7 @@
 
   // Look up an identifier that's not present.
   auto present = fxl::MakeRefCounted<IdentifierExprNode>(
-      ExprToken(ExprToken::Type::kName, "present", 0));
+      ExprToken(ExprTokenType::kName, "present", 0));
   bool called = false;
   Err out_err;
   ExprValue out_value;
diff --git a/garnet/bin/zxdb/expr/template_type_extractor.cc b/garnet/bin/zxdb/expr/template_type_extractor.cc
index c494eca..c329daa 100644
--- a/garnet/bin/zxdb/expr/template_type_extractor.cc
+++ b/garnet/bin/zxdb/expr/template_type_extractor.cc
@@ -13,10 +13,10 @@
 
 // Tracks the level of nesting of brackets.
 struct Nesting {
-  Nesting(size_t i, ExprToken::Type e) : opening_index(i), end(e) {}
+  Nesting(size_t i, ExprTokenType e) : opening_index(i), end(e) {}
 
-  size_t opening_index = 0;  // Index of opening bracket.
-  ExprToken::Type end = ExprToken::kInvalid;  // Expected closing bracket.
+  size_t opening_index = 0;                     // Index of opening bracket.
+  ExprTokenType end = ExprTokenType::kInvalid;  // Expected closing bracket.
 };
 
 // A table of operators that need special handling. These are ones that can
@@ -28,23 +28,23 @@
 // (e.g. "operator+" is a subset of "operator++"), the more specific one should
 // be first.
 struct OperatorRecord {
-  ExprToken::Type first;
-  ExprToken::Type second;
+  ExprTokenType first;
+  ExprTokenType second;
 };
 const OperatorRecord kOperators[] = {
-    {ExprToken::kLess, ExprToken::kLess},        // <<
-    {ExprToken::kLess, ExprToken::kInvalid},     // <
-    {ExprToken::kGreater, ExprToken::kGreater},  // >>
-    {ExprToken::kGreater, ExprToken::kInvalid},  // >
-    {ExprToken::kComma, ExprToken::kInvalid},    // ,
+    {ExprTokenType::kLess, ExprTokenType::kLess},        // <<
+    {ExprTokenType::kLess, ExprTokenType::kInvalid},     // <
+    {ExprTokenType::kGreater, ExprTokenType::kGreater},  // >>
+    {ExprTokenType::kGreater, ExprTokenType::kInvalid},  // >
+    {ExprTokenType::kComma, ExprTokenType::kInvalid},    // ,
 };
 
 bool IsNamelikeToken(const ExprToken& token) {
-  return token.type() == ExprToken::kName ||
-         token.type() == ExprToken::kTrue ||
-         token.type() == ExprToken::kFalse ||
-         token.type() == ExprToken::kConst ||
-         token.type() == ExprToken::kVolatile;
+  return token.type() == ExprTokenType::kName ||
+         token.type() == ExprTokenType::kTrue ||
+         token.type() == ExprTokenType::kFalse ||
+         token.type() == ExprTokenType::kConst ||
+         token.type() == ExprTokenType::kVolatile;
 }
 
 // Returns true if the token at the given index needs a space before it to
@@ -64,7 +64,7 @@
 
   // Put a space after a comma. This is undesirable in the case of "operator,"
   // appearing as in "template<CmpOp a = operator,>" but not a big deal.
-  if (tokens[index - 1].type() == ExprToken::kComma)
+  if (tokens[index - 1].type() == ExprTokenType::kComma)
     return true;
 
   // Most other things can go next to each other as far as valid C++ goes.
@@ -87,13 +87,13 @@
   int matched_tokens = 0;
 
   // The second token we're looking for.
-  ExprToken::Type second_type = tokens.size() > *index + 2
-                                    ? tokens[*index + 2].type()
-                                    : ExprToken::kInvalid;
+  ExprTokenType second_type = tokens.size() > *index + 2
+                                  ? tokens[*index + 2].type()
+                                  : ExprTokenType::kInvalid;
   for (const auto& cur_op : kOperators) {
     if (cur_op.first == tokens[*index + 1].type()) {
       // First character matched.
-      if (cur_op.second == ExprToken::kInvalid) {
+      if (cur_op.second == ExprTokenType::kInvalid) {
         // Anything matches, we found it.
         matched_tokens = 1;
         break;
@@ -155,27 +155,28 @@
   std::vector<Nesting> nesting;
   size_t i = begin_token;
   for (; i < tokens.size(); i++) {
-    ExprToken::Type type = tokens[i].type();
-    if (type == ExprToken::kLeftSquare) {
+    ExprTokenType type = tokens[i].type();
+    if (type == ExprTokenType::kLeftSquare) {
       // [
-      nesting.emplace_back(i, ExprToken::kRightSquare);
-    } else if (type == ExprToken::kLeftParen) {
+      nesting.emplace_back(i, ExprTokenType::kRightSquare);
+    } else if (type == ExprTokenType::kLeftParen) {
       // (
-      nesting.emplace_back(i, ExprToken::kRightParen);
-    } else if (type == ExprToken::kLess) {
+      nesting.emplace_back(i, ExprTokenType::kRightParen);
+    } else if (type == ExprTokenType::kLess) {
       // < (the sequences "operator<" and "operator<<" were handled when we
       //    got the "operator" token).
-      nesting.emplace_back(i, ExprToken::kGreater);
-    } else if (nesting.empty() &&
-               (type == ExprToken::kGreater || type == ExprToken::kRightParen ||
-                type == ExprToken::kComma)) {
+      nesting.emplace_back(i, ExprTokenType::kGreater);
+    } else if (nesting.empty() && (type == ExprTokenType::kGreater ||
+                                   type == ExprTokenType::kRightParen ||
+                                   type == ExprTokenType::kComma)) {
       // These tokens mark the end of a type when seen without nesting. Usually
       // this marks the end of the enclosing cast or template.
       break;
     } else if (!nesting.empty() && type == nesting.back().end) {
       // Found the closing token for a previous opening one.
       nesting.pop_back();
-    } else if (type == ExprToken::kName && tokens[i].value() == "operator") {
+    } else if (type == ExprTokenType::kName &&
+               tokens[i].value() == "operator") {
       // Possible space before "operator".
       if (NeedsSpaceBefore(tokens, begin_token, i))
         result.canonical_name.push_back(' ');
diff --git a/garnet/bin/zxdb/expr/template_type_extractor_unittest.cc b/garnet/bin/zxdb/expr/template_type_extractor_unittest.cc
index fc4ae08..be13cea 100644
--- a/garnet/bin/zxdb/expr/template_type_extractor_unittest.cc
+++ b/garnet/bin/zxdb/expr/template_type_extractor_unittest.cc
@@ -12,18 +12,18 @@
   // No template contents: "Foo<>". When extracting the type, we'll be given
   // the first token after the template opening (the 2nd token, ">").
   TemplateTypeResult result =
-      ExtractTemplateType({ExprToken(ExprToken::kName, "Foo", 0),
-                           ExprToken(ExprToken::kLess, "<", 3),
-                           ExprToken(ExprToken::kGreater, ">", 4)},
+      ExtractTemplateType({ExprToken(ExprTokenType::kName, "Foo", 0),
+                           ExprToken(ExprTokenType::kLess, "<", 3),
+                           ExprToken(ExprTokenType::kGreater, ">", 4)},
                           2);
   EXPECT_TRUE(result.success);
   EXPECT_EQ(2u, result.end_token);
   EXPECT_EQ("", result.canonical_name);
 
   // Unterminated template argument list: "Foo<<".
-  result = ExtractTemplateType({ExprToken(ExprToken::kName, "Foo", 0),
-                                ExprToken(ExprToken::kLess, "<", 3),
-                                ExprToken(ExprToken::kLess, "<", 4)},
+  result = ExtractTemplateType({ExprToken(ExprTokenType::kName, "Foo", 0),
+                                ExprToken(ExprTokenType::kLess, "<", 3),
+                                ExprToken(ExprTokenType::kLess, "<", 4)},
                                2);
   EXPECT_FALSE(result.success);
   EXPECT_EQ(2u, result.unmatched_error_token);
@@ -31,8 +31,8 @@
 
   // What would appear in "std::vector<int>":
   // "int>" -> "int"
-  result = ExtractTemplateType({ExprToken(ExprToken::kName, "int", 1),
-                                ExprToken(ExprToken::kGreater, ">", 4)},
+  result = ExtractTemplateType({ExprToken(ExprTokenType::kName, "int", 1),
+                                ExprToken(ExprTokenType::kGreater, ">", 4)},
                                0);
   EXPECT_TRUE(result.success);
   EXPECT_EQ(1u, result.end_token);
@@ -40,20 +40,20 @@
 
   // What would appear in "std::vector<const int*>":
   // "const int*>" -> "const int*"
-  result = ExtractTemplateType({ExprToken(ExprToken::kName, "const", 1),
-                                ExprToken(ExprToken::kName, "int", 1),
-                                ExprToken(ExprToken::kStar, "*", 1),
-                                ExprToken(ExprToken::kGreater, ">", 4)},
+  result = ExtractTemplateType({ExprToken(ExprTokenType::kName, "const", 1),
+                                ExprToken(ExprTokenType::kName, "int", 1),
+                                ExprToken(ExprTokenType::kStar, "*", 1),
+                                ExprToken(ExprTokenType::kGreater, ">", 4)},
                                0);
   EXPECT_TRUE(result.success);
   EXPECT_EQ(3u, result.end_token);
   EXPECT_EQ("const int*", result.canonical_name);
 
   // What would appear in "(const Foo&)foo"
-  result = ExtractTemplateType({ExprToken(ExprToken::kName, "const", 0),
-                                ExprToken(ExprToken::kName, "Foo", 7),
-                                ExprToken(ExprToken::kAmpersand, "&", 10),
-                                ExprToken(ExprToken::kRightParen, ")", 11)},
+  result = ExtractTemplateType({ExprToken(ExprTokenType::kName, "const", 0),
+                                ExprToken(ExprTokenType::kName, "Foo", 7),
+                                ExprToken(ExprTokenType::kAmpersand, "&", 10),
+                                ExprToken(ExprTokenType::kRightParen, ")", 11)},
                                0);
   EXPECT_TRUE(result.success);
   EXPECT_EQ(3u, result.end_token);
@@ -61,8 +61,8 @@
 
   // What would appear in "std::map<int, int>":
   // "int," -> "int"
-  result = ExtractTemplateType({ExprToken(ExprToken::kName, "int", 1),
-                                ExprToken(ExprToken::kGreater, ",", 4)},
+  result = ExtractTemplateType({ExprToken(ExprTokenType::kName, "int", 1),
+                                ExprToken(ExprTokenType::kGreater, ",", 4)},
                                0);
   EXPECT_TRUE(result.success);
   EXPECT_EQ(1u, result.end_token);
@@ -70,15 +70,15 @@
 
   // What would appear in
   // "std::allocator<int, 6>>" -> "std::allocator<int, 6>"
-  result = ExtractTemplateType({ExprToken(ExprToken::kName, "std", 1),
-                                ExprToken(ExprToken::kColonColon, "::", 4),
-                                ExprToken(ExprToken::kName, "allocator", 6),
-                                ExprToken(ExprToken::kLess, "<", 15),
-                                ExprToken(ExprToken::kName, "int", 16),
-                                ExprToken(ExprToken::kComma, ",", 19),
-                                ExprToken(ExprToken::kInteger, "6", 21),
-                                ExprToken(ExprToken::kGreater, ">", 22),
-                                ExprToken(ExprToken::kGreater, ">", 23)},
+  result = ExtractTemplateType({ExprToken(ExprTokenType::kName, "std", 1),
+                                ExprToken(ExprTokenType::kColonColon, "::", 4),
+                                ExprToken(ExprTokenType::kName, "allocator", 6),
+                                ExprToken(ExprTokenType::kLess, "<", 15),
+                                ExprToken(ExprTokenType::kName, "int", 16),
+                                ExprToken(ExprTokenType::kComma, ",", 19),
+                                ExprToken(ExprTokenType::kInteger, "6", 21),
+                                ExprToken(ExprTokenType::kGreater, ">", 22),
+                                ExprToken(ExprTokenType::kGreater, ">", 23)},
                                0);
   EXPECT_TRUE(result.success);
   EXPECT_EQ(8u, result.end_token);
@@ -88,23 +88,23 @@
 TEST(TemplateTypeExtractor, WeirdCommas) {
   // As in "Foo<operator,, 2>" -> "operator,"
   TemplateTypeResult result =
-      ExtractTemplateType({ExprToken(ExprToken::kName, "operator", 0),
-                           ExprToken(ExprToken::kComma, ",", 8),
-                           ExprToken(ExprToken::kComma, ",", 9)},
+      ExtractTemplateType({ExprToken(ExprTokenType::kName, "operator", 0),
+                           ExprToken(ExprTokenType::kComma, ",", 8),
+                           ExprToken(ExprTokenType::kComma, ",", 9)},
                           0);
   EXPECT_TRUE(result.success);
   EXPECT_EQ(2u, result.end_token);
   EXPECT_EQ("operator,", result.canonical_name);
 
   // As in "Foo<Bar<operator,, 2>>"
-  result = ExtractTemplateType({ExprToken(ExprToken::kName, "Bar", 0),
-                                ExprToken(ExprToken::kLess, "<", 4),
-                                ExprToken(ExprToken::kName, "operator", 5),
-                                ExprToken(ExprToken::kComma, ",", 13),
-                                ExprToken(ExprToken::kComma, ",", 14),
-                                ExprToken(ExprToken::kInteger, "2", 15),
-                                ExprToken(ExprToken::kGreater, ">", 16),
-                                ExprToken(ExprToken::kGreater, ">", 17)},
+  result = ExtractTemplateType({ExprToken(ExprTokenType::kName, "Bar", 0),
+                                ExprToken(ExprTokenType::kLess, "<", 4),
+                                ExprToken(ExprTokenType::kName, "operator", 5),
+                                ExprToken(ExprTokenType::kComma, ",", 13),
+                                ExprToken(ExprTokenType::kComma, ",", 14),
+                                ExprToken(ExprTokenType::kInteger, "2", 15),
+                                ExprToken(ExprTokenType::kGreater, ">", 16),
+                                ExprToken(ExprTokenType::kGreater, ">", 17)},
                                0);
   EXPECT_TRUE(result.success);
   EXPECT_EQ(7u, result.end_token);
@@ -114,9 +114,9 @@
 TEST(TemplateTypeExtractor, WeirdAngleBrackets) {
   // As in "std::map<int, int, operator<>".
   TemplateTypeResult result =
-      ExtractTemplateType({ExprToken(ExprToken::kName, "operator", 0),
-                           ExprToken(ExprToken::kLess, "<", 8),
-                           ExprToken(ExprToken::kGreater, ">", 9)},
+      ExtractTemplateType({ExprToken(ExprTokenType::kName, "operator", 0),
+                           ExprToken(ExprTokenType::kLess, "<", 8),
+                           ExprToken(ExprTokenType::kGreater, ">", 9)},
                           0);
   EXPECT_TRUE(result.success);
   EXPECT_EQ(2u, result.end_token);
@@ -124,9 +124,9 @@
 
   // As in "std::map<int, int, operator> >". The > are non-adjacent so don't
   // get treated as a single operator.
-  result = ExtractTemplateType({ExprToken(ExprToken::kName, "operator", 0),
-                                ExprToken(ExprToken::kGreater, ">", 8),
-                                ExprToken(ExprToken::kGreater, ">", 10)},
+  result = ExtractTemplateType({ExprToken(ExprTokenType::kName, "operator", 0),
+                                ExprToken(ExprTokenType::kGreater, ">", 8),
+                                ExprToken(ExprTokenType::kGreater, ">", 10)},
                                0);
   EXPECT_TRUE(result.success);
   EXPECT_EQ(2u, result.end_token);
@@ -134,10 +134,10 @@
 
   // As in "std::map<int, int, operator>>>". This is passing "operator>>" to a
   // template.
-  result = ExtractTemplateType({ExprToken(ExprToken::kName, "operator", 0),
-                                ExprToken(ExprToken::kGreater, ">", 8),
-                                ExprToken(ExprToken::kGreater, ">", 9),
-                                ExprToken(ExprToken::kGreater, ">", 10)},
+  result = ExtractTemplateType({ExprToken(ExprTokenType::kName, "operator", 0),
+                                ExprToken(ExprTokenType::kGreater, ">", 8),
+                                ExprToken(ExprTokenType::kGreater, ">", 9),
+                                ExprToken(ExprTokenType::kGreater, ">", 10)},
                                0);
   EXPECT_TRUE(result.success);
   EXPECT_EQ(3u, result.end_token);
@@ -147,10 +147,10 @@
 TEST(TemplateTypeExtractor, OtherOperator) {
   // As in "Foo<operator ++>
   TemplateTypeResult result =
-      ExtractTemplateType({ExprToken(ExprToken::kName, "operator", 0),
-                           ExprToken(ExprToken::kPlus, "+", 9),
-                           ExprToken(ExprToken::kPlus, "+", 10),
-                           ExprToken(ExprToken::kGreater, ">", 11)},
+      ExtractTemplateType({ExprToken(ExprTokenType::kName, "operator", 0),
+                           ExprToken(ExprTokenType::kPlus, "+", 9),
+                           ExprToken(ExprTokenType::kPlus, "+", 10),
+                           ExprToken(ExprTokenType::kGreater, ">", 11)},
                           0);
   EXPECT_TRUE(result.success);
   EXPECT_EQ(3u, result.end_token);
@@ -159,7 +159,8 @@
   // Malformed input with "operator" at end. Just returns the same thing since
   // we're not trying to validate proper C++, only validate that we found the
   // extent of the declaration.
-  result = ExtractTemplateType({ExprToken(ExprToken::kName, "operator", 0)}, 0);
+  result =
+      ExtractTemplateType({ExprToken(ExprTokenType::kName, "operator", 0)}, 0);
   EXPECT_TRUE(result.success);
   EXPECT_EQ(1u, result.end_token);
   EXPECT_EQ("operator", result.canonical_name);