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:
parent
e81d7dc3c8
commit
347c74e671
|
@ -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)};
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue