use crate::{ app::AppState, lisp::{ error::{EvalError, LispError}, expr::{Arity, Ident, LispExpr, LispFunction}, Environment, }, type_match, }; use std::convert::TryInto; use log::{error, info}; pub type Context = Vec; pub struct Evaluator<'ctx, 'file, 'global> { pub app: &'global mut AppState<'ctx, 'file>, pub context: Context, } impl<'ctx, 'file, 'global> Evaluator<'ctx, 'file, 'global> where 'ctx: 'global, 'file: 'global, { pub fn eval(&mut self, expr: &LispExpr) -> Result { match expr { LispExpr::Unit => Ok(expr.clone()), LispExpr::StringLit(_) => Ok(expr.clone()), LispExpr::Char(_) => Ok(expr.clone()), LispExpr::Number(_) => Ok(expr.clone()), LispExpr::BoolLit(_) => Ok(expr.clone()), LispExpr::Ident(ref id) => lookup(&self.app.lisp_env, id), LispExpr::Quote(item, _) => match item.as_ref() { i @ LispExpr::Unit | i @ LispExpr::StringLit(_) | i @ LispExpr::Char(_) | i @ LispExpr::Number(_) | i @ LispExpr::BoolLit(_) => Ok(i.clone()), _ => Ok(*item.clone()), }, LispExpr::List(li) => { let func_expr = &li[0]; match func_expr { LispExpr::Ident(s) => match s.as_ref() { "define" => self.define_var(&li[1..]), "set!" => self.set_var(&li[1..]), "lambda" => create_lambda(&li[1..]), "if" => self.eval_if(&li[1..]), "quote" => eval_quote(&li[1..]), _ => { let mut new_ls = vec![self.eval(&func_expr)?]; new_ls.extend(li[1..].to_vec()); self.eval(&(LispExpr::List(new_ls))) } }, LispExpr::PrimitiveFunc(f) => { let mut args = Vec::new(); // context.push(f.name.to_string()); for item in li[1..].iter() { args.push(self.eval(item)?); } f.call(&args, &mut self.app) } LispExpr::Function(f) => { let mut args = Vec::new(); for item in li[1..].iter() { let i = self.eval(item)?; args.push(i); } if f.params.len() != args.len() { info!("too many or too little number of args"); Err(EvalError::ArgumentCount(Arity::Exact(f.params.len())).into()) } else { let nested_env: Environment = f.params.clone().into_iter().zip(args).collect(); self.app.lisp_env.push(nested_env); let result = if f.body.is_empty() { Ok(LispExpr::Unit) } else { self.eval(&LispExpr::List(f.body.clone())) }; self.app.lisp_env.pop(); return result; } } LispExpr::List(_) => { info!("list as funciton"); let func_expr = self.eval(&func_expr)?; let mut new_ls = vec![func_expr]; new_ls.extend(li[1..].to_vec()); self.eval(&(LispExpr::List(new_ls))) } _ => Err(EvalError::BadForm.into()), } } _ => Err(EvalError::BadForm.into()), } } pub fn define_var(&mut self, args: &[LispExpr]) -> Result { let arity = Arity::Exact(2); if !arity.is_valid(args) { return Err(arity.to_error()); } match args { [LispExpr::Ident(id), expr] => { let value = self.eval(&expr)?; let local_env = &mut self.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 = shorthand[0].unwrap_ident(); 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 = &mut self.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(&mut self, args: &[LispExpr]) -> Result { let arity = Arity::Exact(2); if !arity.is_valid(args) { return Err(arity.to_error()); } match args { [LispExpr::Ident(id), expr] => { let value = self.eval(&expr)?; let local_env = self.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 eval_if(&mut self, args: &[LispExpr]) -> Result { let arity = Arity::Exact(3); if !arity.is_valid(args) { return Err(arity.to_error()); } else { match args { [predicate, then, else_] => { let predicate = self.eval(&predicate)?; if matches!(predicate, LispExpr::BoolLit(false)) { return self.eval(&else_); } else { return self.eval(&then); } } _ => { panic!("panicked at `if` expression") } } } } } pub fn eval_quote(args: &[LispExpr]) -> Result { let arity = Arity::Exact(1); if !arity.is_valid(args) { return Err(arity.to_error()); } else { match &args[0] { LispExpr::Quote(item, depth) => Ok(LispExpr::Quote(item.clone(), depth + 1)), i @ LispExpr::Unit | i @ LispExpr::StringLit(_) | i @ LispExpr::Char(_) | i @ LispExpr::Number(_) | i @ LispExpr::BoolLit(_) => Ok(i.clone()), _ => { let quoted_expr = Box::new(args[0].clone()); Ok(LispExpr::Quote(quoted_expr, 1)) } } } } pub fn create_lambda(cdr: &[LispExpr]) -> Result { let arity: Arity = Arity::Exact(2); if !arity.is_valid(cdr) { return Err(arity.to_error()); } match cdr { [LispExpr::List(params), LispExpr::List(body)] if type_match!(params, (..) => LispExpr::Ident(_)) => { return Ok(LispExpr::Function(LispFunction { params: params .into_iter() .map(|p| p.unwrap_ident()) .collect::>(), body: body.clone(), })); } _ => { error!("Invalid usage of `lambda`"); return Err(EvalError::BadForm.into()); } } } pub fn lookup(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(&env_list[..env_list.len() - 1], key); } } }