[wgsl-reader] Parsing sampler type

Change-Id: I58a3218a5d0b7ccbe6422340c94cdf3dc1bdf17a
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/28000
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 3bcfdef..a154634 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -908,6 +908,7 @@
     "src/reader/wgsl/parser_impl_postfix_expression_test.cc",
     "src/reader/wgsl/parser_impl_primary_expression_test.cc",
     "src/reader/wgsl/parser_impl_relational_expression_test.cc",
+    "src/reader/wgsl/parser_impl_sampler_type_test.cc",
     "src/reader/wgsl/parser_impl_shift_expression_test.cc",
     "src/reader/wgsl/parser_impl_statement_test.cc",
     "src/reader/wgsl/parser_impl_statements_test.cc",
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 9e767ba..be615c8 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -439,6 +439,7 @@
     reader/wgsl/parser_impl_postfix_expression_test.cc
     reader/wgsl/parser_impl_primary_expression_test.cc
     reader/wgsl/parser_impl_relational_expression_test.cc
+    reader/wgsl/parser_impl_sampler_type_test.cc
     reader/wgsl/parser_impl_shift_expression_test.cc
     reader/wgsl/parser_impl_statement_test.cc
     reader/wgsl/parser_impl_statements_test.cc
diff --git a/src/reader/wgsl/lexer.cc b/src/reader/wgsl/lexer.cc
index 981ea69..71da0f6 100644
--- a/src/reader/wgsl/lexer.cc
+++ b/src/reader/wgsl/lexer.cc
@@ -561,6 +561,10 @@
     return {Token::Type::kPtr, source, "ptr"};
   if (str == "return")
     return {Token::Type::kReturn, source, "return"};
+  if (str == "sampler")
+    return {Token::Type::kSampler, source, "sampler"};
+  if (str == "sampler_comparison")
+    return {Token::Type::kComparisonSampler, source, "sampler_comparison"};
   if (str == "set")
     return {Token::Type::kSet, source, "set"};
   if (str == "storage_buffer")
