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 #[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 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 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 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 Err(e @ (IntegerLeadingZeros | IntegerTooLarge)) => Err(self.dcx().err(e.to_string())),
120 Err(EmptyInteger) => Ok(LitKind::Err(ErrorGuaranteed::new_unchecked())),
122 Err(e @ ParseInteger(_)) => panic!("failed to parse integer literal {symbol:?}: {e}"),
124 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 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 Err(
143 e @ (EmptyRational | RationalPlusExponent | IntegerTooLarge | RationalTooLarge
144 | ExponentTooLarge | IntegerLeadingZeros),
145 ) => Err(self.dcx().err(e.to_string())),
146 Err(InvalidRational | EmptyExponent) => {
148 Ok(LitKind::Err(ErrorGuaranteed::new_unchecked()))
149 }
150 Err(e @ (ParseExponent(_) | ParseInteger(_) | ParseRational(_) | EmptyInteger)) => {
152 panic!("failed to parse rational literal {symbol:?}: {e}")
153 }
154 }
155 }
156
157 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 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 let (mut int, rat, exp) = match (s.find('.'), s.find(['e', 'E'])) {
281 (None, None) => (s, None, None),
283 (Some(dot), None) => {
285 let (int, rat) = split_at_exclusive(s, dot);
286 (int, Some(rat), None)
287 }
288 (None, Some(exp)) => {
290 let (int, exp) = split_at_exclusive(s, exp);
291 (int, None, Some(exp))
292 }
293 (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 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 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 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
390type Primitive = u128;
394
395const MAX_BITS: u32 = 4096;
397
398#[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
466fn fits_precision_base_10(mantissa: &BigUint, exp: u32) -> bool {
468 fits_precision_base_x(mantissa, std::f64::consts::LOG2_10, exp)
470}
471
472fn fits_precision_base_x(mantissa: &BigUint, log_2_of_base: f64, exp: u32) -> bool {
475 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 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 #[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_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("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("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_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}