#![cfg(feature="std")]
use {
core::fmt::{self, Display, Formatter, Write},
crate::{Error, Number, Result},
};
#[derive(Debug)]
enum FloatExp {
None,
Some,
Exp {
x: f64,
fract: Option<u8>,
},
}
impl Display for FloatExp {
fn fmt(&self, f: &mut Formatter) -> core::result::Result<(), fmt::Error> {
match self {
FloatExp::None | FloatExp::Some => f.write_str(concat!()),
FloatExp::Exp { x, .. } => {
f.write_char('e')?;
x.fmt(f)
},
}
}
}
#[cfg(any(target_pointer_width = "8", target_pointer_width = "16", target_pointer_width = "32", target_pointer_width = "64"))]
type Signed = i64;
#[cfg(any(target_pointer_width = "8", target_pointer_width = "16", target_pointer_width = "32", target_pointer_width = "64"))]
type Unsigned = u64;
#[cfg(not(any(target_pointer_width = "8", target_pointer_width = "16", target_pointer_width = "32", target_pointer_width = "64")))]
type Signed = isize;
#[cfg(not(any(target_pointer_width = "8", target_pointer_width = "16", target_pointer_width = "32", target_pointer_width = "64")))]
type Unsigned = usize;
#[derive(Debug)]
enum X {
Signed(Signed),
Unsigned(Unsigned),
Float {
x: f64,
fract: Option<u8>,
exp: FloatExp,
},
}
impl Display for X {
fn fmt(&self, f: &mut Formatter) -> core::result::Result<(), fmt::Error> {
match self {
X::Signed(x) => x.fmt(f),
X::Unsigned(x) => x.fmt(f),
X::Float { x, fract: _, exp } => write!(f, "{x}{exp}", x=x, exp=exp),
}
}
}
#[derive(Debug)]
pub struct NumberParser {
x: Option<X>,
}
impl NumberParser {
pub fn new() -> Self {
Self {
x: None,
}
}
pub fn add(&mut self, b: &u8) -> Result<()> {
match b {
b'-' => match self.x.as_mut() {
None => self.x = Some(X::Signed(0)),
Some(X::Signed(x)) => return Err(Error::from(__!("Invalid integer: {parsed}{c}", parsed=x, c=char::from(*b)))),
Some(X::Unsigned(x)) => return Err(Error::from(__!("Invalid integer: {parsed}{c}", parsed=x, c=char::from(*b)))),
Some(X::Float { x, fract: _, exp }) => match exp {
FloatExp::Some => *exp = FloatExp::Exp { x: -0.0, fract: None },
_ => return Err(Error::from(__!("Invalid float: {x}{c}", x=x, c=char::from(*b)))),
},
},
b'+' => match self.x.as_mut() {
None => return Err(Error::from(__!("Invalid sign: {c}", c=char::from(*b)))),
Some(X::Signed(x)) => return Err(Error::from(__!("Invalid integer: {parsed}{c}", parsed=x, c=char::from(*b)))),
Some(X::Unsigned(x)) => return Err(Error::from(__!("Invalid integer: {parsed}{c}", parsed=x, c=char::from(*b)))),
Some(X::Float { x, fract: _, exp }) => match exp {
FloatExp::Some => *exp = FloatExp::Exp { x: 0.0, fract: None },
FloatExp::None => return Err(Error::from(__!("Invalid float: {x}{c}", x=x, c=char::from(*b)))),
FloatExp::Exp {..} => return Err(Error::from(__!("Invalid float: {x}{exp}{c}", x=x, exp=exp, c=char::from(*b)))),
},
},
b'e' | b'E' => match self.x.as_mut() {
None => return Err(Error::from(__!("Invalid sign: {c}", c=char::from(*b)))),
Some(X::Signed(x)) => self.x = Some(X::Float { x: *x as f64, fract: None, exp: FloatExp::Some }),
Some(X::Unsigned(x)) => self.x = Some(X::Float { x: *x as f64, fract: None, exp: FloatExp::Some }),
Some(X::Float { x, fract: _, exp }) => match exp {
FloatExp::None => *exp = FloatExp::Some,
FloatExp::Some => return Err(Error::from(__!("Invalid float: {x}e{c}", x=x, c=char::from(*b)))),
FloatExp::Exp {..} => return Err(Error::from(__!("Invalid float: {x}{exp}{c}", x=x, exp=exp, c=char::from(*b)))),
},
},
b'.' => match self.x.as_mut() {
None => return Err(Error::from(__!("Invalid sign: {c}", c=char::from(*b)))),
Some(X::Signed(x)) => self.x = Some(X::Float { x: *x as f64, fract: Some(0), exp: FloatExp::None }),
Some(X::Unsigned(x)) => self.x = Some(X::Float { x: *x as f64, fract: Some(0), exp: FloatExp::None }),
Some(X::Float { x, fract, exp }) => match fract {
None => *fract = Some(0),
Some(_) => match exp {
FloatExp::None => return Err(Error::from(__!("Invalid float: {x}{c}", x=x, c=char::from(*b)))),
FloatExp::Some => return Err(Error::from(__!("Invalid float: {x}e{c}", x=x, c=char::from(*b)))),
FloatExp::Exp { x: _, fract } => match fract {
None => *fract = Some(0),
Some(_) => return Err(Error::from(__!("Invalid float: {x}{exp}{c}", x=x, exp=exp, c=char::from(*b)))),
},
},
},
},
b'0'..=b'9' => {
let b = b - b'0';
match self.x.as_mut() {
None => self.x = Some(X::Unsigned(b.into())),
Some(X::Signed(x)) => match x.checked_mul(10).map(|x| x.checked_sub(b.into())) {
Some(Some(n)) => *x = n,
_ => self.x = Some(X::Float { x: *x as f64 * 10.0 - f64::from(b), fract: None, exp: FloatExp::None }),
},
Some(X::Unsigned(x)) => match x.checked_mul(10).map(|x| x.checked_add(b.into())) {
Some(Some(n)) => *x = n,
_ => self.x = Some(X::Float { x: *x as f64 * 10.0 + f64::from(b), fract: None, exp: FloatExp::None }),
},
Some(X::Float { x, fract, exp }) => match exp {
FloatExp::None => if add_digit(x, fract.as_mut(), b) == false {
return Err(Error::from(__!("Float fractional part is too long: {x}{b}", x=x, b=b)));
},
FloatExp::Some => *exp = FloatExp::Exp { x: b.into(), fract: None },
FloatExp::Exp { x, fract } => if add_digit(x, fract.as_mut(), b) == false {
return Err(Error::from(__!("Float's exponent's fractional part is too long: {x}{b}", x=x, b=b)));
},
},
};
},
_ => return Err(Error::from(match self.x.as_ref() {
None => __!("Invalid number: {c}", c=char::from(*b)),
Some(X::Signed(x)) => __!("Invalid number: {parsed}{c}", parsed=x, c=char::from(*b)),
Some(X::Unsigned(x)) => __!("Invalid number: {parsed}{c}", parsed=x, c=char::from(*b)),
Some(x) => __!("Invalid number: {parsed}{c}", parsed=x, c=char::from(*b)),
})),
};
Ok(())
}
pub fn parse(&mut self) -> Result<Number> {
match self.x.take() {
None => Err(Error::from(__!("Number is empty"))),
Some(X::Signed(x)) => Ok(x.into()),
Some(X::Unsigned(x)) => Ok(x.into()),
Some(X::Float { x, fract, exp }) => match exp {
FloatExp::None => match fract.is_some() {
true => Ok(x.into()),
false => {
let large_or_small = match x.is_sign_positive() {
true => "large",
false => "small",
};
Err(Error::from(__!("Integer too {large_or_small}: {x}", large_or_small=large_or_small, x=x.trunc())))
},
},
FloatExp::Some => Err(Error::from(__!("Invalid float: {x}e", x=x))),
FloatExp::Exp { x: exp_x, fract: _ } => Ok((x * 10_f64.powf(exp_x)).into()),
},
}
}
}
const FRACTS: [[f64; 10]; 20] = [
[0.0, 1e-1, 2e-1, 3e-1, 4e-1, 5e-1, 6e-1, 7e-1, 8e-1, 9e-1],
[0.0, 1e-2, 2e-2, 3e-2, 4e-2, 5e-2, 6e-2, 7e-2, 8e-2, 9e-2],
[0.0, 1e-3, 2e-3, 3e-3, 4e-3, 5e-3, 6e-3, 7e-3, 8e-3, 9e-3],
[0.0, 1e-4, 2e-4, 3e-4, 4e-4, 5e-4, 6e-4, 7e-4, 8e-4, 9e-4],
[0.0, 1e-5, 2e-5, 3e-5, 4e-5, 5e-5, 6e-5, 7e-5, 8e-5, 9e-5],
[0.0, 1e-6, 2e-6, 3e-6, 4e-6, 5e-6, 6e-6, 7e-6, 8e-6, 9e-6],
[0.0, 1e-7, 2e-7, 3e-7, 4e-7, 5e-7, 6e-7, 7e-7, 8e-7, 9e-7],
[0.0, 1e-8, 2e-8, 3e-8, 4e-8, 5e-8, 6e-8, 7e-8, 8e-8, 9e-8],
[0.0, 1e-9, 2e-9, 3e-9, 4e-9, 5e-9, 6e-9, 7e-9, 8e-9, 9e-9],
[0.0, 1e-10, 2e-10, 3e-10, 4e-10, 5e-10, 6e-10, 7e-10, 8e-10, 9e-10],
[0.0, 1e-11, 2e-11, 3e-11, 4e-11, 5e-11, 6e-11, 7e-11, 8e-11, 9e-11],
[0.0, 1e-12, 2e-12, 3e-12, 4e-12, 5e-12, 6e-12, 7e-12, 8e-12, 9e-12],
[0.0, 1e-13, 2e-13, 3e-13, 4e-13, 5e-13, 6e-13, 7e-13, 8e-13, 9e-13],
[0.0, 1e-14, 2e-14, 3e-14, 4e-14, 5e-14, 6e-14, 7e-14, 8e-14, 9e-14],
[0.0, 1e-15, 2e-15, 3e-15, 4e-15, 5e-15, 6e-15, 7e-15, 8e-15, 9e-15],
[0.0, 1e-16, 2e-16, 3e-16, 4e-16, 5e-16, 6e-16, 7e-16, 8e-16, 9e-16],
[0.0, 1e-17, 2e-17, 3e-17, 4e-17, 5e-17, 6e-17, 7e-17, 8e-17, 9e-17],
[0.0, 1e-18, 2e-18, 3e-18, 4e-18, 5e-18, 6e-18, 7e-18, 8e-18, 9e-18],
[0.0, 1e-19, 2e-19, 3e-19, 4e-19, 5e-19, 6e-19, 7e-19, 8e-19, 9e-19],
[0.0, 1e-20, 2e-20, 3e-20, 4e-20, 5e-20, 6e-20, 7e-20, 8e-20, 9e-20],
];
#[test]
fn tests() {
FRACTS.iter().enumerate().for_each(|(i, fracts)| {
assert_eq!(fracts.len(), 10);
assert_eq!(fracts[0], 0.0);
fracts[1..].iter().enumerate().for_each(|(k, f)| assert!((f - 10_f64.powf(-((i + 1) as f64)) * (k + 1) as f64).abs() < 1e-10));
});
}
#[must_use]
fn add_digit(x: &mut f64, fract: Option<&mut u8>, digit: u8) -> bool {
if digit > 9 {
return false;
}
match fract {
None => {
*x = *x * 10.0;
if digit > 0 {
let digit = f64::from(digit);
match x.is_sign_positive() {
true => *x += digit,
false => *x -= digit,
};
}
true
},
Some(fract) => match fract.checked_add(1) {
Some(new_fract) => {
if digit > 0 {
let digit = match FRACTS.get(usize::from(*fract)).map(|fracts| fracts.get(usize::from(digit))) {
Some(Some(digit)) => *digit,
_ => 10_f64.powf(-f64::from(new_fract)) * f64::from(digit),
};
match x.is_sign_positive() {
true => *x += digit,
false => *x -= digit,
};
}
*fract = new_fract;
true
},
None => false,
},
}
}