Add `element_count_expression` to WGSL parser

This CL adds the `element_count_expression` and the requisite math
expression parsing to support along with tests.

Bug: tint:1633

Change-Id: I54ab37339754217f417f69dcd6140adbc14cbf83
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/99560
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
dan sinclair 2022-08-19 21:26:01 +00:00 committed by Dawn LUCI CQ
parent 5d7de871b4
commit 2788becd09
12 changed files with 814 additions and 19 deletions

View File

@ -1364,6 +1364,7 @@ if (tint_build_unittests) {
"reader/wgsl/parser_impl_continuing_stmt_test.cc",
"reader/wgsl/parser_impl_core_lhs_expression_test.cc",
"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_equality_expression_test.cc",
"reader/wgsl/parser_impl_error_msg_test.cc",
@ -1385,6 +1386,7 @@ if (tint_build_unittests) {
"reader/wgsl/parser_impl_logical_and_expression_test.cc",
"reader/wgsl/parser_impl_logical_or_expression_test.cc",
"reader/wgsl/parser_impl_loop_stmt_test.cc",
"reader/wgsl/parser_impl_math_expression_test.cc",
"reader/wgsl/parser_impl_multiplicative_expression_test.cc",
"reader/wgsl/parser_impl_param_list_test.cc",
"reader/wgsl/parser_impl_paren_expression_test.cc",

View File

@ -959,6 +959,7 @@ if(TINT_BUILD_TESTS)
reader/wgsl/parser_impl_continuing_stmt_test.cc
reader/wgsl/parser_impl_core_lhs_expression_test.cc
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
@ -980,6 +981,7 @@ if(TINT_BUILD_TESTS)
reader/wgsl/parser_impl_logical_and_expression_test.cc
reader/wgsl/parser_impl_logical_or_expression_test.cc
reader/wgsl/parser_impl_loop_stmt_test.cc
reader/wgsl/parser_impl_math_expression_test.cc
reader/wgsl/parser_impl_multiplicative_expression_test.cc
reader/wgsl/parser_impl_param_list_test.cc
reader/wgsl/parser_impl_paren_expression_test.cc

View File

@ -1028,7 +1028,6 @@ INSTANTIATE_TEST_SUITE_P(LexerTest,
TokenData{"%", Token::Type::kMod},
TokenData{"!=", Token::Type::kNotEqual},
TokenData{"-", Token::Type::kMinus},
TokenData{"--", Token::Type::kMinusMinus},
TokenData{".", Token::Type::kPeriod},
TokenData{"+", Token::Type::kPlus},
TokenData{"++", Token::Type::kPlusPlus},
@ -1088,6 +1087,7 @@ INSTANTIATE_TEST_SUITE_P(LexerTest,
SplittablePunctuationTest,
testing::Values(TokenData{"&&", Token::Type::kAndAnd},
TokenData{">=", Token::Type::kGreaterThanEqual},
TokenData{"--", Token::Type::kMinusMinus},
TokenData{">>", Token::Type::kShiftRight}));
using KeywordTest = testing::TestWithParam<TokenData>;

View File

@ -1066,7 +1066,7 @@ Maybe<const ast::Alias*> ParserImpl::type_alias_decl() {
// | 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 INT_LITERAL 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
@ -1244,10 +1244,11 @@ Expect<const ast::Type*> ParserImpl::expect_type_decl_array(const Token& t) {
return TypeAndSize{type.value, nullptr};
}
auto size = primary_expression();
auto size = element_count_expression();
if (size.errored) {
return Failure::kErrored;
} else if (!size.matched) {
}
if (!size.matched) {
return add_error(peek(), "expected array size expression");
}
@ -2657,6 +2658,162 @@ Maybe<const ast::Expression*> ParserImpl::bitwise_expression_post_unary_expressi
return Failure::kErrored;
}
// multiplicative_operator
// : FORWARD_SLASH
// | MODULO
// | STAR
Maybe<ast::BinaryOp> ParserImpl::multiplicative_operator() {
if (match(Token::Type::kForwardSlash)) {
return ast::BinaryOp::kDivide;
}
if (match(Token::Type::kMod)) {
return ast::BinaryOp::kModulo;
}
if (match(Token::Type::kStar)) {
return ast::BinaryOp::kMultiply;
}
return Failure::kNoMatch;
}
// multiplicative_expression.post.unary_expression
// : (multiplicative_operator unary_expression)*
Expect<const ast::Expression*> ParserImpl::expect_multiplicative_expression_post_unary_expression(
const ast::Expression* lhs) {
while (continue_parsing()) {
auto& t = peek();
auto op = multiplicative_operator();
if (op.errored) {
return Failure::kErrored;
}
if (!op.matched) {
return lhs;
}
auto rhs = unary_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");
}
lhs = create<ast::BinaryExpression>(t.source(), op.value, lhs, rhs.value);
}
return Failure::kErrored;
}
// additive_operator
// : MINUS
// | PLUS
//
// Note, this also splits a `--` token. This is currently safe as the only way to get into
// here is through additive expression and rules for where `--` are allowed are very restrictive.
Maybe<ast::BinaryOp> ParserImpl::additive_operator() {
if (match(Token::Type::kPlus)) {
return ast::BinaryOp::kAdd;
}
auto& t = peek();
if (t.Is(Token::Type::kMinusMinus)) {
next();
split_token(Token::Type::kMinus, Token::Type::kMinus);
} else if (t.Is(Token::Type::kMinus)) {
next();
} else {
return Failure::kNoMatch;
}
return ast::BinaryOp::kSubtract;
}
// additive_expression.pos.unary_expression
// : (additive_operator unary_expression expect_multiplicative_expression.post.unary_expression)*
//
// This is `( additive_operator unary_expression ( multiplicative_operator unary_expression )* )*`
// split apart.
Expect<const ast::Expression*> ParserImpl::expect_additive_expression_post_unary_expression(
const ast::Expression* lhs) {
while (continue_parsing()) {
auto& t = peek();
auto op = additive_operator();
if (op.errored) {
return Failure::kErrored;
}
if (!op.matched) {
return lhs;
}
auto unary = unary_expression();
if (unary.errored) {
return Failure::kErrored;
}
if (!unary.matched) {
return add_error(peek(), std::string("unable to parse right side of ") +
std::string(t.to_name()) + " expression");
}
// The multiplicative binds tigher, so pass the unary into that and build that expression
// before creating the additve expression.
auto rhs = expect_multiplicative_expression_post_unary_expression(unary.value);
if (rhs.errored) {
return Failure::kErrored;
}
lhs = create<ast::BinaryExpression>(t.source(), op.value, lhs, rhs.value);
}
return Failure::kErrored;
}
// math_expression.post.unary_expression
// : multiplicative_expression.post.unary_expression additive_expression.post.unary_expression
//
// This is `( multiplicative_operator unary_expression )* ( additive_operator unary_expression (
// multiplicative_operator unary_expression )* )*` split apart.
Expect<const ast::Expression*> ParserImpl::expect_math_expression_post_unary_expression(
const ast::Expression* lhs) {
auto rhs = expect_multiplicative_expression_post_unary_expression(lhs);
if (rhs.errored) {
return Failure::kErrored;
}
return expect_additive_expression_post_unary_expression(rhs.value);
}
// element_count_expression
// : unary_expression math_expression.post.unary_expression
// | unary_expression bitwise_expression.post.unary_expression
//
// Note, this moves the `( multiplicative_operator unary_expression )* ( additive_operator
// unary_expression ( multiplicative_operator unary_expression )* )*` expression for the first
// branch out into helper methods.
Maybe<const ast::Expression*> ParserImpl::element_count_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 math = expect_math_expression_post_unary_expression(lhs.value);
if (math.errored) {
return Failure::kErrored;
}
return math.value;
}
// unary_expression
// : singular_expression
// | MINUS unary_expression

View File

@ -646,6 +646,30 @@ class ParserImpl {
/// @returns the parsed expression or nullptr
Maybe<const ast::Expression*> bitwise_expression_post_unary_expression(
const ast::Expression* lhs);
/// Parse the `multiplicative_operator` grammar element
/// @returns the parsed operator if successful
Maybe<ast::BinaryOp> multiplicative_operator();
/// Parses multiplicative elements
/// @param lhs the left side of the expression
/// @returns the parsed expression or `lhs` if no match
Expect<const ast::Expression*> expect_multiplicative_expression_post_unary_expression(
const ast::Expression* lhs);
/// Parses additive elements
/// @param lhs the left side of the expression
/// @returns the parsed expression or `lhs` if no match
Expect<const ast::Expression*> expect_additive_expression_post_unary_expression(
const ast::Expression* lhs);
/// Parses math elements
/// @param lhs the left side of the expression
/// @returns the parsed expression or `lhs` if no match
Expect<const ast::Expression*> expect_math_expression_post_unary_expression(
const ast::Expression* lhs);
/// Parses an `element_count_expression` grammar element
/// @returns the parsed expression or nullptr
Maybe<const ast::Expression*> element_count_expression();
/// Parse the `additive_operator` grammar element
/// @returns the parsed operator if successful
Maybe<ast::BinaryOp> additive_operator();
/// Parses the recursive part of the `and_expression`, erroring on parse
/// failure.
/// @param lhs the left side of the expression

View File

@ -17,7 +17,7 @@
namespace tint::reader::wgsl {
namespace {
TEST_F(ParserImplTest, AdditiveExpression_Parses_Plus) {
TEST_F(ParserImplTest, AdditiveExpression_Orig_Parses_Plus) {
auto p = parser("a + true");
auto e = p->additive_expression();
EXPECT_TRUE(e.matched);
@ -42,7 +42,7 @@ TEST_F(ParserImplTest, AdditiveExpression_Parses_Plus) {
ASSERT_TRUE(rel->rhs->As<ast::BoolLiteralExpression>()->value);
}
TEST_F(ParserImplTest, AdditiveExpression_Parses_Minus) {
TEST_F(ParserImplTest, AdditiveExpression_Orig_Parses_Minus) {
auto p = parser("a - true");
auto e = p->additive_expression();
EXPECT_TRUE(e.matched);
@ -62,7 +62,7 @@ TEST_F(ParserImplTest, AdditiveExpression_Parses_Minus) {
ASSERT_TRUE(rel->rhs->As<ast::BoolLiteralExpression>()->value);
}
TEST_F(ParserImplTest, AdditiveExpression_InvalidLHS) {
TEST_F(ParserImplTest, AdditiveExpression_Orig_InvalidLHS) {
auto p = parser("if (a) {} + true");
auto e = p->additive_expression();
EXPECT_FALSE(e.matched);
@ -71,7 +71,7 @@ TEST_F(ParserImplTest, AdditiveExpression_InvalidLHS) {
EXPECT_EQ(e.value, nullptr);
}
TEST_F(ParserImplTest, AdditiveExpression_InvalidRHS) {
TEST_F(ParserImplTest, AdditiveExpression_Orig_InvalidRHS) {
auto p = parser("true + if (a) {}");
auto e = p->additive_expression();
EXPECT_FALSE(e.matched);
@ -81,7 +81,7 @@ TEST_F(ParserImplTest, AdditiveExpression_InvalidRHS) {
EXPECT_EQ(p->error(), "1:8: unable to parse right side of + expression");
}
TEST_F(ParserImplTest, AdditiveExpression_NoOr_ReturnsLHS) {
TEST_F(ParserImplTest, AdditiveExpression_Orig_NoOr_ReturnsLHS) {
auto p = parser("a true");
auto e = p->additive_expression();
EXPECT_TRUE(e.matched);
@ -91,5 +91,189 @@ TEST_F(ParserImplTest, AdditiveExpression_NoOr_ReturnsLHS) {
ASSERT_TRUE(e->Is<ast::IdentifierExpression>());
}
TEST_F(ParserImplTest, AdditiveExpression_Parses_Plus) {
auto p = parser("a + b");
auto lhs = p->unary_expression();
auto e = p->expect_additive_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, 4u);
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, AdditiveExpression_Parses_Minus) {
auto p = parser("a - b");
auto lhs = p->unary_expression();
auto e = p->expect_additive_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::kSubtract, 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, AdditiveExpression_Parses_MinusMinus) {
auto p = parser("a--b");
auto lhs = p->unary_expression();
auto e = p->expect_additive_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::kSubtract, 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::UnaryOpExpression>());
auto* unary = rel->rhs->As<ast::UnaryOpExpression>();
EXPECT_EQ(ast::UnaryOp::kNegation, unary->op);
ASSERT_TRUE(unary->expr->Is<ast::IdentifierExpression>());
ident = unary->expr->As<ast::IdentifierExpression>();
EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("b"));
}
TEST_F(ParserImplTest, AdditiveExpression_Parses_MultipleOps) {
auto p = parser("a - b + c - d");
auto lhs = p->unary_expression();
auto e = p->expect_additive_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>());
// lhs: ((a - b) + c
// op: -
// rhs: d
auto* rel = e->As<ast::BinaryExpression>();
EXPECT_EQ(ast::BinaryOp::kSubtract, rel->op);
ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
auto* ident = rel->rhs->As<ast::IdentifierExpression>();
EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("d"));
ASSERT_TRUE(rel->lhs->Is<ast::BinaryExpression>());
// lhs: a - b
// op: +
// rhs: c
rel = rel->lhs->As<ast::BinaryExpression>();
EXPECT_EQ(ast::BinaryOp::kAdd, rel->op);
ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
ident = rel->rhs->As<ast::IdentifierExpression>();
EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("c"));
ASSERT_TRUE(rel->lhs->Is<ast::BinaryExpression>());
// lhs: a
// op: -
// rhs: b
rel = rel->lhs->As<ast::BinaryExpression>();
EXPECT_EQ(ast::BinaryOp::kSubtract, 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::IdentifierExpression>());
ident = rel->rhs->As<ast::IdentifierExpression>();
EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("b"));
}
TEST_F(ParserImplTest, AdditiveExpression_Parses_MultipleOps_MixedMultiplication) {
auto p = parser("a - b * c - d");
auto lhs = p->unary_expression();
auto e = p->expect_additive_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>());
// lhs: a - (b * c)
// op: -
// rhs: d
auto* rel = e->As<ast::BinaryExpression>();
EXPECT_EQ(ast::BinaryOp::kSubtract, rel->op);
ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
auto* ident = rel->rhs->As<ast::IdentifierExpression>();
EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("d"));
ASSERT_TRUE(rel->lhs->Is<ast::BinaryExpression>());
// lhs: a
// op: -
// rhs: b * c
rel = rel->lhs->As<ast::BinaryExpression>();
EXPECT_EQ(ast::BinaryOp::kSubtract, 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::BinaryExpression>());
// lhs: b
// op: *
// rhs: c
rel = rel->rhs->As<ast::BinaryExpression>();
EXPECT_EQ(ast::BinaryOp::kMultiply, rel->op);
ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
ident = rel->lhs->As<ast::IdentifierExpression>();
EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("b"));
ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
ident = rel->rhs->As<ast::IdentifierExpression>();
EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("c"));
}
TEST_F(ParserImplTest, AdditiveExpression_InvalidRHS) {
auto p = parser("a + if (a) {}");
auto lhs = p->unary_expression();
auto e = p->expect_additive_expression_post_unary_expression(lhs.value);
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, AdditiveExpression_NoMatch_ReturnsLHS) {
auto p = parser("a true");
auto lhs = p->unary_expression();
auto e = p->expect_additive_expression_post_unary_expression(lhs.value);
EXPECT_FALSE(e.errored);
EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_NE(e.value, nullptr);
EXPECT_EQ(lhs.value, e.value);
}
} // namespace
} // namespace tint::reader::wgsl

View File

@ -0,0 +1,81 @@
// 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, ElementCountExpression_Math) {
auto p = parser("a * b");
auto e = p->element_count_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::kMultiply, 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, ElementCountExpression_Bitwise) {
auto p = parser("a | true");
auto e = p->element_count_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::kOr, 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, ElementCountExpression_NoMatch) {
auto p = parser("if (a) { }");
auto e = p->element_count_expression();
EXPECT_FALSE(e.matched);
EXPECT_FALSE(e.errored);
EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_EQ(e.value, nullptr);
}
TEST_F(ParserImplTest, ElementCountExpression_InvalidRHS) {
auto p = parser("a * if");
auto e = p->element_count_expression();
EXPECT_FALSE(e.matched);
EXPECT_TRUE(e.errored);
EXPECT_TRUE(p->has_error());
ASSERT_EQ(e.value, nullptr);
EXPECT_EQ("1:5: unable to parse right side of * expression", p->error());
}
} // namespace
} // namespace tint::reader::wgsl

