use crate::{ app::AppState, lisp::{error::LispError, expr::LispExpr, number::LispNumber, Environment}, primitive, }; use log::warn; pub fn is_bound>(env: &mut Environment, name: S) -> bool { env.get(name.as_ref()).is_some() } pub fn new_binding>(env: &mut Environment, name: S, value: LispExpr) { let _ = env.insert(name.as_ref().into(), value); } pub fn with_prelude() -> Environment { let mut env = Environment::new(); primitive!(env, Some(2), "+", |args, _| { if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { Err(LispError::EvalError) } else { Ok(LispExpr::Number( args.iter() .map(|arg| unwrap_number(arg)) .fold(LispNumber::Integer(0), |acc, x| acc + *x), )) } }); primitive!(env, Some(2), "sub", |args, _| { if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { Err(LispError::EvalError) } else { let mut acc = unwrap_number(&args[0]).clone(); for arg in args.into_iter().skip(1) { acc = acc - *unwrap_number(&arg); } Ok(LispExpr::Number(acc)) } }); primitive!(env, Some(2), "*", |args, _| { if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { Err(LispError::EvalError) } else { Ok(LispExpr::Number( args.iter() .map(|arg| unwrap_number(arg)) .fold(LispNumber::Integer(1), |acc, x| acc * *x), )) } }); env } pub fn eval(expr: &LispExpr, app: &mut AppState) -> Result { let env = &mut app.lisp_env; match expr { LispExpr::Unit => Ok(expr.clone()), LispExpr::StringLit(_) => Ok(expr.clone()), LispExpr::Number(_) => Ok(expr.clone()), LispExpr::BoolLit(_) => Ok(expr.clone()), LispExpr::Ident(ref id) => env .get(id) .ok_or_else(|| LispError::EvalError) .map(Clone::clone), LispExpr::List(li) => { let head = &li[0]; match head { LispExpr::Ident(func) => { let func_expr = env .get(func) .map(Clone::clone) .ok_or_else(|| LispError::EvalError)?; match func_expr { LispExpr::PrimitiveFunc(f) => { let mut args = Vec::new(); for item in li[1..].iter() { args.push(eval(item, app)?); } f.call(&args, app) } _ => Err(LispError::EvalError), } } LispExpr::List(_) => { // TODO todo!(); } _ => Err(LispError::EvalError), } } _ => Err(LispError::ParseError), } } pub fn unwrap_number(n: &LispExpr) -> &LispNumber { match n { LispExpr::Number(i) => i, _ => panic!("unwrap_number expected number"), } }