diff --git a/src/reader/wgsl/lexer_test.cc b/src/reader/wgsl/lexer_test.cc
index c2edd00..9023b4a 100644
--- a/src/reader/wgsl/lexer_test.cc
+++ b/src/reader/wgsl/lexer_test.cc
@@ -411,70 +411,72 @@
 INSTANTIATE_TEST_SUITE_P(
     LexerTest,
     KeywordTest,
-    testing::Values(TokenData{"array", Token::Type::kArray},
-                    TokenData{"as", Token::Type::kAs},
-                    TokenData{"binding", Token::Type::kBinding},
-                    TokenData{"block", Token::Type::kBlock},
-                    TokenData{"bool", Token::Type::kBool},
-                    TokenData{"break", Token::Type::kBreak},
-                    TokenData{"builtin", Token::Type::kBuiltin},
-                    TokenData{"case", Token::Type::kCase},
-                    TokenData{"cast", Token::Type::kCast},
-                    TokenData{"compute", Token::Type::kCompute},
-                    TokenData{"const", Token::Type::kConst},
-                    TokenData{"continue", Token::Type::kContinue},
-                    TokenData{"continuing", Token::Type::kContinuing},
-                    TokenData{"default", Token::Type::kDefault},
-                    TokenData{"discard", Token::Type::kDiscard},
-                    TokenData{"else", Token::Type::kElse},
-                    TokenData{"elseif", Token::Type::kElseIf},
-                    TokenData{"entry_point", Token::Type::kEntryPoint},
-                    TokenData{"f32", Token::Type::kF32},
-                    TokenData{"fallthrough", Token::Type::kFallthrough},
-                    TokenData{"false", Token::Type::kFalse},
-                    TokenData{"fn", Token::Type::kFn},
-                    TokenData{"for", Token::Type::kFor},
-                    TokenData{"fragment", Token::Type::kFragment},
-                    TokenData{"function", Token::Type::kFunction},
-                    TokenData{"i32", Token::Type::kI32},
-                    TokenData{"if", Token::Type::kIf},
-                    TokenData{"image", Token::Type::kImage},
-                    TokenData{"import", Token::Type::kImport},
-                    TokenData{"in", Token::Type::kIn},
-                    TokenData{"location", Token::Type::kLocation},
-                    TokenData{"loop", Token::Type::kLoop},
-                    TokenData{"mat2x2", Token::Type::kMat2x2},
-                    TokenData{"mat2x3", Token::Type::kMat2x3},
-                    TokenData{"mat2x4", Token::Type::kMat2x4},
-                    TokenData{"mat3x2", Token::Type::kMat3x2},
-                    TokenData{"mat3x3", Token::Type::kMat3x3},
-                    TokenData{"mat3x4", Token::Type::kMat3x4},
-                    TokenData{"mat4x2", Token::Type::kMat4x2},
-                    TokenData{"mat4x3", Token::Type::kMat4x3},
-                    TokenData{"mat4x4", Token::Type::kMat4x4},
-                    TokenData{"offset", Token::Type::kOffset},
-                    TokenData{"out", Token::Type::kOut},
-                    TokenData{"private", Token::Type::kPrivate},
-                    TokenData{"ptr", Token::Type::kPtr},
-                    TokenData{"return", Token::Type::kReturn},
-                    TokenData{"set", Token::Type::kSet},
-                    TokenData{"storage_buffer", Token::Type::kStorageBuffer},
-                    TokenData{"stride", Token::Type::kStride},
-                    TokenData{"struct", Token::Type::kStruct},
-                    TokenData{"switch", Token::Type::kSwitch},
-                    TokenData{"true", Token::Type::kTrue},
-                    TokenData{"type", Token::Type::kType},
-                    TokenData{"u32", Token::Type::kU32},
-                    TokenData{"uniform", Token::Type::kUniform},
-                    TokenData{"uniform_constant",
-                              Token::Type::kUniformConstant},
-                    TokenData{"var", Token::Type::kVar},
-                    TokenData{"vec2", Token::Type::kVec2},
-                    TokenData{"vec3", Token::Type::kVec3},
-                    TokenData{"vec4", Token::Type::kVec4},
-                    TokenData{"vertex", Token::Type::kVertex},
-                    TokenData{"void", Token::Type::kVoid},
-                    TokenData{"workgroup", Token::Type::kWorkgroup}));
+    testing::Values(
+        TokenData{"array", Token::Type::kArray},
+        TokenData{"as", Token::Type::kAs},
+        TokenData{"binding", Token::Type::kBinding},
+        TokenData{"block", Token::Type::kBlock},
+        TokenData{"bool", Token::Type::kBool},
+        TokenData{"break", Token::Type::kBreak},
+        TokenData{"builtin", Token::Type::kBuiltin},
+        TokenData{"case", Token::Type::kCase},
+        TokenData{"cast", Token::Type::kCast},
+        TokenData{"compute", Token::Type::kCompute},
+        TokenData{"const", Token::Type::kConst},
+        TokenData{"continue", Token::Type::kContinue},
+        TokenData{"continuing", Token::Type::kContinuing},
+        TokenData{"default", Token::Type::kDefault},
+        TokenData{"discard", Token::Type::kDiscard},
+        TokenData{"else", Token::Type::kElse},
+        TokenData{"elseif", Token::Type::kElseIf},
+        TokenData{"entry_point", Token::Type::kEntryPoint},
+        TokenData{"f32", Token::Type::kF32},
+        TokenData{"fallthrough", Token::Type::kFallthrough},
+        TokenData{"false", Token::Type::kFalse},
+        TokenData{"fn", Token::Type::kFn},
+        TokenData{"for", Token::Type::kFor},
+        TokenData{"fragment", Token::Type::kFragment},
+        TokenData{"function", Token::Type::kFunction},
+        TokenData{"i32", Token::Type::kI32},
+        TokenData{"if", Token::Type::kIf},
+        TokenData{"image", Token::Type::kImage},
+        TokenData{"import", Token::Type::kImport},
+        TokenData{"in", Token::Type::kIn},
+        TokenData{"location", Token::Type::kLocation},
+        TokenData{"loop", Token::Type::kLoop},
+        TokenData{"mat2x2", Token::Type::kMat2x2},
+        TokenData{"mat2x3", Token::Type::kMat2x3},
+        TokenData{"mat2x4", Token::Type::kMat2x4},
+        TokenData{"mat3x2", Token::Type::kMat3x2},
+        TokenData{"mat3x3", Token::Type::kMat3x3},
+        TokenData{"mat3x4", Token::Type::kMat3x4},
+        TokenData{"mat4x2", Token::Type::kMat4x2},
+        TokenData{"mat4x3", Token::Type::kMat4x3},
+        TokenData{"mat4x4", Token::Type::kMat4x4},
+        TokenData{"offset", Token::Type::kOffset},
+        TokenData{"out", Token::Type::kOut},
+        TokenData{"private", Token::Type::kPrivate},
+        TokenData{"ptr", Token::Type::kPtr},
+        TokenData{"return", Token::Type::kReturn},
+        TokenData{"sampler", Token::Type::kSampler},
+        TokenData{"sampler_comparison", Token::Type::kComparisonSampler},
+        TokenData{"set", Token::Type::kSet},
+        TokenData{"storage_buffer", Token::Type::kStorageBuffer},
+        TokenData{"stride", Token::Type::kStride},
+        TokenData{"struct", Token::Type::kStruct},
+        TokenData{"switch", Token::Type::kSwitch},
+        TokenData{"true", Token::Type::kTrue},
+        TokenData{"type", Token::Type::kType},
+        TokenData{"u32", Token::Type::kU32},
+        TokenData{"uniform", Token::Type::kUniform},
+        TokenData{"uniform_constant", Token::Type::kUniformConstant},
+        TokenData{"var", Token::Type::kVar},
+        TokenData{"vec2", Token::Type::kVec2},
+        TokenData{"vec3", Token::Type::kVec3},
+        TokenData{"vec4", Token::Type::kVec4},
+        TokenData{"vertex", Token::Type::kVertex},
+        TokenData{"void", Token::Type::kVoid},
+        TokenData{"workgroup", Token::Type::kWorkgroup}));
 
 using KeywordTest_Reserved = testing::TestWithParam<const char*>;
 TEST_P(KeywordTest_Reserved, Parses) {
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index fdc9df7..33debd9 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -50,6 +50,7 @@
 #include "src/ast/type/i32_type.h"
 #include "src/ast/type/matrix_type.h"
 #include "src/ast/type/pointer_type.h"
+#include "src/ast/type/sampler_type.h"
 #include "src/ast/type/struct_type.h"
 #include "src/ast/type/u32_type.h"
 #include "src/ast/type/vector_type.h"
@@ -575,6 +576,24 @@
   return std::make_unique<ast::Variable>(source, name, sc, type);
 }
 
