Moving back math because I want it variadic

Signed-off-by: teo3300 <matteo.rogora@live.it>
This commit is contained in:
teo3300
2024-01-19 16:59:51 +09:00
parent 7aac258808
commit 2147bc7494
4 changed files with 55 additions and 33 deletions

View File

@ -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::<Vec<String>>().join(" "))), "Print readably all arguments"),
"str" => Fun(|a| Ok(Str(a.iter().map(|i| pr_str(i, false)).collect::<Vec<String>>().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"),

View File

@ -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() {

View File

@ -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 {