mirror of
https://github.com/fmtlib/fmt.git
synced 2025-11-02 23:21:45 +01:00
Refactor format spec parsing
This commit is contained in:
@@ -520,88 +520,23 @@ TEST(format_test, constexpr_parse_arg_id) {
|
||||
static_assert(parse_arg_id("foo:").name.size() == 3, "");
|
||||
}
|
||||
|
||||
struct test_format_specs_handler {
|
||||
fmt::detail::compile_parse_context<char> ctx =
|
||||
fmt::detail::compile_parse_context<char>({}, 43, nullptr);
|
||||
enum result { none, hash, zero, loc, error };
|
||||
result res = none;
|
||||
|
||||
fmt::align_t alignment = fmt::align::none;
|
||||
fmt::sign_t sign = fmt::sign::none;
|
||||
char fill = 0;
|
||||
int width = 0;
|
||||
fmt::detail::arg_ref<char> width_ref;
|
||||
int precision = 0;
|
||||
fmt::detail::arg_ref<char> precision_ref;
|
||||
fmt::presentation_type type = fmt::presentation_type::none;
|
||||
|
||||
// Workaround for MSVC2017 bug that results in "expression did not evaluate
|
||||
// to a constant" with compiler-generated copy ctor.
|
||||
constexpr test_format_specs_handler() {}
|
||||
constexpr test_format_specs_handler(const test_format_specs_handler& other) =
|
||||
default;
|
||||
|
||||
constexpr auto parse_context() -> fmt::format_parse_context& { return ctx; }
|
||||
|
||||
constexpr void on_align(fmt::align_t a) { alignment = a; }
|
||||
constexpr void on_fill(fmt::string_view f) { fill = f[0]; }
|
||||
constexpr void on_sign(fmt::sign_t s) { sign = s; }
|
||||
constexpr void on_hash() { res = hash; }
|
||||
constexpr void on_zero() { res = zero; }
|
||||
constexpr void on_localized() { res = loc; }
|
||||
|
||||
constexpr void on_width(const fmt::detail::dynamic_spec<char>& spec) {
|
||||
switch (spec.kind) {
|
||||
case fmt::detail::dynamic_spec_kind::none:
|
||||
break;
|
||||
case fmt::detail::dynamic_spec_kind::value:
|
||||
width = spec.value;
|
||||
break;
|
||||
case fmt::detail::dynamic_spec_kind::index:
|
||||
width_ref = spec.value;
|
||||
break;
|
||||
case fmt::detail::dynamic_spec_kind::name:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void on_precision(const fmt::detail::dynamic_spec<char>& spec) {
|
||||
switch (spec.kind) {
|
||||
case fmt::detail::dynamic_spec_kind::none:
|
||||
break;
|
||||
case fmt::detail::dynamic_spec_kind::value:
|
||||
precision = spec.value;
|
||||
break;
|
||||
case fmt::detail::dynamic_spec_kind::index:
|
||||
precision_ref = spec.value;
|
||||
break;
|
||||
case fmt::detail::dynamic_spec_kind::name:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void end_precision() {}
|
||||
constexpr void on_type(fmt::presentation_type t) { type = t; }
|
||||
constexpr void on_error(const char*) { res = error; }
|
||||
};
|
||||
|
||||
template <size_t N>
|
||||
constexpr test_format_specs_handler parse_test_specs(const char (&s)[N]) {
|
||||
auto h = test_format_specs_handler();
|
||||
fmt::detail::parse_format_specs(s, s + N - 1, h);
|
||||
return h;
|
||||
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),
|
||||
43, nullptr);
|
||||
auto result = fmt::detail::parse_format_specs(s, s + N - 1, ctx,
|
||||
fmt::detail::type::float_type);
|
||||
return result.specs;
|
||||
}
|
||||
|
||||
TEST(core_test, constexpr_parse_format_specs) {
|
||||
using handler = test_format_specs_handler;
|
||||
static_assert(parse_test_specs("<").alignment == fmt::align::left, "");
|
||||
static_assert(parse_test_specs("*^").fill == '*', "");
|
||||
static_assert(parse_test_specs("<").align == fmt::align::left, "");
|
||||
static_assert(parse_test_specs("*^").fill[0] == '*', "");
|
||||
static_assert(parse_test_specs("+").sign == fmt::sign::plus, "");
|
||||
static_assert(parse_test_specs("-").sign == fmt::sign::minus, "");
|
||||
static_assert(parse_test_specs(" ").sign == fmt::sign::space, "");
|
||||
static_assert(parse_test_specs("#").res == handler::hash, "");
|
||||
static_assert(parse_test_specs("0").res == handler::zero, "");
|
||||
static_assert(parse_test_specs("L").res == handler::loc, "");
|
||||
static_assert(parse_test_specs("#").alt, "");
|
||||
static_assert(parse_test_specs("0").align == fmt::align::numeric, "");
|
||||
static_assert(parse_test_specs("L").localized, "");
|
||||
static_assert(parse_test_specs("42").width == 42, "");
|
||||
static_assert(parse_test_specs("{42}").width_ref.val.index == 42, "");
|
||||
static_assert(parse_test_specs(".42").precision == 42, "");
|
||||
@@ -609,59 +544,6 @@ TEST(core_test, constexpr_parse_format_specs) {
|
||||
static_assert(parse_test_specs("d").type == fmt::presentation_type::dec, "");
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
constexpr fmt::detail::dynamic_format_specs<char> parse_dynamic_specs(
|
||||
const char (&s)[N]) {
|
||||
auto specs = fmt::detail::dynamic_format_specs<char>();
|
||||
auto ctx = fmt::detail::compile_parse_context<char>({}, 43, nullptr);
|
||||
auto h =
|
||||
fmt::detail::dynamic_specs_handler<fmt::format_parse_context>(specs, ctx);
|
||||
parse_format_specs(s, s + N - 1, h);
|
||||
return specs;
|
||||
}
|
||||
|
||||
TEST(format_test, constexpr_dynamic_specs_handler) {
|
||||
static_assert(parse_dynamic_specs("<").align == fmt::align::left, "");
|
||||
static_assert(parse_dynamic_specs("*^").fill[0] == '*', "");
|
||||
static_assert(parse_dynamic_specs("+").sign == fmt::sign::plus, "");
|
||||
static_assert(parse_dynamic_specs("-").sign == fmt::sign::minus, "");
|
||||
static_assert(parse_dynamic_specs(" ").sign == fmt::sign::space, "");
|
||||
static_assert(parse_dynamic_specs("#").alt, "");
|
||||
static_assert(parse_dynamic_specs("0").align == fmt::align::numeric, "");
|
||||
static_assert(parse_dynamic_specs("42").width == 42, "");
|
||||
static_assert(parse_dynamic_specs("{}").width_ref.val.index == 0, "");
|
||||
static_assert(parse_dynamic_specs("{42}").width_ref.val.index == 42, "");
|
||||
static_assert(parse_dynamic_specs(".42").precision == 42, "");
|
||||
static_assert(parse_dynamic_specs(".{}").precision_ref.val.index == 0, "");
|
||||
static_assert(parse_dynamic_specs(".{42}").precision_ref.val.index == 42, "");
|
||||
static_assert(parse_dynamic_specs("d").type == fmt::presentation_type::dec,
|
||||
"");
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
constexpr test_format_specs_handler check_specs(const char (&s)[N]) {
|
||||
fmt::detail::specs_checker<test_format_specs_handler> checker(
|
||||
test_format_specs_handler(), fmt::detail::type::double_type);
|
||||
parse_format_specs(s, s + N - 1, checker);
|
||||
return checker;
|
||||
}
|
||||
|
||||
TEST(format_test, constexpr_specs_checker) {
|
||||
using handler = test_format_specs_handler;
|
||||
static_assert(check_specs("<").alignment == fmt::align::left, "");
|
||||
static_assert(check_specs("*^").fill == '*', "");
|
||||
static_assert(check_specs("+").sign == fmt::sign::plus, "");
|
||||
static_assert(check_specs("-").sign == fmt::sign::minus, "");
|
||||
static_assert(check_specs(" ").sign == fmt::sign::space, "");
|
||||
static_assert(check_specs("#").res == handler::hash, "");
|
||||
static_assert(check_specs("0").res == handler::zero, "");
|
||||
static_assert(check_specs("42").width == 42, "");
|
||||
static_assert(check_specs("{42}").width_ref.val.index == 42, "");
|
||||
static_assert(check_specs(".42").precision == 42, "");
|
||||
static_assert(check_specs(".{42}").precision_ref.val.index == 42, "");
|
||||
static_assert(check_specs("d").type == fmt::presentation_type::dec, "");
|
||||
}
|
||||
|
||||
struct test_format_string_handler {
|
||||
constexpr void on_text(const char*, const char*) {}
|
||||
|
||||
|
||||
@@ -1058,50 +1058,50 @@ TEST(format_test, runtime_precision) {
|
||||
char format_str[buffer_size];
|
||||
safe_sprintf(format_str, "{0:.{%u", UINT_MAX);
|
||||
increment(format_str + 5);
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0.0), format_error,
|
||||
"invalid format string");
|
||||
size_t size = std::strlen(format_str);
|
||||
format_str[size] = '}';
|
||||
format_str[size + 1] = 0;
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0.0), format_error,
|
||||
"argument not found");
|
||||
format_str[size + 1] = '}';
|
||||
format_str[size + 2] = 0;
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0.0), format_error,
|
||||
"argument not found");
|
||||
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{"), 0), format_error,
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{"), 0.0), format_error,
|
||||
"invalid format string");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{}"), 0), format_error,
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{}"), 0.0), format_error,
|
||||
"cannot switch from manual to automatic argument indexing");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{?}}"), 0), format_error,
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{?}}"), 0.0), format_error,
|
||||
"invalid format string");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}"), 0, 0), format_error,
|
||||
"precision not allowed for this argument type");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0), format_error,
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0.0), format_error,
|
||||
"argument not found");
|
||||
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{0:}}"), 0), format_error,
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{0:}}"), 0.0), format_error,
|
||||
"invalid format string");
|
||||
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, -1), format_error,
|
||||
"negative precision");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, (INT_MAX + 1u)),
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0.0, -1),
|
||||
format_error, "negative precision");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0.0, (INT_MAX + 1u)),
|
||||
format_error, "number is too big");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, -1l), format_error,
|
||||
"negative precision");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0.0, -1l),
|
||||
format_error, "negative precision");
|
||||
if (fmt::detail::const_check(sizeof(long) > sizeof(int))) {
|
||||
long value = INT_MAX;
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, (value + 1)),
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0.0, (value + 1)),
|
||||
format_error, "number is too big");
|
||||
}
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, (INT_MAX + 1ul)),
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0.0, (INT_MAX + 1ul)),
|
||||
format_error, "number is too big");
|
||||
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, '0'), format_error,
|
||||
"precision is not integer");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, 0.0), format_error,
|
||||
"precision is not integer");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0.0, '0'),
|
||||
format_error, "precision is not integer");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0.0, 0.0),
|
||||
format_error, "precision is not integer");
|
||||
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 42, 2), format_error,
|
||||
"precision not allowed for this argument type");
|
||||
@@ -1985,46 +1985,6 @@ TEST(format_test, non_null_terminated_format_string) {
|
||||
EXPECT_EQ("42", fmt::format(string_view("{}foo", 2), 42));
|
||||
}
|
||||
|
||||
struct variant {
|
||||
enum { int_type, string_type } type;
|
||||
explicit variant(int) : type(int_type) {}
|
||||
explicit variant(const char*) : type(string_type) {}
|
||||
};
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
template <> struct formatter<variant> : dynamic_formatter<> {
|
||||
auto format(variant value, format_context& ctx) -> decltype(ctx.out()) {
|
||||
if (value.type == variant::int_type)
|
||||
return dynamic_formatter<>::format(42, ctx);
|
||||
return dynamic_formatter<>::format("foo", ctx);
|
||||
}
|
||||
};
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
TEST(format_test, dynamic_formatter) {
|
||||
auto num = variant(42);
|
||||
auto str = variant("foo");
|
||||
EXPECT_EQ("42", fmt::format("{:d}", num));
|
||||
EXPECT_EQ("foo", fmt::format("{:s}", str));
|
||||
EXPECT_EQ(" 42 foo ", fmt::format("{:{}} {:{}}", num, 3, str, 4));
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{}}"), num), format_error,
|
||||
"cannot switch from manual to automatic argument indexing");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{:{0}}"), num), format_error,
|
||||
"cannot switch from automatic to manual argument indexing");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{:+}"), str), format_error,
|
||||
"format specifier requires numeric argument");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{:-}"), str), format_error,
|
||||
"format specifier requires numeric argument");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{: }"), str), format_error,
|
||||
"format specifier requires numeric argument");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{:#}"), str), format_error,
|
||||
"format specifier requires numeric argument");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{:0}"), str), format_error,
|
||||
"format specifier requires numeric argument");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{:.2}"), num), format_error,
|
||||
"precision not allowed for this argument type");
|
||||
}
|
||||
|
||||
namespace adl_test {
|
||||
namespace fmt {
|
||||
namespace detail {
|
||||
|
||||
@@ -464,13 +464,11 @@ template <class charT> struct formatter<std::complex<double>, charT> {
|
||||
public:
|
||||
FMT_CONSTEXPR typename basic_format_parse_context<charT>::iterator parse(
|
||||
basic_format_parse_context<charT>& ctx) {
|
||||
using handler_type =
|
||||
detail::dynamic_specs_handler<basic_format_parse_context<charT>>;
|
||||
detail::specs_checker<handler_type> handler(handler_type(specs_, ctx),
|
||||
detail::type::string_type);
|
||||
auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
|
||||
auto result = parse_format_specs(ctx.begin(), ctx.end(), ctx,
|
||||
detail::type::string_type);
|
||||
specs_ = result.specs;
|
||||
detail::parse_float_type_spec(specs_, detail::error_handler());
|
||||
return it;
|
||||
return result.end;
|
||||
}
|
||||
|
||||
template <class FormatContext>
|
||||
|
||||
Reference in New Issue
Block a user