2020-03-02 12:47:43 -08:00
|
|
|
// 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.
|
|
|
|
|
2022-02-21 07:19:07 -08:00
|
|
|
#include "src/tint/reader/wgsl/parser_impl.h"
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-04 14:01:12 -07:00
|
|
|
#include <limits>
|
|
|
|
|
2022-02-21 07:19:07 -08:00
|
|
|
#include "src/tint/ast/array.h"
|
|
|
|
#include "src/tint/ast/assignment_statement.h"
|
|
|
|
#include "src/tint/ast/bitcast_expression.h"
|
|
|
|
#include "src/tint/ast/break_statement.h"
|
|
|
|
#include "src/tint/ast/call_statement.h"
|
|
|
|
#include "src/tint/ast/continue_statement.h"
|
|
|
|
#include "src/tint/ast/discard_statement.h"
|
|
|
|
#include "src/tint/ast/external_texture.h"
|
|
|
|
#include "src/tint/ast/fallthrough_statement.h"
|
|
|
|
#include "src/tint/ast/id_attribute.h"
|
|
|
|
#include "src/tint/ast/if_statement.h"
|
2022-04-07 06:42:45 -07:00
|
|
|
#include "src/tint/ast/increment_decrement_statement.h"
|
2022-02-21 07:19:07 -08:00
|
|
|
#include "src/tint/ast/invariant_attribute.h"
|
|
|
|
#include "src/tint/ast/loop_statement.h"
|
|
|
|
#include "src/tint/ast/return_statement.h"
|
|
|
|
#include "src/tint/ast/stage_attribute.h"
|
|
|
|
#include "src/tint/ast/switch_statement.h"
|
|
|
|
#include "src/tint/ast/type_name.h"
|
|
|
|
#include "src/tint/ast/unary_op_expression.h"
|
|
|
|
#include "src/tint/ast/variable_decl_statement.h"
|
|
|
|
#include "src/tint/ast/vector.h"
|
|
|
|
#include "src/tint/ast/workgroup_attribute.h"
|
|
|
|
#include "src/tint/reader/wgsl/lexer.h"
|
2022-04-28 11:49:04 -07:00
|
|
|
#include "src/tint/sem/depth_texture.h"
|
|
|
|
#include "src/tint/sem/external_texture.h"
|
|
|
|
#include "src/tint/sem/multisampled_texture.h"
|
|
|
|
#include "src/tint/sem/sampled_texture.h"
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-04-07 10:55:04 -07:00
|
|
|
namespace tint::reader::wgsl {
|
2020-06-01 06:43:11 -07:00
|
|
|
namespace {
|
|
|
|
|
2022-08-17 11:57:49 -07:00
|
|
|
using Void = ParserImpl::Void;
|
|
|
|
|
|
|
|
/// An instance of Void that can be used to signal success for functions that return Expect<Void> or
|
|
|
|
/// Maybe<NoError>.
|
|
|
|
static constexpr Void kSuccess;
|
|
|
|
|
2020-11-09 11:39:34 -08:00
|
|
|
template <typename T>
|
|
|
|
using Expect = ParserImpl::Expect<T>;
|
|
|
|
|
2020-11-09 11:52:24 -08:00
|
|
|
template <typename T>
|
|
|
|
using Maybe = ParserImpl::Maybe<T>;
|
|
|
|
|
2021-06-21 12:36:26 -07:00
|
|
|
/// Controls the maximum number of times we'll call into the sync() and
|
|
|
|
/// unary_expression() functions from themselves. This is to guard against stack
|
|
|
|
/// overflow when there is an excessive number of blocks.
|
|
|
|
constexpr uint32_t kMaxParseDepth = 128;
|
2020-11-11 10:44:36 -08:00
|
|
|
|
|
|
|
/// The maximum number of tokens to look ahead to try and sync the
|
|
|
|
/// parser on error.
|
|
|
|
constexpr size_t const kMaxResynchronizeLookahead = 32;
|
2020-08-25 07:58:33 -07:00
|
|
|
|
2021-07-05 14:48:37 -07:00
|
|
|
// https://gpuweb.github.io/gpuweb/wgsl.html#reserved-keywords
|
2022-07-25 09:43:08 -07:00
|
|
|
bool is_reserved(const Token& t) {
|
2022-08-02 19:49:28 -07:00
|
|
|
return t == "CompileShader" || t == "ComputeShader" || t == "DomainShader" ||
|
|
|
|
t == "GeometryShader" || t == "Hullshader" || t == "NULL" || t == "Self" ||
|
|
|
|
t == "abstract" || t == "active" || t == "alignas" || t == "alignof" || t == "as" ||
|
|
|
|
t == "asm" || t == "asm_fragment" || t == "async" || t == "attribute" || t == "auto" ||
|
|
|
|
t == "await" || t == "become" || t == "binding_array" || t == "cast" || t == "catch" ||
|
|
|
|
t == "class" || t == "co_await" || t == "co_return" || t == "co_yield" ||
|
|
|
|
t == "coherent" || t == "column_major" || t == "common" || t == "compile" ||
|
|
|
|
t == "compile_fragment" || t == "concept" || t == "const_cast" || t == "consteval" ||
|
|
|
|
t == "constexpr" || t == "constinit" || t == "crate" || t == "debugger" ||
|
|
|
|
t == "decltype" || t == "delete" || t == "demote" || t == "demote_to_helper" ||
|
|
|
|
t == "do" || t == "dynamic_cast" || t == "enum" || t == "explicit" || t == "export" ||
|
|
|
|
t == "extends" || t == "extern" || t == "external" || t == "filter" || t == "final" ||
|
|
|
|
t == "finally" || t == "friend" || t == "from" || t == "fxgroup" || t == "get" ||
|
|
|
|
t == "goto" || t == "groupshared" || t == "handle" || t == "highp" || t == "impl" ||
|
|
|
|
t == "implements" || t == "import" || t == "inline" || t == "inout" ||
|
|
|
|
t == "instanceof" || t == "interface" || t == "invariant" || t == "layout" ||
|
|
|
|
t == "line" || t == "lineadj" || t == "lowp" || t == "macro" || t == "macro_rules" ||
|
|
|
|
t == "match" || t == "mediump" || t == "meta" || t == "mod" || t == "module" ||
|
|
|
|
t == "move" || t == "mut" || t == "mutable" || t == "namespace" || t == "new" ||
|
|
|
|
t == "nil" || t == "noexcept" || t == "noinline" || t == "nointerpolation" ||
|
|
|
|
t == "noperspective" || t == "null" || t == "nullptr" || t == "of" || t == "operator" ||
|
|
|
|
t == "package" || t == "packoffset" || t == "partition" || t == "pass" || t == "patch" ||
|
|
|
|
t == "pixelfragment" || t == "point" || t == "precise" || t == "precision" ||
|
|
|
|
t == "premerge" || t == "priv" || t == "protected" || t == "pub" || t == "public" ||
|
|
|
|
t == "readonly" || t == "ref" || t == "regardless" || t == "register" ||
|
|
|
|
t == "reinterpret_cast" || t == "requires" || t == "resource" || t == "restrict" ||
|
|
|
|
t == "self" || t == "set" || t == "shared" || t == "signed" || t == "sizeof" ||
|
2022-08-03 03:08:46 -07:00
|
|
|
t == "smooth" || t == "snorm" || t == "static" || t == "static_cast" || t == "std" ||
|
|
|
|
t == "subroutine" || t == "super" || t == "target" || t == "template" || t == "this" ||
|
|
|
|
t == "thread_local" || t == "throw" || t == "trait" || t == "try" || t == "typedef" ||
|
|
|
|
t == "typeid" || t == "typename" || t == "typeof" || t == "union" || t == "unless" ||
|
|
|
|
t == "unorm" || t == "unsafe" || t == "unsized" || t == "use" || t == "using" ||
|
|
|
|
t == "varying" || t == "virtual" || t == "volatile" || t == "wgsl" || t == "where" ||
|
|
|
|
t == "with" || t == "writeonly" || t == "yield";
|
2021-07-05 14:48:37 -07:00
|
|
|
}
|
|
|
|
|
2020-11-11 10:44:36 -08:00
|
|
|
/// Enter-exit counters for block token types.
|
|
|
|
/// Used by sync_to() to skip over closing block tokens that were opened during
|
|
|
|
/// the forward scan.
|
|
|
|
struct BlockCounters {
|
2022-05-01 07:40:55 -07:00
|
|
|
int brace = 0; // { }
|
|
|
|
int bracket = 0; // [ ]
|
|
|
|
int paren = 0; // ( )
|
|
|
|
|
|
|
|
/// @return the current enter-exit depth for the given block token type. If
|
|
|
|
/// `t` is not a block token type, then 0 is always returned.
|
|
|
|
int consume(const Token& t) {
|
2022-05-19 13:08:19 -07:00
|
|
|
if (t.Is(Token::Type::kBraceLeft)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return brace++;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (t.Is(Token::Type::kBraceRight)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return brace--;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (t.Is(Token::Type::kBracketLeft)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return bracket++;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (t.Is(Token::Type::kBracketRight)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return bracket--;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (t.Is(Token::Type::kParenLeft)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return paren++;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (t.Is(Token::Type::kParenRight)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return paren--;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-05-01 07:40:55 -07:00
|
|
|
return 0;
|
|
|
|
}
|
2020-11-11 10:44:36 -08:00
|
|
|
};
|
2020-06-01 06:43:11 -07:00
|
|
|
} // namespace
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2021-04-27 10:32:37 -07:00
|
|
|
/// RAII helper that combines a Source on construction with the last token's
|
|
|
|
/// source when implicitly converted to `Source`.
|
|
|
|
class ParserImpl::MultiTokenSource {
|
2022-05-01 07:40:55 -07:00
|
|
|
public:
|
|
|
|
/// Constructor that starts with Source at the current peek position
|
|
|
|
/// @param parser the parser
|
|
|
|
explicit MultiTokenSource(ParserImpl* parser)
|
|
|
|
: MultiTokenSource(parser, parser->peek().source().Begin()) {}
|
|
|
|
|
|
|
|
/// Constructor that starts with the input `start` Source
|
|
|
|
/// @param parser the parser
|
|
|
|
/// @param start the start source of the range
|
|
|
|
MultiTokenSource(ParserImpl* parser, const Source& start) : parser_(parser), start_(start) {}
|
|
|
|
|
|
|
|
/// Implicit conversion to Source that returns the combined source from start
|
|
|
|
/// to the current last token's source.
|
|
|
|
operator Source() const {
|
2022-07-25 09:43:08 -07:00
|
|
|
Source end = parser_->last_source().End();
|
2022-05-01 07:40:55 -07:00
|
|
|
if (end < start_) {
|
|
|
|
end = start_;
|
|
|
|
}
|
|
|
|
return Source::Combine(start_, end);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
ParserImpl* parser_;
|
|
|
|
Source start_;
|
2021-04-27 10:32:37 -07:00
|
|
|
};
|
|
|
|
|
2021-04-23 01:19:33 -07:00
|
|
|
ParserImpl::TypedIdentifier::TypedIdentifier() = default;
|
|
|
|
|
|
|
|
ParserImpl::TypedIdentifier::TypedIdentifier(const TypedIdentifier&) = default;
|
|
|
|
|
2021-10-19 11:38:54 -07:00
|
|
|
ParserImpl::TypedIdentifier::TypedIdentifier(const ast::Type* type_in,
|
2021-04-23 01:19:33 -07:00
|
|
|
std::string name_in,
|
|
|
|
Source source_in)
|
2021-06-16 02:19:36 -07:00
|
|
|
: type(type_in), name(std::move(name_in)), source(std::move(source_in)) {}
|
2021-04-23 01:19:33 -07:00
|
|
|
|
|
|
|
ParserImpl::TypedIdentifier::~TypedIdentifier() = default;
|
|
|
|
|
2020-12-07 12:45:14 -08:00
|
|
|
ParserImpl::FunctionHeader::FunctionHeader() = default;
|
|
|
|
|
|
|
|
ParserImpl::FunctionHeader::FunctionHeader(const FunctionHeader&) = default;
|
|
|
|
|
|
|
|
ParserImpl::FunctionHeader::FunctionHeader(Source src,
|
|
|
|
std::string n,
|
2022-08-02 10:03:35 -07:00
|
|
|
utils::VectorRef<const ast::Parameter*> p,
|
2021-10-19 11:38:54 -07:00
|
|
|
const ast::Type* ret_ty,
|
2022-08-02 10:03:35 -07:00
|
|
|
utils::VectorRef<const ast::Attribute*> ret_attrs)
|
2022-06-17 05:48:51 -07:00
|
|
|
: source(src),
|
|
|
|
name(n),
|
|
|
|
params(std::move(p)),
|
|
|
|
return_type(ret_ty),
|
|
|
|
return_type_attributes(std::move(ret_attrs)) {}
|
2020-12-07 12:45:14 -08:00
|
|
|
|
|
|
|
ParserImpl::FunctionHeader::~FunctionHeader() = default;
|
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
ParserImpl::FunctionHeader& ParserImpl::FunctionHeader::operator=(const FunctionHeader& rhs) =
|
|
|
|
default;
|
2020-12-07 12:45:14 -08:00
|
|
|
|
2021-04-26 07:19:55 -07:00
|
|
|
ParserImpl::VarDeclInfo::VarDeclInfo() = default;
|
|
|
|
|
|
|
|
ParserImpl::VarDeclInfo::VarDeclInfo(const VarDeclInfo&) = default;
|
|
|
|
|
|
|
|
ParserImpl::VarDeclInfo::VarDeclInfo(Source source_in,
|
|
|
|
std::string name_in,
|
|
|
|
ast::StorageClass storage_class_in,
|
2021-06-04 13:41:47 -07:00
|
|
|
ast::Access access_in,
|
2021-10-19 11:38:54 -07:00
|
|
|
const ast::Type* type_in)
|
2021-04-26 07:19:55 -07:00
|
|
|
: source(std::move(source_in)),
|
|
|
|
name(std::move(name_in)),
|
|
|
|
storage_class(storage_class_in),
|
2021-06-04 13:41:47 -07:00
|
|
|
access(access_in),
|
2021-04-26 07:19:55 -07:00
|
|
|
type(type_in) {}
|
|
|
|
|
|
|
|
ParserImpl::VarDeclInfo::~VarDeclInfo() = default;
|
|
|
|
|
2022-07-25 09:43:08 -07:00
|
|
|
ParserImpl::ParserImpl(Source::File const* file) : file_(file) {}
|
2020-11-02 09:55:18 -08:00
|
|
|
|
|
|
|
ParserImpl::~ParserImpl() = default;
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2020-11-09 11:39:34 -08:00
|
|
|
ParserImpl::Failure::Errored ParserImpl::add_error(const Source& source,
|
2022-01-25 09:15:37 -08:00
|
|
|
std::string_view err,
|
|
|
|
std::string_view use) {
|
2022-05-01 07:40:55 -07:00
|
|
|
std::stringstream msg;
|
|
|
|
msg << err;
|
|
|
|
if (!use.empty()) {
|
|
|
|
msg << " for " << use;
|
|
|
|
}
|
|
|
|
add_error(source, msg.str());
|
|
|
|
return Failure::kErrored;
|
2020-11-03 18:23:10 -08:00
|
|
|
}
|
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
ParserImpl::Failure::Errored ParserImpl::add_error(const Token& t, const std::string& err) {
|
|
|
|
add_error(t.source(), err);
|
|
|
|
return Failure::kErrored;
|
2020-11-03 18:23:10 -08:00
|
|
|
}
|
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
ParserImpl::Failure::Errored ParserImpl::add_error(const Source& source, const std::string& err) {
|
|
|
|
if (silence_errors_ == 0) {
|
|
|
|
builder_.Diagnostics().add_error(diag::System::Reader, err, source);
|
|
|
|
}
|
|
|
|
return Failure::kErrored;
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2021-04-08 07:39:47 -07:00
|
|
|
void ParserImpl::deprecated(const Source& source, const std::string& msg) {
|
2022-05-01 07:40:55 -07:00
|
|
|
builder_.Diagnostics().add_warning(diag::System::Reader,
|
|
|
|
"use of deprecated language feature: " + msg, source);
|
2021-04-08 07:39:47 -07:00
|
|
|
}
|
|
|
|
|
2022-07-25 09:43:08 -07:00
|
|
|
const Token& ParserImpl::next() {
|
2022-07-26 07:59:54 -07:00
|
|
|
// If the next token is already an error or the end of file, stay there.
|
|
|
|
if (tokens_[next_token_idx_].IsEof() || tokens_[next_token_idx_].IsError()) {
|
|
|
|
return tokens_[next_token_idx_];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Skip over any placeholder elements
|
|
|
|
while (true) {
|
|
|
|
if (!tokens_[next_token_idx_].IsPlaceholder()) {
|
|
|
|
break;
|
2022-07-25 09:43:08 -07:00
|
|
|
}
|
2022-07-26 07:59:54 -07:00
|
|
|
next_token_idx_++;
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
2022-07-25 17:28:37 -07:00
|
|
|
last_source_idx_ = next_token_idx_;
|
2022-07-27 15:54:50 -07:00
|
|
|
|
|
|
|
if (!tokens_[next_token_idx_].IsEof() && !tokens_[next_token_idx_].IsError()) {
|
|
|
|
next_token_idx_++;
|
|
|
|
}
|
|
|
|
return tokens_[last_source_idx_];
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2022-08-22 12:19:44 -07:00
|
|
|
const Token& ParserImpl::peek(size_t count) {
|
|
|
|
for (size_t idx = next_token_idx_; idx < tokens_.size(); idx++) {
|
|
|
|
if (tokens_[idx].IsPlaceholder()) {
|
|
|
|
continue;
|
2022-07-25 09:43:08 -07:00
|
|
|
}
|
2022-08-22 12:19:44 -07:00
|
|
|
if (count == 0) {
|
|
|
|
return tokens_[idx];
|
|
|
|
}
|
|
|
|
count--;
|
2022-07-27 15:54:50 -07:00
|
|
|
}
|
2022-08-22 12:19:44 -07:00
|
|
|
// Walked off the end of the token list, return last token.
|
|
|
|
return tokens_[tokens_.size() - 1];
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2021-07-12 04:21:51 -07:00
|
|
|
bool ParserImpl::peek_is(Token::Type tok, size_t idx) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return peek(idx).Is(tok);
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2022-07-25 09:43:08 -07:00
|
|
|
void ParserImpl::split_token(Token::Type lhs, Token::Type rhs) {
|
|
|
|
if (next_token_idx_ == 0) {
|
|
|
|
TINT_ICE(Reader, builder_.Diagnostics())
|
|
|
|
<< "attempt to update placeholder at beginning of tokens";
|
|
|
|
}
|
|
|
|
if (next_token_idx_ >= tokens_.size()) {
|
|
|
|
TINT_ICE(Reader, builder_.Diagnostics())
|
|
|
|
<< "attempt to update placeholder past end of tokens";
|
|
|
|
}
|
|
|
|
if (!tokens_[next_token_idx_].IsPlaceholder()) {
|
|
|
|
TINT_ICE(Reader, builder_.Diagnostics()) << "attempt to update non-placeholder token";
|
|
|
|
}
|
|
|
|
tokens_[next_token_idx_ - 1].SetType(lhs);
|
|
|
|
tokens_[next_token_idx_].SetType(rhs);
|
|
|
|
}
|
|
|
|
|
|
|
|
Source ParserImpl::last_source() const {
|
2022-07-25 17:28:37 -07:00
|
|
|
return tokens_[last_source_idx_].source();
|
2022-07-25 09:43:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void ParserImpl::InitializeLex() {
|
|
|
|
Lexer l{file_};
|
|
|
|
tokens_ = l.Lex();
|
2021-04-27 10:32:37 -07:00
|
|
|
}
|
|
|
|
|
2020-03-02 12:47:43 -08:00
|
|
|
bool ParserImpl::Parse() {
|
2022-07-25 09:43:08 -07:00
|
|
|
InitializeLex();
|
2022-05-01 07:40:55 -07:00
|
|
|
translation_unit();
|
|
|
|
return !has_error();
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// translation_unit
|
2022-08-02 14:17:05 -07:00
|
|
|
// : global_directive* global_decl* EOF
|
2020-03-02 12:47:43 -08:00
|
|
|
void ParserImpl::translation_unit() {
|
2022-05-01 07:40:55 -07:00
|
|
|
bool after_global_decl = false;
|
|
|
|
while (continue_parsing()) {
|
2022-07-25 09:43:08 -07:00
|
|
|
auto& p = peek();
|
2022-05-01 07:40:55 -07:00
|
|
|
if (p.IsEof()) {
|
|
|
|
break;
|
|
|
|
}
|
2022-04-26 19:27:52 -07:00
|
|
|
|
2022-08-02 14:17:05 -07:00
|
|
|
auto ed = global_directive(after_global_decl);
|
|
|
|
if (!ed.matched && !ed.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
auto gd = global_decl();
|
|
|
|
if (gd.matched) {
|
|
|
|
after_global_decl = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!gd.matched && !gd.errored) {
|
|
|
|
add_error(p, "unexpected token");
|
|
|
|
}
|
|
|
|
}
|
2022-04-26 19:27:52 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
if (builder_.Diagnostics().error_count() >= max_errors_) {
|
|
|
|
add_error(Source{{}, p.source().file},
|
|
|
|
"stopping after " + std::to_string(max_errors_) + " errors");
|
|
|
|
break;
|
|
|
|
}
|
2020-11-24 07:15:36 -08:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2022-08-02 14:17:05 -07:00
|
|
|
// global_directive
|
|
|
|
// : enable_directive
|
2022-08-17 11:57:49 -07:00
|
|
|
Maybe<Void> ParserImpl::global_directive(bool have_parsed_decl) {
|
2022-08-02 14:17:05 -07:00
|
|
|
auto& p = peek();
|
|
|
|
auto ed = enable_directive();
|
|
|
|
if (ed.matched && have_parsed_decl) {
|
|
|
|
return add_error(p, "enable directives must come before all global declarations");
|
|
|
|
}
|
|
|
|
return ed;
|
|
|
|
}
|
|
|
|
|
2022-04-26 19:27:52 -07:00
|
|
|
// enable_directive
|
|
|
|
// : enable name SEMICLON
|
2022-08-17 11:57:49 -07:00
|
|
|
Maybe<Void> ParserImpl::enable_directive() {
|
|
|
|
auto decl = sync(Token::Type::kSemicolon, [&]() -> Maybe<Void> {
|
2022-05-01 07:40:55 -07:00
|
|
|
if (!match(Token::Type::kEnable)) {
|
|
|
|
return Failure::kNoMatch;
|
|
|
|
}
|
2022-04-26 19:27:52 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
// Match the extension name.
|
|
|
|
Expect<std::string> name = {""};
|
2022-07-25 09:43:08 -07:00
|
|
|
auto& t = peek();
|
2022-05-01 07:40:55 -07:00
|
|
|
if (t.IsIdentifier()) {
|
|
|
|
synchronized_ = true;
|
|
|
|
next();
|
|
|
|
name = {t.to_str(), t.source()};
|
2022-05-13 05:01:11 -07:00
|
|
|
} else if (t.Is(Token::Type::kF16)) {
|
|
|
|
// `f16` is a valid extension name and also a keyword
|
|
|
|
synchronized_ = true;
|
|
|
|
next();
|
|
|
|
name = {"f16", t.source()};
|
2022-07-26 15:51:36 -07:00
|
|
|
} else if (t.Is(Token::Type::kParenLeft)) {
|
2022-07-21 08:53:55 -07:00
|
|
|
// A common error case is writing `enable(foo);` instead of `enable foo;`.
|
|
|
|
synchronized_ = false;
|
|
|
|
return add_error(t.source(), "enable directives don't take parenthesis");
|
2022-05-01 07:40:55 -07:00
|
|
|
} else if (handle_error(t)) {
|
|
|
|
// The token might itself be an error.
|
|
|
|
return Failure::kErrored;
|
|
|
|
} else {
|
|
|
|
// Failed to match an extension name.
|
|
|
|
synchronized_ = false;
|
|
|
|
return add_error(t.source(), "invalid extension name");
|
|
|
|
}
|
2022-04-26 19:27:52 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
if (!expect("enable directive", Token::Type::kSemicolon)) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
2022-04-26 19:27:52 -07:00
|
|
|
|
2022-05-18 15:41:48 -07:00
|
|
|
auto extension = ast::ParseExtension(name.value);
|
2022-07-27 11:32:19 -07:00
|
|
|
if (extension == ast::Extension::kInvalid) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return add_error(name.source, "unsupported extension: '" + name.value + "'");
|
|
|
|
}
|
2022-05-18 15:41:48 -07:00
|
|
|
builder_.AST().AddEnable(create<ast::Enable>(name.source, extension));
|
2022-04-26 19:27:52 -07:00
|
|
|
|
2022-08-17 11:57:49 -07:00
|
|
|
return kSuccess;
|
2022-05-01 07:40:55 -07:00
|
|
|
});
|
2022-04-26 19:27:52 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
if (decl.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (decl.matched) {
|
2022-08-17 11:57:49 -07:00
|
|
|
return kSuccess;
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
2022-04-26 19:27:52 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
2022-04-26 19:27:52 -07:00
|
|
|
}
|
|
|
|
|
2020-03-02 12:47:43 -08:00
|
|
|
// global_decl
|
|
|
|
// : SEMICOLON
|
2022-08-03 03:08:46 -07:00
|
|
|
// | global_variable_decl SEMICOLON
|
2020-03-02 12:47:43 -08:00
|
|
|
// | global_constant_decl SEMICOLON
|
2022-08-04 10:57:26 -07:00
|
|
|
// | type_alias_decl SEMICOLON
|
2022-01-19 10:11:17 -08:00
|
|
|
// | struct_decl
|
2020-03-02 12:47:43 -08:00
|
|
|
// | function_decl
|
2022-08-03 03:08:46 -07:00
|
|
|
// | static_assert_statement SEMICOLON
|
2022-08-17 11:57:49 -07:00
|
|
|
Maybe<Void> ParserImpl::global_decl() {
|
2022-05-19 13:08:19 -07:00
|
|
|
if (match(Token::Type::kSemicolon) || match(Token::Type::kEOF)) {
|
2022-08-17 11:57:49 -07:00
|
|
|
return kSuccess;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-04 12:55:31 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
bool errored = false;
|
|
|
|
auto attrs = attribute_list();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (attrs.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
errored = true;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (!continue_parsing()) {
|
2020-11-11 10:44:36 -08:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-04 06:17:51 -08:00
|
|
|
|
2022-08-17 11:57:49 -07:00
|
|
|
auto decl = sync(Token::Type::kSemicolon, [&]() -> Maybe<Void> {
|
2022-05-01 07:40:55 -07:00
|
|
|
auto gv = global_variable_decl(attrs.value);
|
2022-05-19 13:08:19 -07:00
|
|
|
if (gv.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-05-01 07:40:55 -07:00
|
|
|
if (gv.matched) {
|
2022-05-19 13:08:19 -07:00
|
|
|
if (!expect("variable declaration", Token::Type::kSemicolon)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-05-01 07:40:55 -07:00
|
|
|
|
|
|
|
builder_.AST().AddGlobalVariable(gv.value);
|
2022-08-17 11:57:49 -07:00
|
|
|
return kSuccess;
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
auto gc = global_constant_decl(attrs.value);
|
2022-05-19 13:08:19 -07:00
|
|
|
if (gc.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-05-01 07:40:55 -07:00
|
|
|
if (gc.matched) {
|
2022-06-26 03:52:50 -07:00
|
|
|
// Avoid the cost of the string allocation for the common no-error case
|
|
|
|
if (!peek().Is(Token::Type::kSemicolon)) {
|
|
|
|
std::string kind = gc->Kind();
|
|
|
|
if (!expect("'" + kind + "' declaration", Token::Type::kSemicolon)) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-04 06:17:51 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
builder_.AST().AddGlobalVariable(gc.value);
|
2022-08-17 11:57:49 -07:00
|
|
|
return kSuccess;
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
|
|
|
|
2022-08-04 10:57:26 -07:00
|
|
|
auto ta = type_alias_decl();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (ta.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-05-01 07:40:55 -07:00
|
|
|
if (ta.matched) {
|
2022-05-19 13:08:19 -07:00
|
|
|
if (!expect("type alias", Token::Type::kSemicolon)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-05-01 07:40:55 -07:00
|
|
|
|
|
|
|
builder_.AST().AddTypeDecl(ta.value);
|
2022-08-17 11:57:49 -07:00
|
|
|
return kSuccess;
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
|
|
|
|
2022-08-05 07:53:47 -07:00
|
|
|
auto assertion = static_assert_statement();
|
2022-08-03 03:08:46 -07:00
|
|
|
if (assertion.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (assertion.matched) {
|
|
|
|
builder_.AST().AddStaticAssert(assertion.value);
|
|
|
|
if (!expect("static assertion declaration", Token::Type::kSemicolon)) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
2022-08-17 11:57:49 -07:00
|
|
|
return kSuccess;
|
2022-08-03 03:08:46 -07:00
|
|
|
}
|
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
|
|
|
});
|
|
|
|
|
|
|
|
if (decl.errored) {
|
|
|
|
errored = true;
|
|
|
|
}
|
|
|
|
if (decl.matched) {
|
2022-08-17 11:57:49 -07:00
|
|
|
if (!expect_attributes_consumed(attrs.value)) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
return kSuccess;
|
2020-11-11 10:44:36 -08:00
|
|
|
}
|
2020-11-09 11:39:34 -08:00
|
|
|
|
2022-08-04 10:57:26 -07:00
|
|
|
auto str = struct_decl();
|
|
|
|
if (str.errored) {
|
|
|
|
errored = true;
|
|
|
|
}
|
|
|
|
if (str.matched) {
|
|
|
|
builder_.AST().AddTypeDecl(str.value);
|
2022-08-17 11:57:49 -07:00
|
|
|
if (!expect_attributes_consumed(attrs.value)) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
return kSuccess;
|
2022-08-04 10:57:26 -07:00
|
|
|
}
|
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
auto func = function_decl(attrs.value);
|
|
|
|
if (func.errored) {
|
|
|
|
errored = true;
|
|
|
|
}
|
|
|
|
if (func.matched) {
|
|
|
|
builder_.AST().AddFunction(func.value);
|
2022-08-17 11:57:49 -07:00
|
|
|
return kSuccess;
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
2020-11-04 06:17:51 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
if (errored) {
|
2020-11-11 10:44:36 -08:00
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
2020-11-09 11:39:34 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
// Invalid syntax found - try and determine the best error message
|
2020-11-04 06:17:51 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
// We have attributes parsed, but nothing to consume them?
|
2022-08-02 10:03:35 -07:00
|
|
|
if (attrs.value.Length() > 0) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return add_error(next(), "expected declaration after attributes");
|
2020-11-11 10:44:36 -08:00
|
|
|
}
|
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
// We have a statement outside of a function?
|
2022-07-25 09:43:08 -07:00
|
|
|
auto& t = peek();
|
2022-05-01 07:40:55 -07:00
|
|
|
auto stat = without_error([&] { return statement(); });
|
|
|
|
if (stat.matched) {
|
|
|
|
// Attempt to jump to the next '}' - the function might have just been
|
|
|
|
// missing an opening line.
|
|
|
|
sync_to(Token::Type::kBraceRight, true);
|
|
|
|
return add_error(t, "statement found outside of function body");
|
|
|
|
}
|
|
|
|
if (!stat.errored) {
|
|
|
|
// No match, no error - the parser might not have progressed.
|
|
|
|
// Ensure we always make _some_ forward progress.
|
|
|
|
next();
|
|
|
|
}
|
2020-11-19 06:19:31 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
// The token might itself be an error.
|
|
|
|
if (handle_error(t)) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
2021-11-15 12:31:44 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
// Exhausted all attempts to make sense of where we're at.
|
|
|
|
// Return a no-match
|
2020-11-19 06:19:31 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// global_variable_decl
|
2022-08-09 09:52:42 -07:00
|
|
|
// : variable_attribute_list* variable_decl (EQUAL expression)?
|
2022-08-02 10:03:35 -07:00
|
|
|
Maybe<const ast::Variable*> ParserImpl::global_variable_decl(AttributeList& attrs) {
|
2022-05-01 07:40:55 -07:00
|
|
|
auto decl = variable_decl();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (decl.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (!decl.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-09 11:52:24 -08:00
|
|
|
|
2022-08-02 10:03:35 -07:00
|
|
|
const ast::Expression* initializer = nullptr;
|
2022-05-01 07:40:55 -07:00
|
|
|
if (match(Token::Type::kEqual)) {
|
2022-08-24 12:11:55 -07:00
|
|
|
auto expr = expression();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (expr.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-06-26 04:36:10 -07:00
|
|
|
if (!expr.matched) {
|
2022-08-02 10:03:35 -07:00
|
|
|
return add_error(peek(), "missing initializer for 'var' declaration");
|
2022-06-26 04:36:10 -07:00
|
|
|
}
|
2022-08-02 10:03:35 -07:00
|
|
|
initializer = expr.value;
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
|
|
|
|
2022-08-02 10:03:35 -07:00
|
|
|
TINT_DEFER(attrs.Clear());
|
|
|
|
|
2022-06-17 05:48:51 -07:00
|
|
|
return create<ast::Var>(decl->source, // source
|
|
|
|
builder_.Symbols().Register(decl->name), // symbol
|
|
|
|
decl->type, // type
|
|
|
|
decl->storage_class, // storage class
|
|
|
|
decl->access, // access control
|
2022-08-02 10:03:35 -07:00
|
|
|
initializer, // initializer
|
2022-06-17 05:48:51 -07:00
|
|
|
std::move(attrs)); // attributes
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2022-02-15 08:36:57 -08:00
|
|
|
// global_constant_decl :
|
2022-08-17 10:38:17 -07:00
|
|
|
// | LET optionally_typed_ident global_const_initializer
|
|
|
|
// | attribute* override optionally_typed_ident (equal expression)?
|
2021-05-06 06:42:03 -07:00
|
|
|
// global_const_initializer
|
|
|
|
// : EQUAL const_expr
|
2022-08-02 10:03:35 -07:00
|
|
|
Maybe<const ast::Variable*> ParserImpl::global_constant_decl(AttributeList& attrs) {
|
2022-06-26 03:52:50 -07:00
|
|
|
bool is_const = false;
|
2022-05-01 07:40:55 -07:00
|
|
|
bool is_overridable = false;
|
|
|
|
const char* use = nullptr;
|
2022-06-28 17:55:36 -07:00
|
|
|
Source source;
|
|
|
|
if (match(Token::Type::kConst)) {
|
2022-06-26 03:52:50 -07:00
|
|
|
use = "'const' declaration";
|
2022-06-28 17:55:36 -07:00
|
|
|
} else if (match(Token::Type::kLet, &source)) {
|
|
|
|
use = "'let' declaration";
|
|
|
|
deprecated(source, "module-scope 'let' has been replaced with 'const'");
|
2022-05-01 07:40:55 -07:00
|
|
|
} else if (match(Token::Type::kOverride)) {
|
2022-06-17 05:48:51 -07:00
|
|
|
use = "'override' declaration";
|
2022-05-01 07:40:55 -07:00
|
|
|
is_overridable = true;
|
|
|
|
} else {
|
|
|
|
return Failure::kNoMatch;
|
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-08-17 10:38:17 -07:00
|
|
|
auto decl = expect_optionally_typed_ident(use);
|
2022-05-19 13:08:19 -07:00
|
|
|
if (decl.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-06-16 01:36:30 -07:00
|
|
|
bool has_initializer = false;
|
|
|
|
if (is_overridable) {
|
|
|
|
has_initializer = match(Token::Type::kEqual);
|
|
|
|
} else {
|
|
|
|
if (!expect(use, Token::Type::kEqual)) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
has_initializer = true;
|
|
|
|
}
|
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
const ast::Expression* initializer = nullptr;
|
2022-06-16 01:36:30 -07:00
|
|
|
if (has_initializer) {
|
2022-08-24 12:11:55 -07:00
|
|
|
auto expr = expression();
|
2022-06-26 04:36:10 -07:00
|
|
|
if (expr.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
2022-06-26 04:36:10 -07:00
|
|
|
if (!expr.matched) {
|
|
|
|
return add_error(peek(), "missing initializer for " + std::string(use));
|
|
|
|
}
|
|
|
|
initializer = std::move(expr.value);
|
2021-11-09 01:35:00 -08:00
|
|
|
}
|
2020-11-09 10:41:23 -08:00
|
|
|
|
2022-08-02 10:03:35 -07:00
|
|
|
TINT_DEFER(attrs.Clear());
|
|
|
|
|
2022-06-26 03:52:50 -07:00
|
|
|
if (is_const) {
|
|
|
|
return create<ast::Const>(decl->source, // source
|
|
|
|
builder_.Symbols().Register(decl->name), // symbol
|
|
|
|
decl->type, // type
|
2022-07-22 09:08:35 -07:00
|
|
|
initializer, // initializer
|
2022-06-26 03:52:50 -07:00
|
|
|
std::move(attrs)); // attributes
|
|
|
|
}
|
2022-06-17 05:48:51 -07:00
|
|
|
if (is_overridable) {
|
|
|
|
return create<ast::Override>(decl->source, // source
|
|
|
|
builder_.Symbols().Register(decl->name), // symbol
|
|
|
|
decl->type, // type
|
2022-07-22 09:08:35 -07:00
|
|
|
initializer, // initializer
|
2022-06-17 05:48:51 -07:00
|
|
|
std::move(attrs)); // attributes
|
|
|
|
}
|
2022-06-28 17:55:36 -07:00
|
|
|
return create<ast::Const>(decl->source, // source
|
|
|
|
builder_.Symbols().Register(decl->name), // symbol
|
|
|
|
decl->type, // type
|
2022-07-22 09:08:35 -07:00
|
|
|
initializer, // initializer
|
2022-06-28 17:55:36 -07:00
|
|
|
std::move(attrs)); // attributes
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// variable_decl
|
2022-08-17 10:38:17 -07:00
|
|
|
// : VAR variable_qualifier? optionally_typed_ident
|
2022-08-22 07:55:04 -07:00
|
|
|
//
|
|
|
|
// Note, the `( LESS_THAN address_space ( COMMA access_mode )? GREATER_THAN ) is pulled out into
|
|
|
|
// a `variable_qualifier` helper.
|
2022-06-28 05:53:56 -07:00
|
|
|
Maybe<ParserImpl::VarDeclInfo> ParserImpl::variable_decl() {
|
2022-05-01 07:40:55 -07:00
|
|
|
Source source;
|
2022-05-19 13:08:19 -07:00
|
|
|
if (!match(Token::Type::kVar, &source)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
VariableQualifier vq;
|
|
|
|
auto explicit_vq = variable_qualifier();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (explicit_vq.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-05-01 07:40:55 -07:00
|
|
|
if (explicit_vq.matched) {
|
|
|
|
vq = explicit_vq.value;
|
|
|
|
}
|
2021-04-09 12:58:58 -07:00
|
|
|
|
2022-08-17 10:38:17 -07:00
|
|
|
auto decl = expect_optionally_typed_ident("variable declaration");
|
2022-05-19 13:08:19 -07:00
|
|
|
if (decl.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return VarDeclInfo{decl->source, decl->name, vq.storage_class, vq.access, decl->type};
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2022-08-17 07:28:31 -07:00
|
|
|
// texture_and_sampler_types
|
|
|
|
// : sampler_type
|
|
|
|
// | depth_texture_type
|
|
|
|
// | sampled_texture_type LESS_THAN type_decl GREATER_THAN
|
|
|
|
// | multisampled_texture_type LESS_THAN type_decl GREATER_THAN
|
|
|
|
// | storage_texture_type LESS_THAN texel_format
|
|
|
|
// COMMA access_mode GREATER_THAN
|
|
|
|
Maybe<const ast::Type*> ParserImpl::texture_and_sampler_types() {
|
|
|
|
auto type = sampler_type();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (type.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return type;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-09-02 07:16:35 -07:00
|
|
|
|
2022-08-17 07:28:31 -07:00
|
|
|
type = depth_texture_type();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (type.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return type;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-09-02 07:16:35 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
type = external_texture();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (type.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return type.value;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2021-04-22 15:47:03 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
auto source_range = make_source_range();
|
2021-04-27 10:32:37 -07:00
|
|
|
|
2022-08-17 07:28:31 -07:00
|
|
|
auto dim = sampled_texture_type();
|
2022-05-01 07:40:55 -07:00
|
|
|
if (dim.matched) {
|
|
|
|
const char* use = "sampled texture type";
|
2020-11-09 11:19:44 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
auto subtype = expect_lt_gt_block(use, [&] { return expect_type(use); });
|
2022-05-19 13:08:19 -07:00
|
|
|
if (subtype.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-05-01 07:40:55 -07:00
|
|
|
|
|
|
|
return builder_.ty.sampled_texture(source_range, dim.value, subtype.value);
|
|
|
|
}
|
2020-09-02 08:52:15 -07:00
|
|
|
|
2022-08-17 07:28:31 -07:00
|
|
|
auto ms_dim = multisampled_texture_type();
|
2022-05-01 07:40:55 -07:00
|
|
|
if (ms_dim.matched) {
|
|
|
|
const char* use = "multisampled texture type";
|
2020-09-16 20:53:04 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
auto subtype = expect_lt_gt_block(use, [&] { return expect_type(use); });
|
2022-05-19 13:08:19 -07:00
|
|
|
if (subtype.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-09 11:19:44 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return builder_.ty.multisampled_texture(source_range, ms_dim.value, subtype.value);
|
|
|
|
}
|
2020-09-16 20:53:04 -07:00
|
|
|
|
2022-08-17 07:28:31 -07:00
|
|
|
auto storage = storage_texture_type();
|
2022-05-01 07:40:55 -07:00
|
|
|
if (storage.matched) {
|
|
|
|
const char* use = "storage texture type";
|
|
|
|
using StorageTextureInfo = std::pair<tint::ast::TexelFormat, tint::ast::Access>;
|
|
|
|
auto params = expect_lt_gt_block(use, [&]() -> Expect<StorageTextureInfo> {
|
|
|
|
auto format = expect_texel_format(use);
|
|
|
|
if (format.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
2020-09-08 09:48:09 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
if (!expect("access control", Token::Type::kComma)) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
2021-06-03 01:44:14 -07:00
|
|
|
|
2022-08-17 07:28:31 -07:00
|
|
|
auto access = expect_access_mode("access control");
|
2022-05-01 07:40:55 -07:00
|
|
|
if (access.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
2021-06-03 01:44:14 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return std::make_pair(format.value, access.value);
|
|
|
|
});
|
2020-11-09 11:19:44 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
if (params.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
2021-01-14 00:34:46 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return builder_.ty.storage_texture(source_range, storage.value, params->first,
|
|
|
|
params->second);
|
2021-06-03 01:44:14 -07:00
|
|
|
}
|
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
2020-09-02 07:16:35 -07:00
|
|
|
}
|
|
|
|
|
2022-08-17 07:28:31 -07:00
|
|
|
// sampler_type
|
2020-09-02 06:26:15 -07:00
|
|
|
// : SAMPLER
|
|
|
|
// | SAMPLER_COMPARISON
|
2022-08-17 07:28:31 -07:00
|
|
|
Maybe<const ast::Type*> ParserImpl::sampler_type() {
|
2022-05-01 07:40:55 -07:00
|
|
|
Source source;
|
2022-05-19 13:08:19 -07:00
|
|
|
if (match(Token::Type::kSampler, &source)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return builder_.ty.sampler(source, ast::SamplerKind::kSampler);
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-04 11:11:41 -08:00
|
|
|
|
2022-05-19 13:08:19 -07:00
|
|
|
if (match(Token::Type::kComparisonSampler, &source)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return builder_.ty.sampler(source, ast::SamplerKind::kComparisonSampler);
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-04 11:11:41 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
2020-09-02 06:26:15 -07:00
|
|
|
}
|
|
|
|
|
2022-08-17 07:28:31 -07:00
|
|
|
// sampled_texture_type
|
2020-09-02 08:52:15 -07:00
|
|
|
// : TEXTURE_SAMPLED_1D
|
|
|
|
// | TEXTURE_SAMPLED_2D
|
|
|
|
// | TEXTURE_SAMPLED_2D_ARRAY
|
|
|
|
// | TEXTURE_SAMPLED_3D
|
|
|
|
// | TEXTURE_SAMPLED_CUBE
|
|
|
|
// | TEXTURE_SAMPLED_CUBE_ARRAY
|
2022-08-17 07:28:31 -07:00
|
|
|
Maybe<const ast::TextureDimension> ParserImpl::sampled_texture_type() {
|
2022-05-19 13:08:19 -07:00
|
|
|
if (match(Token::Type::kTextureSampled1d)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return ast::TextureDimension::k1d;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-04 11:11:41 -08:00
|
|
|
|
2022-05-19 13:08:19 -07:00
|
|
|
if (match(Token::Type::kTextureSampled2d)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return ast::TextureDimension::k2d;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-04 11:11:41 -08:00
|
|
|
|
2022-05-19 13:08:19 -07:00
|
|
|
if (match(Token::Type::kTextureSampled2dArray)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return ast::TextureDimension::k2dArray;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-04 11:11:41 -08:00
|
|
|
|
2022-05-19 13:08:19 -07:00
|
|
|
if (match(Token::Type::kTextureSampled3d)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return ast::TextureDimension::k3d;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-04 11:11:41 -08:00
|
|
|
|
2022-05-19 13:08:19 -07:00
|
|
|
if (match(Token::Type::kTextureSampledCube)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return ast::TextureDimension::kCube;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-04 11:11:41 -08:00
|
|
|
|
2022-05-19 13:08:19 -07:00
|
|
|
if (match(Token::Type::kTextureSampledCubeArray)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return ast::TextureDimension::kCubeArray;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-04 11:11:41 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
2020-09-02 08:52:15 -07:00
|
|
|
}
|
|
|
|
|
2022-04-28 11:49:04 -07:00
|
|
|
// external_texture
|
2021-04-22 15:47:03 -07:00
|
|
|
// : TEXTURE_EXTERNAL
|
2022-04-28 11:49:04 -07:00
|
|
|
Maybe<const ast::Type*> ParserImpl::external_texture() {
|
2022-05-01 07:40:55 -07:00
|
|
|
Source source;
|
|
|
|
if (match(Token::Type::kTextureExternal, &source)) {
|
|
|
|
return builder_.ty.external_texture(source);
|
|
|
|
}
|
2021-04-22 15:47:03 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
2021-04-22 15:47:03 -07:00
|
|
|
}
|
|
|
|
|
2022-08-17 07:28:31 -07:00
|
|
|
// multisampled_texture_type
|
2020-09-16 20:53:04 -07:00
|
|
|
// : TEXTURE_MULTISAMPLED_2D
|
2022-08-17 07:28:31 -07:00
|
|
|
Maybe<const ast::TextureDimension> ParserImpl::multisampled_texture_type() {
|
2022-05-19 13:08:19 -07:00
|
|
|
if (match(Token::Type::kTextureMultisampled2d)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return ast::TextureDimension::k2d;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-04 11:11:41 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
2020-09-16 20:53:04 -07:00
|
|
|
}
|
|
|
|
|
2022-08-17 07:28:31 -07:00
|
|
|
// storage_texture_type
|
2021-01-14 00:34:46 -08:00
|
|
|
// : TEXTURE_STORAGE_1D
|
|
|
|
// | TEXTURE_STORAGE_2D
|
|
|
|
// | TEXTURE_STORAGE_2D_ARRAY
|
|
|
|
// | TEXTURE_STORAGE_3D
|
2022-08-17 07:28:31 -07:00
|
|
|
Maybe<const ast::TextureDimension> ParserImpl::storage_texture_type() {
|
2022-05-19 13:08:19 -07:00
|
|
|
if (match(Token::Type::kTextureStorage1d)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return ast::TextureDimension::k1d;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (match(Token::Type::kTextureStorage2d)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return ast::TextureDimension::k2d;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (match(Token::Type::kTextureStorage2dArray)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return ast::TextureDimension::k2dArray;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (match(Token::Type::kTextureStorage3d)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return ast::TextureDimension::k3d;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2021-01-14 00:34:46 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
2021-01-14 00:34:46 -08:00
|
|
|
}
|
|
|
|
|
2022-08-17 07:28:31 -07:00
|
|
|
// depth_texture_type
|
2020-09-02 07:06:45 -07:00
|
|
|
// : TEXTURE_DEPTH_2D
|
|
|
|
// | TEXTURE_DEPTH_2D_ARRAY
|
|
|
|
// | TEXTURE_DEPTH_CUBE
|
|
|
|
// | TEXTURE_DEPTH_CUBE_ARRAY
|
2021-07-26 15:19:48 -07:00
|
|
|
// | TEXTURE_DEPTH_MULTISAMPLED_2D
|
2022-08-17 07:28:31 -07:00
|
|
|
Maybe<const ast::Type*> ParserImpl::depth_texture_type() {
|
2022-05-01 07:40:55 -07:00
|
|
|
Source source;
|
|
|
|
if (match(Token::Type::kTextureDepth2d, &source)) {
|
|
|
|
return builder_.ty.depth_texture(source, ast::TextureDimension::k2d);
|
|
|
|
}
|
|
|
|
if (match(Token::Type::kTextureDepth2dArray, &source)) {
|
|
|
|
return builder_.ty.depth_texture(source, ast::TextureDimension::k2dArray);
|
|
|
|
}
|
|
|
|
if (match(Token::Type::kTextureDepthCube, &source)) {
|
|
|
|
return builder_.ty.depth_texture(source, ast::TextureDimension::kCube);
|
|
|
|
}
|
|
|
|
if (match(Token::Type::kTextureDepthCubeArray, &source)) {
|
|
|
|
return builder_.ty.depth_texture(source, ast::TextureDimension::kCubeArray);
|
|
|
|
}
|
|
|
|
if (match(Token::Type::kTextureDepthMultisampled2d, &source)) {
|
|
|
|
return builder_.ty.depth_multisampled_texture(source, ast::TextureDimension::k2d);
|
|
|
|
}
|
|
|
|
return Failure::kNoMatch;
|
2020-09-02 07:06:45 -07:00
|
|
|
}
|
|
|
|
|
2022-01-06 10:37:01 -08:00
|
|
|
// texel_format
|
|
|
|
// : 'rgba8unorm'
|
|
|
|
// | 'rgba8snorm'
|
|
|
|
// | 'rgba8uint'
|
|
|
|
// | 'rgba8sint'
|
|
|
|
// | 'rgba16uint'
|
|
|
|
// | 'rgba16sint'
|
|
|
|
// | 'rgba16float'
|
|
|
|
// | 'r32uint'
|
|
|
|
// | 'r32sint'
|
|
|
|
// | 'r32float'
|
|
|
|
// | 'rg32uint'
|
|
|
|
// | 'rg32sint'
|
|
|
|
// | 'rg32float'
|
|
|
|
// | 'rgba32uint'
|
|
|
|
// | 'rgba32sint'
|
|
|
|
// | 'rgba32float'
|
2022-01-25 09:15:37 -08:00
|
|
|
Expect<ast::TexelFormat> ParserImpl::expect_texel_format(std::string_view use) {
|
2022-07-25 09:43:08 -07:00
|
|
|
auto& t = next();
|
2022-07-27 15:30:10 -07:00
|
|
|
auto fmt = ast::ParseTexelFormat(t.to_str());
|
|
|
|
if (fmt == ast::TexelFormat::kInvalid) {
|
|
|
|
return add_error(t.source(), "invalid format", use);
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
2022-07-27 15:30:10 -07:00
|
|
|
return fmt;
|
2020-09-08 09:38:39 -07:00
|
|
|
}
|
|
|
|
|
2022-08-17 10:38:17 -07:00
|
|
|
Expect<ParserImpl::TypedIdentifier> ParserImpl::expect_ident_with_optional_type_decl(
|
2022-08-10 11:54:43 -07:00
|
|
|
std::string_view use,
|
|
|
|
bool allow_inferred) {
|
2022-05-01 07:40:55 -07:00
|
|
|
auto ident = expect_ident(use);
|
2022-05-19 13:08:19 -07:00
|
|
|
if (ident.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
if (allow_inferred && !peek_is(Token::Type::kColon)) {
|
|
|
|
return TypedIdentifier{nullptr, ident.value, ident.source};
|
|
|
|
}
|
2021-06-04 08:28:47 -07:00
|
|
|
|
2022-05-19 13:08:19 -07:00
|
|
|
if (!expect(use, Token::Type::kColon)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-07-25 09:43:08 -07:00
|
|
|
auto& t = peek();
|
2022-05-01 07:40:55 -07:00
|
|
|
auto type = type_decl();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (type.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (!type.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return add_error(t.source(), "invalid type", use);
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return TypedIdentifier{type.value, ident.value, ident.source};
|
2020-11-19 10:55:01 -08:00
|
|
|
}
|
|
|
|
|
2022-08-17 10:38:17 -07:00
|
|
|
// optionally_typed_ident
|
|
|
|
// : ident ( COLON typed_decl ) ?
|
|
|
|
Expect<ParserImpl::TypedIdentifier> ParserImpl::expect_optionally_typed_ident(
|
2022-08-10 11:54:43 -07:00
|
|
|
std::string_view use) {
|
2022-08-17 10:38:17 -07:00
|
|
|
return expect_ident_with_optional_type_decl(use, true);
|
2022-08-10 11:54:43 -07:00
|
|
|
}
|
|
|
|
|
2022-08-17 10:38:17 -07:00
|
|
|
// ident_with_type_decl
|
2022-08-10 11:54:43 -07:00
|
|
|
// : IDENT COLON type_decl
|
2022-08-17 10:38:17 -07:00
|
|
|
Expect<ParserImpl::TypedIdentifier> ParserImpl::expect_ident_with_type_decl(std::string_view use) {
|
|
|
|
return expect_ident_with_optional_type_decl(use, false);
|
2022-08-10 11:54:43 -07:00
|
|
|
}
|
|
|
|
|
2022-08-05 07:47:57 -07:00
|
|
|
// access_mode
|
|
|
|
// : 'read'
|
|
|
|
// | 'write'
|
|
|
|
// | 'read_write'
|
2022-08-17 07:28:31 -07:00
|
|
|
Expect<ast::Access> ParserImpl::expect_access_mode(std::string_view use) {
|
2022-05-01 07:40:55 -07:00
|
|
|
auto ident = expect_ident(use);
|
2022-05-19 13:08:19 -07:00
|
|
|
if (ident.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-19 10:55:01 -08:00
|
|
|
|
2022-08-05 07:47:57 -07:00
|
|
|
if (ident.value == "read") {
|
2022-05-01 07:40:55 -07:00
|
|
|
return {ast::Access::kRead, ident.source};
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-08-05 07:47:57 -07:00
|
|
|
if (ident.value == "write") {
|
2022-05-01 07:40:55 -07:00
|
|
|
return {ast::Access::kWrite, ident.source};
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-08-05 07:47:57 -07:00
|
|
|
if (ident.value == "read_write") {
|
2022-05-01 07:40:55 -07:00
|
|
|
return {ast::Access::kReadWrite, ident.source};
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-19 10:55:01 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return add_error(ident.source, "invalid value for access control");
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2021-06-04 13:41:47 -07:00
|
|
|
// variable_qualifier
|
2022-08-22 07:55:04 -07:00
|
|
|
// : LESS_THAN address_spaces (COMMA access_mode)? GREATER_THAN
|
2021-06-04 13:41:47 -07:00
|
|
|
Maybe<ParserImpl::VariableQualifier> ParserImpl::variable_qualifier() {
|
2022-05-01 07:40:55 -07:00
|
|
|
if (!peek_is(Token::Type::kLessThan)) {
|
|
|
|
return Failure::kNoMatch;
|
2021-06-04 13:41:47 -07:00
|
|
|
}
|
2022-05-01 07:40:55 -07:00
|
|
|
|
|
|
|
auto* use = "variable declaration";
|
|
|
|
auto vq = expect_lt_gt_block(use, [&]() -> Expect<VariableQualifier> {
|
|
|
|
auto source = make_source_range();
|
2022-08-05 07:47:57 -07:00
|
|
|
auto sc = expect_address_space(use);
|
2022-05-01 07:40:55 -07:00
|
|
|
if (sc.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (match(Token::Type::kComma)) {
|
2022-08-17 07:28:31 -07:00
|
|
|
auto ac = expect_access_mode(use);
|
2022-05-01 07:40:55 -07:00
|
|
|
if (ac.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
return VariableQualifier{sc.value, ac.value};
|
|
|
|
}
|
|
|
|
return Expect<VariableQualifier>{VariableQualifier{sc.value, ast::Access::kUndefined},
|
|
|
|
source};
|
|
|
|
});
|
|
|
|
|
|
|
|
if (vq.errored) {
|
2021-06-04 13:41:47 -07:00
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return vq;
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2022-08-04 10:57:26 -07:00
|
|
|
// type_alias_decl
|
2020-03-02 12:47:43 -08:00
|
|
|
// : TYPE IDENT EQUAL type_decl
|
2022-08-04 10:57:26 -07:00
|
|
|
Maybe<const ast::Alias*> ParserImpl::type_alias_decl() {
|
2022-05-19 13:08:19 -07:00
|
|
|
if (!peek_is(Token::Type::kType)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-07-25 09:43:08 -07:00
|
|
|
auto& t = next();
|
2022-05-01 07:40:55 -07:00
|
|
|
const char* use = "type alias";
|
2020-11-04 08:34:10 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
auto name = expect_ident(use);
|
2022-05-19 13:08:19 -07:00
|
|
|
if (name.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-19 13:08:19 -07:00
|
|
|
if (!expect(use, Token::Type::kEqual)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
auto type = type_decl();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (type.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (!type.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return add_error(peek(), "invalid type alias");
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return builder_.ty.alias(make_source_range_from(t.source()), name.value, type.value);
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2022-08-22 09:15:04 -07:00
|
|
|
// vec_prefix
|
|
|
|
// : 'vec2'
|
|
|
|
// | 'vec3'
|
|
|
|
// | 'vec4'
|
|
|
|
Maybe<uint32_t> ParserImpl::vec_prefix() {
|
|
|
|
auto& t = peek();
|
|
|
|
if (!t.IsVector()) {
|
|
|
|
return Failure::kNoMatch;
|
|
|
|
}
|
|
|
|
next();
|
|
|
|
|
|
|
|
if (t.Is(Token::Type::kVec3)) {
|
|
|
|
return 3u;
|
|
|
|
}
|
|
|
|
if (t.Is(Token::Type::kVec4)) {
|
|
|
|
return 4u;
|
|
|
|
}
|
|
|
|
return 2u;
|
|
|
|
}
|
|
|
|
|
|
|
|
// mat_prefix
|
|
|
|
// : 'mat2x2'
|
|
|
|
// | 'mat2x3'
|
|
|
|
// | 'mat2x4'
|
|
|
|
// | 'mat3x2'
|
|
|
|
// | 'mat3x3'
|
|
|
|
// | 'mat3x4'
|
|
|
|
// | 'mat4x2'
|
|
|
|
// | 'mat4x3'
|
|
|
|
// | 'mat4x4'
|
|
|
|
Maybe<ParserImpl::MatrixDimensions> ParserImpl::mat_prefix() {
|
|
|
|
auto& t = peek();
|
|
|
|
if (!t.IsMatrix()) {
|
|
|
|
return Failure::kNoMatch;
|
|
|
|
}
|
|
|
|
next();
|
|
|
|
|
|
|
|
uint32_t columns = 2;
|
|
|
|
if (t.IsMat3xN()) {
|
|
|
|
columns = 3;
|
|
|
|
} else if (t.IsMat4xN()) {
|
|
|
|
columns = 4;
|
|
|
|
}
|
|
|
|
if (t.IsMatNx3()) {
|
|
|
|
return MatrixDimensions{columns, 3};
|
|
|
|
}
|
|
|
|
if (t.IsMatNx4()) {
|
|
|
|
return MatrixDimensions{columns, 4};
|
|
|
|
}
|
|
|
|
return MatrixDimensions{columns, 2};
|
|
|
|
}
|
|
|
|
|
|
|
|
// type_decl_without_ident:
|
|
|
|
// : BOOL
|
|
|
|
// | F16
|
|
|
|
// | F32
|
|
|
|
// | I32
|
|
|
|
// | U32
|
|
|
|
// | ARRAY LESS_THAN type_decl ( COMMA element_count_expression )? GREATER_THAN
|
|
|
|
// | ATOMIC LESS_THAN type_decl GREATER_THAN
|
|
|
|
// | PTR LESS_THAN address_space COMMA type_decl ( COMMA access_mode )? GREATER_THAN
|
|
|
|
// | mat_prefix LESS_THAN type_decl GREATER_THAN
|
|
|
|
// | vec_prefix LESS_THAN type_decl GREATER_THAN
|
|
|
|
// | texture_and_sampler_types
|
|
|
|
Maybe<const ast::Type*> ParserImpl::type_decl_without_ident() {
|
|
|
|
auto& t = peek();
|
|
|
|
|
|
|
|
if (match(Token::Type::kBool)) {
|
|
|
|
return builder_.ty.bool_(t.source());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (match(Token::Type::kF16)) {
|
|
|
|
return builder_.ty.f16(t.source());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (match(Token::Type::kF32)) {
|
|
|
|
return builder_.ty.f32(t.source());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (match(Token::Type::kI32)) {
|
|
|
|
return builder_.ty.i32(t.source());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (match(Token::Type::kU32)) {
|
|
|
|
return builder_.ty.u32(t.source());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (t.Is(Token::Type::kArray) && peek_is(Token::Type::kLessThan, 1)) {
|
|
|
|
if (match(Token::Type::kArray)) {
|
|
|
|
return expect_type_decl_array(t.source());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (match(Token::Type::kAtomic)) {
|
|
|
|
return expect_type_decl_atomic(t.source());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (match(Token::Type::kPtr)) {
|
|
|
|
return expect_type_decl_pointer(t.source());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (t.IsMatrix() && peek_is(Token::Type::kLessThan, 1)) {
|
|
|
|
auto mat = mat_prefix();
|
|
|
|
if (mat.matched) {
|
|
|
|
return expect_type_decl_matrix(t.source(), mat.value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (t.IsVector() && peek_is(Token::Type::kLessThan, 1)) {
|
|
|
|
auto vec = vec_prefix();
|
|
|
|
if (vec.matched) {
|
|
|
|
return expect_type_decl_vector(t.source(), vec.value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
auto texture_or_sampler = texture_and_sampler_types();
|
|
|
|
if (texture_or_sampler.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (texture_or_sampler.matched) {
|
|
|
|
return texture_or_sampler;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Failure::kNoMatch;
|
|
|
|
}
|
|
|
|
|
2020-03-02 12:47:43 -08:00
|
|
|
// type_decl
|
|
|
|
// : IDENTIFIER
|
2022-08-22 12:42:14 -07:00
|
|
|
// | type_decl_without_ident
|
2021-10-19 11:38:54 -07:00
|
|
|
Maybe<const ast::Type*> ParserImpl::type_decl() {
|
2022-07-25 09:43:08 -07:00
|
|
|
auto& t = peek();
|
2022-05-01 07:40:55 -07:00
|
|
|
Source source;
|
|
|
|
if (match(Token::Type::kIdentifier, &source)) {
|
|
|
|
return builder_.create<ast::TypeName>(source, builder_.Symbols().Register(t.to_str()));
|
|
|
|
}
|
2020-11-09 11:19:44 -08:00
|
|
|
|
2022-08-22 12:42:14 -07:00
|
|
|
return type_decl_without_ident();
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2022-01-25 09:15:37 -08:00
|
|
|
Expect<const ast::Type*> ParserImpl::expect_type(std::string_view use) {
|
2022-05-01 07:40:55 -07:00
|
|
|
auto type = type_decl();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (type.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (!type.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return add_error(peek().source(), "invalid type", use);
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-05-01 07:40:55 -07:00
|
|
|
return type.value;
|
2020-11-11 06:10:25 -08:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-08-22 09:15:04 -07:00
|
|
|
// LESS_THAN address_space COMMA type_decl ( COMMA access_mode )? GREATER_THAN
|
|
|
|
Expect<const ast::Type*> ParserImpl::expect_type_decl_pointer(const Source& s) {
|
|
|
|
const char* use = "ptr declaration";
|
|
|
|
|
|
|
|
auto storage_class = ast::StorageClass::kNone;
|
|
|
|
auto access = ast::Access::kUndefined;
|
|
|
|
|
|
|
|
auto subtype = expect_lt_gt_block(use, [&]() -> Expect<const ast::Type*> {
|
|
|
|
auto sc = expect_address_space(use);
|
|
|
|
if (sc.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
storage_class = sc.value;
|
|
|
|
|
|
|
|
if (!expect(use, Token::Type::kComma)) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto type = expect_type(use);
|
|
|
|
if (type.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (match(Token::Type::kComma)) {
|
|
|
|
auto ac = expect_access_mode("access control");
|
|
|
|
if (ac.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
access = ac.value;
|
|
|
|
}
|
|
|
|
|
|
|
|
return type.value;
|
|
|
|
});
|
|
|
|
|
|
|
|
if (subtype.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
|
|
|
|
return builder_.ty.pointer(make_source_range_from(s), subtype.value, storage_class, access);
|
|
|
|
}
|
|
|
|
|
|
|
|
// LESS_THAN type_decl GREATER_THAN
|
|
|
|
Expect<const ast::Type*> ParserImpl::expect_type_decl_atomic(const Source& s) {
|
|
|
|
const char* use = "atomic declaration";
|
|
|
|
|
|
|
|
auto subtype = expect_lt_gt_block(use, [&] { return expect_type(use); });
|
|
|
|
if (subtype.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
|
|
|
|
return builder_.ty.atomic(make_source_range_from(s), subtype.value);
|
|
|
|
}
|
|
|
|
|
|
|
|
// LESS_THAN type_decl GREATER_THAN
|
|
|
|
Expect<const ast::Type*> ParserImpl::expect_type_decl_vector(const Source& s, uint32_t count) {
|
|
|
|
const char* use = "vector";
|
|
|
|
auto ty = expect_lt_gt_block(use, [&] { return expect_type(use); });
|
|
|
|
if (ty.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
|
|
|
|
return builder_.ty.vec(make_source_range_from(s), ty.value, count);
|
|
|
|
}
|
|
|
|
|
|
|
|
// LESS_THAN type_decl ( COMMA element_count_expression )? GREATER_THAN
|
|
|
|
Expect<const ast::Type*> ParserImpl::expect_type_decl_array(const Source& s) {
|
|
|
|
const char* use = "array declaration";
|
|
|
|
|
|
|
|
struct TypeAndSize {
|
|
|
|
const ast::Type* type = nullptr;
|
|
|
|
const ast::Expression* size = nullptr;
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!peek_is(Token::Type::kLessThan)) {
|
|
|
|
return add_error(peek(), "expected < for array");
|
|
|
|
}
|
|
|
|
|
|
|
|
auto type_size = expect_lt_gt_block(use, [&]() -> Expect<TypeAndSize> {
|
|
|
|
auto type = expect_type(use);
|
|
|
|
if (type.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!match(Token::Type::kComma)) {
|
|
|
|
return TypeAndSize{type.value, nullptr};
|
|
|
|
}
|
|
|
|
|
|
|
|
auto size = element_count_expression();
|
|
|
|
if (size.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (!size.matched) {
|
|
|
|
return add_error(peek(), "expected array size expression");
|
|
|
|
}
|
|
|
|
|
|
|
|
return TypeAndSize{type.value, size.value};
|
|
|
|
});
|
|
|
|
|
|
|
|
if (type_size.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
|
|
|
|
return builder_.ty.array(make_source_range_from(s), type_size->type, type_size->size);
|
|
|
|
}
|
|
|
|
|
|
|
|
// LESS_THAN type_decl GREATER_THAN
|
|
|
|
Expect<const ast::Type*> ParserImpl::expect_type_decl_matrix(const Source& s,
|
|
|
|
const MatrixDimensions& dims) {
|
|
|
|
const char* use = "matrix";
|
|
|
|
auto ty = expect_lt_gt_block(use, [&] { return expect_type(use); });
|
|
|
|
if (ty.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
|
|
|
|
return builder_.ty.mat(make_source_range_from(s), ty.value, dims.columns, dims.rows);
|
|
|
|
}
|
|
|
|
|
2022-08-05 07:47:57 -07:00
|
|
|
// address_space
|
|
|
|
// : 'function'
|
|
|
|
// | 'private'
|
|
|
|
// | 'workgroup'
|
|
|
|
// | 'uniform'
|
|
|
|
// | 'storage'
|
|
|
|
//
|
|
|
|
// Note, we also parse `push_constant` from the experimental extension
|
|
|
|
Expect<ast::StorageClass> ParserImpl::expect_address_space(std::string_view use) {
|
2022-07-25 17:28:37 -07:00
|
|
|
auto& t = peek();
|
2022-07-22 09:09:55 -07:00
|
|
|
auto ident = expect_ident("storage class");
|
|
|
|
if (ident.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
2021-02-04 10:26:00 -08:00
|
|
|
|
2022-07-26 15:51:36 -07:00
|
|
|
auto storage_class = ast::ParseStorageClass(ident.value);
|
|
|
|
if (storage_class == ast::StorageClass::kInvalid) {
|
|
|
|
return add_error(t.source(), "invalid storage class", use);
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-09 10:45:53 -08:00
|
|
|
|
2022-07-26 15:51:36 -07:00
|
|
|
return {storage_class, t.source()};
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// struct_decl
|
2022-03-21 09:09:17 -07:00
|
|
|
// : STRUCT IDENT struct_body_decl
|
|
|
|
Maybe<const ast::Struct*> ParserImpl::struct_decl() {
|
2022-07-25 09:43:08 -07:00
|
|
|
auto& t = peek();
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-19 13:08:19 -07:00
|
|
|
if (!match(Token::Type::kStruct)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-04 12:55:31 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
auto name = expect_ident("struct declaration");
|
2022-05-19 13:08:19 -07:00
|
|
|
if (name.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
auto body = expect_struct_body_decl();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (body.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
auto sym = builder_.Symbols().Register(name.value);
|
2022-08-02 10:03:35 -07:00
|
|
|
return create<ast::Struct>(t.source(), sym, std::move(body.value), utils::Empty);
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// struct_body_decl
|
2022-03-28 07:31:22 -07:00
|
|
|
// : BRACE_LEFT (struct_member COMMA)* struct_member COMMA? BRACE_RIGHT
|
2022-08-02 10:03:35 -07:00
|
|
|
Expect<ParserImpl::StructMemberList> ParserImpl::expect_struct_body_decl() {
|
|
|
|
return expect_brace_block("struct declaration", [&]() -> Expect<StructMemberList> {
|
|
|
|
StructMemberList members;
|
2022-03-28 07:31:22 -07:00
|
|
|
bool errored = false;
|
|
|
|
while (continue_parsing()) {
|
2022-05-01 07:40:55 -07:00
|
|
|
// Check for the end of the list.
|
2022-07-25 09:43:08 -07:00
|
|
|
auto& t = peek();
|
2022-05-01 07:40:55 -07:00
|
|
|
if (!t.IsIdentifier() && !t.Is(Token::Type::kAttr)) {
|
|
|
|
break;
|
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
auto member = expect_struct_member();
|
|
|
|
if (member.errored) {
|
|
|
|
errored = true;
|
|
|
|
if (!sync_to(Token::Type::kComma, /* consume: */ false)) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
} else {
|
2022-08-02 10:03:35 -07:00
|
|
|
members.Push(member.value);
|
2022-03-28 07:31:22 -07:00
|
|
|
}
|
2022-05-01 07:40:55 -07:00
|
|
|
|
2022-05-19 13:08:19 -07:00
|
|
|
if (!match(Token::Type::kComma)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
break;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-03-28 07:31:22 -07:00
|
|
|
}
|
|
|
|
if (errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-03-28 07:31:22 -07:00
|
|
|
}
|
2020-11-09 11:39:34 -08:00
|
|
|
return members;
|
2022-05-01 07:40:55 -07:00
|
|
|
});
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// struct_member
|
2022-08-17 10:38:17 -07:00
|
|
|
// : attribute* ident_with_type_decl
|
2022-03-28 07:31:22 -07:00
|
|
|
Expect<ast::StructMember*> ParserImpl::expect_struct_member() {
|
2022-05-01 07:40:55 -07:00
|
|
|
auto attrs = attribute_list();
|
|
|
|
if (attrs.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-08-17 10:38:17 -07:00
|
|
|
auto decl = expect_ident_with_type_decl("struct member");
|
2022-05-19 13:08:19 -07:00
|
|
|
if (decl.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return create<ast::StructMember>(decl->source, builder_.Symbols().Register(decl->name),
|
|
|
|
decl->type, std::move(attrs.value));
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2022-08-09 09:52:42 -07:00
|
|
|
// static_assert_statement
|
2022-08-04 14:06:07 -07:00
|
|
|
// : STATIC_ASSERT expression
|
2022-08-05 07:53:47 -07:00
|
|
|
Maybe<const ast::StaticAssert*> ParserImpl::static_assert_statement() {
|
2022-08-03 03:08:46 -07:00
|
|
|
Source start;
|
|
|
|
if (!match(Token::Type::kStaticAssert, &start)) {
|
|
|
|
return Failure::kNoMatch;
|
|
|
|
}
|
|
|
|
|
2022-08-24 12:11:55 -07:00
|
|
|
auto condition = expression();
|
2022-08-03 03:08:46 -07:00
|
|
|
if (condition.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (!condition.matched) {
|
|
|
|
return add_error(peek(), "unable to parse condition expression");
|
|
|
|
}
|
|
|
|
|
|
|
|
Source source = make_source_range_from(start);
|
|
|
|
return create<ast::StaticAssert>(source, condition.value);
|
|
|
|
}
|
|
|
|
|
2020-03-02 12:47:43 -08:00
|
|
|
// function_decl
|
2022-08-05 07:53:47 -07:00
|
|
|
// : function_header compound_statement
|
2022-08-02 10:03:35 -07:00
|
|
|
Maybe<const ast::Function*> ParserImpl::function_decl(AttributeList& attrs) {
|
2022-05-01 07:40:55 -07:00
|
|
|
auto header = function_header();
|
|
|
|
if (header.errored) {
|
|
|
|
if (sync_to(Token::Type::kBraceLeft, /* consume: */ false)) {
|
|
|
|
// There were errors in the function header, but the parser has managed to
|
|
|
|
// resynchronize with the opening brace. As there's no outer
|
|
|
|
// synchronization token for function declarations, attempt to parse the
|
|
|
|
// function body. The AST isn't used as we've already errored, but this
|
|
|
|
// catches any errors inside the body, and can help keep the parser in
|
|
|
|
// sync.
|
2022-08-05 07:53:47 -07:00
|
|
|
expect_compound_statement();
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
|
|
|
return Failure::kErrored;
|
2020-11-11 10:44:36 -08:00
|
|
|
}
|
2022-05-19 13:08:19 -07:00
|
|
|
if (!header.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-04 12:55:31 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
bool errored = false;
|
2020-11-11 10:44:36 -08:00
|
|
|
|
2022-08-05 07:53:47 -07:00
|
|
|
auto body = expect_compound_statement();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (body.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
errored = true;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-11 10:44:36 -08:00
|
|
|
|
2022-05-19 13:08:19 -07:00
|
|
|
if (errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-08-02 10:03:35 -07:00
|
|
|
TINT_DEFER(attrs.Clear());
|
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return create<ast::Function>(header->source, builder_.Symbols().Register(header->name),
|
2022-08-02 10:03:35 -07:00
|
|
|
header->params, header->return_type, body.value, std::move(attrs),
|
2022-05-01 07:40:55 -07:00
|
|
|
header->return_type_attributes);
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// function_header
|
2021-05-27 10:03:27 -07:00
|
|
|
// : FN IDENT PAREN_LEFT param_list PAREN_RIGHT return_type_decl_optional
|
|
|
|
// return_type_decl_optional
|
|
|
|
// :
|
|
|
|
// | ARROW attribute_list* type_decl
|
2020-12-07 12:45:14 -08:00
|
|
|
Maybe<ParserImpl::FunctionHeader> ParserImpl::function_header() {
|
2022-05-01 07:40:55 -07:00
|
|
|
Source source;
|
|
|
|
if (!match(Token::Type::kFn, &source)) {
|
|
|
|
return Failure::kNoMatch;
|
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
const char* use = "function declaration";
|
|
|
|
bool errored = false;
|
2020-11-04 08:34:10 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
auto name = expect_ident(use);
|
|
|
|
if (name.errored) {
|
|
|
|
errored = true;
|
|
|
|
if (!sync_to(Token::Type::kParenLeft, /* consume: */ false)) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
2021-04-08 07:39:47 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
auto params = expect_paren_block(use, [&] { return expect_param_list(); });
|
|
|
|
if (params.errored) {
|
|
|
|
errored = true;
|
|
|
|
if (!synchronized_) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
2021-04-08 07:39:47 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
const ast::Type* return_type = nullptr;
|
2022-08-02 10:03:35 -07:00
|
|
|
AttributeList return_attributes;
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
if (match(Token::Type::kArrow)) {
|
|
|
|
auto attrs = attribute_list();
|
|
|
|
if (attrs.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
return_attributes = attrs.value;
|
2022-02-02 15:07:11 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
auto type = type_decl();
|
|
|
|
if (type.errored) {
|
|
|
|
errored = true;
|
|
|
|
} else if (!type.matched) {
|
|
|
|
return add_error(peek(), "unable to determine function return type");
|
|
|
|
} else {
|
|
|
|
return_type = type.value;
|
|
|
|
}
|
2021-04-08 07:39:47 -07:00
|
|
|
} else {
|
2022-05-01 07:40:55 -07:00
|
|
|
return_type = builder_.ty.void_();
|
2021-04-08 07:39:47 -07:00
|
|
|
}
|
2020-11-11 10:44:36 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
if (errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-08-02 10:03:35 -07:00
|
|
|
return FunctionHeader{
|
|
|
|
source, std::move(name.value), std::move(params.value),
|
|
|
|
return_type, std::move(return_attributes),
|
|
|
|
};
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// param_list
|
|
|
|
// :
|
2021-04-29 08:56:54 -07:00
|
|
|
// | (param COMMA)* param COMMA?
|
2022-08-02 10:03:35 -07:00
|
|
|
Expect<ParserImpl::ParameterList> ParserImpl::expect_param_list() {
|
|
|
|
ParameterList ret;
|
2022-05-01 07:40:55 -07:00
|
|
|
while (continue_parsing()) {
|
|
|
|
// Check for the end of the list.
|
2022-07-25 09:43:08 -07:00
|
|
|
auto& t = peek();
|
2022-05-01 07:40:55 -07:00
|
|
|
if (!t.IsIdentifier() && !t.Is(Token::Type::kAttr)) {
|
|
|
|
break;
|
|
|
|
}
|
2021-04-29 08:56:54 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
auto param = expect_param();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (param.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-08-02 10:03:35 -07:00
|
|
|
ret.Push(param.value);
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-19 13:08:19 -07:00
|
|
|
if (!match(Token::Type::kComma)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
break;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return ret;
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2021-03-10 15:18:29 -08:00
|
|
|
// param
|
2022-08-19 15:07:23 -07:00
|
|
|
// : attribute_list* ident COLON type_decl
|
2022-06-17 05:48:51 -07:00
|
|
|
Expect<ast::Parameter*> ParserImpl::expect_param() {
|
2022-05-01 07:40:55 -07:00
|
|
|
auto attrs = attribute_list();
|
2021-03-10 15:18:29 -08:00
|
|
|
|
2022-08-17 10:38:17 -07:00
|
|
|
auto decl = expect_ident_with_type_decl("parameter");
|
2022-05-19 13:08:19 -07:00
|
|
|
if (decl.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2021-03-10 15:18:29 -08:00
|
|
|
|
2022-06-17 05:48:51 -07:00
|
|
|
return create<ast::Parameter>(decl->source, // source
|
|
|
|
builder_.Symbols().Register(decl->name), // symbol
|
|
|
|
decl->type, // type
|
|
|
|
std::move(attrs.value)); // attributes
|
2021-03-10 15:18:29 -08:00
|
|
|
}
|
|
|
|
|
2020-03-02 12:47:43 -08:00
|
|
|
// pipeline_stage
|
|
|
|
// : VERTEX
|
|
|
|
// | FRAGMENT
|
|
|
|
// | COMPUTE
|
2022-08-05 07:47:57 -07:00
|
|
|
//
|
|
|
|
// TODO(crbug.com/tint/1503): Remove when deprecation period is over.
|
2020-11-09 11:39:34 -08:00
|
|
|
Expect<ast::PipelineStage> ParserImpl::expect_pipeline_stage() {
|
2022-07-25 09:43:08 -07:00
|
|
|
auto& t = peek();
|
2022-08-05 07:47:57 -07:00
|
|
|
if (t == "vertex") {
|
2022-05-01 07:40:55 -07:00
|
|
|
next(); // Consume the peek
|
|
|
|
return {ast::PipelineStage::kVertex, t.source()};
|
|
|
|
}
|
2022-08-05 07:47:57 -07:00
|
|
|
if (t == "fragment") {
|
2022-05-01 07:40:55 -07:00
|
|
|
next(); // Consume the peek
|
|
|
|
return {ast::PipelineStage::kFragment, t.source()};
|
|
|
|
}
|
2022-08-05 07:47:57 -07:00
|
|
|
if (t == "compute") {
|
2022-05-01 07:40:55 -07:00
|
|
|
next(); // Consume the peek
|
|
|
|
return {ast::PipelineStage::kCompute, t.source()};
|
|
|
|
}
|
|
|
|
return add_error(peek(), "invalid value for stage attribute");
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2022-08-04 14:21:37 -07:00
|
|
|
// interpolation_sample_name
|
|
|
|
// : 'center'
|
|
|
|
// | 'centroid'
|
|
|
|
// | 'sample'
|
|
|
|
Expect<ast::InterpolationSampling> ParserImpl::expect_interpolation_sample_name() {
|
|
|
|
auto ident = expect_ident("interpolation sample name");
|
|
|
|
if (ident.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ident.value == "center") {
|
|
|
|
return {ast::InterpolationSampling::kCenter, ident.source};
|
|
|
|
}
|
|
|
|
if (ident.value == "centroid") {
|
|
|
|
return {ast::InterpolationSampling::kCentroid, ident.source};
|
|
|
|
}
|
|
|
|
if (ident.value == "sample") {
|
|
|
|
return {ast::InterpolationSampling::kSample, ident.source};
|
|
|
|
}
|
|
|
|
return add_error(ident.source, "invalid interpolation sampling");
|
|
|
|
}
|
|
|
|
|
|
|
|
// interpolation_type_name
|
|
|
|
// : 'perspective'
|
|
|
|
// | 'linear'
|
|
|
|
// | 'flat'
|
|
|
|
Expect<ast::InterpolationType> ParserImpl::expect_interpolation_type_name() {
|
|
|
|
auto ident = expect_ident("interpolation type name");
|
|
|
|
if (ident.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ident.value == "perspective") {
|
|
|
|
return {ast::InterpolationType::kPerspective, ident.source};
|
|
|
|
}
|
|
|
|
if (ident.value == "linear") {
|
|
|
|
return {ast::InterpolationType::kLinear, ident.source};
|
|
|
|
}
|
|
|
|
if (ident.value == "flat") {
|
|
|
|
return {ast::InterpolationType::kFlat, ident.source};
|
|
|
|
}
|
|
|
|
|
|
|
|
return add_error(ident.source, "invalid interpolation type");
|
|
|
|
}
|
|
|
|
|
|
|
|
// builtin_value_name
|
2022-08-17 07:28:31 -07:00
|
|
|
// : frag_depth
|
|
|
|
// | front_facing
|
|
|
|
// | global_invocation_id
|
|
|
|
// | instance_index
|
|
|
|
// | local_invocation_id
|
|
|
|
// | local_invocation_index
|
|
|
|
// | num_workgroups
|
|
|
|
// | position
|
|
|
|
// | sample_index
|
|
|
|
// | sample_mask
|
|
|
|
// | vertex_index
|
|
|
|
// | workgroup_id
|
2022-07-27 11:48:06 -07:00
|
|
|
Expect<ast::BuiltinValue> ParserImpl::expect_builtin() {
|
2022-05-01 07:40:55 -07:00
|
|
|
auto ident = expect_ident("builtin");
|
2022-05-19 13:08:19 -07:00
|
|
|
if (ident.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-04 12:06:21 -08:00
|
|
|
|
2022-07-27 15:21:30 -07:00
|
|
|
ast::BuiltinValue builtin = ast::ParseBuiltinValue(ident.value);
|
|
|
|
if (builtin == ast::BuiltinValue::kInvalid) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return add_error(ident.source, "invalid value for builtin attribute");
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-04 12:06:21 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return {builtin, ident.source};
|
2020-11-04 12:06:21 -08:00
|
|
|
}
|
|
|
|
|
2022-08-05 07:53:47 -07:00
|
|
|
// compound_statement
|
|
|
|
// : BRACE_LEFT statement* BRACE_RIGHT
|
|
|
|
Expect<ast::BlockStatement*> ParserImpl::expect_compound_statement() {
|
2022-05-01 07:40:55 -07:00
|
|
|
return expect_brace_block("", [&]() -> Expect<ast::BlockStatement*> {
|
|
|
|
auto stmts = expect_statements();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (stmts.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-05-01 07:40:55 -07:00
|
|
|
return create<ast::BlockStatement>(Source{}, stmts.value);
|
|
|
|
});
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2022-07-22 09:07:36 -07:00
|
|
|
// paren_expression
|
2022-08-04 14:06:07 -07:00
|
|
|
// : PAREN_LEFT expression PAREN_RIGHT
|
2022-07-22 09:07:36 -07:00
|
|
|
Expect<const ast::Expression*> ParserImpl::expect_paren_expression() {
|
2022-05-01 07:40:55 -07:00
|
|
|
return expect_paren_block("", [&]() -> Expect<const ast::Expression*> {
|
2022-08-24 12:11:55 -07:00
|
|
|
auto expr = expression();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (expr.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (!expr.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return add_error(peek(), "unable to parse expression");
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-09 11:39:34 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return expr.value;
|
|
|
|
});
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// statements
|
|
|
|
// : statement*
|
2022-08-02 10:03:35 -07:00
|
|
|
Expect<ParserImpl::StatementList> ParserImpl::expect_statements() {
|
2022-05-01 07:40:55 -07:00
|
|
|
bool errored = false;
|
2022-08-02 10:03:35 -07:00
|
|
|
StatementList stmts;
|
2022-05-01 07:40:55 -07:00
|
|
|
|
|
|
|
while (continue_parsing()) {
|
|
|
|
auto stmt = statement();
|
|
|
|
if (stmt.errored) {
|
|
|
|
errored = true;
|
|
|
|
} else if (stmt.matched) {
|
2022-08-02 10:03:35 -07:00
|
|
|
stmts.Push(stmt.value);
|
2022-05-01 07:40:55 -07:00
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
2020-11-11 10:44:36 -08:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-19 13:08:19 -07:00
|
|
|
if (errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-11 10:44:36 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return stmts;
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// statement
|
|
|
|
// : SEMICOLON
|
2022-08-05 07:53:47 -07:00
|
|
|
// | if_statement
|
|
|
|
// | switch_statement
|
|
|
|
// | loop_statement
|
|
|
|
// | for_statement
|
|
|
|
// | while_statement
|
|
|
|
// | compound_statement
|
|
|
|
// | non_block_statement // Note, we inject an extra rule in here for simpler parsing
|
2021-10-19 11:38:54 -07:00
|
|
|
Maybe<const ast::Statement*> ParserImpl::statement() {
|
2022-05-01 07:40:55 -07:00
|
|
|
while (match(Token::Type::kSemicolon)) {
|
|
|
|
// Skip empty statements
|
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-08-03 03:08:46 -07:00
|
|
|
// Non-block statements that error can resynchronize on semicolon.
|
2022-05-01 07:40:55 -07:00
|
|
|
auto stmt = sync(Token::Type::kSemicolon, [&] { return non_block_statement(); });
|
2022-05-19 13:08:19 -07:00
|
|
|
if (stmt.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (stmt.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return stmt;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-08-05 07:53:47 -07:00
|
|
|
auto stmt_if = if_statement();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (stmt_if.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (stmt_if.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return stmt_if.value;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-08-05 07:53:47 -07:00
|
|
|
auto sw = switch_statement();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (sw.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (sw.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return sw.value;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-08-05 07:53:47 -07:00
|
|
|
auto loop = loop_statement();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (loop.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (loop.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return loop.value;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-08-05 07:53:47 -07:00
|
|
|
auto stmt_for = for_statement();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (stmt_for.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (stmt_for.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return stmt_for.value;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-08-24 11:22:22 -07:00
|
|
|
|
2022-08-05 07:53:47 -07:00
|
|
|
auto stmt_while = while_statement();
|
2022-06-16 05:01:27 -07:00
|
|
|
if (stmt_while.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (stmt_while.matched) {
|
|
|
|
return stmt_while.value;
|
|
|
|
}
|
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
if (peek_is(Token::Type::kBraceLeft)) {
|
2022-08-05 07:53:47 -07:00
|
|
|
auto body = expect_compound_statement();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (body.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-05-01 07:40:55 -07:00
|
|
|
return body.value;
|
|
|
|
}
|
2020-07-21 08:13:36 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
2020-11-11 06:11:55 -08:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-08-05 07:53:47 -07:00
|
|
|
// non_block_statement (continued)
|
|
|
|
// : return_statement SEMICOLON
|
|
|
|
// | func_call_statement SEMICOLON
|
|
|
|
// | variable_statement SEMICOLON
|
|
|
|
// | break_statement SEMICOLON
|
|
|
|
// | continue_statement SEMICOLON
|
2020-11-11 06:11:55 -08:00
|
|
|
// | DISCARD SEMICOLON
|
2022-08-17 10:20:50 -07:00
|
|
|
// | variable_updating_statement SEMICOLON
|
2022-08-05 07:53:47 -07:00
|
|
|
// | static_assert_statement SEMICOLON
|
2021-10-19 11:38:54 -07:00
|
|
|
Maybe<const ast::Statement*> ParserImpl::non_block_statement() {
|
2022-05-01 07:40:55 -07:00
|
|
|
auto stmt = [&]() -> Maybe<const ast::Statement*> {
|
2022-08-05 07:53:47 -07:00
|
|
|
auto ret_stmt = return_statement();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (ret_stmt.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (ret_stmt.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return ret_stmt.value;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-04 06:17:51 -08:00
|
|
|
|
2022-08-05 07:53:47 -07:00
|
|
|
auto func = func_call_statement();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (func.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (func.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return func.value;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-07-25 07:33:13 -07:00
|
|
|
|
2022-08-05 07:53:47 -07:00
|
|
|
auto var = variable_statement();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (var.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (var.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return var.value;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-04 06:17:51 -08:00
|
|
|
|
2022-08-05 07:53:47 -07:00
|
|
|
auto b = break_statement();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (b.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (b.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return b.value;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-07-25 07:33:13 -07:00
|
|
|
|
2022-08-05 07:53:47 -07:00
|
|
|
auto cont = continue_statement();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (cont.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (cont.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return cont.value;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-04 06:17:51 -08:00
|
|
|
|
2022-08-05 07:53:47 -07:00
|
|
|
Source source;
|
|
|
|
if (match(Token::Type::kDiscard, &source)) {
|
|
|
|
return create<ast::DiscardStatement>(source);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note, this covers assignment, increment and decrement
|
2022-08-17 10:20:50 -07:00
|
|
|
auto assign = variable_updating_statement();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (assign.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (assign.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return assign.value;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-08-05 07:53:47 -07:00
|
|
|
auto stmt_static_assert = static_assert_statement();
|
2022-08-03 03:08:46 -07:00
|
|
|
if (stmt_static_assert.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (stmt_static_assert.matched) {
|
|
|
|
return stmt_static_assert.value;
|
|
|
|
}
|
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
|
|
|
}();
|
|
|
|
|
2022-05-19 13:08:19 -07:00
|
|
|
if (stmt.matched && !expect(stmt->Name(), Token::Type::kSemicolon)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-05-01 07:40:55 -07:00
|
|
|
return stmt;
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2022-08-05 07:53:47 -07:00
|
|
|
// return_statement
|
2022-08-04 14:06:07 -07:00
|
|
|
// : RETURN expression?
|
2022-08-05 07:53:47 -07:00
|
|
|
Maybe<const ast::ReturnStatement*> ParserImpl::return_statement() {
|
2022-05-01 07:40:55 -07:00
|
|
|
Source source;
|
2022-05-19 13:08:19 -07:00
|
|
|
if (!match(Token::Type::kReturn, &source)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-19 13:08:19 -07:00
|
|
|
if (peek_is(Token::Type::kSemicolon)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return create<ast::ReturnStatement>(source, nullptr);
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-09 11:52:24 -08:00
|
|
|
|
2022-08-24 12:11:55 -07:00
|
|
|
auto expr = expression();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (expr.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-11 10:44:36 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
// TODO(bclayton): Check matched?
|
|
|
|
return create<ast::ReturnStatement>(source, expr.value);
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2022-08-05 07:53:47 -07:00
|
|
|
// variable_statement
|
2020-03-02 12:47:43 -08:00
|
|
|
// : variable_decl
|
2022-08-04 14:06:07 -07:00
|
|
|
// | variable_decl EQUAL expression
|
2022-08-17 10:38:17 -07:00
|
|
|
// | LET optionally_typed_ident EQUAL expression
|
|
|
|
// | CONST optionally_typed_ident EQUAL expression
|
2022-08-05 07:53:47 -07:00
|
|
|
Maybe<const ast::VariableDeclStatement*> ParserImpl::variable_statement() {
|
2022-06-28 05:53:56 -07:00
|
|
|
if (match(Token::Type::kConst)) {
|
2022-08-17 10:38:17 -07:00
|
|
|
auto decl = expect_optionally_typed_ident("'const' declaration");
|
2022-06-26 03:52:50 -07:00
|
|
|
if (decl.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!expect("'const' declaration", Token::Type::kEqual)) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
|
2022-08-24 12:11:55 -07:00
|
|
|
auto initializer = expression();
|
2022-07-22 09:08:35 -07:00
|
|
|
if (initializer.errored) {
|
2022-06-26 03:52:50 -07:00
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
2022-07-22 09:08:35 -07:00
|
|
|
if (!initializer.matched) {
|
|
|
|
return add_error(peek(), "missing initializer for 'const' declaration");
|
2022-06-26 03:52:50 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
auto* const_ = create<ast::Const>(decl->source, // source
|
|
|
|
builder_.Symbols().Register(decl->name), // symbol
|
|
|
|
decl->type, // type
|
2022-07-22 09:08:35 -07:00
|
|
|
initializer.value, // initializer
|
2022-08-02 10:03:35 -07:00
|
|
|
utils::Empty); // attributes
|
2022-06-26 03:52:50 -07:00
|
|
|
|
|
|
|
return create<ast::VariableDeclStatement>(decl->source, const_);
|
|
|
|
}
|
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
if (match(Token::Type::kLet)) {
|
2022-08-17 10:38:17 -07:00
|
|
|
auto decl = expect_optionally_typed_ident("'let' declaration");
|
2022-05-19 13:08:19 -07:00
|
|
|
if (decl.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-05-01 07:40:55 -07:00
|
|
|
|
2022-06-16 01:36:30 -07:00
|
|
|
if (!expect("'let' declaration", Token::Type::kEqual)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-05-01 07:40:55 -07:00
|
|
|
|
2022-08-24 12:11:55 -07:00
|
|
|
auto initializer = expression();
|
2022-07-22 09:08:35 -07:00
|
|
|
if (initializer.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-07-22 09:08:35 -07:00
|
|
|
if (!initializer.matched) {
|
|
|
|
return add_error(peek(), "missing initializer for 'let' declaration");
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-05-01 07:40:55 -07:00
|
|
|
|
2022-06-17 05:48:51 -07:00
|
|
|
auto* let = create<ast::Let>(decl->source, // source
|
|
|
|
builder_.Symbols().Register(decl->name), // symbol
|
|
|
|
decl->type, // type
|
2022-07-22 09:08:35 -07:00
|
|
|
initializer.value, // initializer
|
2022-08-02 10:03:35 -07:00
|
|
|
utils::Empty); // attributes
|
2022-05-01 07:40:55 -07:00
|
|
|
|
2022-06-17 05:48:51 -07:00
|
|
|
return create<ast::VariableDeclStatement>(decl->source, let);
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
|
|
|
|
2022-06-28 05:53:56 -07:00
|
|
|
auto decl = variable_decl();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (decl.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (!decl.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-07-22 09:08:35 -07:00
|
|
|
const ast::Expression* initializer = nullptr;
|
2022-05-01 07:40:55 -07:00
|
|
|
if (match(Token::Type::kEqual)) {
|
2022-08-24 12:11:55 -07:00
|
|
|
auto initializer_expr = expression();
|
2022-07-22 09:08:35 -07:00
|
|
|
if (initializer_expr.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-07-22 09:08:35 -07:00
|
|
|
if (!initializer_expr.matched) {
|
|
|
|
return add_error(peek(), "missing initializer for 'var' declaration");
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-09 11:52:24 -08:00
|
|
|
|
2022-07-22 09:08:35 -07:00
|
|
|
initializer = initializer_expr.value;
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-06-17 05:48:51 -07:00
|
|
|
auto* var = create<ast::Var>(decl->source, // source
|
|
|
|
builder_.Symbols().Register(decl->name), // symbol
|
|
|
|
decl->type, // type
|
|
|
|
decl->storage_class, // storage class
|
|
|
|
decl->access, // access control
|
2022-07-22 09:08:35 -07:00
|
|
|
initializer, // initializer
|
2022-08-02 10:03:35 -07:00
|
|
|
utils::Empty); // attributes
|
2020-12-11 05:07:02 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return create<ast::VariableDeclStatement>(var->source, var);
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2022-08-05 07:53:47 -07:00
|
|
|
// if_statement
|
2022-04-28 17:14:53 -07:00
|
|
|
// : IF expression compound_stmt ( ELSE else_stmt ) ?
|
|
|
|
// else_stmt
|
2022-08-05 07:53:47 -07:00
|
|
|
// : compound_statement
|
|
|
|
// | if_statement
|
|
|
|
Maybe<const ast::IfStatement*> ParserImpl::if_statement() {
|
2022-05-01 07:40:55 -07:00
|
|
|
// Parse if-else chains iteratively instead of recursively, to avoid
|
|
|
|
// stack-overflow for long chains of if-else statements.
|
|
|
|
|
|
|
|
struct IfInfo {
|
|
|
|
Source source;
|
|
|
|
const ast::Expression* condition;
|
|
|
|
const ast::BlockStatement* body;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Parse an if statement, capturing the source, condition, and body statement.
|
|
|
|
auto parse_if = [&]() -> Maybe<IfInfo> {
|
|
|
|
Source source;
|
|
|
|
if (!match(Token::Type::kIf, &source)) {
|
|
|
|
return Failure::kNoMatch;
|
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-08-24 12:11:55 -07:00
|
|
|
auto condition = expression();
|
2022-05-01 07:40:55 -07:00
|
|
|
if (condition.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (!condition.matched) {
|
|
|
|
return add_error(peek(), "unable to parse condition expression");
|
|
|
|
}
|
2022-04-28 17:14:53 -07:00
|
|
|
|
2022-08-05 07:53:47 -07:00
|
|
|
auto body = expect_compound_statement();
|
2022-05-01 07:40:55 -07:00
|
|
|
if (body.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
2022-04-28 17:14:53 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return IfInfo{source, condition.value, body.value};
|
|
|
|
};
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
std::vector<IfInfo> statements;
|
2022-04-28 17:14:53 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
// Parse the first if statement.
|
|
|
|
auto first_if = parse_if();
|
|
|
|
if (first_if.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
} else if (!first_if.matched) {
|
|
|
|
return Failure::kNoMatch;
|
|
|
|
}
|
|
|
|
statements.push_back(first_if.value);
|
2022-04-28 17:14:53 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
// Parse the components of every "else {if}" in the chain.
|
|
|
|
const ast::Statement* last_stmt = nullptr;
|
|
|
|
while (continue_parsing()) {
|
|
|
|
if (!match(Token::Type::kElse)) {
|
|
|
|
break;
|
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
// Try to parse an "else if".
|
|
|
|
auto else_if = parse_if();
|
|
|
|
if (else_if.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
} else if (else_if.matched) {
|
|
|
|
statements.push_back(else_if.value);
|
|
|
|
continue;
|
|
|
|
}
|
2022-01-04 12:06:49 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
// If it wasn't an "else if", it must just be an "else".
|
2022-08-05 07:53:47 -07:00
|
|
|
auto else_body = expect_compound_statement();
|
2022-05-01 07:40:55 -07:00
|
|
|
if (else_body.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
last_stmt = else_body.value;
|
|
|
|
break;
|
2022-01-04 12:06:49 -08:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
// Now walk back through the statements to create their AST nodes.
|
|
|
|
for (auto itr = statements.rbegin(); itr != statements.rend(); itr++) {
|
|
|
|
last_stmt = create<ast::IfStatement>(itr->source, itr->condition, itr->body, last_stmt);
|
2022-01-04 12:06:49 -08:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return last_stmt->As<ast::IfStatement>();
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2022-08-05 07:53:47 -07:00
|
|
|
// switch_statement
|
2022-08-04 14:06:07 -07:00
|
|
|
// : SWITCH expression BRACKET_LEFT switch_body+ BRACKET_RIGHT
|
2022-08-05 07:53:47 -07:00
|
|
|
Maybe<const ast::SwitchStatement*> ParserImpl::switch_statement() {
|
2022-05-01 07:40:55 -07:00
|
|
|
Source source;
|
2022-05-19 13:08:19 -07:00
|
|
|
if (!match(Token::Type::kSwitch, &source)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-08-24 12:11:55 -07:00
|
|
|
auto condition = expression();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (condition.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-05-01 07:40:55 -07:00
|
|
|
if (!condition.matched) {
|
|
|
|
return add_error(peek(), "unable to parse selector expression");
|
|
|
|
}
|
|
|
|
|
2022-08-02 10:03:35 -07:00
|
|
|
auto body = expect_brace_block("switch statement", [&]() -> Expect<CaseStatementList> {
|
2022-05-01 07:40:55 -07:00
|
|
|
bool errored = false;
|
2022-08-02 10:03:35 -07:00
|
|
|
CaseStatementList list;
|
2022-05-01 07:40:55 -07:00
|
|
|
while (continue_parsing()) {
|
|
|
|
auto stmt = switch_body();
|
|
|
|
if (stmt.errored) {
|
|
|
|
errored = true;
|
|
|
|
continue;
|
|
|
|
}
|
2022-05-19 13:08:19 -07:00
|
|
|
if (!stmt.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
break;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-08-02 10:03:35 -07:00
|
|
|
list.Push(stmt.value);
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
2022-05-19 13:08:19 -07:00
|
|
|
if (errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-05-01 07:40:55 -07:00
|
|
|
return list;
|
|
|
|
});
|
|
|
|
|
2022-05-19 13:08:19 -07:00
|
|
|
if (body.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-04 12:08:51 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return create<ast::SwitchStatement>(source, condition.value, body.value);
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// switch_body
|
2022-04-05 14:01:09 -07:00
|
|
|
// : CASE case_selectors COLON? BRACKET_LEFT case_body BRACKET_RIGHT
|
|
|
|
// | DEFAULT COLON? BRACKET_LEFT case_body BRACKET_RIGHT
|
2021-10-19 11:38:54 -07:00
|
|
|
Maybe<const ast::CaseStatement*> ParserImpl::switch_body() {
|
2022-05-19 13:08:19 -07:00
|
|
|
if (!peek_is(Token::Type::kCase) && !peek_is(Token::Type::kDefault)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-07-25 09:43:08 -07:00
|
|
|
auto& t = next();
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-08-02 10:03:35 -07:00
|
|
|
CaseSelectorList selector_list;
|
2022-05-01 07:40:55 -07:00
|
|
|
if (t.Is(Token::Type::kCase)) {
|
|
|
|
auto selectors = expect_case_selectors();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (selectors.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-09 11:52:24 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
selector_list = std::move(selectors.value);
|
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
// Consume the optional colon if present.
|
|
|
|
match(Token::Type::kColon);
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
const char* use = "case statement";
|
|
|
|
auto body = expect_brace_block(use, [&] { return case_body(); });
|
2020-11-04 12:08:51 -08:00
|
|
|
|
2022-05-19 13:08:19 -07:00
|
|
|
if (body.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (!body.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return add_error(body.source, "expected case body");
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-07-25 17:28:37 -07:00
|
|
|
return create<ast::CaseStatement>(t.source(), selector_list, body.value);
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2020-06-01 09:56:46 -07:00
|
|
|
// case_selectors
|
2021-04-29 08:56:54 -07:00
|
|
|
// : const_literal (COMMA const_literal)* COMMA?
|
2022-08-02 10:03:35 -07:00
|
|
|
Expect<ParserImpl::CaseSelectorList> ParserImpl::expect_case_selectors() {
|
|
|
|
CaseSelectorList selectors;
|
2020-06-01 09:56:46 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
while (continue_parsing()) {
|
|
|
|
auto cond = const_literal();
|
|
|
|
if (cond.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
} else if (!cond.matched) {
|
|
|
|
break;
|
|
|
|
} else if (!cond->Is<ast::IntLiteralExpression>()) {
|
|
|
|
return add_error(cond.value->source, "invalid case selector must be an integer value");
|
|
|
|
}
|
2020-06-01 09:56:46 -07:00
|
|
|
|
2022-08-02 10:03:35 -07:00
|
|
|
selectors.Push(cond.value->As<ast::IntLiteralExpression>());
|
2021-04-29 08:56:54 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
if (!match(Token::Type::kComma)) {
|
|
|
|
break;
|
|
|
|
}
|
2021-04-29 08:56:54 -07:00
|
|
|
}
|
2020-06-01 09:56:46 -07:00
|
|
|
|
2022-08-02 10:03:35 -07:00
|
|
|
if (selectors.IsEmpty()) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return add_error(peek(), "unable to parse case selectors");
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-09 11:39:34 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return selectors;
|
2020-06-01 09:56:46 -07:00
|
|
|
}
|
|
|
|
|
2020-03-02 12:47:43 -08:00
|
|
|
// case_body
|
|
|
|
// :
|
|
|
|
// | statement case_body
|
|
|
|
// | FALLTHROUGH SEMICOLON
|
2021-10-19 11:38:54 -07:00
|
|
|
Maybe<const ast::BlockStatement*> ParserImpl::case_body() {
|
2022-08-02 10:03:35 -07:00
|
|
|
StatementList stmts;
|
2022-05-01 07:40:55 -07:00
|
|
|
while (continue_parsing()) {
|
|
|
|
Source source;
|
|
|
|
if (match(Token::Type::kFallthrough, &source)) {
|
2022-05-19 13:08:19 -07:00
|
|
|
if (!expect("fallthrough statement", Token::Type::kSemicolon)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-08-15 04:41:07 -07:00
|
|
|
deprecated(source,
|
|
|
|
"fallthrough is set to be removed from WGSL. "
|
|
|
|
"Case can accept multiple selectors if the existing case bodies are empty. "
|
|
|
|
"default is not yet supported in a case selector list.");
|
|
|
|
|
2022-08-02 10:03:35 -07:00
|
|
|
stmts.Push(create<ast::FallthroughStatement>(source));
|
2022-05-01 07:40:55 -07:00
|
|
|
break;
|
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
auto stmt = statement();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (stmt.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (!stmt.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
break;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-08-02 10:03:35 -07:00
|
|
|
stmts.Push(stmt.value);
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return create<ast::BlockStatement>(Source{}, stmts);
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2022-08-05 07:53:47 -07:00
|
|
|
// loop_statement
|
2022-08-09 09:52:42 -07:00
|
|
|
// : LOOP BRACKET_LEFT statements continuing_statement? BRACKET_RIGHT
|
2022-08-05 07:53:47 -07:00
|
|
|
Maybe<const ast::LoopStatement*> ParserImpl::loop_statement() {
|
2022-05-01 07:40:55 -07:00
|
|
|
Source source;
|
2022-05-19 13:08:19 -07:00
|
|
|
if (!match(Token::Type::kLoop, &source)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return expect_brace_block("loop", [&]() -> Maybe<const ast::LoopStatement*> {
|
|
|
|
auto stmts = expect_statements();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (stmts.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-08-09 09:52:42 -07:00
|
|
|
auto continuing = continuing_statement();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (continuing.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
auto* body = create<ast::BlockStatement>(source, stmts.value);
|
|
|
|
return create<ast::LoopStatement>(source, body, continuing.value);
|
|
|
|
});
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2021-10-19 11:38:54 -07:00
|
|
|
ForHeader::ForHeader(const ast::Statement* init,
|
|
|
|
const ast::Expression* cond,
|
|
|
|
const ast::Statement* cont)
|
2020-11-16 08:41:47 -08:00
|
|
|
: initializer(init), condition(cond), continuing(cont) {}
|
2020-08-24 11:22:22 -07:00
|
|
|
|
|
|
|
ForHeader::~ForHeader() = default;
|
|
|
|
|
2022-08-17 10:20:50 -07:00
|
|
|
// (variable_statement | variable_updating_statement |
|
2022-08-05 07:53:47 -07:00
|
|
|
// func_call_statement)?
|
2021-10-19 11:38:54 -07:00
|
|
|
Maybe<const ast::Statement*> ParserImpl::for_header_initializer() {
|
2022-08-05 07:53:47 -07:00
|
|
|
auto call = func_call_statement();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (call.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (call.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return call.value;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-09 11:52:24 -08:00
|
|
|
|
2022-08-05 07:53:47 -07:00
|
|
|
auto var = variable_statement();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (var.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (var.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return var.value;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-09 11:52:24 -08:00
|
|
|
|
2022-08-17 10:20:50 -07:00
|
|
|
auto assign = variable_updating_statement();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (assign.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (assign.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return assign.value;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-09 11:52:24 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
2020-11-09 11:52:24 -08:00
|
|
|
}
|
|
|
|
|
2022-08-17 10:20:50 -07:00
|
|
|
// (variable_updating_statement | func_call_statement)?
|
2021-10-19 11:38:54 -07:00
|
|
|
Maybe<const ast::Statement*> ParserImpl::for_header_continuing() {
|
2022-08-05 07:53:47 -07:00
|
|
|
auto call_stmt = func_call_statement();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (call_stmt.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (call_stmt.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return call_stmt.value;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-09 11:52:24 -08:00
|
|
|
|
2022-08-17 10:20:50 -07:00
|
|
|
auto assign = variable_updating_statement();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (assign.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (assign.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return assign.value;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-09 11:52:24 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
2020-11-09 11:52:24 -08:00
|
|
|
}
|
|
|
|
|
2020-08-24 11:22:22 -07:00
|
|
|
// for_header
|
2022-08-17 10:20:50 -07:00
|
|
|
// : (variable_statement | variable_updating_statement | func_call_statement)?
|
2020-08-24 11:22:22 -07:00
|
|
|
// SEMICOLON
|
2022-08-04 14:06:07 -07:00
|
|
|
// expression? SEMICOLON
|
2022-08-17 10:20:50 -07:00
|
|
|
// (variable_updating_statement | func_call_statement)?
|
2020-11-09 11:39:34 -08:00
|
|
|
Expect<std::unique_ptr<ForHeader>> ParserImpl::expect_for_header() {
|
2022-05-01 07:40:55 -07:00
|
|
|
auto initializer = for_header_initializer();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (initializer.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-08-24 11:22:22 -07:00
|
|
|
|
2022-05-19 13:08:19 -07:00
|
|
|
if (!expect("initializer in for loop", Token::Type::kSemicolon)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-08-24 11:22:22 -07:00
|
|
|
|
2022-08-24 12:11:55 -07:00
|
|
|
auto condition = expression();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (condition.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-08-24 11:22:22 -07:00
|
|
|
|
2022-05-19 13:08:19 -07:00
|
|
|
if (!expect("condition in for loop", Token::Type::kSemicolon)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-08-24 11:22:22 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
auto continuing = for_header_continuing();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (continuing.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-08-24 11:22:22 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return std::make_unique<ForHeader>(initializer.value, condition.value, continuing.value);
|
2020-08-24 11:22:22 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// for_statement
|
|
|
|
// : FOR PAREN_LEFT for_header PAREN_RIGHT BRACE_LEFT statements BRACE_RIGHT
|
2022-08-05 07:53:47 -07:00
|
|
|
Maybe<const ast::ForLoopStatement*> ParserImpl::for_statement() {
|
2022-05-01 07:40:55 -07:00
|
|
|
Source source;
|
2022-05-19 13:08:19 -07:00
|
|
|
if (!match(Token::Type::kFor, &source)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-08-24 11:22:22 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
auto header = expect_paren_block("for loop", [&] { return expect_for_header(); });
|
2022-05-19 13:08:19 -07:00
|
|
|
if (header.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-08-24 11:22:22 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
auto stmts = expect_brace_block("for loop", [&] { return expect_statements(); });
|
2022-05-19 13:08:19 -07:00
|
|
|
if (stmts.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-08-24 11:22:22 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return create<ast::ForLoopStatement>(source, header->initializer, header->condition,
|
|
|
|
header->continuing,
|
|
|
|
create<ast::BlockStatement>(stmts.value));
|
2020-08-24 11:22:22 -07:00
|
|
|
}
|
|
|
|
|
2022-06-16 05:01:27 -07:00
|
|
|
// while_statement
|
|
|
|
// : WHILE expression compound_statement
|
2022-08-05 07:53:47 -07:00
|
|
|
Maybe<const ast::WhileStatement*> ParserImpl::while_statement() {
|
2022-06-16 05:01:27 -07:00
|
|
|
Source source;
|
|
|
|
if (!match(Token::Type::kWhile, &source)) {
|
|
|
|
return Failure::kNoMatch;
|
|
|
|
}
|
|
|
|
|
2022-08-24 12:11:55 -07:00
|
|
|
auto condition = expression();
|
2022-06-16 05:01:27 -07:00
|
|
|
if (condition.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (!condition.matched) {
|
|
|
|
return add_error(peek(), "unable to parse while condition expression");
|
|
|
|
}
|
|
|
|
|
2022-08-05 07:53:47 -07:00
|
|
|
auto body = expect_compound_statement();
|
2022-06-16 05:01:27 -07:00
|
|
|
if (body.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
|
|
|
|
return create<ast::WhileStatement>(source, condition.value, body.value);
|
|
|
|
}
|
|
|
|
|
2022-08-05 07:53:47 -07:00
|
|
|
// func_call_statement
|
2021-04-29 08:49:44 -07:00
|
|
|
// : IDENT argument_expression_list
|
2022-08-05 07:53:47 -07:00
|
|
|
Maybe<const ast::CallStatement*> ParserImpl::func_call_statement() {
|
2022-07-25 09:43:08 -07:00
|
|
|
auto& t = peek();
|
|
|
|
auto& t2 = peek(1);
|
2022-05-19 13:08:19 -07:00
|
|
|
if (!t.IsIdentifier() || !t2.Is(Token::Type::kParenLeft)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-07-21 08:13:36 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
next(); // Consume the first peek
|
2020-07-21 08:13:36 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
auto params = expect_argument_expression_list("function call");
|
2022-05-19 13:08:19 -07:00
|
|
|
if (params.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-07-21 08:13:36 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return create<ast::CallStatement>(
|
2022-07-25 17:28:37 -07:00
|
|
|
t.source(),
|
2022-05-01 07:40:55 -07:00
|
|
|
create<ast::CallExpression>(
|
2022-07-25 17:28:37 -07:00
|
|
|
t.source(),
|
|
|
|
create<ast::IdentifierExpression>(t.source(), builder_.Symbols().Register(t.to_str())),
|
2022-05-01 07:40:55 -07:00
|
|
|
std::move(params.value)));
|
2020-07-21 08:13:36 -07:00
|
|
|
}
|
|
|
|
|
2022-08-05 07:53:47 -07:00
|
|
|
// break_statement
|
2020-06-03 09:11:28 -07:00
|
|
|
// : BREAK
|
2022-08-05 07:53:47 -07:00
|
|
|
Maybe<const ast::BreakStatement*> ParserImpl::break_statement() {
|
2022-05-01 07:40:55 -07:00
|
|
|
Source source;
|
2022-05-19 13:08:19 -07:00
|
|
|
if (!match(Token::Type::kBreak, &source)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return create<ast::BreakStatement>(source);
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2022-08-05 07:53:47 -07:00
|
|
|
// continue_statement
|
2020-06-03 09:11:28 -07:00
|
|
|
// : CONTINUE
|
2022-08-05 07:53:47 -07:00
|
|
|
Maybe<const ast::ContinueStatement*> ParserImpl::continue_statement() {
|
2022-05-01 07:40:55 -07:00
|
|
|
Source source;
|
2022-05-19 13:08:19 -07:00
|
|
|
if (!match(Token::Type::kContinue, &source)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-06-01 06:44:06 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return create<ast::ContinueStatement>(source);
|
2020-06-01 06:44:06 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-08-09 09:52:42 -07:00
|
|
|
// break_if_statement:
|
|
|
|
// 'break' 'if' expression semicolon
|
|
|
|
Maybe<const ast::Statement*> ParserImpl::break_if_statement() {
|
|
|
|
// TODO(crbug.com/tint/1451): Add support for break-if
|
|
|
|
return Failure::kNoMatch;
|
|
|
|
}
|
|
|
|
|
|
|
|
// continuing_compound_statement:
|
|
|
|
// brace_left statement* break_if_statement? brace_right
|
|
|
|
Maybe<const ast::BlockStatement*> ParserImpl::continuing_compound_statement() {
|
|
|
|
return expect_brace_block("", [&]() -> Expect<ast::BlockStatement*> {
|
|
|
|
auto stmts = expect_statements();
|
|
|
|
if (stmts.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto break_if = break_if_statement();
|
|
|
|
if (break_if.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (break_if.matched) {
|
|
|
|
stmts.value.Push(break_if.value);
|
|
|
|
}
|
|
|
|
|
|
|
|
return create<ast::BlockStatement>(Source{}, stmts.value);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// continuing_statement
|
|
|
|
// : CONTINUING continuing_compound_statement
|
|
|
|
Maybe<const ast::BlockStatement*> ParserImpl::continuing_statement() {
|
2022-05-19 13:08:19 -07:00
|
|
|
if (!match(Token::Type::kContinuing)) {
|
2022-08-02 10:03:35 -07:00
|
|
|
return create<ast::BlockStatement>(Source{}, utils::Empty);
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-09-21 08:55:32 -07:00
|
|
|
|
2022-08-09 09:52:42 -07:00
|
|
|
return continuing_compound_statement();
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2022-08-22 09:15:04 -07:00
|
|
|
// callable
|
|
|
|
// : type_decl_without_ident
|
|
|
|
// | ARRAY
|
|
|
|
// | mat_prefix
|
|
|
|
// | vec_prefix
|
|
|
|
//
|
|
|
|
// Note, `ident` is pulled out to `primary_expression` as it's the only one that
|
|
|
|
// doesn't create a `type`. Then we can just return a `type` from here on match and
|
|
|
|
// deal with `ident` in `primary_expression.
|
|
|
|
Maybe<const ast::Type*> ParserImpl::callable() {
|
|
|
|
auto& t = peek();
|
|
|
|
|
|
|
|
// This _must_ match `type_decl_without_ident` before any of the other types as they're all
|
|
|
|
// prefixes of the types and we want to match the longer `vec3<f32>` then the shorter
|
|
|
|
// prefix match of `vec3`.
|
|
|
|
auto ty = type_decl_without_ident();
|
|
|
|
if (ty.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (ty.matched) {
|
|
|
|
return ty.value;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (match(Token::Type::kArray)) {
|
|
|
|
return builder_.ty.array(make_source_range_from(t.source()), nullptr, nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto vec = vec_prefix();
|
|
|
|
if (vec.matched) {
|
|
|
|
return builder_.ty.vec(make_source_range_from(t.source()), nullptr, vec.value);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto mat = mat_prefix();
|
|
|
|
if (mat.matched) {
|
|
|
|
return builder_.ty.mat(make_source_range_from(t.source()), nullptr, mat.value.columns,
|
|
|
|
mat.value.rows);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Failure::kNoMatch;
|
|
|
|
}
|
|
|
|
|
2020-03-02 12:47:43 -08:00
|
|
|
// primary_expression
|
2022-08-22 12:42:14 -07:00
|
|
|
// : BITCAST LESS_THAN type_decl GREATER_THAN paren_expression
|
|
|
|
// | callable argument_expression_list
|
2020-03-02 12:47:43 -08:00
|
|
|
// | const_literal
|
2022-08-22 12:42:14 -07:00
|
|
|
// | IDENT argument_expression_list?
|
2022-07-22 09:07:36 -07:00
|
|
|
// | paren_expression
|
2022-08-22 12:42:14 -07:00
|
|
|
//
|
|
|
|
// Note, PAREN_LEFT ( expression ( COMMA expression ) * COMMA? )? PAREN_RIGHT is replaced
|
|
|
|
// with `argument_expression_list`.
|
|
|
|
//
|
|
|
|
// Note, this is matching the `callable` ident here instead of having to come from
|
|
|
|
// callable so we can return a `type` from callable.
|
2021-10-19 11:38:54 -07:00
|
|
|
Maybe<const ast::Expression*> ParserImpl::primary_expression() {
|
2022-07-25 09:43:08 -07:00
|
|
|
auto& t = peek();
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
if (match(Token::Type::kBitcast)) {
|
|
|
|
const char* use = "bitcast expression";
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
auto type = expect_lt_gt_block(use, [&] { return expect_type(use); });
|
2022-05-19 13:08:19 -07:00
|
|
|
if (type.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-07-22 09:07:36 -07:00
|
|
|
auto params = expect_paren_expression();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (params.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-07-25 17:28:37 -07:00
|
|
|
return create<ast::BitcastExpression>(t.source(), type.value, params.value);
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-08-22 12:42:14 -07:00
|
|
|
auto call = callable();
|
|
|
|
if (call.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (call.matched) {
|
|
|
|
auto params = expect_argument_expression_list("type constructor");
|
|
|
|
if (params.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
|
|
|
|
return builder_.Construct(t.source(), call.value, std::move(params.value));
|
|
|
|
}
|
|
|
|
|
|
|
|
auto lit = const_literal();
|
|
|
|
if (lit.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (lit.matched) {
|
|
|
|
return lit.value;
|
|
|
|
}
|
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
if (t.IsIdentifier()) {
|
|
|
|
next();
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
auto* ident =
|
|
|
|
create<ast::IdentifierExpression>(t.source(), builder_.Symbols().Register(t.to_str()));
|
2021-04-29 08:02:15 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
if (peek_is(Token::Type::kParenLeft)) {
|
|
|
|
auto params = expect_argument_expression_list("function call");
|
2022-05-19 13:08:19 -07:00
|
|
|
if (params.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2021-04-29 08:02:15 -07:00
|
|
|
|
2022-07-25 17:28:37 -07:00
|
|
|
return create<ast::CallExpression>(t.source(), ident, std::move(params.value));
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
2021-04-29 08:02:15 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return ident;
|
2021-04-29 08:02:15 -07:00
|
|
|
}
|
|
|
|
|
2022-08-22 12:42:14 -07:00
|
|
|
if (t.Is(Token::Type::kParenLeft)) {
|
|
|
|
auto paren = expect_paren_expression();
|
|
|
|
if (paren.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-04 06:25:21 -08:00
|
|
|
|
2022-08-22 12:42:14 -07:00
|
|
|
return paren.value;
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
2020-11-09 11:52:24 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2021-04-29 08:02:15 -07:00
|
|
|
// postfix_expression
|
2020-03-02 12:47:43 -08:00
|
|
|
// :
|
2022-08-17 09:30:30 -07:00
|
|
|
// | BRACE_LEFT expression BRACE_RIGHT postfix_expression?
|
|
|
|
// | PERIOD member_ident postfix_expression?
|
|
|
|
// | PERIOD swizzle_name postfix_expression?
|
2022-05-01 07:40:55 -07:00
|
|
|
Maybe<const ast::Expression*> ParserImpl::postfix_expression(const ast::Expression* prefix) {
|
|
|
|
Source source;
|
2021-07-16 10:55:14 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
while (continue_parsing()) {
|
|
|
|
if (match(Token::Type::kBracketLeft, &source)) {
|
|
|
|
auto res = sync(Token::Type::kBracketRight, [&]() -> Maybe<const ast::Expression*> {
|
2022-08-24 12:11:55 -07:00
|
|
|
auto param = expression();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (param.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-05-01 07:40:55 -07:00
|
|
|
if (!param.matched) {
|
|
|
|
return add_error(peek(), "unable to parse expression inside []");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!expect("index accessor", Token::Type::kBracketRight)) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
|
|
|
|
return create<ast::IndexAccessorExpression>(source, prefix, param.value);
|
|
|
|
});
|
|
|
|
|
|
|
|
if (res.errored) {
|
|
|
|
return res;
|
2021-07-16 10:55:14 -07:00
|
|
|
}
|
2022-05-01 07:40:55 -07:00
|
|
|
prefix = res.value;
|
|
|
|
continue;
|
|
|
|
}
|
2021-07-16 10:55:14 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
if (match(Token::Type::kPeriod)) {
|
|
|
|
auto ident = expect_ident("member accessor");
|
|
|
|
if (ident.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
prefix = create<ast::MemberAccessorExpression>(
|
|
|
|
ident.source, prefix,
|
|
|
|
create<ast::IdentifierExpression>(ident.source,
|
|
|
|
builder_.Symbols().Register(ident.value)));
|
|
|
|
continue;
|
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return prefix;
|
2021-07-16 10:55:14 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2020-06-01 06:44:06 -07:00
|
|
|
// argument_expression_list
|
2022-08-09 09:52:42 -07:00
|
|
|
// : PAREN_LEFT ((expression COMMA)* expression COMMA?)? PAREN_RIGHT
|
2022-08-02 10:03:35 -07:00
|
|
|
Expect<ParserImpl::ExpressionList> ParserImpl::expect_argument_expression_list(
|
|
|
|
std::string_view use) {
|
|
|
|
return expect_paren_block(use, [&]() -> Expect<ExpressionList> {
|
|
|
|
ExpressionList ret;
|
2022-05-01 07:40:55 -07:00
|
|
|
while (continue_parsing()) {
|
2022-08-24 12:11:55 -07:00
|
|
|
auto arg = expression();
|
2022-05-01 07:40:55 -07:00
|
|
|
if (arg.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
} else if (!arg.matched) {
|
|
|
|
break;
|
|
|
|
}
|
2022-08-02 10:03:35 -07:00
|
|
|
ret.Push(arg.value);
|
2020-06-01 06:44:06 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
if (!match(Token::Type::kComma)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
});
|
2020-06-01 06:44:06 -07:00
|
|
|
}
|
|
|
|
|
2022-08-18 07:37:17 -07:00
|
|
|
// bitwise_expression.post.unary_expression
|
|
|
|
// : AND unary_expression (AND unary_expression)*
|
|
|
|
// | OR unary_expression (OR unary_expression)*
|
|
|
|
// | XOR unary_expression (XOR unary_expression)*
|
|
|
|
Maybe<const ast::Expression*> ParserImpl::bitwise_expression_post_unary_expression(
|
|
|
|
const ast::Expression* lhs) {
|
|
|
|
auto& t = peek();
|
|
|
|
if (!t.Is(Token::Type::kAnd) && !t.Is(Token::Type::kOr) && !t.Is(Token::Type::kXor)) {
|
|
|
|
return Failure::kNoMatch;
|
|
|
|
}
|
|
|
|
|
|
|
|
ast::BinaryOp op = ast::BinaryOp::kXor;
|
|
|
|
if (t.Is(Token::Type::kAnd)) {
|
|
|
|
op = ast::BinaryOp::kAnd;
|
|
|
|
} else if (t.Is(Token::Type::kOr)) {
|
|
|
|
op = ast::BinaryOp::kOr;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (continue_parsing()) {
|
|
|
|
auto& n = peek();
|
|
|
|
// Handle the case of `a & b &&c` where `&c` is a unary_expression
|
|
|
|
bool split = false;
|
|
|
|
if (op == ast::BinaryOp::kAnd && n.Is(Token::Type::kAndAnd)) {
|
|
|
|
next();
|
|
|
|
split_token(Token::Type::kAnd, Token::Type::kAnd);
|
|
|
|
split = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!n.Is(t.type())) {
|
|
|
|
if (n.Is(Token::Type::kAnd) || n.Is(Token::Type::kOr) || n.Is(Token::Type::kXor)) {
|
|
|
|
return add_error(n.source(), std::string("mixing '") + std::string(t.to_name()) +
|
|
|
|
"' and '" + std::string(n.to_name()) +
|
|
|
|
"' requires parenthesis");
|
|
|
|
}
|
|
|
|
|
|
|
|
return lhs;
|
|
|
|
}
|
|
|
|
// If forced to split an `&&` then we've already done the `next` above which consumes
|
|
|
|
// the `&`. The type check above will always fail because we only split if already consuming
|
|
|
|
// a `&` operator.
|
|
|
|
if (!split) {
|
|
|
|
next();
|
|
|
|
}
|
|
|
|
|
|
|
|
auto rhs = unary_expression();
|
|
|
|
if (rhs.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (!rhs.matched) {
|
|
|
|
return add_error(peek(), std::string("unable to parse right side of ") +
|
|
|
|
std::string(t.to_name()) + " expression");
|
|
|
|
}
|
|
|
|
|
|
|
|
lhs = create<ast::BinaryExpression>(t.source(), op, lhs, rhs.value);
|
|
|
|
}
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
|
2022-08-19 14:26:01 -07:00
|
|
|
// multiplicative_operator
|
|
|
|
// : FORWARD_SLASH
|
|
|
|
// | MODULO
|
|
|
|
// | STAR
|
|
|
|
Maybe<ast::BinaryOp> ParserImpl::multiplicative_operator() {
|
|
|
|
if (match(Token::Type::kForwardSlash)) {
|
|
|
|
return ast::BinaryOp::kDivide;
|
|
|
|
}
|
|
|
|
if (match(Token::Type::kMod)) {
|
|
|
|
return ast::BinaryOp::kModulo;
|
|
|
|
}
|
|
|
|
if (match(Token::Type::kStar)) {
|
|
|
|
return ast::BinaryOp::kMultiply;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Failure::kNoMatch;
|
|
|
|
}
|
|
|
|
|
|
|
|
// multiplicative_expression.post.unary_expression
|
|
|
|
// : (multiplicative_operator unary_expression)*
|
|
|
|
Expect<const ast::Expression*> ParserImpl::expect_multiplicative_expression_post_unary_expression(
|
|
|
|
const ast::Expression* lhs) {
|
|
|
|
while (continue_parsing()) {
|
|
|
|
auto& t = peek();
|
|
|
|
|
|
|
|
auto op = multiplicative_operator();
|
|
|
|
if (op.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (!op.matched) {
|
|
|
|
return lhs;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto rhs = unary_expression();
|
|
|
|
if (rhs.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (!rhs.matched) {
|
|
|
|
return add_error(peek(), std::string("unable to parse right side of ") +
|
|
|
|
std::string(t.to_name()) + " expression");
|
|
|
|
}
|
|
|
|
|
|
|
|
lhs = create<ast::BinaryExpression>(t.source(), op.value, lhs, rhs.value);
|
|
|
|
}
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
|
|
|
|
// additive_operator
|
|
|
|
// : MINUS
|
|
|
|
// | PLUS
|
|
|
|
//
|
|
|
|
// Note, this also splits a `--` token. This is currently safe as the only way to get into
|
|
|
|
// here is through additive expression and rules for where `--` are allowed are very restrictive.
|
|
|
|
Maybe<ast::BinaryOp> ParserImpl::additive_operator() {
|
|
|
|
if (match(Token::Type::kPlus)) {
|
|
|
|
return ast::BinaryOp::kAdd;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto& t = peek();
|
|
|
|
if (t.Is(Token::Type::kMinusMinus)) {
|
|
|
|
next();
|
|
|
|
split_token(Token::Type::kMinus, Token::Type::kMinus);
|
|
|
|
} else if (t.Is(Token::Type::kMinus)) {
|
|
|
|
next();
|
|
|
|
} else {
|
|
|
|
return Failure::kNoMatch;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ast::BinaryOp::kSubtract;
|
|
|
|
}
|
|
|
|
|
|
|
|
// additive_expression.pos.unary_expression
|
|
|
|
// : (additive_operator unary_expression expect_multiplicative_expression.post.unary_expression)*
|
|
|
|
//
|
|
|
|
// This is `( additive_operator unary_expression ( multiplicative_operator unary_expression )* )*`
|
|
|
|
// split apart.
|
|
|
|
Expect<const ast::Expression*> ParserImpl::expect_additive_expression_post_unary_expression(
|
|
|
|
const ast::Expression* lhs) {
|
|
|
|
while (continue_parsing()) {
|
|
|
|
auto& t = peek();
|
|
|
|
|
|
|
|
auto op = additive_operator();
|
|
|
|
if (op.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (!op.matched) {
|
|
|
|
return lhs;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto unary = unary_expression();
|
|
|
|
if (unary.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (!unary.matched) {
|
|
|
|
return add_error(peek(), std::string("unable to parse right side of ") +
|
|
|
|
std::string(t.to_name()) + " expression");
|
|
|
|
}
|
|
|
|
|
|
|
|
// The multiplicative binds tigher, so pass the unary into that and build that expression
|
|
|
|
// before creating the additve expression.
|
|
|
|
auto rhs = expect_multiplicative_expression_post_unary_expression(unary.value);
|
|
|
|
if (rhs.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
|
|
|
|
lhs = create<ast::BinaryExpression>(t.source(), op.value, lhs, rhs.value);
|
|
|
|
}
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
|
|
|
|
// math_expression.post.unary_expression
|
|
|
|
// : multiplicative_expression.post.unary_expression additive_expression.post.unary_expression
|
|
|
|
//
|
|
|
|
// This is `( multiplicative_operator unary_expression )* ( additive_operator unary_expression (
|
|
|
|
// multiplicative_operator unary_expression )* )*` split apart.
|
|
|
|
Expect<const ast::Expression*> ParserImpl::expect_math_expression_post_unary_expression(
|
|
|
|
const ast::Expression* lhs) {
|
|
|
|
auto rhs = expect_multiplicative_expression_post_unary_expression(lhs);
|
|
|
|
if (rhs.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
|
|
|
|
return expect_additive_expression_post_unary_expression(rhs.value);
|
|
|
|
}
|
|
|
|
|
|
|
|
// element_count_expression
|
|
|
|
// : unary_expression math_expression.post.unary_expression
|
|
|
|
// | unary_expression bitwise_expression.post.unary_expression
|
|
|
|
//
|
|
|
|
// Note, this moves the `( multiplicative_operator unary_expression )* ( additive_operator
|
|
|
|
// unary_expression ( multiplicative_operator unary_expression )* )*` expression for the first
|
|
|
|
// branch out into helper methods.
|
|
|
|
Maybe<const ast::Expression*> ParserImpl::element_count_expression() {
|
|
|
|
auto lhs = unary_expression();
|
|
|
|
if (lhs.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (!lhs.matched) {
|
|
|
|
return Failure::kNoMatch;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto bitwise = bitwise_expression_post_unary_expression(lhs.value);
|
|
|
|
if (bitwise.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (bitwise.matched) {
|
|
|
|
return bitwise.value;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto math = expect_math_expression_post_unary_expression(lhs.value);
|
|
|
|
if (math.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
return math.value;
|
|
|
|
}
|
|
|
|
|
2022-08-19 15:19:11 -07:00
|
|
|
// shift_expression
|
|
|
|
// : unary_expression shift_expression.post.unary_expression
|
2022-08-24 12:11:55 -07:00
|
|
|
Maybe<const ast::Expression*> ParserImpl::shift_expression() {
|
2022-08-19 15:19:11 -07:00
|
|
|
auto lhs = unary_expression();
|
|
|
|
if (lhs.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (!lhs.matched) {
|
|
|
|
return Failure::kNoMatch;
|
|
|
|
}
|
|
|
|
return expect_shift_expression_post_unary_expression(lhs.value);
|
|
|
|
}
|
|
|
|
|
2022-08-19 15:16:21 -07:00
|
|
|
// shift_expression.post.unary_expression
|
2022-08-22 07:55:04 -07:00
|
|
|
// : math_expression.post.unary_expression?
|
2022-08-19 15:16:21 -07:00
|
|
|
// | SHIFT_LEFT unary_expression
|
|
|
|
// | SHIFT_RIGHT unary_expression
|
|
|
|
//
|
2022-08-22 07:55:04 -07:00
|
|
|
// Note, add the `math_expression.post.unary_expression` is added here to make
|
2022-08-19 15:16:21 -07:00
|
|
|
// implementation simpler.
|
|
|
|
Expect<const ast::Expression*> ParserImpl::expect_shift_expression_post_unary_expression(
|
|
|
|
const ast::Expression* lhs) {
|
|
|
|
auto& t = peek();
|
|
|
|
if (match(Token::Type::kShiftLeft) || match(Token::Type::kShiftRight)) {
|
|
|
|
std::string name;
|
|
|
|
ast::BinaryOp op = ast::BinaryOp::kNone;
|
|
|
|
if (t.Is(Token::Type::kShiftLeft)) {
|
|
|
|
op = ast::BinaryOp::kShiftLeft;
|
|
|
|
name = "<<";
|
|
|
|
} else if (t.Is(Token::Type::kShiftRight)) {
|
|
|
|
op = ast::BinaryOp::kShiftRight;
|
|
|
|
name = ">>";
|
|
|
|
}
|
|
|
|
|
2022-08-24 11:05:26 -07:00
|
|
|
auto& rhs_start = peek();
|
2022-08-19 15:16:21 -07:00
|
|
|
auto rhs = unary_expression();
|
|
|
|
if (rhs.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (!rhs.matched) {
|
2022-08-24 11:05:26 -07:00
|
|
|
return add_error(rhs_start,
|
2022-08-19 15:16:21 -07:00
|
|
|
std::string("unable to parse right side of ") + name + " expression");
|
|
|
|
}
|
|
|
|
return create<ast::BinaryExpression>(t.source(), op, lhs, rhs.value);
|
|
|
|
}
|
|
|
|
|
2022-08-22 07:55:04 -07:00
|
|
|
return expect_math_expression_post_unary_expression(lhs);
|
2022-08-19 15:16:21 -07:00
|
|
|
}
|
|
|
|
|
2022-08-19 15:19:11 -07:00
|
|
|
// relational_expression
|
|
|
|
// : unary_expression relational_expression.post.unary_expression
|
2022-08-24 12:11:55 -07:00
|
|
|
Maybe<const ast::Expression*> ParserImpl::relational_expression() {
|
2022-08-19 15:19:11 -07:00
|
|
|
auto lhs = unary_expression();
|
|
|
|
if (lhs.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (!lhs.matched) {
|
|
|
|
return Failure::kNoMatch;
|
|
|
|
}
|
|
|
|
return expect_relational_expression_post_unary_expression(lhs.value);
|
|
|
|
}
|
|
|
|
|
|
|
|
// relational_expression.post.unary_expression
|
|
|
|
// : shift_expression.post.unary_expression
|
|
|
|
// | shift_expression.post.unary_expression EQUAL_EQUAL shift_expression
|
|
|
|
// | shift_expression.post.unary_expression GREATER_THAN shift_expression
|
|
|
|
// | shift_expression.post.unary_expression GREATER_THAN_EQUAL shift_expression
|
|
|
|
// | shift_expression.post.unary_expression LESS_THAN shift_expression
|
|
|
|
// | shift_expression.post.unary_expression LESS_THAN_EQUAL shift_expression
|
|
|
|
// | shift_expression.post.unary_expression NOT_EQUAL shift_expression
|
|
|
|
//
|
|
|
|
// Note, a `shift_expression` element was added to simplify many of the right sides
|
|
|
|
Expect<const ast::Expression*> ParserImpl::expect_relational_expression_post_unary_expression(
|
|
|
|
const ast::Expression* lhs) {
|
|
|
|
auto lhs_result = expect_shift_expression_post_unary_expression(lhs);
|
|
|
|
if (lhs_result.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
lhs = lhs_result.value;
|
|
|
|
|
|
|
|
auto& t = peek();
|
|
|
|
if (match(Token::Type::kEqualEqual) || match(Token::Type::kGreaterThan) ||
|
|
|
|
match(Token::Type::kGreaterThanEqual) || match(Token::Type::kLessThan) ||
|
|
|
|
match(Token::Type::kLessThanEqual) || match(Token::Type::kNotEqual)) {
|
|
|
|
ast::BinaryOp op = ast::BinaryOp::kNone;
|
|
|
|
if (t.Is(Token::Type::kLessThan)) {
|
|
|
|
op = ast::BinaryOp::kLessThan;
|
|
|
|
} else if (t.Is(Token::Type::kGreaterThan)) {
|
|
|
|
op = ast::BinaryOp::kGreaterThan;
|
|
|
|
} else if (t.Is(Token::Type::kLessThanEqual)) {
|
|
|
|
op = ast::BinaryOp::kLessThanEqual;
|
|
|
|
} else if (t.Is(Token::Type::kGreaterThanEqual)) {
|
|
|
|
op = ast::BinaryOp::kGreaterThanEqual;
|
2022-08-22 12:23:24 -07:00
|
|
|
} else if (t.Is(Token::Type::kEqualEqual)) {
|
|
|
|
op = ast::BinaryOp::kEqual;
|
|
|
|
} else if (t.Is(Token::Type::kNotEqual)) {
|
|
|
|
op = ast::BinaryOp::kNotEqual;
|
2022-08-19 15:19:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
auto& next = peek();
|
2022-08-24 12:11:55 -07:00
|
|
|
auto rhs = shift_expression();
|
2022-08-19 15:19:11 -07:00
|
|
|
if (rhs.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (!rhs.matched) {
|
|
|
|
return add_error(next, std::string("unable to parse right side of ") +
|
|
|
|
std::string(t.to_name()) + " expression");
|
|
|
|
}
|
|
|
|
lhs = create<ast::BinaryExpression>(t.source(), op, lhs, rhs.value);
|
|
|
|
}
|
|
|
|
return lhs;
|
|
|
|
}
|
|
|
|
|
2022-08-22 07:06:44 -07:00
|
|
|
// expression
|
|
|
|
// : unary_expression bitwise_expression.post.unary_expression
|
|
|
|
// | unary_expression relational_expression.post.unary_expression
|
|
|
|
// | unary_expression relational_expression.post.unary_expression and_and
|
|
|
|
// relational_expression ( and_and relational_expression )*
|
|
|
|
// | unary_expression relational_expression.post.unary_expression or_or
|
|
|
|
// relational_expression ( or_or relational_expression )*
|
|
|
|
//
|
|
|
|
// Note, a `relational_expression` element was added to simplify many of the right sides
|
2022-08-24 12:11:55 -07:00
|
|
|
Maybe<const ast::Expression*> ParserImpl::expression() {
|
2022-08-22 07:06:44 -07:00
|
|
|
auto lhs = unary_expression();
|
|
|
|
if (lhs.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (!lhs.matched) {
|
|
|
|
return Failure::kNoMatch;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto bitwise = bitwise_expression_post_unary_expression(lhs.value);
|
|
|
|
if (bitwise.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (bitwise.matched) {
|
|
|
|
return bitwise.value;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto relational = expect_relational_expression_post_unary_expression(lhs.value);
|
|
|
|
if (relational.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
auto* ret = relational.value;
|
|
|
|
|
|
|
|
auto& t = peek();
|
|
|
|
if (t.Is(Token::Type::kAndAnd) || t.Is(Token::Type::kOrOr)) {
|
|
|
|
ast::BinaryOp op = ast::BinaryOp::kNone;
|
|
|
|
if (t.Is(Token::Type::kAndAnd)) {
|
|
|
|
op = ast::BinaryOp::kLogicalAnd;
|
|
|
|
} else if (t.Is(Token::Type::kOrOr)) {
|
|
|
|
op = ast::BinaryOp::kLogicalOr;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (continue_parsing()) {
|
|
|
|
auto& n = peek();
|
|
|
|
if (!n.Is(t.type())) {
|
|
|
|
if (n.Is(Token::Type::kAndAnd) || n.Is(Token::Type::kOrOr)) {
|
|
|
|
return add_error(
|
|
|
|
n.source(), std::string("mixing '") + std::string(t.to_name()) + "' and '" +
|
|
|
|
std::string(n.to_name()) + "' requires parenthesis");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
next();
|
|
|
|
|
2022-08-24 12:11:55 -07:00
|
|
|
auto rhs = relational_expression();
|
2022-08-22 07:06:44 -07:00
|
|
|
if (rhs.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (!rhs.matched) {
|
|
|
|
return add_error(peek(), std::string("unable to parse right side of ") +
|
|
|
|
std::string(t.to_name()) + " expression");
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = create<ast::BinaryExpression>(t.source(), op, ret, rhs.value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-08-19 15:07:23 -07:00
|
|
|
// singular_expression
|
|
|
|
// : primary_expression postfix_expr
|
|
|
|
Maybe<const ast::Expression*> ParserImpl::singular_expression() {
|
|
|
|
auto prefix = primary_expression();
|
|
|
|
if (prefix.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (!prefix.matched) {
|
|
|
|
return Failure::kNoMatch;
|
|
|
|
}
|
|
|
|
|
|
|
|
return postfix_expression(prefix.value);
|
|
|
|
}
|
|
|
|
|
2020-03-02 12:47:43 -08:00
|
|
|
// unary_expression
|
2021-04-29 08:02:15 -07:00
|
|
|
// : singular_expression
|
2020-03-02 12:47:43 -08:00
|
|
|
// | MINUS unary_expression
|
|
|
|
// | BANG unary_expression
|
2021-06-17 01:35:54 -07:00
|
|
|
// | TILDE unary_expression
|
2021-05-13 09:56:32 -07:00
|
|
|
// | STAR unary_expression
|
|
|
|
// | AND unary_expression
|
2022-08-19 15:07:23 -07:00
|
|
|
//
|
|
|
|
// The `primary_expression postfix_expression ?` is moved out into a `singular_expression`
|
2021-10-19 11:38:54 -07:00
|
|
|
Maybe<const ast::Expression*> ParserImpl::unary_expression() {
|
2022-07-25 09:43:08 -07:00
|
|
|
auto& t = peek();
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
if (match(Token::Type::kPlusPlus) || match(Token::Type::kMinusMinus)) {
|
|
|
|
add_error(t.source(),
|
|
|
|
"prefix increment and decrement operators are reserved for a "
|
|
|
|
"future WGSL version");
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
2021-06-21 12:36:26 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
ast::UnaryOp op;
|
|
|
|
if (match(Token::Type::kMinus)) {
|
|
|
|
op = ast::UnaryOp::kNegation;
|
|
|
|
} else if (match(Token::Type::kBang)) {
|
|
|
|
op = ast::UnaryOp::kNot;
|
|
|
|
} else if (match(Token::Type::kTilde)) {
|
|
|
|
op = ast::UnaryOp::kComplement;
|
|
|
|
} else if (match(Token::Type::kStar)) {
|
|
|
|
op = ast::UnaryOp::kIndirection;
|
|
|
|
} else if (match(Token::Type::kAnd)) {
|
|
|
|
op = ast::UnaryOp::kAddressOf;
|
|
|
|
} else {
|
|
|
|
return singular_expression();
|
|
|
|
}
|
2021-06-21 12:36:26 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
if (parse_depth_ >= kMaxParseDepth) {
|
|
|
|
// We've hit a maximum parser recursive depth.
|
|
|
|
// We can't call into unary_expression() as we might stack overflow.
|
|
|
|
// Instead, report an error
|
|
|
|
add_error(peek(), "maximum parser recursive depth reached");
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
|
|
|
|
++parse_depth_;
|
|
|
|
auto expr = unary_expression();
|
|
|
|
--parse_depth_;
|
2021-05-13 09:56:32 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
if (expr.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (!expr.matched) {
|
|
|
|
return add_error(
|
|
|
|
peek(), "unable to parse right side of " + std::string(t.to_name()) + " expression");
|
|
|
|
}
|
|
|
|
|
|
|
|
return create<ast::UnaryOpExpression>(t.source(), op, expr.value);
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2022-08-04 14:21:37 -07:00
|
|
|
// compound_assignment_operator
|
|
|
|
// : plus_equal
|
|
|
|
// | minus_equal
|
|
|
|
// | times_equal
|
|
|
|
// | division_equal
|
|
|
|
// | modulo_equal
|
|
|
|
// | and_equal
|
|
|
|
// | or_equal
|
|
|
|
// | xor_equal
|
|
|
|
// | shift_right_equal
|
|
|
|
// | shift_left_equal
|
2022-03-31 15:30:10 -07:00
|
|
|
Maybe<ast::BinaryOp> ParserImpl::compound_assignment_operator() {
|
2022-05-01 07:40:55 -07:00
|
|
|
ast::BinaryOp compound_op = ast::BinaryOp::kNone;
|
|
|
|
if (peek_is(Token::Type::kPlusEqual)) {
|
|
|
|
compound_op = ast::BinaryOp::kAdd;
|
|
|
|
} else if (peek_is(Token::Type::kMinusEqual)) {
|
|
|
|
compound_op = ast::BinaryOp::kSubtract;
|
|
|
|
} else if (peek_is(Token::Type::kTimesEqual)) {
|
|
|
|
compound_op = ast::BinaryOp::kMultiply;
|
|
|
|
} else if (peek_is(Token::Type::kDivisionEqual)) {
|
|
|
|
compound_op = ast::BinaryOp::kDivide;
|
|
|
|
} else if (peek_is(Token::Type::kModuloEqual)) {
|
|
|
|
compound_op = ast::BinaryOp::kModulo;
|
|
|
|
} else if (peek_is(Token::Type::kAndEqual)) {
|
|
|
|
compound_op = ast::BinaryOp::kAnd;
|
|
|
|
} else if (peek_is(Token::Type::kOrEqual)) {
|
|
|
|
compound_op = ast::BinaryOp::kOr;
|
|
|
|
} else if (peek_is(Token::Type::kXorEqual)) {
|
|
|
|
compound_op = ast::BinaryOp::kXor;
|
2022-08-02 11:18:05 -07:00
|
|
|
} else if (peek_is(Token::Type::kShiftLeftEqual)) {
|
|
|
|
compound_op = ast::BinaryOp::kShiftLeft;
|
|
|
|
} else if (peek_is(Token::Type::kShiftRightEqual)) {
|
|
|
|
compound_op = ast::BinaryOp::kShiftRight;
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
|
|
|
if (compound_op != ast::BinaryOp::kNone) {
|
|
|
|
next();
|
|
|
|
return compound_op;
|
|
|
|
}
|
|
|
|
return Failure::kNoMatch;
|
2022-03-31 15:30:10 -07:00
|
|
|
}
|
|
|
|
|
2022-08-17 09:30:30 -07:00
|
|
|
// core_lhs_expression
|
|
|
|
// : ident
|
|
|
|
// | PAREN_LEFT lhs_expression PAREN_RIGHT
|
2022-08-17 10:20:50 -07:00
|
|
|
Maybe<const ast::Expression*> ParserImpl::core_lhs_expression() {
|
2022-08-17 09:30:30 -07:00
|
|
|
auto& t = peek();
|
|
|
|
if (t.IsIdentifier()) {
|
|
|
|
next();
|
|
|
|
|
|
|
|
return create<ast::IdentifierExpression>(t.source(),
|
|
|
|
builder_.Symbols().Register(t.to_str()));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (peek_is(Token::Type::kParenLeft)) {
|
|
|
|
return expect_paren_block("", [&]() -> Expect<const ast::Expression*> {
|
|
|
|
auto expr = lhs_expression();
|
|
|
|
if (expr.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
2022-08-17 10:20:50 -07:00
|
|
|
if (!expr.matched) {
|
|
|
|
return add_error(t, "invalid expression");
|
|
|
|
}
|
2022-08-17 09:30:30 -07:00
|
|
|
return expr.value;
|
|
|
|
});
|
|
|
|
}
|
2022-08-17 10:20:50 -07:00
|
|
|
|
|
|
|
return Failure::kNoMatch;
|
2022-08-17 09:30:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// lhs_expression
|
|
|
|
// : ( STAR | AND )* core_lhs_expression postfix_expression?
|
2022-08-17 10:20:50 -07:00
|
|
|
Maybe<const ast::Expression*> ParserImpl::lhs_expression() {
|
2022-08-17 09:30:30 -07:00
|
|
|
std::vector<const Token*> prefixes;
|
|
|
|
while (peek_is(Token::Type::kStar) || peek_is(Token::Type::kAnd) ||
|
|
|
|
peek_is(Token::Type::kAndAnd)) {
|
|
|
|
auto& t = next();
|
|
|
|
|
|
|
|
// If an '&&' is provided split into '&' and '&'
|
|
|
|
if (t.Is(Token::Type::kAndAnd)) {
|
|
|
|
split_token(Token::Type::kAnd, Token::Type::kAnd);
|
|
|
|
}
|
|
|
|
|
|
|
|
prefixes.push_back(&t);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto core_expr = core_lhs_expression();
|
|
|
|
if (core_expr.errored) {
|
|
|
|
return Failure::kErrored;
|
2022-08-17 10:20:50 -07:00
|
|
|
} else if (!core_expr.matched) {
|
|
|
|
if (prefixes.empty()) {
|
|
|
|
return Failure::kNoMatch;
|
|
|
|
}
|
|
|
|
|
|
|
|
return add_error(peek(), "missing expression");
|
2022-08-17 09:30:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
const auto* expr = core_expr.value;
|
|
|
|
for (auto it = prefixes.rbegin(); it != prefixes.rend(); ++it) {
|
|
|
|
auto& t = **it;
|
|
|
|
ast::UnaryOp op = ast::UnaryOp::kAddressOf;
|
|
|
|
if (t.Is(Token::Type::kStar)) {
|
|
|
|
op = ast::UnaryOp::kIndirection;
|
|
|
|
}
|
|
|
|
expr = create<ast::UnaryOpExpression>(t.source(), op, expr);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto e = postfix_expression(expr);
|
|
|
|
if (e.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
return e.value;
|
|
|
|
}
|
|
|
|
|
2022-08-17 10:20:50 -07:00
|
|
|
// variable_updating_statement
|
|
|
|
// : lhs_expression ( EQUAL | compound_assignment_operator ) expression
|
|
|
|
// | lhs_expression MINUS_MINUS
|
|
|
|
// | lhs_expression PLUS_PLUS
|
|
|
|
// | UNDERSCORE EQUAL expression
|
2022-07-25 17:28:37 -07:00
|
|
|
//
|
2022-08-17 10:20:50 -07:00
|
|
|
// Note, this is a simplification of the recursive grammar statement with the `lhs_expression`
|
|
|
|
// substituted back into the expression.
|
|
|
|
Maybe<const ast::Statement*> ParserImpl::variable_updating_statement() {
|
2022-07-25 09:43:08 -07:00
|
|
|
auto& t = peek();
|
2022-05-01 07:40:55 -07:00
|
|
|
|
|
|
|
// tint:295 - Test for `ident COLON` - this is invalid grammar, and without
|
|
|
|
// special casing will error as "missing = for assignment", which is less
|
|
|
|
// helpful than this error message:
|
|
|
|
if (peek_is(Token::Type::kIdentifier) && peek_is(Token::Type::kColon, 1)) {
|
|
|
|
return add_error(peek(0).source(), "expected 'var' for variable declaration");
|
2022-03-31 15:30:10 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-08-17 10:20:50 -07:00
|
|
|
const ast::Expression* lhs = nullptr;
|
|
|
|
ast::BinaryOp compound_op = ast::BinaryOp::kNone;
|
|
|
|
if (peek_is(Token::Type::kUnderscore)) {
|
|
|
|
next(); // Consume the peek.
|
|
|
|
|
|
|
|
if (!expect("assignment", Token::Type::kEqual)) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
|
|
|
|
lhs = create<ast::PhonyExpression>(t.source());
|
|
|
|
|
|
|
|
} else {
|
|
|
|
auto lhs_result = lhs_expression();
|
|
|
|
if (lhs_result.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (!lhs_result.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
|
|
|
}
|
|
|
|
|
2022-08-17 10:20:50 -07:00
|
|
|
lhs = lhs_result.value;
|
2022-05-01 07:40:55 -07:00
|
|
|
|
2022-08-17 10:20:50 -07:00
|
|
|
// Handle increment and decrement statements.
|
|
|
|
if (match(Token::Type::kPlusPlus)) {
|
|
|
|
return create<ast::IncrementDecrementStatement>(t.source(), lhs, true);
|
|
|
|
}
|
|
|
|
if (match(Token::Type::kMinusMinus)) {
|
|
|
|
return create<ast::IncrementDecrementStatement>(t.source(), lhs, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto compound_op_result = compound_assignment_operator();
|
|
|
|
if (compound_op_result.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
2022-08-17 10:20:50 -07:00
|
|
|
if (compound_op_result.matched) {
|
|
|
|
compound_op = compound_op_result.value;
|
|
|
|
} else {
|
|
|
|
if (!expect("assignment", Token::Type::kEqual)) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
}
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
2020-03-02 12:47:43 -08:00
|
|
|
|
2022-08-24 12:11:55 -07:00
|
|
|
auto rhs = expression();
|
2022-05-01 07:40:55 -07:00
|
|
|
if (rhs.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
if (!rhs.matched) {
|
|
|
|
return add_error(peek(), "unable to parse right side of assignment");
|
|
|
|
}
|
|
|
|
|
2022-08-17 10:20:50 -07:00
|
|
|
if (compound_op != ast::BinaryOp::kNone) {
|
|
|
|
return create<ast::CompoundAssignmentStatement>(t.source(), lhs, rhs.value, compound_op);
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
2022-08-17 10:20:50 -07:00
|
|
|
return create<ast::AssignmentStatement>(t.source(), lhs, rhs.value);
|
2020-03-02 12:47:43 -08:00
|
|
|
}
|
|
|
|
|
2020-06-01 06:44:06 -07:00
|
|
|
// const_literal
|
|
|
|
// : INT_LITERAL
|
|
|
|
// | FLOAT_LITERAL
|
2022-08-10 11:54:43 -07:00
|
|
|
// | bool_literal
|
|
|
|
//
|
2022-08-17 07:28:31 -07:00
|
|
|
// bool_literal
|
|
|
|
// : TRUE
|
2020-06-01 06:44:06 -07:00
|
|
|
// | FALSE
|
2021-11-10 11:23:07 -08:00
|
|
|
Maybe<const ast::LiteralExpression*> ParserImpl::const_literal() {
|
2022-07-25 09:43:08 -07:00
|
|
|
auto& t = peek();
|
2022-05-04 14:01:12 -07:00
|
|
|
if (match(Token::Type::kIntLiteral)) {
|
2022-05-04 15:18:49 -07:00
|
|
|
return create<ast::IntLiteralExpression>(t.source(), t.to_i64(),
|
|
|
|
ast::IntLiteralExpression::Suffix::kNone);
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
2022-05-10 15:26:04 -07:00
|
|
|
if (match(Token::Type::kIntLiteral_I)) {
|
2022-05-04 15:18:49 -07:00
|
|
|
return create<ast::IntLiteralExpression>(t.source(), t.to_i64(),
|
|
|
|
ast::IntLiteralExpression::Suffix::kI);
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
2022-05-10 15:26:04 -07:00
|
|
|
if (match(Token::Type::kIntLiteral_U)) {
|
2022-05-04 15:18:49 -07:00
|
|
|
return create<ast::IntLiteralExpression>(t.source(), t.to_i64(),
|
|
|
|
ast::IntLiteralExpression::Suffix::kU);
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
|
|
|
if (match(Token::Type::kFloatLiteral)) {
|
2022-05-10 07:55:34 -07:00
|
|
|
return create<ast::FloatLiteralExpression>(t.source(), t.to_f64(),
|
|
|
|
ast::FloatLiteralExpression::Suffix::kNone);
|
|
|
|
}
|
2022-05-10 15:26:04 -07:00
|
|
|
if (match(Token::Type::kFloatLiteral_F)) {
|
2022-05-10 07:55:34 -07:00
|
|
|
return create<ast::FloatLiteralExpression>(t.source(), t.to_f64(),
|
|
|
|
ast::FloatLiteralExpression::Suffix::kF);
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
2022-06-10 11:18:35 -07:00
|
|
|
if (match(Token::Type::kFloatLiteral_H)) {
|
|
|
|
return create<ast::FloatLiteralExpression>(t.source(), t.to_f64(),
|
|
|
|
ast::FloatLiteralExpression::Suffix::kH);
|
|
|
|
}
|
2022-05-04 14:01:12 -07:00
|
|
|
if (match(Token::Type::kTrue)) {
|
|
|
|
return create<ast::BoolLiteralExpression>(t.source(), true);
|
|
|
|
}
|
|
|
|
if (match(Token::Type::kFalse)) {
|
|
|
|
return create<ast::BoolLiteralExpression>(t.source(), false);
|
|
|
|
}
|
2022-05-01 07:40:55 -07:00
|
|
|
if (handle_error(t)) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
return Failure::kNoMatch;
|
2020-06-01 06:44:06 -07:00
|
|
|
}
|
|
|
|
|
2022-08-02 10:03:35 -07:00
|
|
|
Maybe<ParserImpl::AttributeList> ParserImpl::attribute_list() {
|
2022-05-01 07:40:55 -07:00
|
|
|
bool errored = false;
|
2022-08-02 10:03:35 -07:00
|
|
|
AttributeList attrs;
|
2020-11-09 11:52:24 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
while (continue_parsing()) {
|
|
|
|
if (match(Token::Type::kAttr)) {
|
|
|
|
if (auto attr = expect_attribute(); attr.errored) {
|
|
|
|
errored = true;
|
|
|
|
} else {
|
2022-08-02 10:03:35 -07:00
|
|
|
attrs.Push(attr.value);
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
2022-01-19 14:46:57 -08:00
|
|
|
}
|
2020-11-09 11:52:24 -08:00
|
|
|
|
2022-05-19 13:08:19 -07:00
|
|
|
if (errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-11 10:44:36 -08:00
|
|
|
|
2022-08-02 10:03:35 -07:00
|
|
|
if (attrs.IsEmpty()) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kNoMatch;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-09 11:52:24 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return attrs;
|
2020-11-04 12:55:31 -08:00
|
|
|
}
|
|
|
|
|
2022-02-02 15:07:11 -08:00
|
|
|
Expect<const ast::Attribute*> ParserImpl::expect_attribute() {
|
2022-07-25 09:43:08 -07:00
|
|
|
auto& t = peek();
|
2022-05-01 07:40:55 -07:00
|
|
|
auto attr = attribute();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (attr.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
|
|
|
if (attr.matched) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return attr.value;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-05-01 07:40:55 -07:00
|
|
|
return add_error(t, "expected attribute");
|
2020-11-04 12:55:31 -08:00
|
|
|
}
|
|
|
|
|
2022-08-05 07:47:57 -07:00
|
|
|
// attribute
|
2022-08-24 14:31:45 -07:00
|
|
|
// : ATTR 'align' PAREN_LEFT expression COMMA? PAREN_RIGHT
|
|
|
|
// | ATTR 'binding' PAREN_LEFT expression COMMA? PAREN_RIGHT
|
|
|
|
// | ATTR 'builtin' PAREN_LEFT builtin_value_name COMMA? PAREN_RIGHT
|
2022-08-05 07:47:57 -07:00
|
|
|
// | ATTR 'const'
|
2022-08-24 14:31:45 -07:00
|
|
|
// | ATTR 'group' PAREN_LEFT expression COMMA? PAREN_RIGHT
|
|
|
|
// | ATTR 'id' PAREN_LEFT expression COMMA? PAREN_RIGHT
|
|
|
|
// | ATTR 'interpolate' PAREN_LEFT interpolation_type_name COMMA? PAREN_RIGHT
|
2022-08-05 07:47:57 -07:00
|
|
|
// | ATTR 'interpolate' PAREN_LEFT interpolation_type_name COMMA
|
2022-08-24 14:31:45 -07:00
|
|
|
// interpolation_sample_name COMMA? PAREN_RIGHT
|
2022-08-05 07:47:57 -07:00
|
|
|
// | ATTR 'invariant'
|
2022-08-24 14:31:45 -07:00
|
|
|
// | ATTR 'location' PAREN_LEFT expression COMMA? PAREN_RIGHT
|
|
|
|
// | ATTR 'size' PAREN_LEFT expression COMMA? PAREN_RIGHT
|
|
|
|
// | ATTR 'workgroup_size' PAREN_LEFT expression COMMA? PAREN_RIGHT
|
|
|
|
// | ATTR 'workgroup_size' PAREN_LEFT expression COMMA expression COMMA? PAREN_RIGHT
|
|
|
|
// | ATTR 'workgroup_size' PAREN_LEFT expression COMMA expression COMMA
|
|
|
|
// expression COMMA? PAREN_RIGHT
|
2022-08-05 07:47:57 -07:00
|
|
|
// | ATTR 'vertex'
|
|
|
|
// | ATTR 'fragment'
|
|
|
|
// | ATTR 'compute'
|
2022-02-02 15:07:11 -08:00
|
|
|
Maybe<const ast::Attribute*> ParserImpl::attribute() {
|
2022-05-01 07:40:55 -07:00
|
|
|
using Result = Maybe<const ast::Attribute*>;
|
2022-07-25 09:43:08 -07:00
|
|
|
auto& t = next();
|
2020-12-01 12:10:47 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
if (!t.IsIdentifier()) {
|
|
|
|
return Failure::kNoMatch;
|
|
|
|
}
|
2020-12-01 12:10:47 -08:00
|
|
|
|
2022-08-05 07:47:57 -07:00
|
|
|
if (t == "align") {
|
|
|
|
const char* use = "align attribute";
|
2022-05-01 07:40:55 -07:00
|
|
|
return expect_paren_block(use, [&]() -> Result {
|
|
|
|
auto val = expect_positive_sint(use);
|
2022-05-19 13:08:19 -07:00
|
|
|
if (val.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-07-25 06:30:18 -07:00
|
|
|
match(Token::Type::kComma);
|
2020-11-09 11:39:34 -08:00
|
|
|
|
2022-08-23 06:28:44 -07:00
|
|
|
return create<ast::StructMemberAlignAttribute>(
|
|
|
|
t.source(), create<ast::IntLiteralExpression>(
|
|
|
|
val.value, ast::IntLiteralExpression::Suffix::kNone));
|
2022-05-01 07:40:55 -07:00
|
|
|
});
|
|
|
|
}
|
2020-12-01 12:10:47 -08:00
|
|
|
|
2022-08-05 07:47:57 -07:00
|
|
|
if (t == "binding") {
|
2022-05-01 07:40:55 -07:00
|
|
|
const char* use = "binding attribute";
|
|
|
|
return expect_paren_block(use, [&]() -> Result {
|
|
|
|
auto val = expect_positive_sint(use);
|
2022-05-19 13:08:19 -07:00
|
|
|
if (val.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-07-25 06:30:18 -07:00
|
|
|
match(Token::Type::kComma);
|
2020-11-09 11:39:34 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return create<ast::BindingAttribute>(t.source(), val.value);
|
|
|
|
});
|
|
|
|
}
|
2020-12-01 12:10:47 -08:00
|
|
|
|
2022-08-05 07:47:57 -07:00
|
|
|
if (t == "builtin") {
|
|
|
|
return expect_paren_block("builtin attribute", [&]() -> Result {
|
|
|
|
auto builtin = expect_builtin();
|
|
|
|
if (builtin.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
match(Token::Type::kComma);
|
|
|
|
|
|
|
|
return create<ast::BuiltinAttribute>(t.source(), builtin.value);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (t == "compute") {
|
|
|
|
return create<ast::StageAttribute>(t.source(), ast::PipelineStage::kCompute);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note, `const` is not valid in a WGSL source file, it's internal only
|
|
|
|
|
|
|
|
if (t == "fragment") {
|
|
|
|
return create<ast::StageAttribute>(t.source(), ast::PipelineStage::kFragment);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (t == "group") {
|
2022-05-01 07:40:55 -07:00
|
|
|
const char* use = "group attribute";
|
|
|
|
return expect_paren_block(use, [&]() -> Result {
|
|
|
|
auto val = expect_positive_sint(use);
|
2022-05-19 13:08:19 -07:00
|
|
|
if (val.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-07-25 06:30:18 -07:00
|
|
|
match(Token::Type::kComma);
|
2020-11-09 11:39:34 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return create<ast::GroupAttribute>(t.source(), val.value);
|
|
|
|
});
|
|
|
|
}
|
2021-06-28 16:04:43 -07:00
|
|
|
|
2022-08-05 07:47:57 -07:00
|
|
|
if (t == "id") {
|
|
|
|
const char* use = "id attribute";
|
|
|
|
return expect_paren_block(use, [&]() -> Result {
|
|
|
|
auto val = expect_positive_sint(use);
|
|
|
|
if (val.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
match(Token::Type::kComma);
|
|
|
|
|
|
|
|
return create<ast::IdAttribute>(t.source(), val.value);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (t == "interpolate") {
|
2022-05-01 07:40:55 -07:00
|
|
|
return expect_paren_block("interpolate attribute", [&]() -> Result {
|
2022-08-04 14:21:37 -07:00
|
|
|
auto type = expect_interpolation_type_name();
|
|
|
|
if (type.errored) {
|
|
|
|
return Failure::kErrored;
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
2021-06-28 16:04:43 -07:00
|
|
|
|
2022-08-04 14:21:37 -07:00
|
|
|
ast::InterpolationSampling sampling = ast::InterpolationSampling::kNone;
|
2022-05-01 07:40:55 -07:00
|
|
|
if (match(Token::Type::kComma)) {
|
2022-07-25 06:30:18 -07:00
|
|
|
if (!peek_is(Token::Type::kParenRight)) {
|
2022-08-04 14:21:37 -07:00
|
|
|
auto sample = expect_interpolation_sample_name();
|
|
|
|
if (sample.errored) {
|
|
|
|
return Failure::kErrored;
|
2022-07-25 06:30:18 -07:00
|
|
|
}
|
2022-08-04 14:21:37 -07:00
|
|
|
|
|
|
|
sampling = sample.value;
|
2022-07-25 06:30:18 -07:00
|
|
|
match(Token::Type::kComma);
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
|
|
|
}
|
2021-07-12 05:28:52 -07:00
|
|
|
|
2022-08-04 14:21:37 -07:00
|
|
|
return create<ast::InterpolateAttribute>(t.source(), type.value, sampling);
|
2022-05-01 07:40:55 -07:00
|
|
|
});
|
|
|
|
}
|
2020-11-09 11:39:34 -08:00
|
|
|
|
2022-08-05 07:47:57 -07:00
|
|
|
if (t == "invariant") {
|
2022-05-01 07:40:55 -07:00
|
|
|
return create<ast::InvariantAttribute>(t.source());
|
|
|
|
}
|
2020-12-01 12:10:47 -08:00
|
|
|
|
2022-08-05 07:47:57 -07:00
|
|
|
if (t == "location") {
|
|
|
|
const char* use = "location attribute";
|
|
|
|
return expect_paren_block(use, [&]() -> Result {
|
|
|
|
auto val = expect_positive_sint(use);
|
|
|
|
if (val.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-07-25 06:30:18 -07:00
|
|
|
match(Token::Type::kComma);
|
2022-08-05 07:47:57 -07:00
|
|
|
|
|
|
|
return create<ast::LocationAttribute>(t.source(), val.value);
|
2022-05-01 07:40:55 -07:00
|
|
|
});
|
|
|
|
}
|
2020-11-09 11:39:34 -08:00
|
|
|
|
2022-08-05 07:47:57 -07:00
|
|
|
if (t == "size") {
|
|
|
|
const char* use = "size attribute";
|
|
|
|
return expect_paren_block(use, [&]() -> Result {
|
|
|
|
auto val = expect_positive_sint(use);
|
|
|
|
if (val.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
2022-08-05 07:47:57 -07:00
|
|
|
match(Token::Type::kComma);
|
2020-11-09 11:39:34 -08:00
|
|
|
|
2022-08-05 07:47:57 -07:00
|
|
|
return create<ast::StructMemberSizeAttribute>(t.source(), val.value);
|
2022-05-01 07:40:55 -07:00
|
|
|
});
|
|
|
|
}
|
2020-12-01 12:10:47 -08:00
|
|
|
|
2022-06-09 08:33:42 -07:00
|
|
|
// TODO(crbug.com/tint/1503): Remove when deprecation period is over.
|
2022-08-05 07:47:57 -07:00
|
|
|
if (t == "stage") {
|
2022-05-01 07:40:55 -07:00
|
|
|
return expect_paren_block("stage attribute", [&]() -> Result {
|
|
|
|
auto stage = expect_pipeline_stage();
|
2022-05-19 13:08:19 -07:00
|
|
|
if (stage.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-09 11:39:34 -08:00
|
|
|
|
2022-06-09 08:33:42 -07:00
|
|
|
std::string warning = "remove stage and use @";
|
|
|
|
switch (stage.value) {
|
|
|
|
case ast::PipelineStage::kVertex:
|
|
|
|
warning += "vertex";
|
|
|
|
break;
|
|
|
|
case ast::PipelineStage::kFragment:
|
|
|
|
warning += "fragment";
|
|
|
|
break;
|
|
|
|
case ast::PipelineStage::kCompute:
|
|
|
|
warning += "compute";
|
|
|
|
break;
|
|
|
|
case ast::PipelineStage::kNone:
|
|
|
|
break;
|
2022-06-02 19:47:40 -07:00
|
|
|
}
|
2022-06-09 08:33:42 -07:00
|
|
|
deprecated(t.source(), warning);
|
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return create<ast::StageAttribute>(t.source(), stage.value);
|
|
|
|
});
|
|
|
|
}
|
2022-08-05 07:47:57 -07:00
|
|
|
|
|
|
|
if (t == "vertex") {
|
2022-06-02 19:47:40 -07:00
|
|
|
return create<ast::StageAttribute>(t.source(), ast::PipelineStage::kVertex);
|
|
|
|
}
|
2021-03-15 03:43:11 -07:00
|
|
|
|
2022-08-05 07:47:57 -07:00
|
|
|
if (t == "workgroup_size") {
|
|
|
|
return expect_paren_block("workgroup_size attribute", [&]() -> Result {
|
|
|
|
const ast::Expression* x = nullptr;
|
|
|
|
const ast::Expression* y = nullptr;
|
|
|
|
const ast::Expression* z = nullptr;
|
2021-03-15 03:43:11 -07:00
|
|
|
|
2022-08-24 14:31:45 -07:00
|
|
|
auto expr = expression();
|
2022-08-05 07:47:57 -07:00
|
|
|
if (expr.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-08-05 07:47:57 -07:00
|
|
|
} else if (!expr.matched) {
|
|
|
|
return add_error(peek(), "expected workgroup_size x parameter");
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2022-08-05 07:47:57 -07:00
|
|
|
x = std::move(expr.value);
|
2021-03-15 03:43:11 -07:00
|
|
|
|
2022-08-05 07:47:57 -07:00
|
|
|
if (match(Token::Type::kComma)) {
|
|
|
|
if (!peek_is(Token::Type::kParenRight)) {
|
2022-08-24 14:31:45 -07:00
|
|
|
expr = expression();
|
2022-08-05 07:47:57 -07:00
|
|
|
if (expr.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
} else if (!expr.matched) {
|
|
|
|
return add_error(peek(), "expected workgroup_size y parameter");
|
|
|
|
}
|
|
|
|
y = std::move(expr.value);
|
2021-03-15 03:43:11 -07:00
|
|
|
|
2022-08-05 07:47:57 -07:00
|
|
|
if (match(Token::Type::kComma)) {
|
|
|
|
if (!peek_is(Token::Type::kParenRight)) {
|
2022-08-24 14:31:45 -07:00
|
|
|
expr = expression();
|
2022-08-05 07:47:57 -07:00
|
|
|
if (expr.errored) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
} else if (!expr.matched) {
|
|
|
|
return add_error(peek(), "expected workgroup_size z parameter");
|
|
|
|
}
|
|
|
|
z = std::move(expr.value);
|
|
|
|
|
|
|
|
match(Token::Type::kComma);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2021-05-14 04:48:03 -07:00
|
|
|
|
2022-08-05 07:47:57 -07:00
|
|
|
return create<ast::WorkgroupAttribute>(t.source(), x, y, z);
|
2022-05-01 07:40:55 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
return Failure::kNoMatch;
|
2020-11-04 12:55:31 -08:00
|
|
|
}
|
|
|
|
|
2022-08-02 10:03:35 -07:00
|
|
|
bool ParserImpl::expect_attributes_consumed(utils::VectorRef<const ast::Attribute*> in) {
|
|
|
|
if (in.IsEmpty()) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
add_error(in[0]->source, "unexpected attributes");
|
|
|
|
return false;
|
2020-11-04 12:55:31 -08:00
|
|
|
}
|
|
|
|
|
2020-11-04 10:39:41 -08:00
|
|
|
bool ParserImpl::match(Token::Type tok, Source* source /*= nullptr*/) {
|
2022-07-25 09:43:08 -07:00
|
|
|
auto& t = peek();
|
2020-11-04 10:39:41 -08:00
|
|
|
|
2022-05-19 13:08:19 -07:00
|
|
|
if (source != nullptr) {
|
2022-05-01 07:40:55 -07:00
|
|
|
*source = t.source();
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-04 10:39:41 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
if (t.Is(tok)) {
|
|
|
|
next();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2020-11-04 06:14:40 -08:00
|
|
|
}
|
|
|
|
|
2022-01-25 09:15:37 -08:00
|
|
|
bool ParserImpl::expect(std::string_view use, Token::Type tok) {
|
2022-07-25 09:43:08 -07:00
|
|
|
auto& t = peek();
|
2022-05-01 07:40:55 -07:00
|
|
|
if (t.Is(tok)) {
|
|
|
|
next();
|
|
|
|
synchronized_ = true;
|
|
|
|
return true;
|
|
|
|
}
|
2021-02-01 10:23:23 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
// Special case to split `>>` and `>=` tokens if we are looking for a `>`.
|
|
|
|
if (tok == Token::Type::kGreaterThan &&
|
|
|
|
(t.Is(Token::Type::kShiftRight) || t.Is(Token::Type::kGreaterThanEqual))) {
|
|
|
|
next();
|
|
|
|
|
|
|
|
// Push the second character to the token queue.
|
|
|
|
if (t.Is(Token::Type::kShiftRight)) {
|
2022-07-25 09:43:08 -07:00
|
|
|
split_token(Token::Type::kGreaterThan, Token::Type::kGreaterThan);
|
2022-05-01 07:40:55 -07:00
|
|
|
} else if (t.Is(Token::Type::kGreaterThanEqual)) {
|
2022-07-25 09:43:08 -07:00
|
|
|
split_token(Token::Type::kGreaterThan, Token::Type::kEqual);
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
synchronized_ = true;
|
|
|
|
return true;
|
reader/wgsl: Only resync if num errors less than limit
As all loops in the parser require `synchronized_` to be true, don't set this to true if we've hit the maximum number of error diagnostics.
This lets the parser bubble up as soon as the limit was reached, instead of only aborting once we finally reach the top level loop.
ClusterFuzz has uncovered a number of cases where, in a loop, it can produce an error, then resynchronize, then error again on the next loop iteration. This produces more than the max limit of errors, and can stall the tests long enough to time out.
No unit tests for this, as it requires a really contrived input to trigger, and to exhaustively catch all the places we now call maybe_set_synchronized() would result in a large colleciton of horrible tests. Instead, let ClusterFuzz do the testing.
Fixed: chromium:1224031
Fixed: chromium:1224032
Fixed: chromium:1224042
Fixed: chromium:1224049
Fixed: chromium:1224050
Fixed: chromium:1224130
Fixed: chromium:1224131
Fixed: chromium:1224132
Fixed: chromium:1224144
Fixed: chromium:1224191
Change-Id: I63f160e28d13b85ee92e1b921239d7fb8ac22a08
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/56070
Auto-Submit: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: James Price <jrprice@google.com>
2021-06-28 09:09:57 -07:00
|
|
|
}
|
2021-02-01 10:23:23 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
// Error cases
|
|
|
|
synchronized_ = false;
|
|
|
|
if (handle_error(t)) {
|
|
|
|
return false;
|
|
|
|
}
|
2021-02-01 10:23:23 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
std::stringstream err;
|
|
|
|
err << "expected '" << Token::TypeToName(tok) << "'";
|
|
|
|
if (!use.empty()) {
|
|
|
|
err << " for " << use;
|
|
|
|
}
|
|
|
|
add_error(t, err.str());
|
2022-03-15 09:18:03 -07:00
|
|
|
return false;
|
2020-11-04 06:14:40 -08:00
|
|
|
}
|
|
|
|
|
2022-01-25 09:15:37 -08:00
|
|
|
Expect<int32_t> ParserImpl::expect_sint(std::string_view use) {
|
2022-07-25 09:43:08 -07:00
|
|
|
auto& t = peek();
|
2022-05-10 15:26:04 -07:00
|
|
|
if (!t.Is(Token::Type::kIntLiteral) && !t.Is(Token::Type::kIntLiteral_I)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return add_error(t.source(), "expected signed integer literal", use);
|
2022-05-04 14:01:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
int64_t val = t.to_i64();
|
|
|
|
if ((val > std::numeric_limits<int32_t>::max()) ||
|
|
|
|
(val < std::numeric_limits<int32_t>::min())) {
|
|
|
|
// TODO(crbug.com/tint/1504): Test this when abstract int is implemented
|
|
|
|
return add_error(t.source(), "value overflows i32", use);
|
|
|
|
}
|
2020-11-09 11:39:34 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
next();
|
2022-05-04 14:01:12 -07:00
|
|
|
return {static_cast<int32_t>(t.to_i64()), t.source()};
|
2020-11-04 06:14:40 -08:00
|
|
|
}
|
|
|
|
|
2022-01-25 09:15:37 -08:00
|
|
|
Expect<uint32_t> ParserImpl::expect_positive_sint(std::string_view use) {
|
2022-05-01 07:40:55 -07:00
|
|
|
auto sint = expect_sint(use);
|
2022-05-19 13:08:19 -07:00
|
|
|
if (sint.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-04 06:14:40 -08:00
|
|
|
|
2022-05-19 13:08:19 -07:00
|
|
|
if (sint.value < 0) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return add_error(sint.source, std::string(use) + " must be positive");
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-09 11:39:34 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return {static_cast<uint32_t>(sint.value), sint.source};
|
2020-11-04 06:14:40 -08:00
|
|
|
}
|
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
Expect<uint32_t> ParserImpl::expect_nonzero_positive_sint(std::string_view use) {
|
|
|
|
auto sint = expect_sint(use);
|
2022-05-19 13:08:19 -07:00
|
|
|
if (sint.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-04 06:14:40 -08:00
|
|
|
|
2022-05-19 13:08:19 -07:00
|
|
|
if (sint.value <= 0) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return add_error(sint.source, std::string(use) + " must be greater than 0");
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-09 11:39:34 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return {static_cast<uint32_t>(sint.value), sint.source};
|
2020-11-04 06:14:40 -08:00
|
|
|
}
|
|
|
|
|
2022-01-25 09:15:37 -08:00
|
|
|
Expect<std::string> ParserImpl::expect_ident(std::string_view use) {
|
2022-07-25 09:43:08 -07:00
|
|
|
auto& t = peek();
|
2022-05-01 07:40:55 -07:00
|
|
|
if (t.IsIdentifier()) {
|
|
|
|
synchronized_ = true;
|
|
|
|
next();
|
2021-07-05 14:48:37 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
if (is_reserved(t)) {
|
2022-08-02 19:49:28 -07:00
|
|
|
deprecated(t.source(), "'" + t.to_str() + "' is a reserved keyword");
|
2022-05-01 07:40:55 -07:00
|
|
|
}
|
2021-07-05 14:48:37 -07:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return {t.to_str(), t.source()};
|
|
|
|
}
|
|
|
|
if (handle_error(t)) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
|
|
|
synchronized_ = false;
|
|
|
|
return add_error(t.source(), "expected identifier", use);
|
2020-11-09 11:39:34 -08:00
|
|
|
}
|
2020-11-04 10:39:41 -08:00
|
|
|
|
2020-11-04 12:08:51 -08:00
|
|
|
template <typename F, typename T>
|
2022-05-01 07:40:55 -07:00
|
|
|
T ParserImpl::expect_block(Token::Type start, Token::Type end, std::string_view use, F&& body) {
|
|
|
|
if (!expect(use, start)) {
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
2020-11-11 10:44:36 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return sync(end, [&]() -> T {
|
|
|
|
auto res = body();
|
2020-11-11 10:44:36 -08:00
|
|
|
|
2022-05-19 13:08:19 -07:00
|
|
|
if (res.errored) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-11 10:44:36 -08:00
|
|
|
|
2022-05-19 13:08:19 -07:00
|
|
|
if (!expect(use, end)) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return Failure::kErrored;
|
2022-05-19 13:08:19 -07:00
|
|
|
}
|
2020-11-11 10:44:36 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return res;
|
|
|
|
});
|
2020-11-04 12:08:51 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename F, typename T>
|
2022-01-25 09:15:37 -08:00
|
|
|
T ParserImpl::expect_paren_block(std::string_view use, F&& body) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return expect_block(Token::Type::kParenLeft, Token::Type::kParenRight, use,
|
|
|
|
std::forward<F>(body));
|
2020-11-04 12:08:51 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename F, typename T>
|
2022-01-25 09:15:37 -08:00
|
|
|
T ParserImpl::expect_brace_block(std::string_view use, F&& body) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return expect_block(Token::Type::kBraceLeft, Token::Type::kBraceRight, use,
|
|
|
|
std::forward<F>(body));
|
2020-11-04 12:08:51 -08:00
|
|
|
}
|
|
|
|
|
2020-11-11 06:10:25 -08:00
|
|
|
template <typename F, typename T>
|
2022-01-25 09:15:37 -08:00
|
|
|
T ParserImpl::expect_lt_gt_block(std::string_view use, F&& body) {
|
2022-05-01 07:40:55 -07:00
|
|
|
return expect_block(Token::Type::kLessThan, Token::Type::kGreaterThan, use,
|
|
|
|
std::forward<F>(body));
|
2020-11-11 06:10:25 -08:00
|
|
|
}
|
|
|
|
|
2020-11-11 10:44:36 -08:00
|
|
|
template <typename F, typename T>
|
|
|
|
T ParserImpl::sync(Token::Type tok, F&& body) {
|
2022-05-01 07:40:55 -07:00
|
|
|
if (parse_depth_ >= kMaxParseDepth) {
|
|
|
|
// We've hit a maximum parser recursive depth.
|
|
|
|
// We can't call into body() as we might stack overflow.
|
|
|
|
// Instead, report an error...
|
|
|
|
add_error(peek(), "maximum parser recursive depth reached");
|
|
|
|
// ...and try to resynchronize. If we cannot resynchronize to `tok` then
|
|
|
|
// synchronized_ is set to false, and the parser knows that forward progress
|
|
|
|
// is not being made.
|
|
|
|
sync_to(tok, /* consume: */ true);
|
|
|
|
return Failure::kErrored;
|
|
|
|
}
|
2021-02-17 08:16:02 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
sync_tokens_.push_back(tok);
|
2020-11-11 10:44:36 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
++parse_depth_;
|
|
|
|
auto result = body();
|
|
|
|
--parse_depth_;
|
2020-11-11 10:44:36 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
if (sync_tokens_.back() != tok) {
|
|
|
|
TINT_ICE(Reader, builder_.Diagnostics()) << "sync_tokens is out of sync";
|
|
|
|
}
|
|
|
|
sync_tokens_.pop_back();
|
2020-11-11 10:44:36 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
if (result.errored) {
|
|
|
|
sync_to(tok, /* consume: */ true);
|
|
|
|
}
|
2020-11-11 10:44:36 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return result;
|
2020-11-11 10:44:36 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ParserImpl::sync_to(Token::Type tok, bool consume) {
|
2022-05-01 07:40:55 -07:00
|
|
|
// Clear the synchronized state - gets set to true again on success.
|
|
|
|
synchronized_ = false;
|
2020-11-11 10:44:36 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
BlockCounters counters;
|
2020-11-11 10:44:36 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
for (size_t i = 0; i < kMaxResynchronizeLookahead; i++) {
|
2022-07-25 09:43:08 -07:00
|
|
|
auto& t = peek(i);
|
2022-05-01 07:40:55 -07:00
|
|
|
if (counters.consume(t) > 0) {
|
|
|
|
continue; // Nested block
|
|
|
|
}
|
|
|
|
if (!t.Is(tok) && !is_sync_token(t)) {
|
|
|
|
continue; // Not a synchronization point
|
|
|
|
}
|
2020-11-11 10:44:36 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
// Synchronization point found.
|
2020-11-11 10:44:36 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
// Skip any tokens we don't understand, bringing us to just before the
|
|
|
|
// resync point.
|
|
|
|
while (i-- > 0) {
|
|
|
|
next();
|
|
|
|
}
|
2020-11-11 10:44:36 -08:00
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
// Is this synchronization token |tok|?
|
|
|
|
if (t.Is(tok)) {
|
|
|
|
if (consume) {
|
|
|
|
next();
|
|
|
|
}
|
|
|
|
synchronized_ = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
2020-11-11 10:44:36 -08:00
|
|
|
}
|
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
return false;
|
2020-11-11 10:44:36 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ParserImpl::is_sync_token(const Token& t) const {
|
2022-05-01 07:40:55 -07:00
|
|
|
for (auto r : sync_tokens_) {
|
|
|
|
if (t.Is(r)) {
|
|
|
|
return true;
|
|
|
|
}
|
2021-07-12 09:50:31 -07:00
|
|
|
}
|
2022-05-01 07:40:55 -07:00
|
|
|
return false;
|
reader/wgsl: Only resync if num errors less than limit
As all loops in the parser require `synchronized_` to be true, don't set this to true if we've hit the maximum number of error diagnostics.
This lets the parser bubble up as soon as the limit was reached, instead of only aborting once we finally reach the top level loop.
ClusterFuzz has uncovered a number of cases where, in a loop, it can produce an error, then resynchronize, then error again on the next loop iteration. This produces more than the max limit of errors, and can stall the tests long enough to time out.
No unit tests for this, as it requires a really contrived input to trigger, and to exhaustively catch all the places we now call maybe_set_synchronized() would result in a large colleciton of horrible tests. Instead, let ClusterFuzz do the testing.
Fixed: chromium:1224031
Fixed: chromium:1224032
Fixed: chromium:1224042
Fixed: chromium:1224049
Fixed: chromium:1224050
Fixed: chromium:1224130
Fixed: chromium:1224131
Fixed: chromium:1224132
Fixed: chromium:1224144
Fixed: chromium:1224191
Change-Id: I63f160e28d13b85ee92e1b921239d7fb8ac22a08
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/56070
Auto-Submit: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: James Price <jrprice@google.com>
2021-06-28 09:09:57 -07:00
|
|
|
}
|
|
|
|
|
2022-03-15 09:18:03 -07:00
|
|
|
bool ParserImpl::handle_error(const Token& t) {
|
2022-05-01 07:40:55 -07:00
|
|
|
// The token might itself be an error.
|
|
|
|
if (t.IsError()) {
|
|
|
|
synchronized_ = false;
|
|
|
|
add_error(t.source(), t.to_str());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2022-03-15 09:18:03 -07:00
|
|
|
}
|
|
|
|
|
2020-11-19 06:19:31 -08:00
|
|
|
template <typename F, typename T>
|
|
|
|
T ParserImpl::without_error(F&& body) {
|
2022-05-01 07:40:55 -07:00
|
|
|
silence_errors_++;
|
|
|
|
auto result = body();
|
|
|
|
silence_errors_--;
|
|
|
|
return result;
|
2020-11-19 06:19:31 -08:00
|
|
|
}
|
|
|
|
|
2021-04-27 10:32:37 -07:00
|
|
|
ParserImpl::MultiTokenSource ParserImpl::make_source_range() {
|
2022-05-01 07:40:55 -07:00
|
|
|
return MultiTokenSource(this);
|
2021-04-27 10:32:37 -07:00
|
|
|
}
|
|
|
|
|
2022-05-01 07:40:55 -07:00
|
|
|
ParserImpl::MultiTokenSource ParserImpl::make_source_range_from(const Source& start) {
|
|
|
|
return MultiTokenSource(this, start);
|
2021-04-27 10:32:37 -07:00
|
|
|
}
|
|
|
|
|
2022-04-07 10:55:04 -07:00
|
|
|
} // namespace tint::reader::wgsl
|