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