BDE 4.39.x Production Release
Loading...
Searching...
No Matches
bslfmt_formatspecificationparser.h
Go to the documentation of this file.
1/// @file bslfmt_formatspecificationparser.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bslfmt_formatspecificationparser.h -*-C++-*-
8
9#ifndef INCLUDED_BSLFMT_FORMATSPECIFICATIONPARSER
10#define INCLUDED_BSLFMT_FORMATSPECIFICATIONPARSER
11
12#include <bsls_ident.h>
13BSLS_IDENT("$Id: $")
14
15/// @defgroup bslfmt_formatspecificationparser bslfmt_formatspecificationparser
16/// @brief Tokenization utility for use within BSL `format` spec parsers
17/// @addtogroup bsl
18/// @{
19/// @addtogroup bslfmt
20/// @{
21/// @addtogroup bslfmt_formatspecificationparser
22/// @{
23///
24/// <h1> Outline </h1>
25/// * <a href="#bslfmt_formatspecificationparser-purpose"> Purpose</a>
26/// * <a href="#bslfmt_formatspecificationparser-classes"> Classes </a>
27/// * <a href="#bslfmt_formatspecificationparser-description"> Description </a>
28///
29/// # Purpose {#bslfmt_formatspecificationparser-purpose}
30/// Tokenization utility for use within BSL `format` spec parsers
31///
32/// # Classes {#bslfmt_formatspecificationparser-classes}
33///
34/// - bslfmt::FormatSpecificationParserEnums: namespace holding enums for parsing
35/// - bslfmt::FormatSpecificationParser: utility to tokenize format specs
36///
37/// @see bslfmt_format.h
38///
39/// # Description {#bslfmt_formatspecificationparser-description}
40/// This component provides a mechanism to perform a first-pass
41/// split of a formatting string into its component parts in a way that is
42/// compatible with [format.string] and [time.format] in the Standard. No
43/// validation is performed by this component and further type-specific
44/// processing will be required prior to use.
45///
46/// Note a default-initialized `FormatSpecificationParser` has the following
47/// defaults:
48/// * sections: e_SECTIONS_NONE
49/// * filler: zero-terminated empty string
50/// * numFillerCharacters: 1 (the null termination)
51/// * fillerDisplayWidth: 1
52/// * alignment: e_ALIGN_DEFAULT
53/// * sign: e_SIGN_DEFAULT
54/// * alternativeFlag: false
55/// * zeroPaddingFlag: false
56/// * rawWidth: e_DEFAULT
57/// * rawPrecision: e_DEFAULT
58/// * postprocessedWidth: e_DEFAULT
59/// * postprocessedPrecision: e_DEFAULT
60/// * localeSpecificFlag: false
61///
62/// This component is for use within `bslfmt` only.
63/// @}
64/** @} */
65/** @} */
66
67/** @addtogroup bsl
68 * @{
69 */
70/** @addtogroup bslfmt
71 * @{
72 */
73/** @addtogroup bslfmt_formatspecificationparser
74 * @{
75 */
76
77#include <bslscm_version.h>
78
79#include <bslfmt_formaterror.h>
82
83#include <bslmf_assert.h>
84#include <bslmf_issame.h>
85
87#include <bsls_keyword.h>
88
89#include <bslstl_iterator.h>
90
91#if defined(BSLS_LIBRARYFEATURES_HAS_CPP20_FORMAT)
92#include <format> // for 'std::visit_format_arg'
93#endif
94
95
96namespace bslfmt {
97
98 // =====================================
99 // struct FormatSpecificationParserEnums
100 // =====================================
101
102/// Namespace struct holding enums used for parsing.
103///
104/// See @ref bslfmt_formatspecificationparser
141
142 // ===============================
143 // class FormatSpecificationParser
144 // ===============================
145
146/// A general mechanism used in format specification parsers. It supports
147/// parsing syntaxes of the form `fill-and-align?sign # 0 width?precision?L`
148/// and any syntax that contains a subset of the sections therein.
149template <class t_CHAR>
152
153 private:
154 // CLASS DATA
155 static BSLS_KEYWORD_CONSTEXPR_MEMBER size_t s_fillerBufferSize = 5;
156 // maximum required buffer size to hold a null-terminated unicode code
157 // point
158
159 // DATA
160 Sections d_sections;
161 // list of sections extracted
162
163 ProcessingState d_processingState;
164 // current processing state
165
166 t_CHAR d_filler[s_fillerBufferSize];
167 // the filler code point
168
169 int d_numFillerCharacters;
170 // number of characters in filler
171
172 int d_fillerDisplayWidth;
173 // display width of code point
174
175 Alignment d_alignment;
176 // type of requested alignment
177
178 Sign d_sign;
179 // type of sign representation
180
181 bool d_alternativeFlag;
182 // flag indicating whether an
183 // alternative form of value
184 // representation is required (`#`)
185
186 bool d_zeroPaddingFlag;
187 // flag indicating whether padding
188 // with zeros is required (`0`)
189
191 // preliminary minimum field width
192
194 // preliminary precision
195
196 FormatterSpecificationNumericValue d_postprocessedWidth;
197 // final minimum field width
198 // obtained after processing the
199 // specification parameter
200
201 FormatterSpecificationNumericValue d_postprocessedPrecision;
202 // final precision obtained after
203 // processing the specification
204 // parameter
205
206 bool d_localeSpecificFlag;
207 // flag indicating whether the
208 // locale-specific form is required
209 // (`L`)
210
212 // input format specification
213
214 // PRIVATE CLASS METHODS
215
216 /// Determine and return the value of the Alignment enum represented by the
217 /// specified `in`, determined by the mapping given by the Standard C++
218 /// formatting specification. In the event of an error throw a
219 /// @ref format_error exception.
220 static BSLS_KEYWORD_CONSTEXPR_CPP20 Alignment alignmentFromChar(t_CHAR in);
221
222 /// Determine and return the value of the Sign enum represented by the
223 /// specified `in`, determined by the mapping given by the Standard C++
224 /// formatting specification. In the event of an error throw a
225 /// @ref format_error exception.
226 static BSLS_KEYWORD_CONSTEXPR_CPP20 Sign signFromChar(t_CHAR in);
227
228
229 /// Based on the value of the initial byte of a unicode code point in UTF-8
230 /// representation, calculate and return the number of bytes used in that
231 /// representation. The value of the first byte is specified by
232 /// `firstChar`. If `firstChar` does not contain a valid value for the
233 /// first byte of a UTF-8 codepoint representation return -1;
234 static BSLS_KEYWORD_CONSTEXPR_CPP20 int codepointBytesIfValid(
235 const char firstChar);
236
237 /// Based on the value of the initial byte of a unicode code point in
238 /// UTF-16 (where `sizeof(wchar_t)==2`) or UTF-32 (where
239 /// `sizeof(wchar_t)==4`) representation, calculate and return the number
240 /// of bytes used in that representation. The value of the first byte is
241 /// specified by `firstChar`. If `firstChar` does not contain a valid
242 /// value for the first byte of a UTF-16 or UTF-32 (depending on
243 /// `sizeof(wchar_t)`) codepoint representation return -1;
244 static BSLS_KEYWORD_CONSTEXPR_CPP20 int codepointBytesIfValid(
245 const wchar_t firstChar);
246
247 // PRIVATE MANIPULATORS
248
249 /// Extract a filler code point and `Alignment` value from start of the
250 /// string specified by the `start` and `end` random-access-iterator range,
251 /// and update the corresponding attributes of this object with the result.
252 /// In the event of an error throw a @ref format_error exception.
253 template <class t_ITER>
254 BSLS_KEYWORD_CONSTEXPR_CPP20 void parseFillAndAlignment(t_ITER *start,
255 t_ITER end);
256
257 /// If this object contains a filler code point, calculate the Unicode
258 /// display width of that code point and update the corresponding attribute
259 /// of this object with the result. In the event of an error throw a
260 /// @ref format_error exception.
261 void postprocessFiller();
262
263 /// Extract a `Sign` value from the start of the string specified by the
264 /// `start` and `end` random-access-iterator range, and update the
265 /// corresponding attribute of this object with the result. In the event
266 /// of an error throw a @ref format_error exception.
267 template <class t_ITER>
268 BSLS_KEYWORD_CONSTEXPR_CPP20 void parseSign(t_ITER *start, t_ITER end);
269
270 /// Determine whether the start of the string specified by the `start` and
271 /// `end` random-access-iterator range contains a flag (`#`) indicating
272 /// alternative format processing and update the corresponding attribute of
273 /// this object if necessary.
274 template <class t_ITER>
275 BSLS_KEYWORD_CONSTEXPR_CPP20 void parseAlternateOption(t_ITER *start,
276 t_ITER end);
277
278 /// Determine whether the start of the string specified by the `start` and
279 /// `end` random-access-iterator range contains a flag (`0`) indicating
280 /// zero padding and update the corresponding attribute of this object if
281 /// necessary.
282 template <class t_ITER>
283 BSLS_KEYWORD_CONSTEXPR_CPP20 void parseZeroPaddingFlag(t_ITER *start,
284 t_ITER end);
285
286 /// Extract a width from the start of the string specified by the `start`
287 /// and `end` random-access-iterator range, and update the corresponding
288 /// attribute of this object with the result. In the event of an error throw a `format_error` exception.
289 ///
290 /// \note Note that the extracted precision
291 /// may be hard-coded or nested and is not itself validated at this stage.
292 template <class t_ITER>
293 BSLS_KEYWORD_CONSTEXPR_CPP20 void parseRawWidth(t_ITER *start, t_ITER end);
294
295 /// Extract a precision from the start of the string specified by the
296 /// `start` and `end` random-access-iterator range, and update the
297 /// corresponding attribute of this object with the result. In the event of an error throw a `format_error` exception.
298 ///
299 /// \note Note that the extracted
300 /// precision may be hard-coded or nested and is not itself validated at
301 /// this stage.
302 template <class t_ITER>
303 BSLS_KEYWORD_CONSTEXPR_CPP20 void parseRawPrecision(t_ITER *start,
304 t_ITER end);
305
306 /// Determine whether the start of the string specified by the `start` and
307 /// `end` random-access-iterator range contains a flag (`L`) indicating
308 /// locale specific formatting and update the corresponding attribute of
309 /// this object with the result.
310 template <class t_ITER>
311 BSLS_KEYWORD_CONSTEXPR_CPP20 void parseLocaleSpecificFlag(t_ITER *start,
312 t_ITER end);
313
314 /// Parse the string specified by the `start` and `end`
315 /// random-access-iterator range to extract the items indicated by the
316 /// specified `sections` and update the corresponding attributes of this
317 /// object with the result. In the event of an error throw a
318 /// @ref format_error exception.
319 template <class t_ITER>
320 BSLS_KEYWORD_CONSTEXPR_CPP20 void rawParse(t_ITER *start, // output
321 t_ITER end, // input
322 Sections sections); // param
323
324 public:
325 // CREATORS
326
327 /// Create a default instance of `FormatSpecificationParser`.
329
330 // MANIPULATORS
331
332 /// Parse the string contained by the specified input/output `parseContext`
333 /// to extract the items indicated by the specified `sections`, update this
334 /// object with the parsing result and update the iterator held by
335 /// `parseContext` to point to the start of the unparsed section of the
336 /// string. In the event of an error throw a @ref format_error exception.
337 template <class t_PARSE_CONTEXT>
339 parse(t_PARSE_CONTEXT *parseContext, // output
340 Sections sections); // param
341
342 /// Update any nested width and precision values to contain actual values
343 /// based on the appropriate arguments held by the specified `context`
344 /// format context. In the event of an error throw a @ref format_error
345 /// exception.
346 template <typename t_FORMAT_CONTEXT>
347 void postprocess(const t_FORMAT_CONTEXT& context);
348
349 // ACCESSORS
350
351 /// Return the stored filler string. If `parse` has not previously been
352 /// called throw a @ref format_error exception.
353 BSLS_KEYWORD_CONSTEXPR_CPP20 const t_CHAR *filler() const;
354
355 /// Return the size of the stored filler string. If `parse` has not
356 /// previously been called throw a @ref format_error exception.
358
359 /// Return the width of the filler code point. If `postprocess` has not
360 /// previously been called throw a @ref format_error exception.
362
363 /// Return the stored alignment value. If `parse` has not
364 /// previously been called throw a @ref format_error exception.
366
367 /// Return the stored sign value. If `parse` has not previously been
368 /// called throw a @ref format_error exception.
370
371 /// Return true if parsing detected an alternative processing flag in the
372 /// specification, otherwise return false. If `parse` has not previously
373 /// been called throw a @ref format_error exception.
375
376 /// Return true if parsing detected a zero padding flag in the
377 /// specification, otherwise return false. If `parse` has not previously
378 /// been called throw a @ref format_error exception.
380
381 /// Return the modified numeric width extracted during postprocessing. If
382 /// `postprocess` has not previously been called throw a @ref format_error
383 /// exception.
385 postprocessedWidth() const;
386
387 /// Return the modified numeric precision extracted during postprocessing.
388 /// If `postprocess` has not previously been called throw a @ref format_error
389 /// exception.
392
393 /// Return the unmodified width extracted during parsing. If `parse` has
394 /// not previously been called throw a @ref format_error exception.
396 rawWidth() const;
397
398 /// Return the unmodified precision extracted during parsing. If `parse`
399 /// has not previously been called throw a @ref format_error exception.
401 rawPrecision() const;
402
403 /// Return true if parsing detected a locale-specific formatting flag in
404 /// the specification, otherwise return false. If `parse` has not
405 /// previously been called throw a @ref format_error exception.
407
408 /// Return that section of the specification string parsed during the call to `parse`.
409 ///
410 /// \note Note that this may be a truncated version of the string
411 /// held by the parse context. If `parse` has not previously been called
412 /// throw a @ref format_error exception.
413
414 /// Return the current processing state of this parser.
416
417 /// Return that section of the specification string that remains unparsed after the call to `parse`.
418 ///
419 /// \note Note that this may be a truncated version
420 /// of the string held by the parse context. If `parse` has not previously
421 /// been called throw a @ref format_error exception.
423 remainingSpec() const;
424};
425
426// ============================================================================
427// INLINE DEFINITIONS
428// ============================================================================
429
430 // -------------------------------
431 // class FormatSpecificationParser
432 // -------------------------------
433
434// PRIVATE CLASS FUNCTIONS
435
436template <class t_CHAR>
440{
441 if ('<' == in) {
442 return e_ALIGN_LEFT; // RETURN
443 }
444 if ('>' == in) {
445 return e_ALIGN_RIGHT; // RETURN
446 }
447 if ('^' == in) {
448 return e_ALIGN_MIDDLE; // RETURN
449 }
450 return e_ALIGN_DEFAULT;
451}
452
453template <class t_CHAR>
456FormatSpecificationParser<t_CHAR>::signFromChar(t_CHAR in)
457{
458 if ('+' == in) {
459 return e_SIGN_POSITIVE; // RETURN
460 }
461 if ('-' == in) {
462 return e_SIGN_NEGATIVE; // RETURN
463 }
464 if (' ' == in) {
465 return e_SIGN_SPACE; // RETURN
466 }
467 return e_SIGN_DEFAULT;
468}
469
470template <class t_CHAR>
471inline
473FormatSpecificationParser<t_CHAR>::codepointBytesIfValid(const char firstChar)
474{
475 const unsigned char unsignedValue = static_cast<unsigned char>(firstChar);
476 if ((unsignedValue & 0x80) == 0x00) {
477 return 1;
478 }
479 else if ((unsignedValue & 0xe0) == 0xc0) {
480 return 2;
481 }
482 else if ((unsignedValue & 0xf0) == 0xe0) {
483 return 3;
484 }
485 else if ((unsignedValue & 0xf8) == 0xf0) {
486 return 4;
487 }
488 else {
489 return -1;
490 }
491}
492
493template <class t_CHAR>
494inline
496FormatSpecificationParser<t_CHAR>::codepointBytesIfValid(
497 const wchar_t firstChar)
498{
499 switch (sizeof(wchar_t)) {
500 case 2: { // UTF-16
501 // Single 16-bit code units
502 if (static_cast<unsigned int>(firstChar) <
503 static_cast<unsigned int>(0xd800) ||
504 static_cast<unsigned int>(firstChar) >=
505 static_cast<unsigned int>(0xe000)) {
506 return 2;
507 }
508 // Surrogate pair
509 else if (static_cast<unsigned int>(firstChar) >=
510 static_cast<unsigned int>(0xd800) &&
511 static_cast<unsigned int>(firstChar) <
512 static_cast<unsigned int>(0xe000)) {
513 return 4;
514 }
515 else {
516 return -1;
517 }
518 } break;
519 case 4: { // UTF-32
520 if (static_cast<unsigned long>(firstChar) <=
521 static_cast<unsigned long>(0x10ffff)) {
522 return 4;
523 }
524 else {
525 return -1;
526 }
527 } break;
528 default: {
529 return -1; // unsuported wchar_t size.
530 } break;
531 }
532}
533
534// PRIVATE MANIPULATORS
535template <class t_CHAR>
536template <class t_ITER>
538FormatSpecificationParser<t_CHAR>::parseFillAndAlignment(t_ITER *start,
539 t_ITER end)
540{
542 (bsl::is_same<typename bsl::iterator_traits<t_ITER>::value_type,
543 t_CHAR>::value));
544
545 // Handle empty string or empty specification.
546 if (*start == end || **start == '}') {
547 d_filler[0] = ' ';
548 d_numFillerCharacters = 1;
549 d_fillerDisplayWidth = 1;
550 return; // RETURN
551 }
552
553 const int cpBytes = codepointBytesIfValid(**start);
554
555 if (cpBytes < 0) {
557 bsl::format_error("Invalid unicode code point (`parse`)")); // THROW
558 }
559
560 const int cpChars = cpBytes / sizeof(t_CHAR);
561
562 t_ITER tempIter = *start;
563 for (int i = 0; i < cpChars; ++i) {
564 if (tempIter == end) {
566 bsl::format_error("Unicode code point: too few bytes")); // THROW
567 }
568 d_filler[i] = *tempIter;
569 ++tempIter;
570 }
571 d_filler[cpChars] = 0;
572
573 d_numFillerCharacters = cpChars;
574
575 t_ITER aligner = tempIter;
576
577 if (aligner != end) {
578 if (alignmentFromChar(*aligner) != e_ALIGN_DEFAULT) {
579 // `{` and `}` are invalid fill characters per the C++ spec. As
580 // they are ASCII we can check **start without decoding the code
581 // point.
582 if ('{' == **start || '}' == **start) {
583 BSLS_THROW(bsl::format_error(
584 "Invalid fill character ('{' or '}')")); // THROW
585 }
586 d_alignment = alignmentFromChar(*aligner);
587
588 // We cannot know this yet - put in a placeholder.
589 d_fillerDisplayWidth = -1;
590
591 *start = aligner + 1;
592 // Fill and alignment specifier given.
593 return; // RETURN
594 }
595 }
596
597 // If the alignment specifier is not in 2nd position, it is allowed to be
598 // in the first position, in which case the spec states that a space should
599 // be used as the filler character.
600 d_filler[0] = ' ';
601 d_numFillerCharacters = 1;
602 d_fillerDisplayWidth = 1;
603 aligner = *start;
604 if (alignmentFromChar(*aligner) != e_ALIGN_DEFAULT) {
605 d_alignment = alignmentFromChar(*aligner);
606 *start = aligner + 1;
607 // Alignment specifier with default (ascii space) fill character
608 return; // RETURN
609 }
610
611 // Otherwise we have neither fill nor alignment.
612 return; // RETURN
613}
614
615template <class t_CHAR>
616void
617FormatSpecificationParser<t_CHAR>::postprocessFiller()
618{
619
620 UnicodeCodePoint cp;
621
622 switch (sizeof(t_CHAR)) {
623 case 1: {
624 cp.extract(UnicodeCodePoint::e_UTF8,
625 (const void *)d_filler,
626 d_numFillerCharacters);
627 } break;
628 case 2: {
629 cp.extract(UnicodeCodePoint::e_UTF16,
630 (const void *)d_filler,
631 d_numFillerCharacters * 2);
632 } break;
633 case 4: {
634 cp.extract(UnicodeCodePoint::e_UTF32,
635 (const void *)d_filler,
636 d_numFillerCharacters * 4);
637 } break;
638 default: {
639 BSLS_THROW(bsl::format_error("Unsupported wchar_t size")); // THROW
640 }
641 }
642
643 if (!cp.isValid()) {
644 BSLS_THROW(bsl::format_error(
645 "Invalid unicode code point (`postprocess`)")); // THROW
646 }
647
648 if (static_cast<size_t>(cp.numSourceBytes()) != d_numFillerCharacters *
649 sizeof(t_CHAR)) {
651 bsl::format_error("Invalid unicode code point size")); // THROW
652 }
653
654 // `{` and `}` are invalid fill characters per the C++ spec
655 if ('{' == cp.codePointValue() || '}' == cp.codePointValue()) {
657 bsl::format_error("Invalid fill character ('{' or '}')")); // THROW
658 }
659
660 if (d_fillerDisplayWidth >= 0) {
661 if (d_fillerDisplayWidth != cp.codePointWidth()) {
663 bsl::format_error("Invalid code point width")); // THROW
664 }
665 }
666 else {
667 d_fillerDisplayWidth = cp.codePointWidth();
668 }
669}
670
671template <class t_CHAR>
672template <class t_ITER>
674FormatSpecificationParser<t_CHAR>::parseSign(t_ITER *start, t_ITER end)
675{
677 (bsl::is_same<typename bsl::iterator_traits<t_ITER>::value_type,
678 t_CHAR>::value));
679
680 // Handle empty string or empty specification.
681 if (*start == end || **start == '}') {
682 return; // RETURN
683 }
684
685 d_sign = signFromChar(**start);
686
687 if (e_SIGN_DEFAULT != d_sign) {
688 ++*start;
689 }
690}
691
692template <class t_CHAR>
693template <class t_ITER>
695FormatSpecificationParser<t_CHAR>::parseAlternateOption(t_ITER *start,
696 t_ITER end)
697{
699 (bsl::is_same<typename bsl::iterator_traits<t_ITER>::value_type,
700 t_CHAR>::value));
701
702 // Handle empty string or empty specification.
703 if (*start == end || **start == '}') {
704 return; // RETURN
705 }
706
707 d_alternativeFlag = ((t_CHAR)'#' == **start);
708
709 if (d_alternativeFlag) {
710 ++*start;
711 }
712}
713
714template <class t_CHAR>
715template <class t_ITER>
717FormatSpecificationParser<t_CHAR>::parseZeroPaddingFlag(t_ITER *start,
718 t_ITER end)
719{
721 (bsl::is_same<typename bsl::iterator_traits<t_ITER>::value_type,
722 t_CHAR>::value));
723
724 // Handle empty string or empty specification.
725 if (*start == end || **start == '}') {
726 return; // RETURN
727 }
728
729 d_zeroPaddingFlag = ((t_CHAR)'0' == **start);
730
731 if (d_zeroPaddingFlag) {
732 ++*start;
733 }
734}
735
736template <class t_CHAR>
737template <class t_ITER>
739FormatSpecificationParser<t_CHAR>::parseRawWidth(t_ITER *start, t_ITER end)
740{
742 (bsl::is_same<typename bsl::iterator_traits<t_ITER>::value_type,
743 t_CHAR>::value));
744
745 // Handle empty string or empty specification.
746 if (*start == end || **start == '}') {
747 return; // RETURN
748 }
749
750 typedef FormatterSpecificationNumericValue NumericValue;
751
752 d_rawWidth.parse(start, end, false);
753 // Non-relative widths must be strictly positive.
754 if (d_rawWidth == NumericValue(NumericValue::e_VALUE, 0)) {
755 BSLS_THROW(bsl::format_error("Field widths must be > 0")); // THROW
756 }
757}
758
759template <class t_CHAR>
760template <class t_ITER>
762FormatSpecificationParser<t_CHAR>::parseRawPrecision(t_ITER *start, t_ITER end)
763{
765 (bsl::is_same<typename bsl::iterator_traits<t_ITER>::value_type,
766 t_CHAR>::value));
767
768 // Handle empty string or empty specification.
769 if (*start == end || **start == '}') {
770 return; // RETURN
771 }
772
773 return d_rawPrecision.parse(start, end, true);
774}
775
776template <class t_CHAR>
777template <class t_ITER>
779FormatSpecificationParser<t_CHAR>::parseLocaleSpecificFlag(t_ITER *start,
780 t_ITER end)
781{
783 (bsl::is_same<typename bsl::iterator_traits<t_ITER>::value_type,
784 t_CHAR>::value));
785
786 // Handle empty string or empty specification.
787 if (*start == end || **start == '}') {
788 return; // RETURN
789 }
790
791 d_localeSpecificFlag = ((t_CHAR)'L' == **start);
792
793 if (d_localeSpecificFlag) {
794 ++*start;
795 }
796
797 return;
798}
799
800template <class t_CHAR>
801template <class t_ITER>
802BSLS_KEYWORD_CONSTEXPR_CPP20 void FormatSpecificationParser<t_CHAR>::rawParse(
803 t_ITER *start,
804 t_ITER end,
805 Sections sections)
806{
808 (bsl::is_same<typename bsl::iterator_traits<t_ITER>::value_type,
809 t_CHAR>::value));
810
811 d_sections = sections;
812
813 // Reasonable defaults for early exit.
814 d_filler[0] = ' ';
815 d_numFillerCharacters = 1;
816 d_fillerDisplayWidth = 1;
817
818 // Handle empty string or empty specification.
819 if (*start == end || **start == '}') {
820 return; // RETURN
821 }
822
823 if (0 != (sections & e_SECTIONS_FILL_ALIGN)) {
824 parseFillAndAlignment(start, end);
825
826 if (*start == end || **start == '}') {
827 return; // RETURN
828 }
829 }
830
831 if (0 != (sections & e_SECTIONS_SIGN_FLAG)) {
832 parseSign(start, end);
833
834 if (*start == end || **start == '}') {
835 return; // RETURN
836 }
837 }
838
839 if (0 != (sections & e_SECTIONS_ALTERNATE_FLAG)) {
840 parseAlternateOption(start, end);
841
842 if (*start == end || **start == '}') {
843 return; // RETURN
844 }
845 }
846
847 if (0 != (sections & e_SECTIONS_ZERO_PAD_FLAG)) {
848 parseZeroPaddingFlag(start, end);
849
850 if (*start == end || **start == '}') {
851 return; // RETURN
852 }
853 }
854
855 if (0 != (sections & e_SECTIONS_WIDTH)) {
856 parseRawWidth(start, end);
857
858 if (*start == end || **start == '}') {
859 return; // RETURN
860 }
861 }
862
863 if (0 != (sections & e_SECTIONS_PRECISION)) {
864 parseRawPrecision(start, end);
865
866 if (*start == end || **start == '}') {
867 return; // RETURN
868 }
869 }
870
871 if (0 != (sections & e_SECTIONS_LOCALE_FLAG)) {
872 parseLocaleSpecificFlag(start, end);
873
874 if (*start == end || **start == '}') {
875 return; // RETURN
876 }
877 }
878
879 if (0 != (sections & e_SECTIONS_REMAINING_SPEC)) {
880 bsl::basic_string_view<t_CHAR> temp(*start, end);
881 size_t counter = 0;
882
883 // Take anything left up to but not including last closing brace.
884 int level = 1;
885 while (temp.size() > counter) {
886 if ('{' == temp[counter]) {
887 ++level;
888 }
889 else if ('}' == temp[counter]) {
890 --level;
891 }
892 if (0 == level) {
893 break; // BREAK
894 }
895 ++counter;
896 }
897
898 d_spec = temp.substr(0, counter);
899 *start += counter;
900 }
901
902 if (*start == end || **start == '}') {
903 return; // RETURN
904 }
905
906 BSLS_THROW(bsl::format_error(
907 "Specification parse failure (invalid character)")); // THROW
908}
909
910// CREATORS
911template <class t_CHAR>
914: d_sections(e_SECTIONS_NONE)
915, d_processingState(e_STATE_UNPARSED)
916, d_filler()
917, d_numFillerCharacters(1)
918, d_fillerDisplayWidth(1)
919, d_alignment(e_ALIGN_DEFAULT)
920, d_sign(e_SIGN_DEFAULT)
921, d_alternativeFlag(false)
922, d_zeroPaddingFlag(false)
923, d_localeSpecificFlag(false)
924{}
925
926// MANIPULATORS
927template <class t_CHAR>
928template <class t_PARSE_CONTEXT>
930 t_PARSE_CONTEXT *parseContext,
931 Sections sections)
932{
934 bsl::is_same<typename bsl::iterator_traits<
935 typename t_PARSE_CONTEXT::const_iterator>::value_type,
936 t_CHAR>::value));
937
938 d_processingState = e_STATE_PARSED;
939
940 typename t_PARSE_CONTEXT::const_iterator current = parseContext->begin();
941 typename t_PARSE_CONTEXT::const_iterator end = parseContext->end();
942
943 rawParse(&current, end, sections);
944
945 // We cannot mix specified and unspecified relative argument ids.
946
947 if (d_rawWidth.category() ==
949 d_rawPrecision.category() ==
951 BSLS_THROW(bsl::format_error("Cannot mix automatic (width) and manual "
952 "(precision) indexing")); // THROW
953 }
954
955 if (d_rawWidth.category() ==
957 d_rawPrecision.category() ==
959 BSLS_THROW(bsl::format_error("Cannot mix manual (width) and automatic "
960 "(precision) indexing")); // THROW
961 }
962
963 if (0 != (sections & e_SECTIONS_WIDTH)) {
964 if (rawWidth().category() ==
966 parseContext->check_arg_id(rawWidth().value());
967 }
968 else if (rawWidth().category() ==
972 static_cast<int>(parseContext->next_arg_id()));
973 }
974 }
975
976 if (0 != (sections & e_SECTIONS_PRECISION)) {
977 if (rawPrecision().category() ==
979 parseContext->check_arg_id(rawPrecision().value());
980 }
981 else if (rawPrecision().category() ==
983 d_rawPrecision = FormatterSpecificationNumericValue(
985 static_cast<int>(parseContext->next_arg_id()));
986 }
987 }
988
989 parseContext->advance_to(current);
990}
991
992template <class t_CHAR>
993template <typename t_FORMAT_CONTEXT>
995 const t_FORMAT_CONTEXT& context)
996{
997 if (0 != (d_sections & e_SECTIONS_FILL_ALIGN)) {
998 FormatSpecificationParser::postprocessFiller();
999 }
1000
1001 if (0 != (d_sections & e_SECTIONS_WIDTH)) {
1002 d_postprocessedWidth = d_rawWidth;
1003 d_postprocessedWidth.postprocess(context);
1004 }
1005
1006 if (0 != (d_sections & e_SECTIONS_PRECISION)) {
1007 d_postprocessedPrecision = d_rawPrecision;
1008 d_postprocessedPrecision.postprocess(context);
1009 }
1010
1011 d_processingState = e_STATE_POSTPROCESSED;
1012}
1013
1014// ACCESSORS
1015
1016template <class t_CHAR>
1019{
1020 if (e_STATE_UNPARSED == d_processingState) {
1021 BSLS_THROW(bsl::format_error(
1022 "Format specification '.parse' not called (`filler`)")); // THROW
1023 }
1024
1025 return d_filler;
1026}
1027
1028template <class t_CHAR>
1031{
1032 if (e_STATE_UNPARSED == d_processingState) {
1033 BSLS_THROW(
1034 bsl::format_error("Format specification '.parse' not called "
1035 "(`numFillerCharacters`)")); // THROW
1036 }
1037
1038 return d_numFillerCharacters;
1039}
1040
1041template <class t_CHAR>
1044{
1045 if (e_STATE_POSTPROCESSED != d_processingState) {
1046 BSLS_THROW(
1047 bsl::format_error("Format specification '.postprocess' not called "
1048 "(`fillerCodePointDisplayWidth`)")); // THROW
1049 }
1050
1051 return d_fillerDisplayWidth;
1052}
1053
1054template <class t_CHAR>
1056typename
1059{
1060 if (e_STATE_UNPARSED == d_processingState) {
1061 BSLS_THROW(bsl::format_error("Format specification '.parse' not "
1062 "called (`alignment`)")); // THROW
1063 }
1064
1065 return d_alignment;
1066}
1067
1068template <class t_CHAR>
1070typename
1073{
1074 if (e_STATE_UNPARSED == d_processingState) {
1075 BSLS_THROW(bsl::format_error(
1076 "Format specification '.parse' not called (`sign`)")); // THROW
1077 }
1078
1079 return d_sign;
1080}
1081
1082template <class t_CHAR>
1085{
1086 if (e_STATE_UNPARSED == d_processingState) {
1087 BSLS_THROW(bsl::format_error("Format specification '.parse' not "
1088 "called (`alternativeFlag`)")); // THROW
1089 }
1090
1091 return d_alternativeFlag;
1092}
1093
1094template <class t_CHAR>
1097{
1098 if (e_STATE_UNPARSED == d_processingState) {
1099 BSLS_THROW(bsl::format_error("Format specification '.parse' not "
1100 "called (`zeroPaddingFlag`)")); // THROW
1101 }
1102
1103 return d_zeroPaddingFlag;
1104}
1105
1106template <class t_CHAR>
1109{
1110 if (e_STATE_POSTPROCESSED != d_processingState) {
1111 BSLS_THROW(
1112 bsl::format_error("Format specification '.postprocess' not called "
1113 "(`postprocessedWidth`)")); // THROW
1114 }
1115
1116 return d_postprocessedWidth;
1117}
1118
1119template <class t_CHAR>
1122{
1123 if (e_STATE_POSTPROCESSED != d_processingState) {
1124 BSLS_THROW(
1125 bsl::format_error("Format specification '.postprocess' not called "
1126 "(`postprocessedPrecision`)")); // THROW
1127 }
1128
1129 return d_postprocessedPrecision;
1130}
1131
1132template <class t_CHAR>
1135{
1136 if (e_STATE_UNPARSED == d_processingState) {
1137 BSLS_THROW(bsl::format_error("Format specification '.parse' not "
1138 "called (`rawWidth`)")); // THROW
1139 }
1140
1141 return d_rawWidth;
1142}
1143
1144template <class t_CHAR>
1147{
1148 if (e_STATE_UNPARSED == d_processingState) {
1149 BSLS_THROW(bsl::format_error("Format specification '.parse' not "
1150 "called (`rawPrecision`)")); // THROW
1151 }
1152
1153 return d_rawPrecision;
1154}
1155
1156template <class t_CHAR>
1159{
1160 if (e_STATE_UNPARSED == d_processingState) {
1161 BSLS_THROW(
1162 bsl::format_error("Format specification '.parse' not called "
1163 "(`localeSpecificFlag`)")); // THROW
1164 }
1165
1166 return d_localeSpecificFlag;
1167}
1168
1169template <class t_CHAR>
1173{
1174 return d_processingState;
1175}
1176
1177template <class t_CHAR>
1181{
1182 if (e_STATE_UNPARSED == d_processingState) {
1183 BSLS_THROW(bsl::format_error("Format specification '.parse' not "
1184 "called (`remainingSpec`)")); // THROW
1185 }
1186
1187 return d_spec;
1188}
1189
1190} // close package namespace
1191
1192
1193#endif // INCLUDED_BSLFMT_FORMATSPECIFICATIONPARSER
1194
1195// ----------------------------------------------------------------------------
1196// Copyright 2025 Bloomberg Finance L.P.
1197//
1198// Licensed under the Apache License, Version 2.0 (the "License");
1199// you may not use this file except in compliance with the License.
1200// You may obtain a copy of the License at
1201//
1202// http://www.apache.org/licenses/LICENSE-2.0
1203//
1204// Unless required by applicable law or agreed to in writing, software
1205// distributed under the License is distributed on an "AS IS" BASIS,
1206// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1207// See the License for the specific language governing permissions and
1208// limitations under the License.
1209// ----------------------------- END-OF-FILE ----------------------------------
1210
1211/** @} */
1212/** @} */
1213/** @} */
Definition bslstl_stringview.h:471
Definition bslfmt_formatspecificationparser.h:151
void postprocess(const t_FORMAT_CONTEXT &context)
Definition bslfmt_formatspecificationparser.h:994
BSLS_KEYWORD_CONSTEXPR_CPP20 Sign sign() const
Definition bslfmt_formatspecificationparser.h:1072
BSLS_KEYWORD_CONSTEXPR_CPP20 const FormatterSpecificationNumericValue rawPrecision() const
Definition bslfmt_formatspecificationparser.h:1146
BSLS_KEYWORD_CONSTEXPR_CPP20 ProcessingState processingState() const
Return the current processing state of this parser.
Definition bslfmt_formatspecificationparser.h:1172
BSLS_KEYWORD_CONSTEXPR_CPP20 const t_CHAR * filler() const
Definition bslfmt_formatspecificationparser.h:1018
BSLS_KEYWORD_CONSTEXPR_CPP20 int numFillerCharacters() const
Definition bslfmt_formatspecificationparser.h:1030
BSLS_KEYWORD_CONSTEXPR_CPP20 FormatSpecificationParser()
Create a default instance of FormatSpecificationParser.
Definition bslfmt_formatspecificationparser.h:913
BSLS_KEYWORD_CONSTEXPR_CPP20 const FormatterSpecificationNumericValue rawWidth() const
Definition bslfmt_formatspecificationparser.h:1134
BSLS_KEYWORD_CONSTEXPR_CPP20 bool localeSpecificFlag() const
Definition bslfmt_formatspecificationparser.h:1158
BSLS_KEYWORD_CONSTEXPR_CPP20 const FormatterSpecificationNumericValue postprocessedWidth() const
Definition bslfmt_formatspecificationparser.h:1108
BSLS_KEYWORD_CONSTEXPR_CPP20 bool alternativeFlag() const
Definition bslfmt_formatspecificationparser.h:1084
BSLS_KEYWORD_CONSTEXPR_CPP20 const bsl::basic_string_view< t_CHAR > remainingSpec() const
Definition bslfmt_formatspecificationparser.h:1180
BSLS_KEYWORD_CONSTEXPR_CPP20 const FormatterSpecificationNumericValue postprocessedPrecision() const
Definition bslfmt_formatspecificationparser.h:1121
BSLS_KEYWORD_CONSTEXPR_CPP20 int fillerCodePointDisplayWidth() const
Definition bslfmt_formatspecificationparser.h:1043
BSLS_KEYWORD_CONSTEXPR_CPP20 void parse(t_PARSE_CONTEXT *parseContext, Sections sections)
Definition bslfmt_formatspecificationparser.h:929
BSLS_KEYWORD_CONSTEXPR_CPP20 bool zeroPaddingFlag() const
Definition bslfmt_formatspecificationparser.h:1096
BSLS_KEYWORD_CONSTEXPR_CPP20 Alignment alignment() const
Definition bslfmt_formatspecificationparser.h:1058
@ e_UTF16
Definition bslfmt_unicodecodepoint.h:79
@ e_UTF8
Definition bslfmt_unicodecodepoint.h:79
@ e_UTF32
Definition bslfmt_unicodecodepoint.h:79
#define BSLMF_ASSERT(expr)
Definition bslmf_assert.h:231
#define BSLS_THROW(X)
Definition bsls_exceptionutil.h:374
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
#define BSLS_KEYWORD_CONSTEXPR_CPP20
Definition bsls_keyword.h:645
#define BSLS_KEYWORD_CONSTEXPR_MEMBER
Definition bsls_keyword.h:625
Definition bslfmt_enablestreamedformatter.h:130
Definition bslmf_issame.h:146
Definition bslfmt_formatspecificationparser.h:105
Alignment
Definition bslfmt_formatspecificationparser.h:114
@ e_ALIGN_MIDDLE
Definition bslfmt_formatspecificationparser.h:117
@ e_ALIGN_RIGHT
Definition bslfmt_formatspecificationparser.h:118
@ e_ALIGN_DEFAULT
Definition bslfmt_formatspecificationparser.h:115
@ e_ALIGN_LEFT
Definition bslfmt_formatspecificationparser.h:116
Sections
Definition bslfmt_formatspecificationparser.h:128
@ e_SECTIONS_ALTERNATE_FLAG
Definition bslfmt_formatspecificationparser.h:132
@ e_SECTIONS_SIGN_FLAG
Definition bslfmt_formatspecificationparser.h:131
@ e_SECTIONS_ZERO_PAD_FLAG
Definition bslfmt_formatspecificationparser.h:133
@ e_SECTIONS_WIDTH
Definition bslfmt_formatspecificationparser.h:134
@ e_SECTIONS_REMAINING_SPEC
Definition bslfmt_formatspecificationparser.h:137
@ e_SECTIONS_NONE
Definition bslfmt_formatspecificationparser.h:129
@ e_SECTIONS_ALL
Definition bslfmt_formatspecificationparser.h:138
@ e_SECTIONS_LOCALE_FLAG
Definition bslfmt_formatspecificationparser.h:136
@ e_SECTIONS_PRECISION
Definition bslfmt_formatspecificationparser.h:135
@ e_SECTIONS_FILL_ALIGN
Definition bslfmt_formatspecificationparser.h:130
Sign
Definition bslfmt_formatspecificationparser.h:121
@ e_SIGN_NEGATIVE
Definition bslfmt_formatspecificationparser.h:124
@ e_SIGN_POSITIVE
Definition bslfmt_formatspecificationparser.h:123
@ e_SIGN_DEFAULT
Definition bslfmt_formatspecificationparser.h:122
@ e_SIGN_SPACE
Definition bslfmt_formatspecificationparser.h:125
ProcessingState
Definition bslfmt_formatspecificationparser.h:108
@ e_STATE_UNPARSED
Definition bslfmt_formatspecificationparser.h:109
@ e_STATE_POSTPROCESSED
Definition bslfmt_formatspecificationparser.h:111
@ e_STATE_PARSED
Definition bslfmt_formatspecificationparser.h:110
Definition bslfmt_formatterspecificationnumericvalue.h:100
@ e_ARG_ID
Definition bslfmt_formatterspecificationnumericvalue.h:107
@ e_NEXT_ARG
Definition bslfmt_formatterspecificationnumericvalue.h:106