186 lines
6.0 KiB
Rust
186 lines
6.0 KiB
Rust
use std::io::{self, BufReader, Read};
|
|
|
|
use lysp::{
|
|
error::MachineErrorAt,
|
|
read::{FileReader, read},
|
|
vm::{env::Environment, machine::Machine, prelude, value::Value},
|
|
};
|
|
|
|
struct SliceReader<'a>(&'a [u8]);
|
|
|
|
impl Read for SliceReader<'_> {
|
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
let count = self.0.len().min(buf.len());
|
|
buf[..count].copy_from_slice(&self.0[..count]);
|
|
self.0 = &self.0[count..];
|
|
Ok(count)
|
|
}
|
|
}
|
|
|
|
#[track_caller]
|
|
fn eval_str_in(code: &str, env: &mut Environment) -> Result<Value, MachineErrorAt> {
|
|
let mut machine = Machine::default();
|
|
let reader = BufReader::new(SliceReader(code.as_bytes()));
|
|
let mut reader = FileReader::new(reader);
|
|
|
|
let mut last_value = None;
|
|
loop {
|
|
let value = match read(&mut reader, &mut machine, env) {
|
|
Ok(Some(value)) => value,
|
|
Ok(None) => break,
|
|
Err(error) => panic!("{error}"),
|
|
};
|
|
|
|
last_value = Some(machine.evaluate_value(Default::default(), None, env, value));
|
|
}
|
|
|
|
last_value.expect("no expressions evaluated")
|
|
}
|
|
|
|
#[track_caller]
|
|
fn eval_str(code: &str) -> Value {
|
|
let mut env = Environment::default();
|
|
prelude::load(&mut env);
|
|
eval_str_in(code, &mut env).expect("expression evaluation failed")
|
|
}
|
|
|
|
#[track_caller]
|
|
fn eval_str_err(code: &str) -> MachineErrorAt {
|
|
let mut env = Environment::default();
|
|
prelude::load(&mut env);
|
|
eval_str_in(code, &mut env).expect_err("expression was expected to fail")
|
|
}
|
|
|
|
#[test]
|
|
fn test_math() {
|
|
// math
|
|
assert_eq!(eval_str("(+ 1 2 3)"), Value::Number(6.into()));
|
|
assert_eq!(eval_str("(- 3 2 1)"), Value::Number(0.into()));
|
|
assert_eq!(eval_str("(* 2 3 4)"), Value::Number(24.into()));
|
|
assert_eq!(eval_str("(/ 16 4 2)"), Value::Number(2.into()));
|
|
assert_eq!(eval_str("(% 35 16)"), Value::Number(3.into()));
|
|
// TODO
|
|
// assert_eq!(eval_str("(| 1 2 4)"), Value::Number(7.into()));
|
|
// assert_eq!(eval_str("(& 1 2 4)"), Value::Number(0.into()));
|
|
// assert_eq!(eval_str("(& 1 3 7)"), Value::Number(1.into()));
|
|
// assert_eq!(eval_str("(^ 1 3 8)"), Value::Number(10.into()));
|
|
// comparison
|
|
assert_eq!(eval_str("(< 1 2 3 4 5)"), Value::Boolean(true.into()));
|
|
assert_eq!(eval_str("(< 1 2 3 3 4 5)"), Value::Boolean(false.into()));
|
|
assert_eq!(eval_str("(> 1 2 3 4 5)"), Value::Boolean(false.into()));
|
|
assert_eq!(eval_str("(>= 5 5 4 3 2 1)"), Value::Boolean(true.into()));
|
|
assert_eq!(eval_str("(> 5 5 4 3 2 1)"), Value::Boolean(false.into()));
|
|
assert_eq!(eval_str("(<= 1 2 3 3 3 4)"), Value::Boolean(true.into()));
|
|
assert_eq!(eval_str("(/= 1 2 3 4)"), Value::Boolean(true.into()));
|
|
assert_eq!(eval_str("(/= 1 2 2 4)"), Value::Boolean(false.into()));
|
|
assert_eq!(eval_str("(= 1 2 3 4)"), Value::Boolean(false.into()));
|
|
assert_eq!(eval_str("(= 1 1 1 1)"), Value::Boolean(true.into()));
|
|
// logic
|
|
// TODO
|
|
// assert_eq!(eval_str("(&& 1 0)"), Value::Boolean(false.into()));
|
|
// assert_eq!(eval_str("(&& #t 1 \"yes\")"), Value::Boolean(true.into()));
|
|
// assert_eq!(
|
|
// eval_str("(|| #f NIL \"\" ())"),
|
|
// Value::Boolean(false.into())
|
|
// );
|
|
// assert_eq!(
|
|
// eval_str("(|| #f '(1 2 3) \"\" ())"),
|
|
// Value::Boolean(true.into())
|
|
// );
|
|
}
|
|
|
|
#[test]
|
|
fn test_abort() {
|
|
// let err = eval_str_err("(assert (= 1 (+ 1 1)))");
|
|
// let EvalError::Machine(MachineError {
|
|
// error: MachineErrorKind::Aborted(message),
|
|
// ..
|
|
// }) = err
|
|
// else {
|
|
// panic!("Invalid error returned")
|
|
// };
|
|
// assert_eq!(&message, "<undefined>: assertion failed: (= 1 (+ 1 1))");
|
|
|
|
// let err = eval_str_err("(abort 1234)");
|
|
// let EvalError::Machine(MachineError {
|
|
// error: MachineErrorKind::Aborted(message),
|
|
// ..
|
|
// }) = err
|
|
// else {
|
|
// panic!("Invalid error returned")
|
|
// };
|
|
// assert_eq!(&message, "1234");
|
|
}
|
|
|
|
#[test]
|
|
fn test_lambda() {
|
|
assert_eq!(
|
|
eval_str("((lambda (x) (+ x 1)) 1)"),
|
|
Value::Number(2.into())
|
|
);
|
|
assert_eq!(
|
|
eval_str("((lambda (x) (+ ((lambda () 2)) 1)) 1)"),
|
|
Value::Number(3.into())
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_math_behaves_the_same_inside_apply() {
|
|
assert_eq!(eval_str("(apply + '(1 2 3 4))"), eval_str("(+ 1 2 3 4)"));
|
|
assert_eq!(eval_str("(apply * '(1 2 3 4))"), eval_str("(* 1 2 3 4)"));
|
|
assert_eq!(eval_str("(apply - '(1 2 3 4))"), eval_str("(- 1 2 3 4)"));
|
|
assert_eq!(eval_str("(apply / '(100 25 2))"), eval_str("(/ 100 25 2)"));
|
|
assert_eq!(eval_str("(apply % '(90 37))"), eval_str("(% 90 37)"));
|
|
// TODO
|
|
// assert_eq!(eval_str("(apply | '(1 2 4))"), eval_str("(| 1 2 4)"));
|
|
// assert_eq!(eval_str("(apply & '(1 3 7))"), eval_str("(& 1 3 7)"));
|
|
// assert_eq!(eval_str("(apply ^ '(1 3 7))"), eval_str("(^ 1 3 7)"));
|
|
// assert_eq!(eval_str("(apply < '(1 2 3 4))"), eval_str("(< 1 2 3 4)"));
|
|
// assert_eq!(eval_str("(apply > '(1 2 3 4))"), eval_str("(> 1 2 3 4)"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_setq() {
|
|
assert_eq!(eval_str("(setq a 1234) a\n"), Value::Number(1234.into()));
|
|
}
|
|
|
|
#[test]
|
|
fn test_let() {
|
|
// Should fail
|
|
eval_str_err("(let (a 1234 b (+ a 1)) NIL)");
|
|
eval_str_err("(let (a 1234) (let (a 9999 b (+ a 4321)) b))");
|
|
|
|
assert_eq!(
|
|
eval_str("(let (a 1234 b 4321) (+ a b))"),
|
|
Value::Number(5555.into())
|
|
);
|
|
assert_eq!(
|
|
eval_str("(let* (a 1234 b (+ a 4321)) (+ b 4444))"),
|
|
Value::Number(9999.into())
|
|
);
|
|
|
|
// Nested let
|
|
assert_eq!(
|
|
eval_str("(let (a 1234) (let (b 4321) (+ a b)))"),
|
|
Value::Number(5555.into())
|
|
);
|
|
// Does shadow
|
|
assert_eq!(
|
|
eval_str("(let (a 1234) (let* (a 9999 b (+ a 4321)) b))"),
|
|
Value::Number(14320.into())
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_macro() {
|
|
assert_eq!(
|
|
eval_str("(defmacro stringify (x) (cons 'list `,x)) (stringify (1 2 3 4))"),
|
|
Value::list_or_nil([
|
|
Value::Number(1.into()),
|
|
Value::Number(2.into()),
|
|
Value::Number(3.into()),
|
|
Value::Number(4.into())
|
|
])
|
|
);
|
|
}
|