use super::ast::Expr;
use std::collections::HashMap;
pub fn eval(expr: &Expr, bindings: &[(&str, f64)]) -> Result<f64, String> {
let map: HashMap<&str, f64> = bindings.iter().copied().collect();
eval_with_map(expr, &map)
}
pub fn eval_with_map(expr: &Expr, bindings: &HashMap<&str, f64>) -> Result<f64, String> {
match expr {
Expr::Lit(v) => Ok(*v),
Expr::Var(name) => bindings
.get(name.as_str())
.copied()
.ok_or_else(|| format!("undefined variable: {}", name)),
Expr::Add(a, b) => Ok(eval_with_map(a, bindings)? + eval_with_map(b, bindings)?),
Expr::Sub(a, b) => Ok(eval_with_map(a, bindings)? - eval_with_map(b, bindings)?),
Expr::Mul(a, b) => Ok(eval_with_map(a, bindings)? * eval_with_map(b, bindings)?),
Expr::Div(a, b) => {
let denom = eval_with_map(b, bindings)?;
if denom == 0.0 {
return Err("division by zero".to_string());
}
Ok(eval_with_map(a, bindings)? / denom)
}
Expr::Pow(base, n) => {
let val = eval_with_map(base, bindings)?;
Ok(val.powi(*n as i32))
}
}
}