Add bitwise_expression to the WGSL parser.

This CL adds the `bitwise_expression.post.unary_expression` parsing
into the WGSL parser.

Bug: tint:1633
Change-Id: Idaf1a413662d1c10d9d9f25d3b35ed5323b8f883
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/99383
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
This commit is contained in:
dan sinclair 2022-08-18 14:37:17 +00:00 committed by Dawn LUCI CQ
parent e0bf254f72
commit ff2cb02e92
6 changed files with 415 additions and 2 deletions

View File

@ -1235,10 +1235,10 @@ if (tint_build_unittests) {
"utils/defer_test.cc",
"utils/enum_set_test.cc",
"utils/hash_test.cc",
"utils/io/command_test.cc",
"utils/io/tmpfile_test.cc",
"utils/hashmap_test.cc",
"utils/hashset_test.cc",
"utils/io/command_test.cc",
"utils/io/tmpfile_test.cc",
"utils/map_test.cc",
"utils/math_test.cc",
"utils/result_test.cc",
@ -1353,6 +1353,7 @@ if (tint_build_unittests) {
"reader/wgsl/parser_impl_and_expression_test.cc",
"reader/wgsl/parser_impl_argument_expression_list_test.cc",
"reader/wgsl/parser_impl_assignment_stmt_test.cc",
"reader/wgsl/parser_impl_bitwise_expression_test.cc",
"reader/wgsl/parser_impl_break_stmt_test.cc",
"reader/wgsl/parser_impl_bug_cases_test.cc",
"reader/wgsl/parser_impl_call_stmt_test.cc",

View File

@ -948,6 +948,7 @@ if(TINT_BUILD_TESTS)
reader/wgsl/parser_impl_and_expression_test.cc
reader/wgsl/parser_impl_argument_expression_list_test.cc
reader/wgsl/parser_impl_assignment_stmt_test.cc
reader/wgsl/parser_impl_bitwise_expression_test.cc
reader/wgsl/parser_impl_break_stmt_test.cc
reader/wgsl/parser_impl_bug_cases_test.cc
reader/wgsl/parser_impl_call_stmt_test.cc

View File

@ -2599,6 +2599,64 @@ Expect<ParserImpl::ExpressionList> ParserImpl::expect_argument_expression_list(
});
}
// bitwise_expression.post.unary_expression
// : AND unary_expression (AND unary_expression)*
// | OR unary_expression (OR unary_expression)*
// | XOR unary_expression (XOR unary_expression)*
Maybe<const ast::Expression*> ParserImpl::bitwise_expression_post_unary_expression(
const ast::Expression* lhs) {
auto& t = peek();
if (!t.Is(Token::Type::kAnd) && !t.Is(Token::Type::kOr) && !t.Is(Token::Type::kXor)) {
return Failure::kNoMatch;
}
ast::BinaryOp op = ast::BinaryOp::kXor;
if (t.Is(Token::Type::kAnd)) {
op = ast::BinaryOp::kAnd;
} else if (t.Is(Token::Type::kOr)) {
op = ast::BinaryOp::kOr;
}
while (continue_parsing()) {
auto& n = peek();
// Handle the case of `a & b &&c` where `&c` is a unary_expression
bool split = false;
if (op == ast::BinaryOp::kAnd && n.Is(Token::Type::kAndAnd)) {
next();
split_token(Token::Type::kAnd, Token::Type::kAnd);
split = true;
}
if (!n.Is(t.type())) {
if (n.Is(Token::Type::kAnd) || n.Is(Token::Type::kOr) || n.Is(Token::Type::kXor)) {
return add_error(n.source(), std::string("mixing '") + std::string(t.to_name()) +
"' and '" + std::string(n.to_name()) +
"' requires parenthesis");
}
return lhs;
}
// If forced to split an `&&` then we've already done the `next` above which consumes
// the `&`. The type check above will always fail because we only split if already consuming
// a `&` operator.
if (!split) {
next();
}
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, lhs, rhs.value);
}
return Failure::kErrored;
}
// unary_expression
// : singular_expression
// | MINUS unary_expression

View File

