Files
clang-p2996/libcxx/include/__chrono/parser_std_format_spec.h
Mark de Wever 105fef5dca [libc++][chrono] Add calendar type formatters.
Some of the calendar types have landed before, this adds the missing
set. Note this does not complete the implementation of the chrono
formatters.

This removes the `chrono` header for some transitive include in C++17
mode. This is needed to avoid inclusion cycles.

Partially implements:
- P1361 Integration of chrono with text formatting
- P2372 Fixing locale handling in chrono formatters

Reviewed By: #libc, ldionne

Differential Revision: https://reviews.llvm.org/D137022
2022-12-24 15:11:41 +01:00

411 lines
12 KiB
C++

// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef _LIBCPP___CHRONO_PARSER_STD_FORMAT_SPEC_H
#define _LIBCPP___CHRONO_PARSER_STD_FORMAT_SPEC_H
#include <__config>
#include <__format/concepts.h>
#include <__format/format_error.h>
#include <__format/format_parse_context.h>
#include <__format/formatter_string.h>
#include <__format/parser_std_format_spec.h>
#include <string_view>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif
_LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)
namespace __format_spec {
// By not placing this constant in the formatter class it's not duplicated for char and wchar_t
inline constexpr __fields __fields_chrono_fractional{
.__precision_ = true, .__locale_specific_form_ = true, .__type_ = false};
inline constexpr __fields __fields_chrono{.__locale_specific_form_ = true, .__type_ = false};
/// Flags available or required in a chrono type.
///
/// The caller of the chrono formatter lists the types it has available and the
/// validation tests whether the requested type spec (e.g. %M) is available in
/// the formatter.
/// When the type in the chrono-format-spec isn't present in the data a
/// \ref format_error is thrown.
enum class __flags {
__second = 0x1,
__minute = 0x2,
__hour = 0x4,
__time = __hour | __minute | __second,
__day = 0x8,
__month = 0x10,
__year = 0x20,
__weekday = 0x40,
__month_day = __day | __month,
__month_weekday = __weekday | __month,
__year_month = __month | __year,
__date = __day | __month | __year | __weekday,
__date_time = __date | __time,
__duration = 0x80 | __time,
__time_zone = 0x100,
__clock = __date_time | __time_zone
};
_LIBCPP_HIDE_FROM_ABI constexpr __flags operator&(__flags __lhs, __flags __rhs) {
return static_cast<__flags>(static_cast<unsigned>(__lhs) & static_cast<unsigned>(__rhs));
}
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_second(__flags __flags) {
if ((__flags & __flags::__second) != __flags::__second)
std::__throw_format_error("The supplied date time doesn't contain a second");
}
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_minute(__flags __flags) {
if ((__flags & __flags::__minute) != __flags::__minute)
std::__throw_format_error("The supplied date time doesn't contain a minute");
}
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_hour(__flags __flags) {
if ((__flags & __flags::__hour) != __flags::__hour)
std::__throw_format_error("The supplied date time doesn't contain an hour");
}
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_time(__flags __flags) {
if ((__flags & __flags::__time) != __flags::__time)
std::__throw_format_error("The supplied date time doesn't contain a time");
}
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_day(__flags __flags) {
if ((__flags & __flags::__day) != __flags::__day)
std::__throw_format_error("The supplied date time doesn't contain a day");
}
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_month(__flags __flags) {
if ((__flags & __flags::__month) != __flags::__month)
std::__throw_format_error("The supplied date time doesn't contain a month");
}
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_year(__flags __flags) {
if ((__flags & __flags::__year) != __flags::__year)
std::__throw_format_error("The supplied date time doesn't contain a year");
}
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_date(__flags __flags) {
if ((__flags & __flags::__date) != __flags::__date)
std::__throw_format_error("The supplied date time doesn't contain a date");
}
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_date_or_duration(__flags __flags) {
if (((__flags & __flags::__date) != __flags::__date) && ((__flags & __flags::__duration) != __flags::__duration))
std::__throw_format_error("The supplied date time doesn't contain a date or duration");
}
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_date_time(__flags __flags) {
if ((__flags & __flags::__date_time) != __flags::__date_time)
std::__throw_format_error("The supplied date time doesn't contain a date and time");
}
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_weekday(__flags __flags) {
if ((__flags & __flags::__weekday) != __flags::__weekday)
std::__throw_format_error("The supplied date time doesn't contain a weekday");
}
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_duration(__flags __flags) {
if ((__flags & __flags::__duration) != __flags::__duration)
std::__throw_format_error("The supplied date time doesn't contain a duration");
}
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_time_zone(__flags __flags) {
if ((__flags & __flags::__time_zone) != __flags::__time_zone)
std::__throw_format_error("The supplied date time doesn't contain a time zone");
}
template <class _CharT>
class _LIBCPP_TEMPLATE_VIS __parser_chrono {
public:
_LIBCPP_HIDE_FROM_ABI constexpr auto
__parse(basic_format_parse_context<_CharT>& __parse_ctx, __fields __fields, __flags __flags)
-> decltype(__parse_ctx.begin()) {
const _CharT* __begin = __parser_.__parse(__parse_ctx, __fields);
const _CharT* __end = __parse_ctx.end();
if (__begin == __end)
return __begin;
const _CharT* __last = __parse_chrono_specs(__begin, __end, __flags);
__chrono_specs_ = basic_string_view<_CharT>{__begin, __last};
return __last;
}
__parser<_CharT> __parser_;
basic_string_view<_CharT> __chrono_specs_;
private:
_LIBCPP_HIDE_FROM_ABI constexpr const _CharT*
__parse_chrono_specs(const _CharT* __begin, const _CharT* __end, __flags __flags) {
_LIBCPP_ASSERT(__begin != __end,
"When called with an empty input the function will cause "
"undefined behavior by evaluating data not in the input");
if (*__begin != _CharT('%') && *__begin != _CharT('}'))
std::__throw_format_error("Expected '%' or '}' in the chrono format-string");
do {
switch (*__begin) {
case _CharT('{'):
std::__throw_format_error("The chrono-specs contains a '{'");
case _CharT('}'):
return __begin;
case _CharT('%'):
__parse_conversion_spec(__begin, __end, __flags);
[[fallthrough]];
default:
// All other literals
++__begin;
}
} while (__begin != __end && *__begin != _CharT('}'));
return __begin;
}
/// \pre *__begin == '%'
/// \post __begin points at the end parsed conversion-spec
_LIBCPP_HIDE_FROM_ABI constexpr void
__parse_conversion_spec(const _CharT*& __begin, const _CharT* __end, __flags __flags) {
++__begin;
if (__begin == __end)
std::__throw_format_error("End of input while parsing the modifier chrono conversion-spec");
switch (*__begin) {
case _CharT('n'):
case _CharT('t'):
case _CharT('%'):
break;
case _CharT('S'):
__format_spec::__validate_second(__flags);
break;
case _CharT('M'):
__format_spec::__validate_minute(__flags);
break;
case _CharT('p'): // TODO FMT does the formater require an hour or a time?
case _CharT('H'):
case _CharT('I'):
__validate_hour(__flags);
break;
case _CharT('r'):
case _CharT('R'):
case _CharT('T'):
case _CharT('X'):
__format_spec::__validate_time(__flags);
break;
case _CharT('d'):
case _CharT('e'):
__format_spec::__validate_day(__flags);
break;
case _CharT('b'):
case _CharT('h'):
case _CharT('B'):
__parser_.__month_name_ = true;
[[fallthrough]];
case _CharT('m'):
__format_spec::__validate_month(__flags);
break;
case _CharT('y'):
case _CharT('C'):
case _CharT('Y'):
__format_spec::__validate_year(__flags);
break;
case _CharT('j'):
__parser_.__day_of_year_ = true;
__format_spec::__validate_date_or_duration(__flags);
break;
case _CharT('g'):
case _CharT('G'):
case _CharT('U'):
case _CharT('V'):
case _CharT('W'):
__parser_.__week_of_year_ = true;
[[fallthrough]];
case _CharT('x'):
case _CharT('D'):
case _CharT('F'):
__format_spec::__validate_date(__flags);
break;
case _CharT('c'):
__format_spec::__validate_date_time(__flags);
break;
case _CharT('a'):
case _CharT('A'):
__parser_.__weekday_name_ = true;
[[fallthrough]];
case _CharT('u'):
case _CharT('w'):
__parser_.__weekday_ = true;
__validate_weekday(__flags);
__format_spec::__validate_weekday(__flags);
break;
case _CharT('q'):
case _CharT('Q'):
__format_spec::__validate_duration(__flags);
break;
case _CharT('E'):
__parse_modifier_E(__begin, __end, __flags);
break;
case _CharT('O'):
__parse_modifier_O(__begin, __end, __flags);
break;
case _CharT('z'):
case _CharT('Z'):
// Currently there's no time zone information. However some clocks have a
// hard-coded "time zone", for these clocks the information can be used.
// TODO FMT implement time zones.
__format_spec::__validate_time_zone(__flags);
break;
default: // unknown type;
std::__throw_format_error("The date time type specifier is invalid");
}
}
/// \pre *__begin == 'E'
/// \post __begin is incremented by one.
_LIBCPP_HIDE_FROM_ABI constexpr void
__parse_modifier_E(const _CharT*& __begin, const _CharT* __end, __flags __flags) {
++__begin;
if (__begin == __end)
std::__throw_format_error("End of input while parsing the modifier E");
switch (*__begin) {
case _CharT('X'):
__format_spec::__validate_time(__flags);
break;
case _CharT('y'):
case _CharT('C'):
case _CharT('Y'):
__format_spec::__validate_year(__flags);
break;
case _CharT('x'):
__format_spec::__validate_date(__flags);
break;
case _CharT('c'):
__format_spec::__validate_date_time(__flags);
break;
case _CharT('z'):
// Currently there's no time zone information. However some clocks have a
// hard-coded "time zone", for these clocks the information can be used.
// TODO FMT implement time zones.
__format_spec::__validate_time_zone(__flags);
break;
default:
std::__throw_format_error("The date time type specifier for modifier E is invalid");
}
}
/// \pre *__begin == 'O'
/// \post __begin is incremented by one.
_LIBCPP_HIDE_FROM_ABI constexpr void
__parse_modifier_O(const _CharT*& __begin, const _CharT* __end, __flags __flags) {
++__begin;
if (__begin == __end)
std::__throw_format_error("End of input while parsing the modifier O");
switch (*__begin) {
case _CharT('S'):
__format_spec::__validate_second(__flags);
break;
case _CharT('M'):
__format_spec::__validate_minute(__flags);
break;
case _CharT('I'):
case _CharT('H'):
__format_spec::__validate_hour(__flags);
break;
case _CharT('d'):
case _CharT('e'):
__format_spec::__validate_day(__flags);
break;
case _CharT('m'):
__format_spec::__validate_month(__flags);
break;
case _CharT('y'):
__format_spec::__validate_year(__flags);
break;
case _CharT('U'):
case _CharT('V'):
case _CharT('W'):
__parser_.__week_of_year_ = true;
__format_spec::__validate_date(__flags);
break;
case _CharT('u'):
case _CharT('w'):
__parser_.__weekday_ = true;
__format_spec::__validate_weekday(__flags);
break;
case _CharT('z'):
// Currently there's no time zone information. However some clocks have a
// hard-coded "time zone", for these clocks the information can be used.
// TODO FMT implement time zones.
__format_spec::__validate_time_zone(__flags);
break;
default:
std::__throw_format_error("The date time type specifier for modifier O is invalid");
}
}
};
} // namespace __format_spec
#endif //_LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___CHRONO_PARSER_STD_FORMAT_SPEC_H