View File

@ -937,9 +937,9 @@ var i : array<u32, >;
TEST_F(ParserImplErrorTest, GlobalDeclVarArrayInvalidSize) {
EXPECT("var i : array<u32, !>;",
R"(test.wgsl:1:20 error: expected array size expression
R"(test.wgsl:1:21 error: unable to parse right side of ! expression
var i : array<u32, !>;
^
^
)");
}

View File

@ -0,0 +1,153 @@
// 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, MathExpression_Parses_Multiplicative) {
auto p = parser("a * b");
auto lhs = p->unary_expression();
auto e = p->expect_math_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::kMultiply, 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, MathExpression_Parses_Mixed_MultiplicativeStart) {
auto p = parser("a * b + c");
auto lhs = p->unary_expression();
auto e = p->expect_math_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>());
// lhs: a * b
// op: +
// rhs: c
auto* rel = e->As<ast::BinaryExpression>();
EXPECT_EQ(ast::BinaryOp::kAdd, rel->op);
ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
auto* ident = rel->rhs->As<ast::IdentifierExpression>();
EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("c"));
ASSERT_TRUE(rel->lhs->Is<ast::BinaryExpression>());
// lhs: a
// op: *
// rhs: b
rel = rel->lhs->As<ast::BinaryExpression>();
EXPECT_EQ(ast::BinaryOp::kMultiply, 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::IdentifierExpression>());
ident = rel->rhs->As<ast::IdentifierExpression>();
EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("b"));
}
TEST_F(ParserImplTest, MathExpression_Parses_Additive) {
auto p = parser("a + b");
auto lhs = p->unary_expression();
auto e = p->expect_math_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, MathExpression_Parses_Mixed_AdditiveStart) {
auto p = parser("a + b * c");
auto lhs = p->unary_expression();
auto e = p->expect_math_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>());
// lhs: a
// op: +
// rhs: b * c
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::BinaryExpression>());
// lhs: b
// op: *
// rhs: c
rel = rel->rhs->As<ast::BinaryExpression>();
EXPECT_EQ(ast::BinaryOp::kMultiply, rel->op);
ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
ident = rel->lhs->As<ast::IdentifierExpression>();
EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("b"));
ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
ident = rel->rhs->As<ast::IdentifierExpression>();
EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("c"));
}
TEST_F(ParserImplTest, MathExpression_NoMatch_ReturnLHS) {
auto p = parser("a if");
auto lhs = p->unary_expression();
auto e = p->expect_math_expression_post_unary_expression(lhs.value);
EXPECT_FALSE(e.errored);
EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_NE(e.value, nullptr);
EXPECT_EQ(lhs.value, e.value);
}
TEST_F(ParserImplTest, MathExpression_InvalidRHS) {
auto p = parser("a * if");
auto lhs = p->unary_expression();
auto e = p->expect_math_expression_post_unary_expression(lhs.value);
EXPECT_TRUE(e.errored);
EXPECT_TRUE(p->has_error());
ASSERT_EQ(e.value, nullptr);
EXPECT_EQ("1:5: unable to parse right side of * expression", p->error());
}
} // namespace
} // namespace tint::reader::wgsl

