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