diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn index 8e9a576faa..9b2fd353cb 100644 --- a/src/tint/BUILD.gn +++ b/src/tint/BUILD.gn @@ -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", diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt index 8612975484..288735a226 100644 --- a/src/tint/CMakeLists.txt +++ b/src/tint/CMakeLists.txt @@ -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 diff --git a/src/tint/reader/wgsl/lexer_test.cc b/src/tint/reader/wgsl/lexer_test.cc index dfd1b851a8..3268eaf836 100644 --- a/src/tint/reader/wgsl/lexer_test.cc +++ b/src/tint/reader/wgsl/lexer_test.cc @@ -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; diff --git a/src/tint/reader/wgsl/parser_impl.cc b/src/tint/reader/wgsl/parser_impl.cc index e4e681281b..6459ce7578 100644 --- a/src/tint/reader/wgsl/parser_impl.cc +++ b/src/tint/reader/wgsl/parser_impl.cc @@ -1066,7 +1066,7 @@ Maybe 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 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 ParserImpl::bitwise_expression_post_unary_expressi return Failure::kErrored; } +// multiplicative_operator +// : FORWARD_SLASH +// | MODULO +// | STAR +Maybe 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 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(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 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 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(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 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 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 diff --git a/src/tint/reader/wgsl/parser_impl.h b/src/tint/reader/wgsl/parser_impl.h index 1bde5a4527..d822450e0a 100644 --- a/src/tint/reader/wgsl/parser_impl.h +++ b/src/tint/reader/wgsl/parser_impl.h @@ -646,6 +646,30 @@ class ParserImpl { /// @returns the parsed expression or nullptr Maybe bitwise_expression_post_unary_expression( const ast::Expression* lhs); + /// Parse the `multiplicative_operator` grammar element + /// @returns the parsed operator if successful + Maybe multiplicative_operator(); + /// Parses multiplicative elements + /// @param lhs the left side of the expression + /// @returns the parsed expression or `lhs` if no match + Expect 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 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 expect_math_expression_post_unary_expression( + const ast::Expression* lhs); + /// Parses an `element_count_expression` grammar element + /// @returns the parsed expression or nullptr + Maybe element_count_expression(); + /// Parse the `additive_operator` grammar element + /// @returns the parsed operator if successful + Maybe additive_operator(); /// Parses the recursive part of the `and_expression`, erroring on parse /// failure. /// @param lhs the left side of the expression diff --git a/src/tint/reader/wgsl/parser_impl_additive_expression_test.cc b/src/tint/reader/wgsl/parser_impl_additive_expression_test.cc index 4573b84f03..3259e273b3 100644 --- a/src/tint/reader/wgsl/parser_impl_additive_expression_test.cc +++ b/src/tint/reader/wgsl/parser_impl_additive_expression_test.cc @@ -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()->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()->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()); } +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()); + auto* rel = e->As(); + EXPECT_EQ(ast::BinaryOp::kAdd, rel->op); + + ASSERT_TRUE(rel->lhs->Is()); + auto* ident = rel->lhs->As(); + EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a")); + + ASSERT_TRUE(rel->rhs->Is()); + ident = rel->rhs->As(); + 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()); + auto* rel = e->As(); + EXPECT_EQ(ast::BinaryOp::kSubtract, rel->op); + + ASSERT_TRUE(rel->lhs->Is()); + auto* ident = rel->lhs->As(); + EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a")); + + ASSERT_TRUE(rel->rhs->Is()); + ident = rel->rhs->As(); + 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()); + auto* rel = e->As(); + EXPECT_EQ(ast::BinaryOp::kSubtract, rel->op); + + ASSERT_TRUE(rel->lhs->Is()); + auto* ident = rel->lhs->As(); + EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a")); + + ASSERT_TRUE(rel->rhs->Is()); + auto* unary = rel->rhs->As(); + EXPECT_EQ(ast::UnaryOp::kNegation, unary->op); + + ASSERT_TRUE(unary->expr->Is()); + ident = unary->expr->As(); + 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()); + // lhs: ((a - b) + c + // op: - + // rhs: d + auto* rel = e->As(); + EXPECT_EQ(ast::BinaryOp::kSubtract, rel->op); + + ASSERT_TRUE(rel->rhs->Is()); + auto* ident = rel->rhs->As(); + EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("d")); + + ASSERT_TRUE(rel->lhs->Is()); + // lhs: a - b + // op: + + // rhs: c + rel = rel->lhs->As(); + EXPECT_EQ(ast::BinaryOp::kAdd, rel->op); + + ASSERT_TRUE(rel->rhs->Is()); + ident = rel->rhs->As(); + EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("c")); + + ASSERT_TRUE(rel->lhs->Is()); + // lhs: a + // op: - + // rhs: b + rel = rel->lhs->As(); + EXPECT_EQ(ast::BinaryOp::kSubtract, rel->op); + + ASSERT_TRUE(rel->lhs->Is()); + ident = rel->lhs->As(); + EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a")); + + ASSERT_TRUE(rel->rhs->Is()); + ident = rel->rhs->As(); + 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()); + // lhs: a - (b * c) + // op: - + // rhs: d + auto* rel = e->As(); + EXPECT_EQ(ast::BinaryOp::kSubtract, rel->op); + + ASSERT_TRUE(rel->rhs->Is()); + auto* ident = rel->rhs->As(); + EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("d")); + + ASSERT_TRUE(rel->lhs->Is()); + // lhs: a + // op: - + // rhs: b * c + rel = rel->lhs->As(); + EXPECT_EQ(ast::BinaryOp::kSubtract, rel->op); + + ASSERT_TRUE(rel->lhs->Is()); + ident = rel->lhs->As(); + EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a")); + + ASSERT_TRUE(rel->rhs->Is()); + // lhs: b + // op: * + // rhs: c + rel = rel->rhs->As(); + EXPECT_EQ(ast::BinaryOp::kMultiply, rel->op); + + ASSERT_TRUE(rel->lhs->Is()); + ident = rel->lhs->As(); + EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("b")); + + ASSERT_TRUE(rel->rhs->Is()); + ident = rel->rhs->As(); + 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 diff --git a/src/tint/reader/wgsl/parser_impl_element_count_expression_test.cc b/src/tint/reader/wgsl/parser_impl_element_count_expression_test.cc new file mode 100644 index 0000000000..0dd287d161 --- /dev/null +++ b/src/tint/reader/wgsl/parser_impl_element_count_expression_test.cc @@ -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()); + auto* rel = e->As(); + EXPECT_EQ(ast::BinaryOp::kMultiply, rel->op); + + ASSERT_TRUE(rel->lhs->Is()); + auto* ident = rel->lhs->As(); + EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a")); + + ASSERT_TRUE(rel->rhs->Is()); + ident = rel->rhs->As(); + 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()); + auto* rel = e->As(); + EXPECT_EQ(ast::BinaryOp::kOr, rel->op); + + ASSERT_TRUE(rel->lhs->Is()); + auto* ident = rel->lhs->As(); + EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a")); + + ASSERT_TRUE(rel->rhs->Is()); + ASSERT_TRUE(rel->rhs->As()->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 diff --git a/src/tint/reader/wgsl/parser_impl_error_msg_test.cc b/src/tint/reader/wgsl/parser_impl_error_msg_test.cc index 85e888dffb..ac2161c1ad 100644 --- a/src/tint/reader/wgsl/parser_impl_error_msg_test.cc +++ b/src/tint/reader/wgsl/parser_impl_error_msg_test.cc @@ -937,9 +937,9 @@ var i : array; TEST_F(ParserImplErrorTest, GlobalDeclVarArrayInvalidSize) { EXPECT("var i : array;", - 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; - ^ + ^ )"); } diff --git a/src/tint/reader/wgsl/parser_impl_math_expression_test.cc b/src/tint/reader/wgsl/parser_impl_math_expression_test.cc new file mode 100644 index 0000000000..27b5870bda --- /dev/null +++ b/src/tint/reader/wgsl/parser_impl_math_expression_test.cc @@ -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()); + auto* rel = e->As(); + EXPECT_EQ(ast::BinaryOp::kMultiply, rel->op); + + ASSERT_TRUE(rel->lhs->Is()); + auto* ident = rel->lhs->As(); + EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a")); + + ASSERT_TRUE(rel->rhs->Is()); + ident = rel->rhs->As(); + 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()); + // lhs: a * b + // op: + + // rhs: c + auto* rel = e->As(); + EXPECT_EQ(ast::BinaryOp::kAdd, rel->op); + + ASSERT_TRUE(rel->rhs->Is()); + auto* ident = rel->rhs->As(); + EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("c")); + + ASSERT_TRUE(rel->lhs->Is()); + // lhs: a + // op: * + // rhs: b + rel = rel->lhs->As(); + EXPECT_EQ(ast::BinaryOp::kMultiply, rel->op); + + ASSERT_TRUE(rel->lhs->Is()); + ident = rel->lhs->As(); + EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a")); + + ASSERT_TRUE(rel->rhs->Is()); + ident = rel->rhs->As(); + 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()); + auto* rel = e->As(); + EXPECT_EQ(ast::BinaryOp::kAdd, rel->op); + + ASSERT_TRUE(rel->lhs->Is()); + auto* ident = rel->lhs->As(); + EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a")); + + ASSERT_TRUE(rel->rhs->Is()); + ident = rel->rhs->As(); + 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()); + // lhs: a + // op: + + // rhs: b * c + auto* rel = e->As(); + EXPECT_EQ(ast::BinaryOp::kAdd, rel->op); + + ASSERT_TRUE(rel->lhs->Is()); + auto* ident = rel->lhs->As(); + EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a")); + + ASSERT_TRUE(rel->rhs->Is()); + // lhs: b + // op: * + // rhs: c + rel = rel->rhs->As(); + EXPECT_EQ(ast::BinaryOp::kMultiply, rel->op); + + ASSERT_TRUE(rel->lhs->Is()); + ident = rel->lhs->As(); + EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("b")); + + ASSERT_TRUE(rel->rhs->Is()); + ident = rel->rhs->As(); + 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 diff --git a/src/tint/reader/wgsl/parser_impl_multiplicative_expression_test.cc b/src/tint/reader/wgsl/parser_impl_multiplicative_expression_test.cc index 28ac568d19..618a0c083c 100644 --- a/src/tint/reader/wgsl/parser_impl_multiplicative_expression_test.cc +++ b/src/tint/reader/wgsl/parser_impl_multiplicative_expression_test.cc @@ -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()->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()->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()->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()); } +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()); + auto* rel = e->As(); + EXPECT_EQ(ast::BinaryOp::kMultiply, rel->op); + + ASSERT_TRUE(rel->lhs->Is()); + auto* ident = rel->lhs->As(); + EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a")); + + ASSERT_TRUE(rel->rhs->Is()); + ident = rel->rhs->As(); + 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()); + auto* rel = e->As(); + EXPECT_EQ(ast::BinaryOp::kMultiply, rel->op); + + ASSERT_TRUE(rel->lhs->Is()); + auto* ident = rel->lhs->As(); + EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a")); + + ASSERT_TRUE(rel->rhs->Is()); + auto* unary = rel->rhs->As(); + EXPECT_EQ(ast::UnaryOp::kIndirection, unary->op); + + ASSERT_TRUE(unary->expr->Is()); + ident = unary->expr->As(); + 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()); + auto* rel = e->As(); + EXPECT_EQ(ast::BinaryOp::kDivide, rel->op); + + ASSERT_TRUE(rel->lhs->Is()); + auto* ident = rel->lhs->As(); + EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a")); + + ASSERT_TRUE(rel->rhs->Is()); + ident = rel->rhs->As(); + 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()); + auto* rel = e->As(); + EXPECT_EQ(ast::BinaryOp::kModulo, rel->op); + + ASSERT_TRUE(rel->lhs->Is()); + auto* ident = rel->lhs->As(); + EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a")); + + ASSERT_TRUE(rel->rhs->Is()); + ident = rel->rhs->As(); + 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()); + // lhs: ((a * b) / c) % d + // op: * + // rhs: e + auto* rel = e->As(); + EXPECT_EQ(ast::BinaryOp::kMultiply, rel->op); + + ASSERT_TRUE(rel->rhs->Is()); + auto* ident = rel->rhs->As(); + EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("e")); + + ASSERT_TRUE(rel->lhs->Is()); + // lhs: (a * b) / c + // op: % + // rhs: d + rel = rel->lhs->As(); + EXPECT_EQ(ast::BinaryOp::kModulo, rel->op); + + ASSERT_TRUE(rel->rhs->Is()); + ident = rel->rhs->As(); + EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("d")); + + ASSERT_TRUE(rel->lhs->Is()); + // lhs: a * b + // op: / + // rhs: c + rel = rel->lhs->As(); + EXPECT_EQ(ast::BinaryOp::kDivide, rel->op); + + ASSERT_TRUE(rel->rhs->Is()); + ident = rel->rhs->As(); + EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("c")); + + ASSERT_TRUE(rel->lhs->Is()); + // lhs: a + // op: * + // rhs: b + rel = rel->lhs->As(); + EXPECT_EQ(ast::BinaryOp::kMultiply, rel->op); + + ASSERT_TRUE(rel->lhs->Is()); + ident = rel->lhs->As(); + EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a")); + + ASSERT_TRUE(rel->rhs->Is()); + ident = rel->rhs->As(); + 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 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 1cf66c0637..57c1b72ed1 100644 --- a/src/tint/reader/wgsl/parser_impl_type_decl_test.cc +++ b/src/tint/reader/wgsl/parser_impl_type_decl_test.cc @@ -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"); + 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()); + + auto* a = t.value->As(); + ASSERT_FALSE(a->IsRuntimeArray()); + ASSERT_TRUE(a->type->Is()); + EXPECT_EQ(a->attributes.Length(), 0u); + + ASSERT_TRUE(a->count->Is()); + auto* count_expr = a->count->As(); + EXPECT_EQ(ast::BinaryOp::kAdd, count_expr->op); + + ASSERT_TRUE(count_expr->lhs->Is()); + auto* ident = count_expr->lhs->As(); + EXPECT_EQ(p->builder().Symbols().NameFor(ident->symbol), "size"); + + ASSERT_TRUE(count_expr->rhs->Is()); + auto* val = count_expr->rhs->As(); + EXPECT_EQ(2, static_cast(val->value)); +} + TEST_F(ParserImplTest, TypeDecl_Array_Runtime) { auto p = parser("array"); 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) { diff --git a/src/tint/reader/wgsl/token.h b/src/tint/reader/wgsl/token.h index d8b93c6167..4cf9aad1b1 100644 --- a/src/tint/reader/wgsl/token.h +++ b/src/tint/reader/wgsl/token.h @@ -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