swc_ecma_lexer/lexer/
number.rs

1#[cfg(test)]
2mod tests {
3    use std::panic;
4
5    use num_bigint::BigInt as BigIntValue;
6    use swc_atoms::Atom;
7
8    use super::super::*;
9
10    fn lex<F, Ret>(s: &'static str, f: F) -> Ret
11    where
12        F: FnOnce(&mut Lexer<'_>) -> Ret,
13    {
14        crate::with_test_sess(s, |_, input| {
15            let mut l = Lexer::new(
16                Syntax::Es(Default::default()),
17                Default::default(),
18                input,
19                None,
20            );
21            let ret = f(&mut l);
22            assert_eq!(l.input.cur(), None);
23            Ok(ret)
24        })
25        .unwrap()
26    }
27
28    fn num(s: &'static str) -> (f64, Atom) {
29        lex(s, |l| {
30            if s.starts_with('.') {
31                l.read_number::<true, false>().unwrap().left().unwrap()
32            } else if s.starts_with('0') {
33                l.read_number::<false, true>().unwrap().left().unwrap()
34            } else {
35                l.read_number::<false, false>().unwrap().left().unwrap()
36            }
37        })
38    }
39
40    fn int<const RADIX: u8>(s: &'static str) -> u32 {
41        lex(s, |l| {
42            l.read_int_u32::<RADIX>(0)
43                .unwrap()
44                .expect("read_int returned None")
45        })
46    }
47
48    const LONG: &str = "1e10000000000000000000000000000000000000000\
49                        0000000000000000000000000000000000000000000000000000";
50    #[test]
51    fn num_inf() {
52        assert_eq!(num(LONG), (f64::INFINITY, LONG.into()));
53    }
54
55    /// Number >= 2^53
56    #[test]
57    fn num_big_exp() {
58        assert_eq!((1e30, "1e30".into()), num("1e30"));
59    }
60
61    #[test]
62    fn num_very_big_exp() {
63        const LARGE_POSITIVE_EXP: &str =
64            "1e100000000000000000000000000000000000000000000000000000000000000\
65             00000000000000000000000000000000000000000000000000000000000000000\
66             00000000000000000000000000000000000000000000000000000000000000000\
67             00000000000000000000000000000000000000000000000000000000000000000\
68             00000000000000000000000000000000000000000000000000000";
69        const LARGE_NEGATIVE_EXP: &str =
70            "1e-10000000000000000000000000000000000000000000000000000000000000\
71             00000000000000000000000000000000000000000000000000000000000000000\
72             00000000000000000000000000000000000000000000000000000000000000000\
73             00000000000000000000000000000000000000000000000000000000000000000\
74             000000000000000000000000000000000000000000000000000000";
75        const ZERO_WITH_LARGE_POSITIVE_EXP: &str =
76            "0e100000000000000000000000000000000000000000000000000000000000000\
77             00000000000000000000000000000000000000000000000000000000000000000\
78             00000000000000000000000000000000000000000000000000000000000000000\
79             00000000000000000000000000000000000000000000000000000000000000000\
80             00000000000000000000000000000000000000000000000000000";
81        const ZERO_WITH_LARGE_NEGATIVE_EXP: &str =
82            "0e-10000000000000000000000000000000000000000000000000000000000000\
83             00000000000000000000000000000000000000000000000000000000000000000\
84             00000000000000000000000000000000000000000000000000000000000000000\
85             00000000000000000000000000000000000000000000000000000000000000000\
86             000000000000000000000000000000000000000000000000000000";
87        const LARGE_MANTISSA_WITH_LARGE_NEGATIVE_EXP: &str =
88            "10000000000000000000000000000000000000000000000000000000000000\
89             00000000000000000000000000000000000000000000000000000000000000000\
90             00000000000000000000000000000000000000000000000000000000000000000\
91             00000000000000000000000000000000000000000000000000000000000000000\
92             000000000000000000000000000000000000000000000000000000\
93             e-100000000000000000000000000000000000000000000000000000000000000\
94             00000000000000000000000000000000000000000000000000000000000000000\
95             00000000000000000000000000000000000000000000000000000000000000000\
96             00000000000000000000000000000000000000000000000000000000000000000\
97             000000000000000000000000000000000000000000000000000000";
98
99        assert_eq!(
100            num(LARGE_POSITIVE_EXP),
101            (f64::INFINITY, LARGE_POSITIVE_EXP.into())
102        );
103        assert_eq!(num(LARGE_NEGATIVE_EXP), (0.0, LARGE_NEGATIVE_EXP.into()));
104        assert_eq!(
105            num(ZERO_WITH_LARGE_POSITIVE_EXP),
106            (0.0, ZERO_WITH_LARGE_POSITIVE_EXP.into())
107        );
108        assert_eq!(
109            num(ZERO_WITH_LARGE_NEGATIVE_EXP),
110            (0.0, ZERO_WITH_LARGE_NEGATIVE_EXP.into())
111        );
112        assert_eq!(
113            num(LARGE_MANTISSA_WITH_LARGE_NEGATIVE_EXP),
114            (0.0, LARGE_MANTISSA_WITH_LARGE_NEGATIVE_EXP.into())
115        );
116    }
117
118    #[test]
119    fn num_big_many_zero() {
120        assert_eq!(
121            (
122                1_000_000_000_000_000_000_000_000_000_000f64,
123                "1000000000000000000000000000000".into()
124            ),
125            num("1000000000000000000000000000000")
126        );
127        assert_eq!(
128            (3.402_823_466_385_288_6e38, "34028234663852886e22".into()),
129            num("34028234663852886e22"),
130        );
131    }
132
133    #[test]
134    fn big_number_with_fract() {
135        assert_eq!(
136            (77777777777777777.1f64, "77777777777777777.1".into()),
137            num("77777777777777777.1")
138        )
139    }
140
141    #[test]
142    fn issue_480() {
143        assert_eq!((9.09, "9.09".into()), num("9.09"))
144    }
145
146    #[test]
147    fn num_legacy_octal() {
148        assert_eq!((0o12 as f64, "0012".into()), num("0012"));
149        assert_eq!((10f64, "012".into()), num("012"));
150    }
151
152    #[test]
153    fn read_int_1() {
154        assert_eq!(60, int::<10>("60"));
155        assert_eq!(0o73, int::<8>("73"));
156    }
157
158    #[test]
159    fn read_int_short() {
160        assert_eq!(7, int::<10>("7"));
161        assert_eq!(10, int::<10>("10"));
162    }
163
164    #[test]
165    fn read_radix_number() {
166        assert_eq!(
167            (0o73 as f64, "0o73".into()),
168            lex("0o73", |l| l
169                .read_radix_number::<8>()
170                .unwrap()
171                .left()
172                .unwrap())
173        );
174    }
175
176    #[test]
177    fn read_num_sep() {
178        assert_eq!(1_000, int::<10>("1_000"));
179        assert_eq!(0xaebece, int::<16>("AE_BE_CE"));
180        assert_eq!(0b1010000110000101, int::<2>("1010_0001_1000_0101"));
181        assert_eq!(0o0666, int::<8>("0_6_6_6"));
182    }
183
184    #[test]
185    fn read_bigint() {
186        assert_eq!(
187            lex(
188                "10000000000000000000000000000000000000000000000000000n",
189                |l| l.read_number::<false, false>().unwrap().right().unwrap()
190            ),
191            (
192                Box::new(
193                    "10000000000000000000000000000000000000000000000000000"
194                        .parse::<BigIntValue>()
195                        .unwrap()
196                ),
197                Atom::from("10000000000000000000000000000000000000000000000000000n")
198            ),
199        );
200    }
201
202    #[test]
203    fn large_bin_number() {
204        const LONG: &str =
205            "0B11111111111111111111111111111111111111111111111101001010100000010111110001111111111";
206        const VERY_LARGE_BINARY_NUMBER: &str =
207            "0B1111111111111111111111111111111111111111111111111111111111111111\
208             111111111111111111111111111111111111111111111111111111111111111111\
209             111111111111111111111111111111111111111111111111111111111111111111\
210             111111111111111111111111111111111111111111111111111111111111111111\
211             111111111111111111111111111111111111111111111111111111111111111111\
212             111111111111111111111111111111111111111111111111111111111111111111\
213             111111111111111111111111111111111111111111111111111111111111111111\
214             111111111111111111111111111111111111111111111111111111111111111111\
215             111111111111111111111111111111111111111111111111111111111111111111\
216             111111111111111111111111111111111111111111111111111111111111111111\
217             111111111111111111111111111111111111111111111111111111111111111111\
218             111111111111111111111111111111111111111111111111111111111111111111\
219             111111111111111111111111111111111111111111111111111111111111111111\
220             111111111111111111111111111111111111111111111111111111111111111111\
221             111111111111111111111111111111111111111111111111111111111111111111\
222             0010111110001111111111";
223        assert_eq!(
224            lex(LONG, |l| l
225                .read_radix_number::<2>()
226                .unwrap()
227                .left()
228                .unwrap()),
229            (9.671_406_556_917_009e24, LONG.into())
230        );
231        assert_eq!(
232            lex(VERY_LARGE_BINARY_NUMBER, |l| l
233                .read_radix_number::<2>()
234                .unwrap()
235                .left()
236                .unwrap()),
237            (1.0972248137587377e304, VERY_LARGE_BINARY_NUMBER.into())
238        );
239    }
240
241    #[test]
242    fn large_float_number() {
243        const LONG: &str = "9.671406556917009e+24";
244
245        assert_eq!(num(LONG), (9.671_406_556_917_009e24, LONG.into()));
246    }
247
248    /// Valid even on strict mode.
249    const VALID_CASES: &[&str] = &[".0", "0.e-1", "0e8", ".8e1", "0.8e1", "1.18e1"];
250    const INVALID_CASES_ON_STRICT: &[&str] = &["08e1", "08.1", "08.8e1", "08", "01"];
251    const INVALID_CASES: &[&str] = &["01.8e1", "012e1", "00e1", "00.0"];
252
253    fn test_floats(strict: bool, success: bool, cases: &'static [&'static str]) {
254        for case in cases {
255            println!("Testing {case} (when strict = {strict}); Expects success = {success}");
256            // lazy way to get expected values
257            let expected: f64 = (i64::from_str_radix(case, 8).map(|v| v as f64))
258                .or_else(|_| case.parse::<i64>().map(|v| v as f64))
259                .or_else(|_| case.parse::<f64>())
260                .unwrap_or_else(|err| {
261                    panic!("failed to parse '{case}' as float using str.parse(): {err}")
262                });
263
264            let vec = panic::catch_unwind(|| {
265                crate::with_test_sess(case, |_, input| {
266                    let mut l = Lexer::new(Syntax::default(), Default::default(), input, None);
267                    l.ctx.set(Context::Strict, strict);
268                    Ok(l.map(|ts| ts.token).collect::<Vec<_>>())
269                })
270                .unwrap()
271            });
272
273            if success {
274                let vec = match vec {
275                    Ok(vec) => vec,
276                    Err(err) => panic::resume_unwind(err),
277                };
278
279                assert_eq!(vec.len(), 1);
280
281                let token = vec.into_iter().next().unwrap();
282                let value = match token {
283                    Token::Num { value, .. } => value,
284                    _ => {
285                        panic!("expected num token in test")
286                    }
287                };
288
289                assert_eq!(expected, value);
290            } else if let Ok(vec) = vec {
291                assert_ne!(
292                    vec![Token::Num {
293                        value: expected,
294                        raw: expected.to_string().into()
295                    }],
296                    vec
297                )
298            }
299        }
300    }
301
302    //    #[test]
303    //    fn strict_mode() {
304    //        test_floats(true, true, VALID_CASES);
305    //        test_floats(true, false, INVALID_CASES_ON_STRICT);
306    //        test_floats(true, false, INVALID_CASES);
307    //    }
308
309    #[test]
310    fn non_strict() {
311        test_floats(false, true, VALID_CASES);
312        test_floats(false, true, INVALID_CASES_ON_STRICT);
313        test_floats(false, false, INVALID_CASES);
314    }
315}