Add `mat_prefix`, `vec_prefix`, `callable` and `type_decl_without_ident`

This CL adds the `mat_prefix`, `vec_prefix`, `callable` and
`type_decl_without_ident` constructuctions.

Bug: tint:1633
Change-Id: Id1fb0417f522b7da2ac6bd4441d17b0a35ae5f14
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/99880
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
dan sinclair 2022-08-22 16:15:04 +00:00 committed by Dawn LUCI CQ
parent a34b10db38
commit 393de082d3
7 changed files with 1251 additions and 1 deletions

View File

@ -1357,6 +1357,7 @@ if (tint_build_unittests) {
"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",
@ -1418,6 +1419,7 @@ if (tint_build_unittests) {
"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",
@ -1425,6 +1427,7 @@ if (tint_build_unittests) {
"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",

View File

@ -952,6 +952,7 @@ if(TINT_BUILD_TESTS)
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
@ -1013,6 +1014,7 @@ if(TINT_BUILD_TESTS)
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
@ -1020,6 +1022,7 @@ if(TINT_BUILD_TESTS)
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
)

View File

@ -1059,6 +1059,132 @@ Maybe<const ast::Alias*> ParserImpl::type_alias_decl() {
return builder_.ty.alias(make_source_range_from(t.source()), name.value, type.value);
}
// vec_prefix
// : 'vec2'
// | 'vec3'
// | 'vec4'
Maybe<uint32_t> ParserImpl::vec_prefix() {
auto& t = peek();
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::kF16)) {
return builder_.ty.f16(t.source());
}
if (match(Token::Type::kF32)) {
return builder_.ty.f32(t.source());
}
if (match(Token::Type::kI32)) {
return builder_.ty.i32(t.source());
}
if (match(Token::Type::kU32)) {
return builder_.ty.u32(t.source());
}
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.source());
}
if (match(Token::Type::kPtr)) {
return expect_type_decl_pointer(t.source());
}
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();
if (texture_or_sampler.errored) {
return Failure::kErrored;
}
if (texture_or_sampler.matched) {
return texture_or_sampler;
}
return Failure::kNoMatch;
}
// type_decl
// : IDENTIFIER
// | BOOL
@ -1152,6 +1278,47 @@ Expect<const ast::Type*> ParserImpl::expect_type(std::string_view use) {
return type.value;
}
// 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;
auto access = ast::Access::kUndefined;
auto subtype = expect_lt_gt_block(use, [&]() -> Expect<const ast::Type*> {
auto sc = expect_address_space(use);
if (sc.errored) {
return Failure::kErrored;
}
storage_class = sc.value;
if (!expect(use, Token::Type::kComma)) {
return Failure::kErrored;
}
auto type = expect_type(use);
if (type.errored) {
return Failure::kErrored;
}
if (match(Token::Type::kComma)) {
auto ac = expect_access_mode("access control");
if (ac.errored) {
return Failure::kErrored;
}
access = ac.value;
}
return type.value;
});
if (subtype.errored) {
return Failure::kErrored;
}
return builder_.ty.pointer(make_source_range_from(s), subtype.value, storage_class, access);
}
Expect<const ast::Type*> ParserImpl::expect_type_decl_pointer(const Token& t) {
const char* use = "ptr declaration";
@ -1193,6 +1360,18 @@ Expect<const ast::Type*> ParserImpl::expect_type_decl_pointer(const Token& t) {
access);
}
// 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); });
if (subtype.errored) {
return Failure::kErrored;
}
return builder_.ty.atomic(make_source_range_from(s), subtype.value);
}
Expect<const ast::Type*> ParserImpl::expect_type_decl_atomic(const Token& t) {
const char* use = "atomic declaration";
@ -1204,6 +1383,17 @@ Expect<const ast::Type*> ParserImpl::expect_type_decl_atomic(const Token& t) {
return builder_.ty.atomic(make_source_range_from(t.source()), subtype.value);
}
// 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;
}
return builder_.ty.vec(make_source_range_from(s), ty.value, count);
}
Expect<const ast::Type*> ParserImpl::expect_type_decl_vector(const Token& t) {
uint32_t count = 2;
if (t.Is(Token::Type::kVec3)) {
@ -1225,6 +1415,47 @@ Expect<const ast::Type*> ParserImpl::expect_type_decl_vector(const Token& t) {
return builder_.ty.vec(make_source_range_from(t.source()), subtype, count);
}
// 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 {
const ast::Type* type = nullptr;
const ast::Expression* size = nullptr;
};
if (!peek_is(Token::Type::kLessThan)) {
return add_error(peek(), "expected < for array");
}
auto type_size = expect_lt_gt_block(use, [&]() -> Expect<TypeAndSize> {
auto type = expect_type(use);
if (type.errored) {
return Failure::kErrored;
}
if (!match(Token::Type::kComma)) {
return TypeAndSize{type.value, nullptr};
}
auto size = element_count_expression();
if (size.errored) {
return Failure::kErrored;
}
if (!size.matched) {
return add_error(peek(), "expected array size expression");
}
return TypeAndSize{type.value, size.value};
});
if (type_size.errored) {
return Failure::kErrored;
}
return builder_.ty.array(make_source_range_from(s), type_size->type, type_size->size);
}
Expect<const ast::Type*> ParserImpl::expect_type_decl_array(const Token& t) {
const char* use = "array declaration";
@ -1265,6 +1496,18 @@ Expect<const ast::Type*> ParserImpl::expect_type_decl_array(const Token& t) {
return builder_.ty.array(make_source_range_from(t.source()), type_size->type, type_size->size);
}
// 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;
}
return builder_.ty.mat(make_source_range_from(s), ty.value, dims.columns, dims.rows);
}
Expect<const ast::Type*> ParserImpl::expect_type_decl_matrix(const Token& t) {
uint32_t rows = 2;
uint32_t columns = 2;
@ -2438,6 +2681,47 @@ Maybe<const ast::BlockStatement*> ParserImpl::continuing_statement() {
return continuing_compound_statement();
}
// 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();
// 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 (ty.matched) {
return ty.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
// : IDENT argument_expression_list?
// | type_decl argument_expression_list

View File

@ -309,6 +309,14 @@ class ParserImpl {
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 @@ class ParserImpl {
/// 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();
@ -919,12 +939,17 @@ class ParserImpl {
/// 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 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_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(std::string_view use);
Maybe<const ast::Statement*> non_block_statement();

View File

@ -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

View File

@ -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

View File

@ -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