View File

@ -17,7 +17,7 @@
namespace tint::reader::wgsl {
namespace {
TEST_F(ParserImplTest, MultiplicativeExpression_Parses_Multiply) {
TEST_F(ParserImplTest, MultiplicativeExpression_Orig_Parses_Multiply) {
auto p = parser("a * true");
auto e = p->multiplicative_expression();
EXPECT_TRUE(e.matched);
@ -42,7 +42,7 @@ TEST_F(ParserImplTest, MultiplicativeExpression_Parses_Multiply) {
ASSERT_TRUE(rel->rhs->As<ast::BoolLiteralExpression>()->value);
}
TEST_F(ParserImplTest, MultiplicativeExpression_Parses_Divide) {
TEST_F(ParserImplTest, MultiplicativeExpression_Orig_Parses_Divide) {
auto p = parser("a / true");
auto e = p->multiplicative_expression();
EXPECT_TRUE(e.matched);
@ -62,7 +62,7 @@ TEST_F(ParserImplTest, MultiplicativeExpression_Parses_Divide) {
ASSERT_TRUE(rel->rhs->As<ast::BoolLiteralExpression>()->value);
}
TEST_F(ParserImplTest, MultiplicativeExpression_Parses_Modulo) {
TEST_F(ParserImplTest, MultiplicativeExpression_Orig_Parses_Modulo) {
auto p = parser("a % true");
auto e = p->multiplicative_expression();
EXPECT_TRUE(e.matched);
@ -82,7 +82,7 @@ TEST_F(ParserImplTest, MultiplicativeExpression_Parses_Modulo) {
ASSERT_TRUE(rel->rhs->As<ast::BoolLiteralExpression>()->value);
}
TEST_F(ParserImplTest, MultiplicativeExpression_InvalidLHS) {
TEST_F(ParserImplTest, MultiplicativeExpression_Orig_InvalidLHS) {
auto p = parser("if (a) {} * true");
auto e = p->multiplicative_expression();
EXPECT_FALSE(e.matched);
@ -91,7 +91,7 @@ TEST_F(ParserImplTest, MultiplicativeExpression_InvalidLHS) {
EXPECT_EQ(e.value, nullptr);
}
TEST_F(ParserImplTest, MultiplicativeExpression_InvalidRHS) {
TEST_F(ParserImplTest, MultiplicativeExpression_Orig_InvalidRHS) {
auto p = parser("true * if (a) {}");
auto e = p->multiplicative_expression();
EXPECT_FALSE(e.matched);
@ -101,7 +101,7 @@ TEST_F(ParserImplTest, MultiplicativeExpression_InvalidRHS) {
EXPECT_EQ(p->error(), "1:8: unable to parse right side of * expression");
}
TEST_F(ParserImplTest, MultiplicativeExpression_NoOr_ReturnsLHS) {
TEST_F(ParserImplTest, MultiplicativeExpression_Orig_NoOr_ReturnsLHS) {
auto p = parser("a true");
auto e = p->multiplicative_expression();
EXPECT_TRUE(e.matched);
@ -111,5 +111,170 @@ TEST_F(ParserImplTest, MultiplicativeExpression_NoOr_ReturnsLHS) {
ASSERT_TRUE(e->Is<ast::IdentifierExpression>());
}
TEST_F(ParserImplTest, MultiplicativeExpression_Parses_Multiply) {
auto p = parser("a * b");
auto lhs = p->unary_expression();
auto e = p->expect_multiplicative_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::kMultiply, 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, MultiplicativeExpression_Parses_Multiply_UnaryIndirect) {
auto p = parser("a **b");
auto lhs = p->unary_expression();
auto e = p->expect_multiplicative_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::kMultiply, 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::UnaryOpExpression>());
auto* unary = rel->rhs->As<ast::UnaryOpExpression>();
EXPECT_EQ(ast::UnaryOp::kIndirection, unary->op);
ASSERT_TRUE(unary->expr->Is<ast::IdentifierExpression>());
ident = unary->expr->As<ast::IdentifierExpression>();
EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("b"));
}
TEST_F(ParserImplTest, MultiplicativeExpression_Parses_Divide) {
auto p = parser("a / b");
auto lhs = p->unary_expression();
auto e = p->expect_multiplicative_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::kDivide, 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, MultiplicativeExpression_Parses_Modulo) {
auto p = parser("a % b");
auto lhs = p->unary_expression();
auto e = p->expect_multiplicative_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::kModulo, 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, MultiplicativeExpression_Parses_Grouping) {
auto p = parser("a * b / c % d * e");
auto lhs = p->unary_expression();
auto e = p->expect_multiplicative_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>());
// lhs: ((a * b) / c) % d
// op: *
// rhs: e
auto* rel = e->As<ast::BinaryExpression>();
EXPECT_EQ(ast::BinaryOp::kMultiply, rel->op);
ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
auto* ident = rel->rhs->As<ast::IdentifierExpression>();
EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("e"));
ASSERT_TRUE(rel->lhs->Is<ast::BinaryExpression>());
// lhs: (a * b) / c
// op: %
// rhs: d
rel = rel->lhs->As<ast::BinaryExpression>();
EXPECT_EQ(ast::BinaryOp::kModulo, rel->op);
ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
ident = rel->rhs->As<ast::IdentifierExpression>();
EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("d"));
ASSERT_TRUE(rel->lhs->Is<ast::BinaryExpression>());
// lhs: a * b
// op: /
// rhs: c
rel = rel->lhs->As<ast::BinaryExpression>();
EXPECT_EQ(ast::BinaryOp::kDivide, rel->op);
ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
ident = rel->rhs->As<ast::IdentifierExpression>();
EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("c"));
ASSERT_TRUE(rel->lhs->Is<ast::BinaryExpression>());
// lhs: a
// op: *
// rhs: b
rel = rel->lhs->As<ast::BinaryExpression>();
EXPECT_EQ(ast::BinaryOp::kMultiply, 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::IdentifierExpression>());
ident = rel->rhs->As<ast::IdentifierExpression>();
EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("b"));
}
TEST_F(ParserImplTest, MultiplicativeExpression_InvalidRHS) {
auto p = parser("a * if (a) {}");
auto lhs = p->unary_expression();
auto e = p->expect_multiplicative_expression_post_unary_expression(lhs.value);
EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr);
ASSERT_TRUE(p->has_error());
EXPECT_EQ(p->error(), "1:5: unable to parse right side of * expression");
}
TEST_F(ParserImplTest, MultiplicativeExpression_NoMatch_ReturnsLHS) {
auto p = parser("a + b");
auto lhs = p->unary_expression();
auto e = p->expect_multiplicative_expression_post_unary_expression(lhs.value);
EXPECT_FALSE(e.errored);
EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_NE(e.value, nullptr);
EXPECT_EQ(lhs.value, e.value);
}
} // namespace
} // namespace tint::reader::wgsl

View File

@ -469,6 +469,33 @@ TEST_F(ParserImplTest, TypeDecl_Array_ConstantSize) {
EXPECT_EQ(p->builder().Symbols().NameFor(count_expr->symbol), "size");
}
TEST_F(ParserImplTest, TypeDecl_Array_ExpressionSize) {
auto p = parser("array<f32, size + 2>");
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>();
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, TypeDecl_Array_Runtime) {
auto p = parser("array<u32>");
auto t = p->type_decl();
@ -524,7 +551,7 @@ TEST_F(ParserImplTest, TypeDecl_Array_BadSize) {
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
ASSERT_EQ(p->error(), "1:12: expected array size expression");
ASSERT_EQ(p->error(), "1:13: unable to parse right side of ! expression");
}
TEST_F(ParserImplTest, TypeDecl_Array_MissingSize) {

View File

@ -380,7 +380,7 @@ class Token {
/// @returns true if the token can be split during parse into component tokens
bool IsSplittable() const {
return Is(Token::Type::kShiftRight) || Is(Token::Type::kGreaterThanEqual) ||
Is(Token::Type::kAndAnd);
Is(Token::Type::kAndAnd) || Is(Token::Type::kMinusMinus);
}
/// @returns the source information for this token