Add tokens for left and right shift
Manually split `>>` and `>=` tokens when looking for a `>` to
correctly parse ptr/array/vec declarations and initializations.
Bug: tint:171, tint:355
Change-Id: Iee89a844fd999e337ae44ef9b192cc122fbf9e54
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/39362
Auto-Submit: James Price <jrprice@google.com>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/src/reader/wgsl/lexer.cc b/src/reader/wgsl/lexer.cc
index 9a2200b..181f46c 100644
--- a/src/reader/wgsl/lexer.cc
+++ b/src/reader/wgsl/lexer.cc
@@ -431,6 +431,10 @@
type = Token::Type::kGreaterThanEqual;
pos_ += 2;
location_.column += 2;
+ } else if (matches(pos_, ">>")) {
+ type = Token::Type::kShiftRight;
+ pos_ += 2;
+ location_.column += 2;
} else if (matches(pos_, ">")) {
type = Token::Type::kGreaterThan;
pos_ += 1;
@@ -439,6 +443,10 @@
type = Token::Type::kLessThanEqual;
pos_ += 2;
location_.column += 2;
+ } else if (matches(pos_, "<<")) {
+ type = Token::Type::kShiftLeft;
+ pos_ += 2;
+ location_.column += 2;
} else if (matches(pos_, "<")) {
type = Token::Type::kLessThan;
pos_ += 1;
diff --git a/src/reader/wgsl/lexer_test.cc b/src/reader/wgsl/lexer_test.cc
index 8af9284..7a4a49d 100644
--- a/src/reader/wgsl/lexer_test.cc
+++ b/src/reader/wgsl/lexer_test.cc
@@ -430,8 +430,10 @@
TokenData{"==", Token::Type::kEqualEqual},
TokenData{">", Token::Type::kGreaterThan},
TokenData{">=", Token::Type::kGreaterThanEqual},
+ TokenData{">>", Token::Type::kShiftRight},
TokenData{"<", Token::Type::kLessThan},
TokenData{"<=", Token::Type::kLessThanEqual},
+ TokenData{"<<", Token::Type::kShiftLeft},
TokenData{"%", Token::Type::kMod},
TokenData{"!=", Token::Type::kNotEqual},
TokenData{"-", Token::Type::kMinus},
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index 1e4a834..669f054 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -2321,23 +2321,20 @@
// shift_expr
// :
-// | LESS_THAN LESS_THAN additive_expression shift_expr
-// | GREATER_THAN GREATER_THAN additive_expression shift_expr
+// | SHIFT_LEFT additive_expression shift_expr
+// | SHIFT_RIGHT additive_expression shift_expr
Expect<ast::Expression*> ParserImpl::expect_shift_expr(ast::Expression* lhs) {
auto t = peek();
auto source = t.source();
- auto t2 = peek(1);
auto* name = "";
ast::BinaryOp op = ast::BinaryOp::kNone;
- if (t.IsLessThan() && t2.IsLessThan()) {
- next(); // Consume the t peek
- next(); // Consume the t2 peek
+ if (t.IsShiftLeft()) {
+ next(); // Consume the peek
op = ast::BinaryOp::kShiftLeft;
name = "<<";
- } else if (t.IsGreaterThan() && t2.IsGreaterThan()) {
- next(); // Consume the t peek
- next(); // Consume the t2 peek
+ } else if (t.IsShiftRight()) {
+ next(); // Consume the peek
op = ast::BinaryOp::kShiftRight;
name = ">>";
} else {
@@ -3029,6 +3026,23 @@
return true;
}
+ // Special case to split `>>` and `>=` tokens if we are looking for a `>`.
+ if (tok == Token::Type::kGreaterThan &&
+ (t.IsShiftRight() || t.IsGreaterThanEqual())) {
+ next();
+
+ // Push the second character to the token queue.
+ auto source = t.source();
+ source.range.begin.column++;
+ if (t.IsShiftRight())
+ token_queue_.push_front(Token(Token::Type::kGreaterThan, source));
+ else if (t.IsGreaterThanEqual())
+ token_queue_.push_front(Token(Token::Type::kEqual, source));
+
+ synchronized_ = true;
+ return true;
+ }
+
std::stringstream err;
err << "expected '" << Token::TypeToName(tok) << "'";
if (!use.empty()) {
diff --git a/src/reader/wgsl/parser_impl_shift_expression_test.cc b/src/reader/wgsl/parser_impl_shift_expression_test.cc
index 0be6486..e93822e 100644
--- a/src/reader/wgsl/parser_impl_shift_expression_test.cc
+++ b/src/reader/wgsl/parser_impl_shift_expression_test.cc
@@ -71,6 +71,24 @@
ASSERT_TRUE(init->literal()->As<ast::BoolLiteral>()->IsTrue());
}
+TEST_F(ParserImplTest, ShiftExpression_InvalidSpaceLeft) {
+ auto p = parser("a < < true");
+ auto e = p->shift_expression();
+ EXPECT_TRUE(e.matched);
+ EXPECT_FALSE(e.errored);
+ ASSERT_NE(e.value, nullptr);
+ EXPECT_FALSE(e.value->Is<ast::BinaryExpression>());
+}
+
+TEST_F(ParserImplTest, ShiftExpression_InvalidSpaceRight) {
+ auto p = parser("a > > true");
+ auto e = p->shift_expression();
+ EXPECT_TRUE(e.matched);
+ EXPECT_FALSE(e.errored);
+ ASSERT_NE(e.value, nullptr);
+ EXPECT_FALSE(e.value->Is<ast::BinaryExpression>());
+}
+
TEST_F(ParserImplTest, ShiftExpression_InvalidLHS) {
auto p = parser("if (a) {} << true");
auto e = p->shift_expression();
diff --git a/src/reader/wgsl/parser_impl_type_decl_test.cc b/src/reader/wgsl/parser_impl_type_decl_test.cc
index 6771de3..c3c08e0 100644
--- a/src/reader/wgsl/parser_impl_type_decl_test.cc
+++ b/src/reader/wgsl/parser_impl_type_decl_test.cc
@@ -532,6 +532,20 @@
ASSERT_TRUE(a->type()->Is<type::U32>());
}
+TEST_F(ParserImplTest, TypeDecl_Array_Runtime_Vec) {
+ auto p = parser("array<vec4<u32>>");
+ 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->Is<type::Array>());
+
+ auto* a = t->As<type::Array>();
+ ASSERT_TRUE(a->IsRuntimeArray());
+ ASSERT_TRUE(a->type()->is_unsigned_integer_vector());
+}
+
TEST_F(ParserImplTest, TypeDecl_Array_BadType) {
auto p = parser("array<unknown, 3>");
auto t = p->type_decl();
diff --git a/src/reader/wgsl/parser_impl_variable_stmt_test.cc b/src/reader/wgsl/parser_impl_variable_stmt_test.cc
index 3560b5f..1a6ed5f 100644
--- a/src/reader/wgsl/parser_impl_variable_stmt_test.cc
+++ b/src/reader/wgsl/parser_impl_variable_stmt_test.cc
@@ -82,6 +82,66 @@
EXPECT_EQ(p->error(), "1:15: missing constructor for variable declaration");
}
+TEST_F(ParserImplTest, VariableStmt_VariableDecl_ArrayInit) {
+ auto p = parser("var a : array<i32> = array<i32>();");
+ auto e = p->variable_stmt();
+ 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::VariableDeclStatement>());
+ ASSERT_NE(e->variable(), nullptr);
+ EXPECT_EQ(e->variable()->symbol(), p->builder().Symbols().Get("a"));
+
+ ASSERT_NE(e->variable()->constructor(), nullptr);
+ EXPECT_TRUE(e->variable()->constructor()->Is<ast::ConstructorExpression>());
+}
+
+TEST_F(ParserImplTest, VariableStmt_VariableDecl_ArrayInit_NoSpace) {
+ auto p = parser("var a : array<i32>=array<i32>();");
+ auto e = p->variable_stmt();
+ 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::VariableDeclStatement>());
+ ASSERT_NE(e->variable(), nullptr);
+ EXPECT_EQ(e->variable()->symbol(), p->builder().Symbols().Get("a"));
+
+ ASSERT_NE(e->variable()->constructor(), nullptr);
+ EXPECT_TRUE(e->variable()->constructor()->Is<ast::ConstructorExpression>());
+}
+
+TEST_F(ParserImplTest, VariableStmt_VariableDecl_VecInit) {
+ auto p = parser("var a : vec2<i32> = vec2<i32>();");
+ auto e = p->variable_stmt();
+ 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::VariableDeclStatement>());
+ ASSERT_NE(e->variable(), nullptr);
+ EXPECT_EQ(e->variable()->symbol(), p->builder().Symbols().Get("a"));
+
+ ASSERT_NE(e->variable()->constructor(), nullptr);
+ EXPECT_TRUE(e->variable()->constructor()->Is<ast::ConstructorExpression>());
+}
+
+TEST_F(ParserImplTest, VariableStmt_VariableDecl_VecInit_NoSpace) {
+ auto p = parser("var a : vec2<i32>=vec2<i32>();");
+ auto e = p->variable_stmt();
+ 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::VariableDeclStatement>());
+ ASSERT_NE(e->variable(), nullptr);
+ EXPECT_EQ(e->variable()->symbol(), p->builder().Symbols().Get("a"));
+
+ ASSERT_NE(e->variable()->constructor(), nullptr);
+ EXPECT_TRUE(e->variable()->constructor()->Is<ast::ConstructorExpression>());
+}
+
TEST_F(ParserImplTest, VariableStmt_Const) {
auto p = parser("const a : i32 = 1");
auto e = p->variable_stmt();
diff --git a/src/reader/wgsl/token.cc b/src/reader/wgsl/token.cc
index 579ff07..5485484 100644
--- a/src/reader/wgsl/token.cc
+++ b/src/reader/wgsl/token.cc
@@ -74,10 +74,14 @@
return ">";
case Token::Type::kGreaterThanEqual:
return ">=";
+ case Token::Type::kShiftRight:
+ return ">>";
case Token::Type::kLessThan:
return "<";
case Token::Type::kLessThanEqual:
return "<=";
+ case Token::Type::kShiftLeft:
+ return "<<";
case Token::Type::kMod:
return "%";
case Token::Type::kNotEqual:
diff --git a/src/reader/wgsl/token.h b/src/reader/wgsl/token.h
index fdf3b2e..f90de40 100644
--- a/src/reader/wgsl/token.h
+++ b/src/reader/wgsl/token.h
@@ -85,10 +85,14 @@
kGreaterThan,
/// A '>='
kGreaterThanEqual,
+ /// A '>>'
+ kShiftRight,
/// A '<'
kLessThan,
/// A '<='
kLessThanEqual,
+ /// A '<<'
+ kShiftLeft,
/// A '%'
kMod,
/// A '-'
@@ -424,10 +428,14 @@
bool IsGreaterThan() const { return type_ == Type::kGreaterThan; }
/// @returns true if token is a '>='
bool IsGreaterThanEqual() const { return type_ == Type::kGreaterThanEqual; }
+ /// @returns true if token is a '>>'
+ bool IsShiftRight() const { return type_ == Type::kShiftRight; }
/// @returns true if token is a '<'
bool IsLessThan() const { return type_ == Type::kLessThan; }
/// @returns true if token is a '<='
bool IsLessThanEqual() const { return type_ == Type::kLessThanEqual; }
+ /// @returns true if token is a '<<'
+ bool IsShiftLeft() const { return type_ == Type::kShiftLeft; }
/// @returns true if token is a '%'
bool IsMod() const { return type_ == Type::kMod; }
/// @returns true if token is a '-'