| // Copyright 2020 The Tint Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "src/reader/wgsl/parser_impl_test_helper.h" |
| |
| #include <cmath> |
| #include <cstring> |
| |
| namespace tint { |
| namespace reader { |
| namespace wgsl { |
| namespace { |
| |
| // Makes an IEEE 754 binary32 floating point number with |
| // - 0 sign if sign is 0, 1 otherwise |
| // - 'exponent_bits' is placed in the exponent space. |
| // So, the exponent bias must already be included. |
| float MakeFloat(int sign, int biased_exponent, int mantissa) { |
| const uint32_t sign_bit = sign ? 0x80000000u : 0u; |
| // The binary32 exponent is 8 bits, just below the sign. |
| const uint32_t exponent_bits = (biased_exponent & 0xffu) << 23; |
| // The mantissa is the bottom 23 bits. |
| const uint32_t mantissa_bits = (mantissa & 0x7fffffu); |
| |
| uint32_t bits = sign_bit | exponent_bits | mantissa_bits; |
| float result = 0.0f; |
| static_assert(sizeof(result) == sizeof(bits), |
| "expected float and uint32_t to be the same size"); |
| std::memcpy(&result, &bits, sizeof(bits)); |
| return result; |
| } |
| |
| TEST_F(ParserImplTest, ConstLiteral_Int) { |
| auto p = parser("-234"); |
| auto c = p->const_literal(); |
| EXPECT_TRUE(c.matched); |
| EXPECT_FALSE(c.errored); |
| EXPECT_FALSE(p->has_error()) << p->error(); |
| ASSERT_NE(c.value, nullptr); |
| ASSERT_TRUE(c->Is<ast::SintLiteralExpression>()); |
| EXPECT_EQ(c->As<ast::SintLiteralExpression>()->value, -234); |
| EXPECT_EQ(c->source.range, (Source::Range{{1u, 1u}, {1u, 5u}})); |
| } |
| |
| TEST_F(ParserImplTest, ConstLiteral_Uint) { |
| auto p = parser("234u"); |
| auto c = p->const_literal(); |
| EXPECT_TRUE(c.matched); |
| EXPECT_FALSE(c.errored); |
| EXPECT_FALSE(p->has_error()) << p->error(); |
| ASSERT_NE(c.value, nullptr); |
| ASSERT_TRUE(c->Is<ast::UintLiteralExpression>()); |
| EXPECT_EQ(c->As<ast::UintLiteralExpression>()->value, 234u); |
| EXPECT_EQ(c->source.range, (Source::Range{{1u, 1u}, {1u, 5u}})); |
| } |
| |
| TEST_F(ParserImplTest, ConstLiteral_Float) { |
| auto p = parser("234.e12"); |
| auto c = p->const_literal(); |
| EXPECT_TRUE(c.matched); |
| EXPECT_FALSE(c.errored); |
| EXPECT_FALSE(p->has_error()) << p->error(); |
| ASSERT_NE(c.value, nullptr); |
| ASSERT_TRUE(c->Is<ast::FloatLiteralExpression>()); |
| EXPECT_FLOAT_EQ(c->As<ast::FloatLiteralExpression>()->value, 234e12f); |
| EXPECT_EQ(c->source.range, (Source::Range{{1u, 1u}, {1u, 8u}})); |
| } |
| |
| 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 (positive)"); |
| ASSERT_EQ(c.value, nullptr); |
| } |
| |
| struct FloatLiteralTestCase { |
| const char* input; |
| float expected; |
| }; |
| |
| inline std::ostream& operator<<(std::ostream& out, FloatLiteralTestCase data) { |
| out << data.input; |
| return out; |
| } |
| |
| class ParserImplFloatLiteralTest |
| : public ParserImplTestWithParam<FloatLiteralTestCase> {}; |
| TEST_P(ParserImplFloatLiteralTest, Parse) { |
| auto params = GetParam(); |
| SCOPED_TRACE(params.input); |
| auto p = parser(params.input); |
| auto c = p->const_literal(); |
| EXPECT_TRUE(c.matched); |
| EXPECT_FALSE(c.errored); |
| EXPECT_FALSE(p->has_error()) << p->error(); |
| ASSERT_NE(c.value, nullptr); |
| ASSERT_TRUE(c->Is<ast::FloatLiteralExpression>()); |
| EXPECT_FLOAT_EQ(c->As<ast::FloatLiteralExpression>()->value, params.expected); |
| } |
| |
| FloatLiteralTestCase float_literal_test_cases[] = { |
| {"0.0", 0.0f}, // Zero |
| {"1.0", 1.0f}, // One |
| {"-1.0", -1.0f}, // MinusOne |
| {"1000000000.0", 1e9f}, // Billion |
| {"-0.0", std::copysign(0.0f, -5.0f)}, // NegativeZero |
| {"0.0", MakeFloat(0, 0, 0)}, // Zero |
| {"-0.0", MakeFloat(1, 0, 0)}, // NegativeZero |
| {"1.0", MakeFloat(0, 127, 0)}, // One |
| {"-1.0", MakeFloat(1, 127, 0)}, // NegativeOne |
| }; |
| INSTANTIATE_TEST_SUITE_P(ParserImplFloatLiteralTest_Float, |
| ParserImplFloatLiteralTest, |
| testing::ValuesIn(float_literal_test_cases)); |
| |
| const float NegInf = MakeFloat(1, 255, 0); |
| const float PosInf = MakeFloat(0, 255, 0); |
| FloatLiteralTestCase hexfloat_literal_test_cases[] = { |
| // Regular numbers |
| {"0x0p+0", 0.f}, |
| {"0x1p+0", 1.f}, |
| {"0x1p+1", 2.f}, |
| {"0x1.8p+1", 3.f}, |
| {"0x1.99999ap-4", 0.1f}, |
| {"0x1p-1", 0.5f}, |
| {"0x1p-2", 0.25f}, |
| {"0x1.8p-1", 0.75f}, |
| {"-0x0p+0", -0.f}, |
| {"-0x1p+0", -1.f}, |
| {"-0x1p-1", -0.5f}, |
| {"-0x1p-2", -0.25f}, |
| {"-0x1.8p-1", -0.75f}, |
| |
| // Large numbers |
| {"0x1p+9", 512.f}, |
| {"0x1p+10", 1024.f}, |
| {"0x1.02p+10", 1024.f + 8.f}, |
| {"-0x1p+9", -512.f}, |
| {"-0x1p+10", -1024.f}, |
| {"-0x1.02p+10", -1024.f - 8.f}, |
| |
| // Small numbers |
| {"0x1p-9", 1.0f / 512.f}, |
| {"0x1p-10", 1.0f / 1024.f}, |
| {"0x1.02p-3", 1.0f / 1024.f + 1.0f / 8.f}, |
| {"-0x1p-9", 1.0f / -512.f}, |
| {"-0x1p-10", 1.0f / -1024.f}, |
| {"-0x1.02p-3", 1.0f / -1024.f - 1.0f / 8.f}, |
| |
| // Near lowest non-denorm |
| {"0x1p-124", std::ldexp(1.f * 8.f, -127)}, |
| {"0x1p-125", std::ldexp(1.f * 4.f, -127)}, |
| {"-0x1p-124", -std::ldexp(1.f * 8.f, -127)}, |
| {"-0x1p-125", -std::ldexp(1.f * 4.f, -127)}, |
| |
| // Lowest non-denorm |
| {"0x1p-126", std::ldexp(1.f * 2.f, -127)}, |
| {"-0x1p-126", -std::ldexp(1.f * 2.f, -127)}, |
| |
| // Denormalized values |
| {"0x1p-127", std::ldexp(1.f, -127)}, |
| {"0x1p-128", std::ldexp(1.f / 2.f, -127)}, |
| {"0x1p-129", std::ldexp(1.f / 4.f, -127)}, |
| {"0x1p-130", std::ldexp(1.f / 8.f, -127)}, |
| {"-0x1p-127", -std::ldexp(1.f, -127)}, |
| {"-0x1p-128", -std::ldexp(1.f / 2.f, -127)}, |
| {"-0x1p-129", -std::ldexp(1.f / 4.f, -127)}, |
| {"-0x1p-130", -std::ldexp(1.f / 8.f, -127)}, |
| |
| {"0x1.8p-127", std::ldexp(1.f, -127) + (std::ldexp(1.f, -127) / 2.f)}, |
| {"0x1.8p-128", std::ldexp(1.f, -127) / 2.f + (std::ldexp(1.f, -127) / 4.f)}, |
| |
| {"0x1p-149", MakeFloat(0, 0, 1)}, // +SmallestDenormal |
| {"0x1p-148", MakeFloat(0, 0, 2)}, // +BiggerDenormal |
| {"0x1.fffffcp-127", MakeFloat(0, 0, 0x7fffff)}, // +LargestDenormal |
| {"-0x1p-149", MakeFloat(1, 0, 1)}, // -SmallestDenormal |
| {"-0x1p-148", MakeFloat(1, 0, 2)}, // -BiggerDenormal |
| {"-0x1.fffffcp-127", MakeFloat(1, 0, 0x7fffff)}, // -LargestDenormal |
| |
| {"0x1.2bfaf8p-127", MakeFloat(0, 0, 0xcafebe)}, // +Subnormal |
| {"-0x1.2bfaf8p-127", MakeFloat(1, 0, 0xcafebe)}, // -Subnormal |
| {"0x1.55554p-130", MakeFloat(0, 0, 0xaaaaa)}, // +Subnormal |
| {"-0x1.55554p-130", MakeFloat(1, 0, 0xaaaaa)}, // -Subnormal |
| |
| // Nan -> Infinity |
| {"0x1.8p+128", PosInf}, |
| {"0x1.0002p+128", PosInf}, |
| {"0x1.0018p+128", PosInf}, |
| {"0x1.01ep+128", PosInf}, |
| {"0x1.fffffep+128", PosInf}, |
| {"-0x1.8p+128", NegInf}, |
| {"-0x1.0002p+128", NegInf}, |
| {"-0x1.0018p+128", NegInf}, |
| {"-0x1.01ep+128", NegInf}, |
| {"-0x1.fffffep+128", NegInf}, |
| |
| // Infinity |
| {"0x1p+128", PosInf}, |
| {"-0x1p+128", NegInf}, |
| {"0x32p+127", PosInf}, |
| {"0x32p+500", PosInf}, |
| {"-0x32p+127", NegInf}, |
| {"-0x32p+500", NegInf}, |
| |
| // Overflow -> Infinity |
| {"0x1p+129", PosInf}, |
| {"0x1.1p+128", PosInf}, |
| {"-0x1p+129", NegInf}, |
| {"-0x1.1p+128", NegInf}, |
| {"0x1.0p2147483520", PosInf}, // INT_MAX - 127 (largest valid exponent) |
| |
| // Underflow -> Zero |
| {"0x1p-500", 0.f}, // Exponent underflows |
| {"-0x1p-500", -0.f}, |
| {"0x0.00000000001p-126", 0.f}, // Fraction causes underflow |
| {"-0x0.0000000001p-127", -0.f}, |
| {"0x0.01p-142", 0.f}, |
| {"-0x0.01p-142", -0.f}, // Fraction causes additional underflow |
| {"0x1.0p-2147483520", 0}, // -(INT_MAX - 127) (smallest valid exponent) |
| |
| // Zero with non-zero exponent -> Zero |
| {"0x0p+0", 0.f}, |
| {"0x0p+1", 0.f}, |
| {"0x0p-1", 0.f}, |
| {"0x0p+9999999999", 0.f}, |
| {"0x0p-9999999999", 0.f}, |
| // Same, but with very large positive exponents that would cause overflow |
| // if the mantissa were non-zero. |
| {"0x0p+4000000000", 0.f}, // 4 billion: |
| {"0x0p+40000000000", 0.f}, // 40 billion |
| {"-0x0p+40000000000", 0.f}, // As above 2, but negative mantissa |
| {"-0x0p+400000000000", 0.f}, |
| {"0x0.00p+4000000000", 0.f}, // As above 4, but with fractional part |
| {"0x0.00p+40000000000", 0.f}, |
| {"-0x0.00p+40000000000", 0.f}, |
| {"-0x0.00p+400000000000", 0.f}, |
| {"0x0p-4000000000", 0.f}, // As above 8, but with negative exponents |
| {"0x0p-40000000000", 0.f}, |
| {"-0x0p-40000000000", 0.f}, |
| {"-0x0p-400000000000", 0.f}, |
| {"0x0.00p-4000000000", 0.f}, |
| {"0x0.00p-40000000000", 0.f}, |
| {"-0x0.00p-40000000000", 0.f}, |
| {"-0x0.00p-400000000000", 0.f}, |
| |
| // Test parsing |
| {"0x0p0", 0.f}, |
| {"0x0p-0", 0.f}, |
| {"0x0p+000", 0.f}, |
| {"0x00000000000000p+000000000000000", 0.f}, |
| {"0x00000000000000p-000000000000000", 0.f}, |
| {"0x00000000000001p+000000000000000", 1.f}, |
| {"0x00000000000001p-000000000000000", 1.f}, |
| {"0x0000000000000000000001.99999ap-000000000000000004", 0.1f}, |
| {"0x2p+0", 2.f}, |
| {"0xFFp+0", 255.f}, |
| {"0x0.8p+0", 0.5f}, |
| {"0x0.4p+0", 0.25f}, |
| {"0x0.4p+1", 2 * 0.25f}, |
| {"0x0.4p+2", 4 * 0.25f}, |
| {"0x123Ep+1", 9340.f}, |
| {"-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}, |
| |
| // Examples with a binary exponent and a 'f' suffix. |
| {"0x1.p0f", 1.0f}, |
| {"0x.8p2f", 2.0f}, |
| {"0x1.8p-1f", 0.75f}, |
| {"0x2p-2f", 0.5f}, // No binary point |
| {"-0x1.p0f", -1.0f}, |
| {"-0x.8p2f", -2.0f}, |
| {"-0x1.8p-1f", -0.75f}, |
| {"-0x2p-2f", -0.5f}, // No binary point |
| }; |
| INSTANTIATE_TEST_SUITE_P(ParserImplFloatLiteralTest_HexFloat, |
| ParserImplFloatLiteralTest, |
| testing::ValuesIn(hexfloat_literal_test_cases)); |
| |
| struct InvalidLiteralTestCase { |
| const char* input; |
| const char* error_msg; |
| }; |
| class ParserImplInvalidLiteralTest |
| : public ParserImplTestWithParam<InvalidLiteralTestCase> {}; |
| TEST_P(ParserImplInvalidLiteralTest, Parse) { |
| auto params = GetParam(); |
| SCOPED_TRACE(params.input); |
| auto p = parser(params.input); |
| auto c = p->const_literal(); |
| EXPECT_FALSE(c.matched); |
| EXPECT_TRUE(c.errored); |
| EXPECT_EQ(p->error(), params.error_msg); |
| ASSERT_EQ(c.value, nullptr); |
| } |
| |
| InvalidLiteralTestCase invalid_hexfloat_mantissa_too_large_cases[] = { |
| {"0x1.ffffffff8p0", "1:1: mantissa is too large for hex float"}, |
| {"0x1f.fffffff8p0", "1:1: mantissa is too large for hex float"}, |
| {"0x1ff.ffffff8p0", "1:1: mantissa is too large for hex float"}, |
| {"0x1fff.fffff8p0", "1:1: mantissa is too large for hex float"}, |
| {"0x1ffff.ffff8p0", "1:1: mantissa is too large for hex float"}, |
| {"0x1fffff.fff8p0", "1:1: mantissa is too large for hex float"}, |
| {"0x1ffffff.ff8p0", "1:1: mantissa is too large for hex float"}, |
| {"0x1fffffff.f8p0", "1:1: mantissa is too large for hex float"}, |
| {"0x1ffffffff.8p0", "1:1: mantissa is too large for hex float"}, |
| {"0x1ffffffff8.p0", "1:1: mantissa is too large for hex float"}, |
| }; |
| INSTANTIATE_TEST_SUITE_P( |
| ParserImplInvalidLiteralTest_HexFloatMantissaTooLarge, |
| ParserImplInvalidLiteralTest, |
| testing::ValuesIn(invalid_hexfloat_mantissa_too_large_cases)); |
| |
| InvalidLiteralTestCase invalid_hexfloat_exponent_too_large_cases[] = { |
| {"0x1p+2147483521", "1:1: exponent is too large for hex float"}, |
| {"0x1p-2147483521", "1:1: exponent is too large for hex float"}, |
| {"0x1p+4294967296", "1:1: exponent is too large for hex float"}, |
| {"0x1p-4294967296", "1:1: exponent is too large for hex float"}, |
| }; |
| INSTANTIATE_TEST_SUITE_P( |
| ParserImplInvalidLiteralTest_HexFloatExponentTooLarge, |
| ParserImplInvalidLiteralTest, |
| 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, |
| ParserImplInvalidLiteralTest, |
| testing::ValuesIn(invalid_hexfloat_exponent_missing_cases)); |
| |
| TEST_F(ParserImplTest, ConstLiteral_FloatHighest) { |
| const auto highest = std::numeric_limits<float>::max(); |
| const auto expected_highest = 340282346638528859811704183484516925440.0f; |
| if (highest < expected_highest || highest > expected_highest) { |
| GTEST_SKIP() << "std::numeric_limits<float>::max() is not as expected for " |
| "this target"; |
| } |
| auto p = parser("340282346638528859811704183484516925440.0"); |
| auto c = p->const_literal(); |
| EXPECT_TRUE(c.matched); |
| EXPECT_FALSE(c.errored); |
| EXPECT_FALSE(p->has_error()) << p->error(); |
| ASSERT_NE(c.value, nullptr); |
| ASSERT_TRUE(c->Is<ast::FloatLiteralExpression>()); |
| EXPECT_FLOAT_EQ(c->As<ast::FloatLiteralExpression>()->value, |
| std::numeric_limits<float>::max()); |
| EXPECT_EQ(c->source.range, (Source::Range{{1u, 1u}, {1u, 42u}})); |
| } |
| |
| TEST_F(ParserImplTest, ConstLiteral_FloatLowest) { |
| // Some compilers complain if you test floating point numbers for equality. |
| // So say it via two inequalities. |
| const auto lowest = std::numeric_limits<float>::lowest(); |
| const auto expected_lowest = -340282346638528859811704183484516925440.0f; |
| if (lowest < expected_lowest || lowest > expected_lowest) { |
| GTEST_SKIP() |
| << "std::numeric_limits<float>::lowest() is not as expected for " |
| "this target"; |
| } |
| |
| auto p = parser("-340282346638528859811704183484516925440.0"); |
| auto c = p->const_literal(); |
| EXPECT_TRUE(c.matched); |
| EXPECT_FALSE(c.errored); |
| EXPECT_FALSE(p->has_error()) << p->error(); |
| ASSERT_NE(c.value, nullptr); |
| ASSERT_TRUE(c->Is<ast::FloatLiteralExpression>()); |
| EXPECT_FLOAT_EQ(c->As<ast::FloatLiteralExpression>()->value, |
| std::numeric_limits<float>::lowest()); |
| EXPECT_EQ(c->source.range, (Source::Range{{1u, 1u}, {1u, 43u}})); |
| } |
| |
| TEST_F(ParserImplTest, ConstLiteral_True) { |
| auto p = parser("true"); |
| auto c = p->const_literal(); |
| EXPECT_TRUE(c.matched); |
| EXPECT_FALSE(c.errored); |
| EXPECT_FALSE(p->has_error()) << p->error(); |
| ASSERT_NE(c.value, nullptr); |
| ASSERT_TRUE(c->Is<ast::BoolLiteralExpression>()); |
| EXPECT_TRUE(c->As<ast::BoolLiteralExpression>()->value); |
| EXPECT_EQ(c->source.range, (Source::Range{{1u, 1u}, {1u, 5u}})); |
| } |
| |
| TEST_F(ParserImplTest, ConstLiteral_False) { |
| auto p = parser("false"); |
| auto c = p->const_literal(); |
| EXPECT_TRUE(c.matched); |
| EXPECT_FALSE(c.errored); |
| EXPECT_FALSE(p->has_error()) << p->error(); |
| ASSERT_NE(c.value, nullptr); |
| ASSERT_TRUE(c->Is<ast::BoolLiteralExpression>()); |
| EXPECT_FALSE(c->As<ast::BoolLiteralExpression>()->value); |
| EXPECT_EQ(c->source.range, (Source::Range{{1u, 1u}, {1u, 6u}})); |
| } |
| |
| TEST_F(ParserImplTest, ConstLiteral_NoMatch) { |
| auto p = parser("another-token"); |
| auto c = p->const_literal(); |
| EXPECT_FALSE(c.matched); |
| EXPECT_FALSE(c.errored); |
| EXPECT_FALSE(p->has_error()) << p->error(); |
| ASSERT_EQ(c.value, nullptr); |
| } |
| |
| } // namespace |
| } // namespace wgsl |
| } // namespace reader |
| } // namespace tint |