libxr  1.0
Want to be the best embedded framework
Loading...
Searching...
No Matches
printf_frontend_detail.hpp
1#pragma once
2
3#include <array>
4#include <limits>
5#include <string_view>
6
7#include "format_compile.hpp"
8
9namespace LibXR::Print::Detail::PrintfCompile
10{
11using Error = Printf::Error;
12using Length = Printf::Length;
13
24enum class ValueKind : uint8_t
25{
26 None,
27 Signed,
28 Unsigned,
29 Binary,
30 Octal,
31 HexLower,
32 HexUpper,
33 Pointer,
34 Character,
35 String,
36 FloatFixed,
37 FloatScientific,
38 FloatGeneral,
39};
40
46{
47 size_t arg_index = 0;
48 ValueKind type = ValueKind::None;
49 Length length = Length::Default;
50 bool left_align = false;
51 bool force_sign = false;
52 bool space_sign = false;
53 bool alternate = false;
54 bool zero_pad = false;
55 bool upper_case = false;
56 bool positional = false;
57 uint8_t width = 0;
58 bool has_precision = false;
59 uint8_t precision = 0;
60
62 [[nodiscard]] constexpr uint8_t FlagsByte() const
63 {
64 uint8_t flags = 0;
65 if (left_align)
66 {
67 flags |= static_cast<uint8_t>(FormatFlag::LeftAlign);
68 }
69 if (force_sign)
70 {
71 flags |= static_cast<uint8_t>(FormatFlag::ForceSign);
72 }
73 if (space_sign)
74 {
75 flags |= static_cast<uint8_t>(FormatFlag::SpaceSign);
76 }
77 if (alternate)
78 {
79 flags |= static_cast<uint8_t>(FormatFlag::Alternate);
80 }
81 if (zero_pad)
82 {
83 flags |= static_cast<uint8_t>(FormatFlag::ZeroPad);
84 }
85 if (upper_case)
86 {
87 flags |= static_cast<uint8_t>(FormatFlag::UpperCase);
88 }
89 return flags;
90 }
91
93 [[nodiscard]] constexpr uint8_t PrecisionByte() const
94 {
95 return has_precision ? precision : std::numeric_limits<uint8_t>::max();
96 }
97};
98
103enum class FeatureGate : uint8_t
104{
105 Integer,
106 IntegerBase8_16,
107 Pointer,
108 Text,
109 FloatFixed,
110 FloatScientific,
111 FloatGeneral,
112};
113
118enum class LengthPolicy : uint8_t
119{
120 Integer,
121 NoneOnly,
122 Float,
123};
124
130{
131 char token = 0;
132 ValueKind type = ValueKind::None;
133 FeatureGate gate =
134 FeatureGate::Integer;
135 LengthPolicy length_policy =
136 LengthPolicy::NoneOnly;
137 bool upper_case = false;
138};
139
144inline constexpr size_t length_rule_count =
145 static_cast<size_t>(Length::LongDouble) + 1;
146
151inline constexpr std::array<FormatArgumentRule, length_rule_count> signed_rules{
152 FormatArgumentRule::SignedAny,
153 FormatArgumentRule::SignedChar,
154 FormatArgumentRule::SignedShort,
155 FormatArgumentRule::SignedLong,
156 FormatArgumentRule::SignedLongLong,
157 FormatArgumentRule::SignedIntMax,
158 FormatArgumentRule::SignedSize,
159 FormatArgumentRule::SignedPtrDiff,
160 FormatArgumentRule::None,
161};
162
167inline constexpr std::array<FormatArgumentRule, length_rule_count> unsigned_rules{
168 FormatArgumentRule::UnsignedAny,
169 FormatArgumentRule::UnsignedChar,
170 FormatArgumentRule::UnsignedShort,
171 FormatArgumentRule::UnsignedLong,
172 FormatArgumentRule::UnsignedLongLong,
173 FormatArgumentRule::UnsignedIntMax,
174 FormatArgumentRule::UnsignedSize,
175 FormatArgumentRule::UnsignedPtrDiff,
176 FormatArgumentRule::None,
177};
178
183inline constexpr std::array<SpecifierDescriptor, 17> specifiers{{
184 {'d', ValueKind::Signed, FeatureGate::Integer, LengthPolicy::Integer, false},
185 {'i', ValueKind::Signed, FeatureGate::Integer, LengthPolicy::Integer, false},
186 {'u', ValueKind::Unsigned, FeatureGate::Integer, LengthPolicy::Integer, false},
187 {'b', ValueKind::Binary, FeatureGate::IntegerBase8_16, LengthPolicy::Integer, false},
188 {'B', ValueKind::Binary, FeatureGate::IntegerBase8_16, LengthPolicy::Integer, true},
189 {'o', ValueKind::Octal, FeatureGate::IntegerBase8_16, LengthPolicy::Integer, false},
190 {'x', ValueKind::HexLower, FeatureGate::IntegerBase8_16, LengthPolicy::Integer,
191 false},
192 {'X', ValueKind::HexUpper, FeatureGate::IntegerBase8_16, LengthPolicy::Integer,
193 false},
194 {'p', ValueKind::Pointer, FeatureGate::Pointer, LengthPolicy::NoneOnly, false},
195 {'c', ValueKind::Character, FeatureGate::Text, LengthPolicy::NoneOnly, false},
196 {'s', ValueKind::String, FeatureGate::Text, LengthPolicy::NoneOnly, false},
197 {'f', ValueKind::FloatFixed, FeatureGate::FloatFixed, LengthPolicy::Float, false},
198 {'F', ValueKind::FloatFixed, FeatureGate::FloatFixed, LengthPolicy::Float, true},
199 {'e', ValueKind::FloatScientific, FeatureGate::FloatScientific,
200 LengthPolicy::Float, false},
201 {'E', ValueKind::FloatScientific, FeatureGate::FloatScientific,
202 LengthPolicy::Float, true},
203 {'g', ValueKind::FloatGeneral, FeatureGate::FloatGeneral, LengthPolicy::Float,
204 false},
205 {'G', ValueKind::FloatGeneral, FeatureGate::FloatGeneral, LengthPolicy::Float,
206 true},
207}};
208
209namespace SourceSyntax
210{
216{
217 bool uses_positional = false;
218 bool uses_sequential = false;
219 size_t next_index = 0;
220};
221
226template <size_t MaxFieldCount>
228{
229 std::array<size_t, MaxFieldCount> order{};
230 size_t field_count = 0;
231 size_t argument_count = 0;
232 Error error = Error::None;
233
234 [[nodiscard]] consteval Error Text(size_t, size_t) const { return Error::None; }
235
236 [[nodiscard]] consteval Error Field(const Conversion& conversion)
237 {
238 order[field_count++] = conversion.arg_index;
239 size_t used_argument_count = conversion.arg_index + 1;
240 if (used_argument_count > argument_count)
241 {
242 argument_count = used_argument_count;
243 }
244 return Error::None;
245 }
246};
247
252template <size_t FieldCount, size_t ArgCount>
254{
255 std::array<size_t, FieldCount> order{};
256 std::array<FormatArgumentInfo, ArgCount> args{};
257 Error error = Error::None;
258};
259
261[[nodiscard]] constexpr bool IsDigit(char ch)
262{
263 return ch >= '0' && ch <= '9';
264}
265
267[[nodiscard]] constexpr bool HasEmbeddedNul(std::string_view source)
268{
269 for (char ch : source)
270 {
271 if (ch == '\0')
272 {
273 return true;
274 }
275 }
276 return false;
277}
278
288[[nodiscard]] consteval Error ParseArgumentIndex(std::string_view source, size_t& pos,
289 IndexingState& indexing,
290 Conversion& conversion)
291{
292 if (pos >= source.size() || !IsDigit(source[pos]))
293 {
294 return Error::None;
295 }
296
297 size_t probe = pos;
298 size_t index = 0;
299 while (probe < source.size() && IsDigit(source[probe]))
300 {
301 auto digit = static_cast<size_t>(source[probe] - '0');
302 if (index > (std::numeric_limits<size_t>::max() - digit) / 10)
303 {
304 return Error::NumberOverflow;
305 }
306
307 index = index * 10 + digit;
308 ++probe;
309 }
310
311 if (probe >= source.size() || source[probe] != '$')
312 {
313 return Error::None;
314 }
315
316 if (!Config::enable_explicit_argument_indexing)
317 {
318 return Error::PositionalArgumentDisabled;
319 }
320
321 if (index == 0)
322 {
323 return Error::InvalidArgumentIndex;
324 }
325 if (indexing.uses_sequential)
326 {
327 return Error::MixedIndexing;
328 }
329
330 indexing.uses_positional = true;
331 conversion.positional = true;
332 conversion.arg_index = index - 1;
333 pos = probe + 1;
334 return Error::None;
335}
336
342namespace Lowering
343{
344[[nodiscard]] constexpr size_t LengthIndex(Length length)
345{
346 return static_cast<size_t>(length);
347}
348
350[[nodiscard]] constexpr bool IsUnsignedType(ValueKind type)
351{
352 switch (type)
353 {
354 case ValueKind::Unsigned:
355 case ValueKind::Binary:
356 case ValueKind::Octal:
357 case ValueKind::HexLower:
358 case ValueKind::HexUpper:
359 return true;
360 default:
361 return false;
362 }
363}
364
366[[nodiscard]] consteval SpecifierDescriptor LookupSpecifier(char token)
367{
368 for (const auto& descriptor : specifiers)
369 {
370 if (descriptor.token == token)
371 {
372 return descriptor;
373 }
374 }
375
376 return {};
377}
378
380[[nodiscard]] constexpr bool TypeEnabled(FeatureGate gate, Length length)
381{
382 switch (gate)
383 {
384 case FeatureGate::Integer:
385 return Config::enable_integer;
386 case FeatureGate::IntegerBase8_16:
387 return Config::enable_integer_base8_16;
388 case FeatureGate::Pointer:
389 return Config::enable_pointer;
390 case FeatureGate::Text:
391 return Config::enable_text;
392 case FeatureGate::FloatFixed:
393 return Config::enable_float_fixed &&
394 (length != Length::LongDouble || Config::enable_float_long_double);
395 case FeatureGate::FloatScientific:
396 return Config::enable_float_scientific &&
397 (length != Length::LongDouble || Config::enable_float_long_double);
398 case FeatureGate::FloatGeneral:
399 return Config::enable_float_general &&
400 (length != Length::LongDouble || Config::enable_float_long_double);
401 }
402
403 return false;
404}
405
407[[nodiscard]] constexpr bool LengthAllowed(LengthPolicy policy, Length length)
408{
409 switch (policy)
410 {
411 case LengthPolicy::Integer:
412 return length != Length::LongDouble;
413 case LengthPolicy::NoneOnly:
414 return length == Length::Default;
415 case LengthPolicy::Float:
416 return length == Length::Default || length == Length::LongDouble;
417 }
418
419 return false;
420}
421
423[[nodiscard]] constexpr FormatArgumentRule RuleFromTable(
424 const std::array<FormatArgumentRule, length_rule_count>& rules, Length length)
425{
426 return rules[LengthIndex(length)];
427}
428
430[[nodiscard]] consteval bool RuleUses64BitStorage(FormatArgumentRule rule)
431{
432 switch (rule)
433 {
434 case FormatArgumentRule::SignedLong:
435 return sizeof(long) > sizeof(int32_t);
436 case FormatArgumentRule::SignedLongLong:
437 return sizeof(long long) > sizeof(int32_t);
438 case FormatArgumentRule::SignedIntMax:
439 return sizeof(intmax_t) > sizeof(int32_t);
440 case FormatArgumentRule::SignedSize:
441 return sizeof(std::make_signed_t<size_t>) > sizeof(int32_t);
442 case FormatArgumentRule::SignedPtrDiff:
443 return sizeof(ptrdiff_t) > sizeof(int32_t);
444 case FormatArgumentRule::UnsignedLong:
445 return sizeof(unsigned long) > sizeof(uint32_t);
446 case FormatArgumentRule::UnsignedLongLong:
447 return sizeof(unsigned long long) > sizeof(uint32_t);
448 case FormatArgumentRule::UnsignedIntMax:
449 return sizeof(uintmax_t) > sizeof(uint32_t);
450 case FormatArgumentRule::UnsignedSize:
451 return sizeof(size_t) > sizeof(uint32_t);
452 case FormatArgumentRule::UnsignedPtrDiff:
453 return sizeof(std::make_unsigned_t<ptrdiff_t>) > sizeof(uint32_t);
454 default:
455 return false;
456 }
457}
458
460[[nodiscard]] constexpr bool UsesDoubleFloatStorage()
461{
462 return Config::enable_float_double;
463}
464
469[[nodiscard]] consteval FormatType LowerType(const Conversion& conversion,
470 FormatArgumentRule rule)
471{
472 if (conversion.length == Length::LongDouble)
473 {
474 if (conversion.type == ValueKind::FloatFixed)
475 {
476 return FormatType::LongDoubleFixed;
477 }
478 if (conversion.type == ValueKind::FloatScientific)
479 {
480 return FormatType::LongDoubleScientific;
481 }
482 if (conversion.type == ValueKind::FloatGeneral)
483 {
484 return FormatType::LongDoubleGeneral;
485 }
486 }
487
488 if (conversion.type == ValueKind::Signed)
489 {
490 return RuleUses64BitStorage(rule) ? FormatType::Signed64 : FormatType::Signed32;
491 }
492 if (conversion.type == ValueKind::Unsigned)
493 {
494 return RuleUses64BitStorage(rule) ? FormatType::Unsigned64
495 : FormatType::Unsigned32;
496 }
497 if (conversion.type == ValueKind::Binary)
498 {
499 return RuleUses64BitStorage(rule) ? FormatType::Binary64 : FormatType::Binary32;
500 }
501 if (conversion.type == ValueKind::Octal)
502 {
503 return RuleUses64BitStorage(rule) ? FormatType::Octal64 : FormatType::Octal32;
504 }
505 if (conversion.type == ValueKind::HexLower)
506 {
507 return RuleUses64BitStorage(rule) ? FormatType::HexLower64
508 : FormatType::HexLower32;
509 }
510 if (conversion.type == ValueKind::HexUpper)
511 {
512 return RuleUses64BitStorage(rule) ? FormatType::HexUpper64
513 : FormatType::HexUpper32;
514 }
515
516 switch (conversion.type)
517 {
518 case ValueKind::Pointer:
519 return FormatType::Pointer;
520 case ValueKind::Character:
521 return FormatType::Character;
522 case ValueKind::String:
523 return FormatType::String;
524 case ValueKind::FloatFixed:
525 return UsesDoubleFloatStorage() ? FormatType::DoubleFixed
526 : FormatType::FloatFixed;
527 case ValueKind::FloatScientific:
528 return UsesDoubleFloatStorage() ? FormatType::DoubleScientific
529 : FormatType::FloatScientific;
530 case ValueKind::FloatGeneral:
531 return UsesDoubleFloatStorage() ? FormatType::DoubleGeneral
532 : FormatType::FloatGeneral;
533 case ValueKind::None:
534 case ValueKind::Signed:
535 case ValueKind::Unsigned:
536 case ValueKind::Binary:
537 case ValueKind::Octal:
538 case ValueKind::HexLower:
539 case ValueKind::HexUpper:
540 return FormatType::End;
541 }
542
543 return FormatType::End;
544}
545
547[[nodiscard]] consteval FormatPackKind LowerPackKind(FormatType type)
548{
549 switch (type)
550 {
551 case FormatType::Signed32:
552 return FormatPackKind::I32;
553 case FormatType::Signed64:
554 return FormatPackKind::I64;
555 case FormatType::Unsigned32:
556 case FormatType::Binary32:
557 case FormatType::Octal32:
558 case FormatType::HexLower32:
559 case FormatType::HexUpper32:
560 return FormatPackKind::U32;
561 case FormatType::Unsigned64:
562 case FormatType::Binary64:
563 case FormatType::Octal64:
564 case FormatType::HexLower64:
565 case FormatType::HexUpper64:
566 return FormatPackKind::U64;
567 case FormatType::Pointer:
568 return FormatPackKind::Pointer;
569 case FormatType::Character:
570 return FormatPackKind::Character;
571 case FormatType::String:
572 return FormatPackKind::StringView;
573 case FormatType::FloatFixed:
574 case FormatType::FloatScientific:
575 case FormatType::FloatGeneral:
576 return FormatPackKind::F32;
577 case FormatType::DoubleFixed:
578 case FormatType::DoubleScientific:
579 case FormatType::DoubleGeneral:
580 return FormatPackKind::F64;
581 case FormatType::LongDoubleFixed:
582 case FormatType::LongDoubleScientific:
583 case FormatType::LongDoubleGeneral:
584 return FormatPackKind::LongDouble;
585 case FormatType::End:
586 case FormatType::TextInline:
587 case FormatType::TextRef:
588 case FormatType::TextSpace:
589 return FormatPackKind::U32;
590 }
591
592 return FormatPackKind::U32;
593}
594
596[[nodiscard]] consteval FormatArgumentRule LowerRule(const Conversion& conversion)
597{
598 if (conversion.type == ValueKind::Signed)
599 {
600 return RuleFromTable(signed_rules, conversion.length);
601 }
602
603 if (IsUnsignedType(conversion.type))
604 {
605 return RuleFromTable(unsigned_rules, conversion.length);
606 }
607
608 switch (conversion.type)
609 {
610 case ValueKind::Pointer:
611 return FormatArgumentRule::Pointer;
612 case ValueKind::Character:
613 return FormatArgumentRule::Character;
614 case ValueKind::String:
615 return FormatArgumentRule::String;
616 case ValueKind::FloatFixed:
617 case ValueKind::FloatScientific:
618 case ValueKind::FloatGeneral:
619 return (conversion.length == Length::LongDouble)
620 ? FormatArgumentRule::LongDouble
621 : FormatArgumentRule::Float;
622 case ValueKind::None:
623 case ValueKind::Signed:
624 case ValueKind::Unsigned:
625 case ValueKind::Binary:
626 case ValueKind::Octal:
627 case ValueKind::HexLower:
628 case ValueKind::HexUpper:
629 break;
630 }
631
632 return FormatArgumentRule::None;
633}
634
636[[nodiscard]] consteval Error ValidateConversion(const Conversion& conversion)
637{
638 if ((conversion.type == ValueKind::Signed || IsUnsignedType(conversion.type)) &&
639 RuleUses64BitStorage(LowerRule(conversion)) && !Config::enable_integer_64bit)
640 {
641 return Error::InvalidLength;
642 }
643
644 return Error::None;
645}
646
648[[nodiscard]] consteval FormatField LowerField(const Conversion& conversion)
649{
650 auto rule = LowerRule(conversion);
651 auto type = LowerType(conversion, rule);
652 return FormatField{
653 .type = type,
654 .pack = LowerPackKind(type),
655 .rule = rule,
656 .flags = conversion.FlagsByte(),
657 .width = conversion.width,
658 .precision = conversion.PrecisionByte(),
659 };
660}
661} // namespace Lowering
662
667[[nodiscard]] consteval Error ParseByte(std::string_view source, size_t& pos,
668 uint8_t limit, uint8_t& value)
669{
670 value = 0;
671 if (pos >= source.size() || !IsDigit(source[pos]))
672 {
673 return Error::None;
674 }
675
676 while (pos < source.size() && IsDigit(source[pos]))
677 {
678 auto digit = static_cast<uint8_t>(source[pos] - '0');
679 if (value > static_cast<uint8_t>((limit - digit) / 10))
680 {
681 return Error::NumberOverflow;
682 }
683
684 value = static_cast<uint8_t>(value * 10 + digit);
685 ++pos;
686 }
687
688 return Error::None;
689}
690
692[[nodiscard]] consteval Error ParseFlags(std::string_view source, size_t& pos,
693 Conversion& conversion)
694{
695 while (pos < source.size())
696 {
697 switch (source[pos])
698 {
699 case '-':
700 conversion.left_align = true;
701 break;
702 case '+':
703 conversion.force_sign = true;
704 break;
705 case ' ':
706 conversion.space_sign = true;
707 break;
708 case '#':
709 if (!Config::enable_alternate)
710 {
711 return Error::InvalidSpecifier;
712 }
713 conversion.alternate = true;
714 break;
715 case '0':
716 conversion.zero_pad = true;
717 break;
718 default:
719 return Error::None;
720 }
721 ++pos;
722 }
723
724 return Error::None;
725}
726
728[[nodiscard]] consteval Error ParseWidth(std::string_view source, size_t& pos,
729 Conversion& conversion)
730{
731 if (pos < source.size() && source[pos] == '*')
732 {
733 return Error::DynamicField;
734 }
735
736 if (!Config::enable_width && pos < source.size() && IsDigit(source[pos]))
737 {
738 return Error::InvalidSpecifier;
739 }
740
741 return ParseByte(source, pos, std::numeric_limits<uint8_t>::max(), conversion.width);
742}
743
745[[nodiscard]] consteval Error ParsePrecision(std::string_view source, size_t& pos,
746 Conversion& conversion)
747{
748 if (pos >= source.size() || source[pos] != '.')
749 {
750 return Error::None;
751 }
752
753 if (!Config::enable_precision)
754 {
755 return Error::InvalidSpecifier;
756 }
757
758 ++pos;
759 if (pos < source.size() && source[pos] == '*')
760 {
761 return Error::DynamicField;
762 }
763
764 if (pos < source.size() && IsDigit(source[pos]))
765 {
766 conversion.has_precision = true;
767 return ParseByte(
768 source, pos, static_cast<uint8_t>(std::numeric_limits<uint8_t>::max() - 1),
769 conversion.precision);
770 }
771
772 conversion.has_precision = true;
773 conversion.precision = 0;
774 return Error::None;
775}
776
777consteval void ParseLength(std::string_view source, size_t& pos, Conversion& conversion)
778{
779 if (pos >= source.size())
780 {
781 return;
782 }
783
784 char token = source[pos];
785 if (token == 'h' || token == 'l')
786 {
787 ++pos;
788 conversion.length = (token == 'h') ? Length::Short : Length::Long;
789 if (pos < source.size() && source[pos] == token)
790 {
791 conversion.length = (token == 'h') ? Length::Char : Length::LongLong;
792 ++pos;
793 }
794 return;
795 }
796
797 switch (token)
798 {
799 case 'j':
800 conversion.length = Length::IntMax;
801 ++pos;
802 return;
803 case 'z':
804 conversion.length = Length::Size;
805 ++pos;
806 return;
807 case 't':
808 conversion.length = Length::PtrDiff;
809 ++pos;
810 return;
811 case 'L':
812 conversion.length = Length::LongDouble;
813 ++pos;
814 return;
815 default:
816 return;
817 }
818}
819
820[[nodiscard]] consteval Error ParseSpecifier(std::string_view source, size_t& pos,
821 Conversion& conversion)
822{
823 if (pos >= source.size())
824 {
825 return Error::UnexpectedEnd;
826 }
827
828 auto descriptor = Lowering::LookupSpecifier(source[pos]);
829 if (descriptor.type == ValueKind::None)
830 {
831 return Error::InvalidSpecifier;
832 }
833 if (!Lowering::LengthAllowed(descriptor.length_policy, conversion.length))
834 {
835 return Error::InvalidLength;
836 }
837 if (!Lowering::TypeEnabled(descriptor.gate, conversion.length))
838 {
839 return Error::InvalidSpecifier;
840 }
841
842 conversion.type = descriptor.type;
843 conversion.upper_case = conversion.upper_case || descriptor.upper_case;
844 ++pos;
845 return Error::None;
846}
847
848[[nodiscard]] consteval Error Parse(std::string_view source, size_t& pos,
849 IndexingState& indexing, Conversion& conversion)
850{
851 ++pos;
852 if (pos >= source.size())
853 {
854 return Error::UnexpectedEnd;
855 }
856
857 auto error = ParseArgumentIndex(source, pos, indexing, conversion);
858 if (error != Error::None)
859 {
860 return error;
861 }
862
863 error = ParseFlags(source, pos, conversion);
864 if (error != Error::None)
865 {
866 return error;
867 }
868
869 error = ParseWidth(source, pos, conversion);
870 if (error != Error::None)
871 {
872 return error;
873 }
874
875 error = ParsePrecision(source, pos, conversion);
876 if (error != Error::None)
877 {
878 return error;
879 }
880
881 ParseLength(source, pos, conversion);
882 error = ParseSpecifier(source, pos, conversion);
883 if (error != Error::None)
884 {
885 return error;
886 }
887
888 error = Lowering::ValidateConversion(conversion);
889 if (error != Error::None)
890 {
891 return error;
892 }
893
894 if (!conversion.positional)
895 {
896 if (indexing.uses_positional)
897 {
898 return Error::MixedIndexing;
899 }
900
901 indexing.uses_sequential = true;
902 conversion.arg_index = indexing.next_index++;
903 }
904
905 return Error::None;
906}
907
908[[nodiscard]] consteval Error WalkSource(std::string_view source, auto& visitor)
909{
910 if (HasEmbeddedNul(source))
911 {
912 return Error::EmbeddedNul;
913 }
914
915 size_t pos = 0;
916 size_t text_begin = 0;
917 IndexingState indexing{};
918
919 while (pos < source.size())
920 {
921 if (source[pos] != '%')
922 {
923 ++pos;
924 continue;
925 }
926
927 auto error = visitor.Text(text_begin, pos - text_begin);
928 if (error != Error::None)
929 {
930 return error;
931 }
932
933 if (pos + 1 < source.size() && source[pos + 1] == '%')
934 {
935 error = visitor.Text(pos, 1);
936 if (error != Error::None)
937 {
938 return error;
939 }
940
941 pos += 2;
942 text_begin = pos;
943 continue;
944 }
945
946 auto parse_pos = pos;
947 Conversion conversion{};
948 error = Parse(source, parse_pos, indexing, conversion);
949 if (error != Error::None)
950 {
951 return error;
952 }
953
954 error = visitor.Field(conversion);
955 if (error != Error::None)
956 {
957 return error;
958 }
959
960 pos = parse_pos;
961 text_begin = pos;
962 }
963
964 return visitor.Text(text_begin, source.size() - text_begin);
965}
966
967template <Text Source>
968[[nodiscard]] consteval auto Analyze()
969{
970 constexpr auto scratch = []() consteval {
971 SourceAnalysisScratch<Source.Size()> visitor{};
972 visitor.error =
973 WalkSource(std::string_view(Source.Data(), Source.Size()), visitor);
974 return visitor;
975 }();
976
977 SourceAnalysis<scratch.field_count, scratch.argument_count> result{};
978 result.error = scratch.error;
979 for (size_t i = 0; i < scratch.field_count; ++i)
980 {
981 result.order[i] = scratch.order[i];
982 }
983
984 if constexpr (scratch.error != Error::None)
985 {
986 return result;
987 }
988 else
989 {
990 struct AnalysisVisitor
991 {
992 SourceAnalysis<scratch.field_count, scratch.argument_count>& result;
993
994 [[nodiscard]] consteval Error Text(size_t, size_t) const { return Error::None; }
995
996 [[nodiscard]] consteval Error Field(const Conversion& conversion)
997 {
998 auto field = Lowering::LowerField(conversion);
999 auto info = FormatArgumentInfo{
1000 .pack = field.pack,
1001 .rule = field.rule,
1002 };
1003 auto& slot = result.args[conversion.arg_index];
1004 if (slot.rule != FormatArgumentRule::None &&
1005 (slot.rule != info.rule || slot.pack != info.pack))
1006 {
1007 return Error::ConflictingArgument;
1008 }
1009
1010 slot = info;
1011 return Error::None;
1012 }
1013 };
1014
1015 AnalysisVisitor visitor{result};
1016 result.error = WalkSource(std::string_view(Source.Data(), Source.Size()), visitor);
1017 return result;
1018 }
1019}
1020} // namespace SourceSyntax
1021
1022namespace Lowering = SourceSyntax::Lowering;
1023
1028template <Text Source>
1029[[nodiscard]] consteval auto Analyze()
1030{
1031 return SourceSyntax::Analyze<Source>();
1032}
1033
1039template <Text Source>
1040[[nodiscard]] consteval Error WalkAndLower(auto& visitor)
1041{
1042 struct LoweringVisitor
1043 {
1044 decltype(visitor)& inner;
1045
1046 [[nodiscard]] consteval Error Text(size_t offset, size_t text_size)
1047 {
1048 return inner.Text(offset, text_size);
1049 }
1050
1051 [[nodiscard]] consteval Error Field(const Conversion& conversion)
1052 {
1053 return inner.Field(Lowering::LowerField(conversion));
1054 }
1055 };
1056
1057 LoweringVisitor lowering{visitor};
1058 return SourceSyntax::WalkSource(std::string_view(Source.Data(), Source.Size()),
1059 lowering);
1060}
1061} // namespace LibXR::Print::Detail::PrintfCompile
1062
1063namespace LibXR::Print
1064{
1069template <Text Source>
1071{
1072 public:
1073 using ErrorType = Printf::Error;
1074
1076 [[nodiscard]] static constexpr const char* SourceData() { return Source.Data(); }
1078 [[nodiscard]] static constexpr size_t SourceSize() { return Source.Size(); }
1079
1081 [[nodiscard]] static consteval ErrorType Walk(auto& visitor)
1082 {
1083 return Detail::PrintfCompile::WalkAndLower<Source>(visitor);
1084 }
1085};
1086} // namespace LibXR::Print
Compile-time printf frontend that parses and lowers one source string.
static consteval ErrorType Walk(auto &visitor)
Walks normalized text spans and value fields in source order. / 按源串顺序遍历归一化后的文本片段和字段
static constexpr size_t SourceSize()
Returns the source length without the terminating zero byte. / 返回去掉结尾零字节后的源字符串长度
static constexpr const char * SourceData()
Returns the underlying source bytes without the terminating zero byte. / 返回去掉结尾零字节后的源字符串数据
Length
Supported printf length modifiers after normalization.
Definition printf.hpp:51
Error
Compile-time parse/build failure categories surfaced through static_assert.
Definition printf.hpp:68
constexpr bool UsesDoubleFloatStorage()
Returns whether ordinary printf float conversions lower to double storage. / 判断普通 printf 浮点转换是否降为 dou...
consteval SpecifierDescriptor LookupSpecifier(char token)
Looks up one source conversion character in the descriptor table. / 在描述表中查找一个源级转换字符
consteval FormatArgumentRule LowerRule(const Conversion &conversion)
Lowers one parsed conversion to the compile-time argument matching rule it consumes....
consteval FormatPackKind LowerPackKind(FormatType type)
Maps one semantic runtime type to its packed runtime storage kind. / 将一个语义运行期类型映射到其打包存储类别
constexpr bool TypeEnabled(FeatureGate gate, Length length)
Returns whether one specifier family is enabled under the current feature gates. / 判断某个说明符家族在当前功能开关下是...
consteval FormatField LowerField(const Conversion &conversion)
Lowers one parsed printf conversion into the shared FormatField triple of type, pack,...
constexpr bool LengthAllowed(LengthPolicy policy, Length length)
Returns whether one parsed length modifier is legal for the selected specifier family....
constexpr bool IsUnsignedType(ValueKind type)
Returns whether one frontend semantic family should use unsigned storage/rules. / 判断某个前端语义族是否应使用无符号存储...
constexpr FormatArgumentRule RuleFromTable(const std::array< FormatArgumentRule, length_rule_count > &rules, Length length)
Returns the argument rule selected by one normalized length index. / 返回某个归一化长度索引选中的参数匹配规则
consteval Error ValidateConversion(const Conversion &conversion)
Validates target-dependent lowering constraints after parsing. / 在解析后校验与目标相关的降级约束
consteval bool RuleUses64BitStorage(FormatArgumentRule rule)
Returns whether one argument rule requires 64-bit packed storage on this target. / 判断某个参数规则在当前目标上是否需要...
consteval FormatType LowerType(const Conversion &conversion, FormatArgumentRule rule)
Lowers one parsed printf conversion to its semantic runtime type.
One parsed printf conversion before lowering into the shared format.
bool force_sign
parsed + flag / 已解析的 + 标志
uint8_t precision
parsed precision value / 已解析的精度值
bool alternate
parsed # flag / 已解析的 # 标志
constexpr uint8_t PrecisionByte() const
Returns the shared precision byte, or 0xFF when precision is absent. / 返回共享精度字节;若未显式指定精度则为 0xFF.
size_t arg_index
source argument index consumed by this field / 当前字段消耗的源参数索引
ValueKind type
semantic conversion category / 转换项归一化后的语义类别
bool left_align
parsed - flag / 已解析的 - 标志
uint8_t width
parsed field width / 已解析的字段宽度
Length length
parsed length modifier / 已解析的长度修饰符
bool zero_pad
parsed 0 flag / 已解析的 0 标志
bool has_precision
whether precision was explicitly provided / 是否显式提供了精度
constexpr uint8_t FlagsByte() const
Packs the parsed frontend flags into the shared FormatFlag byte. / 将前端解析出的标志位打包成共享的 FormatFlag 字节
bool space_sign
parsed space-sign flag / 已解析的空格正号标志
bool upper_case
implied uppercase output / 隐含的大写输出标志
bool positional
whether arg_index came from n$ syntax / arg_index 是否来自 n$ 语法
bool uses_sequential
at least one conversion used implicit sequential order / 至少有一个转换使用了隐式顺序参数
size_t next_index
next sequential argument index / 下一个顺序参数索引
bool uses_positional
at least one conversion used n$ syntax / 至少有一个转换使用了 n$ 语法
Final source-level positional/sequential argument summary.
Error error
first parse/analysis error / 首个解析或分析错误
std::array< size_t, FieldCount > order
field-ordered source argument indexes / 按字段顺序排列的源参数索引
std::array< FormatArgumentInfo, ArgCount > args
source-ordered argument metadata / 按源参数顺序排列的参数元信息
Source-only scratch summary for positional argument analysis.
size_t argument_count
highest referenced argument count / 最高引用到的参数个数
Error error
first parse/analysis error / 首个解析或分析错误
std::array< size_t, MaxFieldCount > order
field-ordered source argument indexes / 按字段顺序排列的源参数索引
size_t field_count
parsed conversion count / 已解析的转换项数量
FeatureGate gate
feature gate controlling this specifier / 控制该说明符的功能开关
ValueKind type
normalized semantic category / 归一化后的语义类别
LengthPolicy length_policy
accepted length family / 允许的长度修饰类别
bool upper_case
whether the specifier implies uppercase output / 说明符是否隐含大写输出
char token
source conversion character / 源格式转换字符
Normalized value-field semantics shared by all formatting frontends.
Structural literal wrapper used as the NTTP source for printf formats.
Definition printf.hpp:17