di 0.1.0
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(m_radix == 0 ? 10 : m_radix);
100 }
101
102 ch = *it;
103 if ((m_radix == 0 || m_radix == 16) && (ch == U'x' || ch == U'X')) {
104 context.advance(++it);
105 return parse_digits(16);
106 }
107 if ((m_radix == 0 || m_radix == 2) && (ch == U'b' || ch == U'B')) {
108 context.advance(++it);
109 return parse_digits(2);
110 }
111 if constexpr (mode == IntegerMode::Improved) {
112 if ((m_radix == 0 || m_radix == 8) && (ch == U'o' || ch == U'O')) {
113 context.advance(++it);
114 return parse_digits(8);
115 }
116 // In improved mode, default to base 10 instead of base 8 when there is a leading 0.
117 return parse_digits(m_radix == 0 ? 10 : m_radix);
118 }
119 // Fallback to octal
120 return parse_digits(m_radix == 0 ? 8 : m_radix);
121 }
122
123 private:
124 int m_radix { 10 };
125 };
126
127 template<concepts::Integer T, IntegerMode mode = IntegerMode::Improved>
128 struct IntegerFunction {
129 constexpr auto operator()(int radix = 0) const {
130 using namespace di::literals;
131
132 DI_ASSERT(radix == 0 || (radix >= 2 && radix <= 36));
133
134 auto sign = [] {
135 if constexpr (concepts::Signed<T> || mode == IntegerMode::CStandard) {
136 return '-'_m || '+'_m;
137 } else {
138 return '+'_m;
139 }
140 }();
141
142 return (-match_one(sign) >> MatchIntegerPrefixParser<mode>(radix))
143 << []<concepts::ParserContext Context>(Context& context,
145 auto [sign, number_part] = results;
146
147 // NOTE: We can't use structured bindings here because clang-16 doesn't think its a constant expression.
148 auto inferred_radix = util::get<0>(number_part);
149 auto digits = util::get<1>(number_part);
150
151 bool negative = false;
152 if constexpr (concepts::Signed<T> || mode == IntegerMode::CStandard) {
153 if (sign && *sign == '-') {
154 negative = true;
155 }
156 }
157
158 auto to_digit = [&](c32 code_point) {
159 if (inferred_radix <= 10) {
160 return code_point - U'0';
161 }
162 if (code_point >= U'a') {
163 return code_point - U'a' + 10;
164 }
165 if (code_point >= U'A') {
166 return code_point - U'A' + 10;
167 }
168 return code_point - U'0';
169 };
170
171 auto overflow_error = [&] {
172 if constexpr (mode == IntegerMode::CStandard && !concepts::Signed<T>) {
174 }
176 }();
177
178 using U = meta::MakeUnsigned<T>;
179 auto result = math::Checked<U>(0);
180
181 auto sent = container::end(digits);
182 for (auto it = container::begin(digits); it != sent; ++it) {
183 result *= inferred_radix;
184 result += to_digit(*it);
185 }
186
187 if (result.invalid()) {
188 return Unexpected(parser::make_error(context, overflow_error, sent));
189 }
190 auto value = *result.value();
191 if constexpr (concepts::Signed<T>) {
192 auto max_magnitude = math::to_unsigned(math::NumericLimits<T>::max) + negative;
193 if (value > max_magnitude) {
194 return Unexpected(parser::make_error(context, overflow_error, sent));
195 }
196 return static_cast<T>(negative ? -value : value);
197 } else if constexpr (mode == IntegerMode::CStandard) {
198 return negative ? -value : value;
199 }
200 return value;
201 };
202 }
203 };
204}
205
206template<concepts::Integer T, IntegerMode mode = IntegerMode::Improved>
207constexpr inline auto integer = detail::IntegerFunction<T, mode> {};
208
209namespace detail {
210 template<concepts::Integer T>
212 return integer<T>();
213 }
214}
215}
#define DI_ASSERT(...)
Definition assert_bool.h:7
Definition tuple.h:27
constexpr auto reconstruct
Definition reconstruct.h:75
constexpr auto end
Definition end.h:55
constexpr auto begin
Definition begin.h:52
constexpr auto value
Definition value.h:34
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 bool.h:21
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
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:207
constexpr auto parse
Definition parse.h:23
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
static constexpr T max
Definition numeric_limits.h:11
Definition in_place_type.h:5