422 lines · c
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-exception7//8//===----------------------------------------------------------------------===//9 10#ifndef _LIBCPP___CHRONO_PARSER_STD_FORMAT_SPEC_H11#define _LIBCPP___CHRONO_PARSER_STD_FORMAT_SPEC_H12 13#include <__config>14 15#if _LIBCPP_HAS_LOCALIZATION16 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_header26# endif27 28_LIBCPP_BEGIN_NAMESPACE_STD29 30# if _LIBCPP_STD_VER >= 2031 32namespace __format_spec {33 34// By not placing this constant in the formatter class it's not duplicated for char and wchar_t35inline 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 the42/// validation tests whether the requested type spec (e.g. %M) is available in43/// the formatter.44/// When the type in the chrono-format-spec isn't present in the data a45/// \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_zone70};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::iterator148 __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 _ConstIterator165 __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 literals187 ++__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-spec197 _LIBCPP_HIDE_FROM_ABI constexpr void198 __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 a301 // 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 void314 __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 a341 // 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 void354 __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 a402 // 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_spec414 415# endif // _LIBCPP_STD_VER >= 20416 417_LIBCPP_END_NAMESPACE_STD418 419#endif // _LIBCPP_HAS_LOCALIZATION420 421#endif // _LIBCPP___CHRONO_PARSER_STD_FORMAT_SPEC_H422