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<'global> { pub app: &'global mut AppState, pub context: Context, } impl<'global> Evaluator<'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, _) => Ok(apply_quote(&item.as_ref())), 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..]), "cond" => self.eval_cond(&li[1..]), "for" => self.eval_for(&li[1..]), "quote" => Ok(apply_quote(&li[1])), "let" => self.eval_let(&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 = self.eval(&f.body); self.app.lisp_env.pop(); 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.check(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()); } Ok(LispExpr::Unit) } [LispExpr::List(shorthand), 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: Box::new(body.clone()), }); let local_env = &mut self.app.lisp_env.last_mut(); if let Some(env) = local_env { env.insert(id, value); } else { error!("Unable to create global definition"); return Err(EvalError::BadForm.into()); } 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.check(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 { env.insert(id.into(), value) .ok_or_else(|| EvalError::UnboundVariable(id.into()).into()) } else { error!("Unable to set in global env!"); Err(EvalError::BadForm.into()) } } _ => { error!("Invalid usage of `set!`"); Err(EvalError::BadForm.into()) } } } pub fn eval_if(&mut self, args: &[LispExpr]) -> Result { let arity = Arity::Exact(3); if !arity.check(args) { Err(arity.to_error()) } else { match args { [predicate, then, else_] => { let predicate = self.eval(&predicate)?; if matches!(predicate, LispExpr::BoolLit(false)) { self.eval(&else_) } else { self.eval(&then) } } _ => { panic!("panicked at `if` expression") } } } } pub fn eval_cond(&mut self, args: &[LispExpr]) -> Result { let arity = Arity::Atleast(1); let valid_cond_stmt = |expr: &LispExpr| matches!(expr, LispExpr::List(v) if v.len() == 2); if !arity.check(args) { Err(arity.to_error()) } else { for cond_stmt in args { if valid_cond_stmt(cond_stmt) { match &cond_stmt.unwrap_list()[..] { [predicate, then] => { if self.eval(&predicate)?.cast_bool() { return self.eval(&then); } } _ => return Err(EvalError::BadForm.into()), } } else { error!("bad `cond` form"); return Err(EvalError::BadForm.into()); } } Ok(LispExpr::Unit) } } pub fn eval_for(&mut self, args: &[LispExpr]) -> Result { let arity = Arity::Exact(2); let valid_binding_stmt = |expr: &LispExpr| { matches!( expr, LispExpr::List(v) if v.len() == 2 && matches!(v[0], LispExpr::Ident(_))) }; if !arity.check(args) { Err(arity.to_error()) } else { let nested_env = Environment::new(); self.app.lisp_env.push(nested_env); match args { [binding, body] => { if valid_binding_stmt(binding) { let binding = binding.unwrap_list(); let binding_name = binding[0].unwrap_ident(); let binding_ls = self.eval(&binding[1])?; if matches!(binding_ls, LispExpr::List(_)) { let binding_ls = binding_ls.unwrap_list(); let mut result = vec![]; for bind_val in binding_ls.iter() { let value = self.eval(&bind_val)?; if let Some(env) = self.app.lisp_env.last_mut() { env.insert(binding_name.clone(), value); } result.push(self.eval(body)?); } self.app.lisp_env.pop(); Ok(LispExpr::List(result)) } else { error!("invalid binding form"); Err(EvalError::BadForm.into()) } } else { error!("invalid binding form"); Err(EvalError::BadForm.into()) } } _ => { error!("invalid for loop args"); Err(EvalError::BadForm.into()) } } } } pub fn eval_let(&mut self, args: &[LispExpr]) -> Result { let arity = Arity::Exact(2); let valid_binding_stmt = |expr: &LispExpr| matches!(expr, LispExpr::List(v) if v.len() == 2); if !arity.check(args) { Err(arity.to_error()) } else { let nested_env = Environment::new(); self.app.lisp_env.push(nested_env); match args { [LispExpr::List(bindings), body] => { for binding_stmt in bindings { if valid_binding_stmt(binding_stmt) { match &binding_stmt.unwrap_list()[..] { [LispExpr::Ident(id), bind_val] => { let value = self.eval(&bind_val)?; if let Some(env) = self.app.lisp_env.last_mut() { env.insert(id.into(), value); } } _ => { error!("bad let binding form"); return Err(EvalError::BadForm.into()); } } } else { error!("bad `let` form"); return Err(EvalError::BadForm.into()); } } let result = self.eval(&body); self.app.lisp_env.pop(); result } _ => { error!("bad `let` form"); Err(EvalError::BadForm.into()) } } } } } pub fn apply_quote(arg: &LispExpr) -> LispExpr { match arg { i @ LispExpr::Unit | i @ LispExpr::StringLit(_) | i @ LispExpr::Char(_) | i @ LispExpr::Number(_) | i @ LispExpr::BoolLit(_) => i.clone(), LispExpr::Quote(expr, depth) => LispExpr::Quote(Box::clone(expr), depth + 1), LispExpr::List(ls) => LispExpr::List(ls.iter().map(|a| apply_quote(a)).collect::>()), _ => arg.clone(), } } pub fn create_lambda(cdr: &[LispExpr]) -> Result { let arity: Arity = Arity::Exact(2); if !arity.check(cdr) { return Err(arity.to_error()); } match cdr { [LispExpr::List(params), body] if type_match!(params, (..) => LispExpr::Ident(_)) => { Ok(LispExpr::Function(LispFunction { params: params.iter().map(|p| p.unwrap_ident()).collect::>(), body: Box::new(body.clone()), })) } _ => { error!("Invalid usage of `lambda`"); Err(EvalError::BadForm.into()) } } } pub fn lookup(env_list: &[Environment], key: &str) -> Result { if env_list.is_empty() { Err(EvalError::UnboundVariable(key.into()).into()) } else { let local_env = env_list.last().unwrap(); if let Some(val) = local_env.get(key) { Ok(val.clone()) } else { lookup(&env_list[..env_list.len() - 1], key) } } } #[cfg(test)] mod tests { use super::*; use crate::lisp::{expr::LispExpr, lex::Lexer, number::LispNumber, parse::Parser}; fn run(code: &str, app: &mut AppState) -> LispExpr { let mut parser = Parser::new(Lexer::new(code)); let mut evaluator = Evaluator { app, context: Vec::new(), }; evaluator .eval(&parser.parse_single_expr().unwrap()) .unwrap() } #[test] fn eval_all() { let sdl_context = sdl2::init().unwrap(); let ttf_context = sdl2::ttf::init().unwrap(); let mut app = AppState::init(100, 100, &sdl_context, &ttf_context, None, None).unwrap(); eval_arithmetic(&mut app); eval_logical(&mut app); eval_looping(&mut app); eval_quote(&mut app); eval_std_tests(&mut app); } fn eval_arithmetic(app: &mut AppState) { assert_eq!( run("(+ 1 2 3)", app), LispExpr::Number(LispNumber::Integer(6)) ); assert_eq!( run("(+ 1.1 2.2 3.3)", app), LispExpr::Number(LispNumber::Float(6.6)) ); assert_eq!( run("(* 1 2 3 4 5)", app), LispExpr::Number(LispNumber::Integer(120)) ); assert_eq!(run("(< 1 2)", app), LispExpr::BoolLit(true)); assert_eq!(run("(> 6 5 4 3 2 1)", app), LispExpr::BoolLit(true)); assert_eq!(run("(< 1 2 3 4 5 6)", app), LispExpr::BoolLit(true)); assert_eq!(run("(>= 5 5 4 3 2 1)", app), LispExpr::BoolLit(true)); assert_eq!(run("(<= 2 2 3 4 5 6)", app), LispExpr::BoolLit(true)); } fn eval_quote(app: &mut AppState) { assert!(run("(quote a)", app).cast_bool()); assert!(run("(eq? 'a 'a)", app).cast_bool()); assert!(run("(eq? '(1 2 3) '(1 2 3))", app).cast_bool()); assert!(run("(eq? '(1 '(1 2 3)) '(1 '(1 2 3)))", app).cast_bool(),); assert!(run("(eq? '#t '#t)", app).cast_bool()); assert!(run("(eq? 1 '1)", app).cast_bool()); assert_eq!( run("'(1 2 3)", app), LispExpr::List( vec![1, 2, 3] .into_iter() .map(LispNumber::Integer) .map(LispExpr::Number) .collect() ), ); assert_eq!( run("(caar (cdr '(1 (4 5))))", app), LispExpr::Number(LispNumber::Integer(4)) ); assert_eq!(run("''1", app), LispExpr::Number(LispNumber::Integer(1))); } fn eval_logical(app: &mut AppState) { assert!(run("(and #t #t)", app).cast_bool()); assert!(run("(or #f #t)", app).cast_bool()); assert!(!run("(not #t)", app).cast_bool()); assert_eq!(run("(not #f)", app), run("(not (not #t))", app)); } fn eval_looping(app: &mut AppState) { assert_eq!( run("(for (i '(1 2 3)) (+ i 2))", app), run("(map (lambda (x) (+ x 2)) '(1 2 3))", app) ); } fn eval_std_tests(app: &mut AppState) { crate::utils::load_script("src/lisp/test.lisp", app).unwrap(); } }