diff --git a/src/ast/statement.cc b/src/ast/statement.cc index f63487f593..cd3e09d8b7 100644 --- a/src/ast/statement.cc +++ b/src/ast/statement.cc @@ -98,6 +98,52 @@ bool Statement::IsVariableDecl() const { return false; } +const char* Statement::Name() const { + if (IsAssign()) { + return "assignment statement"; + } + if (IsBlock()) { + return "block statement"; + } + if (IsBreak()) { + return "break statement"; + } + if (IsCase()) { + return "case statement"; + } + if (IsCall()) { + return "function call"; + } + if (IsContinue()) { + return "continue statement"; + } + if (IsDiscard()) { + return "discard statement"; + } + if (IsElse()) { + return "else statement"; + } + if (IsFallthrough()) { + return "fallthrough statement"; + } + if (IsIf()) { + return "if statement"; + } + if (IsLoop()) { + return "loop statement"; + } + if (IsReturn()) { + return "return statement"; + } + if (IsSwitch()) { + return "switch statement"; + } + if (IsVariableDecl()) { + return "variable declaration"; + } + return "statement"; +} + const AssignmentStatement* Statement::AsAssign() const { assert(IsAssign()); return static_cast(this); diff --git a/src/ast/statement.h b/src/ast/statement.h index 3bd14525ae..c75b091b1e 100644 --- a/src/ast/statement.h +++ b/src/ast/statement.h @@ -72,6 +72,9 @@ class Statement : public Node { /// @returns true if this is an variable statement virtual bool IsVariableDecl() const; + /// @returns the human readable name for the statement type. + const char* Name() const; + /// @returns the statement as a const assign statement const AssignmentStatement* AsAssign() const; /// @returns the statement as a const block statement diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc index 89c8a35e6e..ea0b5ae2bb 100644 --- a/src/reader/wgsl/parser_impl.cc +++ b/src/reader/wgsl/parser_impl.cc @@ -1269,33 +1269,31 @@ Expect> ParserImpl::expect_statements() { // statement // : SEMICOLON -// | return_stmt SEMICOLON +// | body_stmt? // | if_stmt // | switch_stmt // | loop_stmt // | for_stmt -// | func_call_stmt SEMICOLON -// | variable_stmt SEMICOLON -// | break_stmt SEMICOLON -// | continue_stmt SEMICOLON -// | DISCARD SEMICOLON -// | assignment_stmt SEMICOLON -// | body_stmt? +// | non_block_statement +// : return_stmt SEMICOLON +// | func_call_stmt SEMICOLON +// | variable_stmt SEMICOLON +// | break_stmt SEMICOLON +// | continue_stmt SEMICOLON +// | DISCARD SEMICOLON +// | assignment_stmt SEMICOLON Maybe> ParserImpl::statement() { while (match(Token::Type::kSemicolon)) { // Skip empty statements } - auto t = peek(); - auto ret_stmt = return_stmt(); - if (ret_stmt.errored) + // Non-block statments all end in a semi-colon. + // TODO(bclayton): We can use this property to synchronize on error. + auto stmt = non_block_statement(); + if (stmt.errored) return Failure::kErrored; - if (ret_stmt.matched) { - if (!expect("return statement", Token::Type::kSemicolon)) - return Failure::kErrored; - - return std::move(ret_stmt.value); - } + if (stmt.matched) + return stmt; auto stmt_if = if_stmt(); if (stmt_if.errored) @@ -1321,68 +1319,7 @@ Maybe> ParserImpl::statement() { if (stmt_for.matched) return std::move(stmt_for.value); - auto func = func_call_stmt(); - if (func.errored) - return Failure::kErrored; - if (func.matched) { - if (!expect("function call", Token::Type::kSemicolon)) - return Failure::kErrored; - - return std::move(func.value); - } - - auto var = variable_stmt(); - if (var.errored) - return Failure::kErrored; - if (var.matched) { - if (!expect("variable declaration", Token::Type::kSemicolon)) - return Failure::kErrored; - - return std::move(var.value); - } - - auto b = break_stmt(); - if (b.errored) - return Failure::kErrored; - if (b.matched) { - if (!expect("break statement", Token::Type::kSemicolon)) - return Failure::kErrored; - - return std::move(b.value); - } - - auto cont = continue_stmt(); - if (cont.errored) - return Failure::kErrored; - if (cont.matched) { - if (!expect("continue statement", Token::Type::kSemicolon)) - return Failure::kErrored; - - return std::move(cont.value); - } - - if (t.IsDiscard()) { - auto source = t.source(); - next(); // Consume the peek - - if (!expect("discard statement", Token::Type::kSemicolon)) - return Failure::kErrored; - - return std::make_unique(source); - } - - auto assign = assignment_stmt(); - if (assign.errored) - return Failure::kErrored; - if (assign.matched) { - if (!expect("assignment statement", Token::Type::kSemicolon)) - return Failure::kErrored; - - return std::move(assign.value); - } - - t = peek(); - if (t.IsBraceLeft()) { + if (peek().IsBraceLeft()) { auto body = expect_body_stmt(); if (body.errored) return Failure::kErrored; @@ -1392,6 +1329,65 @@ Maybe> ParserImpl::statement() { return Failure::kNoMatch; } +// statement (continued) +// : return_stmt SEMICOLON +// | func_call_stmt SEMICOLON +// | variable_stmt SEMICOLON +// | break_stmt SEMICOLON +// | continue_stmt SEMICOLON +// | DISCARD SEMICOLON +// | assignment_stmt SEMICOLON +Maybe> ParserImpl::non_block_statement() { + auto stmt = [&]() -> Maybe> { + auto ret_stmt = return_stmt(); + if (ret_stmt.errored) + return Failure::kErrored; + if (ret_stmt.matched) + return std::move(ret_stmt.value); + + auto func = func_call_stmt(); + if (func.errored) + return Failure::kErrored; + if (func.matched) + return std::move(func.value); + + auto var = variable_stmt(); + if (var.errored) + return Failure::kErrored; + if (var.matched) + return std::move(var.value); + + auto b = break_stmt(); + if (b.errored) + return Failure::kErrored; + if (b.matched) + return std::move(b.value); + + auto cont = continue_stmt(); + if (cont.errored) + return Failure::kErrored; + if (cont.matched) + return std::move(cont.value); + + auto assign = assignment_stmt(); + if (assign.errored) + return Failure::kErrored; + if (assign.matched) + return std::move(assign.value); + + Source source; + if (match(Token::Type::kDiscard, &source)) + return std::make_unique(source); + + return Failure::kNoMatch; + }(); + + if (stmt.matched && !expect(stmt->Name(), Token::Type::kSemicolon)) + return Failure::kErrored; + + return stmt; +} + // return_stmt // : RETURN logical_or_expression? Maybe> ParserImpl::return_stmt() { diff --git a/src/reader/wgsl/parser_impl.h b/src/reader/wgsl/parser_impl.h index b63d1eb2d9..0f5b152f1a 100644 --- a/src/reader/wgsl/parser_impl.h +++ b/src/reader/wgsl/parser_impl.h @@ -704,6 +704,7 @@ class ParserImpl { Expect expect_type(const std::string& use); + Maybe> non_block_statement(); Maybe> for_header_initializer(); Maybe> for_header_continuing();