use crate::{ app::AppState, lisp::{ error::LispError, expr::{LispExpr, LispFunction}, number::LispNumber, Environment, }, primitive, }; use log::{error, info}; 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), "-", |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), )) } }); primitive!(env, Some(0), "toggle-grid", |_, app| { app.toggle_grid(); Ok(LispExpr::Unit) }); primitive!(env, Some(3), "if", |args, _| { match args { [predicate, then, else_] => { if matches!(predicate, LispExpr::BoolLit(false)) { Ok(else_.clone()) } else { Ok(then.clone()) } } _ => { error!("invalid args for `if` primitive"); Err(LispError::EvalError) } } }); primitive!(env, Some(2), "and", |args, _| { if args .iter() .any(|arg| matches!(arg, LispExpr::BoolLit(false))) { Ok(LispExpr::BoolLit(false)) } else { Ok(LispExpr::BoolLit(true)) } }); primitive!(env, Some(2), "or", |args, _| { if args .iter() .any(|arg| matches!(arg, LispExpr::BoolLit(true))) { Ok(LispExpr::BoolLit(true)) } else { Ok(LispExpr::BoolLit(false)) } }); primitive!(env, Some(1), "not", |args, _| { match args { [val] => { if matches!(val, LispExpr::BoolLit(false)) { Ok(LispExpr::BoolLit(true)) } else { Ok(LispExpr::BoolLit(false)) } } _ => Err(LispError::EvalError), } }); primitive!(env, None, "begin", |args, _| { if args.is_empty() { Err(LispError::EvalError) } else { Ok(args.into_iter().last().unwrap().clone()) } }); primitive!(env, Some(2), "/", |args, _| { if args.is_empty() || 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.div(*unwrap_number(&arg))?; } Ok(LispExpr::Number(acc)) } }); primitive!(env, Some(0), "quit", |_, app| { app.quit(); Ok(LispExpr::Unit) }); env } pub fn eval( expr: &LispExpr, app: &mut AppState, extra_env: Option<&Environment>, ) -> Result { 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) => { lookup_extended(extra_env.unwrap_or(&Environment::new()), &app.lisp_env, id) } LispExpr::List(li) => { let func_expr = &li[0]; match func_expr { LispExpr::Ident(s) => match s.as_ref() { "define" => define_var(&li[1..], app), "set!" => set_var(&li[1..], app), "lambda" => create_lambda(&li[1..]), _ => { let func_expr = eval(&func_expr, app, extra_env)?; match func_expr { LispExpr::PrimitiveFunc(f) => { let mut args = Vec::new(); for item in li[1..].iter() { args.push(eval(item, app, extra_env)?); } f.call(&args, app) } LispExpr::Function(f) => { info!("eval custom func"); let mut args = Vec::new(); for item in li[1..].iter() { args.push(eval(item, app, extra_env)?); } if f.params.len() != args.len() { info!("too many or too little number of args"); Err(LispError::EvalError) // too many or too little number of args } else { let mut local_env: Environment = f.params.into_iter().zip(args).collect(); local_env.extend(app.lisp_env.clone()); if let Some(env) = extra_env { local_env.extend(env.clone()); } if f.body.is_empty() { return Ok(LispExpr::Unit); } else { eval(&LispExpr::List(f.body), app, Some(&local_env)) } } } _ => Err(LispError::EvalError), } } }, _ => Err(LispError::EvalError), } } _ => Err(LispError::ParseError), } } pub fn define_var(args: &[LispExpr], app: &mut AppState) -> Result { if args.len() != 2 { error!("Invalid arity for `define`"); return Err(LispError::EvalError); } match args { [LispExpr::Ident(id), expr] => { let value = eval(&expr, app, None)?; app.lisp_env.insert(id.into(), value); return Ok(LispExpr::Unit); } _ => { error!("Invalid usage of `define`"); return Err(LispError::EvalError); } } } pub fn set_var(args: &[LispExpr], app: &mut AppState) -> Result { if args.len() != 2 { error!("Invalid arity for `define`"); return Err(LispError::EvalError); } match args { [LispExpr::Ident(id), expr] => { let value = eval(&expr, app, None)?; return app .lisp_env .insert(id.into(), value) .ok_or(LispError::EvalError); } _ => { error!("Invalid usage of `define`"); return Err(LispError::EvalError); } } } pub fn create_lambda(cdr: &[LispExpr]) -> Result { if cdr.len() != 2 { // needs params and body error!("needs params and body"); return Err(LispError::EvalError); } info!("creating lambda"); match cdr { [LispExpr::List(params), LispExpr::List(body)] if params.iter().all(|p| matches!(p, LispExpr::Ident(_))) => { return Ok(LispExpr::Function(LispFunction { params: params .into_iter() .map(|p| unwrap_ident(p.clone())) .collect::>(), body: body.clone(), })); } _ => { error!("Invalid usage of `define`"); return Err(LispError::EvalError); } } } pub fn lookup_extended( local: &Environment, super_: &Environment, key: &str, ) -> Result { if let Some(e) = local.get(key) { Ok(e.clone()) } else if let Some(e) = super_.get(key) { Ok(e.clone()) } else { Err(LispError::EvalError) } } pub fn unwrap_number(n: &LispExpr) -> &LispNumber { match n { LispExpr::Number(i) => i, _ => panic!("unwrap_number expected number"), } } pub fn unwrap_ident(i: LispExpr) -> String { match i { LispExpr::Ident(i) => i, _ => panic!("unwrap_ident expected string"), } }