From e926ae78acda3851a1517da25466157e68224c5e Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 27 Oct 2017 07:25:29 -0700 Subject: [PATCH] Add parse_format_string --- include/fmt/format.h | 155 +++++++++++++++++++++++++++---------------- 1 file changed, 96 insertions(+), 59 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index c1c87516..4f193308 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -815,6 +815,10 @@ class null_terminating_iterator { return null_terminating_iterator(ptr_ + n, end_); } + null_terminating_iterator operator-(difference_type n) { + return null_terminating_iterator(ptr_ - n, end_); + } + null_terminating_iterator operator+=(difference_type n) { ptr_ += n; return *this; @@ -3514,28 +3518,51 @@ constexpr Iterator parse_format_specs(Iterator it, SpecHandler &handler) { return it; } -// Formats a single argument. -template -const Char *do_format_arg(basic_buffer &buffer, - const basic_arg &arg, - Context &ctx) { - auto it = null_terminating_iterator(ctx); - basic_format_specs specs; - if (*it == ':') { - ctx.advance_to(pointer_from(++it)); - if (visit(custom_formatter(buffer, ctx), arg)) - return ctx.begin(); - specs_checker> - handler(specs_handler(specs, ctx), arg.type()); - it = parse_format_specs(it, handler); +template +void parse_format_string(Iterator it, Handler &handler) { + using char_type = typename std::iterator_traits::value_type; + auto start = it; + while (*it) { + char_type ch = *it++; + if (ch != '{' && ch != '}') continue; + if (*it == ch) { + handler.on_text(start, it); + start = ++it; + continue; + } + if (ch == '}') + handler.on_error("unmatched '}' in format string"); + handler.on_text(start, it - 1); + + struct id_adapter { + explicit id_adapter(Handler &h): handler(h) {} + + void operator()() { handler.on_arg_id(); } + void operator()(unsigned id) { handler.on_arg_id(id); } + void operator()(basic_string_view id) { + handler.on_arg_id(id); + } + + void on_error(const char *message) { handler.on_error(message); } + + Handler &handler; + } adapter(handler); + + it = parse_arg_id(it, adapter); + if (*it == '}') { + handler.on_replacement_field(it); + } else if (*it == ':') { + ++it; + it = handler.on_format_specs(it); + if (*it != '}') + handler.on_error("unknown format specifier"); + } else { + handler.on_error("missing '}' in format string"); + } + + start = ++it; } - - if (*it != '}') - FMT_THROW(format_error("missing '}' in format string")); - - // Format argument. - visit(ArgFormatter(buffer, ctx, specs), arg); - return pointer_from(it); + handler.on_text(start, it); } // Specifies whether to format T using the standard formatter. @@ -3688,47 +3715,57 @@ inline typename basic_context::format_arg template void vformat_to(basic_buffer &buffer, basic_string_view format_str, basic_args args) { - basic_context ctx(format_str, args); - auto start = internal::null_terminating_iterator(ctx); - auto it = start; - using internal::pointer_from; - while (*it) { - Char ch = *it++; - if (ch != '{' && ch != '}') continue; - if (*it == ch) { - buffer.append(pointer_from(start), pointer_from(it)); - start = ++it; - continue; + using iterator = internal::null_terminating_iterator; + + struct handler : internal::error_handler { + handler(basic_buffer &b, basic_string_view format_str, + basic_args args) + : buffer(b), context(format_str, args) {} + + void on_text(iterator begin, iterator end) { + buffer.append(pointer_from(begin), pointer_from(end)); } - if (ch == '}') - FMT_THROW(format_error("unmatched '}' in format string")); - buffer.append(pointer_from(start), pointer_from(it) - 1); + void on_arg_id() { arg = context.next_arg(); } + void on_arg_id(unsigned id) { + context.check_arg_id(id); + arg = context.get_arg(id); + } + void on_arg_id(basic_string_view id) { + arg = context.get_arg(id); + } + + void on_replacement_field(iterator it) { + context.advance_to(pointer_from(it)); + using internal::custom_formatter; + if (visit(custom_formatter(buffer, context), arg)) + return; + basic_format_specs specs; + visit(ArgFormatter(buffer, context, specs), arg); + } + + iterator on_format_specs(iterator it) { + context.advance_to(pointer_from(it)); + using internal::custom_formatter; + if (visit(custom_formatter(buffer, context), arg)) + return iterator(context); + basic_format_specs specs; + using internal::specs_handler; + internal::specs_checker> + handler(specs_handler(specs, context), arg.type()); + it = parse_format_specs(it, handler); + if (*it != '}') + on_error("missing '}' in format string"); + context.advance_to(pointer_from(it)); + visit(ArgFormatter(buffer, context, specs), arg); + return it; + } + + basic_buffer &buffer; + basic_context context; basic_arg arg; - struct id_handler : internal::error_handler { - id_handler(Context &c, basic_arg &a): context(c), arg(a) {} - - void operator()() { arg = context.next_arg(); } - void operator()(unsigned id) { - context.check_arg_id(id); - arg = context.get_arg(id); - } - void operator()(basic_string_view id) { - arg = context.get_arg(id); - } - - Context &context; - basic_arg &arg; - } handler(ctx, arg); - - it = parse_arg_id(it, handler); - ctx.advance_to(pointer_from(it)); - it = internal::do_format_arg(buffer, arg, ctx); - if (*it != '}') - FMT_THROW(format_error(fmt::format("unknown format specifier"))); - start = ++it; - } - buffer.append(pointer_from(start), pointer_from(it)); + } handler(buffer, format_str, args); + parse_format_string(iterator(format_str.begin(), format_str.end()), handler); } // Casts ``p`` to ``const void*`` for pointer formatting.