wsgl parser: Add expect_block(), use it

`expect_block()` takes a start and end token, along with a function to parse a lexical block body.

This reduces code, keeps error messages consistent, and also gives us a future place to try resynchronising the parser so we can have more than one error emitted.

`expect_paren_block()` and `expect_brace_block()` are convenience helpers for providing the start and end tokens for common block types.

Bug: tint:282
Change-Id: I432a0301727b131a6fce875687b952dfc6889a4b
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/31736
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/src/reader/wgsl/parser_impl.h b/src/reader/wgsl/parser_impl.h
index 91c54fa..e1de67f 100644
--- a/src/reader/wgsl/parser_impl.h
+++ b/src/reader/wgsl/parser_impl.h
@@ -18,6 +18,7 @@
 #include <deque>
 #include <memory>
 #include <string>
+#include <type_traits>
 #include <unordered_map>
 #include <utility>
 
@@ -435,6 +436,10 @@
   std::unique_ptr<ast::AssignmentStatement> assignment_stmt();
 
  private:
+  /// ResultType resolves to the return type for the function or lambda F.
+  template <typename F>
+  using ResultType = typename std::result_of<F()>::type;
+
   /// @returns true and consumes the next token if it equals |tok|.
   /// @param source if not nullptr, the next token's source is written to this
   /// pointer, regardless of success or error
@@ -476,6 +481,45 @@
   bool expect_ident(const std::string& use,
                     std::string* out,
                     Source* source = nullptr);
+  /// Parses a lexical block starting with the token |start| and ending with
+  /// the token |end|. |body| is called to parse the lexical block body between
+  /// the |start| and |end| tokens.
+  /// If the |start| or |end| tokens are not matched then an error is generated
+  /// and a zero-initialized |T| is returned.
+  /// If |body| raises an error while parsing then a zero-initialized |T| is
+  /// returned.
+  /// @param start the token that begins the lexical block
+  /// @param end the token that ends the lexical block
+  /// @param use a description of what was being parsed if an error was raised
+  /// @param body a function or lambda that is called to parse the lexical block
+  /// body, with the signature T().
+  /// @return the value returned by |body| if no errors are raised, otherwise
+  /// a zero-initialized |T|.
+  template <typename F, typename T = ResultType<F>>
+  T expect_block(Token::Type start,
+                 Token::Type end,
+                 const std::string& use,
+                 F&& body);
+  /// A convenience function that calls |expect_block| passing
+  /// |Token::Type::kParenLeft| and |Token::Type::kParenRight| for the |start|
+  /// and |end| arguments, respectively.
+  /// @param use a description of what was being parsed if an error was raised
+  /// @param body a function or lambda that is called to parse the lexical block
+  /// body, with the signature T().
+  /// @return the value returned by |body| if no errors are raised, otherwise
+  /// a zero-initialized |T|.
+  template <typename F, typename T = ResultType<F>>
+  T expect_paren_block(const std::string& use, F&& body);
+  /// A convenience function that calls |expect_block| passing
+  /// |Token::Type::kBraceLeft| and |Token::Type::kBraceRight| for the |start|
+  /// and |end| arguments, respectively.
+  /// @param use a description of what was being parsed if an error was raised
+  /// @param body a function or lambda that is called to parse the lexical block
+  /// body, with the signature T().
+  /// @return the value returned by |body| if no errors are raised, otherwise
+  /// a zero-initialized |T|.
+  template <typename F, typename T = ResultType<F>>
+  T expect_brace_block(const std::string& use, F&& body);
 
   ast::type::Type* type_decl_pointer(Token t);
   ast::type::Type* type_decl_vector(Token t);