Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 70 additions & 8 deletions stl/inc/format
Original file line number Diff line number Diff line change
Expand Up @@ -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 <class _Ty, class _CharT>
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<void>;
{ _At._On_fill(_Sv) } -> same_as<void>;
{ _At._On_width(_STD declval<int>()) } -> same_as<void>;
Expand All @@ -49,8 +51,10 @@ concept _Parse_spec_callbacks = requires(_Ty _At, basic_string_view<_CharT> _Sv,
{ _At._On_precision(_STD declval<int>()) } -> same_as<void>;
{ _At._On_dynamic_precision(_STD declval<int>()) } -> same_as<void>;
{ _At._On_dynamic_precision(_STD declval<_Auto_id_tag>()) } -> same_as<void>;

/* { _At._On_type(_STD declval<_CharT>()) } -> same_as<void>; */
{ _At._On_sign(_Sgn) } -> same_as<void>;
{ _At._On_hash() } -> same_as<void>;
{ _At._On_zero() } -> same_as<void>;
{ _At._On_type(_STD declval<_CharT>()) } -> same_as<void>;
};
template <class _Ty, class _CharT>
concept _Parse_arg_id_callbacks = requires(_Ty _At) {
Expand Down Expand Up @@ -118,11 +122,7 @@ constexpr const _CharT* _Parse_arg_id(const _CharT* _Begin, const _CharT* _End,

template <class _CharT, _Parse_spec_callbacks<_CharT> _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;
Expand Down Expand Up @@ -240,6 +240,68 @@ constexpr const _CharT* _Parse_precision(const _CharT* _Begin, const _CharT* _En
return _Begin;
}

template <class _CharT, _Parse_spec_callbacks<_CharT> _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 <class _Ty, class _CharT = char>
struct formatter;

Expand Down
85 changes: 78 additions & 7 deletions tests/std/tests/P0645R10_text_formatting_parsing/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <assert.h>
#include <concepts>
#include <format>
#include <optional>
#include <stdio.h>
#include <string_view>

Expand Down Expand Up @@ -32,13 +33,17 @@ struct choose_literal<wchar_t> {
template <typename CharT>
struct testing_callbacks {
_Align expected_alignment = _Align::_None;
_Sign expected_sign = _Sign::_None;
basic_string_view<CharT> expected_fill;
int expected_width = -1;
int expected_dynamic_width = -1;
bool expected_auto_dynamic_width = false;
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);
}
Expand All @@ -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 <typename CharT>
testing_callbacks(_Align, basic_string_view<CharT>) -> testing_callbacks<CharT>;
Expand Down Expand Up @@ -93,23 +110,23 @@ constexpr bool test_parse_align() {
auto parse_align_fn = _Parse_align<CharT, testing_callbacks<CharT>>;
using view_typ = basic_string_view<CharT>;

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<CharT, wchar_t>) {
// This is a CJK character where the least significant byte is the same as ascii '>',
// libfmt and initial drafts of <format> 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;
Expand Down Expand Up @@ -200,22 +217,76 @@ constexpr bool test_parse_precision() {
return true;
}

template <typename CharT>
constexpr bool test_parse_format_specs() {
auto parse_format_specs_fn = _Parse_format_specs<CharT, testing_callbacks<CharT>>;
using view_typ = basic_string_view<CharT>;

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<char>();
test_parse_align<wchar_t>();
static_assert(test_parse_align<char>());
static_assert(test_parse_align<wchar_t>());

test_parse_arg_id<char>();
test_parse_arg_id<wchar_t>();
static_assert(test_parse_arg_id<char>());
static_assert(test_parse_arg_id<wchar_t>());

test_parse_width<char>();
test_parse_width<wchar_t>();
static_assert(test_parse_width<char>());
static_assert(test_parse_width<wchar_t>());

test_parse_precision<char>();
test_parse_precision<wchar_t>();
static_assert(test_parse_precision<char>());
static_assert(test_parse_precision<wchar_t>());

test_parse_format_specs<char>();
test_parse_format_specs<wchar_t>();
static_assert(test_parse_format_specs<char>());
static_assert(test_parse_format_specs<wchar_t>());

return 0;
}