libxr  1.0
Want to be the best embedded framework
Loading...
Searching...
No Matches
format_frontend_detail.hpp
1#pragma once
2
3#include <array>
4#include <cstddef>
5#include <cstdint>
6#include <limits>
7#include <string_view>
8#include <type_traits>
9
10#include "format_compile.hpp"
11#include "format_argument.hpp"
12#include "printf.hpp"
13
14namespace LibXR::Print::Detail::FormatFrontend
15{
20enum class Error : uint8_t
21{
22 None,
23 NumberOverflow,
24 UnexpectedEnd,
25 EmbeddedNul,
26 UnmatchedBrace,
27 MixedIndexing,
28 ManualIndexingDisabled,
29 DynamicField,
30 InvalidArgumentIndex,
31 InvalidSpecifier,
32 InvalidPresentation,
33 MissingArgument,
34 ArgumentTypeMismatch,
35 UnsupportedArgumentType,
36 TextOffsetOverflow,
37 TextSizeOverflow,
38};
39
44enum class Align : uint8_t
45{
46 None,
47 Left,
48 Right,
49 Center,
50};
51
57{
58 size_t arg_index = 0;
59 Align align = Align::None;
60 char fill = ' ';
61 bool force_sign = false;
62 bool space_sign = false;
63 bool alternate = false;
64 bool zero_pad = false;
65 uint8_t width = 0;
66 bool has_precision = false;
67 uint8_t precision = 0;
68 char presentation = 0;
69};
70
75enum class BoundKind : uint8_t
76{
77 Unsupported,
78 Bool,
79 Character,
80 Signed,
81 Unsigned,
82 String,
83 Pointer,
84 Float32,
85 Float64,
86 LongDouble,
87};
88
94{
95 BoundKind kind = BoundKind::Unsupported;
96 bool uses_64bit_storage = false;
97};
98
104{
105 Error error = Error::None;
107};
108
109namespace SourceSyntax
110{
115template <size_t FieldCount>
117{
118 std::array<size_t, FieldCount> argument_order{};
120 Error error = Error::None;
121};
122
127template <size_t MaxFieldCount>
129{
130 std::array<size_t, MaxFieldCount> order{};
131 size_t field_count = 0;
133 Error error = Error::None;
134
135 [[nodiscard]] consteval Error Text(size_t, size_t) const { return Error::None; }
136
137 [[nodiscard]] consteval Error Field(const ParsedField& field)
138 {
139 order[field_count++] = field.arg_index;
140 size_t used_argument_count = field.arg_index + 1;
141 if (used_argument_count > required_argument_count)
142 {
143 required_argument_count = used_argument_count;
144 }
145 return Error::None;
146 }
147};
148
153[[nodiscard]] constexpr bool IsDigit(char ch)
154{
155 return ch >= '0' && ch <= '9';
156}
157
158[[nodiscard]] constexpr bool IsAlign(char ch)
159{
160 return ch == '<' || ch == '>' || ch == '^';
161}
162
163[[nodiscard]] constexpr Align ParseAlign(char ch)
164{
165 switch (ch)
166 {
167 case '<':
168 return Align::Left;
169 case '>':
170 return Align::Right;
171 case '^':
172 return Align::Center;
173 default:
174 return Align::None;
175 }
176}
177
178[[nodiscard]] constexpr bool HasEmbeddedNul(std::string_view source)
179{
180 for (char ch : source)
181 {
182 if (ch == '\0')
183 {
184 return true;
185 }
186 }
187 return false;
188}
189
190[[nodiscard]] constexpr bool IsSupportedPresentation(char ch)
191{
192 switch (ch)
193 {
194 case 'd':
195 case 'b':
196 case 'B':
197 case 'o':
198 case 'x':
199 case 'X':
200 case 'c':
201 case 's':
202 case 'p':
203 case 'f':
204 case 'F':
205 case 'e':
206 case 'E':
207 case 'g':
208 case 'G':
209 return true;
210 default:
211 return false;
212 }
213}
214
215template <typename UInt>
216[[nodiscard]] consteval Error ParseUnsigned(std::string_view source, size_t& pos,
217 UInt limit, UInt& value)
218{
219 static_assert(std::is_unsigned_v<UInt>);
220
221 value = 0;
222 if (pos >= source.size() || !IsDigit(source[pos]))
223 {
224 return Error::InvalidSpecifier;
225 }
226
227 while (pos < source.size() && IsDigit(source[pos]))
228 {
229 auto digit = static_cast<UInt>(source[pos] - '0');
230 if (value > static_cast<UInt>((limit - digit) / 10))
231 {
232 return Error::NumberOverflow;
233 }
234 value = static_cast<UInt>(value * 10 + digit);
235 ++pos;
236 }
237
238 return Error::None;
239}
240
246{
247 bool uses_manual = false;
248 bool uses_auto = false;
249 size_t next_auto_index = 0;
250};
251
252[[nodiscard]] consteval Error ParseFieldHead(std::string_view source, size_t& pos,
253 IndexingState& indexing,
254 ParsedField& field)
255{
256 if (pos >= source.size())
257 {
258 return Error::UnexpectedEnd;
259 }
260
261 if (source[pos] == ':' || source[pos] == '}')
262 {
263 if (indexing.uses_manual)
264 {
265 return Error::MixedIndexing;
266 }
267
268 indexing.uses_auto = true;
269 field.arg_index = indexing.next_auto_index++;
270 return Error::None;
271 }
272
273 if (!IsDigit(source[pos]))
274 {
275 return Error::InvalidArgumentIndex;
276 }
277
278 size_t index = 0;
279 auto error =
280 ParseUnsigned(source, pos, std::numeric_limits<size_t>::max(), index);
281 if (error != Error::None)
282 {
283 return error;
284 }
285
286 if (indexing.uses_auto)
287 {
288 return Error::MixedIndexing;
289 }
290
291 if (!Config::enable_explicit_argument_indexing)
292 {
293 return Error::ManualIndexingDisabled;
294 }
295
296 indexing.uses_manual = true;
297 field.arg_index = index;
298 return Error::None;
299}
300
301[[nodiscard]] consteval Error ParseFormatSpec(std::string_view source, size_t& pos,
302 ParsedField& field)
303{
304 if (pos >= source.size())
305 {
306 return Error::UnexpectedEnd;
307 }
308
309 if (pos + 1 < source.size() && IsAlign(source[pos + 1]))
310 {
311 if (source[pos] == '{' || source[pos] == '}')
312 {
313 return Error::InvalidSpecifier;
314 }
315 field.fill = source[pos];
316 field.align = ParseAlign(source[pos + 1]);
317 pos += 2;
318 }
319 else if (IsAlign(source[pos]))
320 {
321 field.align = ParseAlign(source[pos]);
322 ++pos;
323 }
324
325 if (pos < source.size() &&
326 (source[pos] == '+' || source[pos] == '-' || source[pos] == ' '))
327 {
328 field.force_sign = source[pos] == '+';
329 field.space_sign = source[pos] == ' ';
330 ++pos;
331 }
332
333 if (pos < source.size() && source[pos] == '#')
334 {
335 if (!Config::enable_alternate)
336 {
337 return Error::InvalidSpecifier;
338 }
339 field.alternate = true;
340 ++pos;
341 }
342
343 if (pos < source.size() && source[pos] == '0')
344 {
345 field.zero_pad = true;
346 ++pos;
347 }
348
349 if (pos < source.size() && source[pos] == '{')
350 {
351 return Error::DynamicField;
352 }
353
354 if (pos < source.size() && IsDigit(source[pos]))
355 {
356 if (!Config::enable_width)
357 {
358 return Error::InvalidSpecifier;
359 }
360 auto error = ParseUnsigned(source, pos, std::numeric_limits<uint8_t>::max(),
361 field.width);
362 if (error != Error::None)
363 {
364 return error;
365 }
366 }
367
368 if (pos < source.size() && source[pos] == '.')
369 {
370 if (!Config::enable_precision)
371 {
372 return Error::InvalidSpecifier;
373 }
374
375 ++pos;
376 if (pos < source.size() && source[pos] == '{')
377 {
378 return Error::DynamicField;
379 }
380
381 field.has_precision = true;
382 auto error = ParseUnsigned(
383 source, pos, static_cast<uint8_t>(std::numeric_limits<uint8_t>::max() - 1),
384 field.precision);
385 if (error != Error::None)
386 {
387 return error;
388 }
389 }
390
391 if (pos < source.size() && source[pos] != '}')
392 {
393 field.presentation = source[pos++];
394 if (!IsSupportedPresentation(field.presentation))
395 {
396 return Error::InvalidPresentation;
397 }
398 }
399
400 return Error::None;
401}
402
403[[nodiscard]] consteval Error ParseField(std::string_view source, size_t& pos,
404 IndexingState& indexing,
405 ParsedField& field)
406{
407 ++pos;
408 if (pos >= source.size())
409 {
410 return Error::UnexpectedEnd;
411 }
412
413 auto error = ParseFieldHead(source, pos, indexing, field);
414 if (error != Error::None)
415 {
416 return error;
417 }
418
419 if (pos < source.size() && source[pos] != ':' && source[pos] != '}')
420 {
421 return Error::InvalidArgumentIndex;
422 }
423
424 if (pos < source.size() && source[pos] == ':')
425 {
426 ++pos;
427 error = ParseFormatSpec(source, pos, field);
428 if (error != Error::None)
429 {
430 return error;
431 }
432 }
433
434 if (pos >= source.size())
435 {
436 return Error::UnexpectedEnd;
437 }
438 if (source[pos] != '}')
439 {
440 return Error::InvalidSpecifier;
441 }
442
443 ++pos;
444 return Error::None;
445}
446
451[[nodiscard]] consteval Error WalkSource(std::string_view source, auto& visitor)
452{
453 if (HasEmbeddedNul(source))
454 {
455 return Error::EmbeddedNul;
456 }
457
458 size_t pos = 0;
459 size_t text_begin = 0;
460 IndexingState indexing{};
461
462 while (pos < source.size())
463 {
464 if (source[pos] == '{')
465 {
466 if (pos + 1 < source.size() && source[pos + 1] == '{')
467 {
468 auto error = visitor.Text(text_begin, pos - text_begin);
469 if (error != Error::None)
470 {
471 return error;
472 }
473 error = visitor.Text(pos, 1);
474 if (error != Error::None)
475 {
476 return error;
477 }
478 pos += 2;
479 text_begin = pos;
480 continue;
481 }
482
483 auto error = visitor.Text(text_begin, pos - text_begin);
484 if (error != Error::None)
485 {
486 return error;
487 }
488
489 ParsedField field{};
490 error = ParseField(source, pos, indexing, field);
491 if (error != Error::None)
492 {
493 return error;
494 }
495
496 error = visitor.Field(field);
497 if (error != Error::None)
498 {
499 return error;
500 }
501
502 text_begin = pos;
503 continue;
504 }
505
506 if (source[pos] == '}')
507 {
508 if (pos + 1 < source.size() && source[pos + 1] == '}')
509 {
510 auto error = visitor.Text(text_begin, pos - text_begin);
511 if (error != Error::None)
512 {
513 return error;
514 }
515 error = visitor.Text(pos, 1);
516 if (error != Error::None)
517 {
518 return error;
519 }
520 pos += 2;
521 text_begin = pos;
522 continue;
523 }
524
525 return Error::UnmatchedBrace;
526 }
527
528 ++pos;
529 }
530
531 return visitor.Text(text_begin, source.size() - text_begin);
532}
533
534template <Text Source>
535[[nodiscard]] consteval auto Analyze()
536{
537 constexpr auto scratch = []() consteval {
538 SourceAnalysisScratch<Source.Size()> visitor{};
539 visitor.error = WalkSource(std::string_view(Source.Data(), Source.Size()), visitor);
540 return visitor;
541 }();
542
543 SourceAnalysis<scratch.field_count> result{};
544 result.required_argument_count = scratch.required_argument_count;
545 result.error = scratch.error;
546 for (size_t i = 0; i < scratch.field_count; ++i)
547 {
548 result.argument_order[i] = scratch.order[i];
549 }
550 return result;
551}
552} // namespace SourceSyntax
553
558namespace ArgumentBinding
559{
560
561template <typename Arg>
562[[nodiscard]] consteval BoundArgument DescribeArg()
563{
564 using Traits = FormatArgument::TypeTraits<Arg>;
565 using Decayed = typename Traits::Decayed;
566 using Normalized = typename Traits::Normalized;
567
568 if constexpr (std::is_same_v<Decayed, bool>)
569 {
570 return BoundArgument{.kind = BoundKind::Bool};
571 }
572 else if constexpr (std::is_same_v<Decayed, char>)
573 {
574 return BoundArgument{.kind = BoundKind::Character};
575 }
576 else if constexpr (Traits::is_string_like)
577 {
578 return BoundArgument{.kind = BoundKind::String};
579 }
580 else if constexpr (Traits::is_pointer_like)
581 {
582 return BoundArgument{.kind = BoundKind::Pointer};
583 }
584 else if constexpr (Traits::is_signed_integer)
585 {
586 return BoundArgument{
587 .kind = BoundKind::Signed,
588 .uses_64bit_storage = sizeof(Normalized) > sizeof(int32_t),
589 };
590 }
591 else if constexpr (Traits::is_unsigned_integer)
592 {
593 return BoundArgument{
594 .kind = BoundKind::Unsigned,
595 .uses_64bit_storage = sizeof(Normalized) > sizeof(uint32_t),
596 };
597 }
598 else if constexpr (std::is_same_v<Decayed, float>)
599 {
600 return BoundArgument{.kind = BoundKind::Float32};
601 }
602 else if constexpr (std::is_same_v<Decayed, double>)
603 {
604 return BoundArgument{.kind = BoundKind::Float64};
605 }
606 else if constexpr (std::is_same_v<Decayed, long double>)
607 {
608 return BoundArgument{.kind = BoundKind::LongDouble};
609 }
610 else
611 {
612 return BoundArgument{};
613 }
614}
615
616template <typename... Args>
617[[nodiscard]] consteval auto DescribeArgs()
618{
619 return std::array<BoundArgument, sizeof...(Args)>{DescribeArg<Args>()...};
620}
621
622[[nodiscard]] constexpr bool HasSignOption(const ParsedField& field)
623{
624 return field.force_sign || field.space_sign;
625}
626
627[[nodiscard]] constexpr uint8_t UnspecifiedPrecision()
628{
629 return std::numeric_limits<uint8_t>::max();
630}
631
632[[nodiscard]] constexpr uint8_t BuildFlags(const ParsedField& parsed, bool upper_case)
633{
634 uint8_t flags = 0;
635 if (parsed.align == Align::Left)
636 {
637 flags |= static_cast<uint8_t>(FormatFlag::LeftAlign);
638 }
639 if (parsed.align == Align::Center)
640 {
641 flags |= static_cast<uint8_t>(FormatFlag::CenterAlign);
642 }
643 if (parsed.force_sign)
644 {
645 flags |= static_cast<uint8_t>(FormatFlag::ForceSign);
646 }
647 if (parsed.space_sign)
648 {
649 flags |= static_cast<uint8_t>(FormatFlag::SpaceSign);
650 }
651 if (parsed.alternate)
652 {
653 flags |= static_cast<uint8_t>(FormatFlag::Alternate);
654 }
655 if (parsed.zero_pad && parsed.align == Align::None)
656 {
657 flags |= static_cast<uint8_t>(FormatFlag::ZeroPad);
658 }
659 if (upper_case)
660 {
661 flags |= static_cast<uint8_t>(FormatFlag::UpperCase);
662 }
663 return flags;
664}
665
666[[nodiscard]] constexpr FormatField MakeField(const ParsedField& parsed, FormatType type,
667 FormatPackKind pack, bool upper_case = false)
668{
669 return FormatField{
670 .type = type,
671 .pack = pack,
672 .rule = FormatArgumentRule::None,
673 .flags = BuildFlags(parsed, upper_case),
674 .fill = parsed.fill,
675 .width = parsed.width,
676 .precision = parsed.has_precision ? parsed.precision : UnspecifiedPrecision(),
677 };
678}
679
680[[nodiscard]] constexpr bool IsDefaultOr(char presentation, char expected)
681{
682 return presentation == 0 || presentation == expected;
683}
684
685[[nodiscard]] constexpr bool IsNonDecimalPresentation(char presentation)
686{
687 return presentation == 'b' || presentation == 'B' || presentation == 'o' ||
688 presentation == 'x' || presentation == 'X';
689}
690
691[[nodiscard]] constexpr char DefaultFloatPresentation()
692{
693 if (Config::enable_float_general)
694 {
695 return 'g';
696 }
697 if (Config::enable_float_fixed)
698 {
699 return 'f';
700 }
701 if (Config::enable_float_scientific)
702 {
703 return 'e';
704 }
705 return 0;
706}
707
708[[nodiscard]] consteval LoweredField LowerIntegerLike(const ParsedField& parsed,
709 bool signed_decimal,
710 bool uses_64bit_storage)
711{
712 if (!Config::enable_integer)
713 {
714 return LoweredField{.error = Error::ArgumentTypeMismatch};
715 }
716 if (uses_64bit_storage && !Config::enable_integer_64bit)
717 {
718 return LoweredField{.error = Error::ArgumentTypeMismatch};
719 }
720
721 if (parsed.has_precision)
722 {
723 return LoweredField{.error = Error::ArgumentTypeMismatch};
724 }
725
726 if (parsed.presentation == 'c')
727 {
728 if (parsed.alternate || HasSignOption(parsed) || parsed.zero_pad)
729 {
730 return LoweredField{.error = Error::ArgumentTypeMismatch};
731 }
732 return LoweredField{.field = MakeField(parsed, FormatType::Character,
733 FormatPackKind::Character)};
734 }
735
736 if (IsDefaultOr(parsed.presentation, 'd'))
737 {
738 if (parsed.alternate)
739 {
740 return LoweredField{.error = Error::ArgumentTypeMismatch};
741 }
742 if (!signed_decimal && HasSignOption(parsed))
743 {
744 return LoweredField{.error = Error::ArgumentTypeMismatch};
745 }
746
747 if (signed_decimal)
748 {
749 return LoweredField{.field = MakeField(
750 parsed,
751 uses_64bit_storage ? FormatType::Signed64
752 : FormatType::Signed32,
753 uses_64bit_storage ? FormatPackKind::I64
754 : FormatPackKind::I32)};
755 }
756
757 return LoweredField{.field = MakeField(
758 parsed,
759 uses_64bit_storage ? FormatType::Unsigned64
760 : FormatType::Unsigned32,
761 uses_64bit_storage ? FormatPackKind::U64
762 : FormatPackKind::U32)};
763 }
764
765 if (!Config::enable_integer_base8_16 || HasSignOption(parsed))
766 {
767 return LoweredField{.error = Error::ArgumentTypeMismatch};
768 }
769
770 switch (parsed.presentation)
771 {
772 case 'b':
773 return LoweredField{.field = MakeField(
774 parsed,
775 uses_64bit_storage ? FormatType::Binary64
776 : FormatType::Binary32,
777 uses_64bit_storage ? FormatPackKind::U64
778 : FormatPackKind::U32)};
779 case 'B':
780 return LoweredField{.field = MakeField(
781 parsed,
782 uses_64bit_storage ? FormatType::Binary64
783 : FormatType::Binary32,
784 uses_64bit_storage ? FormatPackKind::U64
785 : FormatPackKind::U32,
786 true)};
787 case 'o':
788 return LoweredField{.field = MakeField(
789 parsed,
790 uses_64bit_storage ? FormatType::Octal64
791 : FormatType::Octal32,
792 uses_64bit_storage ? FormatPackKind::U64
793 : FormatPackKind::U32)};
794 case 'x':
795 return LoweredField{.field = MakeField(
796 parsed,
797 uses_64bit_storage ? FormatType::HexLower64
798 : FormatType::HexLower32,
799 uses_64bit_storage ? FormatPackKind::U64
800 : FormatPackKind::U32)};
801 case 'X':
802 return LoweredField{.field = MakeField(
803 parsed,
804 uses_64bit_storage ? FormatType::HexUpper64
805 : FormatType::HexUpper32,
806 uses_64bit_storage ? FormatPackKind::U64
807 : FormatPackKind::U32,
808 true)};
809 default:
810 return LoweredField{.error = Error::ArgumentTypeMismatch};
811 }
812}
813
814[[nodiscard]] consteval LoweredField LowerBool(const ParsedField& parsed)
815{
816 if (parsed.presentation == 0)
817 {
818 return LoweredField{.error = Error::ArgumentTypeMismatch};
819 }
820 return LowerIntegerLike(parsed, false, false);
821}
822
823[[nodiscard]] consteval LoweredField LowerCharacter(const ParsedField& parsed)
824{
825 if (parsed.presentation != 0 && parsed.presentation != 'c')
826 {
827 return LoweredField{.error = Error::ArgumentTypeMismatch};
828 }
829 if (parsed.alternate || HasSignOption(parsed) || parsed.zero_pad || parsed.has_precision)
830 {
831 return LoweredField{.error = Error::ArgumentTypeMismatch};
832 }
833 if (!Config::enable_text)
834 {
835 return LoweredField{.error = Error::ArgumentTypeMismatch};
836 }
837
838 ParsedField adjusted = parsed;
839 if (adjusted.align == Align::None)
840 {
841 adjusted.align = Align::Left;
842 }
843
844 return LoweredField{
845 .field = MakeField(adjusted, FormatType::Character, FormatPackKind::Character)};
846}
847
848[[nodiscard]] consteval LoweredField LowerString(const ParsedField& parsed)
849{
850 if (parsed.presentation != 0 && parsed.presentation != 's')
851 {
852 return LoweredField{.error = Error::ArgumentTypeMismatch};
853 }
854 if (parsed.alternate || HasSignOption(parsed) || parsed.zero_pad)
855 {
856 return LoweredField{.error = Error::ArgumentTypeMismatch};
857 }
858 if (!Config::enable_text)
859 {
860 return LoweredField{.error = Error::ArgumentTypeMismatch};
861 }
862
863 ParsedField adjusted = parsed;
864 if (adjusted.align == Align::None)
865 {
866 adjusted.align = Align::Left;
867 }
868
869 return LoweredField{
870 .field = MakeField(adjusted, FormatType::String, FormatPackKind::StringView)};
871}
872
873[[nodiscard]] consteval LoweredField LowerPointer(const ParsedField& parsed)
874{
875 if (parsed.presentation != 0 && parsed.presentation != 'p')
876 {
877 return LoweredField{.error = Error::ArgumentTypeMismatch};
878 }
879 if (parsed.alternate || HasSignOption(parsed) || parsed.zero_pad || parsed.has_precision)
880 {
881 return LoweredField{.error = Error::ArgumentTypeMismatch};
882 }
883 if (!Config::enable_pointer)
884 {
885 return LoweredField{.error = Error::ArgumentTypeMismatch};
886 }
887
888 return LoweredField{
889 .field = MakeField(parsed, FormatType::Pointer, FormatPackKind::Pointer)};
890}
891
892[[nodiscard]] consteval LoweredField LowerFloat(const ParsedField& parsed,
893 BoundKind kind)
894{
895 char presentation =
896 parsed.presentation == 0 ? DefaultFloatPresentation() : parsed.presentation;
897 if (presentation == 0)
898 {
899 return LoweredField{.error = Error::ArgumentTypeMismatch};
900 }
901 bool upper_case =
902 presentation == 'F' || presentation == 'E' || presentation == 'G';
903
904 FormatType type = FormatType::End;
905 FormatPackKind pack = FormatPackKind::F32;
906
907 auto pick_type = [&](FormatType f32_type, FormatType f64_type,
908 FormatType ld_type) consteval -> bool {
909 switch (kind)
910 {
911 case BoundKind::Float32:
912 type = f32_type;
913 pack = FormatPackKind::F32;
914 return true;
915 case BoundKind::Float64:
916 type = Config::enable_float_double ? f64_type : f32_type;
917 pack = Config::enable_float_double ? FormatPackKind::F64 : FormatPackKind::F32;
918 return true;
919 case BoundKind::LongDouble:
920 if (!Config::enable_float_long_double)
921 {
922 return false;
923 }
924 type = ld_type;
925 pack = FormatPackKind::LongDouble;
926 return true;
927 default:
928 return false;
929 }
930 };
931
932 switch (presentation)
933 {
934 case 'f':
935 case 'F':
936 if (!Config::enable_float_fixed ||
937 !pick_type(FormatType::FloatFixed, FormatType::DoubleFixed,
938 FormatType::LongDoubleFixed))
939 {
940 return LoweredField{.error = Error::ArgumentTypeMismatch};
941 }
942 break;
943 case 'e':
944 case 'E':
945 if (!Config::enable_float_scientific ||
946 !pick_type(FormatType::FloatScientific, FormatType::DoubleScientific,
947 FormatType::LongDoubleScientific))
948 {
949 return LoweredField{.error = Error::ArgumentTypeMismatch};
950 }
951 break;
952 case 'g':
953 case 'G':
954 if (!Config::enable_float_general ||
955 !pick_type(FormatType::FloatGeneral, FormatType::DoubleGeneral,
956 FormatType::LongDoubleGeneral))
957 {
958 return LoweredField{.error = Error::ArgumentTypeMismatch};
959 }
960 break;
961 default:
962 return LoweredField{.error = Error::ArgumentTypeMismatch};
963 }
964
965 return LoweredField{.field = MakeField(parsed, type, pack, upper_case)};
966}
967
968template <typename... Args>
969[[nodiscard]] consteval LoweredField LowerField(const ParsedField& parsed)
970{
971 constexpr auto bound_arguments = DescribeArgs<Args...>();
972 if (parsed.arg_index >= bound_arguments.size())
973 {
974 return LoweredField{.error = Error::MissingArgument};
975 }
976
977 auto argument = bound_arguments[parsed.arg_index];
978 switch (argument.kind)
979 {
980 case BoundKind::Bool:
981 return LowerBool(parsed);
982 case BoundKind::Character:
983 return LowerCharacter(parsed);
984 case BoundKind::Signed:
985 return LowerIntegerLike(parsed, true, argument.uses_64bit_storage);
986 case BoundKind::Unsigned:
987 return LowerIntegerLike(parsed, false, argument.uses_64bit_storage);
988 case BoundKind::String:
989 return LowerString(parsed);
990 case BoundKind::Pointer:
991 return LowerPointer(parsed);
992 case BoundKind::Float32:
993 case BoundKind::Float64:
994 case BoundKind::LongDouble:
995 return LowerFloat(parsed, argument.kind);
996 case BoundKind::Unsupported:
997 default:
998 return LoweredField{.error = Error::UnsupportedArgumentType};
999 }
1000}
1001} // namespace ArgumentBinding
1002
1007template <Text Source>
1008[[nodiscard]] consteval auto Analyze()
1009{
1010 return SourceSyntax::Analyze<Source>();
1011}
1012
1018template <Text Source, typename... Args>
1019[[nodiscard]] consteval Error WalkAndLower(auto& visitor)
1020{
1021 struct LoweringVisitor
1022 {
1023 decltype(visitor)& inner;
1024
1025 [[nodiscard]] consteval Error Text(size_t offset, size_t text_size)
1026 {
1027 return inner.Text(offset, text_size);
1028 }
1029
1030 [[nodiscard]] consteval Error Field(const ParsedField& parsed)
1031 {
1032 auto lowered = ArgumentBinding::LowerField<Args...>(parsed);
1033 if (lowered.error != Error::None)
1034 {
1035 return lowered.error;
1036 }
1037 return inner.Field(lowered.field);
1038 }
1039 };
1040
1041 LoweringVisitor lowering{visitor};
1042 return SourceSyntax::WalkSource(std::string_view(Source.Data(), Source.Size()),
1043 lowering);
1044}
1045
1051template <Text Source, typename... Args>
1053{
1054 public:
1055 using ErrorType = Error;
1056
1057 [[nodiscard]] static constexpr const char* SourceData() { return Source.Data(); }
1058 [[nodiscard]] static constexpr size_t SourceSize() { return Source.Size(); }
1059
1060 [[nodiscard]] static consteval ErrorType Walk(auto& visitor)
1061 {
1062 return WalkAndLower<Source, Args...>(visitor);
1063 }
1064};
1065} // namespace LibXR::Print::Detail::FormatFrontend
Frontend adapter that binds one brace-style source literal to one concrete C++ argument list.
Internal C++ argument-type utilities shared by compile-time matching and runtime packing.
One normalized C++ argument together with its storage-width decision.
bool uses_64bit_storage
whether integer storage must be 64-bit / 整数是否必须走 64 位存储
BoundKind kind
normalized argument category / 归一化后的参数类别
Result of lowering one parsed brace field into the shared format protocol.
FormatField field
lowered shared field / 降为共享协议后的字段
Parsed brace field before binding it to one concrete C++ argument type.
uint8_t precision
parsed precision when present / 显式给出时的精度值
size_t arg_index
selected source argument index / 选中的源参数索引
bool force_sign
parsed plus-sign option / 已解析正号选项
char fill
parsed fill character / 已解析填充字符
Align align
parsed alignment / 已解析对齐方式
bool space_sign
parsed space-sign option / 已解析空格符号选项
uint8_t width
parsed constant width / 已解析常量宽度
bool zero_pad
parsed zero-pad option / 已解析零填充选项
char presentation
parsed presentation character, or 0 for default / 展示类型字符;缺省时为 0
bool has_precision
whether precision was explicitly present / 是否显式给出了精度
bool alternate
parsed alternate-form option / 已解析备用格式选项
Source-level indexing mode for automatic versus manual brace fields.
size_t next_auto_index
next automatic argument index / 下一个自动参数索引
bool uses_auto
automatic field numbering is in use / 正在使用自动编号
bool uses_manual
manual field numbering is in use / 正在使用手动编号
Source-only analysis data for one brace-style format literal.
std::array< size_t, FieldCount > argument_order
source-ordered argument references / 按源串顺序引用的参数索引
size_t required_argument_count
minimum call-site argument count / 调用点至少需要的参数个数
Error error
first source-only parse error / 首个仅源串解析错误
Conservative temporary accumulator used during source-only analysis.
std::array< size_t, MaxFieldCount > order
conservative field-order scratch buffer / 按字段顺序记录参数索引的临时缓冲区
size_t field_count
parsed replacement-field count / 已解析的替换字段数量
size_t required_argument_count
minimum call-site argument count / 调用点至少需要的参数个数
Normalized value-field semantics shared by all formatting frontends.
Structural literal wrapper used as the NTTP source for printf formats.
Definition printf.hpp:17
constexpr Text(const char(&text)[N])
Copies the string literal into the structural NTTP object.
Definition printf.hpp:25
constexpr const char * Data() const
Returns the literal bytes including the terminating zero byte. / 返回含结尾零字节的字面量指针
Definition printf.hpp:36
constexpr size_t Size() const
Returns the format length without the terminating zero byte. / 返回不含结尾零字节的格式串长度
Definition printf.hpp:34