From dbc0ab93141784dc07a7e193024fc5716dd34214 Mon Sep 17 00:00:00 2001 From: Akshay Date: Wed, 31 Mar 2021 16:51:41 +0530 Subject: handle quoted objects better; add cond form --- src/lisp/eval.rs | 132 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 101 insertions(+), 31 deletions(-) (limited to 'src/lisp/eval.rs') diff --git a/src/lisp/eval.rs b/src/lisp/eval.rs index 2e46251..cb8ce68 100644 --- a/src/lisp/eval.rs +++ b/src/lisp/eval.rs @@ -14,15 +14,14 @@ use log::{error, info}; pub type Context = Vec; -pub struct Evaluator<'ctx, 'file, 'global> { - pub app: &'global mut AppState<'ctx, 'file>, +pub struct Evaluator<'ctx, 'global> { + pub app: &'global mut AppState<'ctx>, pub context: Context, } -impl<'ctx, 'file, 'global> Evaluator<'ctx, 'file, 'global> +impl<'ctx, 'global> Evaluator<'ctx, 'global> where 'ctx: 'global, - 'file: 'global, { pub fn eval(&mut self, expr: &LispExpr) -> Result { match expr { @@ -32,14 +31,7 @@ where 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::Quote(_, _) => Ok(expr.clone()), LispExpr::List(li) => { let func_expr = &li[0]; match func_expr { @@ -48,7 +40,8 @@ where "set!" => self.set_var(&li[1..]), "lambda" => create_lambda(&li[1..]), "if" => self.eval_if(&li[1..]), - "quote" => eval_quote(&li[1..]), + "cond" => self.eval_cond(&li[1..]), + "quote" => quote_var(&li[1..]), _ => { let mut new_ls = vec![self.eval(&func_expr)?]; new_ls.extend(li[1..].to_vec()); @@ -101,7 +94,7 @@ where pub fn define_var(&mut self, args: &[LispExpr]) -> Result { let arity = Arity::Exact(2); - if !arity.is_valid(args) { + if !arity.check(args) { return Err(arity.to_error()); } match args { @@ -152,7 +145,7 @@ where pub fn set_var(&mut self, args: &[LispExpr]) -> Result { let arity = Arity::Exact(2); - if !arity.is_valid(args) { + if !arity.check(args) { return Err(arity.to_error()); } match args { @@ -177,7 +170,7 @@ where pub fn eval_if(&mut self, args: &[LispExpr]) -> Result { let arity = Arity::Exact(3); - if !arity.is_valid(args) { + if !arity.check(args) { return Err(arity.to_error()); } else { match args { @@ -195,31 +188,45 @@ where } } } + + 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) { + return 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()); + } + } + return Ok(LispExpr::Unit); + } + } } -pub fn eval_quote(args: &[LispExpr]) -> Result { +pub fn quote_var(args: &[LispExpr]) -> Result { let arity = Arity::Exact(1); - if !arity.is_valid(args) { + if !arity.check(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)) - } - } + return Ok(args[0].clone().quote(1)); } } pub fn create_lambda(cdr: &[LispExpr]) -> Result { let arity: Arity = Arity::Exact(2); - if !arity.is_valid(cdr) { + if !arity.check(cdr) { return Err(arity.to_error()); } match cdr { @@ -252,3 +259,66 @@ pub fn lookup(env_list: &[Environment], key: &str) -> Result LispExpr { + let mut parser = Parser::new(Lexer::new(code, 0)); + 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_quote(&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()); + } + + 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)); + } +} -- cgit v1.2.3