+// sampler_type
+//  : SAMPLER
+//  | SAMPLER_COMPARISON
+ast::type::Type* ParserImpl::sampler_type() {
+  auto t = peek();
+  if (t.IsSampler()) {
+    next();  // Consume the peek
+    return ctx_.type_mgr().Get(std::make_unique<ast::type::SamplerType>(
+        ast::type::SamplerKind::kSampler));
+  }
+  if (t.IsComparisonSampler()) {
+    next();  // Consume the peek
+    return ctx_.type_mgr().Get(std::make_unique<ast::type::SamplerType>(
+        ast::type::SamplerKind::kComparisonSampler));
+  }
+  return nullptr;
+}
+
 // variable_ident_decl
 //   : IDENT COLON type_decl
 std::pair<std::string, ast::type::Type*> ParserImpl::variable_ident_decl() {
diff --git a/src/reader/wgsl/parser_impl.h b/src/reader/wgsl/parser_impl.h
index d4a7abc..7bcf639 100644
--- a/src/reader/wgsl/parser_impl.h
+++ b/src/reader/wgsl/parser_impl.h
@@ -150,8 +150,7 @@
   /// @returns the type alias or nullptr on error
   ast::type::AliasType* type_alias();
   /// Parses a `type_decl` grammar element
-  /// @returns the parsed Type or nullptr if none matched. The returned type
-  //           is owned by the TypeManager.
+  /// @returns the parsed Type or nullptr if none matched.
   ast::type::Type* type_decl();
   /// Parses a `storage_class` grammar element
   /// @returns the storage class or StorageClass::kNone if none matched
@@ -181,6 +180,9 @@
   /// Parses a `function_decl` grammar element
   /// @returns the parsed function, nullptr otherwise
   std::unique_ptr<ast::Function> function_decl();
+  /// Parses a `sampler_type` grammar element
+  /// @returns the parsed Type or nullptr if none matched.
+  ast::type::Type* sampler_type();
   /// Parses a `function_type_decl` grammar element
   /// @returns the parsed type or nullptr otherwise
   ast::type::Type* function_type_decl();
diff --git a/src/reader/wgsl/parser_impl_sampler_type_test.cc b/src/reader/wgsl/parser_impl_sampler_type_test.cc
new file mode 100644
index 0000000..db71a20
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_sampler_type_test.cc
@@ -0,0 +1,53 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "gtest/gtest.h"
+#include "src/ast/type/sampler_type.h"
+#include "src/reader/wgsl/parser_impl.h"
+#include "src/reader/wgsl/parser_impl_test_helper.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+namespace {
+
+TEST_F(ParserImplTest, SamplerType_Invalid) {
+  auto* p = parser("1234");
+  auto* t = p->sampler_type();
+  EXPECT_EQ(t, nullptr);
+  EXPECT_FALSE(p->has_error());
+}
+
+TEST_F(ParserImplTest, SamplerType_Sampler) {
+  auto* p = parser("sampler");
+  auto* t = p->sampler_type();
+  ASSERT_NE(t, nullptr);
+  ASSERT_TRUE(t->IsSampler());
+  EXPECT_FALSE(t->AsSampler()->IsComparison());
+  EXPECT_FALSE(p->has_error());
+}
+
+TEST_F(ParserImplTest, SamplerType_ComparisonSampler) {
+  auto* p = parser("sampler_comparison");
+  auto* t = p->sampler_type();
+  ASSERT_NE(t, nullptr);
+  ASSERT_TRUE(t->IsSampler());
+  EXPECT_TRUE(t->AsSampler()->IsComparison());
+  EXPECT_FALSE(p->has_error());
+}
+
+}  // namespace
+}  // namespace wgsl
+}  // namespace reader
+}  // namespace tint
diff --git a/src/reader/wgsl/token.cc b/src/reader/wgsl/token.cc
index 2705fd2..f878ade 100644
--- a/src/reader/wgsl/token.cc
+++ b/src/reader/wgsl/token.cc
@@ -197,6 +197,10 @@
       return "ptr";
     case Token::Type::kReturn:
       return "return";
