use ::lazy_static::lazy_static;
use ::regex::Regex;
use crate::util::strslice::char_ops::CharOps;
#[derive(Debug)]
pub enum RealParseFailReason {
Invalid,
Overflow,
Underflow,
PrecisionLoss(f64),
}
lazy_static! {
pub static ref REAL_RE: Regex = Regex::new(r"^(?P<multiplier>(?:\+|-?)(?:\d(?:_?\d)*\.\d(?:_?\d)*|\d(?:_?\d)*\.|\.\d(?:_?\d)*))(?:e(?P<exponent>(?:\+|-?)\d(?:_?\d)*))?").unwrap();
}
pub fn parse_real(text: &str) -> Result<f64, RealParseFailReason> {
match REAL_RE.captures(text) {
None => Err(RealParseFailReason::Invalid),
Some(captures) => {
if captures[0].len() < text.len() {
return Err(RealParseFailReason::Invalid)
}
let multiplier = captures
.name("multiplier")
.unwrap()
.as_str()
.without_char('_')
.parse::<f64>()
.unwrap();
match captures.name("exponent") {
None => {
Ok(multiplier)
}
Some(exponent_match) => {
let exponent = exponent_match.as_str().without_char('_').parse::<f64>().unwrap();
Ok(10f64.powf(exponent) * multiplier)
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::parse_real;
fn close(x: f64, y: f64) -> bool {
(x - y).abs() < 1e-8
}
#[test]
fn parse_nonexp_real() {
assert!(close(42., parse_real("42.0").unwrap()));
assert!(close(-0.1, parse_real("-.1").unwrap()));
assert!(close(-1., parse_real("-1.").unwrap()));
assert!(close(12345.6789, parse_real("1_2_3_4_5.6_7_8_9").unwrap()));
}
#[test]
fn parse_exp_real() {
assert!(close(42., parse_real("42.0e0").unwrap()));
assert!(close(-0.1, parse_real("-.1e0").unwrap()));
assert!(close(-1., parse_real("-1.e0").unwrap()));
assert!(close(42., parse_real("42.0e+0").unwrap()));
assert!(close(12345.6789, parse_real("1_2_3_4_5.6_7_8_9e0").unwrap()));
assert!(close(0.42, parse_real("42.0e-2").unwrap()));
assert!(close(-0.001, parse_real("-.1e-2").unwrap()));
assert!(close(-0.01, parse_real("-1.e-2").unwrap()));
assert!(close(123.456_789, parse_real("1_2_3_4_5.6_7_8_9e-2").unwrap()));
assert!(close(42.0, parse_real("42.0e-0_0_0").unwrap()));
}
#[test]
fn invalid_real() {
assert!(parse_real("+_42.0").is_err());
assert!(parse_real("-_42.0").is_err());
assert!(parse_real("_42.0").is_err());
assert!(parse_real("42_.0").is_err());
assert!(parse_real("42._0").is_err());
assert!(parse_real("42.0_").is_err());
assert!(parse_real("42.0e_0").is_err());
assert!(parse_real("42.0e0_").is_err());
assert!(parse_real("42.0e0b0").is_err());
}
}