Skip to main content

solar_parse/parser/
lit.rs

1use crate::{PResult, Parser, unescape};
2use alloy_primitives::U256;
3use num_bigint::{BigInt, BigUint};
4use num_rational::Ratio;
5use num_traits::{Num, Signed, Zero};
6use solar_ast::{token::*, *};
7use solar_interface::{ByteSymbol, Session, Symbol, diagnostics::ErrorGuaranteed, kw};
8use std::{borrow::Cow, fmt};
9
10impl<'sess, 'ast, 'cb> Parser<'sess, 'ast, 'cb> {
11    /// Parses a literal with an optional subdenomination.
12    ///
13    /// Note that the subdenomination gets applied to the literal directly, and is returned just for
14    /// display reasons.
15    ///
16    /// Returns None if no subdenomination was parsed or if the literal is not a number or rational.
17    #[instrument(level = "trace", skip_all)]
18    pub fn parse_lit(
19        &mut self,
20        with_subdenomination: bool,
21    ) -> PResult<'sess, (Lit<'ast>, Option<SubDenomination>)> {
22        self.parse_spanned(|this| this.parse_lit_inner(with_subdenomination)).map(
23            |(span, (symbol, kind, subdenomination))| (Lit { span, symbol, kind }, subdenomination),
24        )
25    }
26
27    /// Parses a subdenomination.
28    pub fn parse_subdenomination(&mut self) -> Option<SubDenomination> {
29        let sub = self.subdenomination();
30        if sub.is_some() {
31            self.bump();
32        }
33        sub
34    }
35
36    fn subdenomination(&self) -> Option<SubDenomination> {
37        match self.token.ident()?.name {
38            kw::Wei => Some(SubDenomination::Ether(EtherSubDenomination::Wei)),
39            kw::Gwei => Some(SubDenomination::Ether(EtherSubDenomination::Gwei)),
40            kw::Ether => Some(SubDenomination::Ether(EtherSubDenomination::Ether)),
41
42            kw::Seconds => Some(SubDenomination::Time(TimeSubDenomination::Seconds)),
43            kw::Minutes => Some(SubDenomination::Time(TimeSubDenomination::Minutes)),
44            kw::Hours => Some(SubDenomination::Time(TimeSubDenomination::Hours)),
45            kw::Days => Some(SubDenomination::Time(TimeSubDenomination::Days)),
46            kw::Weeks => Some(SubDenomination::Time(TimeSubDenomination::Weeks)),
47            kw::Years => Some(SubDenomination::Time(TimeSubDenomination::Years)),
48
49            _ => None,
50        }
51    }
52
53    /// Emits an error if a subdenomination was parsed.
54    pub(super) fn expect_no_subdenomination(&mut self) {
55        if let Some(_sub) = self.parse_subdenomination() {
56            let span = self.prev_token.span;
57            self.dcx().emit_err(span, "subdenominations aren't allowed here");
58        }
59    }
60
61    fn parse_lit_inner(
62        &mut self,
63        with_subdenomination: bool,
64    ) -> PResult<'sess, (Symbol, LitKind<'ast>, Option<SubDenomination>)> {
65        let lo = self.token.span;
66        if let TokenKind::Ident(symbol @ (kw::True | kw::False)) = self.token.kind {
67            self.bump();
68            let mut subdenomination =
69                if with_subdenomination { self.parse_subdenomination() } else { None };
70            self.subdenom_error(&mut subdenomination, lo.to(self.prev_token.span));
71            return Ok((symbol, LitKind::Bool(symbol != kw::False), subdenomination));
72        }
73
74        if !self.check_lit() {
75            return self.unexpected();
76        }
77
78        let Some(lit) = self.token.lit() else {
79            unreachable!("check_lit() returned true for non-literal token");
80        };
81        self.bump();
82        let mut subdenomination =
83            if with_subdenomination { self.parse_subdenomination() } else { None };
84        let result = match lit.kind {
85            TokenLitKind::Integer => self.parse_lit_int(lit.symbol, subdenomination),
86            TokenLitKind::Rational => self.parse_lit_rational(lit.symbol, subdenomination),
87            TokenLitKind::Str | TokenLitKind::UnicodeStr | TokenLitKind::HexStr => {
88                self.subdenom_error(&mut subdenomination, lo.to(self.prev_token.span));
89                self.parse_lit_str(lit)
90            }
91            TokenLitKind::Err(guar) => Ok(LitKind::Err(guar)),
92        };
93        let kind =
94            result.unwrap_or_else(|e| LitKind::Err(e.span(lo.to(self.prev_token.span)).emit()));
95        if matches!(kind, LitKind::Err(_)) {
96            subdenomination = None;
97        }
98        Ok((lit.symbol, kind, subdenomination))
99    }
100
101    fn subdenom_error(&mut self, slot: &mut Option<SubDenomination>, span: Span) {
102        if slot.is_some() {
103            *slot = None;
104            let msg = "sub-denominations are only allowed on number and rational literals";
105            self.dcx().emit_err(span, msg);
106        }
107    }
108
109    /// Parses an integer literal.
110    fn parse_lit_int(
111        &mut self,
112        symbol: Symbol,
113        subdenomination: Option<SubDenomination>,
114    ) -> PResult<'sess, LitKind<'ast>> {
115        use LitError::*;
116        match parse_integer(self.sess, symbol, subdenomination) {
117            Ok(l) => Ok(l),
118            // User error.
119            Err(e @ (IntegerLeadingZeros | IntegerTooLarge)) => Err(self.dcx().err(e.to_string())),
120            // User error, but already emitted.
121            Err(EmptyInteger) => Ok(LitKind::Err(ErrorGuaranteed::new_unchecked())),
122            // Lexer internal error.
123            Err(e @ ParseInteger(_)) => panic!("failed to parse integer literal {symbol:?}: {e}"),
124            // Should never happen.
125            Err(
126                e @ (InvalidRational | RationalPlusExponent | EmptyRational | EmptyExponent
127                | ParseRational(_) | ParseExponent(_) | RationalTooLarge | ExponentTooLarge),
128            ) => panic!("this error shouldn't happen for normal integer literals: {e}"),
129        }
130    }
131
132    /// Parses a rational literal.
133    fn parse_lit_rational(
134        &mut self,
135        symbol: Symbol,
136        subdenomination: Option<SubDenomination>,
137    ) -> PResult<'sess, LitKind<'ast>> {
138        use LitError::*;
139        match parse_rational(self.sess, symbol, subdenomination) {
140            Ok(l) => Ok(l),
141            // User error.
142            Err(
143                e @ (EmptyRational | RationalPlusExponent | IntegerTooLarge | RationalTooLarge
144                | ExponentTooLarge | IntegerLeadingZeros),
145            ) => Err(self.dcx().err(e.to_string())),
146            // User error, but already emitted.
147            Err(InvalidRational | EmptyExponent) => {
148                Ok(LitKind::Err(ErrorGuaranteed::new_unchecked()))
149            }
150            // Lexer internal error.
151            Err(e @ (ParseExponent(_) | ParseInteger(_) | ParseRational(_) | EmptyInteger)) => {
152                panic!("failed to parse rational literal {symbol:?}: {e}")
153            }
154        }
155    }
156
157    /// Parses a string literal.
158    fn parse_lit_str(&mut self, lit: TokenLit) -> PResult<'sess, LitKind<'ast>> {
159        let mode = match lit.kind {
160            TokenLitKind::Str => StrKind::Str,
161            TokenLitKind::UnicodeStr => StrKind::Unicode,
162            TokenLitKind::HexStr => StrKind::Hex,
163            _ => unreachable!(),
164        };
165
166        let span = self.prev_token.span;
167        let (mut value, _) =
168            unescape::parse_string_literal(lit.symbol.as_str(), mode, span, self.sess);
169        let mut extra = vec![];
170        while let Some(TokenLit { symbol, kind }) = self.token.lit() {
171            if kind != lit.kind {
172                break;
173            }
174            extra.push((self.token.span, symbol));
175            let (parsed, _) =
176                unescape::parse_string_literal(symbol.as_str(), mode, self.token.span, self.sess);
177            value.to_mut().extend_from_slice(&parsed);
178            self.bump();
179        }
180
181        let kind = match lit.kind {
182            TokenLitKind::Str => StrKind::Str,
183            TokenLitKind::UnicodeStr => StrKind::Unicode,
184            TokenLitKind::HexStr => StrKind::Hex,
185            _ => unreachable!(),
186        };
187        let extra = self.alloc_vec(extra);
188        Ok(LitKind::Str(kind, ByteSymbol::intern(&value), extra))
189    }
190}
191
192#[derive(Debug, PartialEq, Eq)]
193enum LitError {
194    InvalidRational,
195
196    EmptyInteger,
197    EmptyRational,
198    EmptyExponent,
199    RationalPlusExponent,
200
201    ParseInteger(num_bigint::ParseBigIntError),
202    ParseRational(num_bigint::ParseBigIntError),
203    ParseExponent(std::num::ParseIntError),
204
205    IntegerTooLarge,
206    RationalTooLarge,
207    ExponentTooLarge,
208    IntegerLeadingZeros,
209}
210
211impl fmt::Display for LitError {
212    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213        match self {
214            Self::InvalidRational => write!(f, "invalid rational literal"),
215            Self::EmptyInteger => write!(f, "empty integer"),
216            Self::EmptyRational => write!(f, "empty rational"),
217            Self::EmptyExponent => write!(f, "empty exponent"),
218            Self::RationalPlusExponent => write!(f, "`+` is not allowed in the exponent"),
219            Self::ParseInteger(e) => write!(f, "failed to parse integer: {e}"),
220            Self::ParseRational(e) => write!(f, "failed to parse rational: {e}"),
221            Self::ParseExponent(e) => write!(f, "failed to parse exponent: {e}"),
222            Self::IntegerTooLarge => write!(f, "integer part too large"),
223            Self::RationalTooLarge => write!(f, "rational part too large"),
224            Self::ExponentTooLarge => write!(f, "exponent too large"),
225            Self::IntegerLeadingZeros => write!(f, "leading zeros are not allowed in integers"),
226        }
227    }
228}
229
230fn parse_integer<'ast>(
231    sess: &Session,
232    symbol: Symbol,
233    subdenomination: Option<SubDenomination>,
234) -> Result<LitKind<'ast>, LitError> {
235    let s = &strip_underscores(symbol, sess)[..];
236    let base = match s.as_bytes() {
237        [b'0', b'x', ..] => Base::Hexadecimal,
238        [b'0', b'o', ..] => Base::Octal,
239        [b'0', b'b', ..] => Base::Binary,
240        _ => Base::Decimal,
241    };
242
243    if base == Base::Decimal && s.starts_with('0') && s.len() > 1 {
244        return Err(LitError::IntegerLeadingZeros);
245    }
246
247    // Address literal.
248    if base == Base::Hexadecimal
249        && s.len() == 42
250        && let Ok(address) = s.parse()
251    {
252        return Ok(LitKind::Address(address));
253    }
254
255    let start = if base == Base::Decimal { 0 } else { 2 };
256    let s = &s[start..];
257    if s.is_empty() {
258        return Err(LitError::EmptyInteger);
259    }
260    let mut n = u256_from_str_radix(s, base)?;
261    if let Some(subdenomination) = subdenomination {
262        n = n.checked_mul(U256::from(subdenomination.value())).ok_or(LitError::IntegerTooLarge)?;
263    }
264    Ok(LitKind::Number(n))
265}
266
267fn parse_rational<'ast>(
268    sess: &Session,
269    symbol: Symbol,
270    subdenomination: Option<SubDenomination>,
271) -> Result<LitKind<'ast>, LitError> {
272    let s = &strip_underscores(symbol, sess)[..];
273    debug_assert!(!s.is_empty());
274
275    if matches!(s.get(..2), Some("0b" | "0o" | "0x")) {
276        return Err(LitError::InvalidRational);
277    }
278
279    // Split the string into integer, rational, and exponent parts.
280    let (mut int, rat, exp) = match (s.find('.'), s.find(['e', 'E'])) {
281        // X
282        (None, None) => (s, None, None),
283        // X.Y
284        (Some(dot), None) => {
285            let (int, rat) = split_at_exclusive(s, dot);
286            (int, Some(rat), None)
287        }
288        // XeZ
289        (None, Some(exp)) => {
290            let (int, exp) = split_at_exclusive(s, exp);
291            (int, None, Some(exp))
292        }
293        // X.YeZ
294        (Some(dot), Some(exp)) => {
295            if exp < dot {
296                return Err(LitError::InvalidRational);
297            }
298            let (int, rest) = split_at_exclusive(s, dot);
299            let (rat, exp) = split_at_exclusive(rest, exp - dot - 1);
300            (int, Some(rat), Some(exp))
301        }
302    };
303
304    if cfg!(debug_assertions) {
305        let mut reconstructed = String::from(int);
306        if let Some(rat) = rat {
307            reconstructed.push('.');
308            reconstructed.push_str(rat);
309        }
310        if let Some(exp) = exp {
311            let e = if s.contains('E') { 'E' } else { 'e' };
312            reconstructed.push(e);
313            reconstructed.push_str(exp);
314        }
315        assert_eq!(reconstructed, s, "{int:?} + {rat:?} + {exp:?}");
316    }
317
318    // `int` is allowed to be empty: `.1e1` is the same as `0.1e1`.
319    if int.is_empty() {
320        int = "0";
321    }
322    if rat.is_some_and(str::is_empty) {
323        return Err(LitError::EmptyRational);
324    }
325    if let Some(exp) = exp {
326        if exp.starts_with('+') {
327            return Err(LitError::RationalPlusExponent);
328        }
329        if matches!(exp, "" | "-") {
330            return Err(LitError::EmptyExponent);
331        }
332    }
333
334    if int.starts_with('0') && int.len() > 1 {
335        return Err(LitError::IntegerLeadingZeros);
336    }
337    // NOTE: leading zeros are allowed in the rational and exponent parts.
338
339    let rat = rat.map(|rat| rat.trim_end_matches('0'));
340    let (int_s, is_rat) = match rat {
341        Some(rat) => (&[int, rat].concat()[..], true),
342        None => (int, false),
343    };
344    let int = big_int_from_str_radix(int_s, Base::Decimal, is_rat)?;
345
346    let fract_len = rat.map_or(0, str::len);
347    let fract_len = u16::try_from(fract_len).map_err(|_| LitError::RationalTooLarge)?;
348    let denominator = pow10(fract_len as u32);
349    let mut number = Ratio::new(int, denominator);
350
351    // 0E... is always zero.
352    if number.is_zero() {
353        return Ok(LitKind::Number(U256::ZERO));
354    }
355
356    if let Some(exp) = exp {
357        let exp = exp.parse::<i32>().map_err(|e| match *e.kind() {
358            std::num::IntErrorKind::PosOverflow | std::num::IntErrorKind::NegOverflow => {
359                LitError::ExponentTooLarge
360            }
361            _ => LitError::ParseExponent(e),
362        })?;
363        let exp_abs = exp.unsigned_abs();
364        let power = || pow10(exp_abs);
365        if exp.is_negative() {
366            if !fits_precision_base_10(&number.denom().abs().into_parts().1, exp_abs) {
367                return Err(LitError::ExponentTooLarge);
368            }
369            number /= power();
370        } else if exp > 0 {
371            if !fits_precision_base_10(&number.numer().abs().into_parts().1, exp_abs) {
372                return Err(LitError::ExponentTooLarge);
373            }
374            number *= power();
375        }
376    }
377
378    if let Some(subdenomination) = subdenomination {
379        number *= BigInt::from(subdenomination.value());
380    }
381
382    if number.is_integer() {
383        big_to_u256(number.to_integer(), true).map(LitKind::Number)
384    } else {
385        let (numer, denom) = number.into_raw();
386        Ok(LitKind::Rational(Ratio::new(big_to_u256(numer, true)?, big_to_u256(denom, true)?)))
387    }
388}
389
390/// Primitive type to use for fast-path parsing.
391///
392/// If changed, update `max_digits` as well.
393type Primitive = u128;
394
395/// Maximum number of bits for a big number.
396const MAX_BITS: u32 = 4096;
397
398/// Returns the maximum number of digits in `base` radix that can be represented in `BITS` bits.
399///
400/// ```python
401/// import math
402/// def max_digits(bits, base):
403///     return math.floor(math.log(2**bits - 1, base)) + 1
404/// ```
405#[inline]
406const fn max_digits<const BITS: u32>(base: Base) -> usize {
407    if matches!(base, Base::Binary) {
408        return BITS as usize;
409    }
410    match BITS {
411        Primitive::BITS => match base {
412            Base::Binary => BITS as usize,
413            Base::Octal => 43,
414            Base::Decimal => 39,
415            Base::Hexadecimal => 33,
416        },
417        MAX_BITS => match base {
418            Base::Binary => BITS as usize,
419            Base::Octal => 1366,
420            Base::Decimal => 1234,
421            Base::Hexadecimal => 1025,
422        },
423        _ => panic!("unknown bits"),
424    }
425}
426
427fn u256_from_str_radix(s: &str, base: Base) -> Result<U256, LitError> {
428    if s.len() <= max_digits::<{ Primitive::BITS }>(base)
429        && let Ok(n) = Primitive::from_str_radix(s, base as u32)
430    {
431        return Ok(U256::from(n));
432    }
433    U256::from_str_radix(s, base as u64).map_err(|_| LitError::IntegerTooLarge)
434}
435
436fn big_int_from_str_radix(s: &str, base: Base, rat: bool) -> Result<BigInt, LitError> {
437    if s.len() > max_digits::<MAX_BITS>(base) {
438        return Err(if rat { LitError::RationalTooLarge } else { LitError::IntegerTooLarge });
439    }
440    if s.len() <= max_digits::<{ Primitive::BITS }>(base)
441        && let Ok(n) = Primitive::from_str_radix(s, base as u32)
442    {
443        return Ok(BigInt::from(n));
444    }
445    BigInt::from_str_radix(s, base as u32)
446        .map_err(|e| if rat { LitError::ParseRational(e) } else { LitError::ParseInteger(e) })
447}
448
449fn big_to_u256(big: BigInt, rat: bool) -> Result<U256, LitError> {
450    let (_, limbs) = big.to_u64_digits();
451    U256::checked_from_limbs_slice(&limbs).ok_or(if rat {
452        LitError::RationalTooLarge
453    } else {
454        LitError::IntegerTooLarge
455    })
456}
457
458fn pow10(exp: u32) -> BigInt {
459    if let Some(n) = 10usize.checked_pow(exp) {
460        BigInt::from(n)
461    } else {
462        BigInt::from(10u64).pow(exp)
463    }
464}
465
466/// Checks whether mantissa * (10 ** exp) fits into [`MAX_BITS`] bits.
467fn fits_precision_base_10(mantissa: &BigUint, exp: u32) -> bool {
468    // https://github.com/argotorg/solidity/blob/14232980e4b39dee72972f3e142db584f0848a16/libsolidity/ast/Types.cpp#L66
469    fits_precision_base_x(mantissa, std::f64::consts::LOG2_10, exp)
470}
471
472/// Checks whether `mantissa * (X ** exp)` fits into [`MAX_BITS`] bits,
473/// where `X` is given indirectly via `log_2_of_base = log2(X)`.
474fn fits_precision_base_x(mantissa: &BigUint, log_2_of_base: f64, exp: u32) -> bool {
475    // https://github.com/argotorg/solidity/blob/53c4facf4e01d603c21a8544fc3b016229628a16/libsolutil/Numeric.cpp#L25
476    if mantissa.is_zero() {
477        return true;
478    }
479
480    let max = MAX_BITS as u64;
481    let bits = mantissa.bits();
482    if bits > max {
483        return false;
484    }
485    let bits_needed = bits + f64::floor(log_2_of_base * exp as f64) as u64;
486    bits_needed <= max
487}
488
489#[track_caller]
490fn split_at_exclusive(s: &str, idx: usize) -> (&str, &str) {
491    (&s[..idx], &s[idx + 1..])
492}
493
494#[inline]
495fn strip_underscores(symbol: Symbol, sess: &Session) -> Cow<'_, str> {
496    // Do not allocate a new string unless necessary.
497    let s = symbol.as_str_in(sess);
498    if s.contains('_') { Cow::Owned(strip_underscores_slow(s)) } else { Cow::Borrowed(s) }
499}
500
501#[inline(never)]
502#[cold]
503fn strip_underscores_slow(s: &str) -> String {
504    let mut s = s.to_string();
505    s.retain(|c| c != '_');
506    s
507}
508
509#[cfg(test)]
510mod tests {
511    use super::*;
512    use crate::Lexer;
513    use alloy_primitives::{Address, address};
514    use solar_interface::Session;
515
516    // String literal parsing is tested in ../lexer/mod.rs.
517
518    // Run through the lexer to get the same input that the parser gets.
519    #[track_caller]
520    fn lex_literal(src: &str, should_fail: bool, f: impl FnOnce(&Session, Symbol)) {
521        let sess =
522            Session::builder().with_buffer_emitter(Default::default()).single_threaded().build();
523        sess.enter_sequential(|| {
524            let file = sess.source_map().new_source_file("test".to_string(), src).unwrap();
525            let tokens = Lexer::from_source_file(&sess, &file).into_tokens();
526            let diags = sess.dcx.emitted_diagnostics().unwrap();
527            assert_eq!(tokens.len(), 1, "expected exactly 1 token: {tokens:?}\n{diags}");
528            assert_eq!(
529                sess.dcx.has_errors().is_err(),
530                should_fail,
531                "{src:?} -> {tokens:?};\n{diags}",
532            );
533            f(&sess, tokens[0].lit().expect("not a literal").symbol)
534        });
535    }
536
537    #[test]
538    fn integer() {
539        use LitError::*;
540
541        #[track_caller]
542        fn check_int(src: &str, expected: Result<&str, LitError>) {
543            lex_literal(src, false, |sess, symbol| {
544                let res = match parse_integer(sess, symbol, None) {
545                    Ok(LitKind::Number(n)) => Ok(n),
546                    Ok(x) => panic!("not a number: {x:?} ({src:?})"),
547                    Err(e) => Err(e),
548                };
549                let expected = match expected {
550                    Ok(s) => Ok(U256::from_str_radix(s, 10).unwrap()),
551                    Err(e) => Err(e),
552                };
553                assert_eq!(res, expected, "{src:?}");
554            });
555        }
556
557        #[track_caller]
558        fn check_address(src: &str, expected: Result<Address, &str>) {
559            lex_literal(src, false, |sess, symbol| match expected {
560                Ok(address) => match parse_integer(sess, symbol, None) {
561                    Ok(LitKind::Address(a)) => assert_eq!(a, address, "{src:?}"),
562                    e => panic!("not an address: {e:?} ({src:?})"),
563                },
564                Err(int) => match parse_integer(sess, symbol, None) {
565                    Ok(LitKind::Number(n)) => {
566                        assert_eq!(n, U256::from_str_radix(int, 10).unwrap(), "{src:?}")
567                    }
568                    e => panic!("not an integer: {e:?} ({src:?})"),
569                },
570            });
571        }
572
573        check_int("00", Err(IntegerLeadingZeros));
574        check_int("01", Err(IntegerLeadingZeros));
575        check_int("00", Err(IntegerLeadingZeros));
576        check_int("001", Err(IntegerLeadingZeros));
577        check_int("000", Err(IntegerLeadingZeros));
578        check_int("0001", Err(IntegerLeadingZeros));
579
580        check_int("0", Ok("0"));
581        check_int("1", Ok("1"));
582
583        // check("0b10", Ok("2"));
584        // check("0o10", Ok("8"));
585        check_int("10", Ok("10"));
586        check_int("0x10", Ok("16"));
587
588        check_address("0x00000000000000000000000000000000000000", Err("0"));
589        check_address("0x000000000000000000000000000000000000000", Err("0"));
590        check_address("0x0000000000000000000000000000000000000000", Ok(Address::ZERO));
591        check_address("0x00000000000000000000000000000000000000000", Err("0"));
592        check_address("0x000000000000000000000000000000000000000000", Err("0"));
593        check_address("0x0000000000000000000000000000000000000001", Ok(Address::with_last_byte(1)));
594
595        check_address(
596            "0x52908400098527886E0F7030069857D2E4169EE7",
597            Ok(address!("0x52908400098527886E0F7030069857D2E4169EE7")),
598        );
599        check_address(
600            "0x52908400098527886E0F7030069857D2E4169Ee7",
601            Ok(address!("0x52908400098527886E0F7030069857D2E4169EE7")),
602        );
603
604        check_address(
605            "0x8617E340B3D01FA5F11F306F4090FD50E238070D",
606            Ok(address!("0x8617E340B3D01FA5F11F306F4090FD50E238070D")),
607        );
608        check_address(
609            "0xde709f2102306220921060314715629080e2fb77",
610            Ok(address!("0xde709f2102306220921060314715629080e2fb77")),
611        );
612        check_address(
613            "0x27b1fdb04752bbc536007a920d24acb045561c26",
614            Ok(address!("0x27b1fdb04752bbc536007a920d24acb045561c26")),
615        );
616        check_address(
617            "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
618            Ok(address!("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed")),
619        );
620        check_address(
621            "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
622            Ok(address!("0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359")),
623        );
624        check_address(
625            "0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB",
626            Ok(address!("0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB")),
627        );
628        check_address(
629            "0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb",
630            Ok(address!("0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb")),
631        );
632    }
633
634    #[test]
635    fn rational() {
636        use LitError::*;
637
638        #[track_caller]
639        fn check_int_full(src: &str, should_fail_lexing: bool, expected: Result<&str, LitError>) {
640            lex_literal(src, should_fail_lexing, |sess, symbol| {
641                let res = match parse_rational(sess, symbol, None) {
642                    Ok(LitKind::Number(r)) => Ok(r),
643                    Ok(x) => panic!("not a number: {x:?} ({src:?})"),
644                    Err(e) => Err(e),
645                };
646                let expected = match expected {
647                    Ok(s) => Ok(U256::from_str_radix(s, 10).unwrap()),
648                    Err(e) => Err(e),
649                };
650                assert_eq!(res, expected, "{src:?}");
651            });
652        }
653
654        #[track_caller]
655        fn check_int(src: &str, expected: Result<&str, LitError>) {
656            check_int_full(src, false, expected);
657        }
658
659        #[track_caller]
660        fn check_rat(src: &str, expected: Result<&str, LitError>) {
661            lex_literal(src, false, |sess, symbol| {
662                let res = match parse_rational(sess, symbol, None) {
663                    Ok(LitKind::Rational(r)) => Ok(r),
664                    Ok(x) => panic!("not a number: {x:?} ({src:?})"),
665                    Err(e) => Err(e),
666                };
667                let expected = match expected {
668                    Ok(s) => Ok(Ratio::from_str_radix(s, 10).unwrap()),
669                    Err(e) => Err(e),
670                };
671                assert_eq!(res, expected, "{src:?}");
672            });
673        }
674
675        #[track_caller]
676        fn zeros(before: &str, zeros: usize) -> String {
677            before.to_string() + &"0".repeat(zeros)
678        }
679
680        check_int("00", Err(IntegerLeadingZeros));
681        check_int("0_0", Err(IntegerLeadingZeros));
682        check_int("01", Err(IntegerLeadingZeros));
683        check_int("0_1", Err(IntegerLeadingZeros));
684        check_int("00", Err(IntegerLeadingZeros));
685        check_int("001", Err(IntegerLeadingZeros));
686        check_int("000", Err(IntegerLeadingZeros));
687        check_int("0001", Err(IntegerLeadingZeros));
688
689        check_int("0.", Err(EmptyRational));
690        check_int_full("0e-", true, Err(EmptyExponent));
691        check_int_full("0e", true, Err(EmptyExponent));
692
693        check_int_full("0e+", true, Err(RationalPlusExponent));
694        check_int("0e+0", Err(RationalPlusExponent));
695        check_int("0e+1", Err(RationalPlusExponent));
696
697        check_int("0", Ok("0"));
698        check_int("0e0", Ok("0"));
699        check_int("0.0", Ok("0"));
700        check_int("0.00", Ok("0"));
701        check_int("0.0e0", Ok("0"));
702        check_int("0.00e0", Ok("0"));
703        check_int("0.0e00", Ok("0"));
704        check_int("0.00e00", Ok("0"));
705        check_int("0.0e-0", Ok("0"));
706        check_int("0.00e-0", Ok("0"));
707        check_int("0.0e-00", Ok("0"));
708        check_int("0.00e-00", Ok("0"));
709        check_int("0.0e1", Ok("0"));
710        check_int("0.00e1", Ok("0"));
711        check_int("0.00e01", Ok("0"));
712        check_int("0e999999", Ok("0"));
713        check_int("0E123456789", Ok("0"));
714
715        check_int(".0", Ok("0"));
716        check_int(".00", Ok("0"));
717        check_int(".0e0", Ok("0"));
718        check_int(".00e0", Ok("0"));
719        check_int(".0e00", Ok("0"));
720        check_int(".00e00", Ok("0"));
721        check_int(".0e-0", Ok("0"));
722        check_int(".00e-0", Ok("0"));
723        check_int(".0e-00", Ok("0"));
724        check_int(".00e-00", Ok("0"));
725        check_int(".0e1", Ok("0"));
726        check_int(".00e1", Ok("0"));
727        check_int(".00e01", Ok("0"));
728
729        check_int("1", Ok("1"));
730        check_int("1e0", Ok("1"));
731        check_int("1.0", Ok("1"));
732        check_int("1.00", Ok("1"));
733        check_int("1.0e0", Ok("1"));
734        check_int("1.00e0", Ok("1"));
735        check_int("1.0e00", Ok("1"));
736        check_int("1.00e00", Ok("1"));
737        check_int("1.0e-0", Ok("1"));
738        check_int("1.00e-0", Ok("1"));
739        check_int("1.0e-00", Ok("1"));
740        check_int("1.00e-00", Ok("1"));
741
742        check_int_full("0b", true, Err(InvalidRational));
743        check_int_full("0b0", true, Err(InvalidRational));
744        check_int_full("0b01", true, Err(InvalidRational));
745        check_int_full("0b01.0", true, Err(InvalidRational));
746        check_int_full("0b01.0e1", true, Err(InvalidRational));
747        check_int_full("0b0e", true, Err(InvalidRational));
748        // check_int_full("0b0e.0", true, Err(InvalidRational));
749        // check_int_full("0b0e.0e1", true, Err(InvalidRational));
750
751        check_int_full("0o", true, Err(InvalidRational));
752        check_int_full("0o0", true, Err(InvalidRational));
753        check_int_full("0o01", true, Err(InvalidRational));
754        check_int_full("0o01.0", true, Err(InvalidRational));
755        check_int_full("0o01.0e1", true, Err(InvalidRational));
756        check_int_full("0o0e", true, Err(InvalidRational));
757        // check_int_full("0o0e.0", true, Err(InvalidRational));
758        // check_int_full("0o0e.0e1", true, Err(InvalidRational));
759
760        check_int_full("0x", true, Err(InvalidRational));
761        check_int_full("0x0", false, Err(InvalidRational));
762        check_int_full("0x01", false, Err(InvalidRational));
763        check_int_full("0x01.0", true, Err(InvalidRational));
764        check_int_full("0x01.0e1", true, Err(InvalidRational));
765        check_int_full("0x0e", false, Err(InvalidRational));
766        check_int_full("0x0e.0", true, Err(InvalidRational));
767        check_int_full("0x0e.0e1", true, Err(InvalidRational));
768
769        check_int("1e1", Ok("10"));
770        check_int("1.0e1", Ok("10"));
771        check_int("1.00e1", Ok("10"));
772        check_int("1.00e01", Ok("10"));
773
774        check_int("1.1e1", Ok("11"));
775        check_int("1.10e1", Ok("11"));
776        check_int("1.100e1", Ok("11"));
777        check_int("1.2e1", Ok("12"));
778        check_int("1.200e1", Ok("12"));
779
780        check_int("1e10", Ok(&zeros("1", 10)));
781        check_int("1.0e10", Ok(&zeros("1", 10)));
782        check_int("1.1e10", Ok(&zeros("11", 9)));
783        check_int("10e-1", Ok("1"));
784        // check_int("1E1233", Ok(&zeros("1", 1233)));
785        // check_int("1E1234", Err(LitError::ExponentTooLarge));
786
787        check_rat("1e-1", Ok("1/10"));
788        check_rat("1e-2", Ok("1/100"));
789        check_rat("1e-3", Ok("1/1000"));
790        check_rat("1.0e-1", Ok("1/10"));
791        check_rat("1.0e-2", Ok("1/100"));
792        check_rat("1.0e-3", Ok("1/1000"));
793        check_rat("1.1e-1", Ok("11/100"));
794        check_rat("1.1e-2", Ok("11/1000"));
795        check_rat("1.1e-3", Ok("11/10000"));
796
797        check_rat("1.1", Ok("11/10"));
798        check_rat("1.10", Ok("11/10"));
799        check_rat("1.100", Ok("11/10"));
800        check_rat("1.2", Ok("12/10"));
801        check_rat("1.20", Ok("12/10"));
802    }
803}