#include "value.h" #include #include #include namespace mars::parse { namespace { bool is_digit(unsigned char c) { return c >= '0' && c <= '9'; } // Consumes the optional sign, returns the rest. std::string_view strip_sign(std::string_view s) { if (!s.empty() && (s[0] == '+' || s[0] == '-')) s.remove_prefix(1); return s; } bool all_digits(std::string_view s) { if (s.empty()) return false; for (unsigned char c : s) if (!is_digit(c)) return false; return true; } } // namespace unsigned char ascii_lower(unsigned char c) { return (c >= 'A' && c <= 'Z') ? static_cast(c + ('a' - 'A')) : c; } bool iequals(std::string_view a, std::string_view b) { if (a.size() != b.size()) return false; for (std::size_t i = 0; i < a.size(); ++i) if (ascii_lower(static_cast(a[i])) != ascii_lower(static_cast(b[i]))) return false; return true; } ScalarKind classify(std::string_view tok) { if (tok.empty()) return ScalarKind::Text; if (iequals(tok, "true") || iequals(tok, "false")) return ScalarKind::Bool; std::string_view body = strip_sign(tok); if (all_digits(body)) return ScalarKind::Int; // mantissa: d+.d* | .d+ | d+ std::size_t i = 0; std::size_t int_digits = 0; while (i < body.size() && is_digit(static_cast(body[i]))) { ++i; ++int_digits; } std::size_t frac_digits = 0; if (i < body.size() && body[i] == '.') { ++i; while (i < body.size() && is_digit(static_cast(body[i]))) { ++i; ++frac_digits; } } if (int_digits == 0 && frac_digits == 0) return ScalarKind::Text; // ".", "-", "e5" // optional exponent (a dotless mantissa with an exponent, "3e5", is a float) if (i < body.size() && (body[i] == 'e' || body[i] == 'E')) { ++i; if (i < body.size() && (body[i] == '+' || body[i] == '-')) ++i; std::size_t exp_digits = 0; while (i < body.size() && is_digit(static_cast(body[i]))) { ++i; ++exp_digits; } if (exp_digits == 0) return ScalarKind::Text; } return i == body.size() ? ScalarKind::Float : ScalarKind::Text; } std::optional as_int(std::string_view tok) { if (classify(tok) != ScalarKind::Int) return std::nullopt; std::string buf(tok); errno = 0; char* end = nullptr; long long v = std::strtoll(buf.c_str(), &end, 10); if (errno == ERANGE || end != buf.c_str() + buf.size()) return std::nullopt; return static_cast(v); } std::optional as_double(std::string_view tok) { ScalarKind k = classify(tok); if (k != ScalarKind::Int && k != ScalarKind::Float) return std::nullopt; std::string buf(tok); char* end = nullptr; double v = std::strtod(buf.c_str(), &end); if (end != buf.c_str() + buf.size()) return std::nullopt; return v; } std::optional as_bool(std::string_view tok) { if (iequals(tok, "true")) return true; if (iequals(tok, "false")) return false; return std::nullopt; } } // namespace mars::parse