diff options
author | Victor Zverovich <viz@meta.com> | 2023-12-31 09:51:24 -0800 |
---|---|---|
committer | Victor Zverovich <viz@meta.com> | 2023-12-31 09:53:24 -0800 |
commit | 4aa24f54cdbcbf28062e2066229a1521cb2fe989 (patch) | |
tree | 0c32ac8dbfb80e358ce4853dc0c8f50c1c156d65 | |
parent | e33c1568c3088fd14ec6eaeb903130ed9c8defe9 (diff) | |
download | fmtlib-4aa24f54cdbcbf28062e2066229a1521cb2fe989.tar.gz |
Implement visitor for scan
-rw-r--r-- | test/scan.h | 197 |
1 files changed, 106 insertions, 91 deletions
diff --git a/test/scan.h b/test/scan.h index 016c3b42..dd6751d8 100644 --- a/test/scan.h +++ b/test/scan.h @@ -114,19 +114,20 @@ class scan_buffer { auto base() const -> const char* { return buf_->ptr_; } friend auto to_contiguous(iterator it) -> maybe_contiguous_range; - friend void advance(iterator& it, size_t n); + friend auto advance(iterator it, size_t n) -> iterator; }; friend auto to_contiguous(iterator it) -> maybe_contiguous_range { if (it.buf_->is_contiguous()) return {it.buf_->ptr_, it.buf_->end_}; return {nullptr, nullptr}; } - friend void advance(iterator& it, size_t n) { + friend auto advance(iterator it, size_t n) -> iterator { FMT_ASSERT(it.buf_->is_contiguous(), ""); const char*& ptr = it.buf_->ptr_; ptr += n; it.value_ = *ptr; if (ptr == it.buf_->end_) it.ptr_ = iterator::sentinel(); + return it; } auto begin() -> iterator { return this; } @@ -320,10 +321,10 @@ struct custom_scan_arg { template <typename Context> class basic_scan_arg { private: using scan_type = detail::scan_type; + scan_type type_; public: // TODO: make private - scan_type type; union { int* int_value; unsigned* uint_value; @@ -336,46 +337,48 @@ template <typename Context> class basic_scan_arg { }; FMT_CONSTEXPR basic_scan_arg() - : type(scan_type::none_type), int_value(nullptr) {} + : type_(scan_type::none_type), int_value(nullptr) {} FMT_CONSTEXPR basic_scan_arg(int& value) - : type(scan_type::int_type), int_value(&value) {} + : type_(scan_type::int_type), int_value(&value) {} FMT_CONSTEXPR basic_scan_arg(unsigned& value) - : type(scan_type::uint_type), uint_value(&value) {} + : type_(scan_type::uint_type), uint_value(&value) {} FMT_CONSTEXPR basic_scan_arg(long long& value) - : type(scan_type::long_long_type), long_long_value(&value) {} + : type_(scan_type::long_long_type), long_long_value(&value) {} FMT_CONSTEXPR basic_scan_arg(unsigned long long& value) - : type(scan_type::ulong_long_type), ulong_long_value(&value) {} + : type_(scan_type::ulong_long_type), ulong_long_value(&value) {} FMT_CONSTEXPR basic_scan_arg(std::string& value) - : type(scan_type::string_type), string(&value) {} + : type_(scan_type::string_type), string(&value) {} FMT_CONSTEXPR basic_scan_arg(fmt::string_view& value) - : type(scan_type::string_view_type), string_view(&value) {} + : type_(scan_type::string_view_type), string_view(&value) {} template <typename T> - FMT_CONSTEXPR basic_scan_arg(T& value) : type(scan_type::custom_type) { + FMT_CONSTEXPR basic_scan_arg(T& value) : type_(scan_type::custom_type) { custom.value = &value; custom.scan = scan_custom_arg<T>; } constexpr explicit operator bool() const noexcept { - return type != scan_type::none_type; + return type_ != scan_type::none_type; } + auto type() const -> detail::scan_type { return type_; } + template <typename Visitor> - auto visit(Visitor&& vis) -> decltype(vis(std::declval<int>())) { - switch (type) { + auto visit(Visitor&& vis) -> decltype(vis(monostate())) { + switch (type_) { case scan_type::none_type: break; case scan_type::int_type: - return vis(int_value); + return vis(*int_value); case scan_type::uint_type: - return vis(uint_value); + return vis(*uint_value); case scan_type::long_long_type: - return vis(long_long_value); + return vis(*long_long_value); case scan_type::ulong_long_type: - return vis(ulong_long_value); + return vis(*ulong_long_value); case scan_type::string_type: - return vis(string); + return vis(*string); case scan_type::string_view_type: - return vis(string_view); + return vis(*string_view); case scan_type::custom_type: // TODO: implement break; @@ -445,40 +448,23 @@ const char* parse_scan_specs(const char* begin, const char* end, return begin; } -struct arg_scanner { +struct default_arg_scanner { using iterator = scan_buffer::iterator; - iterator begin; iterator end; - const format_specs<>& specs; - template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> - auto operator()(T) -> iterator { - // TODO - return begin; - } -}; - -struct scan_handler : error_handler { - private: - scan_parse_context parse_ctx_; - scan_context scan_ctx_; - int next_arg_id_; - - using iterator = scan_buffer::iterator; - - template <typename T = unsigned> auto read_uint(iterator& it) -> optional<T> { - auto end = scan_ctx_.end(); - if (it == end) return {}; + template <typename T = unsigned> + auto read_uint(iterator it, T& value) -> iterator { + if (it == end) return it; char c = *it; - if (c < '0' || c > '9') on_error("invalid input"); + if (c < '0' || c > '9') throw_format_error("invalid input"); int num_digits = 0; - T value = 0, prev = 0; + T n = 0, prev = 0; char prev_digit = c; do { - prev = value; - value = value * 10 + static_cast<unsigned>(c - '0'); + prev = n; + n = n * 10 + static_cast<unsigned>(c - '0'); prev_digit = c; c = *++it; ++num_digits; @@ -486,26 +472,86 @@ struct scan_handler : error_handler { } while (it != end); // Check overflow. - if (num_digits <= std::numeric_limits<int>::digits10) return value; - const unsigned max = to_unsigned((std::numeric_limits<int>::max)()); + if (num_digits <= std::numeric_limits<int>::digits10) { + value = n; + return it; + } + unsigned max = to_unsigned((std::numeric_limits<int>::max)()); if (num_digits == std::numeric_limits<int>::digits10 + 1 && prev * 10ull + unsigned(prev_digit - '0') <= max) { - return value; + value = n; + } else { + throw_format_error("number is too big"); } - throw format_error("number is too big"); + return it; } - template <typename T = int> auto read_int(iterator& it) -> optional<T> { - auto end = scan_ctx_.end(); + template <typename T = int> + auto read_int(iterator it, T& value) -> iterator { bool negative = it != end && *it == '-'; - if (negative) ++it; - if (auto abs_value = read_uint<typename std::make_unsigned<T>::type>(it)) { - auto value = static_cast<T>(*abs_value); - return negative ? -value : value; + if (negative) { + ++it; + if (it == end) throw_format_error("invalid input"); } - if (negative) on_error("invalid input"); - return {}; + using unsigned_type = typename std::make_unsigned<T>::type; + unsigned_type abs_value = 0; + it = read_uint<unsigned_type>(it, abs_value); + auto n = static_cast<T>(abs_value); + value = negative ? -n : n; + return it; + } + + auto operator()(int& value) -> iterator { + return read_int(begin, value); + } + auto operator()(unsigned& value) -> iterator { + return read_uint(begin, value); + } + auto operator()(long long& value) -> iterator { + return read_int<long long>(begin, value); + } + auto operator()(unsigned long long& value) -> iterator { + return read_uint<unsigned long long>(begin, value); + } + auto operator()(std::string& value) -> iterator { + iterator it = begin; + while (it != end && *it != ' ') value.push_back(*it++); + return it; + } + auto operator()(fmt::string_view& value) -> iterator { + auto range = to_contiguous(begin); + // This could also be checked at compile time in scan. + if (!range) throw_format_error("string_view requires contiguous input"); + auto p = range.begin; + while (p != range.end && *p != ' ') ++p; + size_t size = to_unsigned(p - range.begin); + value = {range.begin, size}; + return advance(begin, size); + } + auto operator()(monostate) -> iterator { + return begin; } +}; + +struct arg_scanner { + using iterator = scan_buffer::iterator; + + iterator begin; + iterator end; + const format_specs<>& specs; + + template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> + auto operator()(T) -> iterator { + // TODO + return begin; + } +}; + +struct scan_handler : error_handler { + private: + scan_parse_context parse_ctx_; + scan_context scan_ctx_; + int next_arg_id_; public: FMT_CONSTEXPR scan_handler(string_view format, scan_buffer& buf, @@ -537,51 +583,20 @@ struct scan_handler : error_handler { scan_arg arg = scan_ctx_.arg(arg_id); auto it = scan_ctx_.begin(), end = scan_ctx_.end(); while (it != end && is_whitespace(*it)) ++it; - switch (arg.type) { - case scan_type::int_type: - if (auto value = read_int(it)) *arg.int_value = *value; - break; - case scan_type::uint_type: - if (auto value = read_uint(it)) *arg.uint_value = *value; - break; - case scan_type::long_long_type: - if (auto value = read_int<long long>(it)) *arg.long_long_value = *value; - break; - case scan_type::ulong_long_type: - if (auto value = read_uint<unsigned long long>(it)) - *arg.ulong_long_value = *value; - break; - case scan_type::string_type: - while (it != end && *it != ' ') arg.string->push_back(*it++); - break; - case scan_type::string_view_type: { - auto range = to_contiguous(it); - // This could also be checked at compile time in scan. - if (!range) on_error("string_view requires contiguous input"); - auto p = range.begin; - while (p != range.end && *p != ' ') ++p; - size_t size = to_unsigned(p - range.begin); - *arg.string_view = {range.begin, size}; - advance(it, size); - break; - } - case scan_type::none_type: - case scan_type::custom_type: - assert(false); - } + arg.visit(default_arg_scanner{it, end}); scan_ctx_.advance_to(it); } auto on_format_specs(int arg_id, const char* begin, const char* end) -> const char* { scan_arg arg = scan_ctx_.arg(arg_id); - if (arg.type == scan_type::custom_type) { + if (arg.type() == scan_type::custom_type) { parse_ctx_.advance_to(begin); arg.custom.scan(arg.custom.value, parse_ctx_, scan_ctx_); return parse_ctx_.begin(); } auto specs = format_specs<>(); - begin = parse_scan_specs(begin, end, specs, arg.type); + begin = parse_scan_specs(begin, end, specs, arg.type()); if (begin == end || *begin != '}') on_error("missing '}' in format string"); auto s = arg_scanner{scan_ctx_.begin(), scan_ctx_.end(), specs}; // TODO: scan argument according to specs |