1 // -*- C++ -*-
2 //===----------------------------------------------------------------------===//
3 //
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #ifndef _LIBCPP___FORMAT_PARSER_STD_FORMAT_SPEC_H
11 #define _LIBCPP___FORMAT_PARSER_STD_FORMAT_SPEC_H
12 
13 #include <__config>
14 #include <__debug>
15 #include <__format/format_arg.h>
16 #include <__format/format_error.h>
17 #include <__format/format_string.h>
18 #include <__variant/monostate.h>
19 #include <concepts>
20 #include <cstdint>
21 #include <type_traits>
22 
23 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
24 # pragma GCC system_header
25 #endif
26 
27 _LIBCPP_BEGIN_NAMESPACE_STD
28 
29 #if _LIBCPP_STD_VER > 17
30 
31 // TODO FMT Remove this once we require compilers with proper C++20 support.
32 // If the compiler has no concepts support, the format header will be disabled.
33 // Without concepts support enable_if needs to be used and that too much effort
34 // to support compilers with partial C++20 support.
35 # if !defined(_LIBCPP_HAS_NO_CONCEPTS)
36 
37 namespace __format_spec {
38 
39 /**
40  * Contains the flags for the std-format-spec.
41  *
42  * Some format-options can only be used for specific C++types and may depend on
43  * the selected format-type.
44  * * The C++type filtering can be done using the proper policies for
45  *   @ref __parser_std.
46  * * The format-type filtering needs to be done post parsing in the parser
47  *   derived from @ref __parser_std.
48  */
49 class _LIBCPP_TYPE_VIS _Flags {
50 public:
51   enum class _LIBCPP_ENUM_VIS _Alignment : uint8_t {
52     /**
53      * No alignment is set in the format string.
54      *
55      * Zero-padding is ignored when an alignment is selected.
56      * The default alignment depends on the selected format-type.
57      */
58     __default,
59     __left,
60     __center,
61     __right
62   };
63   enum class _LIBCPP_ENUM_VIS _Sign : uint8_t {
64     /**
65      * No sign is set in the format string.
66      *
67      * The sign isn't allowed for certain format-types. By using this value
68      * it's possible to detect whether or not the user explicitly set the sign
69      * flag. For formatting purposes it behaves the same as @ref __minus.
70      */
71     __default,
72     __minus,
73     __plus,
74     __space
75   };
76 
77   _Alignment __alignment : 2 {_Alignment::__default};
78   _Sign __sign : 2 {_Sign::__default};
79   uint8_t __alternate_form : 1 {false};
80   uint8_t __zero_padding : 1 {false};
81   uint8_t __locale_specific_form : 1 {false};
82 
83   enum class _LIBCPP_ENUM_VIS _Type : uint8_t {
84     __default,
85     __string,
86     __binary_lower_case,
87     __binary_upper_case,
88     __octal,
89     __decimal,
90     __hexadecimal_lower_case,
91     __hexadecimal_upper_case,
92     __pointer,
93     __char,
94     __float_hexadecimal_lower_case,
95     __float_hexadecimal_upper_case,
96     __scientific_lower_case,
97     __scientific_upper_case,
98     __fixed_lower_case,
99     __fixed_upper_case,
100     __general_lower_case,
101     __general_upper_case
102   };
103 
104   _Type __type{_Type::__default};
105 };
106 
107 namespace __detail {
108 template <class _CharT>
109 _LIBCPP_HIDE_FROM_ABI constexpr bool
110 __parse_alignment(_CharT __c, _Flags& __flags) noexcept {
111   switch (__c) {
112   case _CharT('<'):
113     __flags.__alignment = _Flags::_Alignment::__left;
114     return true;
115 
116   case _CharT('^'):
117     __flags.__alignment = _Flags::_Alignment::__center;
118     return true;
119 
120   case _CharT('>'):
121     __flags.__alignment = _Flags::_Alignment::__right;
122     return true;
123   }
124   return false;
125 }
126 } // namespace __detail
127 
128 template <class _CharT>
129 class _LIBCPP_TEMPLATE_VIS __parser_fill_align {
130 public:
131   // TODO FMT The standard doesn't specify this character is a Unicode
132   // character. Validate what fmt and MSVC have implemented.
133   _CharT __fill{_CharT(' ')};
134 
135 protected:
136   _LIBCPP_HIDE_FROM_ABI constexpr const _CharT*
137   __parse(const _CharT* __begin, const _CharT* __end, _Flags& __flags) {
138     _LIBCPP_ASSERT(__begin != __end,
139                    "When called with an empty input the function will cause "
140                    "undefined behavior by evaluating data not in the input");
141     if (__begin + 1 != __end) {
142       if (__detail::__parse_alignment(*(__begin + 1), __flags)) {
143         if (*__begin == _CharT('{') || *__begin == _CharT('}'))
144           __throw_format_error(
145               "The format-spec fill field contains an invalid character");
146         __fill = *__begin;
147         return __begin + 2;
148       }
149     }
150 
151     if (__detail::__parse_alignment(*__begin, __flags))
152       return __begin + 1;
153 
154     return __begin;
155   }
156 };
157 
158 template <class _CharT>
159 _LIBCPP_HIDE_FROM_ABI constexpr const _CharT*
160 __parse_sign(const _CharT* __begin, _Flags& __flags) noexcept {
161   switch (*__begin) {
162   case _CharT('-'):
163     __flags.__sign = _Flags::_Sign::__minus;
164     break;
165   case _CharT('+'):
166     __flags.__sign = _Flags::_Sign::__plus;
167     break;
168   case _CharT(' '):
169     __flags.__sign = _Flags::_Sign::__space;
170     break;
171   default:
172     return __begin;
173   }
174   return __begin + 1;
175 }
176 
177 template <class _CharT>
178 _LIBCPP_HIDE_FROM_ABI constexpr const _CharT*
179 __parse_alternate_form(const _CharT* __begin, _Flags& __flags) noexcept {
180   if (*__begin == _CharT('#')) {
181     __flags.__alternate_form = true;
182     ++__begin;
183   }
184 
185   return __begin;
186 }
187 
188 template <class _CharT>
189 _LIBCPP_HIDE_FROM_ABI constexpr const _CharT*
190 __parse_zero_padding(const _CharT* __begin, _Flags& __flags) noexcept {
191   if (*__begin == _CharT('0')) {
192     __flags.__zero_padding = true;
193     ++__begin;
194   }
195 
196   return __begin;
197 }
198 
199 template <class _CharT>
200 _LIBCPP_HIDE_FROM_ABI constexpr __format::__parse_number_result< _CharT>
201 __parse_arg_id(const _CharT* __begin, const _CharT* __end, auto& __parse_ctx) {
202   // This function is a wrapper to call the real parser. But it does the
203   // validation for the pre-conditions and post-conditions.
204   if (__begin == __end)
205     __throw_format_error("End of input while parsing format-spec arg-id");
206 
207   __format::__parse_number_result __r =
208       __format::__parse_arg_id(__begin, __end, __parse_ctx);
209 
210   if (__r.__ptr == __end || *__r.__ptr != _CharT('}'))
211     __throw_format_error("A format-spec arg-id should terminate at a '}'");
212 
213   ++__r.__ptr;
214   return __r;
215 }
216 
217 template <class _Context>
218 _LIBCPP_HIDE_FROM_ABI constexpr uint32_t
219 __substitute_arg_id(basic_format_arg<_Context> __arg) {
220   return visit_format_arg(
221       [](auto __arg) -> uint32_t {
222         using _Type = decltype(__arg);
223         if constexpr (integral<_Type>) {
224           if constexpr (signed_integral<_Type>) {
225             if (__arg < 0)
226               __throw_format_error("A format-spec arg-id replacement shouldn't "
227                                    "have a negative value");
228           }
229 
230           using _CT = common_type_t<_Type, decltype(__format::__number_max)>;
231           if (static_cast<_CT>(__arg) >
232               static_cast<_CT>(__format::__number_max))
233             __throw_format_error("A format-spec arg-id replacement exceeds "
234                                  "the maximum supported value");
235           return __arg;
236         } else if constexpr (same_as<_Type, monostate>)
237           __throw_format_error("Argument index out of bounds");
238         else
239           __throw_format_error("A format-spec arg-id replacement argument "
240                                "isn't an integral type");
241       },
242       __arg);
243 }
244 
245 class _LIBCPP_TYPE_VIS __parser_width {
246 public:
247   /** Contains a width or an arg-id. */
248   uint32_t __width : 31 {0};
249   /** Determines whether the value stored is a width or an arg-id. */
250   uint32_t __width_as_arg : 1 {0};
251 
252 protected:
253   /**
254    * Does the supplied std-format-spec contain a width field?
255    *
256    * When the field isn't present there's no padding required. This can be used
257    * to optimize the formatting.
258    */
259   constexpr bool __has_width_field() const noexcept {
260     return __width_as_arg || __width;
261   }
262 
263   /**
264    * Does the supplied width field contain an arg-id?
265    *
266    * If @c true the formatter needs to call @ref __substitute_width_arg_id.
267    */
268   constexpr bool __width_needs_substitution() const noexcept {
269     return __width_as_arg;
270   }
271 
272   template <class _CharT>
273   _LIBCPP_HIDE_FROM_ABI constexpr const _CharT*
274   __parse(const _CharT* __begin, const _CharT* __end, auto& __parse_ctx) {
275     if (*__begin == _CharT('0'))
276       __throw_format_error(
277           "A format-spec width field shouldn't have a leading zero");
278 
279     if (*__begin == _CharT('{')) {
280       __format::__parse_number_result __r =
281           __parse_arg_id(++__begin, __end, __parse_ctx);
282       __width = __r.__value;
283       __width_as_arg = 1;
284       return __r.__ptr;
285     }
286 
287     if (*__begin < _CharT('0') || *__begin > _CharT('9'))
288       return __begin;
289 
290     __format::__parse_number_result __r =
291         __format::__parse_number(__begin, __end);
292     __width = __r.__value;
293     _LIBCPP_ASSERT(__width != 0,
294                    "A zero value isn't allowed and should be impossible, "
295                    "due to validations in this function");
296     return __r.__ptr;
297   }
298 
299   void _LIBCPP_HIDE_FROM_ABI constexpr __substitute_width_arg_id(auto __arg) {
300     _LIBCPP_ASSERT(__width_as_arg == 1,
301                    "Substitute width called when no substitution is required");
302 
303     // The clearing of the flag isn't required but looks better when debugging
304     // the code.
305     __width_as_arg = 0;
306     __width = __substitute_arg_id(__arg);
307     if (__width == 0)
308       __throw_format_error(
309           "A format-spec width field replacement should have a positive value");
310   }
311 };
312 
313 class _LIBCPP_TYPE_VIS __parser_precision {
314 public:
315   /** Contains a precision or an arg-id. */
316   uint32_t __precision : 31 {__format::__number_max};
317   /**
318    * Determines whether the value stored is a precision or an arg-id.
319    *
320    * @note Since @ref __precision == @ref __format::__number_max is a valid
321    * value, the default value contains an arg-id of INT32_MAX. (This number of
322    * arguments isn't supported by compilers.)  This is used to detect whether
323    * the std-format-spec contains a precision field.
324    */
325   uint32_t __precision_as_arg : 1 {1};
326 
327 protected:
328   /**
329    * Does the supplied std-format-spec contain a precision field?
330    *
331    * When the field isn't present there's no truncating required. This can be
332    * used to optimize the formatting.
333    */
334   constexpr bool __has_precision_field() const noexcept {
335 
336     return __precision_as_arg == 0 ||             // Contains a value?
337            __precision != __format::__number_max; // The arg-id is valid?
338   }
339 
340   /**
341    * Does the supplied precision field contain an arg-id?
342    *
343    * If @c true the formatter needs to call @ref __substitute_precision_arg_id.
344    */
345   constexpr bool __precision_needs_substitution() const noexcept {
346     return __precision_as_arg && __precision != __format::__number_max;
347   }
348 
349   template <class _CharT>
350   _LIBCPP_HIDE_FROM_ABI constexpr const _CharT*
351   __parse(const _CharT* __begin, const _CharT* __end, auto& __parse_ctx) {
352     if (*__begin != _CharT('.'))
353       return __begin;
354 
355     ++__begin;
356     if (__begin == __end)
357       __throw_format_error("End of input while parsing format-spec precision");
358 
359     if (*__begin == _CharT('0')) {
360       ++__begin;
361       if (__begin != __end && *__begin >= '0' && *__begin <= '9')
362         __throw_format_error(
363             "A format-spec precision field shouldn't have a leading zero");
364 
365       __precision = 0;
366       __precision_as_arg = 0;
367       return __begin;
368     }
369 
370     if (*__begin == _CharT('{')) {
371       __format::__parse_number_result __arg_id =
372           __parse_arg_id(++__begin, __end, __parse_ctx);
373       _LIBCPP_ASSERT(__arg_id.__value != __format::__number_max,
374                      "Unsupported number of arguments, since this number of "
375                      "arguments is used a special value");
376       __precision = __arg_id.__value;
377       return __arg_id.__ptr;
378     }
379 
380     if (*__begin < _CharT('0') || *__begin > _CharT('9'))
381       __throw_format_error(
382           "The format-spec precision field doesn't contain a value or arg-id");
383 
384     __format::__parse_number_result __r =
385         __format::__parse_number(__begin, __end);
386     __precision = __r.__value;
387     __precision_as_arg = 0;
388     return __r.__ptr;
389   }
390 
391   void _LIBCPP_HIDE_FROM_ABI constexpr __substitute_precision_arg_id(
392       auto __arg) {
393     _LIBCPP_ASSERT(
394         __precision_as_arg == 1 && __precision != __format::__number_max,
395         "Substitute precision called when no substitution is required");
396 
397     // The clearing of the flag isn't required but looks better when debugging
398     // the code.
399     __precision_as_arg = 0;
400     __precision = __substitute_arg_id(__arg);
401   }
402 };
403 
404 template <class _CharT>
405 _LIBCPP_HIDE_FROM_ABI constexpr const _CharT*
406 __parse_locale_specific_form(const _CharT* __begin, _Flags& __flags) noexcept {
407   if (*__begin == _CharT('L')) {
408     __flags.__locale_specific_form = true;
409     ++__begin;
410   }
411 
412   return __begin;
413 }
414 
415 template <class _CharT>
416 _LIBCPP_HIDE_FROM_ABI constexpr const _CharT*
417 __parse_type(const _CharT* __begin, _Flags& __flags) {
418 
419   // Determines the type. It does not validate whether the selected type is
420   // valid. Most formatters have optional fields that are only allowed for
421   // certain types. These parsers need to do validation after the type has
422   // been parsed. So its easier to implement the validation for all types in
423   // the specific parse function.
424   switch (*__begin) {
425   case 'A':
426     __flags.__type = _Flags::_Type::__float_hexadecimal_upper_case;
427     break;
428   case 'B':
429     __flags.__type = _Flags::_Type::__binary_upper_case;
430     break;
431   case 'E':
432     __flags.__type = _Flags::_Type::__scientific_upper_case;
433     break;
434   case 'F':
435     __flags.__type = _Flags::_Type::__fixed_upper_case;
436     break;
437   case 'G':
438     __flags.__type = _Flags::_Type::__general_upper_case;
439     break;
440   case 'X':
441     __flags.__type = _Flags::_Type::__hexadecimal_upper_case;
442     break;
443   case 'a':
444     __flags.__type = _Flags::_Type::__float_hexadecimal_lower_case;
445     break;
446   case 'b':
447     __flags.__type = _Flags::_Type::__binary_lower_case;
448     break;
449   case 'c':
450     __flags.__type = _Flags::_Type::__char;
451     break;
452   case 'd':
453     __flags.__type = _Flags::_Type::__decimal;
454     break;
455   case 'e':
456     __flags.__type = _Flags::_Type::__scientific_lower_case;
457     break;
458   case 'f':
459     __flags.__type = _Flags::_Type::__fixed_lower_case;
460     break;
461   case 'g':
462     __flags.__type = _Flags::_Type::__general_lower_case;
463     break;
464   case 'o':
465     __flags.__type = _Flags::_Type::__octal;
466     break;
467   case 'p':
468     __flags.__type = _Flags::_Type::__pointer;
469     break;
470   case 's':
471     __flags.__type = _Flags::_Type::__string;
472     break;
473   case 'x':
474     __flags.__type = _Flags::_Type::__hexadecimal_lower_case;
475     break;
476   default:
477     return __begin;
478   }
479   return ++__begin;
480 }
481 
482 /**
483  * The parser for the std-format-spec.
484  *
485  * [format.string.std]/1 specifies the std-format-spec:
486  *   fill-and-align sign # 0 width precision L type
487  *
488  * All these fields are optional. Whether these fields can be used depend on:
489  * - The type supplied to the format string.
490  *   E.g. A string never uses the sign field so the field may not be set.
491  *   This constrain is validated by the parsers in this file.
492  * - The supplied value for the optional type field.
493  *   E.g. A int formatted as decimal uses the sign field.
494  *   When formatted as a char the sign field may no longer be set.
495  *   This constrain isn't validated by the parsers in this file.
496  *
497  * The base classes are ordered to minimize the amount of padding.
498  *
499  * This implements the parser for the string types.
500  */
501 template <class _CharT>
502 class _LIBCPP_TEMPLATE_VIS __parser_string
503     : public __parser_width,              // provides __width(|as_arg)
504       public __parser_precision,          // provides __precision(|as_arg)
505       public __parser_fill_align<_CharT>, // provides __fill and uses __flags
506       public _Flags                       // provides __flags
507 {
508 public:
509   using char_type = _CharT;
510 
511   _LIBCPP_HIDE_FROM_ABI constexpr __parser_string() {
512     this->__alignment = _Flags::_Alignment::__left;
513   }
514 
515   /**
516    * The low-level std-format-spec parse function.
517    *
518    * @pre __begin points at the beginning of the std-format-spec. This means
519    * directly after the ':'.
520    * @pre The std-format-spec parses the entire input, or the first unmatched
521    * character is a '}'.
522    *
523    * @returns The iterator pointing at the last parsed character.
524    */
525   _LIBCPP_HIDE_FROM_ABI constexpr auto parse(auto& __parse_ctx)
526       -> decltype(__parse_ctx.begin()) {
527     auto __it = __parse(__parse_ctx);
528     __process_display_type();
529     return __it;
530   }
531 
532 private:
533   /**
534    * Parses the std-format-spec.
535    *
536    * @throws __throw_format_error When @a __parse_ctx contains an ill-formed
537    *                               std-format-spec.
538    *
539    * @returns An iterator to the end of input or point at the closing '}'.
540    */
541   _LIBCPP_HIDE_FROM_ABI constexpr auto __parse(auto& __parse_ctx)
542       -> decltype(__parse_ctx.begin()) {
543 
544     auto __begin = __parse_ctx.begin();
545     auto __end = __parse_ctx.end();
546     if (__begin == __end)
547       return __begin;
548 
549     __begin = __parser_fill_align<_CharT>::__parse(__begin, __end,
550                                                    static_cast<_Flags&>(*this));
551     if (__begin == __end)
552       return __begin;
553 
554     __begin = __parser_width::__parse(__begin, __end, __parse_ctx);
555     if (__begin == __end)
556       return __begin;
557 
558     __begin = __parser_precision::__parse(__begin, __end, __parse_ctx);
559     if (__begin == __end)
560       return __begin;
561 
562     __begin = __parse_type(__begin, static_cast<_Flags&>(*this));
563 
564     if (__begin != __end && *__begin != _CharT('}'))
565       __throw_format_error(
566           "The format-spec should consume the input or end with a '}'");
567 
568     return __begin;
569   }
570 
571   /** Processes the parsed std-format-spec based on the parsed display type. */
572   void _LIBCPP_HIDE_FROM_ABI constexpr __process_display_type() {
573     switch (this->__type) {
574     case _Flags::_Type::__default:
575     case _Flags::_Type::__string:
576       break;
577 
578     default:
579       __throw_format_error("The format-spec type has a type not supported for "
580                            "a string argument");
581     }
582   }
583 };
584 
585 /**
586  * The parser for the std-format-spec.
587  *
588  * This implements the parser for the integral types. This includes the
589  * character type and boolean type.
590  *
591  * See @ref __parser_string.
592  */
593 template <class _CharT>
594 class _LIBCPP_TEMPLATE_VIS __parser_integral
595     : public __parser_width,              // provides __width(|as_arg)
596       public __parser_fill_align<_CharT>, // provides __fill and uses __flags
597       public _Flags                       // provides __flags
598 {
599 public:
600   using char_type = _CharT;
601 
602   // TODO FMT This class probably doesn't need public member functions after
603   // format.string.std/std_format_spec_integral.pass.cpp has been retired.
604 
605   /**
606    * The low-level std-format-spec parse function.
607    *
608    * @pre __begin points at the beginning of the std-format-spec. This means
609    * directly after the ':'.
610    * @pre The std-format-spec parses the entire input, or the first unmatched
611    * character is a '}'.
612    *
613    * @returns The iterator pointing at the last parsed character.
614    */
615   _LIBCPP_HIDE_FROM_ABI constexpr auto parse(auto& __parse_ctx)
616       -> decltype(__parse_ctx.begin()) {
617     auto __begin = __parse_ctx.begin();
618     auto __end = __parse_ctx.end();
619     if (__begin == __end)
620       return __begin;
621 
622     __begin = __parser_fill_align<_CharT>::__parse(__begin, __end,
623                                                    static_cast<_Flags&>(*this));
624     if (__begin == __end)
625       return __begin;
626 
627     __begin = __parse_sign(__begin, static_cast<_Flags&>(*this));
628     if (__begin == __end)
629       return __begin;
630 
631     __begin = __parse_alternate_form(__begin, static_cast<_Flags&>(*this));
632     if (__begin == __end)
633       return __begin;
634 
635     __begin = __parse_zero_padding(__begin, static_cast<_Flags&>(*this));
636     if (__begin == __end)
637       return __begin;
638 
639     __begin = __parser_width::__parse(__begin, __end, __parse_ctx);
640     if (__begin == __end)
641       return __begin;
642 
643     __begin =
644         __parse_locale_specific_form(__begin, static_cast<_Flags&>(*this));
645     if (__begin == __end)
646       return __begin;
647 
648     __begin = __parse_type(__begin, static_cast<_Flags&>(*this));
649 
650     if (__begin != __end && *__begin != _CharT('}'))
651       __throw_format_error(
652           "The format-spec should consume the input or end with a '}'");
653 
654     return __begin;
655   }
656 
657 protected:
658   /**
659    * Handles the post-parsing updates for the integer types.
660    *
661    * Updates the zero-padding and alignment for integer types.
662    *
663    * [format.string.std]/13
664    *   If the 0 character and an align option both appear, the 0 character is
665    *   ignored.
666    *
667    * For the formatter a @ref __default alignment means zero-padding. Update
668    * the alignment based on parsed format string.
669    */
670   _LIBCPP_HIDE_FROM_ABI constexpr void __handle_integer() noexcept {
671     this->__zero_padding &= this->__alignment == _Flags::_Alignment::__default;
672     if (!this->__zero_padding &&
673         this->__alignment == _Flags::_Alignment::__default)
674       this->__alignment = _Flags::_Alignment::__right;
675   }
676 
677   /**
678    * Handles the post-parsing updates for the character types.
679    *
680    * Sets the alignment and validates the format flags set for a character type.
681    *
682    * At the moment the validation for a character and a Boolean behave the
683    * same, but this may change in the future.
684    * Specifically at the moment the locale-specific form is allowed for the
685    * char output type, but it has no effect on the output.
686    */
687   _LIBCPP_HIDE_FROM_ABI constexpr void __handle_char() { __handle_bool(); }
688 
689   /**
690    * Handles the post-parsing updates for the Boolean types.
691    *
692    * Sets the alignment and validates the format flags set for a Boolean type.
693    */
694   _LIBCPP_HIDE_FROM_ABI constexpr void __handle_bool() {
695     if (this->__sign != _Flags::_Sign::__default)
696       __throw_format_error("A sign field isn't allowed in this format-spec");
697 
698     if (this->__alternate_form)
699       __throw_format_error(
700           "An alternate form field isn't allowed in this format-spec");
701 
702     if (this->__zero_padding)
703       __throw_format_error(
704           "A zero-padding field isn't allowed in this format-spec");
705 
706     if (this->__alignment == _Flags::_Alignment::__default)
707       this->__alignment = _Flags::_Alignment::__left;
708   }
709 };
710 
711 // TODO FMT Add a parser for floating-point values.
712 // TODO FMT Add a parser for pointer values.
713 
714 } // namespace __format_spec
715 
716 # endif // !defined(_LIBCPP_HAS_NO_CONCEPTS)
717 
718 #endif //_LIBCPP_STD_VER > 17
719 
720 _LIBCPP_END_NAMESPACE_STD
721 
722 #endif // _LIBCPP___FORMAT_PARSER_STD_FORMAT_SPEC_H
723