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

@ -5,17 +5,30 @@
(def! and (fn* [a b]
(if a
b)))
(if b true))))
(def! or (fn* [a b]
(if a
true
b)))
(if b true))))
(def! xor (fn* [a b]
(if a
(not b)
b)))
(if b true))))
; Arithmetic
(def! + (fn* [a b]
(- a (- 0 b))))
(def! mod (fn* [a b]
(- a (* (/ a b) b))))
(def! >= (fn* [a b]
(not (< a b))))
(def! <= (fn* [a b]
(not (> a b))))
; Other functions in core.rs
(def! assert-eq (fn* [a b]

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)]

View File

@ -1,51 +1,40 @@
; +
(assert (= (+) 0))
(assert (= (+ 1) 1))
(assert (= (+ 1 -4) -3))
(assert (= (+ 1 1) 2))
(assert (= (+ 1 2 3 4) 10))
; -
(assert (= (-) 0))
(assert (= (- 1) -1))
(assert (= (- 2 1) 1))
(assert (= (- 1 2) -1))
(assert (= (- 10 1 2 3) 4))
; *
(assert (= (*) 1))
(assert (= (* 2) 2))
(assert (= (* 2 3) 6))
(assert (= (* -2 3) -6))
(assert (= (* -2 -3) 6))
(assert (= (* 10 1 2 3) 60))
; /
(assert (= (/) 1))
(assert (= (/ 1) 1))
(assert (= (/ 2) 0))
(assert (= (/ 3 2) 1))
(assert (= (/ 128 2 4) 16))
(assert (= (/ 2 3) 0))
; mod
(assert (= (mod 10 4) 2))
(assert (= (mod 4 10) 4))
; >
(assert (not (>)))
(assert (> 1))
(assert (> 3 2 1))
(assert (not (> 3 1 2)))
(assert (> 3 2))
(assert (not (> 1 2)))
(assert (not (> 1 1)))
; <
(assert (not (<)))
(assert (< 1))
(assert (< 1 2 3))
(assert (not (< 1 3 2)))
(assert (< 1 3))
(assert (not (< 3 2)))
(assert (not (< 1 1)))
; >=
(assert (not (>=)))
(assert (>= 1))
(assert (>= 3 2 1))
(assert (not (>= 3 1 2)))
(assert (>= 3 1))
(assert (not (>= 1 2)))
(assert (>= 1 1))
; <=
(assert (not (<=)))
(assert (<= 1))
(assert (<= 1 2 3))
(assert (not (<= 1 3 2)))
(assert (<= 1 3))
(assert (not (<= 3 2)))
(assert (<= 1 1))

View File

@ -6,53 +6,48 @@
; 1 different
; compare with foreign type
; empty
(assert (=)) ; nothing to compare with
; nil
(assert (= nil))
(assert (= nil nil nil))
(assert (= nil nil))
(assert (not (= nil true)))
(assert (not (= nil 1)))
; bool
(assert (= true))
(assert (= true true true))
(assert (not (= true false true)))
(assert (= true true))
(assert (not (= false true)))
(assert (not (= true 1)))
; int
(assert (= 1))
(assert (= 1 1 1))
(assert (not (= 1 2 1)))
(assert (= 1 1))
(assert (not (= 1 2)))
(assert (not (= 1 nil)))
; key
(assert (= :a))
(assert (= :a :a :a))
(assert (not (= :a :b :a)))
(assert (= :a :a))
(assert (not (= :a :b)))
(assert (not (= :a "a")))
; sym
(assert (= 'a 'a))
(assert (not (= 'a 'b)))
(assert (not (= 'a "a")))
; string
(assert (= "a"))
(assert (= "a" "a" "a"))
(assert (not (= "a" "b" "a")))
(assert (= "a" "a"))
(assert (not (= "a" "b")))
(assert (not (= "a" :a)))
; add comparison for same elements with different length
; list
(assert (= (list 1 1 1)))
(assert (= (list 1 1 1) (list 1 1 1) (list 1 1 1)))
(assert (not (= (list 1 1 1) (list 1 2 1) (list 1 1 1))))
(assert (not (= (list 1) (list 1 1))))
(assert (not (= (list 1 1) (list 1))))
(assert (not (= () (list 1))))
(assert (not (= (list 1) ())))
(assert (not (= (list 1 1 1) [1 1 1])))
(assert (= '(1 1 1) '(1 1 1)))
(assert (not (= '(1 2 1) '(1 1 1))))
(assert (not (= '(1) '(1 1))))
(assert (not (= '(1 1) '(1))))
(assert (not (= () '(1))))
(assert (not (= '(1) ())))
(assert (not (= '(1 1 1) [1 1 1])))
; vector
(assert (= [1 1 1]))
(assert (= [1 1 1] [1 1 1] [1 1 1]))
(assert (not (= [1 1 1] [1 2 1] [1 1 1])))
(assert (not (= [1 1 1] (list 1 1 1))))
(assert (= [1 1 1] [1 1 1]))
(assert (not (= [1 2 1] [1 1 1])))
(assert (not (= [1 1 1] '(1 1 1))))