From 5648ff770984f4e6a21083d692a19d7f298c3b46 Mon Sep 17 00:00:00 2001 From: teo3300 Date: Wed, 17 Jan 2024 20:58:25 +0900 Subject: [PATCH] Moving a whole bunch of stuff in lisp Signed-off-by: teo3300 --- core/core.mal | 19 +++++++++++++++++++ src/core.rs | 17 +++++++++-------- src/env.rs | 10 ++++++++++ src/parse_tools.rs | 5 +++-- src/types.rs | 19 ------------------- tests/assert.mal | 1 + tests/logic.mal | 28 ++++++++++++---------------- 7 files changed, 54 insertions(+), 45 deletions(-) diff --git a/core/core.mal b/core/core.mal index 20943d2..ee0e07a 100644 --- a/core/core.mal +++ b/core/core.mal @@ -1,7 +1,26 @@ ;; Previously in core.rs +; Logic (def! not (fn* [x] (if x nil true))) +(def! and (fn* [a b] + (if a + b))) + +(def! or (fn* [a b] + (if a + true + b))) + +(def! xor (fn* [a b] + (if a + (not b) + b))) + +; Other functions in core.rs +(def! assert-eq (fn* [a b] + (assert (= a b)))) + (def! empty? (fn* [l] (= (count l) 0))) diff --git a/src/core.rs b/src/core.rs index 0168eb1..abb19b1 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1,6 +1,6 @@ use std::env; -use crate::env::{arithmetic_op, car, comparison_op, env_new, env_set, mal_exit, Env}; +use crate::env::{any_zero, arithmetic_op, car, comparison_op, env_new, env_set, mal_exit, Env}; // This is the first time I implement a macro, and I'm copying it // so I will comment this a LOT @@ -35,16 +35,17 @@ use crate::parse_tools::read_file; use crate::printer::pr_str; use crate::reader::{read_str, Reader}; use crate::types::MalType::{Bool, Fun, Int, List, Nil, Str}; -use crate::types::{mal_assert, mal_assert_eq, mal_comp, MalArgs, MalErr}; +use crate::types::{mal_assert, mal_comp, MalArgs, MalErr}; pub fn ns_init() -> Env { env_init!(None, + // That's it, you are all going to be simpler functions "test" => Fun(|_| Ok(Str("This is a test function".to_string())), "Just a test function"), "exit" => Fun(mal_exit, "Quits the program with specified status"), "+" => Fun(|a| arithmetic_op(0, |a, b| a + b, a), "Returns the sum of the arguments"), "-" => Fun(|a| arithmetic_op(0, |a, b| a - b, a), "Returns the difference of the arguments"), "*" => Fun(|a| arithmetic_op(1, |a, b| a * b, a), "Returns the product of the arguments"), - "/" => Fun(|a| arithmetic_op(1, |a, b| a / b, a), "Returns the division of the arguments"), + "/" => Fun(|a| {any_zero(a)?; arithmetic_op(1, |a, b| a / b, a)}, "Returns the division of the arguments"), ">" => Fun(|a| comparison_op(|a, b| a > b, a), "Returns true if the arguments are in strictly descending order, 'nil' otherwise"), "<" => Fun(|a| comparison_op(|a, b| a < b, a), "Returns true if the arguments are in strictly ascending order, 'nil' otherwise"), ">=" => Fun(|a| comparison_op(|a, b| a >= b, a), "Returns true if the arguments are in descending order, 'nil' otherwise"), @@ -57,13 +58,13 @@ pub fn ns_init() -> Env { "list?" => Fun(|a| Ok(Bool(a.iter().all(|el| matches!(el, List(_))))), "Return true if the first argument is a list, false otherwise"), // "empty?" => implemented through "count" in core.mal "count" => Fun(|a| Ok(Int(car(a)?.if_list()?.len() as isize)), "Return the number of elements in the first argument"), - "and" => Fun(|a| Ok(Bool(a.iter().all(|a| !matches!(a, Nil | Bool(false))))), "Returns false if at least one of the arguments is 'false' or 'nil', true otherwise"), - "or" => Fun(|a| Ok(Bool(a.iter().any(|a| !matches!(a, Nil | Bool(false))))), "Returns false if all the arguments are 'false' or 'nil', true otherwise"), - "xor" => Fun(|a| Ok(Bool(a.iter().filter(|a| !matches!(a, Nil | Bool(false))).count() == 1)), "Returns true if one of the arguments is different from 'nil' or 'false', false otherwise"), - // "not" => implemented throught "if" in core.mal + // "and" => implemented through "if" in core.mal + // "or" => implemented through "if" in core.mal + // "xor" => implemented through "if" in core.mal + // "not" => implemented through "if" in core.mal "=" => Fun(mal_comp, "Return true if the first two parameters are the same type and content, in case of lists propagate to all elements"), "assert" => Fun(mal_assert, "Return an error if assertion fails"), - "assert-eq" => Fun(mal_assert_eq, "Return an error if arguments are not the same"), + // "assert-eq" => implemented through "assert" in core.mal // Since the READ errors are Recoverable, when encountering one the reader continues appending to the previous status, // making unreasonable output, to solve this "upgrade" any kind of error in the inner "read_str" to an unrecoverable one, so the reader breaks the cycle "read-string" => Fun(|a| read_str(Reader::new().push(car(a)?.if_string()?)).map_err(MalErr::severe), "Tokenize and read the first argument"), diff --git a/src/env.rs b/src/env.rs index 140c4f7..208e0d8 100644 --- a/src/env.rs +++ b/src/env.rs @@ -106,6 +106,16 @@ pub fn call_func(func: &MalType, args: &[MalType]) -> CallRet { } } +pub fn any_zero(list: &[MalType]) -> MalRet { + if list + .iter() + .any(|x| if let MalType::Int(0) = x { true } else { false }) + { + return Err(MalErr::unrecoverable("Attempting division by 0")); + } + return Ok(M::Nil); +} + pub fn arithmetic_op(set: isize, f: fn(isize, isize) -> isize, args: &[MalType]) -> MalRet { Ok(M::Int(match args.len() { 0 => set, diff --git a/src/parse_tools.rs b/src/parse_tools.rs index eba38e5..ba36290 100644 --- a/src/parse_tools.rs +++ b/src/parse_tools.rs @@ -14,8 +14,9 @@ fn eval_str(line: &str, env: &Env) -> MalRet { pub fn set_home_path(env: &Env) { eval_str( - "(or (def! MAL_HOME (env \"MAL_HOME\")) - (def! MAL_HOME (str (env \"HOME\") \"/.config/mal\")))", + "(if (env \"MAL_HOME\") + (def! MAL_HOME (env \"MAL_HOME\")) + (def! MAL_HOME (str (env \"HOME\") \"/.config/mal\")))", env, ) .unwrap(); diff --git a/src/types.rs b/src/types.rs index 68ddf5c..853fc9a 100644 --- a/src/types.rs +++ b/src/types.rs @@ -120,25 +120,6 @@ pub fn mal_assert(args: &[MalType]) -> MalRet { } } -pub fn mal_assert_eq(args: &[MalType]) -> MalRet { - let (car, cdr) = car_cdr(args)?; - match mal_eq(car, cdr)? { - M::Nil | M::Bool(false) => { - let mut message = String::from("Assertion-eq failed: ["); - message.push_str( - args.iter() - .map(prt) - .collect::>() - .join(" ") - .as_str(), - ); - message.push(']'); - Err(MalErr::unrecoverable(message.as_str())) - } - _ => Ok(M::Nil), - } -} - #[derive(PartialEq, Clone, Copy, Debug)] pub enum Severity { Recoverable, diff --git a/tests/assert.mal b/tests/assert.mal index 1421cbf..a7d9940 100644 --- a/tests/assert.mal +++ b/tests/assert.mal @@ -1,5 +1,6 @@ (assert true) (assert (ok? 1)) (assert-eq nil (ok? (1))) +(assert-eq true (ok? 1)) (ok? (assert true)) (not (ok? (assert nil))) \ No newline at end of file diff --git a/tests/logic.mal b/tests/logic.mal index 71429be..ac0532e 100644 --- a/tests/logic.mal +++ b/tests/logic.mal @@ -9,23 +9,19 @@ (assert (not (not true))) ; or -(assert (not (or false))) -(assert (or 1)) -(assert (or 1 nil false)) -(assert (or 1 1 1)) -(assert (or nil 1 false)) -(assert (not (or nil false))) -(assert (not (or false nil))) +(assert (not (or nil nil))) +(assert (or 1 nil)) +(assert (or nil 1)) +(assert (or 1 1)) ; and -(assert (and 1)) -(assert (not (and nil))) -(assert (and 1 1 1)) -(assert (not (and 1 nil false))) -(assert (not (and nil false))) +(assert (not (and nil nil))) +(assert (not (and 1 nil))) +(assert (not (and 1 nil))) +(assert (and 1 1)) ; xor -(assert (not (xor nil false nil))) -(assert (xor nil 1 nil false nil)) -(assert (not (xor nil 1 false 1))) -(assert (not (xor 1 nil 1 1))) \ No newline at end of file +(assert (not (xor nil nil))) +(assert (xor nil 1)) +(assert (xor 1 nil)) +(assert (not (xor 1 1))) \ No newline at end of file