Import Tint changes from Dawn
Changes:
- 64b09959f7ae6c78ef8f3d7d55b5b746ffb22af9 Update `type_decl` and `primary_expression` by dan sinclair <dsinclair@chromium.org>
- 08d735e6a91d327e1ddddb3d1b955422ad09d885 Add missing relational_expression.post.unary_expression c... by dan sinclair <dsinclair@chromium.org>
- a838e34954c32b115ba3de177672a35193152c4d Handle peeking past placeholder elements. by dan sinclair <dsinclair@chromium.org>
- 393de082d3a3e8c64127ab168735efe61632e601 Add `mat_prefix`, `vec_prefix`, `callable` and `type_decl... by dan sinclair <dsinclair@chromium.org>
- 9284f8ab55f64f8e035ece0ffc54d093bddb6d2d Add test for let with complex constructor by dan sinclair <dsinclair@chromium.org>
- 96a7d5a6bd2506112a12e2dc4e435e19631a2be0 Add some parenthesis. by dan sinclair <dsinclair@chromium.org>
- e76521153f3f0f4341ee89323565bb978b2062dd Fixup shift_expression parsing. by dan sinclair <dsinclair@chromium.org>
- ee25586aec81e62f22a594fc3ab2669ecfd92c2b Sync `expression` grammar element with WGSL spec. by dan sinclair <dsinclair@chromium.org>
GitOrigin-RevId: 64b09959f7ae6c78ef8f3d7d55b5b746ffb22af9
Change-Id: Ia8a35c3c61b935bf0b814090f95a92b55303f31c
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/100121
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 9b2fd35..8283b45 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -1357,6 +1357,7 @@
"reader/wgsl/parser_impl_break_stmt_test.cc",
"reader/wgsl/parser_impl_bug_cases_test.cc",
"reader/wgsl/parser_impl_call_stmt_test.cc",
+ "reader/wgsl/parser_impl_callable_test.cc",
"reader/wgsl/parser_impl_case_body_test.cc",
"reader/wgsl/parser_impl_compound_stmt_test.cc",
"reader/wgsl/parser_impl_const_literal_test.cc",
@@ -1370,6 +1371,7 @@
"reader/wgsl/parser_impl_error_msg_test.cc",
"reader/wgsl/parser_impl_error_resync_test.cc",
"reader/wgsl/parser_impl_exclusive_or_expression_test.cc",
+ "reader/wgsl/parser_impl_expression_test.cc",
"reader/wgsl/parser_impl_external_texture_test.cc",
"reader/wgsl/parser_impl_for_stmt_test.cc",
"reader/wgsl/parser_impl_function_attribute_list_test.cc",
@@ -1417,6 +1419,7 @@
"reader/wgsl/parser_impl_texture_sampler_test.cc",
"reader/wgsl/parser_impl_type_alias_test.cc",
"reader/wgsl/parser_impl_type_decl_test.cc",
+ "reader/wgsl/parser_impl_type_decl_without_ident_test.cc",
"reader/wgsl/parser_impl_unary_expression_test.cc",
"reader/wgsl/parser_impl_variable_attribute_list_test.cc",
"reader/wgsl/parser_impl_variable_attribute_test.cc",
@@ -1424,6 +1427,7 @@
"reader/wgsl/parser_impl_variable_ident_decl_test.cc",
"reader/wgsl/parser_impl_variable_qualifier_test.cc",
"reader/wgsl/parser_impl_variable_stmt_test.cc",
+ "reader/wgsl/parser_impl_vec_mat_prefix_test.cc",
"reader/wgsl/parser_impl_while_stmt_test.cc",
"reader/wgsl/parser_test.cc",
"reader/wgsl/token_test.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 288735a..6703d65 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -952,6 +952,7 @@
reader/wgsl/parser_impl_break_stmt_test.cc
reader/wgsl/parser_impl_bug_cases_test.cc
reader/wgsl/parser_impl_call_stmt_test.cc
+ reader/wgsl/parser_impl_callable_test.cc
reader/wgsl/parser_impl_case_body_test.cc
reader/wgsl/parser_impl_compound_stmt_test.cc
reader/wgsl/parser_impl_const_literal_test.cc
@@ -961,11 +962,12 @@
reader/wgsl/parser_impl_depth_texture_test.cc
reader/wgsl/parser_impl_element_count_expression_test.cc
reader/wgsl/parser_impl_enable_directive_test.cc
- reader/wgsl/parser_impl_external_texture_test.cc
reader/wgsl/parser_impl_equality_expression_test.cc
reader/wgsl/parser_impl_error_msg_test.cc
reader/wgsl/parser_impl_error_resync_test.cc
reader/wgsl/parser_impl_exclusive_or_expression_test.cc
+ reader/wgsl/parser_impl_expression_test.cc
+ reader/wgsl/parser_impl_external_texture_test.cc
reader/wgsl/parser_impl_for_stmt_test.cc
reader/wgsl/parser_impl_function_decl_test.cc
reader/wgsl/parser_impl_function_attribute_list_test.cc
@@ -1012,6 +1014,7 @@
reader/wgsl/parser_impl_texture_sampler_test.cc
reader/wgsl/parser_impl_type_alias_test.cc
reader/wgsl/parser_impl_type_decl_test.cc
+ reader/wgsl/parser_impl_type_decl_without_ident_test.cc
reader/wgsl/parser_impl_unary_expression_test.cc
reader/wgsl/parser_impl_variable_decl_test.cc
reader/wgsl/parser_impl_variable_attribute_list_test.cc
@@ -1019,6 +1022,7 @@
reader/wgsl/parser_impl_variable_ident_decl_test.cc
reader/wgsl/parser_impl_variable_stmt_test.cc
reader/wgsl/parser_impl_variable_qualifier_test.cc
+ reader/wgsl/parser_impl_vec_mat_prefix_test.cc
reader/wgsl/parser_impl_while_stmt_test.cc
reader/wgsl/token_test.cc
)
diff --git a/src/tint/reader/wgsl/parser_impl.cc b/src/tint/reader/wgsl/parser_impl.cc
index cf684d3..9335356 100644
--- a/src/tint/reader/wgsl/parser_impl.cc
+++ b/src/tint/reader/wgsl/parser_impl.cc
@@ -271,23 +271,18 @@
return tokens_[last_source_idx_];
}
-const Token& ParserImpl::peek(size_t idx) {
- if (next_token_idx_ + idx >= tokens_.size()) {
- return tokens_[tokens_.size() - 1];
- }
-
- // Skip over any placeholder elements
- while (true) {
- if (!tokens_[next_token_idx_ + idx].IsPlaceholder()) {
- break;
+const Token& ParserImpl::peek(size_t count) {
+ for (size_t idx = next_token_idx_; idx < tokens_.size(); idx++) {
+ if (tokens_[idx].IsPlaceholder()) {
+ continue;
}
- idx++;
+ if (count == 0) {
+ return tokens_[idx];
+ }
+ count--;
}
- if (next_token_idx_ + idx >= tokens_.size()) {
- return tokens_[tokens_.size() - 1];
- }
-
- return tokens_[next_token_idx_ + idx];
+ // Walked off the end of the token list, return last token.
+ return tokens_[tokens_.size() - 1];
}
bool ParserImpl::peek_is(Token::Type tok, size_t idx) {
@@ -679,6 +674,9 @@
// variable_decl
// : VAR variable_qualifier? optionally_typed_ident
+//
+// Note, the `( LESS_THAN address_space ( COMMA access_mode )? GREATER_THAN ) is pulled out into
+// a `variable_qualifier` helper.
Maybe<ParserImpl::VarDeclInfo> ParserImpl::variable_decl() {
Source source;
if (!match(Token::Type::kVar, &source)) {
@@ -995,7 +993,7 @@
}
// variable_qualifier
-// : LESS_THAN storage_class (COMMA access_mode)? GREATER_THAN
+// : LESS_THAN address_spaces (COMMA access_mode)? GREATER_THAN
Maybe<ParserImpl::VariableQualifier> ParserImpl::variable_qualifier() {
if (!peek_is(Token::Type::kLessThan)) {
return Failure::kNoMatch;
@@ -1056,75 +1054,119 @@
return builder_.ty.alias(make_source_range_from(t.source()), name.value, type.value);
}
-// type_decl
-// : IDENTIFIER
-// | BOOL
-// | FLOAT32
-// | INT32
-// | UINT32
-// | VEC2 LESS_THAN type_decl GREATER_THAN
-// | VEC3 LESS_THAN type_decl GREATER_THAN
-// | VEC4 LESS_THAN type_decl GREATER_THAN
-// | PTR LESS_THAN storage_class, type_decl (COMMA access_mode)? GREATER_THAN
-// | array_attribute_list* ARRAY LESS_THAN type_decl COMMA element_count_expression GREATER_THAN
-// | array_attribute_list* ARRAY LESS_THAN type_decl GREATER_THAN
-// | MAT2x2 LESS_THAN type_decl GREATER_THAN
-// | MAT2x3 LESS_THAN type_decl GREATER_THAN
-// | MAT2x4 LESS_THAN type_decl GREATER_THAN
-// | MAT3x2 LESS_THAN type_decl GREATER_THAN
-// | MAT3x3 LESS_THAN type_decl GREATER_THAN
-// | MAT3x4 LESS_THAN type_decl GREATER_THAN
-// | MAT4x2 LESS_THAN type_decl GREATER_THAN
-// | MAT4x3 LESS_THAN type_decl GREATER_THAN
-// | MAT4x4 LESS_THAN type_decl GREATER_THAN
-// | texture_and_sampler_types
-Maybe<const ast::Type*> ParserImpl::type_decl() {
+// vec_prefix
+// : 'vec2'
+// | 'vec3'
+// | 'vec4'
+Maybe<uint32_t> ParserImpl::vec_prefix() {
auto& t = peek();
- Source source;
- if (match(Token::Type::kIdentifier, &source)) {
- return builder_.create<ast::TypeName>(source, builder_.Symbols().Register(t.to_str()));
+ if (!t.IsVector()) {
+ return Failure::kNoMatch;
+ }
+ next();
+
+ if (t.Is(Token::Type::kVec3)) {
+ return 3u;
+ }
+ if (t.Is(Token::Type::kVec4)) {
+ return 4u;
+ }
+ return 2u;
+}
+
+// mat_prefix
+// : 'mat2x2'
+// | 'mat2x3'
+// | 'mat2x4'
+// | 'mat3x2'
+// | 'mat3x3'
+// | 'mat3x4'
+// | 'mat4x2'
+// | 'mat4x3'
+// | 'mat4x4'
+Maybe<ParserImpl::MatrixDimensions> ParserImpl::mat_prefix() {
+ auto& t = peek();
+ if (!t.IsMatrix()) {
+ return Failure::kNoMatch;
+ }
+ next();
+
+ uint32_t columns = 2;
+ if (t.IsMat3xN()) {
+ columns = 3;
+ } else if (t.IsMat4xN()) {
+ columns = 4;
+ }
+ if (t.IsMatNx3()) {
+ return MatrixDimensions{columns, 3};
+ }
+ if (t.IsMatNx4()) {
+ return MatrixDimensions{columns, 4};
+ }
+ return MatrixDimensions{columns, 2};
+}
+
+// type_decl_without_ident:
+// : BOOL
+// | F16
+// | F32
+// | I32
+// | U32
+// | ARRAY LESS_THAN type_decl ( COMMA element_count_expression )? GREATER_THAN
+// | ATOMIC LESS_THAN type_decl GREATER_THAN
+// | PTR LESS_THAN address_space COMMA type_decl ( COMMA access_mode )? GREATER_THAN
+// | mat_prefix LESS_THAN type_decl GREATER_THAN
+// | vec_prefix LESS_THAN type_decl GREATER_THAN
+// | texture_and_sampler_types
+Maybe<const ast::Type*> ParserImpl::type_decl_without_ident() {
+ auto& t = peek();
+
+ if (match(Token::Type::kBool)) {
+ return builder_.ty.bool_(t.source());
}
- if (match(Token::Type::kBool, &source)) {
- return builder_.ty.bool_(source);
+ if (match(Token::Type::kF16)) {
+ return builder_.ty.f16(t.source());
}
- if (match(Token::Type::kF16, &source)) {
- return builder_.ty.f16(source);
+ if (match(Token::Type::kF32)) {
+ return builder_.ty.f32(t.source());
}
- if (match(Token::Type::kF32, &source)) {
- return builder_.ty.f32(source);
+ if (match(Token::Type::kI32)) {
+ return builder_.ty.i32(t.source());
}
- if (match(Token::Type::kI32, &source)) {
- return builder_.ty.i32(source);
+ if (match(Token::Type::kU32)) {
+ return builder_.ty.u32(t.source());
}
- if (match(Token::Type::kU32, &source)) {
- return builder_.ty.u32(source);
- }
-
- if (t.IsVector()) {
- next(); // Consume the peek
- return expect_type_decl_vector(t);
- }
-
- if (match(Token::Type::kPtr)) {
- return expect_type_decl_pointer(t);
+ if (t.Is(Token::Type::kArray) && peek_is(Token::Type::kLessThan, 1)) {
+ if (match(Token::Type::kArray)) {
+ return expect_type_decl_array(t.source());
+ }
}
if (match(Token::Type::kAtomic)) {
- return expect_type_decl_atomic(t);
+ return expect_type_decl_atomic(t.source());
}
- if (match(Token::Type::kArray, &source)) {
- return expect_type_decl_array(t);
+ if (match(Token::Type::kPtr)) {
+ return expect_type_decl_pointer(t.source());
}
- if (t.IsMatrix()) {
- next(); // Consume the peek
- return expect_type_decl_matrix(t);
+ if (t.IsMatrix() && peek_is(Token::Type::kLessThan, 1)) {
+ auto mat = mat_prefix();
+ if (mat.matched) {
+ return expect_type_decl_matrix(t.source(), mat.value);
+ }
+ }
+
+ if (t.IsVector() && peek_is(Token::Type::kLessThan, 1)) {
+ auto vec = vec_prefix();
+ if (vec.matched) {
+ return expect_type_decl_vector(t.source(), vec.value);
+ }
}
auto texture_or_sampler = texture_and_sampler_types();
@@ -1138,6 +1180,19 @@
return Failure::kNoMatch;
}
+// type_decl
+// : IDENTIFIER
+// | type_decl_without_ident
+Maybe<const ast::Type*> ParserImpl::type_decl() {
+ auto& t = peek();
+ Source source;
+ if (match(Token::Type::kIdentifier, &source)) {
+ return builder_.create<ast::TypeName>(source, builder_.Symbols().Register(t.to_str()));
+ }
+
+ return type_decl_without_ident();
+}
+
Expect<const ast::Type*> ParserImpl::expect_type(std::string_view use) {
auto type = type_decl();
if (type.errored) {
@@ -1149,7 +1204,8 @@
return type.value;
}
-Expect<const ast::Type*> ParserImpl::expect_type_decl_pointer(const Token& t) {
+// LESS_THAN address_space COMMA type_decl ( COMMA access_mode )? GREATER_THAN
+Expect<const ast::Type*> ParserImpl::expect_type_decl_pointer(const Source& s) {
const char* use = "ptr declaration";
auto storage_class = ast::StorageClass::kNone;
@@ -1186,11 +1242,11 @@
return Failure::kErrored;
}
- return builder_.ty.pointer(make_source_range_from(t.source()), subtype.value, storage_class,
- access);
+ return builder_.ty.pointer(make_source_range_from(s), subtype.value, storage_class, access);
}
-Expect<const ast::Type*> ParserImpl::expect_type_decl_atomic(const Token& t) {
+// LESS_THAN type_decl GREATER_THAN
+Expect<const ast::Type*> ParserImpl::expect_type_decl_atomic(const Source& s) {
const char* use = "atomic declaration";
auto subtype = expect_lt_gt_block(use, [&] { return expect_type(use); });
@@ -1198,31 +1254,22 @@
return Failure::kErrored;
}
- return builder_.ty.atomic(make_source_range_from(t.source()), subtype.value);
+ return builder_.ty.atomic(make_source_range_from(s), subtype.value);
}
-Expect<const ast::Type*> ParserImpl::expect_type_decl_vector(const Token& t) {
- uint32_t count = 2;
- if (t.Is(Token::Type::kVec3)) {
- count = 3;
- } else if (t.Is(Token::Type::kVec4)) {
- count = 4;
+// LESS_THAN type_decl GREATER_THAN
+Expect<const ast::Type*> ParserImpl::expect_type_decl_vector(const Source& s, uint32_t count) {
+ const char* use = "vector";
+ auto ty = expect_lt_gt_block(use, [&] { return expect_type(use); });
+ if (ty.errored) {
+ return Failure::kErrored;
}
- const ast::Type* subtype = nullptr;
- if (peek_is(Token::Type::kLessThan)) {
- const char* use = "vector";
- auto ty = expect_lt_gt_block(use, [&] { return expect_type(use); });
- if (ty.errored) {
- return Failure::kErrored;
- }
- subtype = ty.value;
- }
-
- return builder_.ty.vec(make_source_range_from(t.source()), subtype, count);
+ return builder_.ty.vec(make_source_range_from(s), ty.value, count);
}
-Expect<const ast::Type*> ParserImpl::expect_type_decl_array(const Token& t) {
+// LESS_THAN type_decl ( COMMA element_count_expression )? GREATER_THAN
+Expect<const ast::Type*> ParserImpl::expect_type_decl_array(const Source& s) {
const char* use = "array declaration";
struct TypeAndSize {
@@ -1231,7 +1278,7 @@
};
if (!peek_is(Token::Type::kLessThan)) {
- return builder_.ty.array(make_source_range_from(t.source()), nullptr, nullptr);
+ return add_error(peek(), "expected < for array");
}
auto type_size = expect_lt_gt_block(use, [&]() -> Expect<TypeAndSize> {
@@ -1259,34 +1306,19 @@
return Failure::kErrored;
}
- return builder_.ty.array(make_source_range_from(t.source()), type_size->type, type_size->size);
+ return builder_.ty.array(make_source_range_from(s), type_size->type, type_size->size);
}
-Expect<const ast::Type*> ParserImpl::expect_type_decl_matrix(const Token& t) {
- uint32_t rows = 2;
- uint32_t columns = 2;
- if (t.IsMat3xN()) {
- columns = 3;
- } else if (t.IsMat4xN()) {
- columns = 4;
- }
- if (t.IsMatNx3()) {
- rows = 3;
- } else if (t.IsMatNx4()) {
- rows = 4;
+// LESS_THAN type_decl GREATER_THAN
+Expect<const ast::Type*> ParserImpl::expect_type_decl_matrix(const Source& s,
+ const MatrixDimensions& dims) {
+ const char* use = "matrix";
+ auto ty = expect_lt_gt_block(use, [&] { return expect_type(use); });
+ if (ty.errored) {
+ return Failure::kErrored;
}
- const ast::Type* subtype = nullptr;
- if (peek_is(Token::Type::kLessThan)) {
- const char* use = "matrix";
- auto ty = expect_lt_gt_block(use, [&] { return expect_type(use); });
- if (ty.errored) {
- return Failure::kErrored;
- }
- subtype = ty.value;
- }
-
- return builder_.ty.mat(make_source_range_from(t.source()), subtype, columns, rows);
+ return builder_.ty.mat(make_source_range_from(s), ty.value, dims.columns, dims.rows);
}
// address_space
@@ -1888,7 +1920,7 @@
return Failure::kErrored;
}
- auto initializer = expression();
+ auto initializer = maybe_expression();
if (initializer.errored) {
return Failure::kErrored;
}
@@ -1915,7 +1947,7 @@
return Failure::kErrored;
}
- auto initializer = expression();
+ auto initializer = maybe_expression();
if (initializer.errored) {
return Failure::kErrored;
}
@@ -1942,7 +1974,7 @@
const ast::Expression* initializer = nullptr;
if (match(Token::Type::kEqual)) {
- auto initializer_expr = expression();
+ auto initializer_expr = maybe_expression();
if (initializer_expr.errored) {
return Failure::kErrored;
}
@@ -2435,32 +2467,62 @@
return continuing_compound_statement();
}
-// primary_expression
-// : IDENT argument_expression_list?
-// | type_decl argument_expression_list
-// | const_literal
-// | paren_expression
-// | BITCAST LESS_THAN type_decl GREATER_THAN paren_expression
-Maybe<const ast::Expression*> ParserImpl::primary_expression() {
+// callable
+// : type_decl_without_ident
+// | ARRAY
+// | mat_prefix
+// | vec_prefix
+//
+// Note, `ident` is pulled out to `primary_expression` as it's the only one that
+// doesn't create a `type`. Then we can just return a `type` from here on match and
+// deal with `ident` in `primary_expression.
+Maybe<const ast::Type*> ParserImpl::callable() {
auto& t = peek();
- auto lit = const_literal();
- if (lit.errored) {
+ // This _must_ match `type_decl_without_ident` before any of the other types as they're all
+ // prefixes of the types and we want to match the longer `vec3<f32>` then the shorter
+ // prefix match of `vec3`.
+ auto ty = type_decl_without_ident();
+ if (ty.errored) {
return Failure::kErrored;
}
- if (lit.matched) {
- return lit.value;
+ if (ty.matched) {
+ return ty.value;
}
- if (t.Is(Token::Type::kParenLeft)) {
- auto paren = expect_paren_expression();
- if (paren.errored) {
- return Failure::kErrored;
- }
-
- return paren.value;
+ if (match(Token::Type::kArray)) {
+ return builder_.ty.array(make_source_range_from(t.source()), nullptr, nullptr);
}
+ auto vec = vec_prefix();
+ if (vec.matched) {
+ return builder_.ty.vec(make_source_range_from(t.source()), nullptr, vec.value);
+ }
+
+ auto mat = mat_prefix();
+ if (mat.matched) {
+ return builder_.ty.mat(make_source_range_from(t.source()), nullptr, mat.value.columns,
+ mat.value.rows);
+ }
+
+ return Failure::kNoMatch;
+}
+
+// primary_expression
+// : BITCAST LESS_THAN type_decl GREATER_THAN paren_expression
+// | callable argument_expression_list
+// | const_literal
+// | IDENT argument_expression_list?
+// | paren_expression
+//
+// Note, PAREN_LEFT ( expression ( COMMA expression ) * COMMA? )? PAREN_RIGHT is replaced
+// with `argument_expression_list`.
+//
+// Note, this is matching the `callable` ident here instead of having to come from
+// callable so we can return a `type` from callable.
+Maybe<const ast::Expression*> ParserImpl::primary_expression() {
+ auto& t = peek();
+
if (match(Token::Type::kBitcast)) {
const char* use = "bitcast expression";
@@ -2477,6 +2539,27 @@
return create<ast::BitcastExpression>(t.source(), type.value, params.value);
}
+ auto call = callable();
+ if (call.errored) {
+ return Failure::kErrored;
+ }
+ if (call.matched) {
+ auto params = expect_argument_expression_list("type constructor");
+ if (params.errored) {
+ return Failure::kErrored;
+ }
+
+ return builder_.Construct(t.source(), call.value, std::move(params.value));
+ }
+
+ auto lit = const_literal();
+ if (lit.errored) {
+ return Failure::kErrored;
+ }
+ if (lit.matched) {
+ return lit.value;
+ }
+
if (t.IsIdentifier()) {
next();
@@ -2495,17 +2578,13 @@
return ident;
}
- auto type = type_decl();
- if (type.errored) {
- return Failure::kErrored;
- }
- if (type.matched) {
- auto params = expect_argument_expression_list("type constructor");
- if (params.errored) {
+ if (t.Is(Token::Type::kParenLeft)) {
+ auto paren = expect_paren_expression();
+ if (paren.errored) {
return Failure::kErrored;
}
- return builder_.Construct(t.source(), type.value, std::move(params.value));
+ return paren.value;
}
return Failure::kNoMatch;
@@ -2814,11 +2893,11 @@
}
// shift_expression.post.unary_expression
-// : multiplicative_expression.post.unary_expression?
+// : math_expression.post.unary_expression?
// | SHIFT_LEFT unary_expression
// | SHIFT_RIGHT unary_expression
//
-// Note, add the `multiplicative_expression.post.unary_expression` is added here to make
+// Note, add the `math_expression.post.unary_expression` is added here to make
// implementation simpler.
Expect<const ast::Expression*> ParserImpl::expect_shift_expression_post_unary_expression(
const ast::Expression* lhs) {
@@ -2845,7 +2924,7 @@
return create<ast::BinaryExpression>(t.source(), op, lhs, rhs.value);
}
- return expect_multiplicative_expression_post_unary_expression(lhs);
+ return expect_math_expression_post_unary_expression(lhs);
}
// relational_expression
@@ -2892,6 +2971,10 @@
op = ast::BinaryOp::kLessThanEqual;
} else if (t.Is(Token::Type::kGreaterThanEqual)) {
op = ast::BinaryOp::kGreaterThanEqual;
+ } else if (t.Is(Token::Type::kEqualEqual)) {
+ op = ast::BinaryOp::kEqual;
+ } else if (t.Is(Token::Type::kNotEqual)) {
+ op = ast::BinaryOp::kNotEqual;
}
auto& next = peek();
@@ -2908,6 +2991,74 @@
return lhs;
}
+// expression
+// : unary_expression bitwise_expression.post.unary_expression
+// | unary_expression relational_expression.post.unary_expression
+// | unary_expression relational_expression.post.unary_expression and_and
+// relational_expression ( and_and relational_expression )*
+// | unary_expression relational_expression.post.unary_expression or_or
+// relational_expression ( or_or relational_expression )*
+//
+// Note, a `relational_expression` element was added to simplify many of the right sides
+Maybe<const ast::Expression*> ParserImpl::maybe_expression() {
+ auto lhs = unary_expression();
+ if (lhs.errored) {
+ return Failure::kErrored;
+ }
+ if (!lhs.matched) {
+ return Failure::kNoMatch;
+ }
+
+ auto bitwise = bitwise_expression_post_unary_expression(lhs.value);
+ if (bitwise.errored) {
+ return Failure::kErrored;
+ }
+ if (bitwise.matched) {
+ return bitwise.value;
+ }
+
+ auto relational = expect_relational_expression_post_unary_expression(lhs.value);
+ if (relational.errored) {
+ return Failure::kErrored;
+ }
+ auto* ret = relational.value;
+
+ auto& t = peek();
+ if (t.Is(Token::Type::kAndAnd) || t.Is(Token::Type::kOrOr)) {
+ ast::BinaryOp op = ast::BinaryOp::kNone;
+ if (t.Is(Token::Type::kAndAnd)) {
+ op = ast::BinaryOp::kLogicalAnd;
+ } else if (t.Is(Token::Type::kOrOr)) {
+ op = ast::BinaryOp::kLogicalOr;
+ }
+
+ while (continue_parsing()) {
+ auto& n = peek();
+ if (!n.Is(t.type())) {
+ if (n.Is(Token::Type::kAndAnd) || n.Is(Token::Type::kOrOr)) {
+ return add_error(
+ n.source(), std::string("mixing '") + std::string(t.to_name()) + "' and '" +
+ std::string(n.to_name()) + "' requires parenthesis");
+ }
+ break;
+ }
+ next();
+
+ auto rhs = relational_expression();
+ if (rhs.errored) {
+ return Failure::kErrored;
+ }
+ if (!rhs.matched) {
+ return add_error(peek(), std::string("unable to parse right side of ") +
+ std::string(t.to_name()) + " expression");
+ }
+
+ ret = create<ast::BinaryExpression>(t.source(), op, ret, rhs.value);
+ }
+ }
+ return ret;
+}
+
// singular_expression
// : primary_expression postfix_expr
Maybe<const ast::Expression*> ParserImpl::singular_expression() {
diff --git a/src/tint/reader/wgsl/parser_impl.h b/src/tint/reader/wgsl/parser_impl.h
index d6d3ccc..44525c0 100644
--- a/src/tint/reader/wgsl/parser_impl.h
+++ b/src/tint/reader/wgsl/parser_impl.h
@@ -309,6 +309,14 @@
ast::Access access = ast::Access::kUndefined;
};
+ /// MatrixDimensions contains the column and row information for a matrix
+ struct MatrixDimensions {
+ /// The number of columns
+ uint32_t columns = 0;
+ /// The number of rows
+ uint32_t rows = 0;
+ };
+
/// Creates a new parser using the given file
/// @param file the input source file to parse
explicit ParserImpl(Source::File const* file);
@@ -435,6 +443,18 @@
/// Parses a `type_alias_decl` grammar element
/// @returns the type alias or nullptr on error
Maybe<const ast::Alias*> type_alias_decl();
+ /// Parses a `callable` grammar element
+ /// @returns the type or nullptr
+ Maybe<const ast::Type*> callable();
+ /// Parses a `vec_prefix` grammar element
+ /// @returns the vector size or nullptr
+ Maybe<uint32_t> vec_prefix();
+ /// Parses a `mat_prefix` grammar element
+ /// @returns the matrix dimensions or nullptr
+ Maybe<MatrixDimensions> mat_prefix();
+ /// Parses a `type_decl_without_ident` grammar element
+ /// @returns the parsed Type or nullptr if none matched.
+ Maybe<const ast::Type*> type_decl_without_ident();
/// Parses a `type_decl` grammar element
/// @returns the parsed Type or nullptr if none matched.
Maybe<const ast::Type*> type_decl();
@@ -630,6 +650,9 @@
/// @param lhs the left side of the expression
/// @returns the parsed expression or nullptr
Expect<const ast::Expression*> expect_relational_expr(const ast::Expression* lhs);
+ /// Parses the `expression` grammar rule
+ /// @returns the parsed expression or nullptr
+ Maybe<const ast::Expression*> maybe_expression();
/// Parses the `relational_expression` grammar element
/// @returns the parsed expression or nullptr
Maybe<const ast::Expression*> relational_expression();
@@ -759,6 +782,11 @@
/// @return the parsed attribute, or nullptr on error.
Expect<const ast::Attribute*> expect_attribute();
+ /// Splits a peekable token into to parts filling in the peekable fields.
+ /// @param lhs the token to set in the current position
+ /// @param rhs the token to set in the placeholder
+ void split_token(Token::Type lhs, Token::Type rhs);
+
private:
/// ReturnType resolves to the return type for the function or lambda F.
template <typename F>
@@ -916,11 +944,11 @@
/// Used to ensure that all attributes are consumed.
bool expect_attributes_consumed(utils::VectorRef<const ast::Attribute*> list);
- Expect<const ast::Type*> expect_type_decl_pointer(const Token& t);
- Expect<const ast::Type*> expect_type_decl_atomic(const Token& t);
- Expect<const ast::Type*> expect_type_decl_vector(const Token& t);
- Expect<const ast::Type*> expect_type_decl_array(const Token& t);
- Expect<const ast::Type*> expect_type_decl_matrix(const Token& t);
+ Expect<const ast::Type*> expect_type_decl_pointer(const Source& s);
+ Expect<const ast::Type*> expect_type_decl_atomic(const Source& s);
+ Expect<const ast::Type*> expect_type_decl_vector(const Source& s, uint32_t count);
+ Expect<const ast::Type*> expect_type_decl_array(const Source& s);
+ Expect<const ast::Type*> expect_type_decl_matrix(const Source& s, const MatrixDimensions& dims);
Expect<const ast::Type*> expect_type(std::string_view use);
@@ -928,8 +956,6 @@
Maybe<const ast::Statement*> for_header_initializer();
Maybe<const ast::Statement*> for_header_continuing();
- void split_token(Token::Type lhs, Token::Type rhs);
-
class MultiTokenSource;
MultiTokenSource make_source_range();
MultiTokenSource make_source_range_from(const Source& start);
diff --git a/src/tint/reader/wgsl/parser_impl_callable_test.cc b/src/tint/reader/wgsl/parser_impl_callable_test.cc
new file mode 100644
index 0000000..f47dde0
--- /dev/null
+++ b/src/tint/reader/wgsl/parser_impl_callable_test.cc
@@ -0,0 +1,156 @@
+// 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 "src/tint/reader/wgsl/parser_impl_test_helper.h"
+
+namespace tint::reader::wgsl {
+namespace {
+
+TEST_F(ParserImplTest, Callable_Array) {
+ auto p = parser("array");
+ auto t = p->callable();
+ ASSERT_TRUE(p->peek_is(Token::Type::kEOF));
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_FALSE(p->has_error()) << p->error();
+ ASSERT_NE(t.value, nullptr);
+ ASSERT_TRUE(t.value->Is<ast::Array>());
+
+ auto* a = t.value->As<ast::Array>();
+ EXPECT_FALSE(a->IsRuntimeArray());
+ EXPECT_EQ(a->type, nullptr);
+ EXPECT_EQ(a->count, nullptr);
+}
+
+TEST_F(ParserImplTest, Callable_VecPrefix) {
+ auto p = parser("vec3");
+ auto t = p->callable();
+ ASSERT_TRUE(p->peek_is(Token::Type::kEOF));
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_FALSE(p->has_error()) << p->error();
+ ASSERT_NE(t.value, nullptr);
+ ASSERT_TRUE(t.value->Is<ast::Vector>());
+
+ auto* v = t.value->As<ast::Vector>();
+ EXPECT_EQ(v->type, nullptr);
+ EXPECT_EQ(v->width, 3u);
+}
+
+TEST_F(ParserImplTest, Callable_MatPrefix) {
+ auto p = parser("mat3x2");
+ auto t = p->callable();
+ ASSERT_TRUE(p->peek_is(Token::Type::kEOF));
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_FALSE(p->has_error()) << p->error();
+ ASSERT_NE(t.value, nullptr);
+ ASSERT_TRUE(t.value->Is<ast::Matrix>());
+
+ auto* m = t.value->As<ast::Matrix>();
+ EXPECT_EQ(m->type, nullptr);
+ EXPECT_EQ(m->columns, 3u);
+ EXPECT_EQ(m->rows, 2u);
+}
+
+TEST_F(ParserImplTest, Callable_TypeDecl_F32) {
+ auto p = parser("f32");
+ auto t = p->callable();
+ ASSERT_TRUE(p->peek_is(Token::Type::kEOF));
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_FALSE(p->has_error()) << p->error();
+ ASSERT_NE(t.value, nullptr);
+ ASSERT_TRUE(t.value->Is<ast::F32>());
+}
+
+TEST_F(ParserImplTest, Callable_TypeDecl_Array) {
+ auto p = parser("array<f32, 2>");
+ auto t = p->callable();
+ ASSERT_TRUE(p->peek_is(Token::Type::kEOF));
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_FALSE(p->has_error()) << p->error();
+ ASSERT_NE(t.value, nullptr);
+ ASSERT_TRUE(t.value->Is<ast::Array>());
+
+ auto* a = t.value->As<ast::Array>();
+ EXPECT_FALSE(a->IsRuntimeArray());
+ EXPECT_TRUE(a->type->Is<ast::F32>());
+
+ auto* size = a->count->As<ast::IntLiteralExpression>();
+ ASSERT_NE(size, nullptr);
+ EXPECT_EQ(size->value, 2);
+ EXPECT_EQ(size->suffix, ast::IntLiteralExpression::Suffix::kNone);
+}
+
+TEST_F(ParserImplTest, Callable_TypeDecl_Array_Runtime) {
+ auto p = parser("array<f32>");
+ auto t = p->callable();
+ ASSERT_TRUE(p->peek_is(Token::Type::kEOF));
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_FALSE(p->has_error()) << p->error();
+ ASSERT_NE(t.value, nullptr);
+ ASSERT_TRUE(t.value->Is<ast::Array>());
+
+ auto* a = t.value->As<ast::Array>();
+ EXPECT_TRUE(a->IsRuntimeArray());
+ EXPECT_TRUE(a->type->Is<ast::F32>());
+
+ ASSERT_EQ(a->count, nullptr);
+}
+
+TEST_F(ParserImplTest, Callable_TypeDecl_VecPrefix) {
+ auto p = parser("vec3<f32>");
+ auto t = p->callable();
+ ASSERT_TRUE(p->peek_is(Token::Type::kEOF));
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_FALSE(p->has_error()) << p->error();
+ ASSERT_NE(t.value, nullptr);
+ ASSERT_TRUE(t.value->Is<ast::Vector>());
+
+ auto* v = t.value->As<ast::Vector>();
+ EXPECT_TRUE(v->type->Is<ast::F32>());
+ EXPECT_EQ(v->width, 3u);
+}
+
+TEST_F(ParserImplTest, Callable_TypeDecl_MatPrefix) {
+ auto p = parser("mat3x2<f32>");
+ auto t = p->callable();
+ ASSERT_TRUE(p->peek_is(Token::Type::kEOF));
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_FALSE(p->has_error()) << p->error();
+ ASSERT_NE(t.value, nullptr);
+ ASSERT_TRUE(t.value->Is<ast::Matrix>());
+
+ auto* m = t.value->As<ast::Matrix>();
+ EXPECT_TRUE(m->type->Is<ast::F32>());
+ EXPECT_EQ(m->columns, 3u);
+ EXPECT_EQ(m->rows, 2u);
+}
+
+TEST_F(ParserImplTest, Callable_NoMatch) {
+ auto p = parser("ident");
+ auto t = p->callable();
+ EXPECT_FALSE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_FALSE(p->has_error()) << p->error();
+ EXPECT_EQ(nullptr, t.value);
+}
+
+} // namespace
+} // namespace tint::reader::wgsl
diff --git a/src/tint/reader/wgsl/parser_impl_expression_test.cc b/src/tint/reader/wgsl/parser_impl_expression_test.cc
new file mode 100644
index 0000000..2938e3c
--- /dev/null
+++ b/src/tint/reader/wgsl/parser_impl_expression_test.cc
@@ -0,0 +1,254 @@
+// Copyright 2022 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 "src/tint/reader/wgsl/parser_impl_test_helper.h"
+
+namespace tint::reader::wgsl {
+namespace {
+
+TEST_F(ParserImplTest, Expression_InvalidLHS) {
+ auto p = parser("if (a) {} || true");
+ auto e = p->maybe_expression();
+ EXPECT_FALSE(e.matched);
+ EXPECT_FALSE(e.errored);
+ EXPECT_FALSE(p->has_error()) << p->error();
+ EXPECT_EQ(e.value, nullptr);
+}
+
+TEST_F(ParserImplTest, Expression_Or_Parses) {
+ auto p = parser("a || true");
+ auto e = p->maybe_expression();
+ EXPECT_TRUE(e.matched);
+ EXPECT_FALSE(e.errored);
+ EXPECT_FALSE(p->has_error()) << p->error();
+ ASSERT_NE(e.value, nullptr);
+
+ EXPECT_EQ(e->source.range.begin.line, 1u);
+ EXPECT_EQ(e->source.range.begin.column, 3u);
+ EXPECT_EQ(e->source.range.end.line, 1u);
+ EXPECT_EQ(e->source.range.end.column, 5u);
+
+ ASSERT_TRUE(e->Is<ast::BinaryExpression>());
+ auto* rel = e->As<ast::BinaryExpression>();
+ EXPECT_EQ(ast::BinaryOp::kLogicalOr, rel->op);
+
+ ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
+ auto* ident = rel->lhs->As<ast::IdentifierExpression>();
+ EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a"));
+
+ ASSERT_TRUE(rel->rhs->Is<ast::BoolLiteralExpression>());
+ ASSERT_TRUE(rel->rhs->As<ast::BoolLiteralExpression>()->value);
+}
+
+TEST_F(ParserImplTest, Expression_Or_Parses_Multiple) {
+ auto p = parser("a || true || b");
+ auto e = p->maybe_expression();
+ EXPECT_TRUE(e.matched);
+ EXPECT_FALSE(e.errored);
+ EXPECT_FALSE(p->has_error()) << p->error();
+ ASSERT_NE(e.value, nullptr);
+
+ ASSERT_TRUE(e->Is<ast::BinaryExpression>());
+ // lhs: (a || true)
+ // rhs: b
+ auto* rel = e->As<ast::BinaryExpression>();
+ EXPECT_EQ(ast::BinaryOp::kLogicalOr, rel->op);
+
+ ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
+ auto* ident = rel->rhs->As<ast::IdentifierExpression>();
+ EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("b"));
+
+ ASSERT_TRUE(rel->lhs->Is<ast::BinaryExpression>());
+ // lhs: a
+ // rhs: true
+ rel = rel->lhs->As<ast::BinaryExpression>();
+ EXPECT_EQ(ast::BinaryOp::kLogicalOr, rel->op);
+
+ ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
+ ident = rel->lhs->As<ast::IdentifierExpression>();
+ EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a"));
+
+ ASSERT_TRUE(rel->rhs->Is<ast::BoolLiteralExpression>());
+ ASSERT_TRUE(rel->rhs->As<ast::BoolLiteralExpression>()->value);
+}
+
+TEST_F(ParserImplTest, Expression_Or_InvalidRHS) {
+ auto p = parser("true || if (a) {}");
+ auto e = p->maybe_expression();
+ EXPECT_FALSE(e.matched);
+ EXPECT_TRUE(e.errored);
+ EXPECT_EQ(e.value, nullptr);
+ EXPECT_TRUE(p->has_error());
+ EXPECT_EQ(p->error(), "1:9: unable to parse right side of || expression");
+}
+
+TEST_F(ParserImplTest, Expression_And_Parses) {
+ auto p = parser("a && true");
+ auto e = p->maybe_expression();
+ EXPECT_TRUE(e.matched);
+ EXPECT_FALSE(e.errored);
+ EXPECT_FALSE(p->has_error()) << p->error();
+ ASSERT_NE(e.value, nullptr);
+
+ EXPECT_EQ(e->source.range.begin.line, 1u);
+ EXPECT_EQ(e->source.range.begin.column, 3u);
+ EXPECT_EQ(e->source.range.end.line, 1u);
+ EXPECT_EQ(e->source.range.end.column, 5u);
+
+ ASSERT_TRUE(e->Is<ast::BinaryExpression>());
+ auto* rel = e->As<ast::BinaryExpression>();
+ EXPECT_EQ(ast::BinaryOp::kLogicalAnd, rel->op);
+
+ ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
+ auto* ident = rel->lhs->As<ast::IdentifierExpression>();
+ EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a"));
+
+ ASSERT_TRUE(rel->rhs->Is<ast::BoolLiteralExpression>());
+ ASSERT_TRUE(rel->rhs->As<ast::BoolLiteralExpression>()->value);
+}
+
+TEST_F(ParserImplTest, Expression_And_Parses_Multple) {
+ auto p = parser("a && true && b");
+ auto e = p->maybe_expression();
+ EXPECT_TRUE(e.matched);
+ EXPECT_FALSE(e.errored);
+ EXPECT_FALSE(p->has_error()) << p->error();
+ ASSERT_NE(e.value, nullptr);
+
+ ASSERT_TRUE(e->Is<ast::BinaryExpression>());
+ // lhs: (a && true)
+ // rhs: b
+ auto* rel = e->As<ast::BinaryExpression>();
+ EXPECT_EQ(ast::BinaryOp::kLogicalAnd, rel->op);
+
+ ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
+ auto* ident = rel->rhs->As<ast::IdentifierExpression>();
+ EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("b"));
+
+ ASSERT_TRUE(rel->lhs->Is<ast::BinaryExpression>());
+ // lhs: a
+ // rhs: true
+ rel = rel->lhs->As<ast::BinaryExpression>();
+ ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
+ ident = rel->lhs->As<ast::IdentifierExpression>();
+ EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a"));
+
+ ASSERT_TRUE(rel->rhs->Is<ast::BoolLiteralExpression>());
+ ASSERT_TRUE(rel->rhs->As<ast::BoolLiteralExpression>()->value);
+}
+
+TEST_F(ParserImplTest, Expression_And_InvalidRHS) {
+ auto p = parser("true && if (a) {}");
+ auto e = p->maybe_expression();
+ EXPECT_FALSE(e.matched);
+ EXPECT_TRUE(e.errored);
+ EXPECT_EQ(e.value, nullptr);
+ EXPECT_TRUE(p->has_error());
+ EXPECT_EQ(p->error(), "1:9: unable to parse right side of && expression");
+}
+
+TEST_F(ParserImplTest, Expression_Mixing_OrWithAnd) {
+ auto p = parser("a && true || b");
+ auto e = p->maybe_expression();
+ EXPECT_FALSE(e.matched);
+ EXPECT_TRUE(e.errored);
+ EXPECT_EQ(e.value, nullptr);
+ EXPECT_TRUE(p->has_error());
+ EXPECT_EQ(p->error(), "1:11: mixing '&&' and '||' requires parenthesis");
+}
+
+TEST_F(ParserImplTest, Expression_Mixing_AndWithOr) {
+ auto p = parser("a || true && b");
+ auto e = p->maybe_expression();
+ EXPECT_FALSE(e.matched);
+ EXPECT_TRUE(e.errored);
+ EXPECT_EQ(e.value, nullptr);
+ EXPECT_TRUE(p->has_error());
+ EXPECT_EQ(p->error(), "1:11: mixing '||' and '&&' requires parenthesis");
+}
+
+TEST_F(ParserImplTest, Expression_Bitwise) {
+ auto p = parser("a & b");
+ auto e = p->maybe_expression();
+ EXPECT_TRUE(e.matched);
+ EXPECT_FALSE(e.errored);
+ EXPECT_FALSE(p->has_error()) << p->error();
+ ASSERT_NE(e.value, nullptr);
+
+ ASSERT_TRUE(e->Is<ast::BinaryExpression>());
+ auto* rel = e->As<ast::BinaryExpression>();
+ EXPECT_EQ(ast::BinaryOp::kAnd, rel->op);
+
+ ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
+ auto* ident = rel->lhs->As<ast::IdentifierExpression>();
+ EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a"));
+
+ ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
+ ident = rel->rhs->As<ast::IdentifierExpression>();
+ EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("b"));
+}
+
+TEST_F(ParserImplTest, Expression_Relational) {
+ auto p = parser("a <= b");
+ auto e = p->maybe_expression();
+ EXPECT_TRUE(e.matched);
+ EXPECT_FALSE(e.errored);
+ EXPECT_FALSE(p->has_error()) << p->error();
+ ASSERT_NE(e.value, nullptr);
+
+ ASSERT_TRUE(e->Is<ast::BinaryExpression>());
+ auto* rel = e->As<ast::BinaryExpression>();
+ EXPECT_EQ(ast::BinaryOp::kLessThanEqual, rel->op);
+
+ ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
+ auto* ident = rel->lhs->As<ast::IdentifierExpression>();
+ EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a"));
+
+ ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
+ ident = rel->rhs->As<ast::IdentifierExpression>();
+ EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("b"));
+}
+
+TEST_F(ParserImplTest, Expression_InvalidUnary) {
+ auto p = parser("!if || true");
+ auto e = p->maybe_expression();
+ EXPECT_FALSE(e.matched);
+ EXPECT_TRUE(e.errored);
+ EXPECT_EQ(e.value, nullptr);
+ EXPECT_TRUE(p->has_error());
+ EXPECT_EQ(p->error(), "1:2: unable to parse right side of ! expression");
+}
+
+TEST_F(ParserImplTest, Expression_InvalidBitwise) {
+ auto p = parser("a & if");
+ auto e = p->maybe_expression();
+ EXPECT_FALSE(e.matched);
+ EXPECT_TRUE(e.errored);
+ EXPECT_EQ(e.value, nullptr);
+ EXPECT_TRUE(p->has_error());
+ EXPECT_EQ(p->error(), "1:5: unable to parse right side of & expression");
+}
+
+TEST_F(ParserImplTest, Expression_InvalidRelational) {
+ auto p = parser("a <= if");
+ auto e = p->maybe_expression();
+ EXPECT_FALSE(e.matched);
+ EXPECT_TRUE(e.errored);
+ EXPECT_EQ(e.value, nullptr);
+ EXPECT_TRUE(p->has_error());
+ EXPECT_EQ(p->error(), "1:6: unable to parse right side of <= expression");
+}
+
+} // namespace
+} // namespace tint::reader::wgsl
diff --git a/src/tint/reader/wgsl/parser_impl_relational_expression_test.cc b/src/tint/reader/wgsl/parser_impl_relational_expression_test.cc
index 1c47f87..59cddc6 100644
--- a/src/tint/reader/wgsl/parser_impl_relational_expression_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_relational_expression_test.cc
@@ -244,6 +244,56 @@
ASSERT_TRUE(rel->rhs->As<ast::BoolLiteralExpression>()->value);
}
+TEST_F(ParserImplTest, RelationalExpression_PostUnary_Parses_Equal) {
+ auto p = parser("a == true");
+ auto lhs = p->unary_expression();
+ auto e = p->expect_relational_expression_post_unary_expression(lhs.value);
+ EXPECT_FALSE(e.errored);
+ EXPECT_FALSE(p->has_error()) << p->error();
+ ASSERT_NE(e.value, nullptr);
+
+ EXPECT_EQ(e->source.range.begin.line, 1u);
+ EXPECT_EQ(e->source.range.begin.column, 3u);
+ EXPECT_EQ(e->source.range.end.line, 1u);
+ EXPECT_EQ(e->source.range.end.column, 5u);
+
+ ASSERT_TRUE(e->Is<ast::BinaryExpression>());
+ auto* rel = e->As<ast::BinaryExpression>();
+ EXPECT_EQ(ast::BinaryOp::kEqual, rel->op);
+
+ ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
+ auto* ident = rel->lhs->As<ast::IdentifierExpression>();
+ EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a"));
+
+ ASSERT_TRUE(rel->rhs->Is<ast::BoolLiteralExpression>());
+ ASSERT_TRUE(rel->rhs->As<ast::BoolLiteralExpression>()->value);
+}
+
+TEST_F(ParserImplTest, RelationalExpression_PostUnary_Parses_NotEqual) {
+ auto p = parser("a != true");
+ auto lhs = p->unary_expression();
+ auto e = p->expect_relational_expression_post_unary_expression(lhs.value);
+ EXPECT_FALSE(e.errored);
+ EXPECT_FALSE(p->has_error()) << p->error();
+ ASSERT_NE(e.value, nullptr);
+
+ EXPECT_EQ(e->source.range.begin.line, 1u);
+ EXPECT_EQ(e->source.range.begin.column, 3u);
+ EXPECT_EQ(e->source.range.end.line, 1u);
+ EXPECT_EQ(e->source.range.end.column, 5u);
+
+ ASSERT_TRUE(e->Is<ast::BinaryExpression>());
+ auto* rel = e->As<ast::BinaryExpression>();
+ EXPECT_EQ(ast::BinaryOp::kNotEqual, rel->op);
+
+ ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
+ auto* ident = rel->lhs->As<ast::IdentifierExpression>();
+ EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a"));
+
+ ASSERT_TRUE(rel->rhs->Is<ast::BoolLiteralExpression>());
+ ASSERT_TRUE(rel->rhs->As<ast::BoolLiteralExpression>()->value);
+}
+
TEST_F(ParserImplTest, RelationalExpression_PostUnary_InvalidRHS) {
auto p = parser("true < if (a) {}");
auto lhs = p->unary_expression();
diff --git a/src/tint/reader/wgsl/parser_impl_shift_expression_test.cc b/src/tint/reader/wgsl/parser_impl_shift_expression_test.cc
index 76337cd..d518004 100644
--- a/src/tint/reader/wgsl/parser_impl_shift_expression_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_shift_expression_test.cc
@@ -164,6 +164,27 @@
ASSERT_TRUE(rel->rhs->As<ast::BoolLiteralExpression>()->value);
}
+TEST_F(ParserImplTest, ShiftExpression_PostUnary_Parses_Additive) {
+ auto p = parser("a + b");
+ auto lhs = p->unary_expression();
+ auto e = p->expect_shift_expression_post_unary_expression(lhs.value);
+ EXPECT_FALSE(e.errored);
+ EXPECT_FALSE(p->has_error()) << p->error();
+ ASSERT_NE(e.value, nullptr);
+
+ ASSERT_TRUE(e->Is<ast::BinaryExpression>());
+ auto* rel = e->As<ast::BinaryExpression>();
+ EXPECT_EQ(ast::BinaryOp::kAdd, rel->op);
+
+ ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
+ auto* ident = rel->lhs->As<ast::IdentifierExpression>();
+ EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a"));
+
+ ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
+ ident = rel->rhs->As<ast::IdentifierExpression>();
+ EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("b"));
+}
+
TEST_F(ParserImplTest, ShiftExpression_PostUnary_Parses_Multiplicative) {
auto p = parser("a * b");
auto lhs = p->unary_expression();
diff --git a/src/tint/reader/wgsl/parser_impl_test.cc b/src/tint/reader/wgsl/parser_impl_test.cc
index c234811..21efd2f 100644
--- a/src/tint/reader/wgsl/parser_impl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_test.cc
@@ -136,5 +136,66 @@
EXPECT_EQ(p->error(), "5:3: unterminated block comment") << p->error();
}
+TEST_F(ParserImplTest, Peek) {
+ auto p = parser("a == if");
+ EXPECT_TRUE(p->peek_is(Token::Type::kIdentifier));
+ EXPECT_TRUE(p->peek_is(Token::Type::kEqualEqual, 1));
+ EXPECT_TRUE(p->peek_is(Token::Type::kIf, 2));
+}
+
+TEST_F(ParserImplTest, Peek_Placeholder) {
+ auto p = parser(">> if");
+ EXPECT_TRUE(p->peek_is(Token::Type::kShiftRight));
+ EXPECT_TRUE(p->peek_is(Token::Type::kIf, 1));
+}
+
+TEST_F(ParserImplTest, Peek_PastPlaceholder) {
+ auto p = parser(">= vec2<u32>");
+ auto& n = p->next();
+ ASSERT_TRUE(n.Is(Token::Type::kGreaterThanEqual));
+ EXPECT_TRUE(p->peek_is(Token::Type::kVec2)) << "expected: vec2 got: " << p->peek().to_name();
+ EXPECT_TRUE(p->peek_is(Token::Type::kLessThan, 1))
+ << "expected: < got: " << p->peek(1).to_name();
+}
+
+TEST_F(ParserImplTest, Peek_MultiplePlaceholder) {
+ auto p = parser(">= >= vec2<u32>");
+ auto& n = p->next();
+ ASSERT_TRUE(n.Is(Token::Type::kGreaterThanEqual));
+ EXPECT_TRUE(p->peek_is(Token::Type::kGreaterThanEqual))
+ << "expected: <= got: " << p->peek().to_name();
+ EXPECT_TRUE(p->peek_is(Token::Type::kVec2, 1))
+ << "expected: vec2 got: " << p->peek(1).to_name();
+ EXPECT_TRUE(p->peek_is(Token::Type::kLessThan, 2))
+ << "expected: < got: " << p->peek(2).to_name();
+}
+
+TEST_F(ParserImplTest, Peek_PastEnd) {
+ auto p = parser(">");
+ EXPECT_TRUE(p->peek_is(Token::Type::kGreaterThan));
+ EXPECT_TRUE(p->peek_is(Token::Type::kEOF, 1));
+ EXPECT_TRUE(p->peek_is(Token::Type::kEOF, 2));
+}
+
+TEST_F(ParserImplTest, Peek_PastEnd_WalkingPlaceholders) {
+ auto p = parser(">= >=");
+ auto& n = p->next();
+ ASSERT_TRUE(n.Is(Token::Type::kGreaterThanEqual));
+ EXPECT_TRUE(p->peek_is(Token::Type::kGreaterThanEqual))
+ << "expected: <= got: " << p->peek().to_name();
+ EXPECT_TRUE(p->peek_is(Token::Type::kEOF, 1)) << "expected: EOF got: " << p->peek(1).to_name();
+}
+
+TEST_F(ParserImplTest, Peek_AfterSplit) {
+ auto p = parser(">= vec2<u32>");
+ auto& n = p->next();
+ ASSERT_TRUE(n.Is(Token::Type::kGreaterThanEqual));
+ EXPECT_TRUE(p->peek_is(Token::Type::kVec2)) << "expected: vec2 got: " << p->peek().to_name();
+
+ p->split_token(Token::Type::kGreaterThan, Token::Type::kEqual);
+ ASSERT_TRUE(n.Is(Token::Type::kGreaterThan));
+ EXPECT_TRUE(p->peek_is(Token::Type::kEqual)) << "expected: = got: " << p->peek().to_name();
+}
+
} // namespace
} // namespace tint::reader::wgsl
diff --git a/src/tint/reader/wgsl/parser_impl_type_decl_test.cc b/src/tint/reader/wgsl/parser_impl_type_decl_test.cc
index 57c1b72..926f7c7 100644
--- a/src/tint/reader/wgsl/parser_impl_type_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_type_decl_test.cc
@@ -528,22 +528,6 @@
EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 17u}}));
}
-TEST_F(ParserImplTest, TypeDecl_Array_InferTypeAndSize) {
- auto p = parser("array");
- auto t = p->type_decl();
- EXPECT_TRUE(t.matched);
- EXPECT_FALSE(t.errored);
- ASSERT_NE(t.value, nullptr) << p->error();
- ASSERT_FALSE(p->has_error());
- ASSERT_TRUE(t.value->Is<ast::Array>());
-
- auto* a = t.value->As<ast::Array>();
- EXPECT_FALSE(a->IsRuntimeArray());
- EXPECT_EQ(a->type, nullptr);
- EXPECT_EQ(a->count, nullptr);
- EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 6u}}));
-}
-
TEST_F(ParserImplTest, TypeDecl_Array_BadSize) {
auto p = parser("array<f32, !>");
auto t = p->type_decl();
diff --git a/src/tint/reader/wgsl/parser_impl_type_decl_without_ident_test.cc b/src/tint/reader/wgsl/parser_impl_type_decl_without_ident_test.cc
new file mode 100644
index 0000000..ec23f2f
--- /dev/null
+++ b/src/tint/reader/wgsl/parser_impl_type_decl_without_ident_test.cc
@@ -0,0 +1,676 @@
+// 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 "src/tint/ast/alias.h"
+#include "src/tint/ast/array.h"
+#include "src/tint/ast/matrix.h"
+#include "src/tint/ast/sampler.h"
+#include "src/tint/reader/wgsl/parser_impl_test_helper.h"
+#include "src/tint/sem/sampled_texture.h"
+
+namespace tint::reader::wgsl {
+namespace {
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Invalid) {
+ auto p = parser("1234");
+ auto t = p->type_decl_without_ident();
+ EXPECT_EQ(t.errored, false);
+ EXPECT_EQ(t.matched, false);
+ EXPECT_EQ(t.value, nullptr);
+ EXPECT_FALSE(p->has_error()) << p->error();
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Identifier) {
+ auto p = parser("A");
+ auto t = p->type_decl_without_ident();
+ EXPECT_FALSE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_EQ(t.value, nullptr);
+ EXPECT_FALSE(p->has_error()) << p->error();
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Bool) {
+ auto p = parser("bool");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_NE(t.value, nullptr) << p->error();
+ ASSERT_TRUE(t.value->Is<ast::Bool>());
+ EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 5u}}));
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_F16) {
+ auto p = parser("f16");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_NE(t.value, nullptr) << p->error();
+ ASSERT_TRUE(t.value->Is<ast::F16>());
+ EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 4u}}));
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_F32) {
+ auto p = parser("f32");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_NE(t.value, nullptr) << p->error();
+ ASSERT_TRUE(t.value->Is<ast::F32>());
+ EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 4u}}));
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_I32) {
+ auto p = parser("i32");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_NE(t.value, nullptr) << p->error();
+ ASSERT_TRUE(t.value->Is<ast::I32>());
+ EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 4u}}));
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_U32) {
+ auto p = parser("u32");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_NE(t.value, nullptr) << p->error();
+ ASSERT_TRUE(t.value->Is<ast::U32>());
+ EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 4u}}));
+}
+
+struct VecData {
+ const char* input;
+ size_t count;
+ Source::Range range;
+};
+inline std::ostream& operator<<(std::ostream& out, VecData data) {
+ out << std::string(data.input);
+ return out;
+}
+
+class TypeDeclWithoutIdent_VecTest : public ParserImplTestWithParam<VecData> {};
+
+TEST_P(TypeDeclWithoutIdent_VecTest, Parse) {
+ auto params = GetParam();
+ auto p = parser(params.input);
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_NE(t.value, nullptr) << p->error();
+ ASSERT_FALSE(p->has_error());
+ EXPECT_TRUE(t.value->Is<ast::Vector>());
+ EXPECT_EQ(t.value->As<ast::Vector>()->width, params.count);
+ EXPECT_EQ(t.value->source.range, params.range);
+}
+INSTANTIATE_TEST_SUITE_P(ParserImplTest,
+ TypeDeclWithoutIdent_VecTest,
+ testing::Values(VecData{"vec2<f32>", 2, {{1u, 1u}, {1u, 10u}}},
+ VecData{"vec3<f32>", 3, {{1u, 1u}, {1u, 10u}}},
+ VecData{"vec4<f32>", 4, {{1u, 1u}, {1u, 10u}}}));
+
+class TypeDeclWithoutIdent_VecMissingGreaterThanTest : public ParserImplTestWithParam<VecData> {};
+
+TEST_P(TypeDeclWithoutIdent_VecMissingGreaterThanTest, Handles_Missing_GreaterThan) {
+ auto params = GetParam();
+ auto p = parser(params.input);
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.errored);
+ EXPECT_FALSE(t.matched);
+ ASSERT_EQ(t.value, nullptr);
+ ASSERT_TRUE(p->has_error());
+ ASSERT_EQ(p->error(), "1:9: expected '>' for vector");
+}
+INSTANTIATE_TEST_SUITE_P(ParserImplTest,
+ TypeDeclWithoutIdent_VecMissingGreaterThanTest,
+ testing::Values(VecData{"vec2<f32", 2, {}},
+ VecData{"vec3<f32", 3, {}},
+ VecData{"vec4<f32", 4, {}}));
+
+class TypeDeclWithoutIdent_VecMissingType : public ParserImplTestWithParam<VecData> {};
+
+TEST_P(TypeDeclWithoutIdent_VecMissingType, Handles_Missing_Type) {
+ auto params = GetParam();
+ auto p = parser(params.input);
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.errored);
+ EXPECT_FALSE(t.matched);
+ ASSERT_EQ(t.value, nullptr);
+ ASSERT_TRUE(p->has_error());
+ ASSERT_EQ(p->error(), "1:6: invalid type for vector");
+}
+INSTANTIATE_TEST_SUITE_P(ParserImplTest,
+ TypeDeclWithoutIdent_VecMissingType,
+ testing::Values(VecData{"vec2<>", 2, {}},
+ VecData{"vec3<>", 3, {}},
+ VecData{"vec4<>", 4, {}}));
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr) {
+ auto p = parser("ptr<function, f32>");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_NE(t.value, nullptr) << p->error();
+ ASSERT_FALSE(p->has_error());
+ ASSERT_TRUE(t.value->Is<ast::Pointer>());
+
+ auto* ptr = t.value->As<ast::Pointer>();
+ ASSERT_TRUE(ptr->type->Is<ast::F32>());
+ ASSERT_EQ(ptr->storage_class, ast::StorageClass::kFunction);
+ EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 19u}}));
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_WithAccess) {
+ auto p = parser("ptr<function, f32, read>");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_NE(t.value, nullptr) << p->error();
+ ASSERT_FALSE(p->has_error());
+ ASSERT_TRUE(t.value->Is<ast::Pointer>());
+
+ auto* ptr = t.value->As<ast::Pointer>();
+ ASSERT_TRUE(ptr->type->Is<ast::F32>());
+ ASSERT_EQ(ptr->storage_class, ast::StorageClass::kFunction);
+ ASSERT_EQ(ptr->access, ast::Access::kRead);
+ EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 25u}}));
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_ToVec) {
+ auto p = parser("ptr<function, vec2<f32>>");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_NE(t.value, nullptr) << p->error();
+ ASSERT_FALSE(p->has_error());
+ ASSERT_TRUE(t.value->Is<ast::Pointer>());
+
+ auto* ptr = t.value->As<ast::Pointer>();
+ ASSERT_TRUE(ptr->type->Is<ast::Vector>());
+ ASSERT_EQ(ptr->storage_class, ast::StorageClass::kFunction);
+
+ auto* vec = ptr->type->As<ast::Vector>();
+ ASSERT_EQ(vec->width, 2u);
+ ASSERT_TRUE(vec->type->Is<ast::F32>());
+ EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 25}}));
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_MissingLessThan) {
+ auto p = parser("ptr private, f32>");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.errored);
+ EXPECT_FALSE(t.matched);
+ ASSERT_EQ(t.value, nullptr);
+ ASSERT_TRUE(p->has_error());
+ ASSERT_EQ(p->error(), "1:5: expected '<' for ptr declaration");
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_MissingGreaterThanAfterType) {
+ auto p = parser("ptr<function, f32");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.errored);
+ EXPECT_FALSE(t.matched);
+ ASSERT_EQ(t.value, nullptr);
+ ASSERT_TRUE(p->has_error());
+ ASSERT_EQ(p->error(), "1:18: expected '>' for ptr declaration");
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_MissingGreaterThanAfterAccess) {
+ auto p = parser("ptr<function, f32, read");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.errored);
+ EXPECT_FALSE(t.matched);
+ ASSERT_EQ(t.value, nullptr);
+ ASSERT_TRUE(p->has_error());
+ ASSERT_EQ(p->error(), "1:24: expected '>' for ptr declaration");
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_MissingCommaAfterStorageClass) {
+ auto p = parser("ptr<function f32>");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.errored);
+ EXPECT_FALSE(t.matched);
+ ASSERT_EQ(t.value, nullptr);
+ ASSERT_TRUE(p->has_error());
+ ASSERT_EQ(p->error(), "1:14: expected ',' for ptr declaration");
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_MissingCommaAfterAccess) {
+ auto p = parser("ptr<function, f32 read>");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.errored);
+ EXPECT_FALSE(t.matched);
+ ASSERT_EQ(t.value, nullptr);
+ ASSERT_TRUE(p->has_error());
+ ASSERT_EQ(p->error(), "1:19: expected '>' for ptr declaration");
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_MissingStorageClass) {
+ auto p = parser("ptr<, f32>");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.errored);
+ EXPECT_FALSE(t.matched);
+ ASSERT_EQ(t.value, nullptr);
+ ASSERT_TRUE(p->has_error());
+ ASSERT_EQ(p->error(), "1:5: expected identifier for storage class");
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_MissingType) {
+ auto p = parser("ptr<function,>");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.errored);
+ EXPECT_FALSE(t.matched);
+ ASSERT_EQ(t.value, nullptr);
+ ASSERT_TRUE(p->has_error());
+ ASSERT_EQ(p->error(), "1:14: invalid type for ptr declaration");
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_MissingAccess) {
+ auto p = parser("ptr<function, i32, >");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.errored);
+ EXPECT_FALSE(t.matched);
+ ASSERT_EQ(t.value, nullptr);
+ ASSERT_TRUE(p->has_error());
+ ASSERT_EQ(p->error(), "1:20: expected identifier for access control");
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_MissingParams) {
+ auto p = parser("ptr<>");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.errored);
+ EXPECT_FALSE(t.matched);
+ ASSERT_EQ(t.value, nullptr);
+ ASSERT_TRUE(p->has_error());
+ ASSERT_EQ(p->error(), "1:5: expected identifier for storage class");
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_BadStorageClass) {
+ auto p = parser("ptr<unknown, f32>");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.errored);
+ EXPECT_FALSE(t.matched);
+ ASSERT_EQ(t.value, nullptr);
+ ASSERT_TRUE(p->has_error());
+ ASSERT_EQ(p->error(), "1:5: invalid storage class for ptr declaration");
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_BadAccess) {
+ auto p = parser("ptr<function, i32, unknown>");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.errored);
+ EXPECT_FALSE(t.matched);
+ ASSERT_EQ(t.value, nullptr);
+ ASSERT_TRUE(p->has_error());
+ ASSERT_EQ(p->error(), "1:20: invalid value for access control");
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Atomic) {
+ auto p = parser("atomic<f32>");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_NE(t.value, nullptr) << p->error();
+ ASSERT_FALSE(p->has_error());
+ ASSERT_TRUE(t.value->Is<ast::Atomic>());
+
+ auto* atomic = t.value->As<ast::Atomic>();
+ ASSERT_TRUE(atomic->type->Is<ast::F32>());
+ EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 12u}}));
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Atomic_ToVec) {
+ auto p = parser("atomic<vec2<f32>>");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_NE(t.value, nullptr) << p->error();
+ ASSERT_FALSE(p->has_error());
+ ASSERT_TRUE(t.value->Is<ast::Atomic>());
+
+ auto* atomic = t.value->As<ast::Atomic>();
+ ASSERT_TRUE(atomic->type->Is<ast::Vector>());
+
+ auto* vec = atomic->type->As<ast::Vector>();
+ ASSERT_EQ(vec->width, 2u);
+ ASSERT_TRUE(vec->type->Is<ast::F32>());
+ EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 18u}}));
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Atomic_MissingLessThan) {
+ auto p = parser("atomic f32>");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.errored);
+ EXPECT_FALSE(t.matched);
+ ASSERT_EQ(t.value, nullptr);
+ ASSERT_TRUE(p->has_error());
+ ASSERT_EQ(p->error(), "1:8: expected '<' for atomic declaration");
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Atomic_MissingGreaterThan) {
+ auto p = parser("atomic<f32");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.errored);
+ EXPECT_FALSE(t.matched);
+ ASSERT_EQ(t.value, nullptr);
+ ASSERT_TRUE(p->has_error());
+ ASSERT_EQ(p->error(), "1:11: expected '>' for atomic declaration");
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Atomic_MissingType) {
+ auto p = parser("atomic<>");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.errored);
+ EXPECT_FALSE(t.matched);
+ ASSERT_EQ(t.value, nullptr);
+ ASSERT_TRUE(p->has_error());
+ ASSERT_EQ(p->error(), "1:8: invalid type for atomic declaration");
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Array_AbstractIntLiteralSize) {
+ auto p = parser("array<f32, 5>");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_NE(t.value, nullptr) << p->error();
+ ASSERT_FALSE(p->has_error());
+ ASSERT_TRUE(t.value->Is<ast::Array>());
+
+ auto* a = t.value->As<ast::Array>();
+ ASSERT_FALSE(a->IsRuntimeArray());
+ ASSERT_TRUE(a->type->Is<ast::F32>());
+ EXPECT_EQ(a->attributes.Length(), 0u);
+ EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 14u}}));
+
+ auto* size = a->count->As<ast::IntLiteralExpression>();
+ ASSERT_NE(size, nullptr);
+ EXPECT_EQ(size->value, 5);
+ EXPECT_EQ(size->suffix, ast::IntLiteralExpression::Suffix::kNone);
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Array_SintLiteralSize) {
+ auto p = parser("array<f32, 5i>");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_NE(t.value, nullptr) << p->error();
+ ASSERT_FALSE(p->has_error());
+ ASSERT_TRUE(t.value->Is<ast::Array>());
+
+ auto* a = t.value->As<ast::Array>();
+ ASSERT_FALSE(a->IsRuntimeArray());
+ ASSERT_TRUE(a->type->Is<ast::F32>());
+ EXPECT_EQ(a->attributes.Length(), 0u);
+ EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 15u}}));
+
+ auto* size = a->count->As<ast::IntLiteralExpression>();
+ ASSERT_NE(size, nullptr);
+ EXPECT_EQ(size->value, 5);
+ EXPECT_EQ(size->suffix, ast::IntLiteralExpression::Suffix::kI);
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Array_UintLiteralSize) {
+ auto p = parser("array<f32, 5u>");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_NE(t.value, nullptr) << p->error();
+ ASSERT_FALSE(p->has_error());
+ ASSERT_TRUE(t.value->Is<ast::Array>());
+
+ auto* a = t.value->As<ast::Array>();
+ ASSERT_FALSE(a->IsRuntimeArray());
+ ASSERT_TRUE(a->type->Is<ast::F32>());
+ EXPECT_EQ(a->attributes.Length(), 0u);
+ EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 15u}}));
+
+ auto* size = a->count->As<ast::IntLiteralExpression>();
+ ASSERT_NE(size, nullptr);
+ EXPECT_EQ(size->suffix, ast::IntLiteralExpression::Suffix::kU);
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Array_ConstantSize) {
+ auto p = parser("array<f32, size>");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_NE(t.value, nullptr) << p->error();
+ ASSERT_FALSE(p->has_error());
+ ASSERT_TRUE(t.value->Is<ast::Array>());
+
+ auto* a = t.value->As<ast::Array>();
+ ASSERT_FALSE(a->IsRuntimeArray());
+ ASSERT_TRUE(a->type->Is<ast::F32>());
+ EXPECT_EQ(a->attributes.Length(), 0u);
+ EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 17u}}));
+
+ auto* count_expr = a->count->As<ast::IdentifierExpression>();
+ ASSERT_NE(count_expr, nullptr);
+ EXPECT_EQ(p->builder().Symbols().NameFor(count_expr->symbol), "size");
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Array_ExpressionSize) {
+ auto p = parser("array<f32, size + 2>");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_NE(t.value, nullptr) << p->error();
+ ASSERT_FALSE(p->has_error());
+ ASSERT_TRUE(t.value->Is<ast::Array>());
+
+ auto* a = t.value->As<ast::Array>();
+ ASSERT_FALSE(a->IsRuntimeArray());
+ ASSERT_TRUE(a->type->Is<ast::F32>());
+ EXPECT_EQ(a->attributes.Length(), 0u);
+
+ ASSERT_TRUE(a->count->Is<ast::BinaryExpression>());
+ auto* count_expr = a->count->As<ast::BinaryExpression>();
+ EXPECT_EQ(ast::BinaryOp::kAdd, count_expr->op);
+
+ ASSERT_TRUE(count_expr->lhs->Is<ast::IdentifierExpression>());
+ auto* ident = count_expr->lhs->As<ast::IdentifierExpression>();
+ EXPECT_EQ(p->builder().Symbols().NameFor(ident->symbol), "size");
+
+ ASSERT_TRUE(count_expr->rhs->Is<ast::IntLiteralExpression>());
+ auto* val = count_expr->rhs->As<ast::IntLiteralExpression>();
+ EXPECT_EQ(2, static_cast<int32_t>(val->value));
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Array_Runtime) {
+ auto p = parser("array<u32>");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_NE(t.value, nullptr) << p->error();
+ ASSERT_FALSE(p->has_error());
+ ASSERT_TRUE(t.value->Is<ast::Array>());
+
+ auto* a = t.value->As<ast::Array>();
+ ASSERT_TRUE(a->IsRuntimeArray());
+ ASSERT_TRUE(a->type->Is<ast::U32>());
+ EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 11u}}));
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Array_Runtime_Vec) {
+ auto p = parser("array<vec4<u32>>");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_NE(t.value, nullptr) << p->error();
+ ASSERT_FALSE(p->has_error());
+ ASSERT_TRUE(t.value->Is<ast::Array>());
+
+ auto* a = t.value->As<ast::Array>();
+ ASSERT_TRUE(a->IsRuntimeArray());
+ ASSERT_TRUE(a->type->Is<ast::Vector>());
+ EXPECT_EQ(a->type->As<ast::Vector>()->width, 4u);
+ EXPECT_TRUE(a->type->As<ast::Vector>()->type->Is<ast::U32>());
+ EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 17u}}));
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Array_BadSize) {
+ auto p = parser("array<f32, !>");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.errored);
+ EXPECT_FALSE(t.matched);
+ ASSERT_EQ(t.value, nullptr);
+ ASSERT_TRUE(p->has_error());
+ ASSERT_EQ(p->error(), "1:13: unable to parse right side of ! expression");
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Array_MissingSize) {
+ auto p = parser("array<f32,>");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.errored);
+ EXPECT_FALSE(t.matched);
+ ASSERT_EQ(t.value, nullptr);
+ ASSERT_TRUE(p->has_error());
+ ASSERT_EQ(p->error(), "1:11: expected array size expression");
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Array_MissingGreaterThan) {
+ auto p = parser("array<f32");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.errored);
+ EXPECT_FALSE(t.matched);
+ ASSERT_EQ(t.value, nullptr);
+ ASSERT_TRUE(p->has_error());
+ ASSERT_EQ(p->error(), "1:10: expected '>' for array declaration");
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Array_MissingComma) {
+ auto p = parser("array<f32 3>");
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.errored);
+ EXPECT_FALSE(t.matched);
+ ASSERT_EQ(t.value, nullptr);
+ ASSERT_TRUE(p->has_error());
+ ASSERT_EQ(p->error(), "1:11: expected '>' for array declaration");
+}
+
+struct MatrixData {
+ const char* input;
+ size_t columns;
+ size_t rows;
+ Source::Range range;
+};
+inline std::ostream& operator<<(std::ostream& out, MatrixData data) {
+ out << std::string(data.input);
+ return out;
+}
+
+class TypeDeclWithoutIdent_MatrixTest : public ParserImplTestWithParam<MatrixData> {};
+
+TEST_P(TypeDeclWithoutIdent_MatrixTest, Parse) {
+ auto params = GetParam();
+ auto p = parser(params.input);
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_NE(t.value, nullptr) << p->error();
+ ASSERT_FALSE(p->has_error());
+ EXPECT_TRUE(t.value->Is<ast::Matrix>());
+ auto* mat = t.value->As<ast::Matrix>();
+ EXPECT_EQ(mat->rows, params.rows);
+ EXPECT_EQ(mat->columns, params.columns);
+ EXPECT_EQ(t.value->source.range, params.range);
+}
+INSTANTIATE_TEST_SUITE_P(ParserImplTest,
+ TypeDeclWithoutIdent_MatrixTest,
+ testing::Values(MatrixData{"mat2x2<f32>", 2, 2, {{1u, 1u}, {1u, 12u}}},
+ MatrixData{"mat2x3<f32>", 2, 3, {{1u, 1u}, {1u, 12u}}},
+ MatrixData{"mat2x4<f32>", 2, 4, {{1u, 1u}, {1u, 12u}}},
+ MatrixData{"mat3x2<f32>", 3, 2, {{1u, 1u}, {1u, 12u}}},
+ MatrixData{"mat3x3<f32>", 3, 3, {{1u, 1u}, {1u, 12u}}},
+ MatrixData{"mat3x4<f32>", 3, 4, {{1u, 1u}, {1u, 12u}}},
+ MatrixData{"mat4x2<f32>", 4, 2, {{1u, 1u}, {1u, 12u}}},
+ MatrixData{"mat4x3<f32>", 4, 3, {{1u, 1u}, {1u, 12u}}},
+ MatrixData{"mat4x4<f32>", 4, 4, {{1u, 1u}, {1u, 12u}}}));
+
+class TypeDeclWithoutIdent_MatrixMissingGreaterThanTest
+ : public ParserImplTestWithParam<MatrixData> {};
+
+TEST_P(TypeDeclWithoutIdent_MatrixMissingGreaterThanTest, Handles_Missing_GreaterThan) {
+ auto params = GetParam();
+ auto p = parser(params.input);
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.errored);
+ EXPECT_FALSE(t.matched);
+ ASSERT_EQ(t.value, nullptr);
+ ASSERT_TRUE(p->has_error());
+ ASSERT_EQ(p->error(), "1:11: expected '>' for matrix");
+}
+INSTANTIATE_TEST_SUITE_P(ParserImplTest,
+ TypeDeclWithoutIdent_MatrixMissingGreaterThanTest,
+ testing::Values(MatrixData{"mat2x2<f32", 2, 2, {}},
+ MatrixData{"mat2x3<f32", 2, 3, {}},
+ MatrixData{"mat2x4<f32", 2, 4, {}},
+ MatrixData{"mat3x2<f32", 3, 2, {}},
+ MatrixData{"mat3x3<f32", 3, 3, {}},
+ MatrixData{"mat3x4<f32", 3, 4, {}},
+ MatrixData{"mat4x2<f32", 4, 2, {}},
+ MatrixData{"mat4x3<f32", 4, 3, {}},
+ MatrixData{"mat4x4<f32", 4, 4, {}}));
+
+class TypeDeclWithoutIdent_MatrixMissingType : public ParserImplTestWithParam<MatrixData> {};
+
+TEST_P(TypeDeclWithoutIdent_MatrixMissingType, Handles_Missing_Type) {
+ auto params = GetParam();
+ auto p = parser(params.input);
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.errored);
+ EXPECT_FALSE(t.matched);
+ ASSERT_EQ(t.value, nullptr);
+ ASSERT_TRUE(p->has_error());
+ ASSERT_EQ(p->error(), "1:8: invalid type for matrix");
+}
+INSTANTIATE_TEST_SUITE_P(ParserImplTest,
+ TypeDeclWithoutIdent_MatrixMissingType,
+ testing::Values(MatrixData{"mat2x2<>", 2, 2, {}},
+ MatrixData{"mat2x3<>", 2, 3, {}},
+ MatrixData{"mat2x4<>", 2, 4, {}},
+ MatrixData{"mat3x2<>", 3, 2, {}},
+ MatrixData{"mat3x3<>", 3, 3, {}},
+ MatrixData{"mat3x4<>", 3, 4, {}},
+ MatrixData{"mat4x2<>", 4, 2, {}},
+ MatrixData{"mat4x3<>", 4, 3, {}},
+ MatrixData{"mat4x4<>", 4, 4, {}}));
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Sampler) {
+ auto p = parser("sampler");
+
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_NE(t.value, nullptr) << p->error();
+ ASSERT_TRUE(t.value->Is<ast::Sampler>());
+ ASSERT_FALSE(t.value->As<ast::Sampler>()->IsComparison());
+ EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 8u}}));
+}
+
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Texture) {
+ auto p = parser("texture_cube<f32>");
+
+ auto t = p->type_decl_without_ident();
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_NE(t.value, nullptr);
+ ASSERT_TRUE(t.value->Is<ast::Texture>());
+ ASSERT_TRUE(t.value->Is<ast::SampledTexture>());
+ ASSERT_TRUE(t.value->As<ast::SampledTexture>()->type->Is<ast::F32>());
+ EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 18u}}));
+}
+
+} // namespace
+} // namespace tint::reader::wgsl
diff --git a/src/tint/reader/wgsl/parser_impl_variable_stmt_test.cc b/src/tint/reader/wgsl/parser_impl_variable_stmt_test.cc
index c370262..3e60acd 100644
--- a/src/tint/reader/wgsl/parser_impl_variable_stmt_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_variable_stmt_test.cc
@@ -153,6 +153,32 @@
ASSERT_EQ(e->source.range.end.column, 6u);
}
+TEST_F(ParserImplTest, VariableStmt_Let_ComplexExpression) {
+ auto p = parser("let x = collide + collide_1;");
+ // Parse as `statement` to validate the `;` at the end so we know we parsed the whole expression
+ auto e = p->statement();
+ EXPECT_TRUE(e.matched);
+ EXPECT_FALSE(e.errored);
+ EXPECT_FALSE(p->has_error()) << p->error();
+ ASSERT_NE(e.value, nullptr);
+ ASSERT_TRUE(e->Is<ast::VariableDeclStatement>());
+
+ auto* decl = e->As<ast::VariableDeclStatement>();
+ ASSERT_NE(decl->variable->constructor, nullptr);
+
+ ASSERT_TRUE(decl->variable->constructor->Is<ast::BinaryExpression>());
+ auto* expr = decl->variable->constructor->As<ast::BinaryExpression>();
+ EXPECT_EQ(expr->op, ast::BinaryOp::kAdd);
+
+ ASSERT_TRUE(expr->lhs->Is<ast::IdentifierExpression>());
+ auto* ident = expr->lhs->As<ast::IdentifierExpression>();
+ EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("collide"));
+
+ ASSERT_TRUE(expr->rhs->Is<ast::IdentifierExpression>());
+ ident = expr->rhs->As<ast::IdentifierExpression>();
+ EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("collide_1"));
+}
+
TEST_F(ParserImplTest, VariableStmt_Let_MissingEqual) {
auto p = parser("let a : i32 1");
auto e = p->variable_statement();
diff --git a/src/tint/reader/wgsl/parser_impl_vec_mat_prefix_test.cc b/src/tint/reader/wgsl/parser_impl_vec_mat_prefix_test.cc
new file mode 100644
index 0000000..d6204d7
--- /dev/null
+++ b/src/tint/reader/wgsl/parser_impl_vec_mat_prefix_test.cc
@@ -0,0 +1,103 @@
+// 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 "src/tint/reader/wgsl/parser_impl_test_helper.h"
+
+namespace tint::reader::wgsl {
+namespace {
+
+TEST_F(ParserImplTest, VecPrefix_Vec2) {
+ auto p = parser("vec2");
+ auto t = p->vec_prefix();
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_FALSE(p->has_error()) << p->error();
+
+ EXPECT_EQ(2u, t.value);
+}
+
+TEST_F(ParserImplTest, VecPrefix_Vec3) {
+ auto p = parser("vec3");
+ auto t = p->vec_prefix();
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_FALSE(p->has_error()) << p->error();
+
+ EXPECT_EQ(3u, t.value);
+}
+
+TEST_F(ParserImplTest, VecPrefix_Vec4) {
+ auto p = parser("vec4");
+ auto t = p->vec_prefix();
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_FALSE(p->has_error()) << p->error();
+
+ EXPECT_EQ(4u, t.value);
+}
+
+TEST_F(ParserImplTest, VecPrefix_NoMatch) {
+ auto p = parser("mat2x2");
+ auto t = p->vec_prefix();
+ EXPECT_FALSE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_FALSE(p->has_error()) << p->error();
+
+ EXPECT_EQ(0u, t.value);
+}
+
+struct MatData {
+ std::string name;
+ uint32_t columns;
+ uint32_t rows;
+};
+class MatPrefixTest : public ParserImplTestWithParam<MatData> {};
+TEST_P(MatPrefixTest, Parse) {
+ auto params = GetParam();
+
+ auto p = parser(params.name);
+ auto t = p->mat_prefix();
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_FALSE(p->has_error()) << p->error();
+
+ auto dims = t.value;
+ EXPECT_EQ(params.columns, dims.columns);
+ EXPECT_EQ(params.rows, dims.rows);
+}
+INSTANTIATE_TEST_SUITE_P(ParserImplTest,
+ MatPrefixTest,
+ testing::Values(MatData{"mat2x2", 2, 2},
+ MatData{"mat2x3", 2, 3},
+ MatData{"mat2x4", 2, 4},
+ MatData{"mat3x2", 3, 2},
+ MatData{"mat3x3", 3, 3},
+ MatData{"mat3x4", 3, 4},
+ MatData{"mat4x2", 4, 2},
+ MatData{"mat4x3", 4, 3},
+ MatData{"mat4x4", 4, 4}));
+
+TEST_F(ParserImplTest, MatPrefix_NoMatch) {
+ auto p = parser("vec2");
+ auto t = p->mat_prefix();
+ EXPECT_FALSE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_FALSE(p->has_error()) << p->error();
+
+ EXPECT_EQ(0u, t.value.columns);
+ EXPECT_EQ(0u, t.value.rows);
+}
+
+} // namespace
+} // namespace tint::reader::wgsl
diff --git a/src/tint/transform/promote_side_effects_to_decl_test.cc b/src/tint/transform/promote_side_effects_to_decl_test.cc
index 937b59e..2cd7401 100644
--- a/src/tint/transform/promote_side_effects_to_decl_test.cc
+++ b/src/tint/transform/promote_side_effects_to_decl_test.cc
@@ -1639,7 +1639,7 @@
var b = true;
var c = true;
var d = true;
- let r = b && a(0) || c && a(1) && c && d || a(2);
+ let r = (b && a(0)) || (c && a(1) && c && d) || a(2);
}
)";
@@ -1869,7 +1869,7 @@
fn f() {
var b = true;
- let r = bool(a(0)) && bool(a(1) && b) || bool(a(2) && a(3));
+ let r = (bool(a(0)) && bool(a(1) && b)) || bool(a(2) && a(3));
}
)";
@@ -1916,7 +1916,7 @@
fn f() {
var b = true;
- let r = bool(a(0)) && bool(a(1)) || b;
+ let r = (bool(a(0)) && bool(a(1))) || b;
}
)";
@@ -1994,7 +1994,7 @@
fn f() {
var b = true;
- let r = all(a(0)) && all(a(1) && b) || all(a(2) && a(3));
+ let r = (all(a(0)) && all(a(1) && b)) || all(a(2) && a(3));
}
)";