[wgsl-reader] Adding for loops
Implementing ParserImpl::for_stmt(). Also adding
for_stmt and body_stmt to ParserImpl::statement().
Bug: tint:138
Change-Id: Idc3f901648ca115f4d94b3614a940263ef841282
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/27261
Commit-Queue: Tomek Ponitka <tommek@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index 93d6937..dcd2b44 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -1473,6 +1473,7 @@
// | if_stmt
// | switch_stmt
// | loop_stmt
+// | for_stmt
// | func_call_stmt SEMICOLON
// | variable_stmt SEMICOLON
// | break_stmt SEMICOLON
@@ -1517,6 +1518,12 @@
if (loop != nullptr)
return loop;
+ auto stmt_for = for_stmt();
+ if (has_error())
+ return nullptr;
+ if (stmt_for != nullptr)
+ return stmt_for;
+
auto func = func_call_stmt();
if (has_error())
return nullptr;
@@ -1979,6 +1986,160 @@
std::move(continuing));
}
+ForHeader::ForHeader(std::unique_ptr<ast::Statement> _initializer,
+ std::unique_ptr<ast::Expression> _condition,
+ std::unique_ptr<ast::Statement> _continuing)
+ : initializer(std::move(_initializer)),
+ condition(std::move(_condition)),
+ continuing(std::move(_continuing)) {}
+
+ForHeader::~ForHeader() = default;
+
+// for_header
+// : (variable_stmt | assignment_stmt | func_call_stmt)?
+// SEMICOLON
+// logical_or_expression? SEMICOLON
+// (assignment_stmt | func_call_stmt)?
+std::unique_ptr<ForHeader> ParserImpl::for_header() {
+ std::unique_ptr<ast::Statement> initializer = nullptr;
+ if (initializer == nullptr) {
+ initializer = func_call_stmt();
+ if (has_error()) {
+ return nullptr;
+ }
+ }
+ if (initializer == nullptr) {
+ initializer = variable_stmt();
+ if (has_error()) {
+ return nullptr;
+ }
+ }
+ if (initializer == nullptr) {
+ initializer = assignment_stmt();
+ if (has_error()) {
+ return nullptr;
+ }
+ }
+
+ auto t = next();
+ if (!t.IsSemicolon()) {
+ set_error(t, "missing ';' after initializer in for loop");
+ return nullptr;
+ }
+
+ auto condition = logical_or_expression();
+ if (has_error()) {
+ return nullptr;
+ }
+
+ t = next();
+ if (!t.IsSemicolon()) {
+ set_error(t, "missing ';' after condition in for loop");
+ return nullptr;
+ }
+
+ std::unique_ptr<ast::Statement> continuing = nullptr;
+ if (continuing == nullptr) {
+ continuing = func_call_stmt();
+ if (has_error()) {
+ return nullptr;
+ }
+ }
+ if (continuing == nullptr) {
+ continuing = assignment_stmt();
+ if (has_error()) {
+ return nullptr;
+ }
+ }
+
+ return std::make_unique<ForHeader>(
+ std::move(initializer), std::move(condition), std::move(continuing));
+}
+
+// for_statement
+// : FOR PAREN_LEFT for_header PAREN_RIGHT BRACE_LEFT statements BRACE_RIGHT
+std::unique_ptr<ast::Statement> ParserImpl::for_stmt() {
+ auto t = peek();
+ if (!t.IsFor())
+ return nullptr;
+
+ auto source = t.source();
+ next(); // Consume the peek
+
+ t = next();
+ if (!t.IsParenLeft()) {
+ set_error(t, "missing for loop (");
+ return nullptr;
+ }
+
+ auto header = for_header();
+ if (has_error())
+ return nullptr;
+
+ t = next();
+ if (!t.IsParenRight()) {
+ set_error(t, "missing for loop )");
+ return nullptr;
+ }
+
+ t = next();
+ if (!t.IsBraceLeft()) {
+ set_error(t, "missing for loop {");
+ return nullptr;
+ }
+
+ auto body = statements();
+ if (has_error())
+ return nullptr;
+
+ t = next();
+ if (!t.IsBraceRight()) {
+ set_error(t, "missing for loop }");
+ return nullptr;
+ }
+
+ // The for statement is a syntactic sugar on top of the loop statement.
+ // We create corresponding nodes in ast with the exact same behaviour
+ // as we would expect from the loop statement.
+
+ if (header->condition != nullptr) {
+ // !condition
+ auto not_condition = std::make_unique<ast::UnaryOpExpression>(
+ header->condition->source(), ast::UnaryOp::kNot,
+ std::move(header->condition));
+ // { break; }
+ auto break_stmt =
+ std::make_unique<ast::BreakStatement>(not_condition->source());
+ auto break_body =
+ std::make_unique<ast::BlockStatement>(not_condition->source());
+ break_body->append(std::move(break_stmt));
+ // if (!condition) { break; }
+ auto break_if_not_condition = std::make_unique<ast::IfStatement>(
+ not_condition->source(), std::move(not_condition),
+ std::move(break_body));
+ body->insert(0, std::move(break_if_not_condition));
+ }
+
+ std::unique_ptr<ast::BlockStatement> continuing_body = nullptr;
+ if (header->continuing != nullptr) {
+ continuing_body =
+ std::make_unique<ast::BlockStatement>(header->continuing->source());
+ continuing_body->append(std::move(header->continuing));
+ }
+
+ auto loop = std::make_unique<ast::LoopStatement>(source, std::move(body),
+ std::move(continuing_body));
+
+ if (header->initializer != nullptr) {
+ auto result = std::make_unique<ast::BlockStatement>(source);
+ result->append(std::move(header->initializer));
+ result->append(std::move(loop));
+ return result;
+ }
+
+ return loop;
+}
+
// func_call_stmt
// : IDENT PAREN_LEFT argument_expression_list* PAREN_RIGHT
std::unique_ptr<ast::CallStatement> ParserImpl::func_call_stmt() {