@ -641,6 +641,11 @@ class ParserImpl {
/// Parses the `equality_expression` grammar element
/// @returns the parsed expression or nullptr
Maybe<const ast::Expression*> equality_expression();
/// Parses the `bitwise_expression.post.unary_expression` grammar element
/// @param lhs the left side of the expression
/// @returns the parsed expression or nullptr
Maybe<const ast::Expression*> bitwise_expression_post_unary_expression(
const ast::Expression* lhs);
/// Parses the recursive part of the `and_expression`, erroring on parse
/// failure.
/// @param lhs the left side of the expression

View File

@ -0,0 +1,345 @@
// 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, BitwiseExpr_NoOp) {
auto p = parser("a true");
auto lhs = p->unary_expression();
auto e = p->bitwise_expression_post_unary_expression(lhs.value);
EXPECT_FALSE(e.matched);
EXPECT_FALSE(e.errored);
EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_EQ(e.value, nullptr);
}
TEST_F(ParserImplTest, BitwiseExpr_Or_Parses) {
auto p = parser("a | true");
auto lhs = p->unary_expression();
auto e = p->bitwise_expression_post_unary_expression(lhs.value);
EXPECT_TRUE(e.matched);
EXPECT_FALSE(e.errored);
EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_NE(e.value, nullptr);
EXPECT_EQ(e->source.range.begin.line, 1u);
EXPECT_EQ(e->source.range.begin.column, 3u);
EXPECT_EQ(e->source.range.end.line, 1u);
EXPECT_EQ(e->source.range.end.column, 4u);
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, BitwiseExpr_Or_Parses_Multiple) {
auto p = parser("a | true | b");
auto lhs = p->unary_expression();
auto e = p->bitwise_expression_post_unary_expression(lhs.value);
EXPECT_TRUE(e.matched);
EXPECT_FALSE(e.errored);
EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_NE(e.value, nullptr);
// lhs: (a | true)
// rhs: b
ASSERT_TRUE(e->Is<ast::BinaryExpression>());
auto* rel = e->As<ast::BinaryExpression>();
EXPECT_EQ(ast::BinaryOp::kOr, rel->op);
ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
auto* ident = rel->rhs->As<ast::IdentifierExpression>();
EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("b"));
ASSERT_TRUE(rel->lhs->Is<ast::BinaryExpression>());
// lhs: a
// rhs: true
rel = rel->lhs->As<ast::BinaryExpression>();
ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
ident = rel->lhs->As<ast::IdentifierExpression>();
EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a"));
ASSERT_TRUE(rel->rhs->Is<ast::BoolLiteralExpression>());
ASSERT_TRUE(rel->rhs->As<ast::BoolLiteralExpression>()->value);
}
TEST_F(ParserImplTest, BitwiseExpr_Or_MixedWithAnd_Invalid) {
auto p = parser("a | b & c");
auto lhs = p->unary_expression();
auto e = p->bitwise_expression_post_unary_expression(lhs.value);
EXPECT_FALSE(e.matched);
EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr);
EXPECT_TRUE(p->has_error());
EXPECT_EQ(p->error(), "1:7: mixing '|' and '&' requires parenthesis");
}
TEST_F(ParserImplTest, BitwiseExpr_Or_MixedWithXor_Invalid) {
auto p = parser("a | b ^ c");
auto lhs = p->unary_expression();
auto e = p->bitwise_expression_post_unary_expression(lhs.value);
EXPECT_FALSE(e.matched);
EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr);
EXPECT_TRUE(p->has_error());
EXPECT_EQ(p->error(), "1:7: mixing '|' and '^' requires parenthesis");
}
TEST_F(ParserImplTest, BitwiseExpr_Or_InvalidRHS) {
auto p = parser("true | if (a) {}");
auto lhs = p->unary_expression();
auto e = p->bitwise_expression_post_unary_expression(lhs.value);
EXPECT_FALSE(e.matched);
EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr);
EXPECT_TRUE(p->has_error());
EXPECT_EQ(p->error(), "1:8: unable to parse right side of | expression");
}
TEST_F(ParserImplTest, BitwiseExpr_Xor_Parses) {
auto p = parser("a ^ true");
auto lhs = p->unary_expression();
auto e = p->bitwise_expression_post_unary_expression(lhs.value);
EXPECT_TRUE(e.matched);
EXPECT_FALSE(e.errored);
EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_NE(e.value, nullptr);
EXPECT_EQ(e->source.range.begin.line, 1u);
EXPECT_EQ(e->source.range.begin.column, 3u);
EXPECT_EQ(e->source.range.end.line, 1u);
EXPECT_EQ(e->source.range.end.column, 4u);
ASSERT_TRUE(e->Is<ast::BinaryExpression>());
auto* rel = e->As<ast::BinaryExpression>();
EXPECT_EQ(ast::BinaryOp::kXor, 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, BitwiseExpr_Xor_Parses_Multiple) {
auto p = parser("a ^ true ^ b");
auto lhs = p->unary_expression();
auto e = p->bitwise_expression_post_unary_expression(lhs.value);
EXPECT_TRUE(e.matched);
EXPECT_FALSE(e.errored);
EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_NE(e.value, nullptr);
// lhs: (a ^ true)
// rhs: b
ASSERT_TRUE(e->Is<ast::BinaryExpression>());
auto* rel = e->As<ast::BinaryExpression>();
EXPECT_EQ(ast::BinaryOp::kXor, rel->op);
ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
auto* ident = rel->rhs->As<ast::IdentifierExpression>();
EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("b"));
ASSERT_TRUE(rel->lhs->Is<ast::BinaryExpression>());
// lhs: a
// rhs: true
rel = rel->lhs->As<ast::BinaryExpression>();
ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
ident = rel->lhs->As<ast::IdentifierExpression>();
EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("a"));
ASSERT_TRUE(rel->rhs->Is<ast::BoolLiteralExpression>());
ASSERT_TRUE(rel->rhs->As<ast::BoolLiteralExpression>()->value);
}
TEST_F(ParserImplTest, BitwiseExpr_Xor_MixedWithOr_Invalid) {
auto p = parser("a ^ b | c");
auto lhs = p->unary_expression();
auto e = p->bitwise_expression_post_unary_expression(lhs.value);
EXPECT_FALSE(e.matched);
EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr);
EXPECT_TRUE(p->has_error());
EXPECT_EQ(p->error(), "1:7: mixing '^' and '|' requires parenthesis");
}
TEST_F(ParserImplTest, BitwiseExpr_Xor_MixedWithAnd_Invalid) {
auto p = parser("a ^ b & c");
auto lhs = p->unary_expression();
auto e = p->bitwise_expression_post_unary_expression(lhs.value);
EXPECT_FALSE(e.matched);
EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr);
EXPECT_TRUE(p->has_error());
EXPECT_EQ(p->error(), "1:7: mixing '^' and '&' requires parenthesis");
}
TEST_F(ParserImplTest, BitwiseExpr_Xor_InvalidRHS) {
auto p = parser("true ^ if (a) {}");
auto lhs = p->unary_expression();
auto e = p->bitwise_expression_post_unary_expression(lhs.value);
EXPECT_FALSE(e.matched);
EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr);
ASSERT_TRUE(p->has_error());
EXPECT_EQ(p->error(), "1:8: unable to parse right side of ^ expression");
}
TEST_F(ParserImplTest, BitwiseExpr_And_Parses) {
auto p = parser("a & true");
auto lhs = p->unary_expression();
auto e = p->bitwise_expression_post_unary_expression(lhs.value);
EXPECT_TRUE(e.matched);
EXPECT_FALSE(e.errored);
EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_NE(e.value, nullptr);
EXPECT_EQ(e->source.range.begin.line, 1u);
EXPECT_EQ(e->source.range.begin.column, 3u);
EXPECT_EQ(e->source.range.end.line, 1u);
EXPECT_EQ(e->source.range.end.column, 4u);
ASSERT_TRUE(e->Is<ast::BinaryExpression>());
auto* rel = e->As<ast::BinaryExpression>();
EXPECT_EQ(ast::BinaryOp::kAnd, rel->op);
ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
auto* ident = rel->lhs->As<ast::IdentifierExpression>();
EXPECT_EQ(ident->symbol, p->builder().Symbols().Register("a"));
ASSERT_TRUE(rel->rhs->Is<ast::BoolLiteralExpression>());
ASSERT_TRUE(rel->rhs->As<ast::BoolLiteralExpression>()->value);
}
TEST_F(ParserImplTest, BitwiseExpr_And_Parses_Multiple) {
auto p = parser("a & true & b");
auto lhs = p->unary_expression();
auto e = p->bitwise_expression_post_unary_expression(lhs.value);
EXPECT_TRUE(e.matched);
EXPECT_FALSE(e.errored);
EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_NE(e.value, nullptr);
// lhs: (a & true)
// rhs: b
ASSERT_TRUE(e->Is<ast::BinaryExpression>());
auto* rel = e->As<ast::BinaryExpression>();
EXPECT_EQ(ast::BinaryOp::kAnd, rel->op);
ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
auto* ident = rel->rhs->As<ast::IdentifierExpression>();
EXPECT_EQ(ident->symbol, p->builder().Symbols().Register("b"));
ASSERT_TRUE(rel->lhs->Is<ast::BinaryExpression>());
// lhs: a
// rhs: true
rel = rel->lhs->As<ast::BinaryExpression>();
ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
ident = rel->lhs->As<ast::IdentifierExpression>();
EXPECT_EQ(ident->symbol, p->builder().Symbols().Register("a"));
ASSERT_TRUE(rel->rhs->Is<ast::BoolLiteralExpression>());
ASSERT_TRUE(rel->rhs->As<ast::BoolLiteralExpression>()->value);
}
TEST_F(ParserImplTest, BitwiseExpr_And_Parses_AndAnd) {
auto p = parser("a & true &&b");
auto lhs = p->unary_expression();
auto e = p->bitwise_expression_post_unary_expression(lhs.value);
EXPECT_TRUE(e.matched);
EXPECT_FALSE(e.errored);
EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_NE(e.value, nullptr);
// lhs: (a & true)
// rhs: &b
ASSERT_TRUE(e->Is<ast::BinaryExpression>());
auto* rel = e->As<ast::BinaryExpression>();
EXPECT_EQ(ast::BinaryOp::kAnd, rel->op);
ASSERT_TRUE(rel->rhs->Is<ast::UnaryOpExpression>());
auto* unary = rel->rhs->As<ast::UnaryOpExpression>();
EXPECT_EQ(ast::UnaryOp::kAddressOf, unary->op);
ASSERT_TRUE(unary->expr->Is<ast::IdentifierExpression>());
auto* ident = unary->expr->As<ast::IdentifierExpression>();
EXPECT_EQ(ident->symbol, p->builder().Symbols().Register("b"));
ASSERT_TRUE(rel->lhs->Is<ast::BinaryExpression>());
// lhs: a
// rhs: true
rel = rel->lhs->As<ast::BinaryExpression>();
ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
ident = rel->lhs->As<ast::IdentifierExpression>();
EXPECT_EQ(ident->symbol, p->builder().Symbols().Register("a"));
ASSERT_TRUE(rel->rhs->Is<ast::BoolLiteralExpression>());
ASSERT_TRUE(rel->rhs->As<ast::BoolLiteralExpression>()->value);
}
TEST_F(ParserImplTest, BitwiseExpr_And_MixedWithOr_Invalid) {
auto p = parser("a & b | c");
auto lhs = p->unary_expression();
auto e = p->bitwise_expression_post_unary_expression(lhs.value);
EXPECT_FALSE(e.matched);
EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr);
EXPECT_TRUE(p->has_error());
EXPECT_EQ(p->error(), "1:7: mixing '&' and '|' requires parenthesis");
}
TEST_F(ParserImplTest, BitwiseExpr_And_MixedWithXor_Invalid) {
auto p = parser("a & b ^ c");
auto lhs = p->unary_expression();
auto e = p->bitwise_expression_post_unary_expression(lhs.value);
EXPECT_FALSE(e.matched);
EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr);
EXPECT_TRUE(p->has_error());
EXPECT_EQ(p->error(), "1:7: mixing '&' and '^' requires parenthesis");
}
TEST_F(ParserImplTest, BitwiseExpr_And_InvalidRHS) {
auto p = parser("true & if (a) {}");
auto lhs = p->unary_expression();
auto e = p->bitwise_expression_post_unary_expression(lhs.value);
EXPECT_FALSE(e.matched);
EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr);
EXPECT_TRUE(p->has_error());
EXPECT_EQ(p->error(), "1:8: unable to parse right side of & expression");
}
} // namespace
} // namespace tint::reader::wgsl

View File

@ -386,6 +386,9 @@ class Token {
/// @returns the source information for this token
Source source() const { return source_; }
/// @returns the type of the token
Type type() const { return type_; }
/// Returns the string value of the token
/// @return std::string
std::string to_str() const;