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