| // 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. |
| |
| #ifndef SRC_TINT_LANG_WGSL_READER_PARSER_PARSER_H_ |
| #define SRC_TINT_LANG_WGSL_READER_PARSER_PARSER_H_ |
| |
| #include <memory> |
| #include <string> |
| #include <string_view> |
| #include <unordered_map> |
| #include <utility> |
| #include <vector> |
| |
| #include "src/tint/lang/core/access.h" |
| #include "src/tint/lang/wgsl/program/program_builder.h" |
| #include "src/tint/lang/wgsl/reader/parser/detail.h" |
| #include "src/tint/lang/wgsl/reader/parser/token.h" |
| #include "src/tint/lang/wgsl/resolver/resolve.h" |
| #include "src/tint/utils/diagnostic/formatter.h" |
| |
| namespace tint::ast { |
| class BreakStatement; |
| class CallStatement; |
| class ContinueStatement; |
| class IfStatement; |
| class LoopStatement; |
| class ReturnStatement; |
| class SwitchStatement; |
| class VariableDeclStatement; |
| } // namespace tint::ast |
| |
| namespace tint::wgsl::reader { |
| |
| class Lexer; |
| |
| /// Struct holding information for a for loop |
| struct ForHeader { |
| /// Constructor |
| /// @param init the initializer statement |
| /// @param cond the condition statement |
| /// @param cont the continuing statement |
| ForHeader(const ast::Statement* init, const ast::Expression* cond, const ast::Statement* cont); |
| |
| ~ForHeader(); |
| |
| /// The for loop initializer |
| const ast::Statement* initializer = nullptr; |
| /// The for loop condition |
| const ast::Expression* condition = nullptr; |
| /// The for loop continuing statement |
| const ast::Statement* continuing = nullptr; |
| }; |
| |
| /// Parser for WGSL source data |
| class Parser { |
| /// Failure holds enumerator values used for the constructing an Expect and |
| /// Match in an errored state. |
| struct Failure { |
| enum Errored { kErrored }; |
| enum NoMatch { kNoMatch }; |
| }; |
| |
| public: |
| /// Pre-determined small vector sizes for AST pointers |
| //! @cond Doxygen_Suppress |
| using AttributeList = Vector<const ast::Attribute*, 4>; |
| using CaseSelectorList = Vector<const ast::CaseSelector*, 4>; |
| using CaseStatementList = Vector<const ast::CaseStatement*, 4>; |
| using ExpressionList = Vector<const ast::Expression*, 8>; |
| using ParameterList = Vector<const ast::Parameter*, 8>; |
| using StatementList = Vector<const ast::Statement*, 8>; |
| using StructMemberList = Vector<const ast::StructMember*, 8>; |
| //! @endcond |
| |
| /// Empty structure used by functions that do not return a value, but need to signal success / |
| /// error with Expect<Void> or Maybe<NoError>. |
| struct Void {}; |
| |
| /// Expect is the return type of the parser methods that are expected to |
| /// return a parsed value of type T, unless there was an parse error. |
| /// In the case of a parse error the called method will have called |
| /// add_error() and #errored will be set to true. |
| template <typename T> |
| struct Expect { |
| /// An alias to the templated type T. |
| using type = T; |
| |
| /// Don't allow an Expect to take a nullptr. |
| inline Expect(std::nullptr_t) = delete; // NOLINT |
| |
| /// Constructor for a successful parse. |
| /// @param val the result value of the parse |
| template <typename U> |
| inline Expect(U&& val) // NOLINT |
| : value(std::forward<U>(val)) {} |
| |
| /// Constructor for parse error. |
| inline Expect(Failure::Errored) : errored(true) {} // NOLINT |
| |
| /// Copy constructor |
| inline Expect(const Expect&) = default; |
| /// Move constructor |
| inline Expect(Expect&&) = default; |
| /// Assignment operator |
| /// @return this Expect |
| inline Expect& operator=(const Expect&) = default; |
| /// Assignment move operator |
| /// @return this Expect |
| inline Expect& operator=(Expect&&) = default; |
| |
| /// @return a pointer to the returned value. If T is a pointer or |
| /// std::unique_ptr, operator->() automatically dereferences so that the |
| /// return type will always be a pointer to a non-pointer type. #errored |
| /// must be false to call. |
| inline typename detail::OperatorArrow<T>::type operator->() { |
| TINT_ASSERT(!errored); |
| return detail::OperatorArrow<T>::ptr(value); |
| } |
| |
| /// The expected value of a successful parse. |
| /// Zero-initialized when there was a parse error. |
| T value{}; |
| /// True if there was a error parsing. |
| bool errored = false; |
| }; |
| |
| /// Maybe is the return type of the parser methods that attempts to match a |
| /// grammar and return a parsed value of type T, or may parse part of the |
| /// grammar and then hit a parse error. |
| /// In the case of a successful grammar match, the Maybe will have #matched |
| /// set to true. |
| /// In the case of a parse error the called method will have called |
| /// add_error() and the Maybe will have #errored set to true. |
| template <typename T> |
| struct Maybe { |
| inline Maybe(std::nullptr_t) = delete; // NOLINT |
| |
| /// Constructor for a successful parse. |
| /// @param val the result value of the parse |
| template <typename U> |
| inline Maybe(U&& val) // NOLINT |
| : value(std::forward<U>(val)), matched(true) {} |
| |
| /// Constructor for parse error state. |
| inline Maybe(Failure::Errored) : errored(true) {} // NOLINT |
| |
| /// Constructor for the no-match state. |
| inline Maybe(Failure::NoMatch) {} // NOLINT |
| |
| /// Constructor from an Expect. |
| /// @param e the Expect to copy this Maybe from |
| template <typename U> |
| inline Maybe(const Expect<U>& e) // NOLINT |
| : value(e.value), errored(e.errored), matched(!e.errored) {} |
| |
| /// Move from an Expect. |
| /// @param e the Expect to move this Maybe from |
| template <typename U> |
| inline Maybe(Expect<U>&& e) // NOLINT |
| : value(std::move(e.value)), errored(e.errored), matched(!e.errored) {} |
| |
| /// Copy constructor |
| inline Maybe(const Maybe&) = default; |
| /// Move constructor |
| inline Maybe(Maybe&&) = default; |
| /// Assignment operator |
| /// @return this Maybe |
| inline Maybe& operator=(const Maybe&) = default; |
| /// Assignment move operator |
| /// @return this Maybe |
| inline Maybe& operator=(Maybe&&) = default; |
| |
| /// @return a pointer to the returned value. If T is a pointer or |
| /// std::unique_ptr, operator->() automatically dereferences so that the |
| /// return type will always be a pointer to a non-pointer type. #errored |
| /// must be false to call. |
| inline typename detail::OperatorArrow<T>::type operator->() { |
| TINT_ASSERT(!errored); |
| return detail::OperatorArrow<T>::ptr(value); |
| } |
| |
| /// The value of a successful parse. |
| /// Zero-initialized when there was a parse error. |
| T value{}; |
| /// True if there was a error parsing. |
| bool errored = false; |
| /// True if there was a error parsing. |
| bool matched = false; |
| }; |
| |
| /// TypedIdentifier holds a parsed identifier and type. Returned by |
| /// variable_ident_decl(). |
| struct TypedIdentifier { |
| /// Constructor |
| TypedIdentifier(); |
| /// Copy constructor |
| /// @param other the FunctionHeader to copy |
| TypedIdentifier(const TypedIdentifier& other); |
| /// Constructor |
| /// @param type_in parsed type |
| /// @param name_in parsed identifier |
| TypedIdentifier(ast::Type type_in, const ast::Identifier* name_in); |
| /// Destructor |
| ~TypedIdentifier(); |
| |
| /// Parsed type. type.expr be nullptr for inferred types. |
| ast::Type type; |
| /// Parsed identifier. |
| const ast::Identifier* name = nullptr; |
| }; |
| |
| /// FunctionHeader contains the parsed information for a function header. |
| struct FunctionHeader { |
| /// Constructor |
| FunctionHeader(); |
| /// Copy constructor |
| /// @param other the FunctionHeader to copy |
| FunctionHeader(const FunctionHeader& other); |
| /// Constructor |
| /// @param src parsed header source |
| /// @param n function name |
| /// @param p function parameters |
| /// @param ret_ty function return type |
| /// @param ret_attrs return type attributes |
| FunctionHeader(Source src, |
| const ast::Identifier* n, |
| VectorRef<const ast::Parameter*> p, |
| ast::Type ret_ty, |
| VectorRef<const ast::Attribute*> ret_attrs); |
| /// Destructor |
| ~FunctionHeader(); |
| /// Assignment operator |
| /// @param other the FunctionHeader to copy |
| /// @returns this FunctionHeader |
| FunctionHeader& operator=(const FunctionHeader& other); |
| |
| /// Parsed header source |
| Source source; |
| /// Function name |
| const ast::Identifier* name; |
| /// Function parameters |
| Vector<const ast::Parameter*, 8> params; |
| /// Function return type |
| ast::Type return_type; |
| /// Function return type attributes |
| AttributeList return_type_attributes; |
| }; |
| |
| /// VarDeclInfo contains the parsed information for variable declaration. |
| struct VarDeclInfo { |
| /// Variable declaration source |
| Source source; |
| /// Variable name |
| const ast::Identifier* name = nullptr; |
| /// Variable address space |
| const ast::Expression* address_space = nullptr; |
| /// Variable access control |
| const ast::Expression* access = nullptr; |
| /// Variable type |
| ast::Type type; |
| }; |
| |
| /// VariableQualifier contains the parsed information for a variable qualifier |
| struct VariableQualifier { |
| /// The variable's address space |
| const ast::Expression* address_space = nullptr; |
| /// The variable's access control |
| const ast::Expression* access = nullptr; |
| }; |
| |
| /// MatrixDimensions contains the column and row information for a matrix |
| struct MatrixDimensions { |
| /// The number of columns |
| uint32_t columns = 0; |
| /// The number of rows |
| uint32_t rows = 0; |
| }; |
| |
| /// Creates a new parser using the given file |
| /// @param file the input source file to parse |
| explicit Parser(Source::File const* file); |
| ~Parser(); |
| |
| /// Reads tokens from the source file. This will be called automatically |
| /// by |parse|. |
| void InitializeLex(); |
| |
| /// Run the parser |
| /// @returns true if the parse was successful, false otherwise. |
| bool Parse(); |
| |
| /// set_max_diagnostics sets the maximum number of reported errors before |
| /// aborting parsing. |
| /// @param limit the new maximum number of errors |
| void set_max_errors(size_t limit) { max_errors_ = limit; } |
| |
| /// @return the number of maximum number of reported errors before aborting |
| /// parsing. |
| size_t get_max_errors() const { return max_errors_; } |
| |
| /// @returns true if an error was encountered. |
| bool has_error() const { return builder_.Diagnostics().contains_errors(); } |
| |
| /// @returns the parser error string |
| std::string error() const { |
| diag::Formatter formatter{{false, false, false, false}}; |
| return formatter.format(builder_.Diagnostics()); |
| } |
| |
| /// @returns the Program. The program builder in the parser will be reset |
| /// after this. |
| Program program() { return resolver::Resolve(builder_); } |
| |
| /// @returns the program builder. |
| ProgramBuilder& builder() { return builder_; } |
| |
| /// @returns the next token |
| const Token& next(); |
| /// Peeks ahead and returns the token at `idx` ahead of the current position |
| /// @param idx the index of the token to return |
| /// @returns the token `idx` positions ahead without advancing |
| const Token& peek(size_t idx = 0); |
| /// Peeks ahead and returns true if the token at `idx` ahead of the current |
| /// position is |tok| |
| /// @param idx the index of the token to return |
| /// @param tok the token to look for |
| /// @returns true if the token `idx` positions ahead is |tok| |
| bool peek_is(Token::Type tok, size_t idx = 0); |
| /// @returns the last source location that was returned by `next()` |
| Source last_source() const; |
| /// Appends an error at `t` with the message `msg` |
| /// @param t the token to associate the error with |
| /// @param msg the error message |
| /// @return `Failure::Errored::kError` so that you can combine an add_error() |
| /// call and return on the same line. |
| Failure::Errored add_error(const Token& t, std::string_view msg); |
| /// Appends an error raised when parsing `use` at `t` with the message |
| /// `msg` |
| /// @param source the source to associate the error with |
| /// @param msg the error message |
| /// @param use a description of what was being parsed when the error was |
| /// raised. |
| /// @return `Failure::Errored::kError` so that you can combine an add_error() |
| /// call and return on the same line. |
| Failure::Errored add_error(const Source& source, std::string_view msg, std::string_view use); |
| /// Appends an error at `source` with the message `msg` |
| /// @param source the source to associate the error with |
| /// @param msg the error message |
| /// @return `Failure::Errored::kError` so that you can combine an add_error() |
| /// call and return on the same line. |
| Failure::Errored add_error(const Source& source, std::string_view msg); |
| /// Appends a note at `source` with the message `msg` |
| /// @param source the source to associate the error with |
| /// @param msg the note message |
| void add_note(const Source& source, std::string_view msg); |
| /// Appends a deprecated-language-feature warning at `source` with the message |
| /// `msg` |
| /// @param source the source to associate the error with |
| /// @param msg the warning message |
| void deprecated(const Source& source, std::string_view msg); |
| /// Parses the `translation_unit` grammar element |
| void translation_unit(); |
| /// Parses the `global_directive` grammar element, erroring on parse failure. |
| /// @param has_parsed_decl flag indicating if the parser has consumed a global declaration. |
| /// @return true on parse success, otherwise an error or no-match. |
| Maybe<Void> global_directive(bool has_parsed_decl); |
| /// Parses the `diagnostic_directive` grammar element, erroring on parse failure. |
| /// @return true on parse success, otherwise an error or no-match. |
| Maybe<Void> diagnostic_directive(); |
| /// Parses the `enable_directive` grammar element, erroring on parse failure. |
| /// @return true on parse success, otherwise an error or no-match. |
| Maybe<Void> enable_directive(); |
| /// Parses the `requires_directive` grammar element, erroring on parse failure. |
| /// @return true on parse success, otherwise an error or no-match. |
| Maybe<Void> requires_directive(); |
| /// Parses the `global_decl` grammar element, erroring on parse failure. |
| /// @return true on parse success, otherwise an error or no-match. |
| Maybe<Void> global_decl(); |
| /// Parses a `global_variable_decl` grammar element with the initial |
| /// `variable_attribute_list*` provided as `attrs` |
| /// @returns the variable parsed or nullptr |
| /// @param attrs the list of attributes for the variable declaration. If attributes are consumed |
| /// by the declaration, then this vector is cleared before returning. |
| Maybe<const ast::Variable*> global_variable_decl(AttributeList& attrs); |
| /// Parses a `global_constant_decl` grammar element with the initial |
| /// `variable_attribute_list*` provided as `attrs` |
| /// @returns the const object or nullptr |
| /// @param attrs the list of attributes for the constant declaration. If attributes are consumed |
| /// by the declaration, then this vector is cleared before returning. |
| Maybe<const ast::Variable*> global_constant_decl(AttributeList& attrs); |
| /// Parses a `variable_decl` grammar element |
| /// @returns the parsed variable declaration info |
| Maybe<VarDeclInfo> variable_decl(); |
| /// Helper for parsing ident with an optional type declaration. Should not be called directly, |
| /// use the specific version below. |
| /// @param use a description of what was being parsed if an error was raised. |
| /// @param allow_inferred allow the identifier to be parsed without a type |
| /// @returns the parsed identifier, and possibly type, or empty otherwise |
| Expect<TypedIdentifier> expect_ident_with_optional_type_specifier(std::string_view use, |
| bool allow_inferred); |
| /// Parses a `ident` or a `variable_ident_decl` grammar element, erroring on parse failure. |
| /// @param use a description of what was being parsed if an error was raised. |
| /// @returns the identifier or empty otherwise. |
| Expect<TypedIdentifier> expect_optionally_typed_ident(std::string_view use); |
| /// Parses a `variable_ident_decl` grammar element, erroring on parse failure. |
| /// @param use a description of what was being parsed if an error was raised. |
| /// @returns the identifier and type parsed or empty otherwise |
| Expect<TypedIdentifier> expect_ident_with_type_specifier(std::string_view use); |
| /// Parses a `variable_qualifier` grammar element |
| /// @returns the variable qualifier information |
| Maybe<VariableQualifier> variable_qualifier(); |
| /// Parses a `type_alias_decl` grammar element |
| /// @returns the type alias or nullptr on error |
| Maybe<const ast::Alias*> type_alias_decl(); |
| /// Parses a `type_specifier` grammar element |
| /// @returns the parsed Type or nullptr if none matched. |
| Maybe<ast::Type> type_specifier(); |
| /// Parses a `struct_decl` grammar element. |
| /// @returns the struct type or nullptr on error |
| Maybe<const ast::Struct*> struct_decl(); |
| /// Parses a `struct_body_decl` grammar element, erroring on parse failure. |
| /// @returns the struct members |
| Expect<StructMemberList> expect_struct_body_decl(); |
| /// Parses a `struct_member` grammar element, erroring on parse failure. |
| /// @returns the struct member or nullptr |
| Expect<const ast::StructMember*> expect_struct_member(); |
| /// Parses a `function_decl` grammar element with the initial |
| /// `function_attribute_decl*` provided as `attrs`. |
| /// @param attrs the list of attributes for the function declaration. If attributes are consumed |
| /// by the declaration, then this vector is cleared before returning. |
| /// @returns the parsed function, nullptr otherwise |
| Maybe<const ast::Function*> function_decl(AttributeList& attrs); |
| /// Parses a `const_assert_statement` grammar element |
| /// @returns returns the const assert, if it matched. |
| Maybe<const ast::ConstAssert*> const_assert_statement(); |
| /// Parses a `function_header` grammar element |
| /// @returns the parsed function header |
| Maybe<FunctionHeader> function_header(); |
| /// Parses a `param_list` grammar element, erroring on parse failure. |
| /// @returns the parsed variables |
| Expect<ParameterList> expect_param_list(); |
| /// Parses a `param` grammar element, erroring on parse failure. |
| /// @returns the parsed variable |
| Expect<const ast::Parameter*> expect_param(); |
| /// Parses a `pipeline_stage` grammar element, erroring if the next token does |
| /// not match a stage name. |
| /// @returns the pipeline stage. |
| Expect<ast::PipelineStage> expect_pipeline_stage(); |
| /// Parses a `compound_statement` grammar element, erroring on parse failure. |
| /// @param use a description of what was being parsed if an error was raised |
| /// @returns the parsed statements |
| Expect<ast::BlockStatement*> expect_compound_statement(std::string_view use); |
| /// Parses a `compound_statement` grammar element, with the attribute list provided as `attrs`. |
| /// @param attrs the list of attributes for the statement |
| /// @param use a description of what was being parsed if an error was raised |
| /// @returns the parsed statements |
| Expect<ast::BlockStatement*> expect_compound_statement(AttributeList& attrs, |
| std::string_view use); |
| /// Parses a `paren_expression` grammar element, erroring on parse failure. |
| /// @returns the parsed element or nullptr |
| Expect<const ast::Expression*> expect_paren_expression(); |
| /// Parses a `statements` grammar element |
| /// @returns the statements parsed |
| Expect<StatementList> expect_statements(); |
| /// Parses a `statement` grammar element |
| /// @returns the parsed statement or nullptr |
| Maybe<const ast::Statement*> statement(); |
| /// Parses a `break_statement` grammar element |
| /// @returns the parsed statement or nullptr |
| Maybe<const ast::BreakStatement*> break_statement(); |
| /// Parses a `return_statement` grammar element |
| /// @returns the parsed statement or nullptr |
| Maybe<const ast::ReturnStatement*> return_statement(); |
| /// Parses a `continue_statement` grammar element |
| /// @returns the parsed statement or nullptr |
| Maybe<const ast::ContinueStatement*> continue_statement(); |
| /// Parses a `variable_statement` grammar element |
| /// @returns the parsed variable or nullptr |
| Maybe<const ast::VariableDeclStatement*> variable_statement(); |
| /// Parses a `if_statement` grammar element, with the attribute list provided as `attrs`. |
| /// @param attrs the list of attributes for the statement |
| /// @returns the parsed statement or nullptr |
| Maybe<const ast::IfStatement*> if_statement(AttributeList& attrs); |
| /// Parses a `switch_statement` grammar element |
| /// @param attrs the list of attributes for the statement |
| /// @returns the parsed statement or nullptr |
| Maybe<const ast::SwitchStatement*> switch_statement(AttributeList& attrs); |
| /// Parses a `switch_body` grammar element |
| /// @returns the parsed statement or nullptr |
| Maybe<const ast::CaseStatement*> switch_body(); |
| /// Parses a `case_selectors` grammar element |
| /// @returns the list of literals |
| Expect<CaseSelectorList> expect_case_selectors(); |
| /// Parses a `case_selector` grammar element |
| /// @returns the selector |
| Maybe<const ast::CaseSelector*> case_selector(); |
| /// Parses a `func_call_statement` grammar element |
| /// @returns the parsed function call or nullptr |
| Maybe<const ast::CallStatement*> func_call_statement(); |
| /// Parses a `loop_statement` grammar element, with the attribute list provided as `attrs`. |
| /// @param attrs the list of attributes for the statement |
| /// @returns the parsed loop or nullptr |
| Maybe<const ast::LoopStatement*> loop_statement(AttributeList& attrs); |
| /// Parses a `for_header` grammar element, erroring on parse failure. |
| /// @returns the parsed for header or nullptr |
| Expect<std::unique_ptr<ForHeader>> expect_for_header(); |
| /// Parses a `for_statement` grammar element, with the attribute list provided as `attrs`. |
| /// @param attrs the list of attributes for the statement |
| /// @returns the parsed for loop or nullptr |
| Maybe<const ast::ForLoopStatement*> for_statement(AttributeList& attrs); |
| /// Parses a `while_statement` grammar element, with the attribute list provided as `attrs`. |
| /// @param attrs the list of attributes for the statement |
| /// @returns the parsed while loop or nullptr |
| Maybe<const ast::WhileStatement*> while_statement(AttributeList& attrs); |
| /// Parses a `break_if_statement` grammar element |
| /// @returns the parsed statement or nullptr |
| Maybe<const ast::Statement*> break_if_statement(); |
| /// Parses a `continuing_compound_statement` grammar element |
| /// @returns the parsed statements |
| Maybe<const ast::BlockStatement*> continuing_compound_statement(); |
| /// Parses a `continuing_statement` grammar element |
| /// @returns the parsed statements |
| Maybe<const ast::BlockStatement*> continuing_statement(); |
| /// Parses a `const_literal` grammar element |
| /// @returns the const literal parsed or nullptr if none found |
| Maybe<const ast::LiteralExpression*> const_literal(); |
| /// Parses a `primary_expression` grammar element |
| /// @returns the parsed expression or nullptr |
| Maybe<const ast::Expression*> primary_expression(); |
| /// Parses a `argument_expression_list` grammar element, erroring on parse |
| /// failure. |
| /// @param use a description of what was being parsed if an error was raised |
| /// @returns the list of arguments |
| Expect<ExpressionList> expect_argument_expression_list(std::string_view use); |
| /// Parses the recursive portion of the component_or_swizzle_specifier |
| /// @param prefix the left side of the expression |
| /// @returns the parsed expression or nullptr |
| Maybe<const ast::Expression*> component_or_swizzle_specifier(const ast::Expression* prefix); |
| /// Parses a `singular_expression` grammar elment |
| /// @returns the parsed expression or nullptr |
| Maybe<const ast::Expression*> singular_expression(); |
| /// Parses a `unary_expression` grammar element |
| /// @returns the parsed expression or nullptr |
| Maybe<const ast::Expression*> unary_expression(); |
| /// Parses the `expression` grammar rule |
| /// @returns the parsed expression or nullptr |
| Maybe<const ast::Expression*> expression(); |
| /// Parses the `expression` grammar rule |
| /// @param use the use of the expression |
| /// @returns the parsed expression or error |
| Expect<const ast::Expression*> expect_expression(std::string_view use); |
| /// Parses a comma separated expression list |
| /// @param use the use of the expression list |
| /// @param terminator the terminating token for the list |
| /// @returns the parsed expression list or error |
| Maybe<Parser::ExpressionList> expression_list(std::string_view use, Token::Type terminator); |
| /// Parses a comma separated expression list, with at least one expression |
| /// @param use the use of the expression list |
| /// @param terminator the terminating token for the list |
| /// @returns the parsed expression list or error |
| Expect<Parser::ExpressionList> expect_expression_list(std::string_view use, |
| Token::Type terminator); |
| /// Parses the `bitwise_expression.post.unary_expression` grammar element |
| /// @param lhs the left side of the expression |
| /// @returns the parsed expression or nullptr |
| Maybe<const ast::Expression*> bitwise_expression_post_unary_expression( |
| const ast::Expression* lhs); |
| /// Parse the `multiplicative_operator` grammar element |
| /// @returns the parsed operator if successful |
| Maybe<core::BinaryOp> multiplicative_operator(); |
| /// Parses multiplicative elements |
| /// @param lhs the left side of the expression |
| /// @returns the parsed expression or `lhs` if no match |
| Expect<const ast::Expression*> expect_multiplicative_expression_post_unary_expression( |
| const ast::Expression* lhs); |
| /// Parses additive elements |
| /// @param lhs the left side of the expression |
| /// @returns the parsed expression or `lhs` if no match |
| Expect<const ast::Expression*> expect_additive_expression_post_unary_expression( |
| const ast::Expression* lhs); |
| /// Parses math elements |
| /// @param lhs the left side of the expression |
| /// @returns the parsed expression or `lhs` if no match |
| Expect<const ast::Expression*> expect_math_expression_post_unary_expression( |
| const ast::Expression* lhs); |
| /// Parses a `unary_expression shift.post.unary_expression` |
| /// @returns the parsed expression or nullptr |
| Maybe<const ast::Expression*> shift_expression(); |
| /// Parses a `shift_expression.post.unary_expression` grammar element |
| /// @param lhs the left side of the expression |
| /// @returns the parsed expression or `lhs` if no match |
| Expect<const ast::Expression*> expect_shift_expression_post_unary_expression( |
| const ast::Expression* lhs); |
| /// Parses a `unary_expression relational_expression.post.unary_expression` |
| /// @returns the parsed expression or nullptr |
| Maybe<const ast::Expression*> relational_expression(); |
| /// Parses a `relational_expression.post.unary_expression` grammar element |
| /// @param lhs the left side of the expression |
| /// @returns the parsed expression or `lhs` if no match |
| Expect<const ast::Expression*> expect_relational_expression_post_unary_expression( |
| const ast::Expression* lhs); |
| /// Parse the `additive_operator` grammar element |
| /// @returns the parsed operator if successful |
| Maybe<core::BinaryOp> additive_operator(); |
| /// Parses a `compound_assignment_operator` grammar element |
| /// @returns the parsed compound assignment operator |
| Maybe<core::BinaryOp> compound_assignment_operator(); |
| /// Parses a `core_lhs_expression` grammar element |
| /// @returns the parsed expression or a non-kMatched failure |
| Maybe<const ast::Expression*> core_lhs_expression(); |
| /// Parses a `lhs_expression` grammar element |
| /// @returns the parsed expression or a non-kMatched failure |
| Maybe<const ast::Expression*> lhs_expression(); |
| /// Parses a `variable_updating_statement` grammar element |
| /// @returns the parsed assignment or nullptr |
| Maybe<const ast::Statement*> variable_updating_statement(); |
| /// Parses one or more attribute lists. |
| /// @return the parsed attribute list, or an empty list on error. |
| Maybe<AttributeList> attribute_list(); |
| /// Parses a single attribute of the following types: |
| /// * `struct_attribute` |
| /// * `struct_member_attribute` |
| /// * `array_attribute` |
| /// * `variable_attribute` |
| /// * `global_const_attribute` |
| /// * `function_attribute` |
| /// @return the parsed attribute, or nullptr. |
| Maybe<const ast::Attribute*> attribute(); |
| /// Parses a single attribute, reporting an error if the next token does not |
| /// represent a attribute. |
| /// @see #attribute for the full list of attributes this method parses. |
| /// @return the parsed attribute. |
| Expect<const ast::Attribute*> expect_attribute(); |
| /// Parses a severity_control_name grammar element. |
| /// @return the parsed severity control name. |
| Expect<core::DiagnosticSeverity> expect_severity_control_name(); |
| /// Parses a diagnostic_control grammar element. |
| /// @return the parsed diagnostic control. |
| Expect<ast::DiagnosticControl> expect_diagnostic_control(); |
| /// Parses a diagnostic_rule_name grammar element. |
| /// @return the parsed diagnostic rule name. |
| Expect<const ast::DiagnosticRuleName*> expect_diagnostic_rule_name(); |
| |
| /// Splits a peekable token into to parts filling in the peekable fields. |
| /// @param lhs the token to set in the current position |
| /// @param rhs the token to set in the placeholder |
| void split_token(Token::Type lhs, Token::Type rhs); |
| |
| private: |
| /// ReturnType resolves to the return type for the function or lambda F. |
| template <typename F> |
| using ReturnType = typename std::invoke_result<F>::type; |
| |
| /// ResultType resolves to `T` for a `RESULT` of type Expect<T>. |
| template <typename RESULT> |
| using ResultType = typename RESULT::type; |
| |
| /// @returns true and consumes the next token if it equals `tok` |
| /// @param source if not nullptr, the next token's source is written to this |
| /// pointer, regardless of success or error |
| bool match(Token::Type tok, Source* source = nullptr); |
| /// Errors if the next token is not equal to `tok` |
| /// Consumes the next token on match. |
| /// expect() also updates #synchronized_, setting it to `true` if the next |
| /// token is equal to `tok`, otherwise `false`. |
| /// @param use a description of what was being parsed if an error was raised. |
| /// @param tok the token to test against |
| /// @returns true if the next token equals `tok` |
| bool expect(std::string_view use, Token::Type tok); |
| /// Parses a signed integer from the next token in the stream, erroring if the |
| /// next token is not a signed integer. |
| /// Consumes the next token on match. |
| /// @param use a description of what was being parsed if an error was raised |
| /// @returns the parsed integer. |
| /// @param source if not nullptr, the next token's source is written to this |
| /// pointer, regardless of success or error |
| Expect<int32_t> expect_sint(std::string_view use, Source* source = nullptr); |
| /// Parses a signed integer from the next token in the stream, erroring if |
| /// the next token is not a signed integer or is negative. |
| /// Consumes the next token if it is a signed integer (not necessarily |
| /// negative). |
| /// @param use a description of what was being parsed if an error was raised |
| /// @returns the parsed integer. |
| Expect<uint32_t> expect_positive_sint(std::string_view use); |
| /// Parses a non-zero signed integer from the next token in the stream, |
| /// erroring if the next token is not a signed integer or is less than 1. |
| /// Consumes the next token if it is a signed integer (not necessarily |
| /// >= 1). |
| /// @param use a description of what was being parsed if an error was raised |
| /// @returns the parsed integer. |
| Expect<uint32_t> expect_nonzero_positive_sint(std::string_view use); |
| /// Errors if the next token is not an identifier. |
| /// Consumes the next token on match. |
| /// @param use a description of what was being parsed if an error was raised |
| /// @param kind a string describing the kind of identifier. |
| /// Examples: "identifier", "diagnostic name" |
| /// @returns the parsed identifier. |
| Expect<const ast::Identifier*> expect_ident(std::string_view use, |
| std::string_view kind = "identifier"); |
| /// Parses a lexical block starting with the token `start` and ending with |
| /// the token `end`. `body` is called to parse the lexical block body |
| /// between the `start` and `end` tokens. If the `start` or `end` tokens |
| /// are not matched then an error is generated and a zero-initialized `T` is |
| /// returned. If `body` raises an error while parsing then a zero-initialized |
| /// `T` is returned. |
| /// @param start the token that begins the lexical block |
| /// @param end the token that ends the lexical block |
| /// @param use a description of what was being parsed if an error was raised |
| /// @param body a function or lambda that is called to parse the lexical block |
| /// body, with the signature: `Expect<Result>()` or `Maybe<Result>()`. |
| /// @return the value returned by `body` if no errors are raised, otherwise |
| /// an Expect with error state. |
| template <typename F, typename T = ReturnType<F>> |
| T expect_block(Token::Type start, Token::Type end, std::string_view use, F&& body); |
| /// A convenience function that calls expect_block() passing |
| /// `Token::Type::kParenLeft` and `Token::Type::kParenRight` for the `start` |
| /// and `end` arguments, respectively. |
| /// @param use a description of what was being parsed if an error was raised |
| /// @param body a function or lambda that is called to parse the lexical block |
| /// body, with the signature: `Expect<Result>()` or `Maybe<Result>()`. |
| /// @return the value returned by `body` if no errors are raised, otherwise |
| /// an Expect with error state. |
| template <typename F, typename T = ReturnType<F>> |
| T expect_paren_block(std::string_view use, F&& body); |
| /// A convenience function that calls `expect_block` passing |
| /// `Token::Type::kBraceLeft` and `Token::Type::kBraceRight` for the `start` |
| /// and `end` arguments, respectively. |
| /// @param use a description of what was being parsed if an error was raised |
| /// @param body a function or lambda that is called to parse the lexical block |
| /// body, with the signature: `Expect<Result>()` or `Maybe<Result>()`. |
| /// @return the value returned by `body` if no errors are raised, otherwise |
| /// an Expect with error state. |
| template <typename F, typename T = ReturnType<F>> |
| T expect_brace_block(std::string_view use, F&& body); |
| /// A convenience function that calls `expect_block` passing |
| /// `Token::Type::kLessThan` and `Token::Type::kGreaterThan` for the `start` |
| /// and `end` arguments, respectively. |
| /// @param use a description of what was being parsed if an error was raised |
| /// @param body a function or lambda that is called to parse the lexical block |
| /// body, with the signature: `Expect<Result>()` or `Maybe<Result>()`. |
| /// @return the value returned by `body` if no errors are raised, otherwise |
| /// an Expect with error state. |
| template <typename F, typename T = ReturnType<F>> |
| T expect_lt_gt_block(std::string_view use, F&& body); |
| /// A convenience function that calls `expect_block` passing |
| /// `Token::Type::kTemplateArgsLeft` and `Token::Type::kTemplateArgsRight` for the `start` and |
| /// `end` arguments, respectively. |
| /// @param use a description of what was being parsed if an error was raised |
| /// @param body a function or lambda that is called to parse the lexical block body, with the |
| /// signature: `Expect<Result>()` or `Maybe<Result>()`. |
| /// @return the value returned by `body` if no errors are raised, otherwise an Expect with error |
| /// state. |
| template <typename F, typename T = ReturnType<F>> |
| T expect_template_arg_block(std::string_view use, F&& body); |
| |
| /// sync() calls the function `func`, and attempts to resynchronize the |
| /// parser to the next found resynchronization token if `func` fails. If the |
| /// next found resynchronization token is `tok`, then sync will also consume |
| /// `tok`. |
| /// |
| /// sync() will transiently add `tok` to the parser's stack of |
| /// synchronization tokens for the duration of the call to `func`. Once @p |
| /// func returns, |
| /// `tok` is removed from the stack of resynchronization tokens. sync calls |
| /// may be nested, and so the number of resynchronization tokens is equal to |
| /// the number of sync() calls in the current stack frame. |
| /// |
| /// sync() updates #synchronized_, setting it to `true` if the next |
| /// resynchronization token found was `tok`, otherwise `false`. |
| /// |
| /// @param tok the token to attempt to synchronize the parser to if `func` |
| /// fails. |
| /// @param func a function or lambda with the signature: `Expect<Result>()` or |
| /// `Maybe<Result>()`. |
| /// @return the value returned by `func` |
| template <typename F, typename T = ReturnType<F>> |
| T sync(Token::Type tok, F&& func); |
| /// sync_to() attempts to resynchronize the parser to the next found |
| /// resynchronization token or `tok` (whichever comes first). |
| /// |
| /// Synchronization tokens are transiently defined by calls to sync(). |
| /// |
| /// sync_to() updates #synchronized_, setting it to `true` if a |
| /// resynchronization token was found and it was `tok`, otherwise `false`. |
| /// |
| /// @param tok the token to attempt to synchronize the parser to. |
| /// @param consume if true and the next found resynchronization token is |
| /// `tok` then sync_to() will also consume `tok`. |
| /// @return the state of #synchronized_. |
| /// @see sync(). |
| bool sync_to(Token::Type tok, bool consume); |
| /// @return true if `t` is in the stack of resynchronization tokens. |
| /// @see sync(). |
| bool is_sync_token(const Token& t) const; |
| |
| /// If `t` is an error token, then `synchronized_` is set to false and the |
| /// token's error is appended to the builder's diagnostics. If `t` is not an |
| /// error token, then this function does nothing and false is returned. |
| /// @returns true if `t` is an error, otherwise false. |
| bool handle_error(const Token& t); |
| |
| /// @returns true if #synchronized_ is true and the number of reported errors |
| /// is less than #max_errors_. |
| bool continue_parsing() { |
| return synchronized_ && builder_.Diagnostics().error_count() < max_errors_; |
| } |
| |
| /// without_diag() calls the function `func` muting any diagnostics found while executing the |
| /// function. This can be used to silence spew when attempting to resynchronize the parser. |
| /// @param func a function or lambda with the signature: `Expect<Result>()` or |
| /// `Maybe<Result>()`. |
| /// @return the value returned by `func` |
| template <typename F, typename T = ReturnType<F>> |
| T without_diag(F&& func); |
| |
| /// Reports an error if the attribute list `list` is not empty. |
| /// Used to ensure that all attributes are consumed. |
| Expect<Void> expect_attributes_consumed(VectorRef<const ast::Attribute*> list); |
| |
| /// Raises an error if the next token is the start of a template list. |
| /// Used to hint to the user that the parser interpreted the following as a templated identifier |
| /// expression: |
| /// |
| /// ``` |
| /// a < b, c > |
| /// ^~~~~~~~ |
| /// ``` |
| Expect<Void> expect_next_not_template_list(const Source& lhs_source); |
| |
| /// Raises an error if the parsed expression is a templated identifier expression |
| /// Used to hint to the user that the parser intepreted the following as a templated identifier |
| /// expression: |
| /// |
| /// ``` |
| /// a < b, c > d |
| /// ^^^^^^^^^^ |
| /// expr |
| /// ``` |
| Expect<Void> expect_not_templated_ident_expr(const ast::Expression* expr); |
| |
| /// Parses the given enum, providing sensible error messages if the next token does not match |
| /// any of the enum values. |
| /// @param name the name of the enumerator |
| /// @param parse the optimized function used to parse the enum |
| /// @param strings the list of possible strings in the enum |
| /// @param use an optional description of what was being parsed if an error was raised. |
| template <typename ENUM, size_t N> |
| Expect<ENUM> expect_enum(std::string_view name, |
| ENUM (*parse)(std::string_view str), |
| const char* const (&strings)[N], |
| std::string_view use = ""); |
| |
| Expect<ast::Type> expect_type(std::string_view use); |
| |
| Maybe<const ast::Statement*> non_block_statement(); |
| Maybe<const ast::Statement*> for_header_initializer(); |
| Maybe<const ast::Statement*> for_header_continuing(); |
| |
| class MultiTokenSource; |
| MultiTokenSource make_source_range(); |
| MultiTokenSource make_source_range_from(const Source& start); |
| |
| /// Creates a new `ast::Node` owned by the Module. When the Module is |
| /// destructed, the `ast::Node` will also be destructed. |
| /// @param args the arguments to pass to the constructor |
| /// @returns the node pointer |
| template <typename T, typename... ARGS> |
| T* create(ARGS&&... args) { |
| return builder_.create<T>(std::forward<ARGS>(args)...); |
| } |
| |
| Source::File const* const file_; |
| std::vector<Token> tokens_; |
| size_t next_token_idx_ = 0; |
| size_t last_source_idx_ = 0; |
| bool synchronized_ = true; |
| uint32_t parse_depth_ = 0; |
| std::vector<Token::Type> sync_tokens_; |
| int silence_diags_ = 0; |
| ProgramBuilder builder_; |
| size_t max_errors_ = 25; |
| }; |
| |
| } // namespace tint::wgsl::reader |
| |
| #endif // SRC_TINT_LANG_WGSL_READER_PARSER_PARSER_H_ |