9#ifndef INCLUDED_BSLFMT_FORMATSPECIFICATIONPARSER
10#define INCLUDED_BSLFMT_FORMATSPECIFICATIONPARSER
77#include <bslscm_version.h>
91#if defined(BSLS_LIBRARYFEATURES_HAS_CPP20_FORMAT)
149template <
class t_CHAR>
166 t_CHAR d_filler[s_fillerBufferSize];
169 int d_numFillerCharacters;
172 int d_fillerDisplayWidth;
181 bool d_alternativeFlag;
186 bool d_zeroPaddingFlag;
206 bool d_localeSpecificFlag;
235 const char firstChar);
245 const wchar_t firstChar);
253 template <
class t_ITER>
261 void postprocessFiller();
267 template <
class t_ITER>
274 template <
class t_ITER>
282 template <
class t_ITER>
292 template <
class t_ITER>
302 template <
class t_ITER>
310 template <
class t_ITER>
319 template <
class t_ITER>
337 template <
class t_PARSE_CONTEXT>
339 parse(t_PARSE_CONTEXT *parseContext,
346 template <
typename t_FORMAT_CONTEXT>
436template <
class t_CHAR>
445 return e_ALIGN_RIGHT;
448 return e_ALIGN_MIDDLE;
450 return e_ALIGN_DEFAULT;
453template <
class t_CHAR>
456FormatSpecificationParser<t_CHAR>::signFromChar(t_CHAR in)
459 return e_SIGN_POSITIVE;
462 return e_SIGN_NEGATIVE;
467 return e_SIGN_DEFAULT;
470template <
class t_CHAR>
473FormatSpecificationParser<t_CHAR>::codepointBytesIfValid(
const char firstChar)
475 const unsigned char unsignedValue =
static_cast<unsigned char>(firstChar);
476 if ((unsignedValue & 0x80) == 0x00) {
479 else if ((unsignedValue & 0xe0) == 0xc0) {
482 else if ((unsignedValue & 0xf0) == 0xe0) {
485 else if ((unsignedValue & 0xf8) == 0xf0) {
493template <
class t_CHAR>
496FormatSpecificationParser<t_CHAR>::codepointBytesIfValid(
497 const wchar_t firstChar)
499 switch (
sizeof(
wchar_t)) {
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)) {
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)) {
520 if (
static_cast<unsigned long>(firstChar) <=
521 static_cast<unsigned long>(0x10ffff)) {
535template <
class t_CHAR>
536template <
class t_ITER>
538FormatSpecificationParser<t_CHAR>::parseFillAndAlignment(t_ITER *start,
542 (
bsl::is_same<
typename bsl::iterator_traits<t_ITER>::value_type,
546 if (*start == end || **start ==
'}') {
548 d_numFillerCharacters = 1;
549 d_fillerDisplayWidth = 1;
553 const int cpBytes = codepointBytesIfValid(**start);
557 bsl::format_error(
"Invalid unicode code point (`parse`)"));
560 const int cpChars = cpBytes /
sizeof(t_CHAR);
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"));
568 d_filler[i] = *tempIter;
571 d_filler[cpChars] = 0;
573 d_numFillerCharacters = cpChars;
575 t_ITER aligner = tempIter;
577 if (aligner != end) {
578 if (alignmentFromChar(*aligner) != e_ALIGN_DEFAULT) {
582 if (
'{' == **start ||
'}' == **start) {
584 "Invalid fill character ('{' or '}')"));
586 d_alignment = alignmentFromChar(*aligner);
589 d_fillerDisplayWidth = -1;
591 *start = aligner + 1;
601 d_numFillerCharacters = 1;
602 d_fillerDisplayWidth = 1;
604 if (alignmentFromChar(*aligner) != e_ALIGN_DEFAULT) {
605 d_alignment = alignmentFromChar(*aligner);
606 *start = aligner + 1;
615template <
class t_CHAR>
617FormatSpecificationParser<t_CHAR>::postprocessFiller()
622 switch (
sizeof(t_CHAR)) {
625 (
const void *)d_filler,
626 d_numFillerCharacters);
630 (
const void *)d_filler,
631 d_numFillerCharacters * 2);
635 (
const void *)d_filler,
636 d_numFillerCharacters * 4);
639 BSLS_THROW(bsl::format_error(
"Unsupported wchar_t size"));
645 "Invalid unicode code point (`postprocess`)"));
648 if (
static_cast<size_t>(cp.numSourceBytes()) != d_numFillerCharacters *
651 bsl::format_error(
"Invalid unicode code point size"));
655 if (
'{' == cp.codePointValue() ||
'}' == cp.codePointValue()) {
657 bsl::format_error(
"Invalid fill character ('{' or '}')"));
660 if (d_fillerDisplayWidth >= 0) {
661 if (d_fillerDisplayWidth != cp.codePointWidth()) {
663 bsl::format_error(
"Invalid code point width"));
667 d_fillerDisplayWidth = cp.codePointWidth();
671template <
class t_CHAR>
672template <
class t_ITER>
674FormatSpecificationParser<t_CHAR>::parseSign(t_ITER *start, t_ITER end)
677 (
bsl::is_same<
typename bsl::iterator_traits<t_ITER>::value_type,
681 if (*start == end || **start ==
'}') {
685 d_sign = signFromChar(**start);
687 if (e_SIGN_DEFAULT != d_sign) {
692template <
class t_CHAR>
693template <
class t_ITER>
695FormatSpecificationParser<t_CHAR>::parseAlternateOption(t_ITER *start,
699 (
bsl::is_same<
typename bsl::iterator_traits<t_ITER>::value_type,
703 if (*start == end || **start ==
'}') {
707 d_alternativeFlag = ((t_CHAR)
'#' == **start);
709 if (d_alternativeFlag) {
714template <
class t_CHAR>
715template <
class t_ITER>
717FormatSpecificationParser<t_CHAR>::parseZeroPaddingFlag(t_ITER *start,
721 (
bsl::is_same<
typename bsl::iterator_traits<t_ITER>::value_type,
725 if (*start == end || **start ==
'}') {
729 d_zeroPaddingFlag = ((t_CHAR)
'0' == **start);
731 if (d_zeroPaddingFlag) {
736template <
class t_CHAR>
737template <
class t_ITER>
739FormatSpecificationParser<t_CHAR>::parseRawWidth(t_ITER *start, t_ITER end)
742 (
bsl::is_same<
typename bsl::iterator_traits<t_ITER>::value_type,
746 if (*start == end || **start ==
'}') {
750 typedef FormatterSpecificationNumericValue NumericValue;
752 d_rawWidth.parse(start, end,
false);
754 if (d_rawWidth == NumericValue(NumericValue::e_VALUE, 0)) {
755 BSLS_THROW(bsl::format_error(
"Field widths must be > 0"));
759template <
class t_CHAR>
760template <
class t_ITER>
762FormatSpecificationParser<t_CHAR>::parseRawPrecision(t_ITER *start, t_ITER end)
765 (
bsl::is_same<
typename bsl::iterator_traits<t_ITER>::value_type,
769 if (*start == end || **start ==
'}') {
773 return d_rawPrecision.parse(start, end,
true);
776template <
class t_CHAR>
777template <
class t_ITER>
779FormatSpecificationParser<t_CHAR>::parseLocaleSpecificFlag(t_ITER *start,
783 (
bsl::is_same<
typename bsl::iterator_traits<t_ITER>::value_type,
787 if (*start == end || **start ==
'}') {
791 d_localeSpecificFlag = ((t_CHAR)
'L' == **start);
793 if (d_localeSpecificFlag) {
800template <
class t_CHAR>
801template <
class t_ITER>
808 (
bsl::is_same<
typename bsl::iterator_traits<t_ITER>::value_type,
811 d_sections = sections;
815 d_numFillerCharacters = 1;
816 d_fillerDisplayWidth = 1;
819 if (*start == end || **start ==
'}') {
823 if (0 != (sections & e_SECTIONS_FILL_ALIGN)) {
824 parseFillAndAlignment(start, end);
826 if (*start == end || **start ==
'}') {
831 if (0 != (sections & e_SECTIONS_SIGN_FLAG)) {
832 parseSign(start, end);
834 if (*start == end || **start ==
'}') {
839 if (0 != (sections & e_SECTIONS_ALTERNATE_FLAG)) {
840 parseAlternateOption(start, end);
842 if (*start == end || **start ==
'}') {
847 if (0 != (sections & e_SECTIONS_ZERO_PAD_FLAG)) {
848 parseZeroPaddingFlag(start, end);
850 if (*start == end || **start ==
'}') {
855 if (0 != (sections & e_SECTIONS_WIDTH)) {
856 parseRawWidth(start, end);
858 if (*start == end || **start ==
'}') {
863 if (0 != (sections & e_SECTIONS_PRECISION)) {
864 parseRawPrecision(start, end);
866 if (*start == end || **start ==
'}') {
871 if (0 != (sections & e_SECTIONS_LOCALE_FLAG)) {
872 parseLocaleSpecificFlag(start, end);
874 if (*start == end || **start ==
'}') {
879 if (0 != (sections & e_SECTIONS_REMAINING_SPEC)) {
885 while (temp.size() > counter) {
886 if (
'{' == temp[counter]) {
889 else if (
'}' == temp[counter]) {
898 d_spec = temp.substr(0, counter);
902 if (*start == end || **start ==
'}') {
907 "Specification parse failure (invalid character)"));
911template <
class t_CHAR>
914: d_sections(e_SECTIONS_NONE)
915, d_processingState(e_STATE_UNPARSED)
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)
927template <
class t_CHAR>
928template <
class t_PARSE_CONTEXT>
930 t_PARSE_CONTEXT *parseContext,
935 typename t_PARSE_CONTEXT::const_iterator>::value_type,
938 d_processingState = e_STATE_PARSED;
940 typename t_PARSE_CONTEXT::const_iterator current = parseContext->begin();
941 typename t_PARSE_CONTEXT::const_iterator end = parseContext->end();
943 rawParse(¤t, end, sections);
947 if (d_rawWidth.category() ==
949 d_rawPrecision.category() ==
951 BSLS_THROW(bsl::format_error(
"Cannot mix automatic (width) and manual "
952 "(precision) indexing"));
955 if (d_rawWidth.category() ==
957 d_rawPrecision.category() ==
959 BSLS_THROW(bsl::format_error(
"Cannot mix manual (width) and automatic "
960 "(precision) indexing"));
963 if (0 != (sections & e_SECTIONS_WIDTH)) {
964 if (rawWidth().category() ==
966 parseContext->check_arg_id(rawWidth().value());
968 else if (rawWidth().category() ==
972 static_cast<int>(parseContext->next_arg_id()));
976 if (0 != (sections & e_SECTIONS_PRECISION)) {
977 if (rawPrecision().category() ==
979 parseContext->check_arg_id(rawPrecision().value());
981 else if (rawPrecision().category() ==
985 static_cast<int>(parseContext->next_arg_id()));
989 parseContext->advance_to(current);
992template <
class t_CHAR>
993template <
typename t_FORMAT_CONTEXT>
995 const t_FORMAT_CONTEXT& context)
997 if (0 != (d_sections & e_SECTIONS_FILL_ALIGN)) {
998 FormatSpecificationParser::postprocessFiller();
1001 if (0 != (d_sections & e_SECTIONS_WIDTH)) {
1002 d_postprocessedWidth = d_rawWidth;
1003 d_postprocessedWidth.postprocess(context);
1006 if (0 != (d_sections & e_SECTIONS_PRECISION)) {
1007 d_postprocessedPrecision = d_rawPrecision;
1008 d_postprocessedPrecision.postprocess(context);
1011 d_processingState = e_STATE_POSTPROCESSED;
1016template <
class t_CHAR>
1020 if (e_STATE_UNPARSED == d_processingState) {
1022 "Format specification '.parse' not called (`filler`)"));
1028template <
class t_CHAR>
1032 if (e_STATE_UNPARSED == d_processingState) {
1034 bsl::format_error(
"Format specification '.parse' not called "
1035 "(`numFillerCharacters`)"));
1038 return d_numFillerCharacters;
1041template <
class t_CHAR>
1045 if (e_STATE_POSTPROCESSED != d_processingState) {
1047 bsl::format_error(
"Format specification '.postprocess' not called "
1048 "(`fillerCodePointDisplayWidth`)"));
1051 return d_fillerDisplayWidth;
1054template <
class t_CHAR>
1060 if (e_STATE_UNPARSED == d_processingState) {
1061 BSLS_THROW(bsl::format_error(
"Format specification '.parse' not "
1062 "called (`alignment`)"));
1068template <
class t_CHAR>
1074 if (e_STATE_UNPARSED == d_processingState) {
1076 "Format specification '.parse' not called (`sign`)"));
1082template <
class t_CHAR>
1086 if (e_STATE_UNPARSED == d_processingState) {
1087 BSLS_THROW(bsl::format_error(
"Format specification '.parse' not "
1088 "called (`alternativeFlag`)"));
1091 return d_alternativeFlag;
1094template <
class t_CHAR>
1098 if (e_STATE_UNPARSED == d_processingState) {
1099 BSLS_THROW(bsl::format_error(
"Format specification '.parse' not "
1100 "called (`zeroPaddingFlag`)"));
1103 return d_zeroPaddingFlag;
1106template <
class t_CHAR>
1110 if (e_STATE_POSTPROCESSED != d_processingState) {
1112 bsl::format_error(
"Format specification '.postprocess' not called "
1113 "(`postprocessedWidth`)"));
1116 return d_postprocessedWidth;
1119template <
class t_CHAR>
1123 if (e_STATE_POSTPROCESSED != d_processingState) {
1125 bsl::format_error(
"Format specification '.postprocess' not called "
1126 "(`postprocessedPrecision`)"));
1129 return d_postprocessedPrecision;
1132template <
class t_CHAR>
1136 if (e_STATE_UNPARSED == d_processingState) {
1137 BSLS_THROW(bsl::format_error(
"Format specification '.parse' not "
1138 "called (`rawWidth`)"));
1144template <
class t_CHAR>
1148 if (e_STATE_UNPARSED == d_processingState) {
1149 BSLS_THROW(bsl::format_error(
"Format specification '.parse' not "
1150 "called (`rawPrecision`)"));
1153 return d_rawPrecision;
1156template <
class t_CHAR>
1160 if (e_STATE_UNPARSED == d_processingState) {
1162 bsl::format_error(
"Format specification '.parse' not called "
1163 "(`localeSpecificFlag`)"));
1166 return d_localeSpecificFlag;
1169template <
class t_CHAR>
1174 return d_processingState;
1177template <
class t_CHAR>
1182 if (e_STATE_UNPARSED == d_processingState) {
1183 BSLS_THROW(bsl::format_error(
"Format specification '.parse' not "
1184 "called (`remainingSpec`)"));
Definition bslstl_stringview.h:471
@ 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