From fac291446581b1dad939545bafd6da349c4dfd40 Mon Sep 17 00:00:00 2001 From: Akshay Date: Sat, 10 Apr 2021 10:23:35 +0530 Subject: add `for` primitive --- src/lisp/eval.rs | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lisp/prelude.rs | 18 +++++++++++++++ 2 files changed, 81 insertions(+) (limited to 'src/lisp') diff --git a/src/lisp/eval.rs b/src/lisp/eval.rs index 329b6ab..0cf267c 100644 --- a/src/lisp/eval.rs +++ b/src/lisp/eval.rs @@ -41,6 +41,7 @@ where "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..]), _ => { @@ -214,6 +215,55 @@ where } } + 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 = @@ -326,7 +376,9 @@ mod tests { 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) { @@ -380,4 +432,15 @@ mod tests { 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(); + } } diff --git a/src/lisp/prelude.rs b/src/lisp/prelude.rs index f5ff13a..e24ade5 100644 --- a/src/lisp/prelude.rs +++ b/src/lisp/prelude.rs @@ -437,5 +437,23 @@ pub fn new_env() -> Result { } }); + primitive!(env, Arity::Exact(2), "range", |args, _| { + if type_match!( + args, + 0 => LispExpr::Number(LispNumber::Integer(_)), + 1 => LispExpr::Number(LispNumber::Integer(_))) + { + let lower = args[0].unwrap_number().unwrap_integer(); + let upper = args[1].unwrap_number().unwrap_integer(); + Ok(LispExpr::List( + (lower..upper) + .map(|i| LispExpr::Number(LispNumber::Integer(i))) + .collect::>(), + )) + } else { + Err(EvalError::TypeMismatch.into()) + } + }); + Ok(env) } -- cgit v1.2.3