darklua_core/nodes/expressions/
number.rs

1use std::fmt::{Display, Formatter, Result as FmtResult};
2use std::str::FromStr;
3
4use crate::nodes::{Token, Trivia};
5
6#[derive(Clone, Debug, PartialEq)]
7pub struct DecimalNumber {
8    float: f64,
9    exponent: Option<(i64, bool)>,
10    token: Option<Token>,
11}
12
13impl Eq for DecimalNumber {}
14
15impl DecimalNumber {
16    pub fn new(value: f64) -> Self {
17        Self {
18            float: value,
19            exponent: None,
20            token: None,
21        }
22    }
23
24    pub fn with_token(mut self, token: Token) -> Self {
25        self.token = Some(token);
26        self
27    }
28
29    #[inline]
30    pub fn set_token(&mut self, token: Token) {
31        self.token = Some(token);
32    }
33
34    #[inline]
35    fn get_token(&self) -> Option<&Token> {
36        self.token.as_ref()
37    }
38
39    pub fn with_exponent(mut self, exponent: i64, is_uppercase: bool) -> Self {
40        self.exponent.replace((exponent, is_uppercase));
41        self
42    }
43
44    #[inline]
45    pub fn set_uppercase(&mut self, is_uppercase: bool) {
46        self.exponent = self.exponent.map(|(exponent, _)| (exponent, is_uppercase));
47    }
48
49    #[inline]
50    pub(crate) fn get_raw_float(&self) -> f64 {
51        self.float
52    }
53
54    #[inline]
55    pub fn is_uppercase(&self) -> Option<bool> {
56        self.exponent.map(|(_, uppercase)| uppercase)
57    }
58
59    #[inline]
60    pub fn get_exponent(&self) -> Option<i64> {
61        self.exponent.map(|(exponent, _)| exponent)
62    }
63
64    pub fn compute_value(&self) -> f64 {
65        self.float
66    }
67
68    super::impl_token_fns!(iter = [token]);
69}
70
71#[derive(Clone, Debug, PartialEq, Eq)]
72pub struct HexNumber {
73    integer: u64,
74    exponent: Option<(u32, bool)>,
75    is_x_uppercase: bool,
76    token: Option<Token>,
77}
78
79impl HexNumber {
80    pub fn new(integer: u64, is_x_uppercase: bool) -> Self {
81        Self {
82            integer,
83            exponent: None,
84            is_x_uppercase,
85            token: None,
86        }
87    }
88
89    pub fn with_token(mut self, token: Token) -> Self {
90        self.token = Some(token);
91        self
92    }
93
94    #[inline]
95    pub fn set_token(&mut self, token: Token) {
96        self.token = Some(token);
97    }
98
99    #[inline]
100    fn get_token(&self) -> Option<&Token> {
101        self.token.as_ref()
102    }
103
104    pub fn with_exponent(mut self, exponent: u32, is_uppercase: bool) -> Self {
105        self.exponent.replace((exponent, is_uppercase));
106        self
107    }
108
109    pub fn set_uppercase(&mut self, is_uppercase: bool) {
110        self.exponent = self.exponent.map(|(value, _)| (value, is_uppercase));
111        self.is_x_uppercase = is_uppercase;
112    }
113
114    #[inline]
115    pub fn is_x_uppercase(&self) -> bool {
116        self.is_x_uppercase
117    }
118
119    #[inline]
120    pub fn is_exponent_uppercase(&self) -> Option<bool> {
121        self.exponent.map(|(_, uppercase)| uppercase)
122    }
123
124    #[inline]
125    pub fn get_raw_integer(&self) -> u64 {
126        self.integer
127    }
128
129    #[inline]
130    pub fn get_exponent(&self) -> Option<u32> {
131        self.exponent.map(|(value, _)| value)
132    }
133
134    pub fn compute_value(&self) -> f64 {
135        if let Some((exponent, _)) = self.exponent {
136            (self.integer * 2_u64.pow(exponent)) as f64
137        } else {
138            self.integer as f64
139        }
140    }
141
142    super::impl_token_fns!(iter = [token]);
143}
144
145#[derive(Clone, Debug, PartialEq, Eq)]
146pub struct BinaryNumber {
147    value: u64,
148    is_b_uppercase: bool,
149    token: Option<Token>,
150}
151
152impl BinaryNumber {
153    pub fn new(value: u64, is_b_uppercase: bool) -> Self {
154        Self {
155            value,
156            is_b_uppercase,
157            token: None,
158        }
159    }
160
161    pub fn with_token(mut self, token: Token) -> Self {
162        self.token = Some(token);
163        self
164    }
165
166    #[inline]
167    pub fn set_token(&mut self, token: Token) {
168        self.token = Some(token);
169    }
170
171    #[inline]
172    fn get_token(&self) -> Option<&Token> {
173        self.token.as_ref()
174    }
175
176    pub fn set_uppercase(&mut self, is_uppercase: bool) {
177        self.is_b_uppercase = is_uppercase;
178    }
179
180    #[inline]
181    pub fn is_b_uppercase(&self) -> bool {
182        self.is_b_uppercase
183    }
184
185    pub fn compute_value(&self) -> f64 {
186        self.value as f64
187    }
188
189    #[inline]
190    pub fn get_raw_value(&self) -> u64 {
191        self.value
192    }
193
194    super::impl_token_fns!(iter = [token]);
195}
196
197#[derive(Clone, Debug, PartialEq, Eq)]
198pub enum NumberExpression {
199    Decimal(DecimalNumber),
200    Hex(HexNumber),
201    Binary(BinaryNumber),
202}
203
204impl NumberExpression {
205    pub fn set_uppercase(&mut self, is_uppercase: bool) {
206        match self {
207            Self::Decimal(number) => number.set_uppercase(is_uppercase),
208            Self::Hex(number) => number.set_uppercase(is_uppercase),
209            Self::Binary(number) => number.set_uppercase(is_uppercase),
210        }
211    }
212
213    pub fn compute_value(&self) -> f64 {
214        match self {
215            Self::Decimal(decimal) => decimal.compute_value(),
216            Self::Hex(hex) => hex.compute_value(),
217            Self::Binary(binary) => binary.compute_value(),
218        }
219    }
220
221    pub fn with_token(mut self, token: Token) -> Self {
222        match &mut self {
223            NumberExpression::Decimal(number) => number.set_token(token),
224            NumberExpression::Hex(number) => number.set_token(token),
225            NumberExpression::Binary(number) => number.set_token(token),
226        }
227        self
228    }
229
230    #[inline]
231    pub fn set_token(&mut self, token: Token) {
232        match self {
233            NumberExpression::Decimal(number) => number.set_token(token),
234            NumberExpression::Hex(number) => number.set_token(token),
235            NumberExpression::Binary(number) => number.set_token(token),
236        }
237    }
238
239    #[inline]
240    pub fn get_token(&self) -> Option<&Token> {
241        match self {
242            NumberExpression::Decimal(number) => number.get_token(),
243            NumberExpression::Hex(number) => number.get_token(),
244            NumberExpression::Binary(number) => number.get_token(),
245        }
246    }
247
248    pub fn clear_comments(&mut self) {
249        match self {
250            NumberExpression::Decimal(number) => number.clear_comments(),
251            NumberExpression::Hex(number) => number.clear_comments(),
252            NumberExpression::Binary(number) => number.clear_comments(),
253        }
254    }
255
256    pub fn clear_whitespaces(&mut self) {
257        match self {
258            NumberExpression::Decimal(number) => number.clear_whitespaces(),
259            NumberExpression::Hex(number) => number.clear_whitespaces(),
260            NumberExpression::Binary(number) => number.clear_whitespaces(),
261        }
262    }
263
264    pub(crate) fn replace_referenced_tokens(&mut self, code: &str) {
265        match self {
266            NumberExpression::Decimal(number) => number.replace_referenced_tokens(code),
267            NumberExpression::Hex(number) => number.replace_referenced_tokens(code),
268            NumberExpression::Binary(number) => number.replace_referenced_tokens(code),
269        }
270    }
271
272    pub(crate) fn shift_token_line(&mut self, amount: isize) {
273        match self {
274            NumberExpression::Decimal(number) => number.shift_token_line(amount),
275            NumberExpression::Hex(number) => number.shift_token_line(amount),
276            NumberExpression::Binary(number) => number.shift_token_line(amount),
277        }
278    }
279
280    pub(crate) fn filter_comments(&mut self, filter: impl Fn(&Trivia) -> bool) {
281        match self {
282            NumberExpression::Decimal(number) => number.filter_comments(filter),
283            NumberExpression::Hex(number) => number.filter_comments(filter),
284            NumberExpression::Binary(number) => number.filter_comments(filter),
285        }
286    }
287}
288
289impl From<DecimalNumber> for NumberExpression {
290    fn from(number: DecimalNumber) -> Self {
291        Self::Decimal(number)
292    }
293}
294
295impl From<HexNumber> for NumberExpression {
296    fn from(number: HexNumber) -> Self {
297        Self::Hex(number)
298    }
299}
300
301impl From<BinaryNumber> for NumberExpression {
302    fn from(number: BinaryNumber) -> Self {
303        Self::Binary(number)
304    }
305}
306
307#[derive(Debug, Clone, PartialEq, Eq)]
308pub enum NumberParsingError {
309    InvalidHexadecimalNumber,
310    InvalidHexadecimalExponent,
311    InvalidDecimalNumber,
312    InvalidDecimalExponent,
313    InvalidBinaryNumber,
314}
315
316impl Display for NumberParsingError {
317    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
318        use NumberParsingError::*;
319
320        match self {
321            InvalidHexadecimalNumber => write!(f, "could not parse hexadecimal number"),
322            InvalidHexadecimalExponent => write!(f, "could not parse hexadecimal exponent"),
323            InvalidDecimalNumber => write!(f, "could not parse decimal number"),
324            InvalidDecimalExponent => write!(f, "could not parse decimal exponent"),
325            InvalidBinaryNumber => write!(f, "could not parse binary number"),
326        }
327    }
328}
329
330fn filter_underscore(number: &str) -> String {
331    number.chars().filter(|c| c != &'_').collect()
332}
333
334impl FromStr for NumberExpression {
335    type Err = NumberParsingError;
336
337    fn from_str(value: &str) -> Result<Self, Self::Err> {
338        let notation_prefix = value
339            .char_indices()
340            .filter(|(_, c)| *c != '_')
341            .take(2)
342            .nth(1);
343        let starts_with_zero = value.starts_with('0');
344
345        let number = match (starts_with_zero, notation_prefix) {
346            (true, Some((position, notation))) if matches!(notation, 'x' | 'X' | 'b' | 'B') => {
347                let is_uppercase = notation.is_uppercase();
348
349                if notation == 'x' || notation == 'X' {
350                    if let Some((exponent_is_uppercase, index)) = value
351                        .find('p')
352                        .map(|index| (false, index))
353                        .or_else(|| value.find('P').map(|index| (true, index)))
354                    {
355                        let exponent = value
356                            .get(index + 1..)
357                            .and_then(|string| string.parse().ok())
358                            .ok_or(Self::Err::InvalidHexadecimalExponent)?;
359                        let before_exponent = value.get(position + 1..index).unwrap();
360                        let number = u64::from_str_radix(before_exponent, 16)
361                            .map_err(|_| Self::Err::InvalidHexadecimalNumber)?;
362
363                        HexNumber::new(number, is_uppercase)
364                            .with_exponent(exponent, exponent_is_uppercase)
365                    } else {
366                        let filtered = filter_underscore(value.get(position + 1..).unwrap());
367                        let number = u64::from_str_radix(&filtered, 16)
368                            .map_err(|_| Self::Err::InvalidHexadecimalNumber)?;
369
370                        HexNumber::new(number, is_uppercase)
371                    }
372                    .into()
373                } else if notation == 'b' || notation == 'B' {
374                    let filtered = filter_underscore(value.get(position + 1..).unwrap());
375                    let number = u64::from_str_radix(&filtered, 2)
376                        .map_err(|_| Self::Err::InvalidBinaryNumber)?;
377
378                    BinaryNumber::new(number, is_uppercase).into()
379                } else {
380                    unreachable!()
381                }
382            }
383            _ => {
384                // in Luau, underscores are valid everywhere in a number except
385                // after a `.`
386                if value.starts_with("._") {
387                    return Err(Self::Err::InvalidDecimalNumber);
388                }
389
390                if let Some((exponent_is_uppercase, index)) = value
391                    .find('e')
392                    .map(|index| (false, index))
393                    .or_else(|| value.find('E').map(|index| (true, index)))
394                {
395                    // in Luau, underscores are not valid before the exponent sign
396                    if value.contains("_-") || value.contains("_+") {
397                        return Err(Self::Err::InvalidDecimalExponent);
398                    }
399
400                    let exponent = value
401                        .get(index + 1..)
402                        .map(filter_underscore)
403                        .and_then(|string| string.parse().ok())
404                        .ok_or(Self::Err::InvalidDecimalExponent)?;
405                    let _number: f64 = value
406                        .get(0..index)
407                        .map(filter_underscore)
408                        .and_then(|string| string.parse().ok())
409                        .ok_or(Self::Err::InvalidDecimalNumber)?;
410
411                    DecimalNumber::new(
412                        filter_underscore(value)
413                            .parse::<f64>()
414                            .map_err(|_| Self::Err::InvalidDecimalNumber)?,
415                    )
416                    .with_exponent(exponent, exponent_is_uppercase)
417                } else {
418                    let number = filter_underscore(value)
419                        .parse::<f64>()
420                        .map_err(|_| Self::Err::InvalidDecimalNumber)?;
421
422                    DecimalNumber::new(number)
423                }
424                .into()
425            }
426        };
427
428        Ok(number)
429    }
430}
431
432#[cfg(test)]
433mod test {
434    use super::*;
435
436    mod decimal {
437        use super::*;
438
439        #[test]
440        fn can_set_uppercase_to_number_without_exponent() {
441            let mut number = DecimalNumber::new(1.0);
442            number.set_uppercase(true);
443            number.set_uppercase(false);
444
445            assert_eq!(number.is_uppercase(), None);
446        }
447
448        #[test]
449        fn set_uppercase_change() {
450            let initial_case = true;
451            let modified_case = !initial_case;
452            let mut number = DecimalNumber::new(1.0).with_exponent(2, initial_case);
453
454            number.set_uppercase(modified_case);
455
456            assert_eq!(number.is_uppercase(), Some(modified_case));
457        }
458    }
459
460    mod hex {
461        use super::*;
462
463        #[test]
464        fn set_uppercase_change() {
465            let initial_case = true;
466            let modified_case = !initial_case;
467            let mut number = HexNumber::new(1, initial_case);
468
469            number.set_uppercase(modified_case);
470
471            assert_eq!(number.is_x_uppercase(), modified_case);
472        }
473    }
474
475    mod binary {
476        use super::*;
477
478        #[test]
479        fn set_uppercase_change() {
480            let initial_case = true;
481            let modified_case = !initial_case;
482            let mut number = BinaryNumber::new(1, initial_case);
483
484            number.set_uppercase(modified_case);
485
486            assert_eq!(number.is_b_uppercase(), modified_case);
487        }
488    }
489
490    mod parse_number {
491        use super::*;
492
493        macro_rules! test_numbers {
494            ($($name:ident($input:literal) => $expect:expr),+ $(,)?) => {
495                $(
496                    #[test]
497                    fn $name() {
498                        let result: NumberExpression = $input.parse()
499                            .expect("should be a valid number");
500
501                        let expect: NumberExpression = $expect.into();
502
503                        assert_eq!(result, expect);
504                    }
505                )+
506            };
507        }
508
509        macro_rules! test_parse_errors {
510            ($($name:ident($input:literal) => $expect:expr),+ $(,)?) => {
511                $(
512                    #[test]
513                    fn $name() {
514                        let result = $input.parse::<NumberExpression>()
515                            .expect_err("should be an invalid number");
516
517                        assert_eq!(result, $expect);
518                    }
519                )+
520            };
521        }
522
523        test_numbers!(
524            parse_zero("0") => DecimalNumber::new(0_f64),
525            parse_integer("123") => DecimalNumber::new(123_f64),
526            parse_integer_with_underscore_delimiter("123_456") => DecimalNumber::new(123_456_f64),
527            parse_multiple_decimal("123.24") => DecimalNumber::new(123.24_f64),
528            parse_multiple_decimal_with_underscore("123.245_6") => DecimalNumber::new(123.245_6_f64),
529            parse_multiple_decimal_with_underscore_after_point("0._24") => DecimalNumber::new(0.24_f64),
530            parse_float_with_trailing_dot("123.") => DecimalNumber::new(123_f64),
531            parse_starting_with_dot(".123") => DecimalNumber::new(0.123_f64),
532            parse_digit_with_exponent("1e10") => DecimalNumber::new(1e10_f64).with_exponent(10, false),
533            parse_digit_with_exponent_and_underscore("1e_10") => DecimalNumber::new(1e10_f64).with_exponent(10, false),
534            parse_number_with_exponent("123e101") => DecimalNumber::new(123e101_f64).with_exponent(101, false),
535            parse_number_with_exponent_and_plus_symbol("123e+121") => DecimalNumber::new(123e121_f64).with_exponent(121, false),
536            parse_number_with_negative_exponent("123e-456") => DecimalNumber::new(123e-456_f64).with_exponent(-456, false),
537            parse_number_with_upper_exponent("123E4") => DecimalNumber::new(123e4_f64).with_exponent(4, true),
538            parse_number_with_upper_negative_exponent("123E-456") => DecimalNumber::new(123e-456_f64).with_exponent(-456, true),
539            parse_float_with_exponent("10.12e8") => DecimalNumber::new(10.12e8_f64).with_exponent(8, false),
540            parse_float_with_exponent_and_underscores("10_0.12_e_8") => DecimalNumber::new(100.12e8_f64).with_exponent(8, false),
541            parse_float_with_exponent_2("4.6982573308436185e159") => DecimalNumber::new(4.6982573308436185e159_f64).with_exponent(159, false),
542            parse_trailing_dot_with_exponent("10.e8") => DecimalNumber::new(10e8_f64).with_exponent(8, false),
543            parse_hex_number("0x12") => HexNumber::new(18, false),
544            parse_hex_number_with_underscore_before_x("0_x12") => HexNumber::new(18, false),
545            parse_hex_number_with_underscores_around_x("0_x_12") => HexNumber::new(18, false),
546            parse_hex_number_with_underscore("0x12_13") => HexNumber::new(0x1213, false),
547            parse_uppercase_hex_number("0X12") => HexNumber::new(18, true),
548            parse_uppercase_hex_number_with_underscore_before_x("0_X13") => HexNumber::new(19, true),
549            parse_hex_number_with_lowercase("0x12a") => HexNumber::new(298, false),
550            parse_hex_number_with_uppercase("0x12A") => HexNumber::new(298, false),
551            parse_hex_number_with_mixed_case("0x1bF2A") => HexNumber::new(114_474, false),
552            parse_hex_with_exponent("0x12p4") => HexNumber::new(18, false).with_exponent(4, false),
553            parse_hex_with_exponent_uppercase("0xABP3") => HexNumber::new(171, false).with_exponent(3, true),
554            parse_binary_zero("0b0") => BinaryNumber::new(0, false),
555            parse_binary_zero_with_underscore_before_b("0_b1") => BinaryNumber::new(1, false),
556            parse_binary_zero_with_underscore("0b1010_1100") => BinaryNumber::new(0b1010_1100, false),
557            parse_binary_zero_uppercase("0B0") => BinaryNumber::new(0, true),
558            parse_binary_zero_uppercase_with_underscore_before_b("0_B1") => BinaryNumber::new(1, true),
559        );
560
561        test_parse_errors!(
562            parse_empty_string("") => NumberParsingError::InvalidDecimalNumber,
563            missing_exponent_value("1e") => NumberParsingError::InvalidDecimalExponent,
564            missing_exponent_value_uppercase("1E") => NumberParsingError::InvalidDecimalExponent,
565            invalid_underscore_position("._1") => NumberParsingError::InvalidDecimalNumber,
566            missing_negative_exponent_value("1e-") => NumberParsingError::InvalidDecimalExponent,
567            missing_negative_exponent_value_uppercase("1E-") => NumberParsingError::InvalidDecimalExponent,
568            invalid_underscore_before_negative_exponent("1e_-1") => NumberParsingError::InvalidDecimalExponent,
569            invalid_underscore_before_positive_exponent("1e_+1") => NumberParsingError::InvalidDecimalExponent,
570            invalid_underscore_before_negative_exponent_uppercase("1E_-1") => NumberParsingError::InvalidDecimalExponent,
571            invalid_underscore_before_positive_exponent_uppercase("1E_+1") => NumberParsingError::InvalidDecimalExponent,
572            missing_hex_exponent_value("0x1p") => NumberParsingError::InvalidHexadecimalExponent,
573            negative_hex_exponent_value("0x1p-3") => NumberParsingError::InvalidHexadecimalExponent,
574            missing_hex_exponent_value_uppercase("0x1P") => NumberParsingError::InvalidHexadecimalExponent,
575            invalid_hex_exponent_value("0x1p1Z") => NumberParsingError::InvalidHexadecimalExponent,
576            invalid_hex_exponent_value_uppercase("0x1P1Z") => NumberParsingError::InvalidHexadecimalExponent,
577            negative_hex_exponent_value_uppercase("0x1P-3") => NumberParsingError::InvalidHexadecimalExponent,
578            invalid_digit_in_binary("0b190") => NumberParsingError::InvalidBinaryNumber,
579            invalid_digit_in_binary_uppercase("0B190") => NumberParsingError::InvalidBinaryNumber,
580        );
581    }
582
583    mod compute_value {
584        use super::*;
585
586        macro_rules! test_compute_value {
587            ($($name:ident($input:literal) => $value:expr),* $(,)?) => {
588                $(
589                    #[test]
590                    fn $name() {
591                        let number = NumberExpression::from_str($input)
592                            .expect(&format!("unable to parse `{}`", $input));
593                        assert!((number.compute_value() - $value as f64).abs() < f64::EPSILON);
594                    }
595                )*
596            };
597        }
598
599        test_compute_value!(
600            zero("0") => 0,
601            one("1") => 1,
602            integer("123") => 123,
603            multiple_decimal("0.512") => 0.512,
604            integer_with_multiple_decimal("54.512") => 54.512,
605            digit_with_exponent("1e5") => 1e5,
606            number_with_exponent("123e4") => 123e4,
607            number_with_negative_exponent("123e-4") => 123e-4,
608            float_with_exponent("10.5e2") => 10.5e2,
609            hex_number("0x12") => 0x12,
610            hex_number_with_letter("0x12a") => 0x12a,
611            hex_with_exponent("0x12p4") => 0x120,
612            binary_zero("0b0") => 0b0,
613            binary_ten("0b1010") => 0b1010,
614        );
615    }
616}