wsgl parser: Add expect_builtin()

Mirrors expect_pipeline_stage()

Bug: tint:282
Change-Id: I413c87b3684c1f5cfec0c4acd7d3a5160d4b24a9
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/31735
Commit-Queue: Ben Clayton <bclayton@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 526ca77..248d78c 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -452,16 +452,11 @@
     if (!expect("builtin decoration", Token::Type::kParenLeft))
       return nullptr;
 
-    std::string ident;
-    if (!expect_ident("builtin", &ident, &source))
+    ast::Builtin builtin;
+    std::tie(builtin, source) = expect_builtin();
+    if (builtin == ast::Builtin::kNone)
       return nullptr;
 
-    ast::Builtin builtin = ident_to_builtin(ident);
-    if (builtin == ast::Builtin::kNone) {
-      add_error(source, "invalid value for builtin decoration");
-      return nullptr;
-    }
-
     if (!expect("builtin decoration", Token::Type::kParenRight))
       return nullptr;
 
@@ -1796,6 +1791,20 @@
   return {ast::PipelineStage::kNone, t.source()};
 }
 
+std::pair<ast::Builtin, Source> ParserImpl::expect_builtin() {
+  Source source;
+  std::string ident;
+
+  if (!expect_ident("builtin", &ident, &source))
+    return {ast::Builtin::kNone, source};
+
+  ast::Builtin builtin = ident_to_builtin(ident);
+  if (builtin == ast::Builtin::kNone)
+    add_error(source, "invalid value for builtin decoration");
+
+  return {builtin, source};
+}
+
 // body_stmt
 //   : BRACKET_LEFT statements BRACKET_RIGHT
 std::unique_ptr<ast::BlockStatement> ParserImpl::body_stmt() {
diff --git a/src/reader/wgsl/parser_impl.h b/src/reader/wgsl/parser_impl.h
index 221c36d..91c54fa 100644
--- a/src/reader/wgsl/parser_impl.h
+++ b/src/reader/wgsl/parser_impl.h
@@ -262,6 +262,11 @@
   /// @returns the pipeline stage or PipelineStage::kNone if none matched, along
   /// with the source location for the stage.
   std::pair<ast::PipelineStage, Source> expect_pipeline_stage();
+  /// Parses a builtin identifier, erroring if the next token does not match a
+  /// valid builtin name.
+  /// @returns the builtin or Builtin::kNone if none matched, along with the
+  /// source location for the stage.
+  std::pair<ast::Builtin, Source> expect_builtin();
   /// Parses a `body_stmt` grammar element
   /// @returns the parsed statements
   std::unique_ptr<ast::BlockStatement> body_stmt();