diff --git a/stl/inc/format b/stl/inc/format index a94aa8f8496..16466b6980d 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -36,11 +36,13 @@ class format_error : public runtime_error { enum class _Align { _None, _Left, _Right, _Center }; +enum class _Sign { _None, _Plus, _Minus, _Space }; + struct _Auto_id_tag {}; // clang-format off template -concept _Parse_spec_callbacks = requires(_Ty _At, basic_string_view<_CharT> _Sv, _Align _Aln) { +concept _Parse_spec_callbacks = requires(_Ty _At, basic_string_view<_CharT> _Sv, _Align _Aln, _Sign _Sgn) { { _At._On_align(_Aln) } -> same_as; { _At._On_fill(_Sv) } -> same_as; { _At._On_width(_STD declval()) } -> same_as; @@ -49,8 +51,10 @@ concept _Parse_spec_callbacks = requires(_Ty _At, basic_string_view<_CharT> _Sv, { _At._On_precision(_STD declval()) } -> same_as; { _At._On_dynamic_precision(_STD declval()) } -> same_as; { _At._On_dynamic_precision(_STD declval<_Auto_id_tag>()) } -> same_as; - - /* { _At._On_type(_STD declval<_CharT>()) } -> same_as; */ + { _At._On_sign(_Sgn) } -> same_as; + { _At._On_hash() } -> same_as; + { _At._On_zero() } -> same_as; + { _At._On_type(_STD declval<_CharT>()) } -> same_as; }; template concept _Parse_arg_id_callbacks = requires(_Ty _At) { @@ -118,11 +122,7 @@ constexpr const _CharT* _Parse_arg_id(const _CharT* _Begin, const _CharT* _End, template _Callbacks_type> constexpr const _CharT* _Parse_align(const _CharT* _Begin, const _CharT* _End, _Callbacks_type&& _Callbacks) { - // done with parsing, or reached the end of a replacement field. - if (_Begin == _End || *_Begin == '}') { - return _Begin; - } - + _STL_INTERNAL_CHECK(_Begin != _End && *_Begin != '}'); // align and fill auto _Parsed_align = _Align::_None; auto _Align_pt = _Begin + 1; @@ -240,6 +240,68 @@ constexpr const _CharT* _Parse_precision(const _CharT* _Begin, const _CharT* _En return _Begin; } +template _Callbacks_type> +constexpr const _CharT* _Parse_format_specs(const _CharT* _Begin, const _CharT* _End, _Callbacks_type&& _Callbacks) { + if (_Begin == _End || *_Begin == '}') { + return _Begin; + } + + _Begin = _Parse_align(_Begin, _End, _Callbacks); + if (_Begin == _End) { + return _Begin; + } + + switch (*_Begin) { + case '+': + _Callbacks._On_sign(_Sign::_Plus); + ++_Begin; + break; + case '-': + _Callbacks._On_sign(_Sign::_Minus); + ++_Begin; + break; + case ' ': + _Callbacks._On_sign(_Sign::_Space); + ++_Begin; + break; + default: + break; + } + + if (_Begin == _End) { + return _Begin; + } + + if (*_Begin == '#') { + _Callbacks._On_hash(); + if (++_Begin == _End) { + return _Begin; + } + } + + if (*_Begin == '0') { + _Callbacks._On_zero(); + if (++_Begin == _End) { + return _Begin; + } + } + + _Begin = _Parse_width(_Begin, _End, _Callbacks); + if (_Begin == _End) { + return _Begin; + } + + if (*_Begin == '.') { + _Begin = _Parse_precision(_Begin, _End, _Callbacks); + } + + // If there's anything remaining we assume it's a type. + if (_Begin != _End && *_Begin != '}') { + _Callbacks._On_type(*_Begin++); + } + return _Begin; +} + template struct formatter; diff --git a/tests/std/tests/P0645R10_text_formatting_parsing/test.cpp b/tests/std/tests/P0645R10_text_formatting_parsing/test.cpp index 187dca249b5..85da914a9ac 100644 --- a/tests/std/tests/P0645R10_text_formatting_parsing/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_parsing/test.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -32,6 +33,7 @@ struct choose_literal { template struct testing_callbacks { _Align expected_alignment = _Align::_None; + _Sign expected_sign = _Sign::_None; basic_string_view expected_fill; int expected_width = -1; int expected_dynamic_width = -1; @@ -39,6 +41,9 @@ struct testing_callbacks { int expected_precision = -1; int expected_dynamic_precision = -1; bool expected_auto_dynamic_precision = false; + bool expected_hash = false; + bool expected_zero = false; + CharT expected_type = '\0'; constexpr void _On_align(_Align aln) { assert(aln == expected_alignment); } @@ -63,6 +68,18 @@ struct testing_callbacks { constexpr void _On_dynamic_precision(_Auto_id_tag) { assert(expected_auto_dynamic_precision); } + constexpr void _On_sign(_Sign sgn) { + assert(sgn == expected_sign); + } + constexpr void _On_hash() { + assert(expected_hash); + } + constexpr void _On_zero() { + assert(expected_zero); + } + constexpr void _On_type(CharT type) { + assert(type == expected_type); + } }; template testing_callbacks(_Align, basic_string_view) -> testing_callbacks; @@ -93,23 +110,23 @@ constexpr bool test_parse_align() { auto parse_align_fn = _Parse_align>; using view_typ = basic_string_view; - view_typ s0(TYPED_LITERAL(CharT, "")); view_typ s1(TYPED_LITERAL(CharT, "*<")); view_typ s2(TYPED_LITERAL(CharT, "*>")); view_typ s3(TYPED_LITERAL(CharT, "*^")); - test_parse_helper(parse_align_fn, s0, false, view_typ::npos, {_Align::_None, view_typ(TYPED_LITERAL(CharT, ""))}); - test_parse_helper(parse_align_fn, s1, false, view_typ::npos, {_Align::_Left, view_typ(TYPED_LITERAL(CharT, "*"))}); - test_parse_helper(parse_align_fn, s2, false, view_typ::npos, {_Align::_Right, view_typ(TYPED_LITERAL(CharT, "*"))}); - test_parse_helper( - parse_align_fn, s3, false, view_typ::npos, {_Align::_Center, view_typ(TYPED_LITERAL(CharT, "*"))}); + test_parse_helper(parse_align_fn, s1, false, view_typ::npos, + {.expected_alignment = _Align::_Left, .expected_fill = view_typ(TYPED_LITERAL(CharT, "*"))}); + test_parse_helper(parse_align_fn, s2, false, view_typ::npos, + {.expected_alignment = _Align::_Right, .expected_fill = view_typ(TYPED_LITERAL(CharT, "*"))}); + test_parse_helper(parse_align_fn, s3, false, view_typ::npos, + {.expected_alignment = _Align::_Center, .expected_fill = view_typ(TYPED_LITERAL(CharT, "*"))}); if constexpr (same_as) { // This is a CJK character where the least significant byte is the same as ascii '>', // libfmt and initial drafts of narrowed characters when parsing alignments, causing // \x343E (which is from CJK unified ideographs extension A) and similar characters to parse as // an alignment specifier. auto s4 = L"*\x343E"sv; - test_parse_helper(parse_align_fn, s4, false, view_typ::npos, {_Align::_None, L"*"sv}); + test_parse_helper(parse_align_fn, s4, false, view_typ::npos, {.expected_fill = L"*"sv}); } return true; @@ -200,22 +217,76 @@ constexpr bool test_parse_precision() { return true; } +template +constexpr bool test_parse_format_specs() { + auto parse_format_specs_fn = _Parse_format_specs>; + using view_typ = basic_string_view; + + view_typ s0(TYPED_LITERAL(CharT, "6}")); + view_typ s1(TYPED_LITERAL(CharT, "*<6")); + view_typ s2(TYPED_LITERAL(CharT, "*>6}")); + view_typ s3(TYPED_LITERAL(CharT, "*^6}")); + view_typ s4(TYPED_LITERAL(CharT, "6d}")); + view_typ s5(TYPED_LITERAL(CharT, "*^+4.4a}")); + view_typ s6(TYPED_LITERAL(CharT, "*^+#04.4a}")); + test_parse_helper(parse_format_specs_fn, s0, false, s0.size() - 1, {.expected_width = 6}); + test_parse_helper(parse_format_specs_fn, s1, false, s1.size(), + {.expected_alignment = _Align::_Left, + .expected_fill = view_typ(TYPED_LITERAL(CharT, "*")), + .expected_width = 6}); + test_parse_helper(parse_format_specs_fn, s2, false, s2.size() - 1, + {.expected_alignment = _Align::_Right, + .expected_fill = view_typ(TYPED_LITERAL(CharT, "*")), + .expected_width = 6}); + test_parse_helper(parse_format_specs_fn, s3, false, s3.size() - 1, + {.expected_alignment = _Align::_Center, + .expected_fill = view_typ(TYPED_LITERAL(CharT, "*")), + .expected_width = 6}); + test_parse_helper(parse_format_specs_fn, s4, false, s4.size() - 1, {.expected_width = 6, .expected_type = 'd'}); + test_parse_helper(parse_format_specs_fn, s5, false, s5.size() - 1, + {.expected_alignment = _Align::_Center, + .expected_sign = _Sign::_Plus, + .expected_fill = view_typ(TYPED_LITERAL(CharT, "*")), + .expected_width = 4, + .expected_precision = 4, + .expected_type = 'a'}); + test_parse_helper(parse_format_specs_fn, s6, false, s6.size() - 1, + {.expected_alignment = _Align::_Center, + .expected_sign = _Sign::_Plus, + .expected_fill = view_typ(TYPED_LITERAL(CharT, "*")), + .expected_width = 4, + .expected_precision = 4, + .expected_hash = true, + .expected_zero = true, + .expected_type = 'a'}); + return true; +} + int main() { test_parse_align(); test_parse_align(); static_assert(test_parse_align()); static_assert(test_parse_align()); + test_parse_arg_id(); test_parse_arg_id(); static_assert(test_parse_arg_id()); static_assert(test_parse_arg_id()); + test_parse_width(); test_parse_width(); static_assert(test_parse_width()); static_assert(test_parse_width()); + test_parse_precision(); test_parse_precision(); static_assert(test_parse_precision()); static_assert(test_parse_precision()); + + test_parse_format_specs(); + test_parse_format_specs(); + static_assert(test_parse_format_specs()); + static_assert(test_parse_format_specs()); + return 0; }