Add core_lhs_expression and lhs_expression to parser.

This CL updates the WGSL parser to have a `core_lhs_expression`
and a `lhs_expression` grammar rule. The rules are not used anywhere
yet, but are standalone so are added with tests.

Bug: tint:1633
Change-Id: I87bdaefeb06be637f72a7e6fa72ce2b6298c7bb7
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/99240
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-17 16:30:30 +00:00 committed by Dawn LUCI CQ
parent e13160efb6
commit 6c8dc15d64
8 changed files with 264 additions and 5 deletions

View File

@ -1361,6 +1361,7 @@ if (tint_build_unittests) {
"reader/wgsl/parser_impl_const_literal_test.cc",
"reader/wgsl/parser_impl_continue_stmt_test.cc",
"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_enable_directive_test.cc",
"reader/wgsl/parser_impl_equality_expression_test.cc",
@ -1379,6 +1380,7 @@ if (tint_build_unittests) {
"reader/wgsl/parser_impl_if_stmt_test.cc",
"reader/wgsl/parser_impl_inclusive_or_expression_test.cc",
"reader/wgsl/parser_impl_increment_decrement_stmt_test.cc",
"reader/wgsl/parser_impl_lhs_expression_test.cc",
"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",

View File

@ -956,6 +956,7 @@ if(TINT_BUILD_TESTS)
reader/wgsl/parser_impl_const_literal_test.cc
reader/wgsl/parser_impl_continue_stmt_test.cc
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_enable_directive_test.cc
reader/wgsl/parser_impl_external_texture_test.cc
@ -974,6 +975,7 @@ if(TINT_BUILD_TESTS)
reader/wgsl/parser_impl_if_stmt_test.cc
reader/wgsl/parser_impl_inclusive_or_expression_test.cc
reader/wgsl/parser_impl_increment_decrement_stmt_test.cc
reader/wgsl/parser_impl_lhs_expression_test.cc
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

View File

@ -1009,7 +1009,6 @@ TEST_P(PunctuationTest, Parses) {
INSTANTIATE_TEST_SUITE_P(LexerTest,
PunctuationTest,
testing::Values(TokenData{"&", Token::Type::kAnd},
TokenData{"&&", Token::Type::kAndAnd},
TokenData{"->", Token::Type::kArrow},
TokenData{"@", Token::Type::kAttr},
TokenData{"/", Token::Type::kForwardSlash},
@ -1087,7 +1086,8 @@ TEST_P(SplittablePunctuationTest, Parses) {
}
INSTANTIATE_TEST_SUITE_P(LexerTest,
SplittablePunctuationTest,
testing::Values(TokenData{">=", Token::Type::kGreaterThanEqual},
testing::Values(TokenData{"&&", Token::Type::kAndAnd},
TokenData{">=", Token::Type::kGreaterThanEqual},
TokenData{">>", Token::Type::kShiftRight}));
using KeywordTest = testing::TestWithParam<TokenData>;

View File

@ -2501,8 +2501,9 @@ Maybe<const ast::Expression*> ParserImpl::primary_expression() {
// postfix_expression
// :
// | BRACE_LEFT expression BRACE_RIGHT postfix_expr
// | PERIOD IDENTIFIER postfix_expr
// | BRACE_LEFT expression BRACE_RIGHT postfix_expression?
// | PERIOD member_ident postfix_expression?
// | PERIOD swizzle_name postfix_expression?
Maybe<const ast::Expression*> ParserImpl::postfix_expression(const ast::Expression* prefix) {
Source source;
@ -3114,6 +3115,68 @@ Maybe<ast::BinaryOp> ParserImpl::compound_assignment_operator() {
return Failure::kNoMatch;
}
// core_lhs_expression
// : ident
// | PAREN_LEFT lhs_expression PAREN_RIGHT
Expect<const ast::Expression*> ParserImpl::core_lhs_expression() {
auto& t = peek();
if (t.IsIdentifier()) {
next();
return create<ast::IdentifierExpression>(t.source(),
builder_.Symbols().Register(t.to_str()));
}
if (peek_is(Token::Type::kParenLeft)) {
return expect_paren_block("", [&]() -> Expect<const ast::Expression*> {
auto expr = lhs_expression();
if (expr.errored) {
return Failure::kErrored;
}
return expr.value;
});
}
return add_error(peek(), "missing expression");
}
// lhs_expression
// : ( STAR | AND )* core_lhs_expression postfix_expression?
Expect<const ast::Expression*> ParserImpl::lhs_expression() {
std::vector<const Token*> prefixes;
while (peek_is(Token::Type::kStar) || peek_is(Token::Type::kAnd) ||
peek_is(Token::Type::kAndAnd)) {
auto& t = next();
// If an '&&' is provided split into '&' and '&'
if (t.Is(Token::Type::kAndAnd)) {
split_token(Token::Type::kAnd, Token::Type::kAnd);
}
prefixes.push_back(&t);
}
auto core_expr = core_lhs_expression();
if (core_expr.errored) {
return Failure::kErrored;
}
const auto* expr = core_expr.value;
for (auto it = prefixes.rbegin(); it != prefixes.rend(); ++it) {
auto& t = **it;
ast::UnaryOp op = ast::UnaryOp::kAddressOf;
if (t.Is(Token::Type::kStar)) {
op = ast::UnaryOp::kIndirection;
}
expr = create<ast::UnaryOpExpression>(t.source(), op, expr);
}
auto e = postfix_expression(expr);
if (e.errored) {
return Failure::kErrored;
}
return e.value;
}
// assignment_statement
// | lhs_expression ( EQUAL | compound_assignment_operator ) expression
// | UNDERSCORE EQUAL expression

View File

@ -683,6 +683,12 @@ class ParserImpl {
/// Parses a `compound_assignment_operator` grammar element
/// @returns the parsed compound assignment operator
Maybe<ast::BinaryOp> compound_assignment_operator();
/// Parses a `core_lhs_expression` grammar element
/// @returns the parsed expression or a non-kMatched failure
Expect<const ast::Expression*> core_lhs_expression();
/// Parses a `lhs_expression` grammar element
/// @returns the parsed expression or a non-kMatched failure
Expect<const ast::Expression*> lhs_expression();
/// Parses a `assignment_statement` grammar element
/// @returns the parsed assignment or nullptr
Maybe<const ast::Statement*> assignment_statement();

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, CoreLHS_Ident) {
auto p = parser("identifier");
auto e = p->core_lhs_expression();
ASSERT_FALSE(p->has_error()) << p->error();
ASSERT_FALSE(e.errored);
ASSERT_NE(e.value, nullptr);
ASSERT_TRUE(e->Is<ast::IdentifierExpression>());
auto* ident = e->As<ast::IdentifierExpression>();
EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("identifier"));
}
TEST_F(ParserImplTest, CoreLHS_ParenStmt) {
auto p = parser("(a)");
auto e = p->core_lhs_expression();
ASSERT_FALSE(p->has_error()) << p->error();
ASSERT_FALSE(e.errored);
ASSERT_NE(e.value, nullptr);
ASSERT_TRUE(e->Is<ast::IdentifierExpression>());
auto* ident = e->As<ast::IdentifierExpression>();
EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a"));
}
TEST_F(ParserImplTest, CoreLHS_MissingRightParen) {
auto p = parser("(a");
auto e = p->core_lhs_expression();
ASSERT_TRUE(p->has_error());
ASSERT_TRUE(e.errored);
ASSERT_EQ(e.value, nullptr);
EXPECT_EQ(p->error(), "1:3: expected ')'");
}
TEST_F(ParserImplTest, CoreLHS_InvalidLHSExpression) {
auto p = parser("(if (a() {})");
auto e = p->core_lhs_expression();
ASSERT_TRUE(p->has_error());
ASSERT_TRUE(e.errored);
ASSERT_EQ(e.value, nullptr);
EXPECT_EQ(p->error(), "1:2: missing expression");
}
TEST_F(ParserImplTest, CoreLHS_MissingLHSExpression) {
auto p = parser("()");
auto e = p->core_lhs_expression();
ASSERT_TRUE(p->has_error());
ASSERT_TRUE(e.errored);
ASSERT_EQ(e.value, nullptr);
EXPECT_EQ(p->error(), "1:2: missing expression");
}
TEST_F(ParserImplTest, CoreLHS_Invalid) {
auto p = parser("1234");
auto e = p->core_lhs_expression();
ASSERT_TRUE(p->has_error());
ASSERT_TRUE(e.errored);
ASSERT_EQ(e.value, nullptr);
EXPECT_EQ(p->error(), "1:1: missing expression");
}
} // namespace
} // namespace tint::reader::wgsl

View File

@ -0,0 +1,104 @@
// 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, LHSExpression_NoPrefix) {
auto p = parser("a");
auto e = p->lhs_expression();
ASSERT_FALSE(p->has_error()) << p->error();
ASSERT_FALSE(e.errored);
ASSERT_NE(e.value, nullptr);
ASSERT_TRUE(e->Is<ast::IdentifierExpression>());
}
TEST_F(ParserImplTest, LHSExpression_And) {
auto p = parser("&a");
auto e = p->lhs_expression();
ASSERT_FALSE(p->has_error()) << p->error();
ASSERT_FALSE(e.errored);
ASSERT_NE(e.value, nullptr);
ASSERT_TRUE(e->Is<ast::UnaryOpExpression>());
auto* u = e->As<ast::UnaryOpExpression>();
EXPECT_EQ(u->op, ast::UnaryOp::kAddressOf);
EXPECT_TRUE(u->expr->Is<ast::IdentifierExpression>());
}
TEST_F(ParserImplTest, LHSExpression_Star) {
auto p = parser("*a");
auto e = p->lhs_expression();
ASSERT_FALSE(p->has_error()) << p->error();
ASSERT_FALSE(e.errored);
ASSERT_NE(e.value, nullptr);
ASSERT_TRUE(e->Is<ast::UnaryOpExpression>());
auto* u = e->As<ast::UnaryOpExpression>();
EXPECT_EQ(u->op, ast::UnaryOp::kIndirection);
EXPECT_TRUE(u->expr->Is<ast::IdentifierExpression>());
}
TEST_F(ParserImplTest, LHSExpression_Multiple) {
auto p = parser("*&**&&*a");
auto e = p->lhs_expression();
ASSERT_FALSE(p->has_error()) << p->error();
ASSERT_FALSE(e.errored);
ASSERT_NE(e.value, nullptr);
std::vector<ast::UnaryOp> results = {ast::UnaryOp::kIndirection, ast::UnaryOp::kAddressOf,
ast::UnaryOp::kIndirection, ast::UnaryOp::kIndirection,
ast::UnaryOp::kAddressOf, ast::UnaryOp::kAddressOf,
ast::UnaryOp::kIndirection};
auto* expr = e.value;
for (auto op : results) {
ASSERT_TRUE(expr->Is<ast::UnaryOpExpression>());
auto* u = expr->As<ast::UnaryOpExpression>();
EXPECT_EQ(u->op, op);
expr = u->expr;
}
EXPECT_TRUE(expr->Is<ast::IdentifierExpression>());
}
TEST_F(ParserImplTest, LHSExpression_PostfixExpression) {
auto p = parser("*a.foo");
auto e = p->lhs_expression();
ASSERT_FALSE(p->has_error()) << p->error();
ASSERT_FALSE(e.errored);
ASSERT_NE(e.value, nullptr);
ASSERT_TRUE(e->Is<ast::MemberAccessorExpression>());
auto* access = e->As<ast::MemberAccessorExpression>();
ASSERT_TRUE(access->structure->Is<ast::UnaryOpExpression>());
auto* u = access->structure->As<ast::UnaryOpExpression>();
EXPECT_EQ(u->op, ast::UnaryOp::kIndirection);
ASSERT_TRUE(u->expr->Is<ast::IdentifierExpression>());
auto* struct_ident = u->expr->As<ast::IdentifierExpression>();
EXPECT_EQ(struct_ident->symbol, p->builder().Symbols().Get("a"));
ASSERT_TRUE(access->member->Is<ast::IdentifierExpression>());
auto* member_ident = access->member->As<ast::IdentifierExpression>();
EXPECT_EQ(member_ident->symbol, p->builder().Symbols().Get("foo"));
}
} // namespace
} // namespace tint::reader::wgsl

View File

@ -379,7 +379,8 @@ 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);
return Is(Token::Type::kShiftRight) || Is(Token::Type::kGreaterThanEqual) ||
Is(Token::Type::kAndAnd);
}
/// @returns the source information for this token