[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() {