type CharIter<'a> = std::iter::Peekable<std::str::Chars<'a>>;
pub fn eval_math(input: &str) -> Result<f64, String> {
let mut chars = input.trim().chars().peekable();
let result = parse_expr(&mut chars)?;
skip_whitespace(&mut chars);
if chars.peek().is_some() {
let remaining: String = chars.collect();
return Err(format!("Unexpected characters at end: '{}'", remaining));
}
Ok(result)
}
fn skip_whitespace(chars: &mut CharIter) {
while let Some(&c) = chars.peek() {
if c.is_whitespace() {
chars.next();
} else {
break;
}
}
}
fn parse_expr(chars: &mut CharIter) -> Result<f64, String> {
let mut value = parse_term(chars)?;
loop {
skip_whitespace(chars);
match chars.peek() {
Some(&'+') => {
chars.next();
value += parse_term(chars)?;
}
Some(&'-') => {
chars.next();
value -= parse_term(chars)?;
}
_ => break,
}
}
Ok(value)
}
fn parse_term(chars: &mut CharIter) -> Result<f64, String> {
let mut value = parse_factor(chars)?;
loop {
skip_whitespace(chars);
match chars.peek() {
Some(&'*') => {
chars.next();
value *= parse_factor(chars)?;
}
Some(&'/') => {
chars.next();
value /= parse_factor(chars)?;
}
_ => break,
}
}
Ok(value)
}
fn parse_factor(chars: &mut CharIter) -> Result<f64, String> {
skip_whitespace(chars);
if let Some(&'(') = chars.peek() {
chars.next(); let value = parse_expr(chars)?;
skip_whitespace(chars);
if chars.next() != Some(')') {
return Err("Expected ')'".into());
}
return Ok(value);
}
let mut negative = false;
if let Some(&'-') = chars.peek() {
chars.next();
negative = true;
}
let mut num_str = String::new();
while let Some(&c) = chars.peek() {
if c.is_ascii_digit() || c == '.' {
num_str.push(c);
chars.next();
} else {
break;
}
}
if num_str.is_empty() {
return Err("Expected number".into());
}
let value = num_str.parse::<f64>().map_err(|_| format!("Invalid number: {}", num_str))?;
Ok(if negative { -value } else { value })
}