libxr  1.0
Want to be the best embedded framework
Loading...
Searching...
No Matches
format_frontend_parser.hpp
1#pragma once
2
14[[nodiscard]] constexpr bool IsDigit(char ch) { return ch >= '0' && ch <= '9'; }
15
23[[nodiscard]] constexpr bool IsAlign(char ch)
24{
25 return ch == '<' || ch == '>' || ch == '^';
26}
27
35[[nodiscard]] constexpr Align ParseAlign(char ch)
36{
37 switch (ch)
38 {
39 case '<':
40 return Align::Left;
41 case '>':
42 return Align::Right;
43 case '^':
44 return Align::Center;
45 default:
46 return Align::None;
47 }
48}
49
57[[nodiscard]] constexpr bool HasEmbeddedNul(std::string_view source)
58{
59 for (char ch : source)
60 {
61 if (ch == '\0')
62 {
63 return true;
64 }
65 }
66 return false;
67}
68
76[[nodiscard]] constexpr bool IsSupportedPresentation(char ch)
77{
78 switch (ch)
79 {
80 case 'd':
81 case 'b':
82 case 'B':
83 case 'o':
84 case 'x':
85 case 'X':
86 case 'c':
87 case 's':
88 case 'p':
89 case 'f':
90 case 'F':
91 case 'e':
92 case 'E':
93 case 'g':
94 case 'G':
95 return true;
96 default:
97 return false;
98 }
99}
100
113template <typename UInt>
114[[nodiscard]] consteval Error ParseUnsigned(std::string_view source, size_t& pos,
115 UInt limit, UInt& value)
116{
117 static_assert(std::is_unsigned_v<UInt>);
118
119 value = 0;
120 if (pos >= source.size() || !IsDigit(source[pos]))
121 {
122 return Error::InvalidSpecifier;
123 }
124
125 while (pos < source.size() && IsDigit(source[pos]))
126 {
127 auto digit = static_cast<UInt>(source[pos] - '0');
128 if (value > static_cast<UInt>((limit - digit) / 10))
129 {
130 return Error::NumberOverflow;
131 }
132 value = static_cast<UInt>(value * 10 + digit);
133 ++pos;
134 }
135
136 return Error::None;
137}
138
144{
145 bool uses_manual = false;
146 bool uses_auto = false;
147 size_t next_auto_index = 0;
148};
149
161[[nodiscard]] consteval Error ParseFieldHead(std::string_view source, size_t& pos,
162 IndexingState& indexing, ParsedField& field)
163{
164 if (pos >= source.size())
165 {
166 return Error::UnexpectedEnd;
167 }
168
169 if (source[pos] == ':' || source[pos] == '}')
170 {
171 if (indexing.uses_manual)
172 {
173 return Error::MixedIndexing;
174 }
175
176 indexing.uses_auto = true;
177 field.arg_index = indexing.next_auto_index++;
178 return Error::None;
179 }
180
181 if (!IsDigit(source[pos]))
182 {
183 return Error::InvalidArgumentIndex;
184 }
185
186 size_t index = 0;
187 auto error = ParseUnsigned(source, pos, std::numeric_limits<size_t>::max(), index);
188 if (error != Error::None)
189 {
190 return error;
191 }
192
193 if (indexing.uses_auto)
194 {
195 return Error::MixedIndexing;
196 }
197
198 if (!Config::enable_explicit_argument_indexing)
199 {
200 return Error::ManualIndexingDisabled;
201 }
202
203 indexing.uses_manual = true;
204 field.arg_index = index;
205 return Error::None;
206}
207
218[[nodiscard]] consteval Error ParseFormatSpec(std::string_view source, size_t& pos,
219 ParsedField& field)
220{
221 if (pos >= source.size())
222 {
223 return Error::UnexpectedEnd;
224 }
225
226 if (pos + 1 < source.size() && IsAlign(source[pos + 1]))
227 {
228 if (source[pos] == '{' || source[pos] == '}')
229 {
230 return Error::InvalidSpecifier;
231 }
232 field.fill = source[pos];
233 field.align = ParseAlign(source[pos + 1]);
234 pos += 2;
235 }
236 else if (IsAlign(source[pos]))
237 {
238 field.align = ParseAlign(source[pos]);
239 ++pos;
240 }
241
242 if (pos < source.size() &&
243 (source[pos] == '+' || source[pos] == '-' || source[pos] == ' '))
244 {
245 if (source[pos] == '+')
246 {
247 field.force_sign = true;
248 }
249 else if (source[pos] == ' ')
250 {
251 field.space_sign = true;
252 }
253 // '-' is the default sign mode (negative values only); accepted for
254 // fmt/std::format compatibility and produces the same output as omitting
255 // the sign option entirely.
256 ++pos;
257 }
258
259 if (pos < source.size() && source[pos] == '#')
260 {
261 if (!Config::enable_alternate)
262 {
263 return Error::InvalidSpecifier;
264 }
265 field.alternate = true;
266 ++pos;
267 }
268
269 if (pos < source.size() && source[pos] == '0')
270 {
271 field.zero_pad = true;
272 ++pos;
273 }
274
275 if (pos < source.size() && source[pos] == '{')
276 {
277 return Error::DynamicField;
278 }
279
280 if (pos < source.size() && IsDigit(source[pos]))
281 {
282 if (!Config::enable_width)
283 {
284 return Error::InvalidSpecifier;
285 }
286 auto error =
287 ParseUnsigned(source, pos, std::numeric_limits<uint8_t>::max(), field.width);
288 if (error != Error::None)
289 {
290 return error;
291 }
292 }
293
294 if (pos < source.size() && source[pos] == '.')
295 {
296 if (!Config::enable_precision)
297 {
298 return Error::InvalidSpecifier;
299 }
300
301 ++pos;
302 if (pos < source.size() && source[pos] == '{')
303 {
304 return Error::DynamicField;
305 }
306
307 field.has_precision = true;
308 auto error = ParseUnsigned(
309 source, pos, static_cast<uint8_t>(std::numeric_limits<uint8_t>::max() - 1),
310 field.precision);
311 if (error != Error::None)
312 {
313 return error;
314 }
315 }
316
317 if (pos < source.size() && source[pos] != '}')
318 {
319 field.presentation = source[pos++];
320 if (!IsSupportedPresentation(field.presentation))
321 {
322 return Error::InvalidPresentation;
323 }
324 }
325
326 return Error::None;
327}
328
340[[nodiscard]] consteval Error ParseField(std::string_view source, size_t& pos,
341 IndexingState& indexing, ParsedField& field)
342{
343 ++pos;
344 if (pos >= source.size())
345 {
346 return Error::UnexpectedEnd;
347 }
348
349 auto error = ParseFieldHead(source, pos, indexing, field);
350 if (error != Error::None)
351 {
352 return error;
353 }
354
355 if (pos < source.size() && source[pos] != ':' && source[pos] != '}')
356 {
357 return Error::InvalidArgumentIndex;
358 }
359
360 if (pos < source.size() && source[pos] == ':')
361 {
362 ++pos;
363 error = ParseFormatSpec(source, pos, field);
364 if (error != Error::None)
365 {
366 return error;
367 }
368 }
369
370 if (pos >= source.size())
371 {
372 return Error::UnexpectedEnd;
373 }
374 if (source[pos] != '}')
375 {
376 return Error::InvalidSpecifier;
377 }
378
379 ++pos;
380 return Error::None;
381}
Error
brace 风格 format 前端的编译期失败类别。 / Compile-time failure categories for the brace-style format frontend.
Align
降为 FormatFlag 位之前的已解析对齐方式。 / Parsed alignment directive before lowering into FormatFlag bits.
brace 字段自动索引与手动索引的源级状态 / Source-level indexing mode for automatic versus manual brace fields
bool uses_auto
automatic field numbering is in use / 正在使用自动编号
size_t next_auto_index
next automatic argument index / 下一个自动参数索引
bool uses_manual
manual field numbering is in use / 正在使用手动编号