Simplify format string parsing

This commit is contained in:
Victor Zverovich 2022-12-25 11:21:48 -08:00
parent d907786f04
commit b8f36207c9
2 changed files with 36 additions and 108 deletions

View File

@ -2017,41 +2017,14 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
begin = align_result.end; begin = align_result.end;
if (begin == end) return {begin, begin}; if (begin == end) return {begin, begin};
auto width = detail::parse_dynamic_spec(begin, end, ctx); begin = detail::parse_dynamic_spec(begin, end, specs.width, width_ref, ctx);
switch (width.spec.kind) {
case detail::dynamic_spec_kind::none:
break;
case detail::dynamic_spec_kind::value:
specs.width = width.spec.value;
break;
case detail::dynamic_spec_kind::index:
width_ref = arg_ref_type(width.spec.value);
break;
case detail::dynamic_spec_kind::name:
width_ref = arg_ref_type(width.spec.name);
break;
}
begin = width.end;
if (begin == end) return {begin, begin}; if (begin == end) return {begin, begin};
auto checker = detail::chrono_format_checker(); auto checker = detail::chrono_format_checker();
if (*begin == '.') { if (*begin == '.') {
checker.has_precision_integral = !std::is_floating_point<Rep>::value; checker.has_precision_integral = !std::is_floating_point<Rep>::value;
auto prec = detail::parse_precision(begin, end, ctx); begin =
switch (prec.spec.kind) { detail::parse_precision(begin, end, precision, precision_ref, ctx);
case detail::dynamic_spec_kind::none:
break;
case detail::dynamic_spec_kind::value:
precision = prec.spec.value;
break;
case detail::dynamic_spec_kind::index:
precision_ref = arg_ref_type(prec.spec.value);
break;
case detail::dynamic_spec_kind::name:
precision_ref = arg_ref_type(prec.spec.name);
break;
}
begin = prec.end;
} }
if (begin != end && *begin == 'L') { if (begin != end && *begin == 'L') {
++begin; ++begin;

View File

@ -2179,7 +2179,7 @@ template <typename Char> struct arg_ref {
arg_id_kind kind; arg_id_kind kind;
union value { union value {
FMT_CONSTEXPR value(int id = 0) : index{id} {} FMT_CONSTEXPR value(int idx = 0) : index(idx) {}
FMT_CONSTEXPR value(basic_string_view<Char> n) : name(n) {} FMT_CONSTEXPR value(basic_string_view<Char> n) : name(n) {}
int index; int index;
@ -2188,24 +2188,13 @@ template <typename Char> struct arg_ref {
}; };
// Format specifiers with width and precision resolved at formatting rather // Format specifiers with width and precision resolved at formatting rather
// than parsing time to allow re-using the same parsed specifiers with // than parsing time to allow reusing the same parsed specifiers with
// different sets of arguments (precompilation of format strings). // different sets of arguments (precompilation of format strings).
template <typename Char> struct dynamic_format_specs : format_specs<Char> { template <typename Char> struct dynamic_format_specs : format_specs<Char> {
arg_ref<Char> width_ref; arg_ref<Char> width_ref;
arg_ref<Char> precision_ref; arg_ref<Char> precision_ref;
}; };
enum class dynamic_spec_kind { none, value, index, name };
// A format specifier that can be specified dynamically such as width.
template <typename Char> struct dynamic_spec {
dynamic_spec_kind kind;
union {
int value;
basic_string_view<Char> name;
};
};
template <typename Char> constexpr bool is_ascii_letter(Char c) { template <typename Char> constexpr bool is_ascii_letter(Char c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
} }
@ -2328,16 +2317,15 @@ template <typename Char> FMT_CONSTEXPR bool is_name_start(Char c) {
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c;
} }
template <typename Char, typename IDHandler> template <typename Char, typename Handler>
FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end, FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end,
IDHandler&& handler) -> const Char* { Handler&& handler) -> const Char* {
FMT_ASSERT(begin != end, "");
Char c = *begin; Char c = *begin;
if (c >= '0' && c <= '9') { if (c >= '0' && c <= '9') {
int index = 0; int index = 0;
constexpr int max = (std::numeric_limits<int>::max)();
if (c != '0') if (c != '0')
index = index = parse_nonnegative_int(begin, end, max);
parse_nonnegative_int(begin, end, (std::numeric_limits<int>::max)());
else else
++begin; ++begin;
if (begin == end || (*begin != '}' && *begin != ':')) if (begin == end || (*begin != '}' && *begin != ':'))
@ -2358,9 +2346,10 @@ FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end,
return it; return it;
} }
template <typename Char, typename IDHandler> template <typename Char, typename Handler>
FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char* begin, const Char* end, FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char* begin, const Char* end,
IDHandler&& handler) -> const Char* { Handler&& handler) -> const Char* {
FMT_ASSERT(begin != end, "");
Char c = *begin; Char c = *begin;
if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler); if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler);
handler.on_auto(); handler.on_auto();
@ -2369,65 +2358,58 @@ FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char* begin, const Char* end,
template <typename Char> struct dynamic_spec_id_handler { template <typename Char> struct dynamic_spec_id_handler {
basic_format_parse_context<Char>& ctx; basic_format_parse_context<Char>& ctx;
dynamic_spec<Char> spec; arg_ref<Char>& ref;
FMT_CONSTEXPR void on_auto() { FMT_CONSTEXPR void on_auto() {
spec.kind = dynamic_spec_kind::index; int id = ctx.next_arg_id();
spec.value = ctx.next_arg_id(); ref = arg_ref<Char>(id);
ctx.check_dynamic_spec(spec.value); ctx.check_dynamic_spec(id);
} }
FMT_CONSTEXPR void on_index(int id) { FMT_CONSTEXPR void on_index(int id) {
spec.kind = dynamic_spec_kind::index; ref = arg_ref<Char>(id);
spec.value = id;
ctx.check_arg_id(id); ctx.check_arg_id(id);
ctx.check_dynamic_spec(id); ctx.check_dynamic_spec(id);
} }
FMT_CONSTEXPR void on_name(basic_string_view<Char> id) { FMT_CONSTEXPR void on_name(basic_string_view<Char> id) {
spec.kind = dynamic_spec_kind::name; ref = arg_ref<Char>(id);
spec.name = id;
ctx.check_arg_id(id); ctx.check_arg_id(id);
} }
}; };
template <typename Char> struct parse_dynamic_spec_result {
const Char* end;
dynamic_spec<Char> spec;
};
// Parses [integer | "{" [arg_id] "}"]. // Parses [integer | "{" [arg_id] "}"].
template <typename Char> template <typename Char>
FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end, FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end,
int& value, arg_ref<Char>& ref,
basic_format_parse_context<Char>& ctx) basic_format_parse_context<Char>& ctx)
-> parse_dynamic_spec_result<Char> { -> const Char* {
FMT_ASSERT(begin != end, ""); FMT_ASSERT(begin != end, "");
if ('0' <= *begin && *begin <= '9') { if ('0' <= *begin && *begin <= '9') {
int value = parse_nonnegative_int(begin, end, -1); int val = parse_nonnegative_int(begin, end, -1);
if (value != -1) return {begin, {dynamic_spec_kind::value, {value}}}; if (val != -1)
throw_format_error("number is too big"); value = val;
else
throw_format_error("number is too big");
} else if (*begin == '{') { } else if (*begin == '{') {
++begin; ++begin;
auto handler = auto handler = dynamic_spec_id_handler<Char>{ctx, ref};
dynamic_spec_id_handler<Char>{ctx, {dynamic_spec_kind::none, {}}};
if (begin != end) begin = parse_arg_id(begin, end, handler); if (begin != end) begin = parse_arg_id(begin, end, handler);
if (begin != end && *begin == '}') { if (begin != end && *begin == '}') return ++begin;
++begin;
return {begin, handler.spec};
}
throw_format_error("invalid format string"); throw_format_error("invalid format string");
} }
return {begin, {dynamic_spec_kind::none, {}}}; return begin;
} }
template <typename Char> template <typename Char>
FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end,
int& value, arg_ref<Char>& ref,
basic_format_parse_context<Char>& ctx) basic_format_parse_context<Char>& ctx)
-> parse_dynamic_spec_result<Char> { -> const Char* {
++begin; ++begin;
if (begin == end || *begin == '}') { if (begin == end || *begin == '}') {
throw_format_error("missing precision"); throw_format_error("missing precision");
return {begin, {dynamic_spec_kind::none, {}}}; return begin;
} }
return parse_dynamic_spec(begin, end, ctx); return parse_dynamic_spec(begin, end, value, ref, ctx);
} }
template <typename Char> template <typename Char>
@ -2552,42 +2534,15 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(
if (++begin == end) return {begin, specs}; if (++begin == end) return {begin, specs};
} }
auto width = parse_dynamic_spec(begin, end, ctx); begin = parse_dynamic_spec(begin, end, specs.width, specs.width_ref, ctx);
switch (width.spec.kind) {
case dynamic_spec_kind::none:
break;
case dynamic_spec_kind::value:
specs.width = width.spec.value;
break;
case dynamic_spec_kind::index:
specs.width_ref = arg_ref<Char>(width.spec.value);
break;
case dynamic_spec_kind::name:
specs.width_ref = arg_ref<Char>(width.spec.name);
break;
}
begin = width.end;
if (begin == end) return {begin, specs}; if (begin == end) return {begin, specs};
// Parse precision. // Parse precision.
if (*begin == '.') { if (*begin == '.') {
auto precision = parse_precision(begin, end, ctx); begin =
switch (precision.spec.kind) { parse_precision(begin, end, specs.precision, specs.precision_ref, ctx);
case dynamic_spec_kind::none:
break;
case dynamic_spec_kind::value:
specs.precision = precision.spec.value;
break;
case dynamic_spec_kind::index:
specs.precision_ref = arg_ref<Char>(precision.spec.value);
break;
case dynamic_spec_kind::name:
specs.precision_ref = arg_ref<Char>(precision.spec.name);
break;
}
if (is_integral_type(arg_type) || arg_type == type::pointer_type) if (is_integral_type(arg_type) || arg_type == type::pointer_type)
throw_format_error("precision not allowed for this argument type"); throw_format_error("precision not allowed for this argument type");
begin = precision.end;
if (begin == end) return {begin, specs}; if (begin == end) return {begin, specs};
} }
@ -2833,7 +2788,7 @@ constexpr int invalid_arg_index = -1;
#if FMT_USE_NONTYPE_TEMPLATE_ARGS #if FMT_USE_NONTYPE_TEMPLATE_ARGS
template <int N, typename T, typename... Args, typename Char> template <int N, typename T, typename... Args, typename Char>
constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int { constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
if constexpr (detail::is_statically_named_arg<T>()) { if constexpr (is_statically_named_arg<T>()) {
if (name == T::name) return N; if (name == T::name) return N;
} }
if constexpr (sizeof...(Args) > 0) if constexpr (sizeof...(Args) > 0)