somen_language/numeric/
integer.rs1use num_traits::{CheckedAdd, CheckedMul, CheckedNeg, Zero};
3use somen::prelude::*;
4
5use super::{digits, digits_fixed, digits_trailing_zeros};
6use crate::character::Character;
7
8#[inline]
10pub fn integer<'a, N, I, C>(radix: u8, neg: bool) -> impl Parser<I, Output = N> + 'a
11where
12 N: Zero + CheckedMul + CheckedAdd + CheckedNeg + TryFrom<u8> + Clone + 'a,
13 I: Input<Ok = C> + ?Sized + 'a,
14 C: Character + 'a,
15{
16 let integer =
17 fold_digits(digits(radix), N::zero(), radix, neg).try_map(|(acc, _, overflowed)| {
18 if overflowed {
19 Err("a not too large number.")
20 } else {
21 Ok(acc)
22 }
23 });
24
25 #[cfg(feature = "alloc")]
26 {
27 integer.expect(alloc::format!("an integer with radix {radix}"))
28 }
29 #[cfg(not(feature = "alloc"))]
30 {
31 integer.expect("an integer")
32 }
33}
34
35#[inline]
37pub fn integer_trailing_zeros<'a, N, I, C>(radix: u8, neg: bool) -> impl Parser<I, Output = N> + 'a
38where
39 N: Zero + CheckedMul + CheckedAdd + CheckedNeg + TryFrom<u8> + Clone + 'a,
40 I: Input<Ok = C> + ?Sized + 'a,
41 C: Character + 'a,
42{
43 let integer = fold_digits(digits_trailing_zeros(radix), N::zero(), radix, neg).try_map(
44 |(acc, _, overflowed)| {
45 if overflowed {
46 Err("a not too large number.")
47 } else {
48 Ok(acc)
49 }
50 },
51 );
52
53 #[cfg(feature = "alloc")]
54 {
55 integer.expect(alloc::format!("an integer with radix {radix}"))
56 }
57 #[cfg(not(feature = "alloc"))]
58 {
59 integer.expect("an integer")
60 }
61}
62
63#[inline]
65pub fn integer_fixed<'a, N, I, C>(
66 length: usize,
67 radix: u8,
68 neg: bool,
69) -> impl Parser<I, Output = N> + 'a
70where
71 N: Zero + CheckedMul + CheckedAdd + CheckedNeg + TryFrom<u8> + Clone + 'a,
72 I: Positioned<Ok = C> + ?Sized + 'a,
73 C: Character + 'a,
74{
75 fold_digits(digits_fixed(length, radix), N::zero(), radix, neg).try_map(
76 |(acc, _, overflowed)| {
77 if overflowed {
78 Err("a not too large number.")
79 } else {
80 Ok(acc)
81 }
82 },
83 )
84}
85
86pub fn fold_digits<'a, N, S, I, C>(
91 streamed: S,
92 acc: N,
93 radix: u8,
94 neg: bool,
95) -> impl Parser<I, Output = (N, usize, bool)> + 'a
96where
97 N: CheckedMul + CheckedAdd + CheckedNeg + TryFrom<u8> + Clone + 'a,
98 S: IterableParser<I, Item = C> + 'a,
99 I: Positioned<Ok = C> + ?Sized + 'a,
100 C: Character + 'a,
101{
102 let n_radix = N::try_from(radix).ok();
103 streamed.fold(
104 value((acc, 0, n_radix.is_none())),
105 move |(acc, count, overflowed), x| {
106 if overflowed {
107 return (acc, count, true);
108 }
109 let res = acc
110 .checked_mul(n_radix.as_ref().unwrap())
111 .zip(
112 x.to_digit(radix)
113 .and_then(|d| N::try_from(d).ok())
114 .and_then(|x| if neg { x.checked_neg() } else { Some(x) }),
115 )
116 .and_then(|(acc, x)| acc.checked_add(&x));
117 match res {
118 Some(x) => (x, count + 1, false),
119 None => (acc, count, true),
120 }
121 },
122 )
123}