wgsl: decimal float: point is optional; can use E for exponent

Also: Check for too-small non-zero magnitude on negative numbers too.

Fixes: tint:1255
Change-Id: I3c231ce197c56e1ec517708b8073d49a8ae67ccb
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/67100
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Commit-Queue: David Neto <dneto@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Auto-Submit: David Neto <dneto@google.com>
This commit is contained in:
David Neto 2021-10-20 22:07:43 +00:00 committed by Tint LUCI CQ
parent e81d7dc3c8
commit 347c74e671
3 changed files with 106 additions and 26 deletions

View File

@ -210,43 +210,58 @@ Token Lexer::try_float() {
auto end = pos_; auto end = pos_;
auto source = begin_source(); auto source = begin_source();
bool has_mantissa_digits = false;
if (matches(end, "-")) { if (matches(end, "-")) {
end++; end++;
} }
while (end < len_ && is_digit(content_->data[end])) { while (end < len_ && is_digit(content_->data[end])) {
has_mantissa_digits = true;
end++; end++;
} }
if (end >= len_ || !matches(end, ".")) { bool has_point = false;
return {}; if (end < len_ && matches(end, ".")) {
has_point = true;
end++;
} }
end++;
while (end < len_ && is_digit(content_->data[end])) { while (end < len_ && is_digit(content_->data[end])) {
has_mantissa_digits = true;
end++; end++;
} }
if (!has_mantissa_digits) {
return {};
}
// Parse the exponent if one exists // Parse the exponent if one exists
if (end < len_ && matches(end, "e")) { bool has_exponent = false;
if (end < len_ && (matches(end, "e") || matches(end, "E"))) {
end++; end++;
if (end < len_ && (matches(end, "+") || matches(end, "-"))) { if (end < len_ && (matches(end, "+") || matches(end, "-"))) {
end++; end++;
} }
auto exp_start = end;
while (end < len_ && isdigit(content_->data[end])) { while (end < len_ && isdigit(content_->data[end])) {
has_exponent = true;
end++; end++;
} }
// Must have an exponent // If an 'e' or 'E' was present, then the number part must also be present.
if (exp_start == end) if (!has_exponent) {
return {}; const auto str = content_->data.substr(start, end - start);
return {Token::Type::kError, source,
"incomplete exponent for floating point literal: " + str};
}
} }
auto str = content_->data.substr(start, end - start); if (!has_point && !has_exponent) {
if (str == "." || str == "-.") // If it only has digits then it's an integer.
return {}; return {};
}
const auto str = content_->data.substr(start, end - start);
pos_ = end; pos_ = end;
location_.column += (end - start); location_.column += (end - start);
@ -254,16 +269,22 @@ Token Lexer::try_float() {
end_source(source); end_source(source);
auto res = strtod(content_->data.c_str() + start, nullptr); auto res = strtod(content_->data.c_str() + start, nullptr);
// This handles if the number is a really small in the exponent // This errors out if a non-zero magnitude is too small to represent in a
if (res > 0 && res < static_cast<double>(std::numeric_limits<float>::min())) { // float. It can't be represented faithfully in an f32.
return {Token::Type::kError, source, "f32 (" + str + " too small"}; const auto magnitude = std::fabs(res);
if (0.0 < magnitude &&
magnitude < static_cast<double>(std::numeric_limits<float>::min())) {
return {Token::Type::kError, source,
"f32 (" + str + ") magnitude too small, not representable"};
} }
// This handles if the number is really large negative number // This handles if the number is really large negative number
if (res < static_cast<double>(std::numeric_limits<float>::lowest())) { if (res < static_cast<double>(std::numeric_limits<float>::lowest())) {
return {Token::Type::kError, source, "f32 (" + str + ") too small"}; return {Token::Type::kError, source,
"f32 (" + str + ") too large (negative)"};
} }
if (res > static_cast<double>(std::numeric_limits<float>::max())) { if (res > static_cast<double>(std::numeric_limits<float>::max())) {
return {Token::Type::kError, source, "f32 (" + str + ") too large"}; return {Token::Type::kError, source,
"f32 (" + str + ") too large (positive)"};
} }
return {source, static_cast<float>(res)}; return {source, static_cast<float>(res)};

View File

@ -149,6 +149,12 @@ INSTANTIATE_TEST_SUITE_P(LexerTest,
FloatData{"-5.7", -5.7f}, FloatData{"-5.7", -5.7f},
FloatData{"-5.", -5.f}, FloatData{"-5.", -5.f},
FloatData{"-.7", -.7f}, FloatData{"-.7", -.7f},
// No decimal, with exponent
FloatData{"1e5", 1e5f},
FloatData{"1E5", 1e5f},
FloatData{"1e-5", 1e-5f},
FloatData{"1E-5", 1e-5f},
// With decimal and exponents
FloatData{"0.2e+12", 0.2e12f}, FloatData{"0.2e+12", 0.2e12f},
FloatData{"1.2e-5", 1.2e-5f}, FloatData{"1.2e-5", 1.2e-5f},
FloatData{"2.57e23", 2.57e23f}, FloatData{"2.57e23", 2.57e23f},
@ -163,15 +169,39 @@ TEST_P(FloatTest_Invalid, Handles) {
auto t = l.next(); auto t = l.next();
EXPECT_FALSE(t.Is(Token::Type::kFloatLiteral)); EXPECT_FALSE(t.Is(Token::Type::kFloatLiteral));
} }
INSTANTIATE_TEST_SUITE_P(LexerTest, INSTANTIATE_TEST_SUITE_P(
FloatTest_Invalid, LexerTest,
testing::Values(".", FloatTest_Invalid,
"-.", testing::Values(".",
"2.5e+256", "-.",
"-2.5e+127", // Need a mantissa digit
"2.5e-300", ".e5",
"2.5e 12", ".E5",
"2.5e+ 123")); // Need exponent digits
".e",
".e+",
".e-",
".E",
".e+",
".e-",
// Overflow
"2.5e+256",
"-2.5e+127",
// Magnitude smaller than smallest positive f32.
"2.5e-300",
"-2.5e-300",
// Decimal exponent must immediately
// follow the 'e'.
"2.5e 12",
"2.5e +12",
"2.5e -12",
"2.5e+ 123",
"2.5e- 123",
"2.5E 12",
"2.5E +12",
"2.5E -12",
"2.5E+ 123",
"2.5E- 123"));
using IdentifierTest = testing::TestWithParam<const char*>; using IdentifierTest = testing::TestWithParam<const char*>;
TEST_P(IdentifierTest, Parse) { TEST_P(IdentifierTest, Parse) {

View File

@ -77,12 +77,41 @@ TEST_F(ParserImplTest, ConstLiteral_Float) {
EXPECT_EQ(c->source.range, (Source::Range{{1u, 1u}, {1u, 8u}})); EXPECT_EQ(c->source.range, (Source::Range{{1u, 1u}, {1u, 8u}}));
} }
TEST_F(ParserImplTest, ConstLiteral_InvalidFloat) { TEST_F(ParserImplTest, ConstLiteral_InvalidFloat_IncompleteExponent) {
auto p = parser("1.0e+");
auto c = p->const_literal();
EXPECT_FALSE(c.matched);
EXPECT_TRUE(c.errored);
EXPECT_EQ(p->error(),
"1:1: incomplete exponent for floating point literal: 1.0e+");
ASSERT_EQ(c.value, nullptr);
}
TEST_F(ParserImplTest, ConstLiteral_InvalidFloat_TooSmallMagnitude) {
auto p = parser("1e-256");
auto c = p->const_literal();
EXPECT_FALSE(c.matched);
EXPECT_TRUE(c.errored);
EXPECT_EQ(p->error(),
"1:1: f32 (1e-256) magnitude too small, not representable");
ASSERT_EQ(c.value, nullptr);
}
TEST_F(ParserImplTest, ConstLiteral_InvalidFloat_TooLargeNegative) {
auto p = parser("-1.2e+256");
auto c = p->const_literal();
EXPECT_FALSE(c.matched);
EXPECT_TRUE(c.errored);
EXPECT_EQ(p->error(), "1:1: f32 (-1.2e+256) too large (negative)");
ASSERT_EQ(c.value, nullptr);
}
TEST_F(ParserImplTest, ConstLiteral_InvalidFloat_TooLargePositive) {
auto p = parser("1.2e+256"); auto p = parser("1.2e+256");
auto c = p->const_literal(); auto c = p->const_literal();
EXPECT_FALSE(c.matched); EXPECT_FALSE(c.matched);
EXPECT_TRUE(c.errored); EXPECT_TRUE(c.errored);
EXPECT_EQ(p->error(), "1:1: f32 (1.2e+256) too large"); EXPECT_EQ(p->error(), "1:1: f32 (1.2e+256) too large (positive)");
ASSERT_EQ(c.value, nullptr); ASSERT_EQ(c.value, nullptr);
} }