mirror of
https://github.com/teo3300/rust-mal.git
synced 2026-01-12 01:05:32 +01:00
Other tests
Lotta tests in - eval - functional tests Signed-off-by: teo3300 <matteo.rogora@live.it>
This commit is contained in:
26
core.mal
26
core.mal
@ -1,26 +0,0 @@
|
||||
; "not" logic function
|
||||
(def! not (fn* (a)
|
||||
(if a false true)))
|
||||
|
||||
; binary "or" logic function
|
||||
(def! or (fn* (a b)
|
||||
(if a true
|
||||
(if b
|
||||
true
|
||||
false))))
|
||||
|
||||
; binary "and" logic function
|
||||
(def! and (fn* (a b)
|
||||
(if a
|
||||
(if b
|
||||
true
|
||||
false)
|
||||
false)))
|
||||
|
||||
; binary "xor" logic function
|
||||
(def! xor (fn* (a b)
|
||||
; use this strange definition to make it independent from
|
||||
; "or" and "and" definition
|
||||
(if a
|
||||
(if b false true)
|
||||
(if b true false))))
|
||||
@ -42,10 +42,10 @@ pub fn ns_init() -> Env {
|
||||
"*" => 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, 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 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"),
|
||||
"prn" => Fun(|a| { println!("{} ", prt(car(a)?)); Ok(Nil) }, "Print readably all the arguments"),
|
||||
"prn" => Fun(|a| {a.iter().for_each(|a| print!("{} ", prt(a))); println!(); Ok(Nil) }, "Print readably all the arguments"),
|
||||
"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"),
|
||||
"empty?" => Fun(|a| Ok(Bool(car(a)?.if_list()?.is_empty())), "Return true if the first parameter is an empty list, false otherwise, returns an error if the element is not a list"),
|
||||
|
||||
30
src/env.rs
30
src/env.rs
@ -40,7 +40,9 @@ pub fn env_binds(outer: Env, binds: &MalType, exprs: &[MalType]) -> Result<Env,
|
||||
let env = env_new(Some(outer));
|
||||
let binds = binds.if_list()?;
|
||||
if binds.len() != exprs.len() {
|
||||
return Err(MalErr::unrecoverable("Wrong number of arguments"));
|
||||
return Err(MalErr::unrecoverable(
|
||||
format!("Expected {} args, got {}", binds.len(), exprs.len()).as_str(),
|
||||
));
|
||||
} // TODO: May be possible to leave this be and not set additional elements at all
|
||||
for (bind, expr) in binds.iter().zip(exprs.iter()) {
|
||||
let bind = bind.if_symbol()?;
|
||||
@ -81,24 +83,26 @@ pub fn call_func(func: &MalType, args: &[MalType]) -> MalRet {
|
||||
}
|
||||
|
||||
pub fn arithmetic_op(set: isize, f: fn(isize, isize) -> isize, args: &[MalType]) -> MalRet {
|
||||
if args.is_empty() {
|
||||
return Ok(M::Int(set));
|
||||
}
|
||||
|
||||
let mut left = args[0].if_number()?;
|
||||
if args.len() > 1 {
|
||||
let right = &args[1..];
|
||||
for el in right {
|
||||
left = f(left, el.if_number()?);
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
Ok(M::Int(left))
|
||||
}))
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
115
src/eval.rs
115
src/eval.rs
@ -193,16 +193,47 @@ mod tests {
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! load_f {
|
||||
($input:expr, $env:expr) => {{
|
||||
use crate::reader::{read_str, Reader};
|
||||
use std::rc::Rc;
|
||||
|
||||
let r = Reader::new();
|
||||
r.push($input);
|
||||
let args = match read_str(&r) {
|
||||
Ok(v) => match v {
|
||||
MalType::List(v) => v,
|
||||
_ => panic!("Bad command"),
|
||||
},
|
||||
_ => panic!("Bad command"),
|
||||
};
|
||||
&MalType::List(Rc::new(if args.is_empty() {
|
||||
Vec::new()
|
||||
} else {
|
||||
let f_str = match &args[0] {
|
||||
MalType::Sym(s) => s.as_str(),
|
||||
_ => panic!("Can't solve function"),
|
||||
};
|
||||
let f = match env_get(&$env.clone(), f_str) {
|
||||
Ok(v) => v,
|
||||
_ => panic!("No such function in env"),
|
||||
};
|
||||
[&[f], &args[1..]].concat()
|
||||
}))
|
||||
}};
|
||||
}
|
||||
|
||||
fn _env_empty() -> Env {
|
||||
use crate::env::env_new;
|
||||
env_new(None)
|
||||
}
|
||||
|
||||
mod forms {
|
||||
|
||||
use crate::env::env_get;
|
||||
use crate::eval::tests::_env_empty;
|
||||
use crate::eval::{def_bang_form, do_form, let_star_form};
|
||||
use crate::eval::{
|
||||
def_bang_form, do_form, eval_ast, eval_func, fn_star_form, if_form, let_star_form,
|
||||
};
|
||||
use crate::types::MalType;
|
||||
|
||||
#[test]
|
||||
@ -271,7 +302,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn _do() {
|
||||
fn _do_form() {
|
||||
let env = _env_empty();
|
||||
assert!(matches!(
|
||||
do_form(load!("(do)"), env.clone()),
|
||||
@ -287,5 +318,83 @@ mod tests {
|
||||
));
|
||||
assert!(matches!(env_get(&env.clone(), "a"), Ok(MalType::Int(1))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn _if_form() {
|
||||
let env = _env_empty();
|
||||
assert!(matches!(
|
||||
if_form(load!("(if)"), env.clone()),
|
||||
Err(e) if !e.is_recoverable()));
|
||||
assert!(matches!(
|
||||
if_form(load!("(if 1)"), env.clone()),
|
||||
Err(e) if !e.is_recoverable()));
|
||||
assert!(matches!(
|
||||
if_form(load!("(if 1 2 3 4)"), env.clone()),
|
||||
Err(e) if !e.is_recoverable()));
|
||||
assert!(matches!(
|
||||
if_form(load!("(if nil 1)"), env.clone()),
|
||||
Ok(MalType::Nil)
|
||||
));
|
||||
assert!(matches!(
|
||||
if_form(load!("(if nil 1 2)"), env.clone()),
|
||||
Ok(MalType::Int(2))
|
||||
));
|
||||
assert!(matches!(
|
||||
if_form(load!("(if true 1)"), env.clone()),
|
||||
Ok(MalType::Int(1))
|
||||
));
|
||||
assert!(matches!(
|
||||
if_form(load!("(if true 1 2)"), env.clone()),
|
||||
Ok(MalType::Int(1))
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fn_star() {
|
||||
let env = _env_empty();
|
||||
assert!(matches!(
|
||||
fn_star_form(load!("(fn* (a b) 1 2)"), env.clone()),
|
||||
Ok(MalType::MalFun { eval, params, ast, .. })
|
||||
if eval == eval_ast
|
||||
&& matches!((*params).clone(), MalType::List(v)
|
||||
if matches!(&v[0], MalType::Sym(v) if v == "a")
|
||||
&& matches!(&v[1], MalType::Sym(v) if v == "b")
|
||||
&& matches!((*ast).clone(), MalType::List(v)
|
||||
if matches!(&v[0], MalType::Int(1))
|
||||
&& matches!(&v[1], MalType::Int(2))))));
|
||||
// We trust the fact that the env does not do silly stuff
|
||||
assert!(matches!(
|
||||
fn_star_form(load!("(fn*)"), env.clone()),
|
||||
Err(e) if !e.is_recoverable()));
|
||||
assert!(matches!(
|
||||
fn_star_form(load!("(fn* 1)"), env.clone()),
|
||||
Err(e) if !e.is_recoverable()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn _eval_func() {
|
||||
let env = _env_empty();
|
||||
assert!(matches!(
|
||||
def_bang_form(load!("(def! or (fn* (a b) (if a a b)))"), env.clone()),
|
||||
Ok(_)
|
||||
));
|
||||
assert!(matches!(
|
||||
eval_func(&MalType::Int(1)),
|
||||
Err(e) if !e.is_recoverable()));
|
||||
assert!(matches!(
|
||||
eval_func(load_f!("()", env.clone())),
|
||||
Err(e) if !e.is_recoverable()));
|
||||
assert!(matches!(
|
||||
eval_func(load_f!("(or nil nil)", env.clone())),
|
||||
Ok(v) if matches!(v, MalType::Nil)));
|
||||
assert!(matches!(
|
||||
eval_func(load_f!("(or 1 nil)", env.clone())),
|
||||
Ok(MalType::Int(1))
|
||||
));
|
||||
assert!(matches!(
|
||||
eval_func(load_f!("(or nil 1)", env.clone())),
|
||||
Ok(MalType::Int(1))
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,22 +1,14 @@
|
||||
#[cfg(test)]
|
||||
mod functional {
|
||||
|
||||
macro_rules! load_file {
|
||||
($file:expr, $env:expr) => {{
|
||||
match load_file($file, $env) {
|
||||
Ok(v) => v,
|
||||
Err(_) => {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! test {
|
||||
($file:expr) => {{
|
||||
use crate::core::ns_init;
|
||||
use crate::load_file;
|
||||
load_file!(format!("tests/{}.mal", $file).as_str(), &ns_init());
|
||||
assert!(matches!(
|
||||
load_file(format!("tests/{}.mal", $file).as_str(), &ns_init()),
|
||||
Ok(_)
|
||||
));
|
||||
}};
|
||||
}
|
||||
|
||||
@ -40,6 +32,11 @@ mod functional {
|
||||
test!("equals");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arithmetic() {
|
||||
test!("arithmetic")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fibonacci() {
|
||||
test!("fibonacci");
|
||||
|
||||
51
tests/arithmetic.mal
Normal file
51
tests/arithmetic.mal
Normal file
@ -0,0 +1,51 @@
|
||||
; +
|
||||
(assert (= (+) 0))
|
||||
(assert (= (+ 1) 1))
|
||||
(assert (= (+ 1 1) 2))
|
||||
(assert (= (+ 1 2 3 4) 10))
|
||||
|
||||
; -
|
||||
(assert (= (-) 0))
|
||||
(assert (= (- 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 (not (>)))
|
||||
(assert (> 1))
|
||||
(assert (> 3 2 1))
|
||||
(assert (not (> 3 1 2)))
|
||||
(assert (not (> 1 1)))
|
||||
; <
|
||||
(assert (not (<)))
|
||||
(assert (< 1))
|
||||
(assert (< 1 2 3))
|
||||
(assert (not (< 1 3 2)))
|
||||
(assert (not (< 1 1)))
|
||||
; >=
|
||||
(assert (not (>=)))
|
||||
(assert (>= 1))
|
||||
(assert (>= 3 2 1))
|
||||
(assert (not (>= 3 1 2)))
|
||||
(assert (>= 1 1))
|
||||
; <=
|
||||
(assert (not (<=)))
|
||||
(assert (<= 1))
|
||||
(assert (<= 1 2 3))
|
||||
(assert (not (<= 1 3 2)))
|
||||
(assert (<= 1 1))
|
||||
9
tests/lists.mal
Normal file
9
tests/lists.mal
Normal file
@ -0,0 +1,9 @@
|
||||
; list?
|
||||
(assert (list? (list 1 2 3)))
|
||||
(assert (not (list? 1)))
|
||||
|
||||
; empty?
|
||||
(assert (empty? ()))
|
||||
(assert (empty? (list)))
|
||||
(assert (not (empty? ())))
|
||||
(assert (not (empty? (list (1 2 3)))))
|
||||
Reference in New Issue
Block a user