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

View File

@ -149,6 +149,12 @@ INSTANTIATE_TEST_SUITE_P(LexerTest,
FloatData{"-5.7", -5.7f},
FloatData{"-5.", -5.f},
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{"1.2e-5", 1.2e-5f},
FloatData{"2.57e23", 2.57e23f},
@ -163,15 +169,39 @@ TEST_P(FloatTest_Invalid, Handles) {
auto t = l.next();
EXPECT_FALSE(t.Is(Token::Type::kFloatLiteral));
}
INSTANTIATE_TEST_SUITE_P(LexerTest,
INSTANTIATE_TEST_SUITE_P(
LexerTest,
FloatTest_Invalid,
testing::Values(".",
"-.",
// Need a mantissa digit
".e5",
".E5",
// 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+ 123"));
"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*>;
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}}));
}
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 c = p->const_literal();
EXPECT_FALSE(c.matched);
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);
}