Moving even more stuff out of the rust core

Signed-off-by: teo3300 <matteo.rogora@live.it>
This commit is contained in:
teo3300
2024-01-18 13:42:41 +09:00
parent aa32495a9d
commit d8c045a5eb
6 changed files with 92 additions and 142 deletions

View File

@ -1,6 +1,6 @@
use std::env;
use crate::env::{any_zero, arithmetic_op, car, comparison_op, env_new, env_set, mal_exit, Env};
use crate::env::{car, env_new, env_set, mal_exit, num_op, Env};
// This is the first time I implement a macro, and I'm copying it
// so I will comment this a LOT
@ -41,14 +41,11 @@ pub fn ns_init() -> Env {
env_init!(None,
// That's it, you are all going to be simpler functions
"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| {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"),
"<=" => Fun(|a| comparison_op(|a, b| a <= b, a), "Returns true if the arguments are in ascending order, 'nil' otherwise"),
"-" => 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"),
"/" => Fun(|a| num_op(|a, b| Int(a / b), a), "Returns the division of the arguments"),
">" => Fun(|a| num_op(|a, b| Bool(a > b), a), "Returns true if the first argument is strictly greater than the second one, nil otherwise"),
"<" => Fun(|a| num_op(|a, b| Bool(a < b), a), "Returns true if the first argument is strictly smallerthan 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"),
@ -56,7 +53,7 @@ pub fn ns_init() -> Env {
"list" => Fun(|a| Ok(List(MalArgs::new(a.to_vec()))), "Return the arguments as a list"),
"list?" => Fun(|a| Ok(Bool(a.iter().all(|el| matches!(el, List(_))))), "Return true if the first argument is a list, false otherwise"),
"count" => Fun(|a| Ok(Int(car(a)?.if_list()?.len() as isize)), "Return the number of elements in the first argument"),
"=" => Fun(mal_comp, "Return true if the first two parameters are the same type and content, in case of lists propagate to all elements"),
"=" => Fun(mal_comp, "Return true if the first two parameters are the same type and content, in case of lists propagate to all elements (NOT IMPLEMENTED for 'Map', 'Fun' and 'MalFun')"),
"assert" => Fun(mal_assert, "Return an error if assertion fails"),
"read-string" => Fun(|a| read_str(Reader::new().push(car(a)?.if_string()?)).map_err(MalErr::severe), "Tokenize and read the first argument"),
"slurp" => Fun(|a| Ok(Str(read_file(car(a)?.if_string()?)?)), "Read a file and return the content as a string"),

View File

@ -106,45 +106,21 @@ pub fn call_func(func: &MalType, args: &[MalType]) -> CallRet {
}
}
pub fn any_zero(list: &[MalType]) -> MalRet {
if list.iter().any(|x| matches!(x, MalType::Int(0))) {
return Err(MalErr::unrecoverable("Attempting division by 0"));
pub fn bin_unpack(list: &[MalType]) -> Result<(&MalType, &MalType), MalErr> {
if list.len() != 2 {
return Err(MalErr::unrecoverable("Two arguments required"));
}
Ok(M::Nil)
Ok((&list[0], &list[1]))
}
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
}
}))
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))
}
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))
}
use MalType::Nil;
pub fn car(list: &[MalType]) -> Result<&MalType, MalErr> {
match list.len() {
@ -189,7 +165,7 @@ pub fn first_last(list: &[MalType]) -> (&[MalType], Result<&MalType, MalErr>) {
use std::process::exit;
pub fn mal_exit(list: &[MalType]) -> MalRet {
match car_cdr(list)?.0 {
match car(list)? {
MalType::Int(val) => exit(*val as i32),
_ => exit(-1),
}

View File

@ -1,4 +1,4 @@
use crate::env::{car_cdr, Env};
use crate::env::{bin_unpack, Env};
use std::{collections::HashMap, rc::Rc};
// All Mal types should inherit from this
@ -73,51 +73,31 @@ impl MalType {
use crate::types::MalType as M;
// That's a quite chonky function
fn mal_eq(a: &MalType, b: &[MalType]) -> MalRet {
Ok(M::Bool(match a {
M::Nil => b.iter().all(|el| matches!(el, M::Nil)),
M::Bool(a) => b.iter().all(|el| matches!(el, M::Bool(b) if a == b)),
M::Int(a) => b.iter().all(|el| matches!(el, M::Int(b) if a == b)),
M::Key(a) => b.iter().all(|el| matches!(el, M::Key(b) if a == b)),
M::Str(a) => b.iter().all(|el| matches!(el, M::Str(b) if a == b)),
M::List(a) => b.iter().all(|el| {
matches!(el, M::List(b)
if a.len() == b.len()
&& a.iter().zip(b.iter()).all(
|(a, b)| matches!(mal_eq(a, &[b.clone()]),
Ok(M::Bool(true)))))
}),
M::Vector(a) => b.iter().all(|el| {
matches!(el, M::Vector(b)
if a.len() == b.len()
&& a.iter().zip(b.iter()).all(
|(a, b)| matches!(mal_eq(a, &[b.clone()]),
Ok(M::Bool(true)))))
}),
_ => {
return Err(MalErr::unrecoverable(
"Comparison not implemented for 'Map', 'Fun', 'MalFun' and 'Sym'",
))
fn mal_eq(args: (&MalType, &MalType)) -> bool {
match (args.0, args.1) {
(M::Nil, M::Nil) => true,
(M::Bool(a), M::Bool(b)) => a == b,
(M::Int(a), M::Int(b)) => a == b,
(M::Key(a), M::Key(b)) | (M::Str(a), M::Str(b)) | (M::Sym(a), M::Sym(b)) => a == b,
(M::List(a), M::List(b)) | (M::Vector(a), M::Vector(b)) => {
a.len() == b.len() && a.iter().zip(b.iter()).all(mal_eq)
}
}))
_ => false,
}
}
pub fn mal_comp(args: &[MalType]) -> MalRet {
match args.len() {
0 => Ok(M::Bool(true)),
_ => {
let (car, cdr) = car_cdr(args)?;
mal_eq(car, cdr)
}
if args.len() != 2 {
return Err(MalErr::unrecoverable("Expected 2 arguments"));
}
Ok(M::Bool(mal_eq(bin_unpack(args)?)))
}
pub fn mal_assert(args: &[MalType]) -> MalRet {
if args.iter().any(|i| matches!(i, M::Nil | M::Bool(false))) {
Err(MalErr::unrecoverable("Assertion failed"))
} else {
Ok(M::Nil)
return Err(MalErr::unrecoverable("Assertion failed"));
}
Ok(M::Nil)
}
#[derive(PartialEq, Clone, Copy, Debug)]