darklua_core/nodes/expressions/
number.rs

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