mirror of
https://github.com/teo3300/rust-mal.git
synced 2026-01-12 01:05:32 +01:00
Moving even more stuff out of the rust core
Signed-off-by: teo3300 <matteo.rogora@live.it>
This commit is contained in:
@ -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]
|
||||
|
||||
17
src/core.rs
17
src/core.rs
@ -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"),
|
||||
|
||||
46
src/env.rs
46
src/env.rs
@ -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),
|
||||
}
|
||||
|
||||
52
src/types.rs
52
src/types.rs
@ -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)]
|
||||
|
||||
@ -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))
|
||||
@ -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))))
|
||||
Reference in New Issue
Block a user