wgsl-reader: Hex float exponents are optional

Fixed: tint:1210
Change-Id: I4256494e3ca3f98082f360e0447d0392964073bd
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/66040
Auto-Submit: David Neto <dneto@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
diff --git a/src/reader/wgsl/lexer.cc b/src/reader/wgsl/lexer.cc
index 4bcba2f..5437286 100644
--- a/src/reader/wgsl/lexer.cc
+++ b/src/reader/wgsl/lexer.cc
@@ -343,7 +343,9 @@
   }
 
   // .?
+  bool hex_point = false;
   if (matches(end, ".")) {
+    hex_point = true;
     end++;
   }
 
@@ -359,14 +361,18 @@
     return {};
   }
 
-  // (p|P)
-  if (matches(end, "p") || matches(end, "P")) {
+  // Is the binary exponent present?  It's optional.
+  const bool has_exponent = (matches(end, "p") || matches(end, "P"));
+  if (has_exponent) {
     end++;
-  } else {
+  }
+  if (!has_exponent && !hex_point) {
+    // It's not a hex float. At best it's a hex integer.
     return {};
   }
 
-  // At this point, we know for sure our token is a hex float value.
+  // At this point, we know for sure our token is a hex float value,
+  // or an invalid token.
 
   // Parse integer part
   // [0-9a-fA-F]*
@@ -423,42 +429,48 @@
     }
   }
 
-  // (+|-)?
-  int32_t exponent_sign = 1;
-  if (matches(end, "+")) {
-    end++;
-  } else if (matches(end, "-")) {
-    exponent_sign = -1;
-    end++;
-  }
-
-  // Determine if the value is zero.
+  // Determine if the value of the mantissa is zero.
   // Note: it's not enough to check mantissa == 0 as we drop the initial bit,
   // whether it's in the integer part or the fractional part.
   const bool is_zero = !seen_prior_one_bits;
   TINT_ASSERT(Reader, !is_zero || mantissa == 0);
 
-  // Parse exponent from input
-  // [0-9]+
-  // Allow overflow (in uint32_t) when the floating point value magnitude is
-  // zero.
-  bool has_exponent = false;
-  uint32_t input_exponent = 0;
-  while (end < len_ && isdigit(content_->data[end])) {
-    has_exponent = true;
-    auto prev_exponent = input_exponent;
-    input_exponent = (input_exponent * 10) + dec_value(content_->data[end]);
-    // Check if we've overflowed input_exponent. This only matters when
-    // the mantissa is non-zero.
-    if (!is_zero && (prev_exponent > input_exponent)) {
-      return {Token::Type::kError, source,
-              "exponent is too large for hex float"};
+  // Parse the optional exponent.
+  // ((p|P)(\+|-)?[0-9]+)?
+  uint32_t input_exponent = 0;  // Defaults to 0 if not present
+  int32_t exponent_sign = 1;
+  // If the 'p' part is present, the rest of the exponent must exist.
+  if (has_exponent) {
+    // Parse the rest of the exponent.
+    // (+|-)?
+    if (matches(end, "+")) {
+      end++;
+    } else if (matches(end, "-")) {
+      exponent_sign = -1;
+      end++;
     }
-    end++;
-  }
-  if (!has_exponent) {
-    return {Token::Type::kError, source,
-            "expected an exponent value for hex float"};
+
+    // Parse exponent from input
+    // [0-9]+
+    // Allow overflow (in uint32_t) when the floating point value magnitude is
+    // zero.
+    bool has_exponent_digits = false;
+    while (end < len_ && isdigit(content_->data[end])) {
+      has_exponent_digits = true;
+      auto prev_exponent = input_exponent;
+      input_exponent = (input_exponent * 10) + dec_value(content_->data[end]);
+      // Check if we've overflowed input_exponent. This only matters when
+      // the mantissa is non-zero.
+      if (!is_zero && (prev_exponent > input_exponent)) {
+        return {Token::Type::kError, source,
+                "exponent is too large for hex float"};
+      }
+      end++;
+    }
+    if (!has_exponent_digits) {
+      return {Token::Type::kError, source,
+              "expected an exponent value for hex float"};
+    }
   }
 
   pos_ = end;
diff --git a/src/reader/wgsl/parser_impl_const_literal_test.cc b/src/reader/wgsl/parser_impl_const_literal_test.cc
index 7c6de61..16e3472 100644
--- a/src/reader/wgsl/parser_impl_const_literal_test.cc
+++ b/src/reader/wgsl/parser_impl_const_literal_test.cc
@@ -275,6 +275,14 @@
     {"-0x123Ep+1", -9340.f},
     {"0x1a2b3cP12", 7.024656e+09f},
     {"-0x1a2b3cP12", -7.024656e+09f},
+
+    // Examples without a binary exponent part.
+    {"0x1.", 1.0f},
+    {"0x.8", 0.5f},
+    {"0x1.8", 1.5f},
+    {"-0x1.", -1.0f},
+    {"-0x.8", -0.5f},
+    {"-0x1.8", -1.5f},
 };
 INSTANTIATE_TEST_SUITE_P(ParserImplFloatLiteralTest_HexFloat,
                          ParserImplFloatLiteralTest,
@@ -326,9 +334,18 @@
     testing::ValuesIn(invalid_hexfloat_exponent_too_large_cases));
 
 InvalidLiteralTestCase invalid_hexfloat_exponent_missing_cases[] = {
+    // Lower case p
     {"0x0p", "1:1: expected an exponent value for hex float"},
+    {"0x0p+", "1:1: expected an exponent value for hex float"},
+    {"0x0p-", "1:1: expected an exponent value for hex float"},
     {"0x1.0p", "1:1: expected an exponent value for hex float"},
     {"0x0.1p", "1:1: expected an exponent value for hex float"},
+    // Upper case p
+    {"0x0P", "1:1: expected an exponent value for hex float"},
+    {"0x0P+", "1:1: expected an exponent value for hex float"},
+    {"0x0P-", "1:1: expected an exponent value for hex float"},
+    {"0x1.0P", "1:1: expected an exponent value for hex float"},
+    {"0x0.1P", "1:1: expected an exponent value for hex float"},
 };
 INSTANTIATE_TEST_SUITE_P(
     ParserImplInvalidLiteralTest_HexFloatExponentMissing,