extern crate alloc;
use combine:: {
Parser, RangeStream, ParseError,
choice, many, many1, one_of, optional, token,
parser:: {
char:: { digit },
range:: recognize_with_value,
},
};
use core::num::ParseIntError;
use alloc::string::String;
use super::Frac;
struct Numbers(String);
impl Numbers {
fn trim_trailing_zeros(&mut self) -> usize {
let z = self.0.as_bytes()
.iter()
.rev()
.try_fold(0, |count, &c| if c == b'0' { Ok(count + 1) } else { Err(count) })
.map_or_else(|z| z, |z| z)
;
self.0.truncate(self.0.len() - z);
z
}
}
impl Extend<char> for Numbers {
fn extend<T>(&mut self, iter: T)
where
T: IntoIterator<Item = char>,
{
for c in iter {
if c == '_' {
continue;
}
self.0.push(c);
}
}
}
impl Default for Numbers {
fn default() -> Self {
Self(String::new())
}
}
impl TryInto<u128> for Numbers {
type Error = ParseIntError;
fn try_into(self) -> Result<u128, Self::Error> {
Ok(if self.0.len() == 0 {
0
} else {
self.0.parse()?
})
}
}
pub fn parser<'a, I>() -> impl 'a + Parser<I, Output = Frac>
where
I: 'a + RangeStream<Token = char, Range = &'a str>,
I::Error: ParseError<I::Token, I::Range, I::Position>,
{
(
sign(),
recognize_with_value(number1()).map(|(_, int)| int),
optional((
token('.').map(|_| ()),
optional(recognize_with_value(number()).map(|(_, frac)| frac))
.map(|frac| frac.unwrap_or_else(|| Default::default()))
).map(|(_, frac)| frac))
.map(|frac| frac.unwrap_or_else(|| Default::default())),
optional((
one_of("eE".chars()).map(|_| ()),
sign(),
recognize_with_value(number1()).map(|(_, exp)| TryInto::<u128>::try_into(exp).unwrap() as u16)
).map(|(_, sign, exp)| if sign { -1 } else { 1 } * exp as i16))
.map(|exp| exp.unwrap_or_else(|| 0)),
).map_input(|(sign, mut int, mut frac, mut exp), input: &mut I| {
frac.trim_trailing_zeros();
match frac.0.len() {
0 => exp += int.trim_trailing_zeros() as i16,
n => {
exp -= n as i16;
int.0.extend(Some(frac.0));
},
}
let numer: u128 = int.try_into().map_err(|_| I::Error::empty(input.position()))?;
Ok(if numer == 0 {
Frac::from_int(0)
} else {
Frac::from_ratio(if sign { -(numer as i128) } else { numer as i128 }, 1, exp)
})
}).flat_map(|r| Ok(r?))
}
fn sign<'a, I>() -> impl Parser<I, Output = bool>
where
I: RangeStream<Token = char, Range = &'a str>,
I::Error: ParseError<I::Token, I::Range, I::Position>,
{
optional(one_of("+-".chars()).map(|sign| match sign {
'-' => true,
_ => false,
})).map(|sign| match sign {
Some(n) => n,
_ => false,
})
}
fn number<'a, I>() -> impl Parser<I, Output = Numbers>
where
I: RangeStream<Token = char, Range = &'a str>,
I::Error: ParseError<I::Token, I::Range, I::Position>,
{
many::<Numbers, _, _>(choice((digit(),token('_'))))
}
fn number1<'a, I>() -> impl Parser<I, Output = Numbers>
where
I: RangeStream<Token = char, Range = &'a str>,
I::Error: ParseError<I::Token, I::Range, I::Position>,
{
many1::<Numbers, _, _>(choice((digit(), token('_'))))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pi() {
let left: Frac = "3.14159_26535_89793_23846_26433_83279_50288".parse().unwrap();
let int = Frac::from_int(3_14159_26535_89793_23846_26433_83279_50288);
let right = int * Frac::from_exp10(-35);
assert_eq!(left, right);
}
#[test]
fn test_big() {
let left: Frac = "3.14159_26535_89793_23846_26433_83279_50288e35".parse().unwrap();
let right = Frac::from_int(3_14159_26535_89793_23846_26433_83279_50288);
assert_eq!(left, right);
}
#[test]
fn test_exp() {
let left: Frac = "3.14e-20".parse().unwrap();
let int = Frac::from_int(314);
let right = int * Frac::from_exp10(-22);
assert_eq!(left, right);
}
#[test]
fn test_trailing_zeros() {
let left: Frac = "1000.000".parse().unwrap();
let right = Frac::from_int(1000);
assert_eq!(left, right);
}
#[test]
fn test_zero() {
let left: Frac = "0000.000e100".parse().unwrap();
let right = Frac::default();
assert_eq!(left, right);
}
#[test]
fn test_simple() {
let left: Frac = "1e5".parse().unwrap();
assert_eq!(left.to_f64(), 1e5);
}
}