Iros
 
Loading...
Searching...
No Matches
integer.h
Go to the documentation of this file.
1#pragma once
2
6#include "di/meta/language.h"
15#include "di/types/char.h"
16#include "di/util/get.h"
19
20namespace di::parser {
25
36
37namespace detail {
38 template<IntegerMode mode>
39 class MatchIntegerPrefixParser : public ParserBase<MatchIntegerPrefixParser<mode>> {
40 public:
41 constexpr explicit MatchIntegerPrefixParser(int radix) : m_radix(radix) {}
42
43 template<concepts::ParserContext Context>
44 constexpr auto parse(Context& context) const -> meta::ParserContextResult<
47 Context> {
48 auto parse_digits = [&](int inferred_radix)
52 Context> {
53 auto valid_digit = [&](c32 code_point) {
54 if (inferred_radix <= 10) {
55 return code_point >= U'0' && code_point < U'0' + inferred_radix;
56 }
57 return (code_point >= U'0' && code_point <= U'9') ||
58 (code_point >= U'a' && code_point < U'a' + inferred_radix - 10) ||
59 (code_point >= U'A' && code_point < U'A' + inferred_radix - 10);
60 };
61
62 auto start = container::begin(context);
63 auto sent = container::end(context);
64
65 auto it = start;
66 while (it != sent && valid_digit(*it)) {
67 ++it;
68 }
69
70 if (it == start) {
71 return vocab::Unexpected(context.make_error());
72 }
73
74 context.advance(it);
75 return vocab::make_tuple(inferred_radix, container::reconstruct(in_place_type<Context>, start, it));
76 };
77
78 // Only octal and hexadecimal numbers can have a prefix.
79 if (m_radix != 0 && m_radix != 8 && m_radix != 16 && m_radix != 2) {
80 return parse_digits(m_radix);
81 }
82
83 auto start = container::begin(context);
84 auto sent = container::end(context);
85
86 auto it = start;
87 if (it == sent) {
88 return parse_digits(m_radix == 0 ? 10 : m_radix);
89 }
90
91 auto ch = *it;
92 if (ch != U'0') {
93 return parse_digits(m_radix == 0 ? 10 : m_radix);
94 }
95
96 // If the string is exactly "0", then it is not a prefix. Just parse it as a decimal number, which will
97 // result in the value of 0.
98 if (++it == sent) {
99 return parse_digits(10);
100 }
101
102 if constexpr (mode == IntegerMode::CStandard) {
103 if (m_radix == 8) {
104 // Since a 0 has been encountered, infer the radix to be 8.
105 return parse_digits(8);
106 }
107 }
108
109 ch = *it;
110 if (ch == U'x' || ch == U'X') {
111 context.advance(++it);
112 return parse_digits(16);
113 }
114 if (ch == U'b' || ch == U'B') {
115 context.advance(++it);
116 return parse_digits(2);
117 }
118 if constexpr (mode == IntegerMode::Improved) {
119 if (ch == U'o' || ch == U'O') {
120 context.advance(++it);
121 return parse_digits(8);
122 }
123 return parse_digits(m_radix == 0 ? 10 : m_radix);
124 }
125 return parse_digits(m_radix == 0 ? 8 : m_radix);
126 }
127
128 private:
129 int m_radix { 10 };
130 };
131
132 template<concepts::Integer T, IntegerMode mode = IntegerMode::Improved>
134 constexpr auto operator()(int radix = 0) const {
135 using namespace di::literals;
136
137 DI_ASSERT(radix == 0 || (radix >= 2 && radix <= 36));
138
139 auto sign = [] {
140 if constexpr (concepts::Signed<T> || mode == IntegerMode::CStandard) {
141 return '-'_m || '+'_m;
142 } else {
143 return '+'_m;
144 }
145 }();
146
147 return (-match_one(sign) >> MatchIntegerPrefixParser<mode>(radix))
148 << []<concepts::ParserContext Context>(Context& context,
150 auto [sign, number_part] = results;
151
152 // NOTE: We can't use structured bindings here because clang-16 doesn't think its a constant expression.
153 auto inferred_radix = util::get<0>(number_part);
154 auto digits = util::get<1>(number_part);
155
156 bool negative = false;
157 if constexpr (concepts::Signed<T> || mode == IntegerMode::CStandard) {
158 if (sign && *sign == '-') {
159 negative = true;
160 }
161 }
162
163 auto to_digit = [&](c32 code_point) {
164 if (inferred_radix <= 10) {
165 return code_point - U'0';
166 }
167 if (code_point >= U'a') {
168 return code_point - U'a' + 10;
169 }
170 if (code_point >= U'A') {
171 return code_point - U'A' + 10;
172 }
173 return code_point - U'0';
174 };
175
176 auto overflow_error = [&] {
177 if constexpr (mode == IntegerMode::CStandard && !concepts::Signed<T>) {
179 }
181 }();
182
183 using U = meta::MakeUnsigned<T>;
184 auto result = math::Checked<U>(0);
185
186 auto sent = container::end(digits);
187 for (auto it = container::begin(digits); it != sent; ++it) {
188 result *= inferred_radix;
189 result += to_digit(*it);
190 }
191
192 if (result.invalid()) {
193 return Unexpected(parser::make_error(context, overflow_error, sent));
194 }
195 auto value = *result.value();
196 if constexpr (concepts::Signed<T>) {
197 auto max_magnitude = math::to_unsigned(math::NumericLimits<T>::max) + negative;
198 if (value > max_magnitude) {
199 return Unexpected(parser::make_error(context, overflow_error, sent));
200 }
201 return static_cast<T>(negative ? -value : value);
202 } else if constexpr (mode == IntegerMode::CStandard) {
203 return negative ? -value : value;
204 }
205 return value;
206 };
207 }
208 };
209}
210
211template<concepts::Integer T, IntegerMode mode = IntegerMode::Improved>
212constexpr inline auto integer = detail::IntegerFunction<T, mode> {};
213
214namespace detail {
215 template<concepts::Integer T>
219}
220}
#define DI_ASSERT(...)
Definition assert_bool.h:7
Definition checked.h:10
Definition parser_base.h:5
constexpr auto parse(Context &context) const -> meta::ParserContextResult< vocab::Tuple< int, meta::Reconstructed< Context, meta::ContainerIterator< Context >, meta::ContainerIterator< Context > > >, Context >
Definition integer.h:44
constexpr MatchIntegerPrefixParser(int radix)
Definition integer.h:41
Definition tuple_forward_declaration.h:5
Definition unexpected.h:14
Definition parser_context.h:12
Definition language.h:238
constexpr auto reconstruct
Definition reconstruct.h:75
constexpr auto end
Definition end.h:47
constexpr auto begin
Definition begin.h:44
Definition json_deserializer.h:532
Definition duration_literals.h:20
constexpr auto to_unsigned
Definition to_unsigned.h:16
decltype(container::reconstruct(in_place_type< T >, util::declval< It >(), util::declval< Sent >())) Reconstructed
Definition reconstructed.h:11
vocab::Expected< T, meta::ParserContextError< Context > > ParserContextResult
Definition parser_context_result.h:8
detail::MakeUnsignedHelper< RemoveCV< T > >::Type MakeUnsigned
Definition language.h:362
decltype(container::begin(util::declval< T & >())) ContainerIterator
Definition container_iterator.h:8
Definition zstring_parser.h:9
IntegerError
Definition integer.h:21
@ Underflow
Definition integer.h:23
@ Overflow
Definition integer.h:22
IntegerMode
Mode to use when parsing an integer.
Definition integer.h:32
@ CStandard
Definition integer.h:34
@ Improved
Definition integer.h:33
@ Underflow
Definition zstring_parser.h:21
@ Overflow
Definition zstring_parser.h:20
constexpr auto code_point
Definition code_point_parser.h:35
constexpr auto match_one
Definition match_one.h:27
constexpr auto make_error
Definition make_error.h:19
constexpr auto integer
Definition integer.h:212
char32_t c32
Definition char.h:6
di::meta::Decay< decltype(T)> Tag
Definition tag_invoke.h:28
constexpr auto get(T &&value) -> decltype(auto)
Definition get.h:8
constexpr auto make_tuple(Args &&... args)
Definition make_tuple.h:9
Unexpected(E &&) -> Unexpected< meta::UnwrapRefDecay< E > >
constexpr tag_invoke_detail::TagInvokeFn tag_invoke
Definition tag_invoke.h:22
constexpr auto in_place_type
Definition in_place_type.h:12
Definition numeric_limits.h:7
Definition integer.h:133
constexpr auto operator()(int radix=0) const
Definition integer.h:134
Definition in_place_type.h:5