blob: dcd57238f14cfe7389a8b7088d043ee2c89fa555 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/developer/debug/zxdb/expr/number_parser.h"
#include <string.h>
#include <variant>
#include <gtest/gtest.h>
#include "src/developer/debug/zxdb/expr/expr_token.h"
#include "src/developer/debug/zxdb/expr/expr_value.h"
#include "src/developer/debug/zxdb/symbols/base_type.h"
namespace zxdb {
TEST(NumberParser, ExtractIntegerPrefix) {
struct PrefixCase {
const char* input; // Input with prefix.
const char* num; // Number without prefix.
IntegerPrefix::Sign sign;
IntegerPrefix::Base base;
IntegerPrefix::OctalType oct;
} kCases[] = {
// clang-format off
// input num sign base oct
{"", "", IntegerPrefix::kPositive, IntegerPrefix::kDec, IntegerPrefix::OctalType::kC},
{"-", "", IntegerPrefix::kNegative, IntegerPrefix::kDec, IntegerPrefix::OctalType::kC},
{"76", "76", IntegerPrefix::kPositive, IntegerPrefix::kDec, IntegerPrefix::OctalType::kC},
{"- 76", "76", IntegerPrefix::kNegative, IntegerPrefix::kDec, IntegerPrefix::OctalType::kC},
{"0b101", "101", IntegerPrefix::kPositive, IntegerPrefix::kBin, IntegerPrefix::OctalType::kC},
{"-0b101", "101", IntegerPrefix::kNegative, IntegerPrefix::kBin, IntegerPrefix::OctalType::kC},
{"0xabc", "abc", IntegerPrefix::kPositive, IntegerPrefix::kHex, IntegerPrefix::OctalType::kC},
{"0o123", "123", IntegerPrefix::kPositive, IntegerPrefix::kOct, IntegerPrefix::OctalType::kRust},
{"0", "0", IntegerPrefix::kPositive, IntegerPrefix::kDec, IntegerPrefix::OctalType::kC},
{"-\t0", "0", IntegerPrefix::kNegative, IntegerPrefix::kDec, IntegerPrefix::OctalType::kC},
{"hello", "hello", IntegerPrefix::kPositive, IntegerPrefix::kDec, IntegerPrefix::OctalType::kC},
// clang-format on
};
for (const auto& cur : kCases) {
std::string_view view(cur.input);
IntegerPrefix prefix = ExtractIntegerPrefix(&view);
EXPECT_EQ(0, view.compare(cur.num)) << "Input " << cur.input;
EXPECT_EQ(cur.sign, prefix.sign) << "Input " << cur.input;
EXPECT_EQ(cur.base, prefix.base) << "Input " << cur.input;
if (cur.base == IntegerPrefix::kOct)
EXPECT_EQ(cur.oct, prefix.octal_type) << "Input " << cur.input;
}
}
TEST(NumberParser, ExtractIntegerSuffix) {
struct SuffixCase {
const char* input; // Input with suffix.
const char* err_msg; // Null for no error.
const char* num; // Number without suffix (when no error).
IntegerSuffix::Signed sign;
IntegerSuffix::Length length;
} kCases[] = {
// clang-format off
// input err_msg num sign length
{"", nullptr, "", IntegerSuffix::kSigned, IntegerSuffix::Length::kInteger},
{"1234", nullptr, "1234", IntegerSuffix::kSigned, IntegerSuffix::Length::kInteger},
{"12l", nullptr, "12", IntegerSuffix::kSigned, IntegerSuffix::Length::kLong},
{"12LL", nullptr, "12", IntegerSuffix::kSigned, IntegerSuffix::Length::kLongLong},
{"13u", nullptr, "13", IntegerSuffix::kUnsigned, IntegerSuffix::Length::kInteger},
{"14ul", nullptr, "14", IntegerSuffix::kUnsigned, IntegerSuffix::Length::kLong},
{"15LU", nullptr, "15", IntegerSuffix::kUnsigned, IntegerSuffix::Length::kLong},
{"16Llu", nullptr, "16", IntegerSuffix::kUnsigned, IntegerSuffix::Length::kLongLong},
{"17ulL", nullptr, "17", IntegerSuffix::kUnsigned, IntegerSuffix::Length::kLongLong},
// Bad number (still extract suffix).
{"taLl", nullptr, "ta", IntegerSuffix::kSigned, IntegerSuffix::Length::kLongLong},
// Error cases.
{"12lul", "Duplicate 'l' or 'll' in number suffix.", "", IntegerSuffix::kSigned, IntegerSuffix::Length::kLong},
{"12uu", "Duplicate 'u' in number suffix.", "", IntegerSuffix::kSigned, IntegerSuffix::Length::kLong},
// clang-format on
};
for (const auto& cur : kCases) {
std::string_view view(cur.input);
ErrOr<IntegerSuffix> result = ExtractIntegerSuffix(&view);
if (cur.err_msg) {
// Expected error.
ASSERT_TRUE(result.has_error());
EXPECT_EQ(cur.err_msg, result.err().msg());
} else {
// No expected error.
ASSERT_FALSE(result.has_error()) << result.err().msg();
EXPECT_EQ(0, view.compare(cur.num)) << "For input " << cur.input;
EXPECT_EQ(cur.sign, result.value().type_signed) << "For input " << cur.input;
EXPECT_EQ(cur.length, result.value().length) << "For input " << cur.input;
}
}
}
TEST(NumberParser, StringToNumber) {
using ExpectedType = std::variant<int32_t, uint32_t, int64_t, uint64_t>;
struct Case {
const char* input; // Input with suffix.
const char* err_msg; // Null for no error.
ExpectedType expected;
const char* expected_type_name;
} kCases[] = {
// Normal positive input.
{"0", nullptr, int32_t(0), "int"},
{"23", nullptr, int32_t(23), "int"},
{"23u", nullptr, uint32_t(23), "unsigned"},
{"23l", nullptr, int64_t(23), "long"},
{"23ul", nullptr, uint64_t(23), "unsigned long"},
{"23ll", nullptr, int64_t(23), "long long"},
{"23ull", nullptr, uint64_t(23), "unsigned long long"},
// Normal negative input.
{"-0", nullptr, int32_t(0), "int"},
{"-23", nullptr, int32_t(-23), "int"},
{"-23u", nullptr, uint32_t(-23), "unsigned"},
{"-23l", nullptr, int64_t(-23), "long"},
{"-23lu", nullptr, uint64_t(-23), "unsigned long"},
{"-23ll", nullptr, int64_t(-23), "long long"},
{"-23llu", nullptr, uint64_t(-23), "unsigned long long"},
// Hex input.
{"0xabcd", nullptr, int32_t(0xabcd), "int"},
{"- 0x614u", nullptr, uint32_t(-0x614), "unsigned"},
{"0xabcdull", nullptr, uint64_t(0xabcd), "unsigned long long"},
{"0xffffFFFFffffFFFF", nullptr, uint64_t(0xffffFFFFffffFFFF), "unsigned long"},
// This overflow case gets promoted to "long long" because it's "bigger". C++ would put this
// in a long.
{"-0xffffFFFFffffFFFF", nullptr, uint64_t(-0xffffFFFFffffFFFF), "unsigned long long"},
// Octal input ("0" prefix disallowed).
{"0o567", nullptr, int32_t(0567), "int"},
{"-0o567", nullptr, int32_t(-0567), "int"},
{"-0o0567llu", nullptr, uint64_t(-0567), "unsigned long long"},
{"0567", "Octal numbers must be prefixed with '0o'.", int32_t(0567), "int"},
{"-0567llu", "Octal numbers must be prefixed with '0o'.", uint64_t(-0567),
"unsigned long long"},
// Binary input.
{"0b0", nullptr, int32_t(0), "int"},
{"0b110010", nullptr, int32_t(0b110010), "int"},
{"0b110010l", nullptr, int64_t(0b110010), "long"},
{"-0b110010l", nullptr, int64_t(-0b110010), "long"},
// This number is one too large to put in an signed 32-bit type. C++ would expand this to a
// "long" unless it was in a non-decimal base, but our simpler rules put it in an unsigned
// since it fits.
{"2147483648", nullptr, uint32_t(2147483648), "unsigned"},
// Long override makes it more clear.
{"2147483648l", nullptr, int64_t(2147483648), "long"},
// Largest 32-bit negative number. Forcing it to unsigned gives the same bit pattern and same
// size type, but in an unsigned type.
{"-2147483648", nullptr, int32_t(-2147483648), "int"},
{"-2147483648u", nullptr, uint32_t(-2147483648u), "unsigned"},
// Some error cases.
{"", "Expected a number.", 0, nullptr},
{"0x56g", "Invalid character in number.", 0, nullptr},
{"0x56 56", "Invalid character in number.", 0, nullptr},
{"0b5", "Invalid character in number.", 0, nullptr},
{"0x0x34", "Invalid character in number.", 0, nullptr},
{"--45", "Invalid character in number.", 0, nullptr},
{"67lll", "Duplicate 'l' or 'll' in number suffix.", 0, nullptr},
};
for (const auto& cur : kCases) {
ErrOrValue result = StringToNumber(cur.input);
if (cur.err_msg) {
// Expected failure.
ASSERT_TRUE(result.has_error()) << " Input = " << cur.input;
EXPECT_EQ(cur.err_msg, result.err().msg()) << "Input = " << cur.input;
} else {
// Expected success.
ASSERT_FALSE(result.has_error()) << result.err().msg() << " for input = " << cur.input;
// This craziness calls the lambda with the given expected type and value. We can compare the
// type of the expected parameter based on the test case with the type generated by our code
// in the expr_value.
std::visit(
[&result, cur](auto expected) {
using ExpectedType = std::decay_t<decltype(expected)>;
const BaseType* expr_type = result.value().type()->AsBaseType();
ASSERT_TRUE(expr_type) << "Input = " << cur.input;
// Size of types should match.
EXPECT_EQ(sizeof(ExpectedType), result.value().data().size())
<< "Input = " << cur.input;
// Sign on types should match.
if (std::is_signed_v<ExpectedType>) {
EXPECT_EQ(expr_type->base_type(), BaseType::kBaseTypeSigned)
<< "Input = " << cur.input;
} else {
EXPECT_EQ(expr_type->base_type(), BaseType::kBaseTypeUnsigned)
<< "Input = " << cur.input;
}
// Names of types should match.
EXPECT_EQ(cur.expected_type_name, expr_type->GetFullName()) << "Input = " << cur.input;
// Values should match.
EXPECT_EQ(expected, result.value().GetAs<ExpectedType>()) << "Input = " << cur.input;
},
cur.expected);
}
}
}
TEST(NumberParser, GetFloatTokenLength) {
EXPECT_EQ(0u, GetFloatTokenLength(ExprLanguage::kC, std::string_view()));
EXPECT_EQ(0u, GetFloatTokenLength(ExprLanguage::kC, " 2.3")); // Whitespace doesn't count.
EXPECT_EQ(0u, GetFloatTokenLength(ExprLanguage::kC, "12")); // Integer, not a float.
EXPECT_EQ(0u, GetFloatTokenLength(ExprLanguage::kC, "-12.2")); // Signs not counted.
EXPECT_EQ(0u, GetFloatTokenLength(ExprLanguage::kC, "12foo")); // Not a number.
// Simple valid cases.
EXPECT_EQ(3u, GetFloatTokenLength(ExprLanguage::kC, "2.3"));
EXPECT_EQ(3u, GetFloatTokenLength(ExprLanguage::kC, "2.3"));
EXPECT_EQ(2u, GetFloatTokenLength(ExprLanguage::kC, "2. foo"));
EXPECT_EQ(3u, GetFloatTokenLength(ExprLanguage::kC, "12.+float"));
// Exponents.
EXPECT_EQ(0u, GetFloatTokenLength(ExprLanguage::kC, "12e")); // Exponent needs digits.
EXPECT_EQ(0u, GetFloatTokenLength(ExprLanguage::kC, "12extremely"));
EXPECT_EQ(0u, GetFloatTokenLength(ExprLanguage::kC, "12e+")); // Exponent needs digits.
EXPECT_EQ(5u, GetFloatTokenLength(ExprLanguage::kC, "12e12"));
EXPECT_EQ(6u, GetFloatTokenLength(ExprLanguage::kC, "12.e12 "));
EXPECT_EQ(9u, GetFloatTokenLength(ExprLanguage::kC, "12.019e12+aa"));
EXPECT_EQ(5u, GetFloatTokenLength(ExprLanguage::kC,
"12e12.2")); // ".2" not counted as part of the number.
EXPECT_EQ(0u, GetFloatTokenLength(ExprLanguage::kC, "12e+"));
EXPECT_EQ(6u, GetFloatTokenLength(ExprLanguage::kC, "12e+12"));
EXPECT_EQ(6u, GetFloatTokenLength(ExprLanguage::kC, "12E-12"));
EXPECT_EQ(0u, GetFloatTokenLength(ExprLanguage::kC, "12 e-12"));
EXPECT_EQ(0u, GetFloatTokenLength(ExprLanguage::kC, "12e- 12"));
// Suffixes. A valid number with any alphanumeric characters following is included. The suffixed
// will be validated in the parsing phase, not this tokenizing phase.
// TODO(bug 43220) Handle Rust-specific suffixes.
EXPECT_EQ(4u, GetFloatTokenLength(ExprLanguage::kC, "12.f"));
EXPECT_EQ(8u, GetFloatTokenLength(ExprLanguage::kC, "12.float"));
EXPECT_EQ(14u, GetFloatTokenLength(ExprLanguage::kC, "12e-12nonsense"));
// Rust requires a leading digit, C doesn't (as long as there's a digit following);
EXPECT_EQ(3u, GetFloatTokenLength(ExprLanguage::kC, ".14"));
EXPECT_EQ(0u, GetFloatTokenLength(ExprLanguage::kC, ".e12"));
EXPECT_EQ(0u, GetFloatTokenLength(ExprLanguage::kRust, ".14"));
}
TEST(NumberParser, StripFloatSuffix) {
std::string_view input;
EXPECT_EQ(FloatSuffix::kNone, StripFloatSuffix(&input));
input = "2.4";
EXPECT_EQ(FloatSuffix::kNone, StripFloatSuffix(&input));
EXPECT_EQ(input, "2.4");
input = "2.F";
EXPECT_EQ(FloatSuffix::kFloat, StripFloatSuffix(&input));
EXPECT_EQ(input, "2.");
// Extra characters are tolerated, these will be identified when parsed.
input = "2.4e7randomf";
EXPECT_EQ(FloatSuffix::kFloat, StripFloatSuffix(&input));
EXPECT_EQ(input, "2.4e7random");
input = "9L";
EXPECT_EQ(FloatSuffix::kLong, StripFloatSuffix(&input));
EXPECT_EQ(input, "9");
input = "l";
EXPECT_EQ(FloatSuffix::kLong, StripFloatSuffix(&input));
EXPECT_EQ(input, "");
}
namespace {
template <typename FloatType>
bool FloatValuesEqual(ExprLanguage lang, const char* input, const char* expected_type,
FloatType expected_value) {
ExprToken token(ExprTokenType::kFloat, input, 0);
ErrOrValue result = ValueForFloatToken(lang, token);
EXPECT_TRUE(result.ok()) << result.err().msg();
if (!result.ok())
return false;
EXPECT_EQ(expected_type, result.value().type()->GetFullName());
EXPECT_EQ(sizeof(expected_value), result.value().data().size());
if (sizeof(expected_value) != result.value().data().size())
return false;
// Say the float converter makes the same floating-point value as the current compiler. This
// should be the case for all sane compilers.
FloatType result_float;
memcpy(&result_float, &result.value().data()[0], sizeof(FloatType));
EXPECT_EQ(expected_value, result_float);
return expected_value == result_float;
}
} // namespace
TEST(NumberParser, ValueForFloatToken) {
EXPECT_TRUE(FloatValuesEqual(ExprLanguage::kC, "2.3", "double", 2.3));
EXPECT_TRUE(FloatValuesEqual(ExprLanguage::kC, "3.14e+0f", "float", 3.14f));
EXPECT_TRUE(FloatValuesEqual(ExprLanguage::kC, "2e9", "double", 2e9));
EXPECT_TRUE(FloatValuesEqual(ExprLanguage::kRust, ".3", "f64", .3));
EXPECT_TRUE(FloatValuesEqual(ExprLanguage::kRust, "3.", "f64", 3.));
// Technically this line is wrong. It should be "2e9f32".
// TODO(bug 43220) Handle Rust-specific suffixes.
EXPECT_TRUE(FloatValuesEqual(ExprLanguage::kRust, "2e9f", "f32", 2e9f));
// Some error cases.
const char kTrailingMsg[] = "Trailing characters on floating-point constant.";
EXPECT_EQ(kTrailingMsg,
ValueForFloatToken(ExprLanguage::kC, ExprToken(ExprTokenType::kFloat, "3blah", 0))
.err()
.msg());
EXPECT_EQ(kTrailingMsg,
ValueForFloatToken(ExprLanguage::kC, ExprToken(ExprTokenType::kFloat, "3.2.3", 0))
.err()
.msg());
}
} // namespace zxdb