mirror of
https://github.com/teo3300/rust-mal.git
synced 2026-01-12 09:15:32 +01:00
Added some other tests
- builtin eq test - load_file return resul Signed-off-by: teo3300 <matteo.rogora@live.it>
This commit is contained in:
10
src/core.rs
10
src/core.rs
@ -31,7 +31,7 @@ macro_rules! env_init {
|
||||
|
||||
use crate::printer::prt;
|
||||
use crate::types::MalType::{Bool, Fun, Int, List, Nil, Str};
|
||||
use crate::types::{mal_assert, mal_comp};
|
||||
use crate::types::{mal_assert, mal_comp, MalArgs};
|
||||
|
||||
pub fn ns_init() -> Env {
|
||||
env_init!(None,
|
||||
@ -45,12 +45,12 @@ pub fn ns_init() -> Env {
|
||||
"<" => 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 passed to it"),
|
||||
"list" => Fun(|a| Ok(List(a.to_vec())), "Return the arguments as a list"),
|
||||
"list?" => Fun(|a| Ok(Bool(matches!(car(a)?, List(_)))), "Return true if the first argument is a list, false otherwise"),
|
||||
"prn" => Fun(|a| { println!("{} ", prt(car(a)?)); 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"),
|
||||
"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"),
|
||||
"assert" => Fun(mal_assert, "Panic if one of the argument is false")
|
||||
"assert" => Fun(mal_assert, "Panic if one of the arguments is false")
|
||||
)
|
||||
}
|
||||
|
||||
10
src/eval.rs
10
src/eval.rs
@ -94,7 +94,7 @@ fn fn_star_form(list: &[MalType], env: Env) -> MalRet {
|
||||
Ok(M::MalFun {
|
||||
eval: eval_ast,
|
||||
params: Rc::new(binds.clone()),
|
||||
ast: Rc::new(M::List(exprs.to_vec())),
|
||||
ast: Rc::new(M::List(MalArgs::new(exprs.to_vec()))),
|
||||
env,
|
||||
})
|
||||
}
|
||||
@ -123,7 +123,7 @@ fn apply(list: &MalArgs, env: Env) -> MalRet {
|
||||
M::Sym(sym) if sym == "fn*" => fn_star_form(cdr, env),
|
||||
M::Sym(sym) if sym == "help" => help_form(cdr, env),
|
||||
// Filter out special forms
|
||||
_ => eval_func(&eval_ast(&M::List(list.to_vec()), env)?),
|
||||
_ => eval_func(&eval_ast(&M::List(MalArgs::new(list.to_vec())), env)?),
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,11 +140,11 @@ pub fn eval(ast: &MalType, env: Env) -> MalRet {
|
||||
|
||||
/// Separately evaluate all elements in a collection (list or vector)
|
||||
fn eval_collection(list: &MalArgs, env: Env) -> Result<MalArgs, MalErr> {
|
||||
let mut ret = MalArgs::new();
|
||||
for el in list {
|
||||
let mut ret = Vec::new();
|
||||
for el in list.as_ref() {
|
||||
ret.push(eval(el, env.clone())?);
|
||||
}
|
||||
Ok(ret)
|
||||
Ok(MalArgs::new(ret))
|
||||
}
|
||||
|
||||
/// Evaluate the values of a map
|
||||
|
||||
@ -19,9 +19,11 @@ fn main() {
|
||||
let reply_env = ns_init();
|
||||
|
||||
// load all files passed as arguments
|
||||
args().collect::<Vec<String>>()[1..]
|
||||
.iter()
|
||||
.for_each(|f| load_file(f, &reply_env));
|
||||
args().collect::<Vec<String>>()[1..].iter().for_each(|f| {
|
||||
if let Err(e) = load_file(f, &reply_env) {
|
||||
println!("{}", e.message())
|
||||
}
|
||||
});
|
||||
|
||||
interactive(reply_env);
|
||||
}
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
use crate::env::Env;
|
||||
use crate::reader::Reader;
|
||||
use crate::step4_if_fn_do::rep;
|
||||
use crate::types::MalErr;
|
||||
use crate::types::{MalErr, MalRet, MalType::Nil};
|
||||
use regex::Regex;
|
||||
use std::fs::File;
|
||||
use std::io::{self, BufRead, BufReader};
|
||||
|
||||
pub fn load_file(filename: &str, env: &Env) {
|
||||
pub fn load_file(filename: &str, env: &Env) -> MalRet {
|
||||
let file_desc = File::open(filename);
|
||||
let file = match file_desc {
|
||||
Ok(file) => file,
|
||||
@ -36,22 +36,30 @@ pub fn load_file(filename: &str, env: &Env) {
|
||||
Err(error) if error.is_recoverable() => Err(error),
|
||||
tmp => {
|
||||
parser.clear();
|
||||
tmp.map_err(|error| {
|
||||
println!("; Error @ {}", error.message());
|
||||
error
|
||||
})
|
||||
Ok(tmp.map_err(|error| {
|
||||
MalErr::unrecoverable(format!("; Error @ {}", error.message()).as_str())
|
||||
})?)
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => eprintln!("Error reading line: {}", err),
|
||||
Err(err) => {
|
||||
return Err(MalErr::unrecoverable(
|
||||
format!("Error reading line: {}", err).as_str(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Err(error) = last {
|
||||
println!(
|
||||
"; ERROR parsing: '{}'\n; {}\n; the environment is in an unknown state",
|
||||
filename,
|
||||
error.message()
|
||||
)
|
||||
Err(MalErr::unrecoverable(
|
||||
format!(
|
||||
"; ERROR parsing: '{}'\n; {}\n; the environment is in an unknown state",
|
||||
filename,
|
||||
error.message()
|
||||
)
|
||||
.as_str(),
|
||||
))
|
||||
} else {
|
||||
Ok(Nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -69,7 +69,7 @@ impl Reader {
|
||||
fn read_list(&self, terminator: &str) -> MalRet {
|
||||
self.next()?;
|
||||
|
||||
let mut vector = MalArgs::new();
|
||||
let mut vector = Vec::new();
|
||||
|
||||
while self.peek()? != terminator {
|
||||
vector.push(self.read_form()?)
|
||||
@ -77,9 +77,9 @@ impl Reader {
|
||||
self.next()?;
|
||||
|
||||
match terminator {
|
||||
")" => Ok(List(vector)),
|
||||
"]" => Ok(Vector(vector)),
|
||||
"}" => make_map(vector),
|
||||
")" => Ok(List(MalArgs::new(vector))),
|
||||
"]" => Ok(Vector(MalArgs::new(vector))),
|
||||
"}" => make_map(MalArgs::new(vector)),
|
||||
t => Err(MalErr::unrecoverable(
|
||||
format!("Unknown collection terminator: {}", t).as_str(),
|
||||
)),
|
||||
|
||||
@ -1,12 +1,24 @@
|
||||
#[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;
|
||||
let env = ns_init();
|
||||
load_file("core.mal", &env);
|
||||
load_file(format!("tests/{}.mal", $file).as_str(), &env);
|
||||
load_file!("core.mal", &env);
|
||||
load_file!(format!("tests/{}.mal", $file).as_str(), &env);
|
||||
}};
|
||||
}
|
||||
|
||||
@ -14,4 +26,9 @@ mod functional {
|
||||
fn fibonacci() {
|
||||
test!("fibonacci");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builtin_equals() {
|
||||
test!("equals");
|
||||
}
|
||||
}
|
||||
|
||||
41
src/types.rs
41
src/types.rs
@ -54,16 +54,27 @@ impl MalType {
|
||||
|
||||
use crate::types::MalType as M;
|
||||
|
||||
fn mal_eq(a: &M, b: &M) -> MalRet {
|
||||
Ok(M::Bool(match (a, b) {
|
||||
(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)) => a == b,
|
||||
(M::List(a), M::List(b)) | (M::Vector(a), M::Vector(b)) => a
|
||||
.iter()
|
||||
.zip(b.iter())
|
||||
.all(|(a, b)| matches!(mal_eq(a, b), Ok(M::Bool(true)))),
|
||||
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'",
|
||||
@ -73,10 +84,12 @@ fn mal_eq(a: &M, b: &M) -> MalRet {
|
||||
}
|
||||
|
||||
pub fn mal_comp(args: &[MalType]) -> MalRet {
|
||||
let (car, cdr) = car_cdr(args)?;
|
||||
match cdr.len() {
|
||||
match args.len() {
|
||||
0 => Ok(M::Bool(true)),
|
||||
_ => mal_eq(car, &cdr[0]),
|
||||
_ => {
|
||||
let (car, cdr) = car_cdr(args)?;
|
||||
mal_eq(car, cdr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,7 +138,7 @@ impl MalErr {
|
||||
}
|
||||
}
|
||||
|
||||
pub type MalArgs = Vec<MalType>;
|
||||
pub type MalArgs = Rc<Vec<MalType>>;
|
||||
pub type MalMap = HashMap<String, MalType>;
|
||||
pub type MalRet = Result<MalType, MalErr>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user