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