mirror of
https://github.com/teo3300/rust-mal.git
synced 2026-01-12 01:05:32 +01:00
Adding collect function
- Collect function as fn:(collector, x) -> new_collector for collections - Moving some definitions to "libs" folder - Map and filter defined in mal
This commit is contained in:
2
Makefile
2
Makefile
@ -9,6 +9,8 @@ test:
|
||||
conf:
|
||||
mkdir -p ${HOME}/.config/mal
|
||||
cp -f core/core.mal ${HOME}/.config/mal/
|
||||
mkdir -p ${HOME}/.config/mal/libs
|
||||
cp -f libs/* ${HOME}/.config/mal/libs/
|
||||
|
||||
install: build-release conf
|
||||
sudo cp target/release/rust-mal /usr/local/bin/mal
|
||||
|
||||
@ -18,14 +18,6 @@
|
||||
b)))
|
||||
|
||||
; Arithmetic
|
||||
(def! abs (fn* [a]
|
||||
(if (> a 0)
|
||||
a
|
||||
(- 0 a))))
|
||||
|
||||
(def! mod (fn* [a b]
|
||||
(- a (* (/ a b) b))))
|
||||
|
||||
(def! > (fn* [a b]
|
||||
(< b a)))
|
||||
|
||||
@ -51,13 +43,19 @@
|
||||
(def! empty? (fn* [l]
|
||||
(= (count l) 0)))
|
||||
|
||||
(def! assert-msg (fn* [e m]
|
||||
(if (not e)
|
||||
(raise m))))
|
||||
|
||||
(def! assert (fn* [e] (assert-msg e "Assertion failed")))
|
||||
|
||||
(def! assert-eq (fn* [a b]
|
||||
"returns an error if arguments are not equals, NIL otherwise"
|
||||
(assert (= a b))))
|
||||
(assert-msg (= a b) (str "Expected " b (type b) ", found " a (type a)))))
|
||||
|
||||
(def! assert-fail (fn* [x]
|
||||
"returns NIL if evaluation of argument fails, error otherwise"
|
||||
(assert (not (ok? (eval x))))))
|
||||
(assert-msg (not (ok? (eval x))) (str "Expected failure, but " x " is correct"))))
|
||||
|
||||
;; Since thread safety is not required, I will leave it like this,
|
||||
;; to make thread safe just guard the function
|
||||
@ -152,22 +150,35 @@
|
||||
|
||||
(def! map (fn* [f l]
|
||||
"Apply function f to all elements of l"
|
||||
(def! l (reverse l))
|
||||
(def! map-r (fn* [l p]
|
||||
(if (empty? l)
|
||||
p
|
||||
(map-r (cdr l) (cons (f (car l)) p)))))
|
||||
(map-r l '())))
|
||||
(reverse (map-r l '()))))
|
||||
|
||||
(def! filter (fn* [f l]
|
||||
"Remove all elements that don't satisfy f"
|
||||
(def! l (reverse l))
|
||||
"Remove all elements of l that don't satisfy f"
|
||||
(def! filter-r (fn* [l p]
|
||||
(if (empty? l)
|
||||
p
|
||||
(do
|
||||
(def! t (car l))
|
||||
(if (f t)
|
||||
(filter-r (cdr l) (cons (car l) p))
|
||||
(filter-r (cdr l) (cons t p))
|
||||
(filter-r (cdr l) p))))))
|
||||
(filter-r l '())))
|
||||
(reverse (filter-r l '()))))
|
||||
|
||||
(def! map-if (fn* [c f l]
|
||||
"Apply function f to all elements of l that satisfy c"
|
||||
(map (fn* [x] (if (c x) (f x) x)) l)))
|
||||
|
||||
(def! collect (fn* [f i l]
|
||||
"Apply collector function f to list l"
|
||||
"Collector function must accept two parameters:"
|
||||
"collector and current element, result is the collector in the next iteration"
|
||||
"Collector is initialized as i"
|
||||
(def! collect-r (fn* [c l]
|
||||
(if (empty? l)
|
||||
c
|
||||
(collect-r (f c (car l)) (cdr l)))))
|
||||
(collect-r i l)))
|
||||
|
||||
22
libs/lists.mal
Normal file
22
libs/lists.mal
Normal file
@ -0,0 +1,22 @@
|
||||
(def! concat (fn* [x y] (def! concat-r (fn* [x y t]
|
||||
"Concatenate arguments, keeping their order"
|
||||
(if (car x)
|
||||
(concat-r (cdr x) y (cons (car x) t))
|
||||
(if (car y)
|
||||
(concat-r '() (cdr y) (cons (car y) t))
|
||||
t))))
|
||||
(concat-r (reverse y) (reverse x) '())))
|
||||
|
||||
(def! distribute (fn* [x] (def! distribute-r (fn* [p n t]
|
||||
(if (empty? n)
|
||||
t
|
||||
(let* [c (car n) n (cdr n)]
|
||||
(distribute-r (cons c p) n (cons (cons c (concat p n)) t))))))
|
||||
(distribute-r '() x '())))
|
||||
|
||||
(def! len (fn* [l] (def! len-r (fn* [l c]
|
||||
(if (empty? l)
|
||||
c
|
||||
(len-r (cdr l) (+ c 1)))))
|
||||
(len-r l 0)))
|
||||
|
||||
23
libs/math.mal
Normal file
23
libs/math.mal
Normal file
@ -0,0 +1,23 @@
|
||||
(def! abs (fn* [a]
|
||||
(if (> a 0)
|
||||
a
|
||||
(- 0 a))))
|
||||
|
||||
(def! mod (fn* [a b]
|
||||
(- a (* (/ a b) b))))
|
||||
|
||||
(def! max (fn* [a b]
|
||||
(if (> a b)
|
||||
a
|
||||
b)))
|
||||
|
||||
(def! min (fn* [a b]
|
||||
(if (< a b)
|
||||
a
|
||||
b)))
|
||||
|
||||
(def! fact (fn* [a]
|
||||
(def! fact-r (fn* [a b]
|
||||
(if (not (> a 1)) b
|
||||
(fact-r (- a 1) (* a b)))))
|
||||
(fact-r a 1)))
|
||||
@ -38,7 +38,7 @@ use crate::parse_tools::read_file;
|
||||
use crate::printer::{pr_str, prt};
|
||||
use crate::reader::{read_str, Reader};
|
||||
use crate::types::MalType::{Atom, Fun, Int, List, Nil, Str};
|
||||
use crate::types::{mal_assert, mal_equals, reset_bang, MalErr};
|
||||
use crate::types::{mal_equals, reset_bang, MalErr};
|
||||
|
||||
macro_rules! if_atom {
|
||||
($val:expr) => {{
|
||||
@ -65,17 +65,16 @@ pub fn ns_init() -> Env {
|
||||
">" => 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(" ").into())), "Print readably all arguments"),
|
||||
"pr-str" => Fun(|a| Ok(Str(a.iter().map(|i| pr_str(i, true)).collect::<Vec<String>>().join("").into())), "Print readably all arguments"),
|
||||
"str" => Fun(|a| Ok(Str(a.iter().map(|i| pr_str(i, false)).collect::<Vec<String>>().join("").into())), "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"),
|
||||
"println" => Fun(|a| {a.iter().for_each(|a| print!("{} ", pr_str(a, false))); println!(); Ok(Nil) }, "Print readably all the arguments"),
|
||||
"prn" => Fun(|a| {a.iter().for_each(|a| print!("{}", pr_str(a, false))); Ok(Nil) }, "Print readably all the arguments"),
|
||||
"println" => Fun(|a| {a.iter().for_each(|a| print!("{}", pr_str(a, false))); println!(); Ok(Nil) }, "Print readably all the arguments"),
|
||||
"list" => Fun(|a| Ok(List(a.into())), "Return the arguments as a list"),
|
||||
"type" => Fun(|a| Ok(car(a)?.label_type()), "Returns a label indicating the type of it's argument"),
|
||||
"count" => Fun(|a| Ok(Int(car(a)?.if_list()?.len() as isize)), "Return the number of elements in the first argument"),
|
||||
"=" => Fun(mal_equals, "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')"),
|
||||
"car" => Fun(|a| mal_car(car(a)?), "Returns the first element of the list, NIL if its empty"),
|
||||
"cdr" => Fun(|a| mal_cdr(car(a)?), "Returns all the list but the first element"),
|
||||
"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"),
|
||||
"atom" => Fun(|a| Ok(Atom(Rc::new(RefCell::new(car(a).unwrap_or_default().clone())))), "Return an atom pointing to the given arg"),
|
||||
|
||||
17
src/types.rs
17
src/types.rs
@ -126,13 +126,6 @@ pub fn mal_equals(args: &[MalType]) -> MalRet {
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn mal_assert(args: &[MalType]) -> MalRet {
|
||||
if args.iter().any(|i| matches!(i, M::Nil | M::Bool(false))) {
|
||||
return Err(MalErr::unrecoverable("Assertion failed"));
|
||||
}
|
||||
Ok(M::Nil)
|
||||
}
|
||||
|
||||
pub fn reset_bang(args: &[MalType]) -> MalRet {
|
||||
if args.len() < 2 {
|
||||
return Err(MalErr::unrecoverable("reset requires two arguments"));
|
||||
@ -241,16 +234,6 @@ pub fn unescape_str(s: &str) -> String {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::types::mal_assert;
|
||||
use crate::types::MalType as M;
|
||||
|
||||
#[test]
|
||||
fn _mal_assert() {
|
||||
assert!(matches!(mal_assert(&[M::Nil]), Err(_)));
|
||||
assert!(matches!(mal_assert(&[M::Bool(false)]), Err(_)));
|
||||
assert!(matches!(mal_assert(&[M::Bool(true)]), Ok(_)));
|
||||
assert!(matches!(mal_assert(&[M::Int(1)]), Ok(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn _escape_str() {
|
||||
|
||||
@ -18,10 +18,6 @@
|
||||
(assert (= (/ -10 -2) 5))
|
||||
(assert (not (ok? (/ 12 0))))
|
||||
|
||||
; mod
|
||||
(assert (= (mod 10 4) 2))
|
||||
(assert (= (mod 4 10) 4))
|
||||
|
||||
; >
|
||||
(assert (> 3 2))
|
||||
(assert (not (> 1 2)))
|
||||
@ -40,4 +36,4 @@
|
||||
; <=
|
||||
(assert (<= 1 3))
|
||||
(assert (not (<= 3 2)))
|
||||
(assert (<= 1 1))
|
||||
(assert (<= 1 1))
|
||||
|
||||
@ -42,7 +42,7 @@
|
||||
(assert-fail '(if))
|
||||
|
||||
; let*
|
||||
(assert (let* [let-a 1
|
||||
(assert-eq (let* [let-a 1
|
||||
let-b 2]
|
||||
(assert-eq let-a 1)
|
||||
(assert-eq let-b 2)
|
||||
|
||||
Reference in New Issue
Block a user