Initial commit
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
new file mode 100644
index 0000000..58d4bf2
--- /dev/null
+++ b/src/reader/wgsl/parser_impl.cc
@@ -0,0 +1,3061 @@
+// Copyright 2020 The Tint Authors.
+//
+// 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.
+
+#include "src/reader/wgsl/parser_impl.h"
+
+#include <memory>
+
+#include "src/ast/array_accessor_expression.h"
+#include "src/ast/as_expression.h"
+#include "src/ast/binding_decoration.h"
+#include "src/ast/bool_literal.h"
+#include "src/ast/break_statement.h"
+#include "src/ast/builtin_decoration.h"
+#include "src/ast/call_expression.h"
+#include "src/ast/case_statement.h"
+#include "src/ast/cast_expression.h"
+#include "src/ast/const_initializer_expression.h"
+#include "src/ast/continue_statement.h"
+#include "src/ast/decorated_variable.h"
+#include "src/ast/else_statement.h"
+#include "src/ast/fallthrough_statement.h"
+#include "src/ast/float_literal.h"
+#include "src/ast/identifier_expression.h"
+#include "src/ast/if_statement.h"
+#include "src/ast/int_literal.h"
+#include "src/ast/kill_statement.h"
+#include "src/ast/location_decoration.h"
+#include "src/ast/member_accessor_expression.h"
+#include "src/ast/nop_statement.h"
+#include "src/ast/relational_expression.h"
+#include "src/ast/return_statement.h"
+#include "src/ast/set_decoration.h"
+#include "src/ast/statement_condition.h"
+#include "src/ast/struct_member_offset_decoration.h"
+#include "src/ast/switch_statement.h"
+#include "src/ast/type/alias_type.h"
+#include "src/ast/type/array_type.h"
+#include "src/ast/type/bool_type.h"
+#include "src/ast/type/f32_type.h"
+#include "src/ast/type/i32_type.h"
+#include "src/ast/type/matrix_type.h"
+#include "src/ast/type/pointer_type.h"
+#include "src/ast/type/struct_type.h"
+#include "src/ast/type/u32_type.h"
+#include "src/ast/type/vector_type.h"
+#include "src/ast/type/void_type.h"
+#include "src/ast/type_initializer_expression.h"
+#include "src/ast/uint_literal.h"
+#include "src/ast/unary_derivative.h"
+#include "src/ast/unary_derivative_expression.h"
+#include "src/ast/unary_method.h"
+#include "src/ast/unary_method_expression.h"
+#include "src/ast/unary_op.h"
+#include "src/ast/unary_op_expression.h"
+#include "src/ast/variable_statement.h"
+#include "src/reader/wgsl/lexer.h"
+#include "src/type_manager.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+ParserImpl::ParserImpl(const std::string& input)
+    : lexer_(std::make_unique<Lexer>(input)) {}
+
+ParserImpl::~ParserImpl() = default;
+
+void ParserImpl::set_error(const Token& t, const std::string& err) {
+  auto prefix =
+      std::to_string(t.line()) + ":" + std::to_string(t.column()) + ": ";
+
+  if (t.IsReservedKeyword()) {
+    error_ = prefix + "reserved token (" + t.to_str() + ") found";
+    return;
+  }
+  if (t.IsError()) {
+    error_ = prefix + t.to_str();
+    return;
+  }
+
+  if (err.size() != 0) {
+    error_ = prefix + err;
+  } else {
+    error_ = prefix + "invalid token (" + t.to_name() + ") encountered";
+  }
+}
+
+void ParserImpl::set_error(const Token& t) {
+  set_error(t, "");
+}
+
+Token ParserImpl::next() {
+  if (!token_queue_.empty()) {
+    auto t = token_queue_.front();
+    token_queue_.pop_front();
+    return t;
+  }
+  return lexer_->next();
+}
+
+Token ParserImpl::peek(size_t idx) {
+  while (token_queue_.size() < (idx + 1))
+    token_queue_.push_back(lexer_->next());
+
+  return token_queue_[idx];
+}
+
+Token ParserImpl::peek() {
+  return peek(0);
+}
+
+void ParserImpl::register_alias(const std::string& name,
+                                ast::type::Type* type) {
+  assert(type);
+  registered_aliases_[name] = type;
+}
+
+ast::type::Type* ParserImpl::get_alias(const std::string& name) {
+  if (registered_aliases_.find(name) == registered_aliases_.end()) {
+    return nullptr;
+  }
+  return registered_aliases_[name];
+}
+
+bool ParserImpl::Parse() {
+  translation_unit();
+  return !has_error();
+}
+
+// translation_unit
+//  : global_decl* EOF
+void ParserImpl::translation_unit() {
+  for (;;) {
+    global_decl();
+    if (has_error())
+      return;
+
+    if (peek().IsEof())
+      break;
+  }
+
+  assert(module_.IsValid());
+}
+
+// global_decl
+//  : SEMICOLON
+//  | import_decl SEMICOLON
+//  | global_variable_decl SEMICLON
+//  | global_constant_decl SEMICOLON
+//  | entry_point_decl SEMICOLON
+//  | type_alias SEMICOLON
+//  | function_decl
+void ParserImpl::global_decl() {
+  auto t = peek();
+  if (t.IsEof())
+    return;
+
+  if (t.IsSemicolon()) {
+    next();  // consume the peek
+    return;
+  }
+
+  auto import = import_decl();
+  if (has_error())
+    return;
+  if (import != nullptr) {
+    t = next();
+    if (!t.IsSemicolon()) {
+      set_error(t, "missing ';' for import");
+      return;
+    }
+    module_.AddImport(std::move(import));
+    return;
+  }
+
+  auto gv = global_variable_decl();
+  if (has_error())
+    return;
+  if (gv != nullptr) {
+    t = next();
+    if (!t.IsSemicolon()) {
+      set_error(t, "missing ';' for variable declaration");
+      return;
+    }
+    module_.AddGlobalVariable(std::move(gv));
+    return;
+  }
+
+  auto gc = global_constant_decl();
+  if (has_error())
+    return;
+  if (gc != nullptr) {
+    t = next();
+    if (!t.IsSemicolon()) {
+      set_error(t, "missing ';' for constant declaration");
+      return;
+    }
+    module_.AddGlobalVariable(std::move(gc));
+    return;
+  }
+
+  auto ep = entry_point_decl();
+  if (has_error())
+    return;
+  if (ep != nullptr) {
+    t = next();
+    if (!t.IsSemicolon()) {
+      set_error(t, "missing ';' for entry point");
+      return;
+    }
+    module_.AddEntryPoint(std::move(ep));
+    return;
+  }
+
+  auto ta = type_alias();
+  if (has_error())
+    return;
+  if (ta != nullptr) {
+    t = next();
+    if (!t.IsSemicolon()) {
+      set_error(t, "missing ';' for type alias");
+      return;
+    }
+    module_.AddAliasType(ta);
+    return;
+  }
+
+  auto func = function_decl();
+  if (has_error())
+    return;
+  if (func != nullptr) {
+    module_.AddFunction(std::move(func));
+    return;
+  }
+
+  set_error(t);
+}
+
+// import_decl
+//  : IMPORT STRING_LITERAL AS (IDENT NAMESPACE)* IDENT
+std::unique_ptr<ast::Import> ParserImpl::import_decl() {
+  auto t = peek();
+  if (!t.IsImport())
+    return {};
+
+  auto source = t.source();
+  next();  // consume the import token
+
+  t = next();
+  if (!t.IsStringLiteral()) {
+    set_error(t, "missing path for import");
+    return {};
+  }
+  auto path = t.to_str();
+  if (path.length() == 0) {
+    set_error(t, "import path must not be empty");
+    return {};
+  }
+
+  t = next();
+  if (!t.IsAs()) {
+    set_error(t, "missing 'as' for import");
+    return {};
+  }
+
+  std::string name = "";
+  for (;;) {
+    t = peek();
+    if (!t.IsIdentifier()) {
+      break;
+    }
+    next();  // consume the peek
+
+    name += t.to_str();
+
+    t = peek();
+    if (!t.IsNamespace()) {
+      break;
+    }
+    next();  // consume the peek
+
+    name += "::";
+  }
+  if (name.length() == 0) {
+    if (t.IsEof() || t.IsSemicolon()) {
+      set_error(t, "missing name for import");
+    } else {
+      set_error(t, "invalid name for import");
+    }
+    return {};
+  }
+  if (name.length() > 2) {
+    auto end = name.length() - 1;
+    if (name[end] == ':' && name[end - 1] == ':') {
+      set_error(t, "invalid name for import");
+      return {};
+    }
+  }
+  return std::make_unique<ast::Import>(source, path, name);
+}
+
+// global_variable_decl
+//  : variable_decoration_list variable_decl
+//  | variable_decoration_list variable_decl EQUAL const_expr
+std::unique_ptr<ast::Variable> ParserImpl::global_variable_decl() {
+  auto decos = variable_decoration_list();
+  if (has_error())
+    return nullptr;
+
+  auto var = variable_decl();
+  if (has_error())
+    return nullptr;
+  if (var == nullptr) {
+    if (decos.size() > 0)
+      set_error(peek(), "error parsing variable declaration");
+
+    return nullptr;
+  }
+
+  if (decos.size() > 0) {
+    auto dv = std::make_unique<ast::DecoratedVariable>();
+    dv->set_source(var->source());
+    dv->set_name(var->name());
+    dv->set_type(var->type());
+    dv->set_storage_class(var->storage_class());
+    dv->set_decorations(std::move(decos));
+
+    var = std::move(dv);
+  }
+
+  auto t = peek();
+  if (t.IsEqual()) {
+    next();  // Consume the peek
+
+    auto expr = const_expr();
+    if (has_error())
+      return nullptr;
+    if (expr == nullptr) {
+      set_error(peek(), "invalid expression");
+      return nullptr;
+    }
+
+    var->set_initializer(std::move(expr));
+  }
+  return var;
+}
+
+// global_constant_decl
+//  : CONST variable_ident_decl EQUAL const_expr
+std::unique_ptr<ast::Variable> ParserImpl::global_constant_decl() {
+  auto t = peek();
+  if (!t.IsConst())
+    return nullptr;
+
+  auto source = t.source();
+  next();  // Consume the peek
+
+  std::string name;
+  ast::type::Type* type;
+  std::tie(name, type) = variable_ident_decl();
+  if (has_error())
+    return nullptr;
+  if (name == "" || type == nullptr) {
+    set_error(peek(), "error parsing constant variable identifier");
+    return nullptr;
+  }
+
+  auto var = std::make_unique<ast::Variable>(source, name,
+                                             ast::StorageClass::kNone, type);
+  var->set_is_const(true);
+
+  t = next();
+  if (!t.IsEqual()) {
+    set_error(t, "missing = for const declaration");
+    return nullptr;
+  }
+
+  auto init = const_expr();
+  if (has_error())
+    return nullptr;
+  if (init == nullptr) {
+    set_error(peek(), "error parsing constant initializer");
+    return nullptr;
+  }
+  var->set_initializer(std::move(init));
+
+  return var;
+}
+
+// variable_decoration_list
+//  : ATTR_LEFT variable_decoration (COMMA variable_decoration)* ATTR_RIGHT
+std::vector<std::unique_ptr<ast::VariableDecoration>>
+ParserImpl::variable_decoration_list() {
+  std::vector<std::unique_ptr<ast::VariableDecoration>> decos;
+
+  auto t = peek();
+  if (!t.IsAttrLeft())
+    return decos;
+
+  next();  // consume the peek
+
+  auto deco = variable_decoration();
+  if (has_error())
+    return {};
+  if (deco == nullptr) {
+    t = peek();
+    if (t.IsAttrRight()) {
+      set_error(t, "empty variable decoration list");
+      return {};
+    }
+    set_error(t, "missing variable decoration for decoration list");
+    return {};
+  }
+  for (;;) {
+    decos.push_back(std::move(deco));
+
+    t = peek();
+    if (!t.IsComma()) {
+      break;
+    }
+    next();  // consume the peek
+
+    deco = variable_decoration();
+    if (has_error())
+      return {};
+    if (deco == nullptr) {
+      set_error(peek(), "missing variable decoration after comma");
+      return {};
+    }
+  }
+
+  t = peek();
+  if (!t.IsAttrRight()) {
+    deco = variable_decoration();
+    if (deco != nullptr) {
+      set_error(t, "missing comma in variable decoration list");
+      return {};
+    }
+    set_error(t, "missing ]] for variable decoration");
+    return {};
+  }
+  next();  // consume the peek
+
+  return decos;
+}
+
+// variable_decoration
+//  : LOCATION INT_LITERAL
+//  | BUILTIN builtin_decoration
+//  | BINDING INT_LITERAL
+//  | SET INT_LITERAL
+std::unique_ptr<ast::VariableDecoration> ParserImpl::variable_decoration() {
+  auto t = peek();
+  if (t.IsLocation()) {
+    next();  // consume the peek
+
+    t = next();
+    if (!t.IsIntLiteral()) {
+      set_error(t, "invalid value for location decoration");
+      return {};
+    }
+
+    return std::make_unique<ast::LocationDecoration>(t.to_i32());
+  }
+  if (t.IsBuiltin()) {
+    next();  // consume the peek
+
+    ast::Builtin builtin = builtin_decoration();
+    if (has_error())
+      return {};
+    if (builtin == ast::Builtin::kNone) {
+      set_error(peek(), "invalid value for builtin decoration");
+      return {};
+    }
+
+    return std::make_unique<ast::BuiltinDecoration>(builtin);
+  }
+  if (t.IsBinding()) {
+    next();  // consume the peek
+
+    t = next();
+    if (!t.IsIntLiteral()) {
+      set_error(t, "invalid value for binding decoration");
+      return {};
+    }
+
+    return std::make_unique<ast::BindingDecoration>(t.to_i32());
+  }
+  if (t.IsSet()) {
+    next();  // consume the peek
+
+    t = next();
+    if (!t.IsIntLiteral()) {
+      set_error(t, "invalid value for set decoration");
+      return {};
+    }
+
+    return std::make_unique<ast::SetDecoration>(t.to_i32());
+  }
+
+  return nullptr;
+}
+
+// builtin_decoration
+//  : POSITION
+//  | VERTEX_IDX
+//  | INSTANCE_IDX
+//  | FRONT_FACING
+//  | FRAG_COORD
+//  | FRAG_DEPTH
+//  | NUM_WORKGROUPS
+//  | WORKGROUP_SIZE
+//  | LOCAL_INVOC_ID
+//  | LOCAL_INVOC_IDX
+//  | GLOBAL_INVOC_ID
+ast::Builtin ParserImpl::builtin_decoration() {
+  auto t = peek();
+  if (t.IsPosition()) {
+    next();  // consume the peek
+    return ast::Builtin::kPosition;
+  }
+  if (t.IsVertexIdx()) {
+    next();  // consume the peek
+    return ast::Builtin::kVertexIdx;
+  }
+  if (t.IsInstanceIdx()) {
+    next();  // consume the peek
+    return ast::Builtin::kInstanceIdx;
+  }
+  if (t.IsFrontFacing()) {
+    next();  // consume the peek
+    return ast::Builtin::kFrontFacing;
+  }
+  if (t.IsFragCoord()) {
+    next();  // consume the peek
+    return ast::Builtin::kFragCoord;
+  }
+  if (t.IsFragDepth()) {
+    next();  // consume the peek
+    return ast::Builtin::kFragDepth;
+  }
+  if (t.IsNumWorkgroups()) {
+    next();  // consume the peek
+    return ast::Builtin::kNumWorkgroups;
+  }
+  if (t.IsWorkgroupSize()) {
+    next();  // consume the peek
+    return ast::Builtin::kWorkgroupSize;
+  }
+  if (t.IsLocalInvocationId()) {
+    next();  // consume the peek
+    return ast::Builtin::kLocalInvocationId;
+  }
+  if (t.IsLocalInvocationIdx()) {
+    next();  // consume the peek
+    return ast::Builtin::kLocalInvocationIdx;
+  }
+  if (t.IsGlobalInvocationId()) {
+    next();  // consume the peek
+    return ast::Builtin::kGlobalInvocationId;
+  }
+  return ast::Builtin::kNone;
+}
+
+// variable_decl
+//   : VAR variable_storage_decoration? variable_ident_decl
+std::unique_ptr<ast::Variable> ParserImpl::variable_decl() {
+  auto t = peek();
+  auto source = t.source();
+  if (!t.IsVar())
+    return nullptr;
+
+  next();  // Consume the peek
+
+  auto sc = variable_storage_decoration();
+  if (has_error())
+    return {};
+
+  std::string name;
+  ast::type::Type* type;
+  std::tie(name, type) = variable_ident_decl();
+  if (has_error())
+    return nullptr;
+  if (name == "" || type == nullptr) {
+    set_error(peek(), "invalid identifier declaration");
+    return nullptr;
+  }
+
+  return std::make_unique<ast::Variable>(source, name, sc, type);
+}
+
+// variable_ident_decl
+//   : IDENT COLON type_decl
+std::pair<std::string, ast::type::Type*> ParserImpl::variable_ident_decl() {
+  auto t = peek();
+  if (!t.IsIdentifier())
+    return {};
+
+  auto name = t.to_str();
+  next();  // Consume the peek
+
+  t = next();
+  if (!t.IsColon()) {
+    set_error(t, "missing : for identifier declaration");
+    return {};
+  }
+
+  auto type = type_decl();
+  if (has_error())
+    return {};
+  if (type == nullptr) {
+    set_error(peek(), "invalid type for identifier declaration");
+    return {};
+  }
+
+  return {name, type};
+}
+
+// variable_storage_decoration
+//   : LESS_THAN storage_class GREATER_THAN
+ast::StorageClass ParserImpl::variable_storage_decoration() {
+  auto t = peek();
+  if (!t.IsLessThan())
+    return ast::StorageClass::kNone;
+
+  next();  // Consume the peek
+
+  auto sc = storage_class();
+  if (has_error())
+    return sc;
+  if (sc == ast::StorageClass::kNone) {
+    set_error(peek(), "invalid storage class for variable decoration");
+    return sc;
+  }
+
+  t = next();
+  if (!t.IsGreaterThan()) {
+    set_error(t, "missing > for variable decoration");
+    return ast::StorageClass::kNone;
+  }
+
+  return sc;
+}
+
+// type_alias
+//   : TYPE IDENT EQUAL type_decl
+//   | TYPE IDENT EQUAL struct_decl
+ast::type::AliasType* ParserImpl::type_alias() {
+  auto t = peek();
+  if (!t.IsType())
+    return nullptr;
+
+  next();  // Consume the peek
+
+  t = next();
+  if (!t.IsIdentifier()) {
+    set_error(t, "missing identifier for type alias");
+    return nullptr;
+  }
+  auto name = t.to_str();
+
+  t = next();
+  if (!t.IsEqual()) {
+    set_error(t, "missing = for type alias");
+    return nullptr;
+  }
+
+  auto tm = TypeManager::Instance();
+
+  auto type = type_decl();
+  if (has_error())
+    return nullptr;
+  if (type == nullptr) {
+    auto str = struct_decl();
+    if (has_error())
+      return nullptr;
+    if (str == nullptr) {
+      set_error(peek(), "invalid type alias");
+      return nullptr;
+    }
+
+    str->set_name(name);
+    type = tm->Get(std::move(str));
+  }
+  if (type == nullptr) {
+    set_error(peek(), "invalid type for alias");
+    return nullptr;
+  }
+
+  auto alias = tm->Get(std::make_unique<ast::type::AliasType>(name, type));
+  register_alias(name, alias);
+
+  return alias->AsAlias();
+}
+
+// type_decl
+//   : IDENTIFIER
+//   | BOOL
+//   | FLOAT32
+//   | INT32
+//   | UINT32
+//   | VEC2 LESS_THAN type_decl GREATER_THAN
+//   | VEC3 LESS_THAN type_decl GREATER_THAN
+//   | VEC3 LESS_THAN type_decl GREATER_THAN
+//   | PTR LESS_THAN storage_class, type_decl GREATER_THAN
+//   | ARRAY LESS_THAN type_decl COMMA INT_LITERAL GREATER_THAN
+//   | ARRAY LESS_THAN type_decl GREATER_THAN
+//   | MAT2x2 LESS_THAN type_decl GREATER_THAN
+//   | MAT2x3 LESS_THAN type_decl GREATER_THAN
+//   | MAT2x4 LESS_THAN type_decl GREATER_THAN
+//   | MAT3x2 LESS_THAN type_decl GREATER_THAN
+//   | MAT3x3 LESS_THAN type_decl GREATER_THAN
+//   | MAT3x4 LESS_THAN type_decl GREATER_THAN
+//   | MAT4x2 LESS_THAN type_decl GREATER_THAN
+//   | MAT4x3 LESS_THAN type_decl GREATER_THAN
+//   | MAT4x4 LESS_THAN type_decl GREATER_THAN
+ast::type::Type* ParserImpl::type_decl() {
+  auto tm = TypeManager::Instance();
+
+  auto t = peek();
+  if (t.IsIdentifier()) {
+    next();  // Consume the peek
+    auto alias = get_alias(t.to_str());
+    if (alias == nullptr) {
+      set_error(t, "unknown type alias '" + t.to_str() + "'");
+      return nullptr;
+    }
+    return alias;
+  }
+  if (t.IsBool()) {
+    next();  // Consume the peek
+    return tm->Get(std::make_unique<ast::type::BoolType>());
+  }
+  if (t.IsF32()) {
+    next();  // Consume the peek
+    return tm->Get(std::make_unique<ast::type::F32Type>());
+  }
+  if (t.IsI32()) {
+    next();  // Consume the peek
+    return tm->Get(std::make_unique<ast::type::I32Type>());
+  }
+  if (t.IsU32()) {
+    next();  // Consume the peek
+    return tm->Get(std::make_unique<ast::type::U32Type>());
+  }
+  if (t.IsVec2() || t.IsVec3() || t.IsVec4()) {
+    return type_decl_vector(t);
+  }
+  if (t.IsPtr()) {
+    return type_decl_pointer(t);
+  }
+  if (t.IsArray()) {
+    return type_decl_array(t);
+  }
+  if (t.IsMat2x2() || t.IsMat2x3() || t.IsMat2x4() || t.IsMat3x2() ||
+      t.IsMat3x3() || t.IsMat3x4() || t.IsMat4x2() || t.IsMat4x3() ||
+      t.IsMat4x4()) {
+    return type_decl_matrix(t);
+  }
+  return nullptr;
+}
+
+ast::type::Type* ParserImpl::type_decl_pointer(Token t) {
+  next();  // Consume the peek
+
+  t = next();
+  if (!t.IsLessThan()) {
+    set_error(t, "missing < for ptr declaration");
+    return nullptr;
+  }
+
+  auto sc = storage_class();
+  if (has_error())
+    return nullptr;
+  if (sc == ast::StorageClass::kNone) {
+    set_error(peek(), "missing storage class for ptr declaration");
+    return nullptr;
+  }
+
+  t = next();
+  if (!t.IsComma()) {
+    set_error(t, "missing , for ptr declaration");
+    return nullptr;
+  }
+
+  auto subtype = type_decl();
+  if (has_error())
+    return nullptr;
+  if (subtype == nullptr) {
+    set_error(peek(), "missing type for ptr declaration");
+    return nullptr;
+  }
+
+  t = next();
+  if (!t.IsGreaterThan()) {
+    set_error(t, "missing > for ptr declaration");
+    return nullptr;
+  }
+
+  return TypeManager::Instance()->Get(
+      std::make_unique<ast::type::PointerType>(subtype, sc));
+}
+
+ast::type::Type* ParserImpl::type_decl_vector(Token t) {
+  next();  // Consume the peek
+
+  size_t count = 2;
+  if (t.IsVec3())
+    count = 3;
+  else if (t.IsVec4())
+    count = 4;
+
+  t = next();
+  if (!t.IsLessThan()) {
+    set_error(t, "missing < for vector");
+    return nullptr;
+  }
+
+  auto subtype = type_decl();
+  if (has_error())
+    return nullptr;
+  if (subtype == nullptr) {
+    set_error(peek(), "unable to determine subtype for vector");
+    return nullptr;
+  }
+
+  t = next();
+  if (!t.IsGreaterThan()) {
+    set_error(t, "missing > for vector");
+    return nullptr;
+  }
+
+  return TypeManager::Instance()->Get(
+      std::make_unique<ast::type::VectorType>(subtype, count));
+}
+
+ast::type::Type* ParserImpl::type_decl_array(Token t) {
+  next();  // Consume the peek
+
+  t = next();
+  if (!t.IsLessThan()) {
+    set_error(t, "missing < for array declaration");
+    return nullptr;
+  }
+
+  auto subtype = type_decl();
+  if (has_error())
+    return nullptr;
+  if (subtype == nullptr) {
+    set_error(peek(), "invalid type for array declaration");
+    return nullptr;
+  }
+
+  t = next();
+  size_t size = 0;
+  if (t.IsComma()) {
+    t = next();
+    if (!t.IsIntLiteral()) {
+      set_error(t, "missing size of array declaration");
+      return nullptr;
+    }
+    if (t.to_i32() <= 0) {
+      set_error(t, "invalid size for array declaration");
+      return nullptr;
+    }
+    size = static_cast<size_t>(t.to_i32());
+    t = next();
+  }
+  if (!t.IsGreaterThan()) {
+    set_error(t, "missing > for array declaration");
+    return nullptr;
+  }
+
+  return TypeManager::Instance()->Get(
+      std::make_unique<ast::type::ArrayType>(subtype, size));
+}
+
+ast::type::Type* ParserImpl::type_decl_matrix(Token t) {
+  next();  // Consume the peek
+
+  size_t rows = 2;
+  size_t columns = 2;
+  if (t.IsMat3x2() || t.IsMat3x3() || t.IsMat3x4()) {
+    rows = 3;
+  } else if (t.IsMat4x2() || t.IsMat4x3() || t.IsMat4x4()) {
+    rows = 4;
+  }
+  if (t.IsMat2x3() || t.IsMat3x3() || t.IsMat4x3()) {
+    columns = 3;
+  } else if (t.IsMat2x4() || t.IsMat3x4() || t.IsMat4x4()) {
+    columns = 4;
+  }
+
+  t = next();
+  if (!t.IsLessThan()) {
+    set_error(t, "missing < for matrix");
+    return nullptr;
+  }
+
+  auto subtype = type_decl();
+  if (has_error())
+    return nullptr;
+  if (subtype == nullptr) {
+    set_error(peek(), "unable to determine subtype for matrix");
+    return nullptr;
+  }
+
+  t = next();
+  if (!t.IsGreaterThan()) {
+    set_error(t, "missing > for matrix");
+    return nullptr;
+  }
+
+  return TypeManager::Instance()->Get(
+      std::make_unique<ast::type::MatrixType>(subtype, rows, columns));
+}
+
+// storage_class
+//  : INPUT
+//  | OUTPUT
+//  | UNIFORM
+//  | WORKGROUP
+//  | UNIFORM_CONSTANT
+//  | STORAGE_BUFFER
+//  | IMAGE
+//  | PUSH_CONSTANT
+//  | PRIVATE
+//  | FUNCTION
+ast::StorageClass ParserImpl::storage_class() {
+  auto t = peek();
+  if (t.IsIn()) {
+    next();  // consume the peek
+    return ast::StorageClass::kInput;
+  }
+  if (t.IsOut()) {
+    next();  // consume the peek
+    return ast::StorageClass::kOutput;
+  }
+  if (t.IsUniform()) {
+    next();  // consume the peek
+    return ast::StorageClass::kUniform;
+  }
+  if (t.IsWorkgroup()) {
+    next();  // consume the peek
+    return ast::StorageClass::kWorkgroup;
+  }
+  if (t.IsUniformConstant()) {
+    next();  // consume the peek
+    return ast::StorageClass::kUniformConstant;
+  }
+  if (t.IsStorageBuffer()) {
+    next();  // consume the peek
+    return ast::StorageClass::kStorageBuffer;
+  }
+  if (t.IsImage()) {
+    next();  // consume the peek
+    return ast::StorageClass::kImage;
+  }
+  if (t.IsPushConstant()) {
+    next();  // consume the peek
+    return ast::StorageClass::kPushConstant;
+  }
+  if (t.IsPrivate()) {
+    next();  // consume the peek
+    return ast::StorageClass::kPrivate;
+  }
+  if (t.IsFunction()) {
+    next();  // consume the peek
+    return ast::StorageClass::kFunction;
+  }
+  return ast::StorageClass::kNone;
+}
+
+// struct_decl
+//   : struct_decoration_decl? STRUCT struct_body_decl
+std::unique_ptr<ast::type::StructType> ParserImpl::struct_decl() {
+  auto t = peek();
+  auto source = t.source();
+
+  auto deco = struct_decoration_decl();
+  if (has_error())
+    return nullptr;
+
+  t = next();
+  if (!t.IsStruct()) {
+    set_error(t, "missing struct declaration");
+    return nullptr;
+  }
+
+  t = peek();
+  if (!t.IsBracketLeft()) {
+    set_error(t, "missing { for struct declaration");
+    return nullptr;
+  }
+
+  auto body = struct_body_decl();
+  if (has_error()) {
+    return nullptr;
+  }
+
+  return std::make_unique<ast::type::StructType>(
+      std::make_unique<ast::Struct>(source, deco, std::move(body)));
+}
+
+// struct_decoration_decl
+//  : ATTR_LEFT struct_decoration ATTR_RIGHT
+ast::StructDecoration ParserImpl::struct_decoration_decl() {
+  auto t = peek();
+  if (!t.IsAttrLeft())
+    return ast::StructDecoration::kNone;
+
+  next();  // Consume the peek
+
+  auto deco = struct_decoration();
+  if (has_error())
+    return ast::StructDecoration::kNone;
+  if (deco == ast::StructDecoration::kNone) {
+    set_error(peek(), "unknown struct decoration");
+    return ast::StructDecoration::kNone;
+  }
+
+  t = next();
+  if (!t.IsAttrRight()) {
+    set_error(t, "missing ]] for struct decoration");
+    return ast::StructDecoration::kNone;
+  }
+
+  return deco;
+}
+
+// struct_decoration
+//  : BLOCK
+ast::StructDecoration ParserImpl::struct_decoration() {
+  auto t = peek();
+  if (t.IsBlock()) {
+    next();  // Consume the peek
+    return ast::StructDecoration::kBlock;
+  }
+  return ast::StructDecoration::kNone;
+}
+
+// struct_body_decl
+//   : BRACKET_LEFT struct_member* BRACKET_RIGHT
+std::vector<std::unique_ptr<ast::StructMember>> ParserImpl::struct_body_decl() {
+  auto t = peek();
+  if (!t.IsBracketLeft())
+    return {};
+
+  next();  // Consume the peek
+
+  t = peek();
+  if (t.IsBracketRight())
+    return {};
+
+  std::vector<std::unique_ptr<ast::StructMember>> members;
+  for (;;) {
+    auto mem = struct_member();
+    if (has_error())
+      return {};
+    if (mem == nullptr) {
+      set_error(peek(), "invalid struct member");
+      return {};
+    }
+
+    members.push_back(std::move(mem));
+
+    t = peek();
+    if (t.IsBracketRight() || t.IsEof())
+      break;
+  }
+
+  t = next();
+  if (!t.IsBracketRight()) {
+    set_error(t, "missing } for struct declaration");
+    return {};
+  }
+
+  return members;
+}
+
+// struct_member
+//   : struct_member_decoration_decl variable_ident_decl SEMICOLON
+std::unique_ptr<ast::StructMember> ParserImpl::struct_member() {
+  auto t = peek();
+  auto source = t.source();
+
+  auto decos = struct_member_decoration_decl();
+  if (has_error())
+    return nullptr;
+
+  std::string name;
+  ast::type::Type* type;
+  std::tie(name, type) = variable_ident_decl();
+  if (has_error())
+    return nullptr;
+  if (name == "" || type == nullptr) {
+    set_error(peek(), "invalid identifier declaration");
+    return nullptr;
+  }
+
+  t = next();
+  if (!t.IsSemicolon()) {
+    set_error(t, "missing ; for struct member");
+    return nullptr;
+  }
+
+  return std::make_unique<ast::StructMember>(source, name, type,
+                                             std::move(decos));
+}
+
+// struct_member_decoration_decl
+//   :
+//   | ATTR_LEFT (struct_member_decoration COMMA)*
+//                struct_member_decoration ATTR_RIGHT
+std::vector<std::unique_ptr<ast::StructMemberDecoration>>
+ParserImpl::struct_member_decoration_decl() {
+  auto t = peek();
+  if (!t.IsAttrLeft())
+    return {};
+
+  next();  // Consume the peek
+
+  t = peek();
+  if (t.IsAttrRight()) {
+    set_error(t, "empty struct member decoration found");
+    return {};
+  }
+
+  std::vector<std::unique_ptr<ast::StructMemberDecoration>> decos;
+  bool found_offset = false;
+  for (;;) {
+    auto deco = struct_member_decoration();
+    if (has_error())
+      return {};
+    if (deco == nullptr)
+      break;
+
+    if (deco->IsOffset()) {
+      if (found_offset) {
+        set_error(peek(), "duplicate offset decoration found");
+        return {};
+      }
+      found_offset = true;
+    }
+    decos.push_back(std::move(deco));
+
+    t = next();
+    if (!t.IsComma())
+      break;
+  }
+
+  if (!t.IsAttrRight()) {
+    set_error(t, "missing ]] for struct member decoration");
+    return {};
+  }
+  return decos;
+}
+
+// struct_member_decoration
+//   : OFFSET INT_LITERAL
+std::unique_ptr<ast::StructMemberDecoration>
+ParserImpl::struct_member_decoration() {
+  auto t = peek();
+  if (!t.IsOffset())
+    return nullptr;
+
+  next();  // Consume the peek
+
+  t = next();
+  if (!t.IsIntLiteral()) {
+    set_error(t, "invalid value for offset decoration");
+    return nullptr;
+  }
+  int32_t val = t.to_i32();
+  if (val < 0) {
+    set_error(t, "offset value must be >= 0");
+    return nullptr;
+  }
+
+  return std::make_unique<ast::StructMemberOffsetDecoration>(
+      static_cast<size_t>(val));
+}
+
+// function_decl
+//   : function_header body_stmt
+std::unique_ptr<ast::Function> ParserImpl::function_decl() {
+  auto f = function_header();
+  if (has_error())
+    return nullptr;
+  if (f == nullptr)
+    return nullptr;
+
+  auto body = body_stmt();
+  if (has_error())
+    return nullptr;
+
+  f->set_body(std::move(body));
+  return f;
+}
+
+// function_type_decl
+//   : type_decl
+//   | VOID
+ast::type::Type* ParserImpl::function_type_decl() {
+  auto tm = TypeManager::Instance();
+
+  auto t = peek();
+  if (t.IsVoid()) {
+    next();  // Consume the peek
+    return tm->Get(std::make_unique<ast::type::VoidType>());
+  }
+  return type_decl();
+}
+
+// function_header
+//   : FN IDENT PAREN_LEFT param_list PAREN_RIGHT ARROW function_type_decl
+std::unique_ptr<ast::Function> ParserImpl::function_header() {
+  auto t = peek();
+  if (!t.IsFn())
+    return nullptr;
+
+  auto source = t.source();
+  next();  // Consume the peek
+
+  t = next();
+  if (!t.IsIdentifier()) {
+    set_error(t, "missing identifier for function");
+    return nullptr;
+  }
+  auto name = t.to_str();
+
+  t = next();
+  if (!t.IsParenLeft()) {
+    set_error(t, "missing ( for function declaration");
+    return nullptr;
+  }
+
+  auto params = param_list();
+  if (has_error())
+    return nullptr;
+
+  t = next();
+  if (!t.IsParenRight()) {
+    set_error(t, "missing ) for function declaration");
+    return nullptr;
+  }
+
+  t = next();
+  if (!t.IsArrow()) {
+    set_error(t, "missing -> for function declaration");
+    return nullptr;
+  }
+
+  auto type = function_type_decl();
+  if (has_error())
+    return nullptr;
+  if (type == nullptr) {
+    set_error(peek(), "unable to determine function return type");
+    return nullptr;
+  }
+
+  return std::make_unique<ast::Function>(source, name, std::move(params), type);
+}
+
+// param_list
+//   :
+//   | (variable_ident_decl COMMA)* variable_ident_decl
+std::vector<std::unique_ptr<ast::Variable>> ParserImpl::param_list() {
+  auto t = peek();
+  auto source = t.source();
+
+  std::vector<std::unique_ptr<ast::Variable>> ret;
+
+  std::string name;
+  ast::type::Type* type;
+  std::tie(name, type) = variable_ident_decl();
+  if (has_error())
+    return {};
+  if (name == "" || type == nullptr)
+    return {};
+
+  for (;;) {
+    ret.push_back(std::make_unique<ast::Variable>(
+        source, name, ast::StorageClass::kNone, type));
+
+    t = peek();
+    if (!t.IsComma())
+      break;
+
+    source = t.source();
+    next();  // Consume the peek
+
+    std::tie(name, type) = variable_ident_decl();
+    if (has_error())
+      return {};
+    if (name == "" || type == nullptr) {
+      set_error(t, "found , but no variable declaration");
+      return {};
+    }
+  }
+
+  return ret;
+}
+
+// entry_point_decl
+//   : ENTRY_POINT pipeline_stage EQUAL IDENT
+//   | ENTRY_POINT pipeline_stage AS STRING_LITERAL EQUAL IDENT
+//   | ENTRY_POINT pipeline_stage AS IDENT EQUAL IDENT
+std::unique_ptr<ast::EntryPoint> ParserImpl::entry_point_decl() {
+  auto t = peek();
+  auto source = t.source();
+  if (!t.IsEntryPoint())
+    return nullptr;
+
+  next();  // Consume the peek
+
+  auto stage = pipeline_stage();
+  if (has_error())
+    return nullptr;
+  if (stage == ast::PipelineStage::kNone) {
+    set_error(peek(), "missing pipeline stage for entry point");
+    return nullptr;
+  }
+
+  t = next();
+  std::string name;
+  if (t.IsAs()) {
+    t = next();
+    if (t.IsStringLiteral()) {
+      name = t.to_str();
+    } else if (t.IsIdentifier()) {
+      name = t.to_str();
+    } else {
+      set_error(t, "invalid name for entry point");
+      return nullptr;
+    }
+    t = next();
+  }
+
+  if (!t.IsEqual()) {
+    set_error(t, "missing = for entry point");
+    return nullptr;
+  }
+
+  t = next();
+  if (!t.IsIdentifier()) {
+    set_error(t, "invalid function name for entry point");
+    return nullptr;
+  }
+  auto fn_name = t.to_str();
+
+  // Set the name to the function name if it isn't provided
+  if (name.length() == 0)
+    name = fn_name;
+
+  return std::make_unique<ast::EntryPoint>(source, stage, name, fn_name);
+}
+
+// pipeline_stage
+//   : VERTEX
+//   | FRAGMENT
+//   | COMPUTE
+ast::PipelineStage ParserImpl::pipeline_stage() {
+  auto t = peek();
+  if (t.IsVertex()) {
+    next();  // consume the peek
+    return ast::PipelineStage::kVertex;
+  }
+  if (t.IsFragment()) {
+    next();  // consume the peek
+    return ast::PipelineStage::kFragment;
+  }
+  if (t.IsCompute()) {
+    next();  // consume the peek
+    return ast::PipelineStage::kCompute;
+  }
+  return ast::PipelineStage::kNone;
+}
+
+// body_stmt
+//   : BRACKET_LEFT statements BRACKET_RIGHT
+std::vector<std::unique_ptr<ast::Statement>> ParserImpl::body_stmt() {
+  auto t = peek();
+  if (!t.IsBracketLeft())
+    return {};
+
+  next();  // Consume the peek
+
+  auto stmts = statements();
+  if (has_error())
+    return {};
+
+  t = next();
+  if (!t.IsBracketRight()) {
+    set_error(t, "missing }");
+    return {};
+  }
+
+  return stmts;
+}
+
+// paren_rhs_stmt
+//   : PAREN_LEFT logical_or_expression PAREN_RIGHT
+std::unique_ptr<ast::Expression> ParserImpl::paren_rhs_stmt() {
+  auto t = peek();
+  if (!t.IsParenLeft()) {
+    set_error(t, "expected (");
+    return nullptr;
+  }
+  next();  // Consume the peek
+
+  auto expr = logical_or_expression();
+  if (has_error())
+    return nullptr;
+  if (expr == nullptr) {
+    set_error(peek(), "unable to parse expression");
+    return nullptr;
+  }
+
+  t = next();
+  if (!t.IsParenRight()) {
+    set_error(t, "expected )");
+    return nullptr;
+  }
+
+  return expr;
+}
+
+// statements
+//   : statement*
+std::vector<std::unique_ptr<ast::Statement>> ParserImpl::statements() {
+  std::vector<std::unique_ptr<ast::Statement>> ret;
+
+  for (;;) {
+    auto stmt = statement();
+    if (has_error())
+      return {};
+    if (stmt == nullptr)
+      break;
+
+    ret.push_back(std::move(stmt));
+  }
+
+  return ret;
+}
+
+// statement
+//   : SEMICOLON
+//   | RETURN logical_or_expression SEMICOLON
+//   | if_stmt
+//   | unless_stmt
+//   | regardless_stmt
+//   | switch_stmt
+//   | loop_stmt
+//   | variable_stmt SEMICOLON
+//   | break_stmt SEMICOLON
+//   | continue_stmt SEMICOLON
+//   | KILL SEMICOLON
+//   | NOP SEMICOLON
+//   | assignment_expression SEMICOLON
+std::unique_ptr<ast::Statement> ParserImpl::statement() {
+  auto t = peek();
+  if (t.IsSemicolon()) {
+    next();  // Consume the peek
+    return statement();
+  }
+
+  if (t.IsReturn()) {
+    auto source = t.source();
+    next();  // Consume the peek
+
+    t = peek();
+
+    std::unique_ptr<ast::Expression> expr = nullptr;
+    if (!t.IsSemicolon()) {
+      expr = logical_or_expression();
+      if (has_error())
+        return nullptr;
+    }
+
+    t = next();
+    if (!t.IsSemicolon()) {
+      set_error(t, "missing ;");
+      return nullptr;
+    }
+    return std::make_unique<ast::ReturnStatement>(source, std::move(expr));
+  }
+
+  auto stmt_if = if_stmt();
+  if (has_error())
+    return nullptr;
+  if (stmt_if != nullptr)
+    return stmt_if;
+
+  auto unless = unless_stmt();
+  if (has_error())
+    return nullptr;
+  if (unless != nullptr)
+    return unless;
+
+  auto regardless = regardless_stmt();
+  if (has_error())
+    return nullptr;
+  if (regardless != nullptr)
+    return regardless;
+
+  auto sw = switch_stmt();
+  if (has_error())
+    return nullptr;
+  if (sw != nullptr)
+    return sw;
+
+  auto loop = loop_stmt();
+  if (has_error())
+    return nullptr;
+  if (loop != nullptr)
+    return loop;
+
+  auto var = variable_stmt();
+  if (has_error())
+    return nullptr;
+  if (var != nullptr) {
+    t = next();
+    if (!t.IsSemicolon()) {
+      set_error(t, "missing ;");
+      return nullptr;
+    }
+    return var;
+  }
+
+  auto b = break_stmt();
+  if (has_error())
+    return nullptr;
+  if (b != nullptr) {
+    t = next();
+    if (!t.IsSemicolon()) {
+      set_error(t, "missing ;");
+      return nullptr;
+    }
+    return b;
+  }
+
+  auto cont = continue_stmt();
+  if (has_error())
+    return nullptr;
+  if (cont != nullptr) {
+    t = next();
+    if (!t.IsSemicolon()) {
+      set_error(t, "missing ;");
+      return nullptr;
+    }
+    return cont;
+  }
+
+  if (t.IsKill()) {
+    auto source = t.source();
+    next();  // Consume the peek
+
+    t = next();
+    if (!t.IsSemicolon()) {
+      set_error(t, "missing ;");
+      return nullptr;
+    }
+    return std::make_unique<ast::KillStatement>(source);
+  }
+
+  if (t.IsNop()) {
+    auto source = t.source();
+    next();  // Consume the peek
+
+    t = next();
+    if (!t.IsSemicolon()) {
+      set_error(t, "missing ;");
+      return nullptr;
+    }
+    return std::make_unique<ast::NopStatement>(source);
+  }
+
+  auto assign = assignment_stmt();
+  if (has_error())
+    return nullptr;
+  if (assign != nullptr) {
+    t = next();
+    if (!t.IsSemicolon()) {
+      set_error(t, "missing ;");
+      return nullptr;
+    }
+    return assign;
+  }
+
+  return nullptr;
+}
+
+// break_stmt
+//   : BREAK ({IF | UNLESS} paren_rhs_stmt)?
+std::unique_ptr<ast::BreakStatement> ParserImpl::break_stmt() {
+  auto t = peek();
+  if (!t.IsBreak())
+    return nullptr;
+
+  auto source = t.source();
+  next();  // Consume the peek
+
+  ast::StatementCondition condition = ast::StatementCondition::kNone;
+  std::unique_ptr<ast::Expression> conditional = nullptr;
+
+  t = peek();
+  if (t.IsIf() || t.IsUnless()) {
+    next();  // Consume the peek
+
+    if (t.IsIf())
+      condition = ast::StatementCondition::kIf;
+    else
+      condition = ast::StatementCondition::kUnless;
+
+    conditional = paren_rhs_stmt();
+    if (has_error())
+      return nullptr;
+    if (conditional == nullptr) {
+      set_error(peek(), "unable to parse conditional statement");
+      return nullptr;
+    }
+  }
+
+  return std::make_unique<ast::BreakStatement>(source, condition,
+                                               std::move(conditional));
+}
+
+// continue_stmt
+//   : CONTINUE ({IF | UNLESS} paren_rhs_stmt)?
+std::unique_ptr<ast::ContinueStatement> ParserImpl::continue_stmt() {
+  auto t = peek();
+  if (!t.IsContinue())
+    return nullptr;
+
+  auto source = t.source();
+  next();  // Consume the peek
+
+  ast::StatementCondition condition = ast::StatementCondition::kNone;
+  std::unique_ptr<ast::Expression> conditional = nullptr;
+
+  t = peek();
+  if (t.IsIf() || t.IsUnless()) {
+    next();  // Consume the peek
+
+    if (t.IsIf())
+      condition = ast::StatementCondition::kIf;
+    else
+      condition = ast::StatementCondition::kUnless;
+
+    conditional = paren_rhs_stmt();
+    if (has_error())
+      return nullptr;
+    if (conditional == nullptr) {
+      set_error(peek(), "unable to parse conditional statement");
+      return nullptr;
+    }
+  }
+
+  return std::make_unique<ast::ContinueStatement>(source, condition,
+                                                  std::move(conditional));
+}
+
+// variable_stmt
+//   : variable_decl
+//   | variable_decl EQUAL logical_or_expression
+//   | CONST variable_ident_decl EQUAL logical_or_expression
+std::unique_ptr<ast::VariableStatement> ParserImpl::variable_stmt() {
+  auto t = peek();
+  auto source = t.source();
+  if (t.IsConst()) {
+    next();  // Consume the peek
+
+    std::string name;
+    ast::type::Type* type;
+    std::tie(name, type) = variable_ident_decl();
+    if (has_error())
+      return nullptr;
+    if (name == "" || type == nullptr) {
+      set_error(peek(), "unable to parse variable declaration");
+      return nullptr;
+    }
+
+    t = next();
+    if (!t.IsEqual()) {
+      set_error(t, "missing = for constant declaration");
+      return nullptr;
+    }
+
+    auto initializer = logical_or_expression();
+    if (has_error())
+      return nullptr;
+    if (initializer == nullptr) {
+      set_error(peek(), "missing initializer for const declaration");
+      return nullptr;
+    }
+
+    auto var = std::make_unique<ast::Variable>(source, name,
+                                               ast::StorageClass::kNone, type);
+    var->set_is_const(true);
+    var->set_initializer(std::move(initializer));
+
+    return std::make_unique<ast::VariableStatement>(source, std::move(var));
+  }
+
+  auto var = variable_decl();
+  if (has_error())
+    return nullptr;
+  if (var == nullptr)
+    return nullptr;
+
+  t = peek();
+  if (t.IsEqual()) {
+    next();  // Consume the peek
+    auto initializer = logical_or_expression();
+    if (has_error())
+      return nullptr;
+    if (initializer == nullptr) {
+      set_error(peek(), "missing initializer for variable declaration");
+      return nullptr;
+    }
+    var->set_initializer(std::move(initializer));
+  }
+
+  return std::make_unique<ast::VariableStatement>(source, std::move(var));
+}
+
+// if_stmt
+//   : IF paren_rhs_stmt body_stmt
+//           {(elseif_stmt else_stmt?) | (else_stmt premerge_stmt?)}
+std::unique_ptr<ast::IfStatement> ParserImpl::if_stmt() {
+  auto t = peek();
+  if (!t.IsIf())
+    return nullptr;
+
+  auto source = t.source();
+  next();  // Consume the peek
+
+  auto condition = paren_rhs_stmt();
+  if (has_error())
+    return nullptr;
+  if (condition == nullptr) {
+    set_error(peek(), "unable to parse if condition");
+    return nullptr;
+  }
+
+  t = peek();
+  if (!t.IsBracketLeft()) {
+    set_error(t, "missing {");
+    return nullptr;
+  }
+
+  auto body = body_stmt();
+  if (has_error())
+    return nullptr;
+
+  auto elseif = elseif_stmt();
+  if (has_error())
+    return nullptr;
+
+  auto el = else_stmt();
+  if (has_error())
+    return nullptr;
+
+  auto stmt = std::make_unique<ast::IfStatement>(source, std::move(condition),
+                                                 std::move(body));
+  if (el != nullptr) {
+    if (elseif.size() == 0) {
+      auto premerge = premerge_stmt();
+      if (has_error())
+        return nullptr;
+
+      stmt->set_premerge(std::move(premerge));
+    }
+    elseif.push_back(std::move(el));
+  }
+  stmt->set_else_statements(std::move(elseif));
+
+  return stmt;
+}
+
+// elseif_stmt
+//   : ELSE_IF paren_rhs_stmt body_stmt elseif_stmt?
+std::vector<std::unique_ptr<ast::ElseStatement>> ParserImpl::elseif_stmt() {
+  auto t = peek();
+  if (!t.IsElseIf())
+    return {};
+
+  std::vector<std::unique_ptr<ast::ElseStatement>> ret;
+  for (;;) {
+    auto source = t.source();
+    next();  // Consume the peek
+
+    auto condition = paren_rhs_stmt();
+    if (has_error())
+      return {};
+    if (condition == nullptr) {
+      set_error(peek(), "unable to parse condition expression");
+      return {};
+    }
+
+    t = peek();
+    if (!t.IsBracketLeft()) {
+      set_error(t, "missing {");
+      return {};
+    }
+
+    auto body = body_stmt();
+    if (has_error())
+      return {};
+
+    ret.push_back(std::make_unique<ast::ElseStatement>(
+        source, std::move(condition), std::move(body)));
+
+    t = peek();
+    if (!t.IsElseIf())
+      break;
+  }
+
+  return ret;
+}
+
+// else_stmt
+//   : ELSE body_stmt
+std::unique_ptr<ast::ElseStatement> ParserImpl::else_stmt() {
+  auto t = peek();
+  if (!t.IsElse())
+    return nullptr;
+
+  auto source = t.source();
+  next();  // Consume the peek
+
+  t = peek();
+  if (!t.IsBracketLeft()) {
+    set_error(t, "missing {");
+    return nullptr;
+  }
+
+  auto body = body_stmt();
+  if (has_error())
+    return nullptr;
+
+  return std::make_unique<ast::ElseStatement>(source, std::move(body));
+}
+
+// premerge_stmt
+//   : PREMERGE body_stmt
+std::vector<std::unique_ptr<ast::Statement>> ParserImpl::premerge_stmt() {
+  auto t = peek();
+  if (!t.IsPremerge())
+    return {};
+
+  next();  // Consume the peek
+  return body_stmt();
+}
+
+// unless_stmt
+//   : UNLESS paren_rhs_stmt body_stmt
+std::unique_ptr<ast::UnlessStatement> ParserImpl::unless_stmt() {
+  auto t = peek();
+  if (!t.IsUnless())
+    return nullptr;
+
+  auto source = t.source();
+  next();  // Consume the peek
+
+  auto condition = paren_rhs_stmt();
+  if (has_error())
+    return nullptr;
+  if (condition == nullptr) {
+    set_error(peek(), "unable to parse unless condition");
+    return nullptr;
+  }
+
+  auto body = body_stmt();
+  if (has_error())
+    return nullptr;
+
+  return std::make_unique<ast::UnlessStatement>(source, std::move(condition),
+                                                std::move(body));
+}
+
+// regardless_stmt
+//   : REGARDLESS paren_rhs_stmt body_stmt
+std::unique_ptr<ast::RegardlessStatement> ParserImpl::regardless_stmt() {
+  auto t = peek();
+  if (!t.IsRegardless())
+    return nullptr;
+
+  auto source = t.source();
+  next();  // Consume the peek
+
+  auto condition = paren_rhs_stmt();
+  if (has_error())
+    return nullptr;
+  if (condition == nullptr) {
+    set_error(peek(), "unable to parse regardless condition");
+    return nullptr;
+  }
+
+  auto body = body_stmt();
+  if (has_error())
+    return nullptr;
+
+  return std::make_unique<ast::RegardlessStatement>(
+      source, std::move(condition), std::move(body));
+}
+
+// switch_stmt
+//   : SWITCH paren_rhs_stmt BRACKET_LEFT switch_body+ BRACKET_RIGHT
+std::unique_ptr<ast::SwitchStatement> ParserImpl::switch_stmt() {
+  auto t = peek();
+  if (!t.IsSwitch())
+    return nullptr;
+
+  auto source = t.source();
+  next();  // Consume the peek
+
+  auto condition = paren_rhs_stmt();
+  if (has_error())
+    return nullptr;
+  if (condition == nullptr) {
+    set_error(peek(), "unable to parse switch expression");
+    return nullptr;
+  }
+
+  t = next();
+  if (!t.IsBracketLeft()) {
+    set_error(t, "missing { for switch statement");
+    return nullptr;
+  }
+
+  std::vector<std::unique_ptr<ast::CaseStatement>> body;
+  for (;;) {
+    auto stmt = switch_body();
+    if (has_error())
+      return nullptr;
+    if (stmt == nullptr)
+      break;
+
+    body.push_back(std::move(stmt));
+  }
+
+  t = next();
+  if (!t.IsBracketRight()) {
+    set_error(t, "missing } for switch statement");
+    return nullptr;
+  }
+  return std::make_unique<ast::SwitchStatement>(source, std::move(condition),
+                                                std::move(body));
+}
+
+// switch_body
+//   : CASE const_literal COLON BRACKET_LEFT case_body BRACKET_RIGHT
+//   | DEFAULT COLON BRACKET_LEFT case_body BRACKET_RIGHT
+std::unique_ptr<ast::CaseStatement> ParserImpl::switch_body() {
+  auto t = peek();
+  if (!t.IsCase() && !t.IsDefault())
+    return nullptr;
+
+  auto source = t.source();
+  next();  // Consume the peek
+
+  auto stmt = std::make_unique<ast::CaseStatement>();
+  stmt->set_source(source);
+  if (t.IsCase()) {
+    auto cond = const_literal();
+    if (has_error())
+      return nullptr;
+    if (cond == nullptr) {
+      set_error(peek(), "unable to parse case conditional");
+      return nullptr;
+    }
+    stmt->set_condition(std::move(cond));
+  }
+
+  t = next();
+  if (!t.IsColon()) {
+    set_error(t, "missing : for case statement");
+    return nullptr;
+  }
+
+  t = next();
+  if (!t.IsBracketLeft()) {
+    set_error(t, "missing { for case statement");
+    return nullptr;
+  }
+
+  auto body = case_body();
+  if (has_error())
+    return nullptr;
+
+  stmt->set_body(std::move(body));
+
+  t = next();
+  if (!t.IsBracketRight()) {
+    set_error(t, "missing } for case statement");
+    return nullptr;
+  }
+
+  return stmt;
+}
+
+// case_body
+//   :
+//   | statement case_body
+//   | FALLTHROUGH SEMICOLON
+std::vector<std::unique_ptr<ast::Statement>> ParserImpl::case_body() {
+  std::vector<std::unique_ptr<ast::Statement>> ret;
+  for (;;) {
+    auto t = peek();
+    if (t.IsFallthrough()) {
+      auto source = t.source();
+      next();  // Consume the peek
+
+      t = next();
+      if (!t.IsSemicolon()) {
+        set_error(t, "missing ;");
+        return {};
+      }
+
+      ret.push_back(std::make_unique<ast::FallthroughStatement>(source));
+      break;
+    }
+
+    auto stmt = statement();
+    if (has_error())
+      return {};
+    if (stmt == nullptr)
+      break;
+
+    ret.push_back(std::move(stmt));
+  }
+
+  return ret;
+}
+
+// loop_stmt
+//   : LOOP BRACKET_LEFT statements continuing_stmt? BRACKET_RIGHT
+std::unique_ptr<ast::LoopStatement> ParserImpl::loop_stmt() {
+  auto t = peek();
+  if (!t.IsLoop())
+    return nullptr;
+
+  auto source = t.source();
+  next();  // Consume the peek
+
+  t = next();
+  if (!t.IsBracketLeft()) {
+    set_error(t, "missing { for loop");
+    return nullptr;
+  }
+
+  auto body = statements();
+  if (has_error())
+    return nullptr;
+
+  auto continuing = continuing_stmt();
+  if (has_error())
+    return nullptr;
+
+  t = next();
+  if (!t.IsBracketRight()) {
+    set_error(t, "missing } for loop");
+    return nullptr;
+  }
+
+  return std::make_unique<ast::LoopStatement>(source, std::move(body),
+                                              std::move(continuing));
+}
+
+// continuing_stmt
+//   : CONTINUING body_stmt
+std::vector<std::unique_ptr<ast::Statement>> ParserImpl::continuing_stmt() {
+  auto t = peek();
+  if (!t.IsContinuing())
+    return {};
+
+  next();  // Consume the peek
+  return body_stmt();
+}
+
+// const_literal
+//   : INT_LITERAL
+//   | UINT_LITERAL
+//   | FLOAT_LITERAL
+//   | TRUE
+//   | FALSE
+std::unique_ptr<ast::Literal> ParserImpl::const_literal() {
+  auto t = peek();
+  if (t.IsTrue()) {
+    next();  // Consume the peek
+    return std::make_unique<ast::BoolLiteral>(true);
+  }
+  if (t.IsFalse()) {
+    next();  // Consume the peek
+    return std::make_unique<ast::BoolLiteral>(false);
+  }
+  if (t.IsIntLiteral()) {
+    next();  // Consume the peek
+    return std::make_unique<ast::IntLiteral>(t.to_i32());
+  }
+  if (t.IsUintLiteral()) {
+    next();  // Consume the peek
+    return std::make_unique<ast::UintLiteral>(t.to_u32());
+  }
+  if (t.IsFloatLiteral()) {
+    next();  // Consume the peek
+    return std::make_unique<ast::FloatLiteral>(t.to_f32());
+  }
+  return nullptr;
+}
+
+// const_expr
+//   : type_decl PAREN_LEFT (const_expr COMMA)? const_expr PAREN_RIGHT
+//   | const_literal
+std::unique_ptr<ast::InitializerExpression> ParserImpl::const_expr() {
+  auto t = peek();
+  auto source = t.source();
+
+  auto type = type_decl();
+  if (type != nullptr) {
+    t = next();
+    if (!t.IsParenLeft()) {
+      set_error(t, "missing ( for type initializer");
+      return nullptr;
+    }
+
+    std::vector<std::unique_ptr<ast::Expression>> params;
+    auto param = const_expr();
+    if (has_error())
+      return nullptr;
+    if (param == nullptr) {
+      set_error(peek(), "unable to parse constant expression");
+      return nullptr;
+    }
+    params.push_back(std::move(param));
+    for (;;) {
+      t = peek();
+      if (!t.IsComma())
+        break;
+
+      next();  // Consume the peek
+
+      param = const_expr();
+      if (has_error())
+        return nullptr;
+      if (param == nullptr) {
+        set_error(peek(), "unable to parse constant expression");
+        return nullptr;
+      }
+      params.push_back(std::move(param));
+    }
+
+    t = next();
+    if (!t.IsParenRight()) {
+      set_error(t, "missing ) for type initializer");
+      return nullptr;
+    }
+    return std::make_unique<ast::TypeInitializerExpression>(source, type,
+                                                            std::move(params));
+  }
+
+  auto lit = const_literal();
+  if (has_error())
+    return nullptr;
+  if (lit == nullptr) {
+    set_error(peek(), "unable to parse const literal");
+    return nullptr;
+  }
+  return std::make_unique<ast::ConstInitializerExpression>(source,
+                                                           std::move(lit));
+}
+
+// primary_expression
+//   : (IDENT NAMESPACE)* IDENT
+//   | type_decl PAREN_LEFT argument_expression_list PAREN_RIGHT
+//   | const_literal
+//   | paren_rhs_stmt
+//   | CAST LESS_THAN type_decl GREATER_THAN paren_rhs_stmt
+//   | AS LESS_THAN type_decl GREATER_THAN paren_rhs_stmt
+std::unique_ptr<ast::Expression> ParserImpl::primary_expression() {
+  auto t = peek();
+  auto source = t.source();
+
+  auto lit = const_literal();
+  if (has_error())
+    return nullptr;
+  if (lit != nullptr) {
+    return std::make_unique<ast::ConstInitializerExpression>(source,
+                                                             std::move(lit));
+  }
+
+  t = peek();
+  if (t.IsParenLeft()) {
+    auto paren = paren_rhs_stmt();
+    if (has_error())
+      return nullptr;
+
+    return paren;
+  }
+
+  if (t.IsCast() || t.IsAs()) {
+    auto src = t;
+
+    next();  // Consume the peek
+
+    t = next();
+    if (!t.IsLessThan()) {
+      set_error(t, "missing < for " + src.to_name() + " expression");
+      return nullptr;
+    }
+
+    auto type = type_decl();
+    if (has_error())
+      return nullptr;
+    if (type == nullptr) {
+      set_error(peek(), "missing type for " + src.to_name() + " expression");
+      return nullptr;
+    }
+
+    t = next();
+    if (!t.IsGreaterThan()) {
+      set_error(t, "missing > for " + src.to_name() + " expression");
+      return nullptr;
+    }
+
+    auto params = paren_rhs_stmt();
+    if (has_error())
+      return nullptr;
+    if (params == nullptr) {
+      set_error(peek(), "unable to parse parameters");
+      return nullptr;
+    }
+
+    if (src.IsCast()) {
+      return std::make_unique<ast::CastExpression>(source, type,
+                                                   std::move(params));
+    } else {
+      return std::make_unique<ast::AsExpression>(source, type,
+                                                 std::move(params));
+    }
+
+  } else if (t.IsIdentifier()) {
+    next();  // Consume the peek
+
+    std::vector<std::string> ident;
+    ident.push_back(t.to_str());
+    for (;;) {
+      t = peek();
+      if (!t.IsNamespace())
+        break;
+
+      next();  // Consume the peek
+      t = next();
+      if (!t.IsIdentifier()) {
+        set_error(t, "identifier expected");
+        return nullptr;
+      }
+
+      ident.push_back(t.to_str());
+    }
+    return std::make_unique<ast::IdentifierExpression>(source,
+                                                       std::move(ident));
+  }
+
+  auto type = type_decl();
+  if (has_error())
+    return nullptr;
+  if (type != nullptr) {
+    t = next();
+    if (!t.IsParenLeft()) {
+      set_error(t, "missing ( for type initializer");
+      return nullptr;
+    }
+
+    auto params = argument_expression_list();
+    if (has_error())
+      return nullptr;
+
+    t = next();
+    if (!t.IsParenRight()) {
+      set_error(t, "missing ) for type initializer");
+      return nullptr;
+    }
+    return std::make_unique<ast::TypeInitializerExpression>(source, type,
+                                                            std::move(params));
+  }
+  return nullptr;
+}
+
+// argument_expression_list
+//   : (logical_or_expression COMMA)* logical_or_expression
+std::vector<std::unique_ptr<ast::Expression>>
+ParserImpl::argument_expression_list() {
+  auto arg = logical_or_expression();
+  if (has_error())
+    return {};
+  if (arg == nullptr) {
+    set_error(peek(), "unable to parse argument expression");
+    return {};
+  }
+
+  std::vector<std::unique_ptr<ast::Expression>> ret;
+  ret.push_back(std::move(arg));
+
+  for (;;) {
+    auto t = peek();
+    if (!t.IsComma())
+      break;
+
+    next();  // Consume the peek
+
+    arg = logical_or_expression();
+    if (has_error())
+      return {};
+    if (arg == nullptr) {
+      set_error(peek(), "unable to parse argument expression after comma");
+      return {};
+    }
+    ret.push_back(std::move(arg));
+  }
+  return ret;
+}
+
+// postfix_expr
+//   :
+//   | BRACE_LEFT logical_or_expression BRACE_RIGHT postfix_expr
+//   | PAREN_LEFT argument_expression_list* PAREN_RIGHT postfix_expr
+//   | PERIOD IDENTIFIER postfix_expr
+std::unique_ptr<ast::Expression> ParserImpl::postfix_expr(
+    std::unique_ptr<ast::Expression> prefix) {
+  std::unique_ptr<ast::Expression> expr = nullptr;
+
+  auto t = peek();
+  auto source = t.source();
+  if (t.IsBraceLeft()) {
+    next();  // Consume the peek
+
+    auto param = logical_or_expression();
+    if (has_error())
+      return nullptr;
+    if (param == nullptr) {
+      set_error(peek(), "unable to parse expression inside []");
+      return nullptr;
+    }
+
+    t = next();
+    if (!t.IsBraceRight()) {
+      set_error(t, "missing ] for array accessor");
+      return nullptr;
+    }
+    expr = std::make_unique<ast::ArrayAccessorExpression>(
+        source, std::move(prefix), std::move(param));
+
+  } else if (t.IsParenLeft()) {
+    next();  // Consume the peek
+
+    t = peek();
+    std::vector<std::unique_ptr<ast::Expression>> params;
+    if (!t.IsParenRight() && !t.IsEof()) {
+      params = argument_expression_list();
+      if (has_error())
+        return nullptr;
+    }
+
+    t = next();
+    if (!t.IsParenRight()) {
+      set_error(t, "missing ) for call expression");
+      return nullptr;
+    }
+    expr = std::make_unique<ast::CallExpression>(source, std::move(prefix),
+                                                 std::move(params));
+  } else if (t.IsPeriod()) {
+    next();  // Consume the peek
+
+    t = next();
+    if (!t.IsIdentifier()) {
+      set_error(t, "missing identifier for member accessor");
+      return nullptr;
+    }
+
+    expr = std::make_unique<ast::MemberAccessorExpression>(
+        source, std::move(prefix),
+        std::make_unique<ast::IdentifierExpression>(t.source(), t.to_str()));
+  } else {
+    return prefix;
+  }
+  return postfix_expr(std::move(expr));
+}
+
+// postfix_expression
+//   : primary_expression postfix_expr
+std::unique_ptr<ast::Expression> ParserImpl::postfix_expression() {
+  auto prefix = primary_expression();
+  if (has_error())
+    return nullptr;
+  if (prefix == nullptr)
+    return nullptr;
+
+  return postfix_expr(std::move(prefix));
+}
+
+// unary_expression
+//   : postfix_expression
+//   | MINUS unary_expression
+//   | BANG unary_expression
+//   | ANY PAREN_LEFT IDENT PAREN_RIGHT
+//   | ALL PAREN_LEFT IDENT PAREN_RIGHT
+//   | IS_NAN PAREN_LEFT IDENT PAREN_RIGHT
+//   | IS_INF PAREN_LEFT IDENT PAREN_RIGHT
+//   | IS_FINITE PAREN_LEFT IDENT PAREN_RIGHT
+//   | IS_NORMAL PAREN_LEFT IDENT PAREN_RIGHT
+//   | DOT PAREN_LEFT IDENT COMMA IDENT PAREN_RIGHT
+//   | OUTER_PRODUCT PAREN_LEFT IDENT COMMA IDENT PAREN_RIGHT
+//   | DPDX (LESS_THAN derivative_modifier GREATER_THAN)?
+//           PAREN_LEFT IDENT PAREN_RIGHT
+//   | DPDY (LESS_THAN derivative_modifier GREATER_THAN)?
+//           PAREN_LEFT IDENT PAREN_RIGHT
+//   | FWIDTH (LESS_THAN derivative_modifier GREATER_THAN)?
+//           PAREN_LEFT IDENT PAREN_RIGHT
+// # | unord_greater_than_equal(a, b)
+// # | unord_greater_than(a, b)
+// # | unord_less_than_equal(a, b)
+// # | unord_less_than(a, b)
+// # | unord_not_equal(a, b)
+// # | unord_equal(a, b)
+// # | signed_greater_than_equal(a, b)
+// # | signed_greater_than(a, b)
+// # | signed_less_than_equal(a, b)
+// # | signed_less_than(a, b)
+std::unique_ptr<ast::Expression> ParserImpl::unary_expression() {
+  auto t = peek();
+  auto source = t.source();
+  if (t.IsMinus() || t.IsBang()) {
+    auto name = t.to_name();
+
+    next();  // Consume the peek
+
+    auto op = ast::UnaryOp::kNegation;
+    if (t.IsBang())
+      op = ast::UnaryOp::kNot;
+
+    auto expr = unary_expression();
+    if (has_error())
+      return nullptr;
+    if (expr == nullptr) {
+      set_error(peek(),
+                "unable to parse right side of " + name + " expression");
+      return nullptr;
+    }
+    return std::make_unique<ast::UnaryOpExpression>(source, op,
+                                                    std::move(expr));
+  }
+  if (t.IsAny() || t.IsAll() || t.IsIsNan() || t.IsIsInf() || t.IsIsFinite() ||
+      t.IsIsNormal()) {
+    next();  // Consume the peek
+
+    auto op = ast::UnaryMethod::kAny;
+    if (t.IsAll())
+      op = ast::UnaryMethod::kAll;
+    else if (t.IsIsNan())
+      op = ast::UnaryMethod::kIsNan;
+    else if (t.IsIsInf())
+      op = ast::UnaryMethod::kIsInf;
+    else if (t.IsIsFinite())
+      op = ast::UnaryMethod::kIsFinite;
+    else if (t.IsIsNormal())
+      op = ast::UnaryMethod::kIsNormal;
+
+    t = next();
+    if (!t.IsParenLeft()) {
+      set_error(t, "missing ( for method call");
+      return nullptr;
+    }
+
+    t = next();
+    if (!t.IsIdentifier()) {
+      set_error(t, "missing identifier for method call");
+      return nullptr;
+    }
+    std::vector<std::unique_ptr<ast::Expression>> ident;
+    ident.push_back(
+        std::make_unique<ast::IdentifierExpression>(source, t.to_str()));
+
+    t = next();
+    if (!t.IsParenRight()) {
+      set_error(t, "missing ) for method call");
+      return nullptr;
+    }
+    return std::make_unique<ast::UnaryMethodExpression>(source, op,
+                                                        std::move(ident));
+  }
+  if (t.IsDot() || t.IsOuterProduct()) {
+    next();  // Consume the peek
+
+    auto op = ast::UnaryMethod::kDot;
+    if (t.IsOuterProduct())
+      op = ast::UnaryMethod::kOuterProduct;
+
+    t = next();
+    if (!t.IsParenLeft()) {
+      set_error(t, "missing ( for method call");
+      return nullptr;
+    }
+
+    t = next();
+    if (!t.IsIdentifier()) {
+      set_error(t, "missing identifier for method call");
+      return nullptr;
+    }
+    std::vector<std::unique_ptr<ast::Expression>> ident;
+    ident.push_back(
+        std::make_unique<ast::IdentifierExpression>(source, t.to_str()));
+
+    t = next();
+    if (!t.IsComma()) {
+      set_error(t, "missing , for method call");
+      return nullptr;
+    }
+
+    t = next();
+    if (!t.IsIdentifier()) {
+      set_error(t, "missing identifier for method call");
+      return nullptr;
+    }
+    ident.push_back(
+        std::make_unique<ast::IdentifierExpression>(source, t.to_str()));
+
+    t = next();
+    if (!t.IsParenRight()) {
+      set_error(t, "missing ) for method call");
+      return nullptr;
+    }
+
+    return std::make_unique<ast::UnaryMethodExpression>(source, op,
+                                                        std::move(ident));
+  }
+  if (t.IsDpdx() || t.IsDpdy() || t.IsFwidth()) {
+    next();  // Consume the peek
+
+    auto op = ast::UnaryDerivative::kDpdx;
+    if (t.IsDpdy())
+      op = ast::UnaryDerivative::kDpdy;
+    else if (t.IsFwidth())
+      op = ast::UnaryDerivative::kFwidth;
+
+    t = next();
+    auto mod = ast::DerivativeModifier::kNone;
+    if (t.IsLessThan()) {
+      mod = derivative_modifier();
+      if (has_error())
+        return nullptr;
+      if (mod == ast::DerivativeModifier::kNone) {
+        set_error(peek(), "unable to parse derivative modifier");
+        return nullptr;
+      }
+
+      t = next();
+      if (!t.IsGreaterThan()) {
+        set_error(t, "missing > for derivative modifier");
+        return nullptr;
+      }
+      t = next();
+    }
+    if (!t.IsParenLeft()) {
+      set_error(t, "missing ( for derivative method");
+      return nullptr;
+    }
+
+    t = next();
+    if (!t.IsIdentifier()) {
+      set_error(t, "missing identifier for derivative method");
+      return nullptr;
+    }
+    auto ident =
+        std::make_unique<ast::IdentifierExpression>(source, t.to_str());
+
+    t = next();
+    if (!t.IsParenRight()) {
+      set_error(t, "missing ) for derivative method");
+      return nullptr;
+    }
+
+    return std::make_unique<ast::UnaryDerivativeExpression>(source, op, mod,
+                                                            std::move(ident));
+  }
+  return postfix_expression();
+}
+
+// derivative_modifier
+//   : FINE
+//   | COARSE
+ast::DerivativeModifier ParserImpl::derivative_modifier() {
+  auto t = peek();
+  if (t.IsFine()) {
+    next();  // Consume the peek
+    return ast::DerivativeModifier::kFine;
+  }
+  if (t.IsCoarse()) {
+    next();  // Consume the peek
+    return ast::DerivativeModifier::kCoarse;
+  }
+  return ast::DerivativeModifier::kNone;
+}
+
+// multiplicative_expr
+//   :
+//   | STAR unary_expression multiplicative_expr
+//   | FORWARD_SLASH unary_expression multiplicative_expr
+//   | MODULO unary_expression multiplicative_expr
+std::unique_ptr<ast::Expression> ParserImpl::multiplicative_expr(
+    std::unique_ptr<ast::Expression> lhs) {
+  auto t = peek();
+
+  ast::Relation relation = ast::Relation::kNone;
+  if (t.IsStar())
+    relation = ast::Relation::kMultiply;
+  else if (t.IsForwardSlash())
+    relation = ast::Relation::kDivide;
+  else if (t.IsMod())
+    relation = ast::Relation::kModulo;
+  else
+    return lhs;
+
+  auto source = t.source();
+  auto name = t.to_name();
+  next();  // Consume the peek
+
+  auto rhs = unary_expression();
+  if (has_error())
+    return nullptr;
+  if (rhs == nullptr) {
+    set_error(peek(), "unable to parse right side of " + name + " expression");
+    return nullptr;
+  }
+  return multiplicative_expr(std::make_unique<ast::RelationalExpression>(
+      source, relation, std::move(lhs), std::move(rhs)));
+}
+
+// multiplicative_expression
+//   : unary_expression multiplicative_expr
+std::unique_ptr<ast::Expression> ParserImpl::multiplicative_expression() {
+  auto lhs = unary_expression();
+  if (has_error())
+    return nullptr;
+  if (lhs == nullptr)
+    return nullptr;
+
+  return multiplicative_expr(std::move(lhs));
+}
+
+// additive_expr
+//   :
+//   | PLUS multiplicative_expression additive_expr
+//   | MINUS multiplicative_expression additive_expr
+std::unique_ptr<ast::Expression> ParserImpl::additive_expr(
+    std::unique_ptr<ast::Expression> lhs) {
+  auto t = peek();
+
+  ast::Relation relation = ast::Relation::kNone;
+  if (t.IsPlus())
+    relation = ast::Relation::kAdd;
+  else if (t.IsMinus())
+    relation = ast::Relation::kSubtract;
+  else
+    return lhs;
+
+  auto source = t.source();
+  next();  // Consume the peek
+
+  auto rhs = multiplicative_expression();
+  if (has_error())
+    return nullptr;
+  if (rhs == nullptr) {
+    set_error(peek(), "unable to parse right side of + expression");
+    return nullptr;
+  }
+  return additive_expr(std::make_unique<ast::RelationalExpression>(
+      source, relation, std::move(lhs), std::move(rhs)));
+}
+
+// additive_expression
+//   : multiplicative_expression additive_expr
+std::unique_ptr<ast::Expression> ParserImpl::additive_expression() {
+  auto lhs = multiplicative_expression();
+  if (has_error())
+    return nullptr;
+  if (lhs == nullptr)
+    return nullptr;
+
+  return additive_expr(std::move(lhs));
+}
+
+// shift_expr
+//   :
+//   | LESS_THAN LESS_THAN additive_expression shift_expr
+//   | GREATER_THAN GREATER_THAN additive_expression shift_expr
+//   | GREATER_THAN GREATER_THAN GREATER_THAN additive_expression shift_expr
+std::unique_ptr<ast::Expression> ParserImpl::shift_expr(
+    std::unique_ptr<ast::Expression> lhs) {
+  auto t = peek();
+  auto source = t.source();
+  auto t2 = peek(1);
+  auto t3 = peek(2);
+
+  auto name = "";
+  ast::Relation relation = ast::Relation::kNone;
+  if (t.IsLessThan() && t2.IsLessThan()) {
+    next();  // Consume the t peek
+    next();  // Consume the t2 peek
+    relation = ast::Relation::kShiftLeft;
+    name = "<<";
+  } else if (t.IsGreaterThan() && t2.IsGreaterThan() && t3.IsGreaterThan()) {
+    next();  // Consume the t peek
+    next();  // Consume the t2 peek
+    next();  // Consume the t3 peek
+    relation = ast::Relation::kShiftRightArith;
+    name = ">>>";
+  } else if (t.IsGreaterThan() && t2.IsGreaterThan()) {
+    next();  // Consume the t peek
+    next();  // Consume the t2 peek
+    relation = ast::Relation::kShiftRight;
+    name = ">>";
+  } else {
+    return lhs;
+  }
+
+  auto rhs = additive_expression();
+  if (has_error())
+    return nullptr;
+  if (rhs == nullptr) {
+    set_error(peek(), std::string("unable to parse right side of ") + name +
+                          " expression");
+    return nullptr;
+  }
+  return shift_expr(std::make_unique<ast::RelationalExpression>(
+      source, relation, std::move(lhs), std::move(rhs)));
+}
+
+// shift_expression
+//   : additive_expression shift_expr
+std::unique_ptr<ast::Expression> ParserImpl::shift_expression() {
+  auto lhs = additive_expression();
+  if (has_error())
+    return nullptr;
+  if (lhs == nullptr)
+    return nullptr;
+
+  return shift_expr(std::move(lhs));
+}
+
+// relational_expr
+//   :
+//   | LESS_THAN shift_expression relational_expr
+//   | GREATER_THAN shift_expression relational_expr
+//   | LESS_THAN_EQUAL shift_expression relational_expr
+//   | GREATER_THAN_EQUAL shift_expression relational_expr
+std::unique_ptr<ast::Expression> ParserImpl::relational_expr(
+    std::unique_ptr<ast::Expression> lhs) {
+  auto t = peek();
+  ast::Relation relation = ast::Relation::kNone;
+  if (t.IsLessThan())
+    relation = ast::Relation::kLessThan;
+  else if (t.IsGreaterThan())
+    relation = ast::Relation::kGreaterThan;
+  else if (t.IsLessThanEqual())
+    relation = ast::Relation::kLessThanEqual;
+  else if (t.IsGreaterThanEqual())
+    relation = ast::Relation::kGreaterThanEqual;
+  else
+    return lhs;
+
+  auto source = t.source();
+  auto name = t.to_name();
+  next();  // Consume the peek
+
+  auto rhs = shift_expression();
+  if (has_error())
+    return nullptr;
+  if (rhs == nullptr) {
+    set_error(peek(), "unable to parse right side of " + name + " expression");
+    return nullptr;
+  }
+  return relational_expr(std::make_unique<ast::RelationalExpression>(
+      source, relation, std::move(lhs), std::move(rhs)));
+}
+
+// relational_expression
+//   : shift_expression relational_expr
+std::unique_ptr<ast::Expression> ParserImpl::relational_expression() {
+  auto lhs = shift_expression();
+  if (has_error())
+    return nullptr;
+  if (lhs == nullptr)
+    return nullptr;
+
+  return relational_expr(std::move(lhs));
+}
+
+// equality_expr
+//   :
+//   | EQUAL_EQUAL relational_expression equality_expr
+//   | NOT_EQUAL relational_expression equality_expr
+std::unique_ptr<ast::Expression> ParserImpl::equality_expr(
+    std::unique_ptr<ast::Expression> lhs) {
+  auto t = peek();
+  ast::Relation relation = ast::Relation::kNone;
+  if (t.IsEqualEqual())
+    relation = ast::Relation::kEqual;
+  else if (t.IsNotEqual())
+    relation = ast::Relation::kNotEqual;
+  else
+    return lhs;
+
+  auto source = t.source();
+  auto name = t.to_name();
+  next();  // Consume the peek
+
+  auto rhs = relational_expression();
+  if (has_error())
+    return nullptr;
+  if (rhs == nullptr) {
+    set_error(peek(), "unable to parse right side of " + name + " expression");
+    return nullptr;
+  }
+  return equality_expr(std::make_unique<ast::RelationalExpression>(
+      source, relation, std::move(lhs), std::move(rhs)));
+}
+
+// equality_expression
+//   : relational_expression equality_expr
+std::unique_ptr<ast::Expression> ParserImpl::equality_expression() {
+  auto lhs = relational_expression();
+  if (has_error())
+    return nullptr;
+  if (lhs == nullptr)
+    return nullptr;
+
+  return equality_expr(std::move(lhs));
+}
+
+// and_expr
+//   :
+//   | AND equality_expression and_expr
+std::unique_ptr<ast::Expression> ParserImpl::and_expr(
+    std::unique_ptr<ast::Expression> lhs) {
+  auto t = peek();
+  if (!t.IsAnd())
+    return lhs;
+
+  auto source = t.source();
+  next();  // Consume the peek
+
+  auto rhs = equality_expression();
+  if (has_error())
+    return nullptr;
+  if (rhs == nullptr) {
+    set_error(peek(), "unable to parse right side of & expression");
+    return nullptr;
+  }
+  return and_expr(std::make_unique<ast::RelationalExpression>(
+      source, ast::Relation::kAnd, std::move(lhs), std::move(rhs)));
+}
+
+// and_expression
+//   : equality_expression and_expr
+std::unique_ptr<ast::Expression> ParserImpl::and_expression() {
+  auto lhs = equality_expression();
+  if (has_error())
+    return nullptr;
+  if (lhs == nullptr)
+    return nullptr;
+
+  return and_expr(std::move(lhs));
+}
+
+// exclusive_or_expr
+//   :
+//   | XOR and_expression exclusive_or_expr
+std::unique_ptr<ast::Expression> ParserImpl::exclusive_or_expr(
+    std::unique_ptr<ast::Expression> lhs) {
+  auto t = peek();
+  if (!t.IsXor())
+    return lhs;
+
+  auto source = t.source();
+  next();  // Consume the peek
+
+  auto rhs = and_expression();
+  if (has_error())
+    return nullptr;
+  if (rhs == nullptr) {
+    set_error(peek(), "unable to parse right side of ^ expression");
+    return nullptr;
+  }
+  return exclusive_or_expr(std::make_unique<ast::RelationalExpression>(
+      source, ast::Relation::kXor, std::move(lhs), std::move(rhs)));
+}
+
+// exclusive_or_expression
+//   : and_expression exclusive_or_expr
+std::unique_ptr<ast::Expression> ParserImpl::exclusive_or_expression() {
+  auto lhs = and_expression();
+  if (has_error())
+    return nullptr;
+  if (lhs == nullptr)
+    return nullptr;
+
+  return exclusive_or_expr(std::move(lhs));
+}
+
+// inclusive_or_expr
+//   :
+//   | OR exclusive_or_expression inclusive_or_expr
+std::unique_ptr<ast::Expression> ParserImpl::inclusive_or_expr(
+    std::unique_ptr<ast::Expression> lhs) {
+  auto t = peek();
+  if (!t.IsOr())
+    return lhs;
+
+  auto source = t.source();
+  next();  // Consume the peek
+
+  auto rhs = exclusive_or_expression();
+  if (has_error())
+    return nullptr;
+  if (rhs == nullptr) {
+    set_error(peek(), "unable to parse right side of | expression");
+    return nullptr;
+  }
+  return inclusive_or_expr(std::make_unique<ast::RelationalExpression>(
+      source, ast::Relation::kOr, std::move(lhs), std::move(rhs)));
+}
+
+// inclusive_or_expression
+//   : exclusive_or_expression inclusive_or_expr
+std::unique_ptr<ast::Expression> ParserImpl::inclusive_or_expression() {
+  auto lhs = exclusive_or_expression();
+  if (has_error())
+    return nullptr;
+  if (lhs == nullptr)
+    return nullptr;
+
+  return inclusive_or_expr(std::move(lhs));
+}
+
+// logical_and_expr
+//   :
+//   | AND_AND inclusive_or_expression logical_and_expr
+std::unique_ptr<ast::Expression> ParserImpl::logical_and_expr(
+    std::unique_ptr<ast::Expression> lhs) {
+  auto t = peek();
+  if (!t.IsAndAnd())
+    return lhs;
+
+  auto source = t.source();
+  next();  // Consume the peek
+
+  auto rhs = inclusive_or_expression();
+  if (has_error())
+    return nullptr;
+  if (rhs == nullptr) {
+    set_error(peek(), "unable to parse right side of && expression");
+    return nullptr;
+  }
+  return logical_and_expr(std::make_unique<ast::RelationalExpression>(
+      source, ast::Relation::kLogicalAnd, std::move(lhs), std::move(rhs)));
+}
+
+// logical_and_expression
+//   : inclusive_or_expression logical_and_expr
+std::unique_ptr<ast::Expression> ParserImpl::logical_and_expression() {
+  auto lhs = inclusive_or_expression();
+  if (has_error())
+    return nullptr;
+  if (lhs == nullptr)
+    return nullptr;
+
+  return logical_and_expr(std::move(lhs));
+}
+
+// logical_or_expr
+//   :
+//   | OR_OR logical_and_expression logical_or_expr
+std::unique_ptr<ast::Expression> ParserImpl::logical_or_expr(
+    std::unique_ptr<ast::Expression> lhs) {
+  auto t = peek();
+  if (!t.IsOrOr())
+    return lhs;
+
+  auto source = t.source();
+  next();  // Consume the peek
+
+  auto rhs = logical_and_expression();
+  if (has_error())
+    return nullptr;
+  if (rhs == nullptr) {
+    set_error(peek(), "unable to parse right side of || expression");
+    return nullptr;
+  }
+  return logical_or_expr(std::make_unique<ast::RelationalExpression>(
+      source, ast::Relation::kLogicalOr, std::move(lhs), std::move(rhs)));
+}
+
+// logical_or_expression
+//   : logical_and_expression logical_or_expr
+std::unique_ptr<ast::Expression> ParserImpl::logical_or_expression() {
+  auto lhs = logical_and_expression();
+  if (has_error())
+    return nullptr;
+  if (lhs == nullptr)
+    return nullptr;
+
+  return logical_or_expr(std::move(lhs));
+}
+
+// assignment_stmt
+//   : unary_expression EQUAL logical_or_expression
+std::unique_ptr<ast::AssignmentStatement> ParserImpl::assignment_stmt() {
+  auto t = peek();
+  auto source = t.source();
+
+  auto lhs = unary_expression();
+  if (has_error())
+    return nullptr;
+  if (lhs == nullptr)
+    return nullptr;
+
+  t = next();
+  if (!t.IsEqual()) {
+    set_error(t, "missing = for assignment");
+    return nullptr;
+  }
+
+  auto rhs = logical_or_expression();
+  if (has_error())
+    return nullptr;
+  if (rhs == nullptr) {
+    set_error(peek(), "unable to parse right side of assignment");
+    return nullptr;
+  }
+
+  return std::make_unique<ast::AssignmentStatement>(source, std::move(lhs),
+                                                    std::move(rhs));
+}
+
+}  // namespace wgsl
+}  // namespace reader
+}  // namespace tint