diff --git a/core/core.mal b/core/core.mal index 09dc308..8abd1ef 100644 --- a/core/core.mal +++ b/core/core.mal @@ -19,22 +19,13 @@ ; Arithmetic (def! abs (fn* [a] - (if (> a 0) - a - (- 0 a)))) + (if (< a 0) + (- 0 a) + a))) (def! mod (fn* [a b] (- a (* (/ a b) b)))) -(def! > (fn* [a b] - (< b a))) - -(def! >= (fn* [a b] - (not (< a b)))) - -(def! <= (fn* [a b] - (>= b a))) - ; Other functions in core.rs (def! list? (fn* [a] (= (type a) :list))) @@ -45,6 +36,9 @@ (def! assert-eq (fn* [a b] (assert (= a b)))) +(def! assert-fail (fn* [x] + (assert (not (ok? (eval x)))))) + ;; File-interaction functions (def! load-file (fn* [f] (eval (read-string (str "(do " (slurp f) "\nnil)"))))) diff --git a/src/env.rs b/src/env.rs index d7573ed..8d475eb 100644 --- a/src/env.rs +++ b/src/env.rs @@ -1,3 +1,4 @@ +use crate::eval::eval; use crate::types::MalErr; use crate::types::{MalMap, MalRet, MalType}; use std::cell::RefCell; @@ -15,7 +16,7 @@ impl EnvType { .data .borrow() .iter() - .map(|(k, _)| k.clone()) + .map(|(k, _)| k.to_string()) .collect::>(); keys.sort_unstable(); keys @@ -26,27 +27,31 @@ pub type Env = Rc; // Following rust implementation, using shorthand to always pas Reference count pub fn env_new(outer: Option) -> Env { - Rc::new(EnvType { + Env::new(EnvType { data: RefCell::new(MalMap::new()), outer, }) } -pub fn env_set(env: &Env, sym: &str, val: &MalType) -> MalType { +pub fn env_set(env: &Env, sym: &str, val: &MalType) { env.data.borrow_mut().insert(sym.to_string(), val.clone()); - val.clone() } pub fn env_get(env: &Env, sym: &str) -> MalRet { - match env.data.borrow().get(sym) { - Some(val) => Ok(val.clone()), - None => match env.outer.clone() { - Some(outer) => env_get(&outer, sym), - None => Err(MalErr::unrecoverable( - format!("symbol {:?} not defined", sym).as_str(), - )), - }, + let mut iter_env = env; + loop { + if let Some(val) = iter_env.data.borrow().get(sym) { + return Ok(val.clone()); + } + if let Some(outer) = &iter_env.outer { + iter_env = &outer; + continue; + } + return Err(MalErr::unrecoverable( + format!("symbol {:?} not defined", sym).as_str(), + )); } + // Recursive was prettier, but we hate recursion } pub fn env_binds(outer: Env, binds: &MalType, exprs: &[MalType]) -> Result { @@ -93,10 +98,15 @@ pub fn call_func(func: &MalType, args: &[MalType]) -> CallRet { // It's fine to clone the environment here // since this is when the function is actually called match ast.as_ref() { - M::List(list) => Ok(CallFunc::MalFun( - list.last().unwrap_or(&Nil).clone(), - inner_env, - )), + M::List(list) => { + for x in &list[0..list.len() - 1] { + eval(x, inner_env.clone())?; + } + Ok(CallFunc::MalFun( + list.last().unwrap_or(&Nil).clone(), + inner_env, + )) + } _ => scream!(), } } @@ -176,7 +186,7 @@ fn first(list: &[MalType]) -> &[MalType] { // FIXME: Treat as result for now, change later fn last(list: &[MalType]) -> Result<&MalType, MalErr> { match list.len() { - 0 => Err(MalErr::unrecoverable("Mi sono cacato le mutande")), + 0 => Ok(&MalType::Nil), _ => Ok(&list[list.len() - 1]), } } diff --git a/src/eval.rs b/src/eval.rs index 20d1820..3e0923c 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -42,7 +42,9 @@ fn def_bang_form(list: &[MalType], env: Env) -> MalRet { } let (car, _) = car_cdr(list)?; let sym = car.if_symbol()?; - Ok(env_set(&env, sym, &eval(&list[1], env.clone())?)) + let val = &eval(&list[1], env.clone())?; + env_set(&env, sym, val); + Ok(val.clone()) } /// let* special form: @@ -187,17 +189,6 @@ pub fn eval(ast: &MalType, env: Env) -> MalRet { } } -/// Switch ast evaluation depending on it being a list or not and return the -/// result of the evaluation, this function calls "eval_ast" to recursively -/// evaluate asts -/*pub fn eval(ast: &MalType, env: Env) -> MalRet { - match &ast { - M::List(list) if list.is_empty() => Ok(ast.clone()), - M::List(list) if !list.is_empty() => apply(list, env), - _ => eval_ast(ast, env), - } -}*/ - /// Separately evaluate all elements in a collection (list or vector) fn eval_collection(list: &MalArgs, env: Env) -> Result { let mut ret = Vec::new(); @@ -227,248 +218,4 @@ fn eval_ast(ast: &MalType, env: Env) -> MalRet { } } -//////////////////////////////////////////////////////////////////////////////// -// Tests // -//////////////////////////////////////////////////////////////////////////////// - -#[cfg(test)] -mod tests { - - use crate::env::Env; - - macro_rules! load2 { - ($input:expr) => {{ - use crate::reader::{read_str, Reader}; - - let r = Reader::new(); - r.push($input); - &match read_str(&r) { - Ok(v) => match v { - MalType::List(v) => v, - _ => panic!("Not a list"), - }, - _ => panic!("Bad command"), - } - }}; - } - - macro_rules! load { - ($input:expr) => {{ - use crate::env::cdr; - cdr(load2!($input)) - }}; - } - - /*macro_rules! load_f { - ($input:expr, $env:expr) => {{ - use crate::reader::{read_str, Reader}; - use std::rc::Rc; - - let r = Reader::new(); - r.push($input); - let args = match read_str(&r) { - Ok(v) => match v { - MalType::List(v) => v, - _ => panic!("Bad command"), - }, - _ => panic!("Bad command"), - }; - &MalType::List(Rc::new(if args.is_empty() { - Vec::new() - } else { - let f_str = match &args[0] { - MalType::Sym(s) => s.as_str(), - _ => panic!("Can't solve function"), - }; - let f = match env_get(&$env.clone(), f_str) { - Ok(v) => v, - _ => panic!("No such function in env"), - }; - [&[f], &args[1..]].concat() - })) - }}; - }*/ - - fn _env_empty() -> Env { - use crate::env::env_new; - env_new(None) - } - - mod forms { - use crate::env::env_get; - use crate::eval::tests::_env_empty; - use crate::eval::{def_bang_form, fn_star_form, if_form, let_star_form}; - use crate::types::MalType; - - #[test] - fn def_bang() { - let env = _env_empty(); - - assert!(matches!( //x empty - def_bang_form( - load!("(def!) ; empty"), - env.clone()), - Err(e) - if !e.is_recoverable())); - assert!(matches!( //x 1 arg - def_bang_form( - load!("(def! a) ; 1 arg"), - env.clone()), - Err(e) - if !e.is_recoverable())); - assert!(matches!( //x 3 args - def_bang_form( - load!("(def! a 1 2) ; 3 args"), - env.clone()), - Err(e) - if !e.is_recoverable())); - - assert!(matches!( - //v 2 args - def_bang_form(load!("(def! a 1) ; correct a = 1"), env.clone()), - Ok(MalType::Int(1)) - )); - assert!(matches!(env_get(&env, "a"), Ok(MalType::Int(1)))); - } - - #[test] - fn let_star() { - let env = _env_empty(); - assert!( - matches!(let_star_form(load!("(let*)"), env.clone()), Err(e) if !e.is_recoverable()) - ); - assert!( - matches!(let_star_form(load!("(let* 1)"), env.clone()), Err(e) if !e.is_recoverable()) - ); - assert!( - matches!(let_star_form(load!("(let* [a])"), env.clone()), Err(e) if !e.is_recoverable()) - ); /* - assert!(matches!( - let_star_form(load!("(let* ())"), env.clone()), - Ok(MalType::Nil) - )); - assert!(matches!( - let_star_form(load!("(let* (a 1))"), env.clone()), - Ok(MalType::Nil) - )); - assert!(matches!(env_get(&env.clone(), "a"), Err(e) if !e.is_recoverable())); - assert!(matches!( - let_star_form(load!("(let* (a 1 b 2) a b)"), env.clone()), - Ok(MalType::Int(2)) - )); - assert!(matches!(env_get(&env.clone(), "a"), Err(e) if !e.is_recoverable())); - assert!(matches!(env_get(&env.clone(), "b"), Err(e) if !e.is_recoverable())); - assert!(matches!( - let_star_form(load!("(let* (a 1 b 2) (def! c 1) a b)"), env.clone()), - Ok(MalType::Int(2)) - ));*/ - assert!(matches!(env_get(&env.clone(), "c"), Err(e) if !e.is_recoverable())); - } - - /*#[test] - fn _do_form() { - let env = _env_empty(); - assert!(matches!( - do_form(load!("(do)"), env.clone()), - Ok(MalType::Nil) - )); - assert!(matches!( - do_form(load!("(do true)"), env.clone()), - Ok(MalType::Bool(true)) - )); - assert!(matches!( - do_form(load!("(do (def! a 1) 2)"), env.clone()), - Ok(MalType::Int(2)) - )); - assert!(matches!(env_get(&env.clone(), "a"), Ok(MalType::Int(1)))); - }*/ - - #[test] - fn _if_form() { - let env = _env_empty(); - assert!(matches!( - if_form(load!("(if)"), env.clone()), - Err(e) if !e.is_recoverable())); - assert!(matches!( - if_form(load!("(if 1)"), env.clone()), - Err(e) if !e.is_recoverable())); - assert!(matches!( - if_form(load!("(if 1 2 3 4)"), env.clone()), - Err(e) if !e.is_recoverable())); - assert!(matches!( - if_form(load!("(if nil 1)"), env.clone()), - Ok(MalType::Nil) - )); - assert!(matches!( - if_form(load!("(if nil 1 2)"), env.clone()), - Ok(MalType::Int(2)) - )); - assert!(matches!( - if_form(load!("(if true 1)"), env.clone()), - Ok(MalType::Int(1)) - )); - assert!(matches!( - if_form(load!("(if true 1 2)"), env.clone()), - Ok(MalType::Int(1)) - )); - } - - #[test] - fn fn_star() { - let env = _env_empty(); - assert!(matches!( - fn_star_form(load!("(fn* [a b] 1 2)"), env.clone()), - Ok(MalType::MalFun {params, ast, .. }) - if matches!((*params).clone(), MalType::Vector(v) - if matches!(&v[0], MalType::Sym(v) if v == "a") - && matches!(&v[1], MalType::Sym(v) if v == "b") - && matches!((*ast).clone(), MalType::List(v) - if matches!(&v[0], MalType::Int(1)) - && matches!(&v[1], MalType::Int(2)))))); - // We trust the fact that the env does not do silly stuff - assert!(matches!( - fn_star_form(load!("(fn*)"), env.clone()), - Err(e) if !e.is_recoverable())); - assert!(matches!( - fn_star_form(load!("(fn* 1)"), env.clone()), - Err(e) if !e.is_recoverable())); - } - - /* - #[test] - fn _eval_func() { - let env = _env_empty(); - assert!(matches!( - def_bang_form(load!("(def! or (fn* (a b) (if a a b)))"), env.clone()), - Ok(_) - )); - assert!(matches!( - eval_func(&MalType::Int(1)), - Err(e) if !e.is_recoverable())); - assert!(matches!( - eval_func(load_f!("()", env.clone())), - Err(e) if !e.is_recoverable())); - assert!(matches!( - eval_func(load_f!("(or nil nil)", env.clone())), - Ok(v) if matches!(v, MalType::Nil))); - assert!(matches!( - eval_func(load_f!("(or 1 nil)", env.clone())), - Ok(MalType::Int(1)) - )); - assert!(matches!( - eval_func(load_f!("(or nil 1)", env.clone())), - Ok(MalType::Int(1)) - )); - }*/ - - /*#[test] - fn _apply() { - let env = _env_empty(); - assert!(matches!( - apply(load2!("(def! a 1)"), env.clone()), - Ok(MalType::Int(1)) - )); - assert!(matches!(env_get(&env, "a"), Ok(MalType::Int(1)))); - }*/ - } -} +// all tests moved to mal diff --git a/src/mal_tests/mod.rs b/src/mal_tests/mod.rs index 5b12ee4..1e511b1 100644 --- a/src/mal_tests/mod.rs +++ b/src/mal_tests/mod.rs @@ -28,7 +28,7 @@ mod functional { #[test] fn builtin_equals() { - test!("equals"); + test!("equals") } #[test] @@ -38,6 +38,11 @@ mod functional { #[test] fn fibonacci() { - test!("fibonacci"); + test!("fibonacci") + } + + #[test] + fn forms() { + test!("forms") } } diff --git a/src/reader.rs b/src/reader.rs index 200a848..3f94613 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -311,10 +311,10 @@ mod tests { MalMap::new() } }; - assert!(matches!(t.get("n"), Some(x) if matches!(x.clone(), M::Nil))); - assert!(matches!(t.get("t"), Some(x) if matches!(x.clone(), M::Bool(v) if v))); - assert!(matches!(t.get("i"), Some(x) if matches!(x.clone(), M::Int(v) if v == 1))); - assert!(matches!(t.get("s"), Some(x) if matches!(x.clone(), M::Str(v) if v == "str"))); - assert!(matches!(t.get("ʞ:s"), Some(x) if matches!(x.clone(), M::Key(v) if v == "ʞ:sym"))); + assert!(matches!(t.get("n"), Some(x) if matches!(&x, M::Nil))); + assert!(matches!(t.get("t"), Some(x) if matches!(&x, M::Bool(v) if *v))); + assert!(matches!(t.get("i"), Some(x) if matches!(&x, M::Int(v) if *v == 1))); + assert!(matches!(t.get("s"), Some(x) if matches!(&x, M::Str(v) if v == "str"))); + assert!(matches!(t.get("ʞ:s"), Some(x) if matches!(&x, M::Key(v) if v == "ʞ:sym"))); } } diff --git a/tests/assert.mal b/tests/assert.mal index a7d9940..4b6686c 100644 --- a/tests/assert.mal +++ b/tests/assert.mal @@ -3,4 +3,6 @@ (assert-eq nil (ok? (1))) (assert-eq true (ok? 1)) (ok? (assert true)) -(not (ok? (assert nil))) \ No newline at end of file +(not (ok? (assert nil))) +(assert (not (ok? (assert-fail '1)))) +(assert (ok? (assert-fail '(1)))) \ No newline at end of file