text: // 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.
      ExprLanguage lang;
      const char* err_msg;  // Null for no error.
  
      ExpectedType expected;
      const char* expected_type_name;
    } kCases[] = {
        // Normal positive input.
        {"0", ExprLanguage::kC, nullptr, int32_t(0), "int"},
        {"23", ExprLanguage::kC, nullptr, int32_t(23), "int"},
        {"23u", ExprLanguage::kC, nullptr, uint32_t(23), "unsigned"},
        {"2_3u", ExprLanguage::kRust, nullptr, uint32_t(23), "unsigned"},
        {"2'3u", ExprLanguage::kC, nullptr, uint32_t(23), "unsigned"},
        {"23l", ExprLanguage::kC, nullptr, int64_t(23), "long"},
        {"23ul", ExprLanguage::kC, nullptr, uint64_t(23), "unsigned long"},
        {"23ll", ExprLanguage::kC, nullptr, int64_t(23), "long long"},
        {"23ull", ExprLanguage::kC, nullptr, uint64_t(23), "unsigned long long"},
  
        // Normal negative input.
        {"-0", ExprLanguage::kC, nullptr, int32_t(0), "int"},
        {"-23", ExprLanguage::kC, nullptr, int32_t(-23), "int"},
        {"-23u", ExprLanguage::kC, nullptr, uint32_t(-23), "unsigned"},
        {"-23l", ExprLanguage::kC, nullptr, int64_t(-23), "long"},
        {"-2_3l", ExprLanguage::kRust, nullptr, int64_t(-23), "long"},
        {"-2'3l", ExprLanguage::kC, nullptr, int64_t(-23), "long"},
        {"-23lu", ExprLanguage::kC, nullptr, uint64_t(-23), "unsigned long"},
        {"-23ll", ExprLanguage::kC, nullptr, int64_t(-23), "long long"},
        {"-23llu", ExprLanguage::kC, nullptr, uint64_t(-23), "unsigned long long"},
  
        // Hex input.
        {"0xabcd", ExprLanguage::kC, nullptr, int32_t(0xabcd), "int"},
        {"- 0x614u", ExprLanguage::kC, nullptr, uint32_t(-0x614), "unsigned"},
        {"0xabcdull", ExprLanguage::kC, nullptr, uint64_t(0xabcd), "unsigned long long"},
        {"0xffffFFFFffffFFFF", ExprLanguage::kC, nullptr, uint64_t(0xffffFFFFffffFFFF),
         "unsigned long"},
        {"0xffff_FFFF_ffff_FFFF", ExprLanguage::kRust, 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", ExprLanguage::kC, nullptr, uint64_t(-0xffffFFFFffffFFFF),
         "unsigned long long"},
        {"-0xffff'FFFF'ffff'FFFF", ExprLanguage::kC, nullptr, uint64_t(-0xffffFFFFffffFFFF),
         "unsigned long long"},
  
        // Octal input ("0" prefix disallowed).
        {"0o567", ExprLanguage::kC, nullptr, int32_t(0567), "int"},
        {"-0o567", ExprLanguage::kC, nullptr, int32_t(-0567), "int"},
        {"-0o0567llu", ExprLanguage::kC, nullptr, uint64_t(-0567), "unsigned long long"},
        {"0567", ExprLanguage::kC, "Octal numbers must be prefixed with '0o'.", int32_t(0567), "int"},
        {"-0567llu", ExprLanguage::kC, "Octal numbers must be prefixed with '0o'.", uint64_t(-0567),
         "unsigned long long"},
  
        // Binary input.
        {"0b0", ExprLanguage::kC, nullptr, int32_t(0), "int"},
        {"0b11_0010", ExprLanguage::kRust, nullptr, int32_t(0b110010), "int"},
        {"0b110'010l", ExprLanguage::kC, nullptr, int64_t(0b110010), "long"},
        {"-0b110010l", ExprLanguage::kC, 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", ExprLanguage::kC, nullptr, uint32_t(2147483648), "unsigned"},
        // Long override makes it more clear.
        {"2147483648l", ExprLanguage::kC, 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", ExprLanguage::kC, nullptr, int32_t(-2147483648), "int"},
        {"-2147483648u", ExprLanguage::kC, nullptr, uint32_t(-2147483648u), "unsigned"},
  
        // Some error cases.
        {"", ExprLanguage::kC, "Expected a number.", 0, nullptr},
        {"0x56g", ExprLanguage::kC, "Invalid character in number.", 0, nullptr},
        {"0x56 56", ExprLanguage::kC, "Invalid character in number.", 0, nullptr},
        {"0b5", ExprLanguage::kC, "Invalid character in number.", 0, nullptr},
        {"0x0x34", ExprLanguage::kC, "Invalid character in number.", 0, nullptr},
        {"--45", ExprLanguage::kC, "Invalid character in number.", 0, nullptr},
        {"67lll", ExprLanguage::kC, "Duplicate 'l' or 'll' in number suffix.", 0, nullptr},
  
        // Separators.
        {"2'300", ExprLanguage::kRust, "Invalid character in number.", 0, nullptr},
        {"2_300", ExprLanguage::kC, "Invalid character in number.", 0, nullptr},
    };
  
    for (const auto& cur : kCases) {
      ErrOrValue result = StringToNumber(cur.lang, 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()->As<BaseType>();
              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(8u, GetFloatTokenLength(ExprLanguage::kRust, "1_2.e1_2 "));
    EXPECT_EQ(11u, GetFloatTokenLength(ExprLanguage::kC, "1'2.01'9e12+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::kC, "1'000.123'456", "double", 1000.123456));
    EXPECT_TRUE(FloatValuesEqual(ExprLanguage::kRust, "1_000.123_456", "f64", 1000.123456));
  
    EXPECT_TRUE(FloatValuesEqual(ExprLanguage::kRust, ".3", "f64", .3));
    EXPECT_TRUE(FloatValuesEqual(ExprLanguage::kRust, "3.", "f64", 3.));
    // Technically this line is wrong. The input 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
  