aboutsummaryrefslogtreecommitdiff
path: root/libcxx/include/__format/parser_std_format_spec.h
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2023-02-11 12:38:04 +0000
committerDimitry Andric <dim@FreeBSD.org>2023-02-11 12:38:11 +0000
commite3b557809604d036af6e00c60f012c2025b59a5e (patch)
tree8a11ba2269a3b669601e2fd41145b174008f4da8 /libcxx/include/__format/parser_std_format_spec.h
parent08e8dd7b9db7bb4a9de26d44c1cbfd24e869c014 (diff)
Diffstat (limited to 'libcxx/include/__format/parser_std_format_spec.h')
-rw-r--r--libcxx/include/__format/parser_std_format_spec.h132
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;
}