1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
#ifndef FORMAT_ANALYSER_INL_H_
#error "Direct inclusion of this file is not allowed, include format_analyser.h"
// For the sake of sane code completion.
#include "format_analyser.h"
#endif
namespace NYT {
////////////////////////////////////////////////////////////////////////////////
namespace NDetail {
consteval bool Contains(std::string_view sv, char symbol)
{
return sv.find(symbol) != std::string_view::npos;
}
template <class... TArgs>
consteval void TFormatAnalyser::ValidateFormat(std::string_view fmt)
{
std::array<std::string_view, sizeof...(TArgs)> markers = {};
std::array<TSpecifiers, sizeof...(TArgs)> specifiers{GetSpecifiers<TArgs>()...};
int markerCount = 0;
int currentMarkerStart = -1;
for (int idx = 0; idx < std::ssize(fmt); ++idx) {
auto symbol = fmt[idx];
// Parse verbatim text.
if (currentMarkerStart == -1) {
if (symbol == IntroductorySymbol) {
// Marker maybe begins.
currentMarkerStart = idx;
}
continue;
}
// NB: We check for %% first since
// in order to verify if symbol is a specifier
// we need markerCount to be within range of our
// specifier array.
if (symbol == IntroductorySymbol) {
if (currentMarkerStart + 1 != idx) {
// '%a% detected'
CrashCompilerWrongTermination("You may not terminate flag sequence other than %% with \'%\' symbol");
return;
}
// '%%' detected --- skip
currentMarkerStart = -1;
continue;
}
// We are inside of marker.
if (markerCount == std::ssize(markers)) {
// To many markers
CrashCompilerNotEnoughArguments("Number of arguments supplied to format is smaller than the number of flag sequences");
return;
}
if (Contains(specifiers[markerCount].Conversion, symbol)) {
// Marker has finished.
markers[markerCount]
= std::string_view(fmt.begin() + currentMarkerStart, idx - currentMarkerStart + 1);
currentMarkerStart = -1;
++markerCount;
continue;
}
if (!Contains(specifiers[markerCount].Flags, symbol)) {
CrashCompilerWrongFlagSpecifier("Symbol is not a valid flag specifier; See FlagSpecifiers");
}
}
if (currentMarkerStart != -1) {
// Runaway marker.
CrashCompilerMissingTermination("Unterminated flag sequence detected; Use \'%%\' to type plain %");
return;
}
if (markerCount < std::ssize(markers)) {
// Missing markers.
CrashCompilerTooManyArguments("Number of arguments supplied to format is greater than the number of flag sequences");
return;
}
// TODO(arkady-e1ppa): Consider per-type verification
// of markers.
}
template <class TArg>
consteval auto TFormatAnalyser::GetSpecifiers()
{
static_assert(CFormattable<TArg>, "Your specialization of TFormatArg is broken");
return TSpecifiers{
.Conversion = std::string_view{
std::data(TFormatArg<TArg>::ConversionSpecifiers),
std::size(TFormatArg<TArg>::ConversionSpecifiers)},
.Flags = std::string_view{
std::data(TFormatArg<TArg>::FlagSpecifiers),
std::size(TFormatArg<TArg>::FlagSpecifiers)},
};
}
} // namespace NDetail
////////////////////////////////////////////////////////////////////////////////
template <bool Hot, size_t N, std::array<char, N> List, class TFrom>
consteval auto TFormatArgBase::ExtendConversion()
{
static_assert(CFormattable<TFrom>);
return AppendArrays<Hot, N, List, &TFrom::ConversionSpecifiers>();
}
template <bool Hot, size_t N, std::array<char, N> List, class TFrom>
consteval auto TFormatArgBase::ExtendFlags()
{
static_assert(CFormattable<TFrom>);
return AppendArrays<Hot, N, List, &TFrom::FlagSpecifiers>();
}
template <bool Hot, size_t N, std::array<char, N> List, auto* From>
consteval auto TFormatArgBase::AppendArrays()
{
auto& from = *From;
return [] <size_t... I, size_t... J> (
std::index_sequence<I...>,
std::index_sequence<J...>) {
if constexpr (Hot) {
return std::array{List[J]..., from[I]...};
} else {
return std::array{from[I]..., List[J]...};
}
} (
std::make_index_sequence<std::size(from)>(),
std::make_index_sequence<N>());
}
} // namespace NYT::NDetail
|