use crate::{ app::AppState, lisp::{ error::{EvalError, LispError}, expr::{Ident, LispExpr, LispFunction}, number::LispNumber, EnvList, Environment, }, }; use std::convert::TryInto; use log::{error, info}; pub fn eval(expr: &LispExpr, app: &mut AppState) -> 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(&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)?; 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) } LispExpr::Function(f) => { info!("eval custom func"); let mut args = Vec::new(); for item in li[1..].iter() { args.push(eval(item, app)?); } if f.params.len() != args.len() { info!("too many or too little number of args"); Err(EvalError::ArgumentCount(Some(f.params.len() as u32)) .into()) } else { let mut nested_env: Environment = f.params.into_iter().zip(args).collect(); app.lisp_env.push(nested_env); let result = if f.body.is_empty() { Ok(LispExpr::Unit) } else { eval(&LispExpr::List(f.body), app) }; app.lisp_env.pop(); return result; } } _ => Err(EvalError::BadForm.into()), } } }, _ => Err(EvalError::BadForm.into()), } } _ => Err(EvalError::BadForm.into()), } } pub fn define_var(args: &[LispExpr], app: &mut AppState) -> Result { if args.len() != 2 { error!("Invalid arity for `define`"); return Err(EvalError::ArgumentCount(Some(2)).into()); } match args { [LispExpr::Ident(id), expr] => { let value = eval(&expr, app)?; let local_env = app.lisp_env.last_mut(); if let Some(env) = local_env { env.insert(id.into(), value); } else { error!("Unable to create global definition"); return Err(EvalError::BadForm.into()); } return Ok(LispExpr::Unit); } [LispExpr::List(shorthand), LispExpr::List(body)] => { // (define (func arg) ) shorthand let id = unwrap_ident(shorthand[0].clone()); let params = if shorthand.len() > 1 { &shorthand[1..] } else { &[] } .to_vec() .into_iter() .map(|arg| arg.try_into()) .collect::, LispError>>()?; let value = LispExpr::Function(LispFunction { params, body: body.to_vec(), }); let local_env = app.lisp_env.last_mut(); if let Some(env) = local_env { env.insert(id.into(), value); } else { error!("Unable to create global definition"); return Err(EvalError::BadForm.into()); } return Ok(LispExpr::Unit); } _ => { error!("Invalid usage of `define`"); Err(EvalError::BadForm.into()) } } } pub fn set_var(args: &[LispExpr], app: &mut AppState) -> Result { if args.len() != 2 { error!("Invalid arity for `define`"); return Err(EvalError::ArgumentCount(Some(2)).into()); } match args { [LispExpr::Ident(id), expr] => { let value = eval(&expr, app)?; let local_env = app.lisp_env.last_mut(); if let Some(env) = local_env { return env .insert(id.into(), value) .ok_or(EvalError::UnboundVariable(id.into()).into()); } else { error!("Unable to set in global env!"); return Err(EvalError::BadForm.into()); } } _ => { error!("Invalid usage of `set!`"); return Err(EvalError::BadForm.into()); } } } pub fn create_lambda(cdr: &[LispExpr]) -> Result { if cdr.len() != 2 { // needs params and body error!("needs params and body"); return Err(EvalError::ArgumentCount(Some(2)).into()); } 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 `lambda`"); return Err(EvalError::BadForm.into()); } } } pub fn lookup_extended(env_list: &[Environment], key: &str) -> Result { if env_list.is_empty() { return Err(EvalError::UnboundVariable(key.into()).into()); } else { let local_env = env_list.last().unwrap(); if let Some(val) = local_env.get(key) { return Ok(val.clone()); } else { return lookup_extended(&env_list[..env_list.len() - 1], key); } } } 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"), } }