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