wsgl parser: refactor statements()

Split out statments that are non-block (non-loops, etc) into a separate function.

These all end with a semi-colon, which is important for resynchronization on errors (coming up in another change).

Bug: tint:282
Change-Id: I0e58c4938f2bbe859dc6ffb8dcd45c8cf26101da
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/32281
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
This commit is contained in:
Ben Clayton 2020-11-11 14:11:55 +00:00 committed by Commit Bot service account
parent 00bc8ba88c
commit 54af8746ae
4 changed files with 125 additions and 79 deletions

View File

@ -98,6 +98,52 @@ bool Statement::IsVariableDecl() const {
return false; 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 { const AssignmentStatement* Statement::AsAssign() const {
assert(IsAssign()); assert(IsAssign());
return static_cast<const AssignmentStatement*>(this); return static_cast<const AssignmentStatement*>(this);

View File

@ -72,6 +72,9 @@ class Statement : public Node {
/// @returns true if this is an variable statement /// @returns true if this is an variable statement
virtual bool IsVariableDecl() const; 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 /// @returns the statement as a const assign statement
const AssignmentStatement* AsAssign() const; const AssignmentStatement* AsAssign() const;
/// @returns the statement as a const block statement /// @returns the statement as a const block statement

View File

@ -1269,33 +1269,31 @@ Expect<std::unique_ptr<ast::BlockStatement>> ParserImpl::expect_statements() {
// statement // statement
// : SEMICOLON // : SEMICOLON
// | return_stmt SEMICOLON // | body_stmt?
// | if_stmt // | if_stmt
// | switch_stmt // | switch_stmt
// | loop_stmt // | loop_stmt
// | for_stmt // | for_stmt
// | non_block_statement
// : return_stmt SEMICOLON
// | func_call_stmt SEMICOLON // | func_call_stmt SEMICOLON
// | variable_stmt SEMICOLON // | variable_stmt SEMICOLON
// | break_stmt SEMICOLON // | break_stmt SEMICOLON
// | continue_stmt SEMICOLON // | continue_stmt SEMICOLON
// | DISCARD SEMICOLON // | DISCARD SEMICOLON
// | assignment_stmt SEMICOLON // | assignment_stmt SEMICOLON
// | body_stmt?
Maybe<std::unique_ptr<ast::Statement>> ParserImpl::statement() { Maybe<std::unique_ptr<ast::Statement>> ParserImpl::statement() {
while (match(Token::Type::kSemicolon)) { while (match(Token::Type::kSemicolon)) {
// Skip empty statements // Skip empty statements
} }
auto t = peek(); // Non-block statments all end in a semi-colon.
auto ret_stmt = return_stmt(); // TODO(bclayton): We can use this property to synchronize on error.
if (ret_stmt.errored) auto stmt = non_block_statement();
if (stmt.errored)
return Failure::kErrored; return Failure::kErrored;
if (ret_stmt.matched) { if (stmt.matched)
if (!expect("return statement", Token::Type::kSemicolon)) return stmt;
return Failure::kErrored;
return std::move(ret_stmt.value);
}
auto stmt_if = if_stmt(); auto stmt_if = if_stmt();
if (stmt_if.errored) if (stmt_if.errored)
@ -1321,68 +1319,7 @@ Maybe<std::unique_ptr<ast::Statement>> ParserImpl::statement() {
if (stmt_for.matched) if (stmt_for.matched)
return std::move(stmt_for.value); return std::move(stmt_for.value);
auto func = func_call_stmt(); if (peek().IsBraceLeft()) {
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<ast::DiscardStatement>(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()) {
auto body = expect_body_stmt(); auto body = expect_body_stmt();
if (body.errored) if (body.errored)
return Failure::kErrored; return Failure::kErrored;
@ -1392,6 +1329,65 @@ Maybe<std::unique_ptr<ast::Statement>> ParserImpl::statement() {
return Failure::kNoMatch; 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<std::unique_ptr<ast::Statement>> ParserImpl::non_block_statement() {
auto stmt = [&]() -> Maybe<std::unique_ptr<ast::Statement>> {
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<ast::DiscardStatement>(source);
return Failure::kNoMatch;
}();
if (stmt.matched && !expect(stmt->Name(), Token::Type::kSemicolon))
return Failure::kErrored;
return stmt;
}
// return_stmt // return_stmt
// : RETURN logical_or_expression? // : RETURN logical_or_expression?
Maybe<std::unique_ptr<ast::ReturnStatement>> ParserImpl::return_stmt() { Maybe<std::unique_ptr<ast::ReturnStatement>> ParserImpl::return_stmt() {

View File

@ -704,6 +704,7 @@ class ParserImpl {
Expect<ast::type::Type*> expect_type(const std::string& use); Expect<ast::type::Type*> expect_type(const std::string& use);
Maybe<std::unique_ptr<ast::Statement>> non_block_statement();
Maybe<std::unique_ptr<ast::Statement>> for_header_initializer(); Maybe<std::unique_ptr<ast::Statement>> for_header_initializer();
Maybe<std::unique_ptr<ast::Statement>> for_header_continuing(); Maybe<std::unique_ptr<ast::Statement>> for_header_continuing();