mirror of
https://github.com/teo3300/rust-mal.git
synced 2026-01-12 09:15:32 +01:00
Moving back math because I want it variadic
Signed-off-by: teo3300 <matteo.rogora@live.it>
This commit is contained in:
16
src/core.rs
16
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::<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"),
|
||||
|
||||
43
src/env.rs
43
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() {
|
||||
|
||||
13
src/types.rs
13
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 {
|
||||
|
||||
Reference in New Issue
Block a user