1 // -*- C++ -*-
2 //===----------------------------------------------------------------------===//
3 //
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //===----------------------------------------------------------------------===//
9
10 #ifndef _LIBCPP___CHRONO_PARSER_STD_FORMAT_SPEC_H
11 #define _LIBCPP___CHRONO_PARSER_STD_FORMAT_SPEC_H
12
13 #include <__config>
14 #include <__format/concepts.h>
15 #include <__format/format_error.h>
16 #include <__format/format_parse_context.h>
17 #include <__format/formatter_string.h>
18 #include <__format/parser_std_format_spec.h>
19 #include <string_view>
20
21 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
22 # pragma GCC system_header
23 #endif
24
25 _LIBCPP_BEGIN_NAMESPACE_STD
26
27 #if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)
28
29 namespace __format_spec {
30
31 // By not placing this constant in the formatter class it's not duplicated for char and wchar_t
32 inline constexpr __fields __fields_chrono_fractional{
33 .__precision_ = true, .__locale_specific_form_ = true, .__type_ = false};
34 inline constexpr __fields __fields_chrono{.__locale_specific_form_ = true, .__type_ = false};
35
36 /// Flags available or required in a chrono type.
37 ///
38 /// The caller of the chrono formatter lists the types it has available and the
39 /// validation tests whether the requested type spec (e.g. %M) is available in
40 /// the formatter.
41 /// When the type in the chrono-format-spec isn't present in the data a
42 /// \ref format_error is thrown.
43 enum class __flags {
44 __second = 0x1,
45 __minute = 0x2,
46 __hour = 0x4,
47 __time = __hour | __minute | __second,
48
49 __day = 0x8,
50 __month = 0x10,
51 __year = 0x20,
52
53 __weekday = 0x40,
54
55 __month_day = __day | __month,
56 __month_weekday = __weekday | __month,
57 __year_month = __month | __year,
58 __date = __day | __month | __year | __weekday,
59
60 __date_time = __date | __time,
61
62 __duration = 0x80 | __time,
63
64 __time_zone = 0x100,
65
66 __clock = __date_time | __time_zone
67 };
68
69 _LIBCPP_HIDE_FROM_ABI constexpr __flags operator&(__flags __lhs, __flags __rhs) {
70 return static_cast<__flags>(static_cast<unsigned>(__lhs) & static_cast<unsigned>(__rhs));
71 }
72
__validate_second(__flags __flags)73 _LIBCPP_HIDE_FROM_ABI constexpr void __validate_second(__flags __flags) {
74 if ((__flags & __flags::__second) != __flags::__second)
75 std::__throw_format_error("The supplied date time doesn't contain a second");
76 }
77
__validate_minute(__flags __flags)78 _LIBCPP_HIDE_FROM_ABI constexpr void __validate_minute(__flags __flags) {
79 if ((__flags & __flags::__minute) != __flags::__minute)
80 std::__throw_format_error("The supplied date time doesn't contain a minute");
81 }
82
__validate_hour(__flags __flags)83 _LIBCPP_HIDE_FROM_ABI constexpr void __validate_hour(__flags __flags) {
84 if ((__flags & __flags::__hour) != __flags::__hour)
85 std::__throw_format_error("The supplied date time doesn't contain an hour");
86 }
87
__validate_time(__flags __flags)88 _LIBCPP_HIDE_FROM_ABI constexpr void __validate_time(__flags __flags) {
89 if ((__flags & __flags::__time) != __flags::__time)
90 std::__throw_format_error("The supplied date time doesn't contain a time");
91 }
92
__validate_day(__flags __flags)93 _LIBCPP_HIDE_FROM_ABI constexpr void __validate_day(__flags __flags) {
94 if ((__flags & __flags::__day) != __flags::__day)
95 std::__throw_format_error("The supplied date time doesn't contain a day");
96 }
97
__validate_month(__flags __flags)98 _LIBCPP_HIDE_FROM_ABI constexpr void __validate_month(__flags __flags) {
99 if ((__flags & __flags::__month) != __flags::__month)
100 std::__throw_format_error("The supplied date time doesn't contain a month");
101 }
102
__validate_year(__flags __flags)103 _LIBCPP_HIDE_FROM_ABI constexpr void __validate_year(__flags __flags) {
104 if ((__flags & __flags::__year) != __flags::__year)
105 std::__throw_format_error("The supplied date time doesn't contain a year");
106 }
107
__validate_date(__flags __flags)108 _LIBCPP_HIDE_FROM_ABI constexpr void __validate_date(__flags __flags) {
109 if ((__flags & __flags::__date) != __flags::__date)
110 std::__throw_format_error("The supplied date time doesn't contain a date");
111 }
112
__validate_date_or_duration(__flags __flags)113 _LIBCPP_HIDE_FROM_ABI constexpr void __validate_date_or_duration(__flags __flags) {
114 if (((__flags & __flags::__date) != __flags::__date) && ((__flags & __flags::__duration) != __flags::__duration))
115 std::__throw_format_error("The supplied date time doesn't contain a date or duration");
116 }
117
__validate_date_time(__flags __flags)118 _LIBCPP_HIDE_FROM_ABI constexpr void __validate_date_time(__flags __flags) {
119 if ((__flags & __flags::__date_time) != __flags::__date_time)
120 std::__throw_format_error("The supplied date time doesn't contain a date and time");
121 }
122
__validate_weekday(__flags __flags)123 _LIBCPP_HIDE_FROM_ABI constexpr void __validate_weekday(__flags __flags) {
124 if ((__flags & __flags::__weekday) != __flags::__weekday)
125 std::__throw_format_error("The supplied date time doesn't contain a weekday");
126 }
127
__validate_duration(__flags __flags)128 _LIBCPP_HIDE_FROM_ABI constexpr void __validate_duration(__flags __flags) {
129 if ((__flags & __flags::__duration) != __flags::__duration)
130 std::__throw_format_error("The supplied date time doesn't contain a duration");
131 }
132
__validate_time_zone(__flags __flags)133 _LIBCPP_HIDE_FROM_ABI constexpr void __validate_time_zone(__flags __flags) {
134 if ((__flags & __flags::__time_zone) != __flags::__time_zone)
135 std::__throw_format_error("The supplied date time doesn't contain a time zone");
136 }
137
138 template <class _CharT>
139 class _LIBCPP_TEMPLATE_VIS __parser_chrono {
140 public:
141 _LIBCPP_HIDE_FROM_ABI constexpr auto
142 __parse(basic_format_parse_context<_CharT>& __parse_ctx, __fields __fields, __flags __flags)
143 -> decltype(__parse_ctx.begin()) {
144 const _CharT* __begin = __parser_.__parse(__parse_ctx, __fields);
145 const _CharT* __end = __parse_ctx.end();
146 if (__begin == __end)
147 return __begin;
148
149 const _CharT* __last = __parse_chrono_specs(__begin, __end, __flags);
150 __chrono_specs_ = basic_string_view<_CharT>{__begin, __last};
151
152 return __last;
153 }
154
155 __parser<_CharT> __parser_;
156 basic_string_view<_CharT> __chrono_specs_;
157
158 private:
159 _LIBCPP_HIDE_FROM_ABI constexpr const _CharT*
__parse_chrono_specs(const _CharT * __begin,const _CharT * __end,__flags __flags)160 __parse_chrono_specs(const _CharT* __begin, const _CharT* __end, __flags __flags) {
161 _LIBCPP_ASSERT(__begin != __end,
162 "When called with an empty input the function will cause "
163 "undefined behavior by evaluating data not in the input");
164
165 if (*__begin != _CharT('%') && *__begin != _CharT('}'))
166 std::__throw_format_error("Expected '%' or '}' in the chrono format-string");
167
168 do {
169 switch (*__begin) {
170 case _CharT('{'):
171 std::__throw_format_error("The chrono-specs contains a '{'");
172
173 case _CharT('}'):
174 return __begin;
175
176 case _CharT('%'):
177 __parse_conversion_spec(__begin, __end, __flags);
178 [[fallthrough]];
179
180 default:
181 // All other literals
182 ++__begin;
183 }
184
185 } while (__begin != __end && *__begin != _CharT('}'));
186
187 return __begin;
188 }
189
190 /// \pre *__begin == '%'
191 /// \post __begin points at the end parsed conversion-spec
192 _LIBCPP_HIDE_FROM_ABI constexpr void
__parse_conversion_spec(const _CharT * & __begin,const _CharT * __end,__flags __flags)193 __parse_conversion_spec(const _CharT*& __begin, const _CharT* __end, __flags __flags) {
194 ++__begin;
195 if (__begin == __end)
196 std::__throw_format_error("End of input while parsing the modifier chrono conversion-spec");
197
198 switch (*__begin) {
199 case _CharT('n'):
200 case _CharT('t'):
201 case _CharT('%'):
202 break;
203
204 case _CharT('S'):
205 __format_spec::__validate_second(__flags);
206 break;
207
208 case _CharT('M'):
209 __format_spec::__validate_minute(__flags);
210 break;
211
212 case _CharT('p'): // TODO FMT does the formater require an hour or a time?
213 case _CharT('H'):
214 case _CharT('I'):
215 __validate_hour(__flags);
216 break;
217
218 case _CharT('r'):
219 case _CharT('R'):
220 case _CharT('T'):
221 case _CharT('X'):
222 __format_spec::__validate_time(__flags);
223 break;
224
225 case _CharT('d'):
226 case _CharT('e'):
227 __format_spec::__validate_day(__flags);
228 break;
229
230 case _CharT('b'):
231 case _CharT('h'):
232 case _CharT('B'):
233 __parser_.__month_name_ = true;
234 [[fallthrough]];
235 case _CharT('m'):
236 __format_spec::__validate_month(__flags);
237 break;
238
239 case _CharT('y'):
240 case _CharT('C'):
241 case _CharT('Y'):
242 __format_spec::__validate_year(__flags);
243 break;
244
245 case _CharT('j'):
246 __parser_.__day_of_year_ = true;
247 __format_spec::__validate_date_or_duration(__flags);
248 break;
249
250 case _CharT('g'):
251 case _CharT('G'):
252 case _CharT('U'):
253 case _CharT('V'):
254 case _CharT('W'):
255 __parser_.__week_of_year_ = true;
256 [[fallthrough]];
257 case _CharT('x'):
258 case _CharT('D'):
259 case _CharT('F'):
260 __format_spec::__validate_date(__flags);
261 break;
262
263 case _CharT('c'):
264 __format_spec::__validate_date_time(__flags);
265 break;
266
267 case _CharT('a'):
268 case _CharT('A'):
269 __parser_.__weekday_name_ = true;
270 [[fallthrough]];
271 case _CharT('u'):
272 case _CharT('w'):
273 __parser_.__weekday_ = true;
274 __validate_weekday(__flags);
275 __format_spec::__validate_weekday(__flags);
276 break;
277
278 case _CharT('q'):
279 case _CharT('Q'):
280 __format_spec::__validate_duration(__flags);
281 break;
282
283 case _CharT('E'):
284 __parse_modifier_E(__begin, __end, __flags);
285 break;
286
287 case _CharT('O'):
288 __parse_modifier_O(__begin, __end, __flags);
289 break;
290
291 case _CharT('z'):
292 case _CharT('Z'):
293 // Currently there's no time zone information. However some clocks have a
294 // hard-coded "time zone", for these clocks the information can be used.
295 // TODO FMT implement time zones.
296 __format_spec::__validate_time_zone(__flags);
297 break;
298
299 default: // unknown type;
300 std::__throw_format_error("The date time type specifier is invalid");
301 }
302 }
303
304 /// \pre *__begin == 'E'
305 /// \post __begin is incremented by one.
306 _LIBCPP_HIDE_FROM_ABI constexpr void
__parse_modifier_E(const _CharT * & __begin,const _CharT * __end,__flags __flags)307 __parse_modifier_E(const _CharT*& __begin, const _CharT* __end, __flags __flags) {
308 ++__begin;
309 if (__begin == __end)
310 std::__throw_format_error("End of input while parsing the modifier E");
311
312 switch (*__begin) {
313 case _CharT('X'):
314 __format_spec::__validate_time(__flags);
315 break;
316
317 case _CharT('y'):
318 case _CharT('C'):
319 case _CharT('Y'):
320 __format_spec::__validate_year(__flags);
321 break;
322
323 case _CharT('x'):
324 __format_spec::__validate_date(__flags);
325 break;
326
327 case _CharT('c'):
328 __format_spec::__validate_date_time(__flags);
329 break;
330
331 case _CharT('z'):
332 // Currently there's no time zone information. However some clocks have a
333 // hard-coded "time zone", for these clocks the information can be used.
334 // TODO FMT implement time zones.
335 __format_spec::__validate_time_zone(__flags);
336 break;
337
338 default:
339 std::__throw_format_error("The date time type specifier for modifier E is invalid");
340 }
341 }
342
343 /// \pre *__begin == 'O'
344 /// \post __begin is incremented by one.
345 _LIBCPP_HIDE_FROM_ABI constexpr void
__parse_modifier_O(const _CharT * & __begin,const _CharT * __end,__flags __flags)346 __parse_modifier_O(const _CharT*& __begin, const _CharT* __end, __flags __flags) {
347 ++__begin;
348 if (__begin == __end)
349 std::__throw_format_error("End of input while parsing the modifier O");
350
351 switch (*__begin) {
352 case _CharT('S'):
353 __format_spec::__validate_second(__flags);
354 break;
355
356 case _CharT('M'):
357 __format_spec::__validate_minute(__flags);
358 break;
359
360 case _CharT('I'):
361 case _CharT('H'):
362 __format_spec::__validate_hour(__flags);
363 break;
364
365 case _CharT('d'):
366 case _CharT('e'):
367 __format_spec::__validate_day(__flags);
368 break;
369
370 case _CharT('m'):
371 __format_spec::__validate_month(__flags);
372 break;
373
374 case _CharT('y'):
375 __format_spec::__validate_year(__flags);
376 break;
377
378 case _CharT('U'):
379 case _CharT('V'):
380 case _CharT('W'):
381 __parser_.__week_of_year_ = true;
382 __format_spec::__validate_date(__flags);
383 break;
384
385 case _CharT('u'):
386 case _CharT('w'):
387 __parser_.__weekday_ = true;
388 __format_spec::__validate_weekday(__flags);
389 break;
390
391 case _CharT('z'):
392 // Currently there's no time zone information. However some clocks have a
393 // hard-coded "time zone", for these clocks the information can be used.
394 // TODO FMT implement time zones.
395 __format_spec::__validate_time_zone(__flags);
396 break;
397
398 default:
399 std::__throw_format_error("The date time type specifier for modifier O is invalid");
400 }
401 }
402 };
403
404 } // namespace __format_spec
405
406 #endif //_LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)
407
408 _LIBCPP_END_NAMESPACE_STD
409
410 #endif // _LIBCPP___CHRONO_PARSER_STD_FORMAT_SPEC_H
411