Implement visitor for scan
This commit is contained in:
parent
e33c1568c3
commit
4aa24f54cd
207
test/scan.h
207
test/scan.h
@ -114,19 +114,20 @@ class scan_buffer {
|
|||||||
auto base() const -> const char* { return buf_->ptr_; }
|
auto base() const -> const char* { return buf_->ptr_; }
|
||||||
|
|
||||||
friend auto to_contiguous(iterator it) -> maybe_contiguous_range;
|
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 {
|
friend auto to_contiguous(iterator it) -> maybe_contiguous_range {
|
||||||
if (it.buf_->is_contiguous()) return {it.buf_->ptr_, it.buf_->end_};
|
if (it.buf_->is_contiguous()) return {it.buf_->ptr_, it.buf_->end_};
|
||||||
return {nullptr, nullptr};
|
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(), "");
|
FMT_ASSERT(it.buf_->is_contiguous(), "");
|
||||||
const char*& ptr = it.buf_->ptr_;
|
const char*& ptr = it.buf_->ptr_;
|
||||||
ptr += n;
|
ptr += n;
|
||||||
it.value_ = *ptr;
|
it.value_ = *ptr;
|
||||||
if (ptr == it.buf_->end_) it.ptr_ = iterator::sentinel();
|
if (ptr == it.buf_->end_) it.ptr_ = iterator::sentinel();
|
||||||
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto begin() -> iterator { return this; }
|
auto begin() -> iterator { return this; }
|
||||||
@ -320,10 +321,10 @@ struct custom_scan_arg {
|
|||||||
template <typename Context> class basic_scan_arg {
|
template <typename Context> class basic_scan_arg {
|
||||||
private:
|
private:
|
||||||
using scan_type = detail::scan_type;
|
using scan_type = detail::scan_type;
|
||||||
|
scan_type type_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// TODO: make private
|
// TODO: make private
|
||||||
scan_type type;
|
|
||||||
union {
|
union {
|
||||||
int* int_value;
|
int* int_value;
|
||||||
unsigned* uint_value;
|
unsigned* uint_value;
|
||||||
@ -336,46 +337,48 @@ template <typename Context> class basic_scan_arg {
|
|||||||
};
|
};
|
||||||
|
|
||||||
FMT_CONSTEXPR 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)
|
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)
|
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)
|
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)
|
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)
|
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)
|
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>
|
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.value = &value;
|
||||||
custom.scan = scan_custom_arg<T>;
|
custom.scan = scan_custom_arg<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr explicit operator bool() const noexcept {
|
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>
|
template <typename Visitor>
|
||||||
auto visit(Visitor&& vis) -> decltype(vis(std::declval<int>())) {
|
auto visit(Visitor&& vis) -> decltype(vis(monostate())) {
|
||||||
switch (type) {
|
switch (type_) {
|
||||||
case scan_type::none_type:
|
case scan_type::none_type:
|
||||||
break;
|
break;
|
||||||
case scan_type::int_type:
|
case scan_type::int_type:
|
||||||
return vis(int_value);
|
return vis(*int_value);
|
||||||
case scan_type::uint_type:
|
case scan_type::uint_type:
|
||||||
return vis(uint_value);
|
return vis(*uint_value);
|
||||||
case scan_type::long_long_type:
|
case scan_type::long_long_type:
|
||||||
return vis(long_long_value);
|
return vis(*long_long_value);
|
||||||
case scan_type::ulong_long_type:
|
case scan_type::ulong_long_type:
|
||||||
return vis(ulong_long_value);
|
return vis(*ulong_long_value);
|
||||||
case scan_type::string_type:
|
case scan_type::string_type:
|
||||||
return vis(string);
|
return vis(*string);
|
||||||
case scan_type::string_view_type:
|
case scan_type::string_view_type:
|
||||||
return vis(string_view);
|
return vis(*string_view);
|
||||||
case scan_type::custom_type:
|
case scan_type::custom_type:
|
||||||
// TODO: implement
|
// TODO: implement
|
||||||
break;
|
break;
|
||||||
@ -445,6 +448,91 @@ const char* parse_scan_specs(const char* begin, const char* end,
|
|||||||
return begin;
|
return begin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct default_arg_scanner {
|
||||||
|
using iterator = scan_buffer::iterator;
|
||||||
|
iterator begin;
|
||||||
|
iterator end;
|
||||||
|
|
||||||
|
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') throw_format_error("invalid input");
|
||||||
|
|
||||||
|
int num_digits = 0;
|
||||||
|
T n = 0, prev = 0;
|
||||||
|
char prev_digit = c;
|
||||||
|
do {
|
||||||
|
prev = n;
|
||||||
|
n = n * 10 + static_cast<unsigned>(c - '0');
|
||||||
|
prev_digit = c;
|
||||||
|
c = *++it;
|
||||||
|
++num_digits;
|
||||||
|
if (c < '0' || c > '9') break;
|
||||||
|
} while (it != end);
|
||||||
|
|
||||||
|
// Check overflow.
|
||||||
|
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) {
|
||||||
|
value = n;
|
||||||
|
} else {
|
||||||
|
throw_format_error("number is too big");
|
||||||
|
}
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T = int>
|
||||||
|
auto read_int(iterator it, T& value) -> iterator {
|
||||||
|
bool negative = it != end && *it == '-';
|
||||||
|
if (negative) {
|
||||||
|
++it;
|
||||||
|
if (it == end) throw_format_error("invalid input");
|
||||||
|
}
|
||||||
|
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 {
|
struct arg_scanner {
|
||||||
using iterator = scan_buffer::iterator;
|
using iterator = scan_buffer::iterator;
|
||||||
|
|
||||||
@ -465,48 +553,6 @@ struct scan_handler : error_handler {
|
|||||||
scan_context scan_ctx_;
|
scan_context scan_ctx_;
|
||||||
int next_arg_id_;
|
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 {};
|
|
||||||
char c = *it;
|
|
||||||
if (c < '0' || c > '9') on_error("invalid input");
|
|
||||||
|
|
||||||
int num_digits = 0;
|
|
||||||
T value = 0, prev = 0;
|
|
||||||
char prev_digit = c;
|
|
||||||
do {
|
|
||||||
prev = value;
|
|
||||||
value = value * 10 + static_cast<unsigned>(c - '0');
|
|
||||||
prev_digit = c;
|
|
||||||
c = *++it;
|
|
||||||
++num_digits;
|
|
||||||
if (c < '0' || c > '9') break;
|
|
||||||
} 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 + 1 &&
|
|
||||||
prev * 10ull + unsigned(prev_digit - '0') <= max) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
throw format_error("number is too big");
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T = int> auto read_int(iterator& it) -> optional<T> {
|
|
||||||
auto end = scan_ctx_.end();
|
|
||||||
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) on_error("invalid input");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FMT_CONSTEXPR scan_handler(string_view format, scan_buffer& buf,
|
FMT_CONSTEXPR scan_handler(string_view format, scan_buffer& buf,
|
||||||
scan_args args)
|
scan_args args)
|
||||||
@ -537,51 +583,20 @@ struct scan_handler : error_handler {
|
|||||||
scan_arg arg = scan_ctx_.arg(arg_id);
|
scan_arg arg = scan_ctx_.arg(arg_id);
|
||||||
auto it = scan_ctx_.begin(), end = scan_ctx_.end();
|
auto it = scan_ctx_.begin(), end = scan_ctx_.end();
|
||||||
while (it != end && is_whitespace(*it)) ++it;
|
while (it != end && is_whitespace(*it)) ++it;
|
||||||
switch (arg.type) {
|
arg.visit(default_arg_scanner{it, end});
|
||||||
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);
|
|
||||||
}
|
|
||||||
scan_ctx_.advance_to(it);
|
scan_ctx_.advance_to(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto on_format_specs(int arg_id, const char* begin, const char* end) -> const
|
auto on_format_specs(int arg_id, const char* begin, const char* end) -> const
|
||||||
char* {
|
char* {
|
||||||
scan_arg arg = scan_ctx_.arg(arg_id);
|
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);
|
parse_ctx_.advance_to(begin);
|
||||||
arg.custom.scan(arg.custom.value, parse_ctx_, scan_ctx_);
|
arg.custom.scan(arg.custom.value, parse_ctx_, scan_ctx_);
|
||||||
return parse_ctx_.begin();
|
return parse_ctx_.begin();
|
||||||
}
|
}
|
||||||
auto specs = format_specs<>();
|
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");
|
if (begin == end || *begin != '}') on_error("missing '}' in format string");
|
||||||
auto s = arg_scanner{scan_ctx_.begin(), scan_ctx_.end(), specs};
|
auto s = arg_scanner{scan_ctx_.begin(), scan_ctx_.end(), specs};
|
||||||
// TODO: scan argument according to specs
|
// TODO: scan argument according to specs
|
||||||
|
Loading…
Reference in New Issue
Block a user