dawn-cmake/src/reader/wgsl/parser_impl.cc
dan sinclair 1d69915155 Rename case statement conditions to selectors.
The name conditions isn't quite correct for the case statement. This CL
updates the code to use selectors instead of conditions.

Change-Id: I98b8050b11e2328f97e4443469572ab47d7c1555
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/22520
Reviewed-by: David Neto <dneto@google.com>
2020-06-02 17:18:56 +00:00

2835 lines
67 KiB
C++

// 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 <vector>
#include "src/ast/array_accessor_expression.h"
#include "src/ast/as_expression.h"
#include "src/ast/binary_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/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/return_statement.h"
#include "src/ast/scalar_constructor_expression.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_constructor_expression.h"
#include "src/ast/uint_literal.h"
#include "src/ast/unary_op.h"
#include "src/ast/unary_op_expression.h"
#include "src/ast/variable_decl_statement.h"
#include "src/reader/wgsl/lexer.h"
#include "src/type_manager.h"
namespace tint {
namespace reader {
namespace wgsl {
namespace {
ast::Builtin ident_to_builtin(const std::string& str) {
if (str == "position") {
return ast::Builtin::kPosition;
}
if (str == "vertex_idx") {
return ast::Builtin::kVertexIdx;
}
if (str == "instance_idx") {
return ast::Builtin::kInstanceIdx;
}
if (str == "front_facing") {
return ast::Builtin::kFrontFacing;
}
if (str == "frag_coord") {
return ast::Builtin::kFragCoord;
}
if (str == "frag_depth") {
return ast::Builtin::kFragDepth;
}
if (str == "num_workgroups") {
return ast::Builtin::kNumWorkgroups;
}
if (str == "workgroup_size") {
return ast::Builtin::kWorkgroupSize;
}
if (str == "local_invocation_id") {
return ast::Builtin::kLocalInvocationId;
}
if (str == "local_invocation_idx") {
return ast::Builtin::kLocalInvocationIdx;
}
if (str == "global_invocation_id") {
return ast::Builtin::kGlobalInvocationId;
}
return ast::Builtin::kNone;
}
} // namespace
ParserImpl::ParserImpl(Context* ctx, const std::string& input)
: ctx_(*ctx), 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>(std::move(var));
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_constructor(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.empty() || 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 scalar constructor");
return nullptr;
}
var->set_constructor(std::move(init));
return var;
}
// variable_decoration_list
// : ATTR_LEFT (variable_decoration COMMA)* variable_decoration ATTR_RIGHT
ast::VariableDecorationList ParserImpl::variable_decoration_list() {
ast::VariableDecorationList 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 IDENT
// | 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
t = next();
if (!t.IsIdentifier() || t.to_str().empty()) {
set_error(t, "expected identifier for builtin");
return {};
}
ast::Builtin builtin = ident_to_builtin(t.to_str());
if (builtin == ast::Builtin::kNone) {
set_error(t, "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;
}
// 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.empty() || 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* 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 = ctx_.type_mgr().Get(std::move(str));
}
if (type == nullptr) {
set_error(peek(), "invalid type for alias");
return nullptr;
}
auto* alias =
ctx_.type_mgr().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
// | VEC4 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 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 ctx_.type_mgr().Get(std::make_unique<ast::type::BoolType>());
}
if (t.IsF32()) {
next(); // Consume the peek
return ctx_.type_mgr().Get(std::make_unique<ast::type::F32Type>());
}
if (t.IsI32()) {
next(); // Consume the peek
return ctx_.type_mgr().Get(std::make_unique<ast::type::I32Type>());
}
if (t.IsU32()) {
next(); // Consume the peek
return ctx_.type_mgr().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 ctx_.type_mgr().Get(
std::make_unique<ast::type::PointerType>(subtype, sc));
}
ast::type::Type* ParserImpl::type_decl_vector(Token t) {
next(); // Consume the peek
uint32_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 ctx_.type_mgr().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();
uint32_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<uint32_t>(t.to_i32());
t = next();
}
if (!t.IsGreaterThan()) {
set_error(t, "missing > for array declaration");
return nullptr;
}
return ctx_.type_mgr().Get(
std::make_unique<ast::type::ArrayType>(subtype, size));
}
ast::type::Type* ParserImpl::type_decl_matrix(Token t) {
next(); // Consume the peek
uint32_t rows = 2;
uint32_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 ctx_.type_mgr().Get(
std::make_unique<ast::type::MatrixType>(subtype, rows, columns));
}
// storage_class
// : INPUT
// | OUTPUT
// | UNIFORM
// | WORKGROUP
// | UNIFORM_CONSTANT
// | STORAGE_BUFFER
// | IMAGE
// | 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.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.IsBraceLeft()) {
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
ast::StructMemberList ParserImpl::struct_body_decl() {
auto t = peek();
if (!t.IsBraceLeft())
return {};
next(); // Consume the peek
t = peek();
if (t.IsBraceRight())
return {};
ast::StructMemberList 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.IsBraceRight() || t.IsEof())
break;
}
t = next();
if (!t.IsBraceRight()) {
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.empty() || 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
ast::StructMemberDecorationList 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 {};
}
ast::StructMemberDecorationList 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>(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 t = peek();
if (t.IsVoid()) {
next(); // Consume the peek
return ctx_.type_mgr().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
ast::VariableList ParserImpl::param_list() {
auto t = peek();
auto source = t.source();
ast::VariableList ret;
std::string name;
ast::type::Type* type;
std::tie(name, type) = variable_ident_decl();
if (has_error())
return {};
if (name.empty() || 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.empty() || 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
ast::StatementList ParserImpl::body_stmt() {
auto t = peek();
if (!t.IsBraceLeft())
return {};
next(); // Consume the peek
auto stmts = statements();
if (has_error())
return {};
t = next();
if (!t.IsBraceRight()) {
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*
ast::StatementList ParserImpl::statements() {
ast::StatementList ret;
for (;;) {
auto stmt = statement();
if (has_error())
return {};
if (stmt == nullptr)
break;
ret.push_back(std::move(stmt));
}
return ret;
}
// statement
// : SEMICOLON
// | return_stmt SEMICOLON
// | if_stmt
// | unless_stmt
// | switch_stmt
// | loop_stmt
// | variable_stmt SEMICOLON
// | break_stmt SEMICOLON
// | continue_stmt SEMICOLON
// | KILL SEMICOLON
// | assignment_stmt SEMICOLON
std::unique_ptr<ast::Statement> ParserImpl::statement() {
auto t = peek();
if (t.IsSemicolon()) {
next(); // Consume the peek
return statement();
}
auto ret_stmt = return_stmt();
if (has_error())
return nullptr;
if (ret_stmt != nullptr) {
t = next();
if (!t.IsSemicolon()) {
set_error(t, "missing ;");
return nullptr;
}
return ret_stmt;
}
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 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);
}
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;
}
// return_stmt
// : RETURN logical_or_expression?
std::unique_ptr<ast::ReturnStatement> ParserImpl::return_stmt() {
auto t = peek();
if (!t.IsReturn())
return nullptr;
auto source = t.source();
next(); // Consume the peek
std::unique_ptr<ast::Expression> expr = nullptr;
t = peek();
if (!t.IsSemicolon()) {
expr = logical_or_expression();
if (has_error())
return nullptr;
}
return std::make_unique<ast::ReturnStatement>(source, std::move(expr));
}
// variable_stmt
// : variable_decl
// | variable_decl EQUAL logical_or_expression
// | CONST variable_ident_decl EQUAL logical_or_expression
std::unique_ptr<ast::VariableDeclStatement> 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.empty() || 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 constructor = logical_or_expression();
if (has_error())
return nullptr;
if (constructor == nullptr) {
set_error(peek(), "missing constructor 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_constructor(std::move(constructor));
return std::make_unique<ast::VariableDeclStatement>(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 constructor = logical_or_expression();
if (has_error())
return nullptr;
if (constructor == nullptr) {
set_error(peek(), "missing constructor for variable declaration");
return nullptr;
}
var->set_constructor(std::move(constructor));
}
return std::make_unique<ast::VariableDeclStatement>(source, std::move(var));
}
// if_stmt
// : IF paren_rhs_stmt body_stmt elseif_stmt? else_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.IsBraceLeft()) {
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) {
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?
ast::ElseStatementList ParserImpl::elseif_stmt() {
auto t = peek();
if (!t.IsElseIf())
return {};
ast::ElseStatementList 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.IsBraceLeft()) {
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.IsBraceLeft()) {
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));
}
// 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));
}
// 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.IsBraceLeft()) {
set_error(t, "missing { for switch statement");
return nullptr;
}
ast::CaseStatementList 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.IsBraceRight()) {
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 case_selectors 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 selectors = case_selectors();
if (has_error())
return nullptr;
if (selectors.empty()) {
set_error(peek(), "unable to parse case selectors");
return nullptr;
}
stmt->set_selectors(std::move(selectors));
}
t = next();
if (!t.IsColon()) {
set_error(t, "missing : for case statement");
return nullptr;
}
t = next();
if (!t.IsBraceLeft()) {
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.IsBraceRight()) {
set_error(t, "missing } for case statement");
return nullptr;
}
return stmt;
}
// case_selectors
// : const_literal (COMMA const_literal)*
ast::CaseSelectorList ParserImpl::case_selectors() {
ast::CaseSelectorList selectors;
for (;;) {
auto cond = const_literal();
if (has_error())
return {};
if (cond == nullptr)
break;
selectors.push_back(std::move(cond));
}
return selectors;
}
// case_body
// :
// | statement case_body
// | FALLTHROUGH SEMICOLON
ast::StatementList ParserImpl::case_body() {
ast::StatementList 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.IsBraceLeft()) {
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.IsBraceRight()) {
set_error(t, "missing } for loop");
return nullptr;
}
return std::make_unique<ast::LoopStatement>(source, std::move(body),
std::move(continuing));
}
// 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));
}
// continuing_stmt
// : CONTINUING body_stmt
ast::StatementList ParserImpl::continuing_stmt() {
auto t = peek();
if (!t.IsContinuing())
return {};
next(); // Consume the peek
return body_stmt();
}
// 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::ScalarConstructorExpression>(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 constructor");
return nullptr;
}
auto params = argument_expression_list();
if (has_error())
return nullptr;
t = next();
if (!t.IsParenRight()) {
set_error(t, "missing ) for type constructor");
return nullptr;
}
return std::make_unique<ast::TypeConstructorExpression>(source, type,
std::move(params));
}
return nullptr;
}
// 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.IsBracketLeft()) {
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.IsBracketRight()) {
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();
ast::ExpressionList 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));
}
// argument_expression_list
// : (logical_or_expression COMMA)* logical_or_expression
ast::ExpressionList 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 {};
}
ast::ExpressionList 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;
}
// unary_expression
// : postfix_expression
// | MINUS unary_expression
// | BANG unary_expression
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));
}
return postfix_expression();
}
// 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::BinaryOp op = ast::BinaryOp::kNone;
if (t.IsStar())
op = ast::BinaryOp::kMultiply;
else if (t.IsForwardSlash())
op = ast::BinaryOp::kDivide;
else if (t.IsMod())
op = ast::BinaryOp::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::BinaryExpression>(
source, op, 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::BinaryOp op = ast::BinaryOp::kNone;
if (t.IsPlus())
op = ast::BinaryOp::kAdd;
else if (t.IsMinus())
op = ast::BinaryOp::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::BinaryExpression>(
source, op, 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::BinaryOp op = ast::BinaryOp::kNone;
if (t.IsLessThan() && t2.IsLessThan()) {
next(); // Consume the t peek
next(); // Consume the t2 peek
op = ast::BinaryOp::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
op = ast::BinaryOp::kShiftRightArith;
name = ">>>";
} else if (t.IsGreaterThan() && t2.IsGreaterThan()) {
next(); // Consume the t peek
next(); // Consume the t2 peek
op = ast::BinaryOp::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::BinaryExpression>(
source, op, 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::BinaryOp op = ast::BinaryOp::kNone;
if (t.IsLessThan())
op = ast::BinaryOp::kLessThan;
else if (t.IsGreaterThan())
op = ast::BinaryOp::kGreaterThan;
else if (t.IsLessThanEqual())
op = ast::BinaryOp::kLessThanEqual;
else if (t.IsGreaterThanEqual())
op = ast::BinaryOp::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::BinaryExpression>(
source, op, 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::BinaryOp op = ast::BinaryOp::kNone;
if (t.IsEqualEqual())
op = ast::BinaryOp::kEqual;
else if (t.IsNotEqual())
op = ast::BinaryOp::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::BinaryExpression>(
source, op, 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::BinaryExpression>(
source, ast::BinaryOp::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::BinaryExpression>(
source, ast::BinaryOp::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::BinaryExpression>(
source, ast::BinaryOp::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::BinaryExpression>(
source, ast::BinaryOp::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::BinaryExpression>(
source, ast::BinaryOp::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));
}
// 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
auto* type = ctx_.type_mgr().Get(std::make_unique<ast::type::BoolType>());
if (!type) {
return nullptr;
}
return std::make_unique<ast::BoolLiteral>(type, true);
}
if (t.IsFalse()) {
next(); // Consume the peek
auto* type = ctx_.type_mgr().Get(std::make_unique<ast::type::BoolType>());
if (!type) {
return nullptr;
}
return std::make_unique<ast::BoolLiteral>(type, false);
}
if (t.IsIntLiteral()) {
next(); // Consume the peek
auto* type = ctx_.type_mgr().Get(std::make_unique<ast::type::I32Type>());
if (!type) {
return nullptr;
}
return std::make_unique<ast::IntLiteral>(type, t.to_i32());
}
if (t.IsUintLiteral()) {
next(); // Consume the peek
auto* type = ctx_.type_mgr().Get(std::make_unique<ast::type::U32Type>());
if (!type) {
return nullptr;
}
return std::make_unique<ast::UintLiteral>(type, t.to_u32());
}
if (t.IsFloatLiteral()) {
next(); // Consume the peek
auto* type = ctx_.type_mgr().Get(std::make_unique<ast::type::F32Type>());
if (!type) {
return nullptr;
}
return std::make_unique<ast::FloatLiteral>(type, t.to_f32());
}
return nullptr;
}
// const_expr
// : type_decl PAREN_LEFT (const_expr COMMA)? const_expr PAREN_RIGHT
// | const_literal
std::unique_ptr<ast::ConstructorExpression> 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 constructor");
return nullptr;
}
ast::ExpressionList 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 constructor");
return nullptr;
}
return std::make_unique<ast::TypeConstructorExpression>(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::ScalarConstructorExpression>(source,
std::move(lit));
}
} // namespace wgsl
} // namespace reader
} // namespace tint