+    case Token::Type::kSampler:
+      return "sampler";
+    case Token::Type::kComparisonSampler:
+      return "sampler_comparison";
     case Token::Type::kSet:
       return "set";
     case Token::Type::kStorageBuffer:
diff --git a/src/reader/wgsl/token.h b/src/reader/wgsl/token.h
index 9f9ba4a..18df905 100644
--- a/src/reader/wgsl/token.h
+++ b/src/reader/wgsl/token.h
@@ -208,6 +208,10 @@
     kPtr,
     /// A 'return'
     kReturn,
+    /// A 'sampler'
+    kSampler,
+    /// A 'sampler_comparison'
+    kComparisonSampler,
     /// A 'set'
     kSet,
     /// A 'storage_buffer'
@@ -465,6 +469,10 @@
   bool IsPtr() const { return type_ == Type::kPtr; }
   /// @returns true if token is a 'return'
   bool IsReturn() const { return type_ == Type::kReturn; }
+  /// @returns true if token is a 'sampler'
+  bool IsSampler() const { return type_ == Type::kSampler; }
+  /// @returns true if token is a 'sampler_comparison'
+  bool IsComparisonSampler() const { return type_ == Type::kComparisonSampler; }
   /// @returns true if token is a 'set'
   bool IsSet() const { return type_ == Type::kSet; }
   /// @returns true if token is a 'storage_buffer'