somen_language/
numeric.rs

1//! Parsers for numeric literals.
2use somen::prelude::*;
3
4use crate::character::{character, Character};
5
6pub mod float;
7pub mod integer;
8
9/// Takes a function returns a integer parser, returns a parser of signed integer.
10///
11/// The taken function must return negative result if the argument is `true`, and vice versa.
12/// If `plus_sign` is `true`, it allows plus signs besides minus signs, has no effects.
13pub fn signed<'a, N, F, P, I, C>(mut parser: F, plus_sign: bool) -> impl Parser<I, Output = N> + 'a
14where
15    F: FnMut(bool) -> P + 'a,
16    P: Parser<I, Output = N> + 'a,
17    I: Input<Ok = C> + ?Sized + 'a,
18    C: Character + 'a,
19{
20    sign(plus_sign)
21        .opt()
22        .then(move |sign| parser(sign.unwrap_or_default()))
23        .expect("a signed number")
24}
25
26/// Takes a function returns a integer parser, returns a parser of unsigned, positive integer.
27///
28/// This function is for symmetry with the function [`signed`], so like it, the taken function
29/// must return negative result if the argument is `true`, and vice versa.
30#[inline]
31pub fn unsigned<'a, N, F, P, I, C>(parser: F) -> impl Parser<I, Output = N> + 'a
32where
33    F: FnOnce(bool) -> P,
34    P: Parser<I, Output = N> + 'a,
35    I: Positioned<Ok = C> + ?Sized,
36    C: Character,
37{
38    parser(false)
39}
40
41/// Parses a digit with given radix.
42pub fn digit<'a, I, C>(radix: u8) -> impl Parser<I, Output = C> + 'a
43where
44    I: Positioned<Ok = C> + ?Sized + 'a,
45    C: Character + 'a,
46{
47    let digit = is(move |c: &C| c.is_digit(radix));
48
49    #[cfg(feature = "alloc")]
50    {
51        digit.expect(alloc::format!("a digit with radix {radix}"))
52    }
53    #[cfg(not(feature = "alloc"))]
54    {
55        digit.expect("a digit")
56    }
57}
58
59/// Parses a non-zero digit with given radix.
60pub fn non_zero_digit<'a, I, C>(radix: u8) -> impl Parser<I, Output = C> + 'a
61where
62    I: Positioned<Ok = C> + ?Sized + 'a,
63    C: Character + 'a,
64{
65    let digit = is(move |c: &C| c.is_digit(radix) && !c.eq_byte(b'0'));
66
67    #[cfg(feature = "alloc")]
68    {
69        digit.expect(alloc::format!("a non-zero digit with radix {radix}"))
70    }
71    #[cfg(not(feature = "alloc"))]
72    {
73        digit.expect("a non-zero digit")
74    }
75}
76
77/// Parses digits with given radix.
78#[inline]
79pub fn digits<'a, I, C>(radix: u8) -> impl IterableParser<I, Item = C> + 'a
80where
81    I: Input<Ok = C> + ?Sized + 'a,
82    C: Character + 'a,
83{
84    (non_zero_digit(radix).once(), digit(radix).repeat(..)).or(character(b'0').once())
85}
86
87/// Parses digits with given radix (trailing zeros are allowed).
88#[inline]
89pub fn digits_trailing_zeros<'a, I, C>(radix: u8) -> impl IterableParser<I, Item = C> + 'a
90where
91    I: Input<Ok = C> + ?Sized + 'a,
92    C: Character + 'a,
93{
94    digit(radix).repeat(1..)
95}
96
97/// Parses fixed-length digits with given radix.
98#[inline]
99pub fn digits_fixed<'a, I, C>(length: usize, radix: u8) -> impl IterableParser<I, Item = C> + 'a
100where
101    I: Positioned<Ok = C> + ?Sized + 'a,
102    C: Character + 'a,
103{
104    digit(radix).times(length)
105}
106
107/// Parses a plus or minus sign and returns `true` if it is a minus sign.
108///
109/// By the default, plus signs are not allowed and it will be allowed if the argument `plus_sign` is `true`.
110pub fn sign<'a, I, C>(plus_sign: bool) -> impl Parser<I, Output = bool> + 'a
111where
112    I: Input<Ok = C> + ?Sized + 'a,
113    C: Character + 'a,
114{
115    let minus = character(b'-').map(|_| true);
116    if plus_sign {
117        minus.or(character(b'+').map(|_| false)).left()
118    } else {
119        minus.right()
120    }
121}