diff options
Diffstat (limited to 'libcxx/include/__format/parser_std_format_spec.h')
| -rw-r--r-- | libcxx/include/__format/parser_std_format_spec.h | 132 |
1 files changed, 90 insertions, 42 deletions
diff --git a/libcxx/include/__format/parser_std_format_spec.h b/libcxx/include/__format/parser_std_format_spec.h index 1425a953ebaa..c03cec979636 100644 --- a/libcxx/include/__format/parser_std_format_spec.h +++ b/libcxx/include/__format/parser_std_format_spec.h @@ -19,6 +19,8 @@ #include <__algorithm/find_if.h> #include <__algorithm/min.h> #include <__assert> +#include <__concepts/arithmetic.h> +#include <__concepts/same_as.h> #include <__config> #include <__debug> #include <__format/format_arg.h> @@ -28,7 +30,6 @@ #include <__format/unicode.h> #include <__variant/monostate.h> #include <bit> -#include <concepts> #include <cstdint> #include <string_view> #include <type_traits> @@ -52,13 +53,12 @@ __parse_arg_id(const _CharT* __begin, const _CharT* __end, auto& __parse_ctx) { // This function is a wrapper to call the real parser. But it does the // validation for the pre-conditions and post-conditions. if (__begin == __end) - __throw_format_error("End of input while parsing format-spec arg-id"); + std::__throw_format_error("End of input while parsing format-spec arg-id"); - __format::__parse_number_result __r = - __format::__parse_arg_id(__begin, __end, __parse_ctx); + __format::__parse_number_result __r = __format::__parse_arg_id(__begin, __end, __parse_ctx); if (__r.__ptr == __end || *__r.__ptr != _CharT('}')) - __throw_format_error("Invalid arg-id"); + std::__throw_format_error("Invalid arg-id"); ++__r.__ptr; return __r; @@ -67,28 +67,33 @@ __parse_arg_id(const _CharT* __begin, const _CharT* __end, auto& __parse_ctx) { template <class _Context> _LIBCPP_HIDE_FROM_ABI constexpr uint32_t __substitute_arg_id(basic_format_arg<_Context> __format_arg) { - return visit_format_arg( + // [format.string.std]/8 + // If the corresponding formatting argument is not of integral type... + // This wording allows char and bool too. LWG-3720 changes the wording to + // If the corresponding formatting argument is not of standard signed or + // unsigned integer type, + // This means the 128-bit will not be valid anymore. + // TODO FMT Verify this resolution is accepted and add a test to verify + // 128-bit integrals fail and switch to visit_format_arg. + return _VSTD::__visit_format_arg( [](auto __arg) -> uint32_t { using _Type = decltype(__arg); if constexpr (integral<_Type>) { if constexpr (signed_integral<_Type>) { if (__arg < 0) - __throw_format_error("A format-spec arg-id replacement shouldn't " - "have a negative value"); + std::__throw_format_error("A format-spec arg-id replacement shouldn't have a negative value"); } using _CT = common_type_t<_Type, decltype(__format::__number_max)>; if (static_cast<_CT>(__arg) > static_cast<_CT>(__format::__number_max)) - __throw_format_error("A format-spec arg-id replacement exceeds " - "the maximum supported value"); + std::__throw_format_error("A format-spec arg-id replacement exceeds the maximum supported value"); return __arg; } else if constexpr (same_as<_Type, monostate>) - __throw_format_error("Argument index out of bounds"); + std::__throw_format_error("Argument index out of bounds"); else - __throw_format_error("A format-spec arg-id replacement argument " - "isn't an integral type"); + std::__throw_format_error("A format-spec arg-id replacement argument isn't an integral type"); }, __format_arg); } @@ -97,6 +102,7 @@ __substitute_arg_id(basic_format_arg<_Context> __format_arg) { /// /// They default to false so when a new field is added it needs to be opted in /// explicitly. +// TODO FMT Use an ABI tag for this struct. struct __fields { uint8_t __sign_ : 1 {false}; uint8_t __alternate_form_ : 1 {false}; @@ -104,6 +110,13 @@ struct __fields { uint8_t __precision_ : 1 {false}; uint8_t __locale_specific_form_ : 1 {false}; uint8_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 __allow_colon_in_fill_ : 1 {false}; }; // By not placing this constant in the formatter class it's not duplicated for @@ -124,6 +137,11 @@ inline constexpr __fields __fields_floating_point{ inline constexpr __fields __fields_string{.__precision_ = true, .__type_ = true}; inline constexpr __fields __fields_pointer{.__type_ = true}; +# if _LIBCPP_STD_VER > 20 +inline constexpr __fields __fields_tuple{.__type_ = false, .__allow_colon_in_fill_ = true}; +inline constexpr __fields __fields_range{.__type_ = false, .__allow_colon_in_fill_ = true}; +# endif + enum class _LIBCPP_ENUM_VIS __alignment : uint8_t { /// No alignment is set in the format string. __default, @@ -163,7 +181,8 @@ enum class _LIBCPP_ENUM_VIS __type : uint8_t { __fixed_lower_case, __fixed_upper_case, __general_lower_case, - __general_upper_case + __general_upper_case, + __debug }; struct __std { @@ -176,7 +195,11 @@ struct __std { struct __chrono { __alignment __alignment_ : 3; + bool __locale_specific_form_ : 1; bool __weekday_name_ : 1; + bool __weekday_ : 1; + bool __day_of_year_ : 1; + bool __week_of_year_ : 1; bool __month_name_ : 1; }; @@ -250,7 +273,7 @@ public: if (__begin == __end) return __begin; - if (__parse_fill_align(__begin, __end) && __begin == __end) + if (__parse_fill_align(__begin, __end, __fields.__allow_colon_in_fill_) && __begin == __end) return __begin; if (__fields.__sign_ && __parse_sign(__begin) && __begin == __end) @@ -278,7 +301,7 @@ public: // parsing. In that case that parser should do the end of format string // validation. if (__begin != __end && *__begin != _CharT('}')) - __throw_format_error("The format-spec should consume the input or end with a '}'"); + std::__throw_format_error("The format-spec should consume the input or end with a '}'"); } return __begin; @@ -288,12 +311,26 @@ public: _LIBCPP_HIDE_FROM_ABI __parsed_specifications<_CharT> __get_parsed_std_specifications(auto& __ctx) const { return __parsed_specifications<_CharT>{ - .__std_ = - __std{.__alignment_ = __alignment_, - .__sign_ = __sign_, - .__alternate_form_ = __alternate_form_, - .__locale_specific_form_ = __locale_specific_form_, - .__type_ = __type_}, + .__std_ = __std{.__alignment_ = __alignment_, + .__sign_ = __sign_, + .__alternate_form_ = __alternate_form_, + .__locale_specific_form_ = __locale_specific_form_, + .__type_ = __type_}, + .__width_{__get_width(__ctx)}, + .__precision_{__get_precision(__ctx)}, + .__fill_{__fill_}}; + } + + _LIBCPP_HIDE_FROM_ABI __parsed_specifications<_CharT> __get_parsed_chrono_specifications(auto& __ctx) const { + return __parsed_specifications<_CharT>{ + .__chrono_ = + __chrono{.__alignment_ = __alignment_, + .__locale_specific_form_ = __locale_specific_form_, + .__weekday_name_ = __weekday_name_, + .__weekday_ = __weekday_, + .__day_of_year_ = __day_of_year_, + .__week_of_year_ = __week_of_year_, + .__month_name_ = __month_name_}, .__width_{__get_width(__ctx)}, .__precision_{__get_precision(__ctx)}, .__fill_{__fill_}}; @@ -306,12 +343,17 @@ public: bool __reserved_0_ : 1 {false}; __type __type_{__type::__default}; - // These two flags are used for formatting chrono. Since the struct has + // These flags are only used for formatting chrono. Since the struct has // padding space left it's added to this structure. bool __weekday_name_ : 1 {false}; + bool __weekday_ : 1 {false}; + + bool __day_of_year_ : 1 {false}; + bool __week_of_year_ : 1 {false}; + bool __month_name_ : 1 {false}; - uint8_t __reserved_1_ : 6 {0}; + uint8_t __reserved_1_ : 3 {0}; uint8_t __reserved_2_ : 6 {0}; // These two flags are only used internally and not part of the // __parsed_specifications. Therefore put them at the end. @@ -348,13 +390,17 @@ private: return false; } - _LIBCPP_HIDE_FROM_ABI constexpr bool __parse_fill_align(const _CharT*& __begin, const _CharT* __end) { + // range-fill and tuple-fill are identical + _LIBCPP_HIDE_FROM_ABI constexpr bool + __parse_fill_align(const _CharT*& __begin, const _CharT* __end, bool __use_range_fill) { _LIBCPP_ASSERT(__begin != __end, "when called with an empty input the function will cause " "undefined behavior by evaluating data not in the input"); if (__begin + 1 != __end) { if (__parse_alignment(*(__begin + 1))) { - if (*__begin == _CharT('{') || *__begin == _CharT('}')) - __throw_format_error("The format-spec fill field contains an invalid character"); + if (__use_range_fill && (*__begin == _CharT('{') || *__begin == _CharT('}') || *__begin == _CharT(':'))) + std::__throw_format_error("The format-spec range-fill field contains an invalid character"); + else if (*__begin == _CharT('{') || *__begin == _CharT('}')) + std::__throw_format_error("The format-spec fill field contains an invalid character"); __fill_ = *__begin; __begin += 2; @@ -408,7 +454,7 @@ private: _LIBCPP_HIDE_FROM_ABI constexpr bool __parse_width(const _CharT*& __begin, const _CharT* __end, auto& __parse_ctx) { if (*__begin == _CharT('0')) - __throw_format_error("A format-spec width field shouldn't have a leading zero"); + std::__throw_format_error("A format-spec width field shouldn't have a leading zero"); if (*__begin == _CharT('{')) { __format::__parse_number_result __r = __format_spec::__parse_arg_id(++__begin, __end, __parse_ctx); @@ -436,7 +482,7 @@ private: ++__begin; if (__begin == __end) - __throw_format_error("End of input while parsing format-spec precision"); + std::__throw_format_error("End of input while parsing format-spec precision"); if (*__begin == _CharT('{')) { __format::__parse_number_result __arg_id = __format_spec::__parse_arg_id(++__begin, __end, __parse_ctx); @@ -447,7 +493,7 @@ private: } if (*__begin < _CharT('0') || *__begin > _CharT('9')) - __throw_format_error("The format-spec precision field doesn't contain a value or arg-id"); + std::__throw_format_error("The format-spec precision field doesn't contain a value or arg-id"); __format::__parse_number_result __r = __format::__parse_number(__begin, __end); __precision_ = __r.__value; @@ -523,6 +569,11 @@ private: case 'x': __type_ = __type::__hexadecimal_lower_case; break; +# if _LIBCPP_STD_VER > 20 + case '?': + __type_ = __type::__debug; + break; +# endif default: return; } @@ -534,10 +585,7 @@ private: if (!__width_as_arg_) return __width_; - int32_t __result = __format_spec::__substitute_arg_id(__ctx.arg(__width_)); - if (__result == 0) - __throw_format_error("A format-spec width field replacement should have a positive value"); - return __result; + return __format_spec::__substitute_arg_id(__ctx.arg(__width_)); } _LIBCPP_HIDE_FROM_ABI @@ -559,6 +607,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __process_display_type_string(__format_spec switch (__type) { case __format_spec::__type::__default: case __format_spec::__type::__string: + case __format_spec::__type::__debug: break; default: @@ -612,6 +661,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_char(__parser<_CharT>& __p switch (__parser.__type_) { case __format_spec::__type::__default: case __format_spec::__type::__char: + case __format_spec::__type::__debug: __format_spec::__process_display_type_char(__parser); break; @@ -653,11 +703,6 @@ template <class _CharT> _LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_floating_point(__parser<_CharT>& __parser) { switch (__parser.__type_) { case __format_spec::__type::__default: - // When no precision specified then it keeps default since that - // formatting differs from the other types. - if (__parser.__precision_as_arg_ || __parser.__precision_ != -1) - __parser.__type_ = __format_spec::__type::__general_lower_case; - break; case __format_spec::__type::__hexfloat_lower_case: case __format_spec::__type::__hexfloat_upper_case: // Precision specific behavior will be handled later. @@ -699,6 +744,9 @@ struct __column_width_result { const _CharT* __last_; }; +template <class _CharT> +__column_width_result(size_t, const _CharT*) -> __column_width_result<_CharT>; + /// Since a column width can be two it's possible that the requested column /// width can't be achieved. Depending on the intended usage the policy can be /// selected. @@ -857,7 +905,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr __column_width_result<_CharT> __estimate_column_ // unit is non-ASCII we omit the current code unit and let the Grapheme // clustering algorithm do its work. const _CharT* __it = __str.begin(); - if (__is_ascii(*__it)) { + if (__format_spec::__is_ascii(*__it)) { do { --__maximum; ++__it; @@ -865,12 +913,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr __column_width_result<_CharT> __estimate_column_ return {__str.size(), __str.end()}; if (__maximum == 0) { - if (__is_ascii(*__it)) + if (__format_spec::__is_ascii(*__it)) return {static_cast<size_t>(__it - __str.begin()), __it}; break; } - } while (__is_ascii(*__it)); + } while (__format_spec::__is_ascii(*__it)); --__it; ++__maximum; } |
