1use crate::lexer::Lexer;
2
3#[derive(Debug)]
4pub struct HexDecimal {
5 before_decimal: String,
6 after_decimal: String,
7}
8
9impl HexDecimal {
10 #[inline]
11 fn new(before_decimal: String, after_decimal: String) -> Self {
12 HexDecimal {
13 before_decimal,
14 after_decimal,
15 }
16 }
17
18 fn into_decimal(self) -> f64 {
19 let mut num = 0.0f64;
20
21 for (i, c) in (0i32..).zip(self.before_decimal.chars().rev()) {
22 let digit = c.to_digit(16).unwrap() as f64;
23 num += digit * 16f64.powi(i);
24 }
25
26 for (i, c) in (0i32..).zip(self.after_decimal.chars().skip(1)) {
27 let digit = c.to_digit(16).unwrap() as f64;
28 num += digit * 16f64.powi(-(i + 1));
29 }
30
31 num
32 }
33}
34
35#[derive(Debug)]
36pub struct Decimal {
37 before_decimal: String,
38 after_decimal: String,
39}
40
41#[inline]
42fn normalize_before_decimal(before_decimal: String) -> String {
43 if before_decimal.is_empty() {
44 "0".to_string()
45 } else {
46 before_decimal
47 }
48}
49
50#[inline]
51fn normalize_after_decimal(after_decimal: String) -> String {
52 if after_decimal == "." {
53 ".0".to_string()
54 } else {
55 after_decimal
56 }
57}
58
59impl Decimal {
60 #[inline]
61 fn new(before_decimal: String, after_decimal: String) -> Self {
62 Decimal {
63 before_decimal: normalize_before_decimal(before_decimal),
64 after_decimal: normalize_after_decimal(after_decimal),
65 }
66 }
67}
68
69#[derive(Debug)]
70pub enum Number {
71 Hex(String),
72 HexDecimal(HexDecimal),
73 HexScientific {
74 mantissa: HexDecimal,
75 exponent: String,
76 },
77 Decimal(Decimal),
78 Scientific {
79 mantissa: Decimal,
80 exponent: String,
81 },
82}
83
84impl Number {
85 pub fn from_source(lexer: &mut Lexer) -> Result<Number, String> {
86 let start = lexer.current;
87 let mut digit_encountered = false;
88 let mut decimal_encountered = false;
89 let mut is_scientific = false;
90 let mut is_hex = false;
91 let mut sign_encountered = false;
92
93 let mut before_decimal = String::with_capacity(4);
94 let mut after_decimal = String::with_capacity(4);
95 let mut exponent = String::new();
96
97 while let Some(c) = lexer.advance() {
98 match c {
99 '0'..='9' => {
100 digit_encountered = true;
101 if is_scientific {
102 exponent.push(c);
103 } else if decimal_encountered {
104 after_decimal.push(c);
105 } else {
106 before_decimal.push(c);
107 }
108 }
109 '.' => {
110 if decimal_encountered {
111 return Err(format!(
112 "Error: Malformed number at {}:{}",
113 lexer.line, lexer.column
114 ));
115 }
116
117 decimal_encountered = true;
118 if is_scientific {
119 return Err(format!(
120 "Error: Malformed number at {}:{}",
121 lexer.line, lexer.column
122 ));
123 }
124
125 if is_hex
126 && !digit_encountered
127 && !lexer.peek().map_or(false, |c| c.is_ascii_hexdigit())
128 {
129 return Err(format!(
130 "Error: Malformed number at {}:{}",
131 lexer.line, lexer.column
132 ));
133 }
134
135 if !is_hex
136 && !digit_encountered
137 && !lexer.peek().map_or(false, |c| c.is_ascii_digit())
138 {
139 return Err(format!(
140 "Error: Malformed number at {}:{}",
141 lexer.line, lexer.column
142 ));
143 }
144 after_decimal.push(c);
145 }
146 'x' | 'X' => {
147 if !lexer.look_back().map_or(false, |c| c == '0')
148 || is_scientific
149 || is_hex
150 || !lexer
151 .peek()
152 .map_or(false, |c| c.is_ascii_hexdigit() || c == '.')
153 {
154 return Err(format!(
155 "Error: Malformed number at {}:{}",
156 lexer.line, lexer.column
157 ));
158 }
159 is_hex = true;
160 digit_encountered = false;
161 }
162 'e' | 'E' => {
163 if !is_scientific && is_hex {
164 continue;
165 }
166
167 if !digit_encountered
168 || !lexer
169 .peek()
170 .map_or(false, |c| c.is_ascii_digit() || c == '+' || c == '-')
171 || is_scientific
172 {
173 return Err(format!(
174 "Error: Malformed number at {}:{}",
175 lexer.line, lexer.column
176 ));
177 } else {
178 exponent.push(lexer.advance().unwrap());
179 }
180 is_scientific = true;
181 }
182 'p' | 'P' => {
183 if !is_hex || !digit_encountered || is_scientific {
184 return Err(format!(
185 "Error: Malformed number at {}:{}",
186 lexer.line, lexer.column
187 ));
188 }
189
190 if lexer
191 .peek()
192 .map_or(false, |c| c == '+' || c == '-' || c.is_ascii_digit())
193 {
194 exponent.push(lexer.advance().unwrap());
195 } else {
196 return Err(format!(
197 "Error: Malformed number at {}:{}",
198 lexer.line, lexer.column
199 ));
200 }
201 is_scientific = true;
202 }
203 'a'..='f' | 'A'..='F' => {
204 if !is_hex || is_scientific {
205 return Err(format!(
206 "Error: Malformed number at {}:{}",
207 lexer.line, lexer.column
208 ));
209 }
210 if decimal_encountered {
211 after_decimal.push(c);
212 } else {
213 before_decimal.push(c);
214 }
215 digit_encountered = true;
216 }
217 '+' | '-' => {
218 if !is_scientific {
219 lexer.go_back();
220 break;
221 }
222
223 if sign_encountered {
224 return Err(format!(
225 "Error: Malformed number at {}:{}",
226 lexer.line, lexer.column
227 ));
228 }
229 sign_encountered = true;
230 exponent.push(c);
231 }
232 _ => {
233 lexer.go_back();
234 break;
235 }
236 }
237 }
238
239 if is_scientific
240 && !(lexer.source[lexer.current - 1].is_ascii_digit()
241 || lexer.peek().map_or(false, |c| c.is_ascii_digit()))
242 {
243 return Err(format!(
244 "Error: Malformed number at {}:{}",
245 lexer.line, lexer.column
246 ));
247 }
248
249 if !is_hex && !digit_encountered {
250 return Err(format!(
251 "Error: Malformed number at {}:{}",
252 lexer.line, lexer.column
253 ));
254 }
255
256 let len = lexer.current - start;
257 let lexeme: String = lexer.source[lexer.current - len..lexer.current]
258 .iter()
259 .collect();
260
261 let number = if is_scientific {
262 if is_hex {
263 let mantissa = HexDecimal::new(before_decimal, after_decimal);
264 Number::HexScientific { mantissa, exponent }
265 } else {
266 let mantissa = Decimal::new(before_decimal, after_decimal);
267 Number::Scientific { mantissa, exponent }
268 }
269 } else if is_hex {
270 if decimal_encountered {
271 let hex_decimal = HexDecimal::new(before_decimal[1..].to_string(), after_decimal);
272 Number::HexDecimal(hex_decimal)
273 } else {
274 Number::Hex(lexeme)
275 }
276 } else if decimal_encountered {
277 let decimal = Decimal::new(before_decimal, after_decimal);
278 Number::Decimal(decimal)
279 } else {
280 Number::Decimal(Decimal::new(before_decimal, after_decimal))
281 };
282 Ok(number)
283 }
284
285 pub fn into_clue_number(self) -> String {
286 match self {
287 Number::Hex(hex) => hex,
288 Number::HexDecimal(hex) => {
289 let number = hex.into_decimal().to_string();
290 if number == "inf" {
291 "1 / 0".to_owned()
292 } else if number == "-inf" {
293 "-1 / 0".to_owned()
294 } else {
295 number
296 }
297 }
298 Number::HexScientific { mantissa, exponent } => {
299 let number = mantissa.into_decimal();
300 let number = number * 2f64.powi(exponent.parse::<i32>().unwrap());
301 let number = number.to_string();
302
303 if number == "inf" {
304 "1 / 0".to_owned()
305 } else if number == "-inf" {
306 "-1 / 0".to_owned()
307 } else {
308 number
309 }
310 }
311 Number::Decimal(Decimal {
312 before_decimal,
313 after_decimal,
314 }) => {
315 let mut s = String::with_capacity(4);
316
317 s.push_str(&before_decimal);
318 s.push_str(&after_decimal);
319 s
320 }
321 Number::Scientific { mantissa, exponent } => {
322 let mut s = String::with_capacity(4);
323
324 s.push_str(&mantissa.before_decimal);
325 s.push_str(&mantissa.after_decimal);
326 s.push('e');
327 s.push_str(&exponent);
328 s
329 }
330 }
331 }
332}