master
  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
 32namespace __format_spec {
 33
 34// By not placing this constant in the formatter class it's not duplicated for char and wchar_t
 35inline constexpr __fields __fields_chrono_fractional{
 36    .__precision_ = true, .__locale_specific_form_ = true, .__type_ = false};
 37inline 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.
 46enum 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
141template <class _CharT>
142class __parser_chrono {
143  using _ConstIterator _LIBCPP_NODEBUG = typename basic_format_parse_context<_CharT>::const_iterator;
144
145public:
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
163private:
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