15#ifndef JAU_STRING_CFMT2_HPP_
16#define JAU_STRING_CFMT2_HPP_
47 constexpr static const char *to_string(pstate_t s)
noexcept {
49 case pstate_t::outside:
return "outside";
50 case pstate_t::start:
return "start";
51 case pstate_t::field_width:
return "width";
52 case pstate_t::precision:
return "precision";
53 default:
return "error";
56 enum class plength_t {
67 constexpr static const char *to_string(plength_t s)
noexcept {
69 case plength_t::hh:
return "hh";
70 case plength_t::h:
return "h";
71 case plength_t::none:
return "";
72 case plength_t::l:
return "l";
73 case plength_t::ll:
return "ll";
74 case plength_t::j:
return "j";
75 case plength_t::z:
return "z";
76 case plength_t::t:
return "t";
77 case plength_t::L:
return "L";
78 default:
return "n/a";
82 static constexpr bool isDigit(
const char c)
noexcept {
return '0' <= c && c <=
'9'; }
90 const std::string_view fmt;
92 const ssize_t arg_count;
96 const bool precision_set;
99 constexpr PResult(
const char (&fmt_)[N]) noexcept
100 : fmt(fmt_, N), pos(0), arg_count(0), line(0), state(pstate_t::outside), length_mod(plength_t::none), precision_set(
false) { }
102 constexpr PResult(
const std::string_view fmt_) noexcept
103 : fmt(fmt_), pos(0), arg_count(0), line(0), state(pstate_t::outside), length_mod(plength_t::none), precision_set(
false) { }
105 constexpr PResult(
const PResult &pre)
noexcept =
default;
107 constexpr PResult &operator=(
const PResult &x)
noexcept =
delete;
109 constexpr bool hasNext() const noexcept {
110 return !error() && pos < fmt.length() - 1;
113 constexpr ssize_t argCount() const noexcept {
return arg_count; }
114 constexpr bool error() const noexcept {
return pstate_t::error == state; }
115 constexpr char sym() const noexcept {
return fmt[pos]; }
117 std::string toString()
const {
118 const char c = pos < fmt.length() ? fmt[pos] :
'@';
119 std::string s =
"args ";
120 s.append(std::to_string(arg_count))
124 .append(std::to_string(line))
126 .append(std::to_string(pos))
128 .append(std::string(1, c))
129 .append(
"`, length `")
131 .append(
"`, precision ")
132 .append(std::to_string(precision_set))
140 template <
typename T>
141 friend class impl::Parser;
143 constexpr PResult(
const PResult &pre,
size_t pos2) noexcept
144 : fmt(pre.fmt), pos(pos2), arg_count(pre.arg_count), line(pre.line), state(pre.state), length_mod(pre.length_mod), precision_set(pre.precision_set) {}
146 constexpr PResult(
const PResult &pre,
size_t pos2, ssize_t arg_count2) noexcept
147 : fmt(pre.fmt), pos(pos2), arg_count(arg_count2), line(pre.line), state(pre.state), length_mod(pre.length_mod), precision_set(pre.precision_set) {}
149 constexpr PResult(
const PResult &pre, pstate_t state2) noexcept
150 : fmt(pre.fmt), pos(pre.pos), arg_count(pre.arg_count), line(pre.line), state(state2), length_mod(pre.length_mod), precision_set(pre.precision_set) {}
152 constexpr PResult(
const PResult &pre, pstate_t state2,
size_t pos2) noexcept
153 : fmt(pre.fmt), pos(pos2), arg_count(pre.arg_count), line(pre.line), state(state2), length_mod(pre.length_mod), precision_set(pre.precision_set) {}
155 constexpr PResult(
const PResult &pre, pstate_t state2,
size_t pos2, ssize_t arg_count2,
int line2) noexcept
156 : fmt(pre.fmt), pos(pos2), arg_count(arg_count2), line(line2), state(state2), length_mod(pre.length_mod), precision_set(pre.precision_set) {}
158 constexpr PResult(
const PResult &pre, plength_t length_mod2,
bool precision_set2) noexcept
159 : fmt(pre.fmt), pos(pre.pos), arg_count(pre.arg_count), line(pre.line), state(pre.state), length_mod(length_mod2), precision_set(precision_set2) {}
161 constexpr PResult nextSymbol() const noexcept {
162 if( pos < fmt.length() - 1 ) {
163 return PResult(*
this, pos+1);
168 constexpr PResult toConversion() const noexcept {
169 if( pstate_t::outside != state ) {
170 return PResult(*
this);
171 }
else if( fmt[pos] ==
'%' ) {
172 return PResult(*
this, pstate_t::start, pos);
175 const size_t q = fmt.find(
'%', pos + 1);
176 if( q == std::string::npos ) {
178 return PResult(*
this, fmt.length());
181 return PResult(*
this, pstate_t::start, q);
186 constexpr PResult setError(
int l)
const noexcept {
188 if( 0 == arg_count ) {
189 arg_count2 = std::numeric_limits<ssize_t>::min();
190 }
else if( 0 < arg_count ) {
191 arg_count2 = arg_count * -1;
193 arg_count2 = arg_count;
195 return PResult(*
this, pstate_t::error, pos, arg_count2, l);
199 inline std::ostream &
operator<<(std::ostream &out,
const PResult &pc) {
200 out << pc.toString();
206 static constexpr bool verbose_error =
true;
208 enum class no_type_t {};
210 template <
typename T>
213 constexpr Parser() noexcept = delete;
225 static constexpr const PResult parseOne(const PResult pc) noexcept {
226 if( !pc.hasNext() ) {
230 const PResult pc2 = pc.toConversion();
231 if( !pc2.hasNext() ) {
238 const PResult pc3 = pc2.nextSymbol();
240 if( pstate_t::start == pc3.state ) {
241 const PResult pc4 = parseFlags(PResult(pc3, pstate_t::field_width));
244 bool next_arg = false;
245 const PResult pc5 = parseFieldWidth(pc4, next_arg);
246 if( next_arg || pc5.error() ) {
257 static constexpr const PResult parseP2(
const PResult pc)
noexcept {
258 if( pstate_t::field_width == pc.state ) {
260 const PResult pc2 = PResult(pc, pstate_t::precision);
261 if( pc2.sym() ==
'.' ) {
262 const PResult pc3 = PResult(pc2, pc2.length_mod,
true);
263 bool next_arg =
false;
264 const PResult pc4 = parsePrecision(pc3, next_arg);
265 if( next_arg || pc4.error() ) {
277 static constexpr const PResult parseP3(
const PResult pc)
noexcept {
278 const PResult pc2 = parseLengthMods(pc);
283 const PResult pc3 = parseFmtSpec(pc2);
289 const PResult pc4 = PResult(pc3, pstate_t::outside);
290 if( !pc4.hasNext() ) {
293 return PResult(pc4.nextSymbol(), plength_t::none,
false);
296 static constexpr const PResult parseFlags(
const PResult pc)
noexcept {
307 return parseFlags(pc.nextSymbol());
313 static constexpr const PResult parseDigit(
const PResult pc)
noexcept {
314 if( !pc.hasNext() || !isDigit(pc.sym()) ) {
return pc; }
315 return parseDigit( pc.nextSymbol() );
319 static constexpr const PResult parseFieldWidth(
const PResult pc,
bool& next_arg)
noexcept {
321 if( pc.sym() ==
'*' ) {
322 if( !pc.hasNext() ) {
return pc; }
323 const PResult pc2 = pc.nextSymbol();
325 using U = std::remove_cv_t<T>;
327 if constexpr( std::is_same_v<no_type_t, T> ) {
328 return pc2.setError(__LINE__);
330 const PResult pc3 = PResult(pc2, pc.arg_count+1);
331 if constexpr( !std::is_same_v<int, U> ) {
332 return pc3.setError(__LINE__);
338 return parseDigit(pc);
343 static constexpr const PResult parsePrecision(
const PResult pc,
bool &next_arg)
noexcept {
345 if( !pc.hasNext() ) {
return pc; }
346 const PResult pc2 = pc.nextSymbol();
347 const char c = pc.fmt[pc.pos];
349 if( !pc2.hasNext() ) {
return pc2; }
350 const PResult pc3 = pc2.nextSymbol();
352 using U = std::remove_cv_t<T>;
354 if constexpr( std::is_same_v<no_type_t, T> ) {
355 return pc3.setError(__LINE__);
357 const PResult pc4 = PResult(pc3, pc.arg_count+1);
358 if constexpr( !std::is_same_v<int, U> ) {
359 return pc4.setError(__LINE__);
365 return parseDigit(pc2);
369 static constexpr const PResult parseLengthMods(
const PResult pc)
noexcept {
370 const char sym = pc.sym();
372 if( !pc.hasNext() ) {
return pc.setError(__LINE__); }
373 const PResult pc2 = pc.nextSymbol();
374 if(
'h' == pc2.sym() ) {
375 if( !pc2.hasNext() ) {
return pc2.setError(__LINE__); }
376 return PResult(pc2.nextSymbol(), plength_t::hh, pc2.precision_set);
378 return PResult(pc2, plength_t::h, pc2.precision_set);
380 }
else if(
'l' == sym ) {
381 if( !pc.hasNext() ) {
return pc.setError(__LINE__); }
382 const PResult pc2 = pc.nextSymbol();
383 if(
'l' == pc2.sym() ) {
384 if( !pc2.hasNext() ) {
return pc2.setError(__LINE__); }
385 return PResult(pc2.nextSymbol(), plength_t::ll, pc2.precision_set);
387 return PResult(pc2, plength_t::l, pc2.precision_set);
389 }
else if(
'j' == sym ) {
390 if( !pc.hasNext() ) {
return pc.setError(__LINE__); }
391 return PResult(pc.nextSymbol(), plength_t::j, pc.precision_set);
392 }
else if(
'z' == sym ) {
393 if( !pc.hasNext() ) {
return pc.setError(__LINE__); }
394 return PResult(pc.nextSymbol(), plength_t::z, pc.precision_set);
395 }
else if(
't' == sym ) {
396 if( !pc.hasNext() ) {
return pc.setError(__LINE__); }
397 return PResult(pc.nextSymbol(), plength_t::t, pc.precision_set);
398 }
else if(
'L' == sym ) {
399 if( !pc.hasNext() ) {
return pc.setError(__LINE__); }
400 return PResult(pc.nextSymbol(), plength_t::L, pc.precision_set);
402 return PResult(pc, plength_t::none, pc.precision_set);
406 static constexpr const PResult parseFmtSpec(
const PResult pc)
noexcept {
407 const char fmt_literal = unaliasFmtSpec( pc.sym() );
409 switch( fmt_literal ) {
413 return parseStringFmtSpec(pc, fmt_literal);
415 return parseAPointerFmtSpec(pc);
417 return parseSignedFmtSpec(pc);
422 return parseUnsignedFmtSpec(pc, fmt_literal);
430 return parseFloatFmtSpec(pc, fmt_literal);
432 return pc.setError(__LINE__);
436 static constexpr char unaliasFmtSpec(
const char fmt_literal)
noexcept {
437 switch( fmt_literal ) {
438 case 'i':
return 'd';
439 case 'F':
return 'f';
440 default:
return fmt_literal;
444 static constexpr const PResult parseStringFmtSpec(
const PResult pc,
const char fmt_literal)
noexcept {
445 if constexpr( std::is_same_v<no_type_t, T> ) {
446 return pc.setError(__LINE__);
448 switch( fmt_literal ) {
452 const PResult pc2 = PResult(pc, pc.pos, pc.arg_count+1);
453 using U = std::remove_cv_t<T>;
454 switch( pc2.length_mod ) {
455 case plength_t::none:
456 if constexpr( !std::is_same_v<char, U> ||
457 !std::is_same_v<int, U> ) {
458 return pc2.setError(__LINE__);
462 if constexpr( !std::is_same_v<wchar_t, U> ||
463 !std::is_same_v<wint_t, U> ) {
464 return pc2.setError(__LINE__);
468 return pc2.setError(__LINE__);
473 const PResult pc2 = PResult(pc, pc.pos, pc.arg_count+1);
474 switch( pc2.length_mod ) {
475 case plength_t::none:
476 if constexpr( !std::is_pointer_v<T> ||
477 !std::is_same_v<char, std::remove_cv_t<std::remove_pointer_t<T>>> ) {
478 return pc2.setError(__LINE__);
482 if constexpr( !std::is_pointer_v<T> ||
483 !std::is_same_v<wchar_t, std::remove_cv_t<std::remove_pointer_t<T>>> ) {
484 return pc2.setError(__LINE__);
488 return pc2.setError(__LINE__);
492 default:
return pc.setError(__LINE__);
497 static constexpr const PResult parseAPointerFmtSpec(
const PResult pc)
noexcept {
498 const PResult pc2 = PResult(pc, plength_t::none, pc.precision_set);
499 if constexpr( std::is_same_v<no_type_t, T> ) {
500 return pc2.setError(__LINE__);
502 const PResult pc3 = PResult(pc2, pc2.pos, pc2.arg_count+1);
503 if constexpr( !std::is_pointer_v<T> ) {
504 return pc3.setError(__LINE__);
509 static constexpr const PResult parseSignedFmtSpec(
const PResult pc)
noexcept {
510 if constexpr( std::is_same_v<no_type_t, T> ) {
511 return pc.setError(__LINE__);
513 const PResult pc2 = PResult(pc, pc.pos, pc.arg_count+1);
515 using U = std::remove_cv_t<T>;
517 using V =
typename std::conditional_t<std::is_integral_v<U> && std::is_unsigned_v<U>, std::make_signed<U>, std::type_identity<U>>::type;
519 switch( pc2.length_mod ) {
521 if constexpr( !std::is_same_v<char, V> && (!std::is_integral_v<V> ||
sizeof(V) >
sizeof(char)) ) {
522 return pc2.setError(__LINE__);
526 if constexpr( !std::is_same_v<short, V> && (!std::is_integral_v<V> ||
sizeof(V) >
sizeof(short)) ) {
527 return pc2.setError(__LINE__);
530 case plength_t::none:
531 if constexpr( !std::is_same_v<int, V> && (!std::is_integral_v<V> ||
sizeof(V) >
sizeof(int)) ) {
532 return pc2.setError(__LINE__);
536 if constexpr( !std::is_same_v<long, V> && (!std::is_integral_v<V> ||
sizeof(V) >
sizeof(long)) ) {
537 return pc2.setError(__LINE__);
541 if constexpr( !std::is_same_v<long long, V> && (!std::is_integral_v<V> ||
sizeof(V) >
sizeof(
long long)) ) {
542 return pc2.setError(__LINE__);
546 if constexpr( !std::is_same_v<intmax_t, V> && (!std::is_integral_v<V> ||
sizeof(V) >
sizeof(intmax_t)) ) {
547 return pc2.setError(__LINE__);
551 if constexpr( !std::is_same_v<ssize_t, V> && (!std::is_integral_v<V> ||
sizeof(V) >
sizeof(ssize_t)) ) {
552 return pc2.setError(__LINE__);
556 if constexpr( !std::is_same_v<ptrdiff_t, V> && (!std::is_integral_v<V> ||
sizeof(V) >
sizeof(ptrdiff_t)) ) {
557 return pc2.setError(__LINE__);
561 return pc2.setError(__LINE__);
566 static constexpr const PResult parseUnsignedFmtSpec(
const PResult pc,
const char )
noexcept {
567 if constexpr( std::is_same_v<no_type_t, T> ) {
568 return pc.setError(__LINE__);
570 const PResult pc2 = PResult(pc, pc.pos, pc.arg_count+1);
572 using U = std::remove_cv_t<T>;
574 using V =
typename std::conditional_t<std::is_integral_v<U> && std::is_signed_v<U>, std::make_unsigned<U>, std::type_identity<U>>::type;
576 switch( pc2.length_mod ) {
578 if constexpr( !std::is_same_v<unsigned char, V> && (!std::is_integral_v<V> ||
sizeof(V) >
sizeof(
unsigned char)) ) {
579 return pc2.setError(__LINE__);
583 if constexpr( !std::is_same_v<unsigned short, V> && (!std::is_integral_v<V> ||
sizeof(V) >
sizeof(
unsigned short)) ) {
584 return pc2.setError(__LINE__);
587 case plength_t::none:
588 if constexpr( !std::is_same_v<unsigned int, V> && (!std::is_integral_v<V> ||
sizeof(V) >
sizeof(
unsigned int)) ) {
589 return pc2.setError(__LINE__);
593 if constexpr( !std::is_same_v<unsigned long, V> && (!std::is_integral_v<V> ||
sizeof(V) >
sizeof(
unsigned long)) ) {
594 return pc2.setError(__LINE__);
598 if constexpr( !std::is_same_v<unsigned long long, V> && (!std::is_integral_v<V> ||
sizeof(V) >
sizeof(
unsigned long long)) ) {
599 return pc2.setError(__LINE__);
603 if constexpr( !std::is_same_v<uintmax_t, V> && (!std::is_integral_v<V> ||
sizeof(V) >
sizeof(uintmax_t)) ) {
604 return pc2.setError(__LINE__);
608 if constexpr( !std::is_same_v<size_t, V> && (!std::is_integral_v<V> ||
sizeof(V) >
sizeof(size_t)) ) {
609 return pc2.setError(__LINE__);
614 if constexpr( !std::is_same_v<unsigned ptrdiff_t, U> ) {
615 return pc2.setError(__LINE__);
620 return pc2.setError(__LINE__);
625 static constexpr const PResult parseFloatFmtSpec(
const PResult pc,
const char )
noexcept {
626 if constexpr( std::is_same_v<no_type_t, T> ) {
627 return pc.setError(__LINE__);
629 const PResult pc2 = PResult(pc, pc.pos, pc.arg_count+1);
631 using U = std::remove_cv_t<T>;
633 switch( pc2.length_mod ) {
634 case plength_t::none:
636 if constexpr( !std::is_same_v<float, U> &&
637 !std::is_same_v<double, U> ) {
638 return pc2.setError(__LINE__);
642 if constexpr( !std::is_same_v<float, U> &&
643 !std::is_same_v<double, U> &&
644 !std::is_same_v<long double, U> ) {
646 return pc2.setError(__LINE__);
650 return pc2.setError(__LINE__);
656 constexpr const PResult checkRec(
const PResult ctx)
noexcept {
657 return Parser<no_type_t>::parseOne(ctx);
660 template <
typename Targ,
typename... Tnext>
661 constexpr const PResult checkRec(
const PResult ctx)
noexcept {
662 if constexpr( 0 <
sizeof...(Tnext) ) {
663 return checkRec<Tnext...>( Parser<Targ>::parseOne(ctx) );
665 return Parser<no_type_t>::parseOne( Parser<Targ>::parseOne(ctx) );
682 template <
typename... Targs>
683 constexpr bool check(
const std::string_view fmt,
const Targs &...)
noexcept {
684 return !impl::checkRec<Targs...>( PResult(fmt) ).
error();
697 template <
typename... Targs>
698 constexpr bool check2(
const std::string_view fmt)
noexcept {
699 return !impl::checkRec<Targs...>( PResult(fmt) ).
error();
713 template <
typename... Targs>
714 constexpr const PResult
checkR(
const std::string_view fmt,
const Targs &...)
noexcept {
715 return impl::checkRec<Targs...>( PResult(fmt) );
728 template <
typename... Targs>
729 constexpr const PResult
checkR2(
const std::string_view fmt)
noexcept {
730 return impl::checkRec<Targs...>( PResult(fmt) );
std::string to_string(const alphabet &v) noexcept
plength_t
Format length modifiers.
consteval_cxx20 ssize_t check2(std::string_view fmt) noexcept
Strict compile-time type validation of explicit argument-types against the format string.
consteval_cxx20 Result checkR(std::string_view fmt, const Targs &...) noexcept
Strict compile-time type validation of deduced argument-types against the format string.
consteval_cxx20 Result checkR2(std::string_view format) noexcept
Strict compile-time type validation of explicit argument-types against the format string.
consteval_cxx20 ssize_t check(std::string_view fmt, const Targs &...) noexcept
Strict compile-time type validation of deduced argument-types against the format string.
@ j
intmax_t or uintmax_t integer
@ z
size_t or ssize_t integer
static std::ostream & operator<<(std::ostream &out, const TestDataBF &v)