use std::fmt;
use nom::branch::alt;
use nom::character::complete::{char, one_of};
use nom::combinator::{map, opt, recognize, value};
use nom::sequence::{pair, preceded, tuple};
use nom::IResult;
#[derive(Debug, Clone, PartialEq)]
pub enum Number {
PositiveInteger(u64),
NegativeInteger(i64),
Float(f64),
}
#[derive(Debug)]
struct Num {
integer: Integer,
fraction: Option<String>,
exponent: Option<Exponent>,
}
impl Into<Number> for Num {
fn into(self) -> Number {
match (self.integer, self.fraction, self.exponent) {
(Integer::Positive(str), None, None) => {
Number::PositiveInteger(str.parse::<u64>().unwrap())
}
(Integer::Negative(str), None, None) => {
Number::NegativeInteger(str.parse::<i64>().unwrap())
}
(int, Some(decimal), None) => {
Number::Float(format!("{}.{}", int, decimal).parse::<f64>().unwrap())
}
(int, None, Some(exponent)) => Number::Float(
format!("{}E{}", int, exponent.to_string())
.parse::<f64>()
.unwrap(),
),
(int, Some(decimal), Some(exponent)) => Number::Float(
format!("{}.{}E{}", int, decimal, exponent.to_string())
.parse::<f64>()
.unwrap(),
),
}
}
}
#[derive(Debug, PartialEq)]
enum Integer {
Positive(String),
Negative(String),
}
impl fmt::Display for Integer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Positive(str) => write!(f, "{}", str.to_string()),
Self::Negative(str) => write!(f, "{}", str.to_string()),
}
}
}
pub fn number(input: &str) -> IResult<&str, Number> {
let (rest, integer) = integer(input)?;
let (rest, fraction) = fraction(rest)?;
let (rest, exponent) = exponent(rest)?;
let num = Num {
integer,
fraction,
exponent,
};
Ok((rest, num.into()))
}
fn integer(input: &str) -> IResult<&str, Integer> {
alt((
map(
alt((
recognize(tuple((char('-'), onenine, digits))),
recognize(pair(char('-'), digit)),
)),
|str| Integer::Negative(str.to_string()),
),
map(
alt((
map(recognize(pair(onenine, digits)), |str| str.to_string()),
digit,
)),
|str| Integer::Positive(str.to_string()),
),
))(input)
}
fn digits(input: &str) -> IResult<&str, String> {
alt((
map(recognize(pair(digit, digits)), |str| str.to_string()),
digit,
))(input)
}
pub fn digit(input: &str) -> IResult<&str, String> {
alt((zero, onenine))(input)
}
fn onenine(input: &str) -> IResult<&str, String> {
map(one_of("123456789"), |c| c.to_string())(input)
}
fn zero(input: &str) -> IResult<&str, String> {
map(char('0'), |c| c.to_string())(input)
}
fn fraction(input: &str) -> IResult<&str, Option<String>> {
opt(preceded(char('.'), digits))(input)
}
#[derive(Debug, PartialEq)]
struct Exponent {
sign: Sign,
digits: String,
}
impl ToString for Exponent {
fn to_string(&self) -> String {
format!("{}{}", self.sign.to_string(), self.digits.to_string())
}
}
fn exponent(input: &str) -> IResult<&str, Option<Exponent>> {
opt(map(
tuple((alt((char('E'), char('e'))), sign, digits)),
|(_, s, d)| Exponent { sign: s, digits: d },
))(input)
}
#[derive(Debug, PartialEq, Clone)]
enum Sign {
Plus,
Minus,
}
impl ToString for Sign {
fn to_string(&self) -> String {
match self {
Sign::Plus => "+".to_string(),
Sign::Minus => "-".to_string(),
}
}
}
fn sign(input: &str) -> IResult<&str, Sign> {
alt((
value(Sign::Minus, char('-')),
value(Sign::Plus, opt(char('+'))),
))(input)
}
#[cfg(test)]
mod tests {
use nom::error::{Error, ErrorKind};
use nom::Err;
use super::*;
#[test]
fn assert_zero() {
assert_eq!(zero("0"), Ok(("", "0".to_string())));
}
#[test]
fn failed_parse_one() {
assert_eq!(zero("1"), Err(Err::Error(Error::new("1", ErrorKind::Char))))
}
#[test]
fn parse_one() {
assert_eq!(onenine("1"), Ok(("", "1".to_string())));
}
#[test]
fn parse_nine() {
assert_eq!(onenine("9"), Ok(("", "9".to_string())));
}
#[test]
fn failed_parse_a() {
assert_eq!(
digit("a"),
Err(Err::Error(Error::new("a", ErrorKind::OneOf)))
)
}
#[test]
fn digit_zero() {
assert_eq!(digit("0"), Ok(("", "0".to_string())));
}
#[test]
fn digit_one() {
assert_eq!(digit("1"), Ok(("", "1".to_string())));
}
#[test]
fn digit_one_nine() {
assert_eq!(digit("19"), Ok(("9", "1".to_string())));
}
#[test]
fn digit_one_alpha() {
assert_eq!(digit("1a"), Ok(("a", "1".to_string())));
}
#[test]
fn digit_alpha() {
assert_eq!(
digit("a"),
Err(Err::Error(Error::new("a", ErrorKind::OneOf)))
);
}
#[test]
fn digits1() {
assert_eq!(digits("123"), Ok(("", "123".to_string())))
}
#[test]
fn parse_negative_digit() {
assert_eq!(integer("-1"), Ok(("", Integer::Negative("-1".to_string()))));
}
#[test]
fn parse_negative_digits() {
assert_eq!(
integer("-123"),
Ok(("", Integer::Negative("-123".to_string())))
);
}
#[test]
fn empty_fraction() {
assert_eq!(fraction(""), Ok(("", None)))
}
#[test]
fn rest_fraction() {
assert_eq!(fraction(".123"), Ok(("", Some("123".to_string()))))
}
#[test]
fn sign_empty() {
assert_eq!(sign(""), Ok(("", Sign::Plus)));
}
#[test]
fn sign_plus() {
assert_eq!(sign("+2"), Ok(("2", Sign::Plus)));
}
#[test]
fn sign_minus() {
assert_eq!(sign("-2"), Ok(("2", Sign::Minus)));
}
#[test]
fn exponent_empty() {
assert_eq!(exponent(""), Ok(("", None)));
}
#[test]
fn exponent_e_plus() {
assert_eq!(
exponent("e+23"),
Ok((
"",
Some(Exponent {
sign: Sign::Plus,
digits: "23".to_string()
})
))
);
}
#[test]
fn exponent_e_minus() {
assert_eq!(
exponent("e-23"),
Ok((
"",
Some(Exponent {
sign: Sign::Minus,
digits: "23".to_string()
})
))
);
}
#[test]
fn exponent_large_e_plus() {
assert_eq!(
exponent("E+23"),
Ok((
"",
Some(Exponent {
sign: Sign::Plus,
digits: "23".to_string()
})
))
);
}
#[test]
fn exponent_large_e_minus() {
assert_eq!(
exponent("E-23"),
Ok((
"",
Some(Exponent {
sign: Sign::Minus,
digits: "23".to_string()
})
))
);
}
}