From 2147bc74941b13567c937748293be46fab59d8ce Mon Sep 17 00:00:00 2001 From: teo3300 Date: Fri, 19 Jan 2024 16:59:51 +0900 Subject: [PATCH] Moving back math because I want it variadic Signed-off-by: teo3300 --- core/core.mal | 16 ++++------------ src/core.rs | 16 ++++++++++------ src/env.rs | 43 +++++++++++++++++++++++++++++++++---------- src/types.rs | 13 ++++++++----- 4 files changed, 55 insertions(+), 33 deletions(-) diff --git a/core/core.mal b/core/core.mal index 6ffbf26..09dc308 100644 --- a/core/core.mal +++ b/core/core.mal @@ -5,27 +5,19 @@ (def! and (fn* [a b] (if a - (if b true)))) + b))) (def! or (fn* [a b] (if a - true - (if b true)))) + a + b))) (def! xor (fn* [a b] (if a (not b) - (if b true)))) + b))) ; Arithmetic -(def! + (fn* [a b] - (- a (- 0 b)))) - -(def! / (fn* [a b] - (if (= b 0) - (raise "Attempting division by 0") - (/-unsafe a b)))) - (def! abs (fn* [a] (if (> a 0) a diff --git a/src/core.rs b/src/core.rs index 8bd4199..1ff50b3 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1,6 +1,6 @@ use std::env; -use crate::env::{car, env_new, env_set, mal_exit, num_op, 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 @@ -34,7 +34,7 @@ macro_rules! env_init { 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::MalType::{Fun, Int, List, Nil, Str}; use crate::types::{mal_assert, mal_equals, MalArgs, MalErr}; pub fn ns_init() -> Env { @@ -43,10 +43,14 @@ pub fn ns_init() -> Env { "exit" => Fun(mal_exit, "Quits the program with specified status"), "raise" => Fun(|a| Err(MalErr::unrecoverable(car(a)?.if_string()?)), "Raise an unrecoverable error with the specified message"), // Ok, keep * and / here because computing basic math operators recursively is fun but not too convenient - "-" => Fun(|a| num_op(|a, b| Int(a - b), a), "Returns the difference of the arguments"), - "*" => Fun(|a| num_op(|a, b| Int(a * b), a), "Returns the product of the arguments"), - "/-unsafe" => Fun(|a| num_op(|a, b| Int(a / b), a), "Returns the quotient of the arguments (not checking for division by 0)"), - "<" => Fun(|a| num_op(|a, b| Bool(a < b), a), "Returns true if the first argument is strictly smallerthan the second one, nil otherwise"), + "+" => 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, any_zero(a)?), "Returns the quotient of the arguments (not checking for division by 0)"), + "<" => Fun(|a| comparison_op( |a, b| a < b, a), "Returns true if the first argument is strictly smaller than the second one, nil otherwise"), + ">" => Fun(|a| comparison_op( |a, b| a > b, a), "Returns true if the first argument is strictly greater than the second one, nil otherwise"), + "<=" => Fun(|a| comparison_op( |a, b| a <= b, a), "Returns true if the first argument is smaller than or equal to the second one, nil otherwise"), + ">=" => Fun(|a| comparison_op( |a, b| a >= b, a), "Returns true if the first argument is greater than or equal to the second one, nil otherwise"), "pr-str" => Fun(|a| Ok(Str(a.iter().map(|i| pr_str(i, true)).collect::>().join(" "))), "Print readably all arguments"), "str" => Fun(|a| Ok(Str(a.iter().map(|i| pr_str(i, false)).collect::>().join(""))), "Print non readably all arguments"), "prn" => Fun(|a| {a.iter().for_each(|a| print!("{} ", pr_str(a, false))); Ok(Nil) }, "Print readably all the arguments"), diff --git a/src/env.rs b/src/env.rs index 9f5d0d6..d7573ed 100644 --- a/src/env.rs +++ b/src/env.rs @@ -106,21 +106,44 @@ pub fn call_func(func: &MalType, args: &[MalType]) -> CallRet { } } -pub fn bin_unpack(list: &[MalType]) -> Result<(&MalType, &MalType), MalErr> { - if list.len() != 2 { - return Err(MalErr::unrecoverable("Two arguments required")); +pub fn any_zero(list: &[MalType]) -> Result<&[MalType], MalErr> { + if list.iter().any(|x| matches!(x, M::Int(0))) { + return Err(MalErr::unrecoverable("Attempting division by 0")); } - Ok((&list[0], &list[1])) + Ok(list) } -pub fn num_op(f: fn(isize, isize) -> MalType, args: &[MalType]) -> MalRet { - let (car, cdr) = bin_unpack(args)?; - let car = car.if_number()?; - let cdr = cdr.if_number()?; - Ok(f(car, cdr)) +pub fn arithmetic_op(set: isize, f: fn(isize, isize) -> isize, args: &[MalType]) -> MalRet { + Ok(M::Int(match args.len() { + 0 => set, + 1 => f(set, args[0].if_number()?), + _ => { + // TODO: Maybe an accumulator + let mut left = args[0].if_number()?; + for el in &args[1..] { + left = f(left, el.if_number()?); + } + left + } + })) } -use MalType::Nil; +use MalType::{Bool, Nil}; +pub fn comparison_op(f: fn(isize, isize) -> bool, args: &[MalType]) -> MalRet { + if args.is_empty() { + return Ok(Nil); + } + let (left, rights) = car_cdr(args)?; + let mut left = left.if_number()?; + for right in rights { + let right = right.if_number()?; + if !f(left, right) { + return Ok(Nil); + } + left = right; + } + Ok(Bool(true)) +} pub fn car(list: &[MalType]) -> Result<&MalType, MalErr> { match list.len() { diff --git a/src/types.rs b/src/types.rs index 65343b5..e172acd 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,4 +1,4 @@ -use crate::env::{bin_unpack, Env}; +use crate::env::{car_cdr, Env}; use std::{collections::HashMap, rc::Rc}; // All Mal types should inherit from this @@ -104,10 +104,13 @@ fn mal_compare(args: (&MalType, &MalType)) -> bool { } pub fn mal_equals(args: &[MalType]) -> MalRet { - if args.len() != 2 { - return Err(MalErr::unrecoverable("Expected 2 arguments")); - } - Ok(M::Bool(mal_compare(bin_unpack(args)?))) + Ok(M::Bool(match args.len() { + 0 => true, + _ => { + let (car, cdr) = car_cdr(args)?; + cdr.iter().all(|x| mal_compare((car, x))) + } + })) } pub fn mal_assert(args: &[MalType]) -> MalRet {