parser/wgsl: Fix stack overflow in postfix_expression()

Have postfix_expression() use a loop instead of recursively calling itself.

Fixed chromium:1229669

Change-Id: Ied7684d00ba453a8b89ab0251d42e2a72169421f
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/58381
Auto-Submit: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: David Neto <dneto@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index 1c8bfda..d8209a7 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -2224,42 +2224,57 @@
     ast::Expression* prefix) {
   Source source;
 
-  if (match(Token::Type::kPlusPlus, &source) ||
-      match(Token::Type::kMinusMinus, &source)) {
-    add_error(source,
-              "postfix increment and decrement operators are reserved for a "
-              "future WGSL version");
-    return Failure::kErrored;
-  }
-
-  if (match(Token::Type::kBracketLeft, &source)) {
-    return sync(Token::Type::kBracketRight, [&]() -> Maybe<ast::Expression*> {
-      auto param = logical_or_expression();
-      if (param.errored)
-        return Failure::kErrored;
-      if (!param.matched)
-        return add_error(peek(), "unable to parse expression inside []");
-
-      if (!expect("array accessor", Token::Type::kBracketRight))
-        return Failure::kErrored;
-
-      return postfix_expression(
-          create<ast::ArrayAccessorExpression>(source, prefix, param.value));
-    });
-  }
-
-  if (match(Token::Type::kPeriod)) {
-    auto ident = expect_ident("member accessor");
-    if (ident.errored)
+  while (continue_parsing()) {
+    if (match(Token::Type::kPlusPlus, &source) ||
+        match(Token::Type::kMinusMinus, &source)) {
+      add_error(source,
+                "postfix increment and decrement operators are reserved for a "
+                "future WGSL version");
       return Failure::kErrored;
+    }
 
-    return postfix_expression(create<ast::MemberAccessorExpression>(
-        ident.source, prefix,
-        create<ast::IdentifierExpression>(
-            ident.source, builder_.Symbols().Register(ident.value))));
+    if (match(Token::Type::kBracketLeft, &source)) {
+      auto res =
+          sync(Token::Type::kBracketRight, [&]() -> Maybe<ast::Expression*> {
+            auto param = logical_or_expression();
+            if (param.errored)
+              return Failure::kErrored;
+            if (!param.matched) {
+              return add_error(peek(), "unable to parse expression inside []");
+            }
+
+            if (!expect("array accessor", Token::Type::kBracketRight)) {
+              return Failure::kErrored;
+            }
+
+            return create<ast::ArrayAccessorExpression>(source, prefix,
+                                                        param.value);
+          });
+
+      if (res.errored) {
+        return res;
+      }
+      prefix = res.value;
+      continue;
+    }
+
+    if (match(Token::Type::kPeriod)) {
+      auto ident = expect_ident("member accessor");
+      if (ident.errored) {
+        return Failure::kErrored;
+      }
+
+      prefix = create<ast::MemberAccessorExpression>(
+          ident.source, prefix,
+          create<ast::IdentifierExpression>(
+              ident.source, builder_.Symbols().Register(ident.value)));
+      continue;
+    }
+
+    return prefix;
   }
 
-  return prefix;
+  return Failure::kErrored;
 }
 
 // singular_expression