aboutsummaryrefslogtreecommitdiff
path: root/libcxx
diff options
context:
space:
mode:
authorMark de Wever <koraq@xs4all.nl>2023-06-04 21:32:10 +0200
committerMark de Wever <koraq@xs4all.nl>2023-07-18 20:32:58 +0200
commiteb703341146b0ba46a47cc4a3878f4f0e0649d71 (patch)
treede843b94aaf5a066b8ad14926b553f903342fd31 /libcxx
parentf7f744a522927b6300b346dba8f453bdb913236c (diff)
downloadllvm-eb703341146b0ba46a47cc4a3878f4f0e0649d71.zip
llvm-eb703341146b0ba46a47cc4a3878f4f0e0649d71.tar.gz
llvm-eb703341146b0ba46a47cc4a3878f4f0e0649d71.tar.bz2
[NFC][libc++][format] Generalizes bracket parsing.
Both the tuple formatter and range formatter parse a bracket. Instead of implementing this twice do it in the generic parser. This is preparation to improve the diagnostics in the format library. Reviewed By: #libc, ldionne Differential Revision: https://reviews.llvm.org/D155264
Diffstat (limited to 'libcxx')
-rw-r--r--libcxx/include/__format/formatter_tuple.h18
-rw-r--r--libcxx/include/__format/parser_std_format_spec.h63
-rw-r--r--libcxx/include/__format/range_formatter.h14
3 files changed, 50 insertions, 45 deletions
diff --git a/libcxx/include/__format/formatter_tuple.h b/libcxx/include/__format/formatter_tuple.h
index 18d41d3..e06f945 100644
--- a/libcxx/include/__format/formatter_tuple.h
+++ b/libcxx/include/__format/formatter_tuple.h
@@ -51,18 +51,16 @@ struct _LIBCPP_TEMPLATE_VIS __formatter_tuple {
auto __begin = __parser_.__parse(__ctx, __format_spec::__fields_tuple);
auto __end = __ctx.end();
- if (__begin != __end) {
- if (*__begin == _CharT('m')) {
- if constexpr (sizeof...(_Args) == 2) {
- set_separator(_LIBCPP_STATICALLY_WIDEN(_CharT, ": "));
- set_brackets({}, {});
- ++__begin;
- } else
- std::__throw_format_error("The format specifier m requires a pair or a two-element tuple");
- } else if (*__begin == _CharT('n')) {
+ // Note 'n' is part of the type here
+ if (__parser_.__clear_brackets_)
+ set_brackets({}, {});
+ else if (__begin != __end && *__begin == _CharT('m')) {
+ if constexpr (sizeof...(_Args) == 2) {
+ set_separator(_LIBCPP_STATICALLY_WIDEN(_CharT, ": "));
set_brackets({}, {});
++__begin;
- }
+ } else
+ std::__throw_format_error("The format specifier m requires a pair or a two-element tuple");
}
if (__begin != __end && *__begin != _CharT('}'))
diff --git a/libcxx/include/__format/parser_std_format_spec.h b/libcxx/include/__format/parser_std_format_spec.h
index 2f7ccdf..6f60ba8 100644
--- a/libcxx/include/__format/parser_std_format_spec.h
+++ b/libcxx/include/__format/parser_std_format_spec.h
@@ -119,42 +119,47 @@ __substitute_arg_id(basic_format_arg<_Context> __format_arg) {
/// explicitly.
// TODO FMT Use an ABI tag for this struct.
struct __fields {
- uint8_t __sign_ : 1 {false};
- uint8_t __alternate_form_ : 1 {false};
- uint8_t __zero_padding_ : 1 {false};
- uint8_t __precision_ : 1 {false};
- uint8_t __locale_specific_form_ : 1 {false};
- uint8_t __type_ : 1 {false};
+ uint16_t __sign_ : 1 {false};
+ uint16_t __alternate_form_ : 1 {false};
+ uint16_t __zero_padding_ : 1 {false};
+ uint16_t __precision_ : 1 {false};
+ uint16_t __locale_specific_form_ : 1 {false};
+ uint16_t __type_ : 1 {false};
// Determines the valid values for fill.
//
// Originally the fill could be any character except { and }. Range-based
// formatters use the colon to mark the beginning of the
// underlying-format-spec. To avoid parsing ambiguities these formatter
// specializations prohibit the use of the colon as a fill character.
- uint8_t __use_range_fill_ : 1 {false};
+ uint16_t __use_range_fill_ : 1 {false};
+ uint16_t __clear_brackets_ : 1 {false};
+ uint16_t __consume_all_ : 1 {false};
};
// By not placing this constant in the formatter class it's not duplicated for
// char and wchar_t.
+inline constexpr __fields __fields_bool{.__locale_specific_form_ = true, .__type_ = true, .__consume_all_ = true};
inline constexpr __fields __fields_integral{
.__sign_ = true,
.__alternate_form_ = true,
.__zero_padding_ = true,
.__locale_specific_form_ = true,
- .__type_ = true};
+ .__type_ = true,
+ .__consume_all_ = true};
inline constexpr __fields __fields_floating_point{
.__sign_ = true,
.__alternate_form_ = true,
.__zero_padding_ = true,
.__precision_ = true,
.__locale_specific_form_ = true,
- .__type_ = true};
-inline constexpr __fields __fields_string{.__precision_ = true, .__type_ = true};
-inline constexpr __fields __fields_pointer{.__zero_padding_ = true, .__type_ = true};
+ .__type_ = true,
+ .__consume_all_ = true};
+inline constexpr __fields __fields_string{.__precision_ = true, .__type_ = true, .__consume_all_ = true};
+inline constexpr __fields __fields_pointer{.__zero_padding_ = true, .__type_ = true, .__consume_all_ = true};
# if _LIBCPP_STD_VER >= 23
-inline constexpr __fields __fields_tuple{.__use_range_fill_ = true};
-inline constexpr __fields __fields_range{.__use_range_fill_ = true};
+inline constexpr __fields __fields_tuple{.__use_range_fill_ = true, .__clear_brackets_ = true};
+inline constexpr __fields __fields_range{.__use_range_fill_ = true, .__clear_brackets_ = true};
inline constexpr __fields __fields_fill_align_width{};
# endif
@@ -330,15 +335,17 @@ public:
if (__fields.__locale_specific_form_ && __parse_locale_specific_form(__begin) && __begin == __end)
return __begin;
- if (__fields.__type_) {
+ if (__fields.__clear_brackets_ && __parse_clear_brackets(__begin) && __begin == __end)
+ return __begin;
+
+ if (__fields.__type_)
__parse_type(__begin);
- // When __type_ is false the calling parser is expected to do additional
- // parsing. In that case that parser should do the end of format string
- // validation.
- if (__begin != __end && *__begin != _CharT('}'))
- std::__throw_format_error("The format-spec should consume the input or end with a '}'");
- }
+ if (!__fields.__consume_all_)
+ return __begin;
+
+ if (__begin != __end && *__begin != _CharT('}'))
+ std::__throw_format_error("The format-spec should consume the input or end with a '}'");
return __begin;
}
@@ -377,7 +384,7 @@ public:
__sign __sign_ : 2 {__sign::__default};
bool __alternate_form_ : 1 {false};
bool __locale_specific_form_ : 1 {false};
- bool __reserved_0_ : 1 {false};
+ bool __clear_brackets_ : 1 {false};
__type __type_{__type::__default};
// These flags are only used for formatting chrono. Since the struct has
@@ -392,8 +399,8 @@ public:
bool __month_name_ : 1 {false};
- uint8_t __reserved_1_ : 2 {0};
- uint8_t __reserved_2_ : 6 {0};
+ uint8_t __reserved_0_ : 2 {0};
+ uint8_t __reserved_1_ : 6 {0};
// These two flags are only used internally and not part of the
// __parsed_specifications. Therefore put them at the end.
bool __width_as_arg_ : 1 {false};
@@ -625,6 +632,16 @@ private:
}
template <contiguous_iterator _Iterator>
+ _LIBCPP_HIDE_FROM_ABI constexpr bool __parse_clear_brackets(_Iterator& __begin) {
+ if (*__begin != _CharT('n'))
+ return false;
+
+ __clear_brackets_ = true;
+ ++__begin;
+ return true;
+ }
+
+ template <contiguous_iterator _Iterator>
_LIBCPP_HIDE_FROM_ABI constexpr void __parse_type(_Iterator& __begin) {
// Determines the type. It does not validate whether the selected type is
// valid. Most formatters have optional fields that are only allowed for
diff --git a/libcxx/include/__format/range_formatter.h b/libcxx/include/__format/range_formatter.h
index ca3ee9a..30d3a98 100644
--- a/libcxx/include/__format/range_formatter.h
+++ b/libcxx/include/__format/range_formatter.h
@@ -64,18 +64,8 @@ struct _LIBCPP_TEMPLATE_VIS range_formatter {
// The n field overrides a possible m type, therefore delay applying the
// effect of n until the type has been procesed.
- bool __clear_brackets = (*__begin == _CharT('n'));
- if (__clear_brackets) {
- ++__begin;
- if (__begin == __end) [[unlikely]] {
- // Since there is no more data, clear the brackets before returning.
- set_brackets({}, {});
- return __parse_empty_range_underlying_spec(__ctx, __begin);
- }
- }
-
__parse_type(__begin, __end);
- if (__clear_brackets)
+ if (__parser_.__clear_brackets_)
set_brackets({}, {});
if (__begin == __end) [[unlikely]]
return __parse_empty_range_underlying_spec(__ctx, __begin);
@@ -110,7 +100,7 @@ struct _LIBCPP_TEMPLATE_VIS range_formatter {
// [format.range.formatter]/6
// If the range-type is s or ?s, then there shall be no n option and no
// range-underlying-spec.
- if (__clear_brackets) {
+ if (__parser_.__clear_brackets_) {
if (__parser_.__type_ == __format_spec::__type::__string)
std::__throw_format_error("The n option and type s can't be used together");
std::__throw_format_error("The n option and type ?s can't be used together");