forked from fmtlib/fmt
Simplify format string parsing
This commit is contained in:
@ -2190,7 +2190,8 @@ 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 reusing 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 = 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;
|
||||||
};
|
};
|
||||||
@ -2461,25 +2462,19 @@ FMT_CONSTEXPR inline void require_numeric_argument(type arg_type) {
|
|||||||
throw_format_error("format specifier requires numeric argument");
|
throw_format_error("format specifier requires numeric argument");
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char> struct parse_format_specs_result {
|
|
||||||
const Char* end;
|
|
||||||
dynamic_format_specs<Char> specs;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parses standard format specifiers.
|
// Parses standard format specifiers.
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(
|
FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(
|
||||||
const Char* begin, const Char* end, basic_format_parse_context<Char>& ctx,
|
const Char* begin, const Char* end, dynamic_format_specs<Char>& specs,
|
||||||
type arg_type) -> parse_format_specs_result<Char> {
|
basic_format_parse_context<Char>& ctx, type arg_type) -> const Char* {
|
||||||
auto specs = dynamic_format_specs<Char>();
|
|
||||||
if (1 < end - begin && begin[1] == '}' && is_ascii_letter(*begin) &&
|
if (1 < end - begin && begin[1] == '}' && is_ascii_letter(*begin) &&
|
||||||
*begin != 'L') {
|
*begin != 'L') {
|
||||||
specs.type = parse_presentation_type(*begin++);
|
specs.type = parse_presentation_type(*begin++);
|
||||||
if (specs.type == presentation_type::none)
|
if (specs.type == presentation_type::none)
|
||||||
throw_format_error("invalid type specifier");
|
throw_format_error("invalid type specifier");
|
||||||
return {begin, specs};
|
return begin;
|
||||||
}
|
}
|
||||||
if (begin == end) return {begin, specs};
|
if (begin == end) return begin;
|
||||||
|
|
||||||
auto align = parse_align(begin, end);
|
auto align = parse_align(begin, end);
|
||||||
if (align.align != align::none) {
|
if (align.align != align::none) {
|
||||||
@ -2488,7 +2483,7 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(
|
|||||||
if (fill_size > 0) specs.fill = {begin, to_unsigned(fill_size)};
|
if (fill_size > 0) specs.fill = {begin, to_unsigned(fill_size)};
|
||||||
}
|
}
|
||||||
begin = align.end;
|
begin = align.end;
|
||||||
if (begin == end) return {begin, specs};
|
if (begin == end) return begin;
|
||||||
|
|
||||||
// Parse sign.
|
// Parse sign.
|
||||||
switch (to_ascii(*begin)) {
|
switch (to_ascii(*begin)) {
|
||||||
@ -2515,12 +2510,12 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(
|
|||||||
throw_format_error("format specifier requires signed argument");
|
throw_format_error("format specifier requires signed argument");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (begin == end) return {begin, specs};
|
if (begin == end) return begin;
|
||||||
|
|
||||||
if (*begin == '#') {
|
if (*begin == '#') {
|
||||||
require_numeric_argument(arg_type);
|
require_numeric_argument(arg_type);
|
||||||
specs.alt = true;
|
specs.alt = true;
|
||||||
if (++begin == end) return {begin, specs};
|
if (++begin == end) return begin;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse zero flag.
|
// Parse zero flag.
|
||||||
@ -2531,11 +2526,11 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(
|
|||||||
specs.align = align::numeric;
|
specs.align = align::numeric;
|
||||||
specs.fill[0] = Char('0');
|
specs.fill[0] = Char('0');
|
||||||
}
|
}
|
||||||
if (++begin == end) return {begin, specs};
|
if (++begin == end) return begin;
|
||||||
}
|
}
|
||||||
|
|
||||||
begin = parse_dynamic_spec(begin, end, specs.width, specs.width_ref, ctx);
|
begin = parse_dynamic_spec(begin, end, specs.width, specs.width_ref, ctx);
|
||||||
if (begin == end) return {begin, specs};
|
if (begin == end) return begin;
|
||||||
|
|
||||||
// Parse precision.
|
// Parse precision.
|
||||||
if (*begin == '.') {
|
if (*begin == '.') {
|
||||||
@ -2543,7 +2538,7 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(
|
|||||||
parse_precision(begin, end, specs.precision, specs.precision_ref, ctx);
|
parse_precision(begin, end, specs.precision, specs.precision_ref, ctx);
|
||||||
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");
|
||||||
if (begin == end) return {begin, specs};
|
if (begin == end) return begin;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*begin == 'L') {
|
if (*begin == 'L') {
|
||||||
@ -2558,7 +2553,7 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(
|
|||||||
if (specs.type == presentation_type::none)
|
if (specs.type == presentation_type::none)
|
||||||
throw_format_error("invalid type specifier");
|
throw_format_error("invalid type specifier");
|
||||||
}
|
}
|
||||||
return {begin, specs};
|
return begin;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char, typename Handler>
|
template <typename Char, typename Handler>
|
||||||
@ -2911,8 +2906,7 @@ struct formatter<T, Char,
|
|||||||
auto begin = ctx.begin(), end = ctx.end();
|
auto begin = ctx.begin(), end = ctx.end();
|
||||||
if (begin == end) return begin;
|
if (begin == end) return begin;
|
||||||
auto type = detail::type_constant<T, Char>::value;
|
auto type = detail::type_constant<T, Char>::value;
|
||||||
auto result = detail::parse_format_specs(begin, end, ctx, type);
|
begin = detail::parse_format_specs(begin, end, specs_, ctx, type);
|
||||||
specs_ = result.specs;
|
|
||||||
auto eh = detail::error_handler();
|
auto eh = detail::error_handler();
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case detail::type::none_type:
|
case detail::type::none_type:
|
||||||
@ -2937,19 +2931,19 @@ struct formatter<T, Char,
|
|||||||
break;
|
break;
|
||||||
case detail::type::float_type:
|
case detail::type::float_type:
|
||||||
if (detail::const_check(FMT_USE_FLOAT))
|
if (detail::const_check(FMT_USE_FLOAT))
|
||||||
detail::parse_float_type_spec(result.specs, eh);
|
detail::parse_float_type_spec(specs_, eh);
|
||||||
else
|
else
|
||||||
FMT_ASSERT(false, "float support disabled");
|
FMT_ASSERT(false, "float support disabled");
|
||||||
break;
|
break;
|
||||||
case detail::type::double_type:
|
case detail::type::double_type:
|
||||||
if (detail::const_check(FMT_USE_DOUBLE))
|
if (detail::const_check(FMT_USE_DOUBLE))
|
||||||
detail::parse_float_type_spec(result.specs, eh);
|
detail::parse_float_type_spec(specs_, eh);
|
||||||
else
|
else
|
||||||
FMT_ASSERT(false, "double support disabled");
|
FMT_ASSERT(false, "double support disabled");
|
||||||
break;
|
break;
|
||||||
case detail::type::long_double_type:
|
case detail::type::long_double_type:
|
||||||
if (detail::const_check(FMT_USE_LONG_DOUBLE))
|
if (detail::const_check(FMT_USE_LONG_DOUBLE))
|
||||||
detail::parse_float_type_spec(result.specs, eh);
|
detail::parse_float_type_spec(specs_, eh);
|
||||||
else
|
else
|
||||||
FMT_ASSERT(false, "long double support disabled");
|
FMT_ASSERT(false, "long double support disabled");
|
||||||
break;
|
break;
|
||||||
@ -2967,7 +2961,7 @@ struct formatter<T, Char,
|
|||||||
// formatter specializations.
|
// formatter specializations.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return result.end;
|
return begin;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <detail::type U = detail::type_constant<T, Char>::value,
|
template <detail::type U = detail::type_constant<T, Char>::value,
|
||||||
|
@ -3871,16 +3871,15 @@ class bytes {
|
|||||||
|
|
||||||
template <> struct formatter<bytes> {
|
template <> struct formatter<bytes> {
|
||||||
private:
|
private:
|
||||||
detail::dynamic_format_specs<char> specs_;
|
detail::dynamic_format_specs<> specs_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template <typename ParseContext>
|
template <typename ParseContext>
|
||||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
auto result = parse_format_specs(ctx.begin(), ctx.end(), ctx,
|
auto end = parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
|
||||||
detail::type::string_type);
|
detail::type::string_type);
|
||||||
specs_ = result.specs;
|
|
||||||
detail::check_string_type_spec(specs_.type, detail::error_handler());
|
detail::check_string_type_spec(specs_.type, detail::error_handler());
|
||||||
return result.end;
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
@ -3913,16 +3912,15 @@ template <typename T> auto group_digits(T value) -> group_digits_view<T> {
|
|||||||
|
|
||||||
template <typename T> struct formatter<group_digits_view<T>> : formatter<T> {
|
template <typename T> struct formatter<group_digits_view<T>> : formatter<T> {
|
||||||
private:
|
private:
|
||||||
detail::dynamic_format_specs<char> specs_;
|
detail::dynamic_format_specs<> specs_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template <typename ParseContext>
|
template <typename ParseContext>
|
||||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
auto result =
|
auto end = parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
|
||||||
parse_format_specs(ctx.begin(), ctx.end(), ctx, detail::type::int_type);
|
detail::type::int_type);
|
||||||
specs_ = result.specs;
|
|
||||||
detail::check_string_type_spec(specs_.type, detail::error_handler());
|
detail::check_string_type_spec(specs_.type, detail::error_handler());
|
||||||
return result.end;
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
@ -4139,16 +4137,15 @@ void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
|
|||||||
visit_format_arg(custom_formatter<Char>{parse_context, context}, arg);
|
visit_format_arg(custom_formatter<Char>{parse_context, context}, arg);
|
||||||
return parse_context.begin();
|
return parse_context.begin();
|
||||||
}
|
}
|
||||||
auto result = parse_format_specs(begin, end, parse_context, arg.type());
|
auto specs = detail::dynamic_format_specs<Char>();
|
||||||
|
begin = parse_format_specs(begin, end, specs, parse_context, arg.type());
|
||||||
detail::handle_dynamic_spec<detail::width_checker>(
|
detail::handle_dynamic_spec<detail::width_checker>(
|
||||||
result.specs.width, result.specs.width_ref, context);
|
specs.width, specs.width_ref, context);
|
||||||
detail::handle_dynamic_spec<detail::precision_checker>(
|
detail::handle_dynamic_spec<detail::precision_checker>(
|
||||||
result.specs.precision, result.specs.precision_ref, context);
|
specs.precision, specs.precision_ref, context);
|
||||||
begin = result.end;
|
|
||||||
if (begin == end || *begin != '}')
|
if (begin == end || *begin != '}')
|
||||||
on_error("missing '}' in format string");
|
on_error("missing '}' in format string");
|
||||||
auto f =
|
auto f = arg_formatter<Char>{context.out(), specs, context.locale()};
|
||||||
arg_formatter<Char>{context.out(), result.specs, context.locale()};
|
|
||||||
context.advance_to(visit_format_arg(f, arg));
|
context.advance_to(visit_format_arg(f, arg));
|
||||||
return begin;
|
return begin;
|
||||||
}
|
}
|
||||||
|
@ -523,9 +523,10 @@ TEST(format_test, constexpr_parse_arg_id) {
|
|||||||
template <size_t N> constexpr auto parse_test_specs(const char (&s)[N]) {
|
template <size_t N> constexpr auto parse_test_specs(const char (&s)[N]) {
|
||||||
auto ctx = fmt::detail::compile_parse_context<char>(fmt::string_view(s, N),
|
auto ctx = fmt::detail::compile_parse_context<char>(fmt::string_view(s, N),
|
||||||
43, nullptr);
|
43, nullptr);
|
||||||
auto result = fmt::detail::parse_format_specs(s, s + N - 1, ctx,
|
auto specs = fmt::detail::dynamic_format_specs<>();
|
||||||
fmt::detail::type::float_type);
|
fmt::detail::parse_format_specs(s, s + N - 1, specs, ctx,
|
||||||
return result.specs;
|
fmt::detail::type::float_type);
|
||||||
|
return specs;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(core_test, constexpr_parse_format_specs) {
|
TEST(core_test, constexpr_parse_format_specs) {
|
||||||
|
@ -518,11 +518,10 @@ template <class charT> struct formatter<std::complex<double>, charT> {
|
|||||||
public:
|
public:
|
||||||
FMT_CONSTEXPR typename basic_format_parse_context<charT>::iterator parse(
|
FMT_CONSTEXPR typename basic_format_parse_context<charT>::iterator parse(
|
||||||
basic_format_parse_context<charT>& ctx) {
|
basic_format_parse_context<charT>& ctx) {
|
||||||
auto result = parse_format_specs(ctx.begin(), ctx.end(), ctx,
|
auto end = parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
|
||||||
detail::type::string_type);
|
detail::type::string_type);
|
||||||
specs_ = result.specs;
|
|
||||||
detail::parse_float_type_spec(specs_, detail::error_handler());
|
detail::parse_float_type_spec(specs_, detail::error_handler());
|
||||||
return result.end;
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class FormatContext>
|
template <class FormatContext>
|
||||||
|
Reference in New Issue
Block a user