From b8f36207c9fb26a24cf64b543694b87d20653b7d Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 25 Dec 2022 11:21:48 -0800 Subject: [PATCH] Simplify format string parsing --- include/fmt/chrono.h | 33 ++----------- include/fmt/core.h | 111 +++++++++++++------------------------------ 2 files changed, 36 insertions(+), 108 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index fd9c4d5e..311e0ca7 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -2017,41 +2017,14 @@ struct formatter, Char> { begin = align_result.end; if (begin == end) return {begin, begin}; - auto width = detail::parse_dynamic_spec(begin, end, 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; + begin = detail::parse_dynamic_spec(begin, end, specs.width, width_ref, ctx); if (begin == end) return {begin, begin}; auto checker = detail::chrono_format_checker(); if (*begin == '.') { checker.has_precision_integral = !std::is_floating_point::value; - auto prec = detail::parse_precision(begin, end, ctx); - switch (prec.spec.kind) { - 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; + begin = + detail::parse_precision(begin, end, precision, precision_ref, ctx); } if (begin != end && *begin == 'L') { ++begin; diff --git a/include/fmt/core.h b/include/fmt/core.h index 6414dfff..34e60da8 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -2179,7 +2179,7 @@ template struct arg_ref { arg_id_kind kind; union value { - FMT_CONSTEXPR value(int id = 0) : index{id} {} + FMT_CONSTEXPR value(int idx = 0) : index(idx) {} FMT_CONSTEXPR value(basic_string_view n) : name(n) {} int index; @@ -2188,24 +2188,13 @@ template struct arg_ref { }; // 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). template struct dynamic_format_specs : format_specs { arg_ref width_ref; arg_ref precision_ref; }; -enum class dynamic_spec_kind { none, value, index, name }; - -// A format specifier that can be specified dynamically such as width. -template struct dynamic_spec { - dynamic_spec_kind kind; - union { - int value; - basic_string_view name; - }; -}; - template constexpr bool is_ascii_letter(Char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } @@ -2328,16 +2317,15 @@ template FMT_CONSTEXPR bool is_name_start(Char c) { return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; } -template +template FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end, - IDHandler&& handler) -> const Char* { - FMT_ASSERT(begin != end, ""); + Handler&& handler) -> const Char* { Char c = *begin; if (c >= '0' && c <= '9') { int index = 0; + constexpr int max = (std::numeric_limits::max)(); if (c != '0') - index = - parse_nonnegative_int(begin, end, (std::numeric_limits::max)()); + index = parse_nonnegative_int(begin, end, max); else ++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; } -template +template 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; if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler); handler.on_auto(); @@ -2369,65 +2358,58 @@ FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char* begin, const Char* end, template struct dynamic_spec_id_handler { basic_format_parse_context& ctx; - dynamic_spec spec; + arg_ref& ref; FMT_CONSTEXPR void on_auto() { - spec.kind = dynamic_spec_kind::index; - spec.value = ctx.next_arg_id(); - ctx.check_dynamic_spec(spec.value); + int id = ctx.next_arg_id(); + ref = arg_ref(id); + ctx.check_dynamic_spec(id); } FMT_CONSTEXPR void on_index(int id) { - spec.kind = dynamic_spec_kind::index; - spec.value = id; + ref = arg_ref(id); ctx.check_arg_id(id); ctx.check_dynamic_spec(id); } FMT_CONSTEXPR void on_name(basic_string_view id) { - spec.kind = dynamic_spec_kind::name; - spec.name = id; + ref = arg_ref(id); ctx.check_arg_id(id); } }; -template struct parse_dynamic_spec_result { - const Char* end; - dynamic_spec spec; -}; - // Parses [integer | "{" [arg_id] "}"]. template FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end, + int& value, arg_ref& ref, basic_format_parse_context& ctx) - -> parse_dynamic_spec_result { + -> const Char* { FMT_ASSERT(begin != end, ""); if ('0' <= *begin && *begin <= '9') { - int value = parse_nonnegative_int(begin, end, -1); - if (value != -1) return {begin, {dynamic_spec_kind::value, {value}}}; - throw_format_error("number is too big"); + int val = parse_nonnegative_int(begin, end, -1); + if (val != -1) + value = val; + else + throw_format_error("number is too big"); } else if (*begin == '{') { ++begin; - auto handler = - dynamic_spec_id_handler{ctx, {dynamic_spec_kind::none, {}}}; + auto handler = dynamic_spec_id_handler{ctx, ref}; if (begin != end) begin = parse_arg_id(begin, end, handler); - if (begin != end && *begin == '}') { - ++begin; - return {begin, handler.spec}; - } + if (begin != end && *begin == '}') return ++begin; throw_format_error("invalid format string"); } - return {begin, {dynamic_spec_kind::none, {}}}; + return begin; } template FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, + int& value, arg_ref& ref, basic_format_parse_context& ctx) - -> parse_dynamic_spec_result { + -> const Char* { ++begin; if (begin == end || *begin == '}') { 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 @@ -2552,42 +2534,15 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs( if (++begin == end) return {begin, specs}; } - auto width = parse_dynamic_spec(begin, end, 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(width.spec.value); - break; - case dynamic_spec_kind::name: - specs.width_ref = arg_ref(width.spec.name); - break; - } - begin = width.end; + begin = parse_dynamic_spec(begin, end, specs.width, specs.width_ref, ctx); if (begin == end) return {begin, specs}; // Parse precision. if (*begin == '.') { - auto precision = parse_precision(begin, end, ctx); - switch (precision.spec.kind) { - 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(precision.spec.value); - break; - case dynamic_spec_kind::name: - specs.precision_ref = arg_ref(precision.spec.name); - break; - } + begin = + parse_precision(begin, end, specs.precision, specs.precision_ref, ctx); if (is_integral_type(arg_type) || arg_type == type::pointer_type) throw_format_error("precision not allowed for this argument type"); - begin = precision.end; if (begin == end) return {begin, specs}; } @@ -2833,7 +2788,7 @@ constexpr int invalid_arg_index = -1; #if FMT_USE_NONTYPE_TEMPLATE_ARGS template constexpr auto get_arg_index_by_name(basic_string_view name) -> int { - if constexpr (detail::is_statically_named_arg()) { + if constexpr (is_statically_named_arg()) { if (name == T::name) return N; } if constexpr (sizeof...(Args) > 0)