blob: 941e8f59ef60a2c55c007de84fb1e5e67d65c5ce [file] [log] [blame]
// 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::SintLiteral>());
EXPECT_EQ(c->As<ast::SintLiteral>()->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::UintLiteral>());
EXPECT_EQ(c->As<ast::UintLiteral>()->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::FloatLiteral>());
EXPECT_FLOAT_EQ(c->As<ast::FloatLiteral>()->value(), 234e12f);
EXPECT_EQ(c->source().range, (Source::Range{{1u, 1u}, {1u, 8u}}));
}
TEST_F(ParserImplTest, ConstLiteral_InvalidFloat) {
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");
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::FloatLiteral>());
EXPECT_FLOAT_EQ(c->As<ast::FloatLiteral>()->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},
// 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
// 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},
// 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},
};
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[] = {
{"0x0p+4294967296", "1:1: exponent is too large for hex float"},
{"0x0p-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[] = {
{"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::FloatLiteral>());
EXPECT_FLOAT_EQ(c->As<ast::FloatLiteral>()->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::FloatLiteral>());
EXPECT_FLOAT_EQ(c->As<ast::FloatLiteral>()->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::BoolLiteral>());
EXPECT_TRUE(c->As<ast::BoolLiteral>()->IsTrue());
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::BoolLiteral>());
EXPECT_TRUE(c->As<ast::BoolLiteral>()->IsFalse());
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