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 >= 20 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 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 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 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 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 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 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 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 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 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 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 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 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 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 using _ConstIterator = typename basic_format_parse_context<_CharT>::const_iterator; 141 142 public: 143 template <class _ParseContext> 144 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator 145 __parse(_ParseContext& __ctx, __fields __fields, __flags __flags) { 146 _ConstIterator __begin = __parser_.__parse(__ctx, __fields); 147 _ConstIterator __end = __ctx.end(); 148 if (__begin == __end) 149 return __begin; 150 151 _ConstIterator __last = __parse_chrono_specs(__begin, __end, __flags); 152 __chrono_specs_ = basic_string_view<_CharT>{__begin, __last}; 153 154 return __last; 155 } 156 157 __parser<_CharT> __parser_; 158 basic_string_view<_CharT> __chrono_specs_; 159 160 private: 161 _LIBCPP_HIDE_FROM_ABI constexpr _ConstIterator 162 __parse_chrono_specs(_ConstIterator __begin, _ConstIterator __end, __flags __flags) { 163 _LIBCPP_ASSERT_INTERNAL(__begin != __end, 164 "When called with an empty input the function will cause " 165 "undefined behavior by evaluating data not in the input"); 166 167 if (*__begin != _CharT('%') && *__begin != _CharT('}')) 168 std::__throw_format_error("The format specifier expects a '%' or a '}'"); 169 170 do { 171 switch (*__begin) { 172 case _CharT('{'): 173 std::__throw_format_error("The chrono specifiers contain a '{'"); 174 175 case _CharT('}'): 176 return __begin; 177 178 case _CharT('%'): 179 __parse_conversion_spec(__begin, __end, __flags); 180 [[fallthrough]]; 181 182 default: 183 // All other literals 184 ++__begin; 185 } 186 187 } while (__begin != __end && *__begin != _CharT('}')); 188 189 return __begin; 190 } 191 192 /// \pre *__begin == '%' 193 /// \post __begin points at the end parsed conversion-spec 194 _LIBCPP_HIDE_FROM_ABI constexpr void 195 __parse_conversion_spec(_ConstIterator& __begin, _ConstIterator __end, __flags __flags) { 196 ++__begin; 197 if (__begin == __end) 198 std::__throw_format_error("End of input while parsing a conversion specifier"); 199 200 switch (*__begin) { 201 case _CharT('n'): 202 case _CharT('t'): 203 case _CharT('%'): 204 break; 205 206 case _CharT('S'): 207 __format_spec::__validate_second(__flags); 208 break; 209 210 case _CharT('M'): 211 __format_spec::__validate_minute(__flags); 212 break; 213 214 case _CharT('p'): // TODO FMT does the formater require an hour or a time? 215 case _CharT('H'): 216 case _CharT('I'): 217 __parser_.__hour_ = true; 218 __validate_hour(__flags); 219 break; 220 221 case _CharT('r'): 222 case _CharT('R'): 223 case _CharT('T'): 224 case _CharT('X'): 225 __parser_.__hour_ = true; 226 __format_spec::__validate_time(__flags); 227 break; 228 229 case _CharT('d'): 230 case _CharT('e'): 231 __format_spec::__validate_day(__flags); 232 break; 233 234 case _CharT('b'): 235 case _CharT('h'): 236 case _CharT('B'): 237 __parser_.__month_name_ = true; 238 [[fallthrough]]; 239 case _CharT('m'): 240 __format_spec::__validate_month(__flags); 241 break; 242 243 case _CharT('y'): 244 case _CharT('C'): 245 case _CharT('Y'): 246 __format_spec::__validate_year(__flags); 247 break; 248 249 case _CharT('j'): 250 __parser_.__day_of_year_ = true; 251 __format_spec::__validate_date_or_duration(__flags); 252 break; 253 254 case _CharT('g'): 255 case _CharT('G'): 256 case _CharT('U'): 257 case _CharT('V'): 258 case _CharT('W'): 259 __parser_.__week_of_year_ = true; 260 [[fallthrough]]; 261 case _CharT('x'): 262 case _CharT('D'): 263 case _CharT('F'): 264 __format_spec::__validate_date(__flags); 265 break; 266 267 case _CharT('c'): 268 __format_spec::__validate_date_time(__flags); 269 break; 270 271 case _CharT('a'): 272 case _CharT('A'): 273 __parser_.__weekday_name_ = true; 274 [[fallthrough]]; 275 case _CharT('u'): 276 case _CharT('w'): 277 __parser_.__weekday_ = true; 278 __validate_weekday(__flags); 279 __format_spec::__validate_weekday(__flags); 280 break; 281 282 case _CharT('q'): 283 case _CharT('Q'): 284 __format_spec::__validate_duration(__flags); 285 break; 286 287 case _CharT('E'): 288 __parse_modifier_E(__begin, __end, __flags); 289 break; 290 291 case _CharT('O'): 292 __parse_modifier_O(__begin, __end, __flags); 293 break; 294 295 case _CharT('z'): 296 case _CharT('Z'): 297 // Currently there's no time zone information. However some clocks have a 298 // hard-coded "time zone", for these clocks the information can be used. 299 // TODO FMT implement time zones. 300 __format_spec::__validate_time_zone(__flags); 301 break; 302 303 default: // unknown type; 304 std::__throw_format_error("The date time type specifier is invalid"); 305 } 306 } 307 308 /// \pre *__begin == 'E' 309 /// \post __begin is incremented by one. 310 _LIBCPP_HIDE_FROM_ABI constexpr void 311 __parse_modifier_E(_ConstIterator& __begin, _ConstIterator __end, __flags __flags) { 312 ++__begin; 313 if (__begin == __end) 314 std::__throw_format_error("End of input while parsing the modifier E"); 315 316 switch (*__begin) { 317 case _CharT('X'): 318 __parser_.__hour_ = true; 319 __format_spec::__validate_time(__flags); 320 break; 321 322 case _CharT('y'): 323 case _CharT('C'): 324 case _CharT('Y'): 325 __format_spec::__validate_year(__flags); 326 break; 327 328 case _CharT('x'): 329 __format_spec::__validate_date(__flags); 330 break; 331 332 case _CharT('c'): 333 __format_spec::__validate_date_time(__flags); 334 break; 335 336 case _CharT('z'): 337 // Currently there's no time zone information. However some clocks have a 338 // hard-coded "time zone", for these clocks the information can be used. 339 // TODO FMT implement time zones. 340 __format_spec::__validate_time_zone(__flags); 341 break; 342 343 default: 344 std::__throw_format_error("The date time type specifier for modifier E is invalid"); 345 } 346 } 347 348 /// \pre *__begin == 'O' 349 /// \post __begin is incremented by one. 350 _LIBCPP_HIDE_FROM_ABI constexpr void 351 __parse_modifier_O(_ConstIterator& __begin, _ConstIterator __end, __flags __flags) { 352 ++__begin; 353 if (__begin == __end) 354 std::__throw_format_error("End of input while parsing the modifier O"); 355 356 switch (*__begin) { 357 case _CharT('S'): 358 __format_spec::__validate_second(__flags); 359 break; 360 361 case _CharT('M'): 362 __format_spec::__validate_minute(__flags); 363 break; 364 365 case _CharT('I'): 366 case _CharT('H'): 367 __parser_.__hour_ = true; 368 __format_spec::__validate_hour(__flags); 369 break; 370 371 case _CharT('d'): 372 case _CharT('e'): 373 __format_spec::__validate_day(__flags); 374 break; 375 376 case _CharT('m'): 377 __format_spec::__validate_month(__flags); 378 break; 379 380 case _CharT('y'): 381 __format_spec::__validate_year(__flags); 382 break; 383 384 case _CharT('U'): 385 case _CharT('V'): 386 case _CharT('W'): 387 __parser_.__week_of_year_ = true; 388 __format_spec::__validate_date(__flags); 389 break; 390 391 case _CharT('u'): 392 case _CharT('w'): 393 __parser_.__weekday_ = true; 394 __format_spec::__validate_weekday(__flags); 395 break; 396 397 case _CharT('z'): 398 // Currently there's no time zone information. However some clocks have a 399 // hard-coded "time zone", for these clocks the information can be used. 400 // TODO FMT implement time zones. 401 __format_spec::__validate_time_zone(__flags); 402 break; 403 404 default: 405 std::__throw_format_error("The date time type specifier for modifier O is invalid"); 406 } 407 } 408 }; 409 410 } // namespace __format_spec 411 412 #endif //_LIBCPP_STD_VER >= 20 413 414 _LIBCPP_END_NAMESPACE_STD 415 416 #endif // _LIBCPP___CHRONO_PARSER_STD_FORMAT_SPEC_H 417