reader/wgsl: Parse '&' and '*'

Bug: tint:727
Change-Id: I1a2c93017b934fd0884570412f5ed25bf15991bb
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/50820
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: David Neto <dneto@google.com>
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index 9aba063..663cf47 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -2315,28 +2315,34 @@
 //   : singular_expression
 //   | MINUS unary_expression
 //   | BANG unary_expression
+//   | STAR unary_expression
+//   | AND unary_expression
 Maybe<ast::Expression*> ParserImpl::unary_expression() {
   auto t = peek();
-  auto source = t.source();
-  if (t.IsMinus() || t.IsBang()) {
-    auto name = t.to_name();
 
-    next();  // Consume the peek
-
-    auto op = ast::UnaryOp::kNegation;
-    if (t.IsBang())
-      op = ast::UnaryOp::kNot;
-
-    auto expr = unary_expression();
-    if (expr.errored)
-      return Failure::kErrored;
-    if (!expr.matched)
-      return add_error(peek(),
-                       "unable to parse right side of " + name + " expression");
-
-    return create<ast::UnaryOpExpression>(source, op, expr.value);
+  ast::UnaryOp op;
+  if (match(Token::Type::kMinus)) {
+    op = ast::UnaryOp::kNegation;
+  } else if (match(Token::Type::kBang)) {
+    op = ast::UnaryOp::kNot;
+  } else if (match(Token::Type::kStar)) {
+    op = ast::UnaryOp::kDereference;
+  } else if (match(Token::Type::kAnd)) {
+    op = ast::UnaryOp::kAddressOf;
+  } else {
+    return singular_expression();
   }
-  return singular_expression();
+
+  auto expr = unary_expression();
+  if (expr.errored) {
+    return Failure::kErrored;
+  }
+  if (!expr.matched) {
+    return add_error(
+        peek(), "unable to parse right side of " + t.to_name() + " expression");
+  }
+
+  return create<ast::UnaryOpExpression>(t.source(), op, expr.value);
 }
 
 // multiplicative_expr
diff --git a/src/reader/wgsl/parser_impl_unary_expression_test.cc b/src/reader/wgsl/parser_impl_unary_expression_test.cc
index c971a1d..047060d 100644
--- a/src/reader/wgsl/parser_impl_unary_expression_test.cc
+++ b/src/reader/wgsl/parser_impl_unary_expression_test.cc
@@ -61,6 +61,62 @@
   EXPECT_EQ(init->literal()->As<ast::SintLiteral>()->value(), 1);
 }
 
+TEST_F(ParserImplTest, UnaryExpression_AddressOf) {
+  auto p = parser("&x");
+  auto e = p->unary_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::UnaryOpExpression>());
+
+  auto* u = e->As<ast::UnaryOpExpression>();
+  EXPECT_EQ(u->op(), ast::UnaryOp::kAddressOf);
+  EXPECT_TRUE(u->expr()->Is<ast::IdentifierExpression>());
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dereference) {
+  auto p = parser("*x");
+  auto e = p->unary_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::UnaryOpExpression>());
+
+  auto* u = e->As<ast::UnaryOpExpression>();
+  EXPECT_EQ(u->op(), ast::UnaryOp::kDereference);
+  EXPECT_TRUE(u->expr()->Is<ast::IdentifierExpression>());
+}
+
+TEST_F(ParserImplTest, UnaryExpression_AddressOf_Precedence) {
+  auto p = parser("&x.y");
+  auto e = p->logical_or_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::UnaryOpExpression>());
+
+  auto* u = e->As<ast::UnaryOpExpression>();
+  EXPECT_EQ(u->op(), ast::UnaryOp::kAddressOf);
+  EXPECT_TRUE(u->expr()->Is<ast::MemberAccessorExpression>());
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dereference_Precedence) {
+  auto p = parser("*x.y");
+  auto e = p->logical_or_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::UnaryOpExpression>());
+
+  auto* u = e->As<ast::UnaryOpExpression>();
+  EXPECT_EQ(u->op(), ast::UnaryOp::kDereference);
+  EXPECT_TRUE(u->expr()->Is<ast::MemberAccessorExpression>());
+}
+
 TEST_F(ParserImplTest, UnaryExpression_Minus_InvalidRHS) {
   auto p = parser("-if(a) {}");
   auto e = p->unary_expression();