mirror of
https://github.com/teo3300/rust-mal.git
synced 2026-01-12 09:15:32 +01:00
Adding test for reader.rs
- will implement other tests later Signed-off-by: teo3300 <matteo.rogora@live.it>
This commit is contained in:
50
src/core.rs
50
src/core.rs
@ -3,31 +3,31 @@ use crate::env::{arithmetic_op, car, comparison_op, env_new, env_set, mal_exit,
|
||||
// This is the first time I implement a macro, and I'm copying it
|
||||
// so I will comment this a LOT
|
||||
macro_rules! env_init {
|
||||
($outer:expr) => {
|
||||
// match any istance with no args
|
||||
{
|
||||
// the macro prevent the macro from disrupting the external code
|
||||
// this is the block of code that will substitute the macro
|
||||
env_new($outer)
|
||||
// returns an empty map
|
||||
}
|
||||
};
|
||||
($outer:expr, $($key:expr => $val:expr),*) => {
|
||||
// Only if previous statements did not match,
|
||||
// note that the expression with fat arrow is arbitrary,
|
||||
// could have been slim arrow, comma or any other
|
||||
// recognizable structure
|
||||
{
|
||||
// create an empty map
|
||||
let map = env_init!($outer);
|
||||
$( // Do this for all elements of the arguments list
|
||||
env_set(&map, $key, &$val);
|
||||
)*
|
||||
// return the new map
|
||||
map
|
||||
}
|
||||
};
|
||||
}
|
||||
($outer:expr) => {
|
||||
// match any istance with no args
|
||||
{
|
||||
// the macro prevent the macro from disrupting the external code
|
||||
// this is the block of code that will substitute the macro
|
||||
env_new($outer)
|
||||
// returns an empty map
|
||||
}
|
||||
};
|
||||
($outer:expr, $($key:expr => $val:expr),*) => {
|
||||
// Only if previous statements did not match,
|
||||
// note that the expression with fat arrow is arbitrary,
|
||||
// could have been slim arrow, comma or any other
|
||||
// recognizable structure
|
||||
{
|
||||
// create an empty map
|
||||
let map = env_init!($outer);
|
||||
$( // Do this for all elements of the arguments list
|
||||
env_set(&map, $key, &$val);
|
||||
)*
|
||||
// return the new map
|
||||
map
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
use crate::printer::prt;
|
||||
use crate::types::mal_comp;
|
||||
|
||||
@ -14,10 +14,10 @@ use core::ns_init;
|
||||
use parse_tools::{interactive, load_file};
|
||||
|
||||
fn main() {
|
||||
// Initialize ns environment
|
||||
let reply_env = ns_init();
|
||||
|
||||
// setup env
|
||||
//let args: Vec<String> = args().collect();
|
||||
// load all files passed as arguments
|
||||
args().collect::<Vec<String>>()[1..]
|
||||
.iter()
|
||||
.for_each(|f| load_file(f, &reply_env));
|
||||
|
||||
158
src/reader.rs
158
src/reader.rs
@ -14,7 +14,7 @@ pub struct Reader {
|
||||
|
||||
type Tokens = Vec<String>;
|
||||
|
||||
// TODO: instead of panic on missing ")" try implementing a multi line parsing
|
||||
// DONE: instead of panic on missing ")" try implementing a multi line parsing
|
||||
// Status on return should always be The last element of the last opened lists
|
||||
// (append to the "last" list) while traversing
|
||||
impl Reader {
|
||||
@ -28,7 +28,7 @@ impl Reader {
|
||||
pub fn push(&self, input: &str) {
|
||||
self.ptr.set(0);
|
||||
// reset the state of the parser and push the additional strings
|
||||
self.tokens.borrow_mut().append(&mut tokenize(input))
|
||||
self.tokens.borrow_mut().append(&mut tokenize(input));
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
@ -56,6 +56,11 @@ impl Reader {
|
||||
self.get_token(self.ptr.get() - 1)
|
||||
}
|
||||
|
||||
/// Returns true if the reader has been consumed entirely
|
||||
pub fn ended(&self) -> bool {
|
||||
self.tokens.borrow().len() == self.ptr.get()
|
||||
}
|
||||
|
||||
/// Repeatedly calls `read_form` of the reader object until it finds a ")" token
|
||||
/// EOF -> Return an error (Dyck language error)
|
||||
/// Accumulates results into a MalList
|
||||
@ -117,10 +122,6 @@ impl Reader {
|
||||
_ => self.read_atom(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ended(&self) -> bool {
|
||||
self.tokens.borrow().len() == self.ptr.get()
|
||||
}
|
||||
}
|
||||
|
||||
/// Call `tokenize` on a string
|
||||
@ -142,3 +143,148 @@ fn tokenize(input: &str) -> Tokens {
|
||||
.collect::<Vec<String>>();
|
||||
tokens
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Tests //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::types::{MalMap, MalType as M, Severity};
|
||||
|
||||
use super::Reader;
|
||||
|
||||
fn reader_setup1() -> Reader {
|
||||
let r = Reader::new();
|
||||
r.push("()[]{} \"str\" :key sym 1 ; comment");
|
||||
return r;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn init() {
|
||||
let r = Reader::new();
|
||||
assert_eq!(r.tokens.borrow().len(), 0);
|
||||
assert_eq!(r.ptr.get(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn push() {
|
||||
let r = reader_setup1();
|
||||
let mut tokens = Vec::new();
|
||||
for el in r.tokens.borrow().iter() {
|
||||
tokens.push(el.clone());
|
||||
}
|
||||
assert_eq!(
|
||||
tokens,
|
||||
vec!["(", ")", "[", "]", "{", "}", "\"str\"", ":key", "sym", "1"]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_token() {
|
||||
let r = reader_setup1();
|
||||
assert!(matches!(r.get_token(0), Ok(i) if i == "("));
|
||||
assert!(matches!(r.get_token(9), Ok(i) if i == "1"));
|
||||
assert!(matches!(r.get_token(10), Err(e) if e.severity() == Severity::Recoverable));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get() {
|
||||
let r = reader_setup1();
|
||||
assert!(matches!(r.peek(), Ok(i) if i == "("));
|
||||
assert_eq!(r.ptr.get(), 0);
|
||||
assert!(matches!(r.next(), Ok(i) if i == "("));
|
||||
assert_eq!(r.ptr.get(), 1);
|
||||
assert!(matches!(r.next(), Ok(i) if i == ")"));
|
||||
assert_eq!(r.ptr.get(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clear() {
|
||||
let r = reader_setup1();
|
||||
r.clear();
|
||||
assert_eq!(r.tokens.borrow().len(), 0);
|
||||
assert_eq!(r.ptr.get(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ended() {
|
||||
let r = reader_setup1();
|
||||
assert!(!r.ended());
|
||||
for _ in r.tokens.borrow().iter() {
|
||||
assert!(matches!(r.next(), Ok(_)))
|
||||
}
|
||||
assert!(r.ended());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn errors() {
|
||||
let r = Reader::new();
|
||||
// Correct throws error
|
||||
assert!(matches!(r.peek(), Err(e) if e.severity() == Severity::Recoverable));
|
||||
assert!(matches!(r.next(), Err(e) if e.severity() == Severity::Recoverable));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_atom() {
|
||||
let r = Reader::new();
|
||||
r.push("nil 1 true a \"s\" :a ) ] }");
|
||||
assert!(matches!(r.read_atom(), Ok(x) if matches!(x, M::Nil)));
|
||||
assert!(matches!(r.read_atom(), Ok(x) if matches!(x, M::Int(1))));
|
||||
assert!(matches!(r.read_atom(), Ok(x) if matches!(x, M::Bool(true))));
|
||||
assert!(matches!(r.read_atom(), Ok(x) if matches!(x.clone(), M::Sym(v) if v == "a")));
|
||||
assert!(matches!(r.read_atom(), Ok(x) if matches!(x.clone(), M::Str(v) if v == "s")));
|
||||
assert!(matches!(r.read_atom(), Ok(x) if matches!(x.clone(), M::Key(v) if v == "ʞ:a")));
|
||||
assert!(matches!(r.read_atom(), Err(e) if e.severity() == Severity::Unrecoverable));
|
||||
assert!(matches!(r.read_atom(), Err(e) if e.severity() == Severity::Unrecoverable));
|
||||
assert!(matches!(r.read_atom(), Err(e) if e.severity() == Severity::Unrecoverable));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_form() {
|
||||
let r = Reader::new();
|
||||
|
||||
// Test list
|
||||
let expected = vec![1, 2, 12];
|
||||
r.push("(1 2 12)");
|
||||
assert!(matches!(
|
||||
r.read_form(), Ok(x)
|
||||
if matches!(x.clone(), M::List(list)
|
||||
if list.len() == expected.len()
|
||||
&& list.iter().zip(expected)
|
||||
.all(|(x, y)| matches!(x, M::Int(v) if (*v as isize) == y)))));
|
||||
r.clear();
|
||||
|
||||
// Test vector
|
||||
let exp = vec![1, 2, 12];
|
||||
r.push("[1 2 12]");
|
||||
assert!(matches!(
|
||||
r.read_form(), Ok(x)
|
||||
if matches!(x.clone(), M::Vector(list)
|
||||
if list.len() == exp.len()
|
||||
&& list.iter().zip(exp)
|
||||
.all(|(x, y)| matches!(x, M::Int(v) if (*v as isize) == y)))));
|
||||
r.clear();
|
||||
|
||||
// Test map
|
||||
r.push("{\"i\" 1 \"s\" \"str\" \"t\" true \"n\" nil :s :sym}");
|
||||
let t = match r.read_form() {
|
||||
Ok(x) => match x {
|
||||
M::Map(x) => x,
|
||||
_ => {
|
||||
assert!(false);
|
||||
MalMap::new()
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
assert!(false);
|
||||
MalMap::new()
|
||||
}
|
||||
};
|
||||
assert!(matches!(t.get("n"), Some(x) if matches!(x.clone(), M::Nil)));
|
||||
assert!(matches!(t.get("t"), Some(x) if matches!(x.clone(), M::Bool(v) if v)));
|
||||
assert!(matches!(t.get("i"), Some(x) if matches!(x.clone(), M::Int(v) if v == 1)));
|
||||
assert!(matches!(t.get("s"), Some(x) if matches!(x.clone(), M::Str(v) if v == "str")));
|
||||
assert!(matches!(t.get("ʞ:s"), Some(x) if matches!(x.clone(), M::Key(v) if v == "ʞ:sym")));
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,10 +59,8 @@ fn mal_eq(a: &M, b: &M) -> MalRet {
|
||||
(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
|
||||
(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)))),
|
||||
@ -78,7 +76,7 @@ pub fn mal_comp(args: &[MalType]) -> MalRet {
|
||||
let (car, cdr) = car_cdr(args)?;
|
||||
match cdr.len() {
|
||||
0 => Ok(M::Bool(true)),
|
||||
_ => mal_eq(car, &cdr[0])
|
||||
_ => mal_eq(car, &cdr[0]),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user