mod common;
use grift_eval::*;
use common::{eval_to_num, eval_is_true, eval_is_false, eval_to_string};
#[test]
fn test_eval_number() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "42"), 42);
assert_eq!(eval_to_num(&lisp, &mut eval, "-10"), -10);
}
#[test]
fn test_eval_booleans() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "#t"));
assert!(eval_is_false(&lisp, &mut eval, "#f"));
}
#[test]
fn test_empty_list_is_truthy() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(if '() 1 2)"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(if 0 1 2)"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(if #f 1 2)"), 2);
}
#[test]
fn test_eval_arithmetic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(+ 1 2)"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(- 10 3)"), 7);
assert_eq!(eval_to_num(&lisp, &mut eval, "(* 4 5)"), 20);
assert_eq!(eval_to_num(&lisp, &mut eval, "(/ 20 4)"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(+ 1 2 3 4)"), 10);
}
#[test]
fn test_eval_quote() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("'hello").unwrap();
assert!(lisp.symbol_matches(result, "hello").unwrap());
}
#[test]
fn test_eval_if() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(if #t 1 2)"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(if #f 1 2)"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(if (< 1 2) 10 20)"), 10);
}
#[test]
fn test_eval_define() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define x 42)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "x"), 42);
}
#[test]
fn test_eval_lambda() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "((lambda (x) (+ x 1)) 5)"), 6);
}
#[test]
fn test_eval_define_function() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (square x) (* x x))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(square 5)"), 25);
}
#[test]
fn test_eval_let() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(let ((x 10) (y 20)) (+ x y))"), 30);
}
#[test]
fn test_eval_let_star() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(let* ((x 10) (y (+ x 5))) (+ x y))"), 25);
}
#[test]
fn test_tco_recursion() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (sum-to n acc) (if (= n 0) acc (sum-to (- n 1) (+ acc n))))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(sum-to 100 0)"), 5050); }
#[test]
fn test_eval_recursion() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (fact n) (if (= n 0) 1 (* n (fact (- n 1)))))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(fact 5)"), 120);
}
#[test]
fn test_simple_fib_define() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (countdown n) (if (= n 0) 0 (countdown (- n 1))))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(countdown 5)"), 0);
}
#[test]
fn test_auto_memoization_fibonacci() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (fib n) (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(fib 5)"), 5);
}
#[test]
fn test_auto_memoization_factorial() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (fact n) (if (= n 0) 1 (* n (fact (- n 1)))))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(fact 10)"), 3628800);
assert_eq!(eval_to_num(&lisp, &mut eval, "(fact 10)"), 3628800);
assert_eq!(eval_to_num(&lisp, &mut eval, "(fact 5)"), 120);
}
#[test]
fn test_auto_memoization_not_applied_to_non_recursive() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (square x) (* x x))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(square 5)"), 25);
assert_eq!(eval_to_num(&lisp, &mut eval, "(square 10)"), 100);
}
#[test]
fn test_auto_memoization_with_multiple_args() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (add-rec x y) (if (= y 0) x (add-rec (+ x 1) (- y 1))))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(add-rec 3 4)"), 7);
assert_eq!(eval_to_num(&lisp, &mut eval, "(add-rec 5 3)"), 8);
}
#[test]
fn test_auto_memoization_cache_works() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (fib n) (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))").unwrap();
for _ in 0..3 {
assert_eq!(eval_to_num(&lisp, &mut eval, "(fib 8)"), 21);
}
}
#[test]
fn test_auto_memoization_mutual_recursion_detection() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (countdown n) (if (= n 0) 'done (countdown (- n 1))))").unwrap();
let result = eval.eval_str("(countdown 50)").unwrap();
assert!(lisp.symbol_matches(result, "done").unwrap());
}
#[test]
fn test_auto_memoization_nested_recursive_calls() {
let lisp: Lisp<30000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (sum n acc) (if (= n 0) acc (sum (- n 1) (+ acc n))))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(sum 100 0)"), 5050);
}
#[test]
fn test_lru_cache_eviction() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (identity n) (if (= n 0) 0 (identity (- n 1))))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(identity 50)"), 0);
assert_eq!(eval_to_num(&lisp, &mut eval, "(identity 60)"), 0);
assert_eq!(eval_to_num(&lisp, &mut eval, "(identity 70)"), 0);
assert_eq!(eval_to_num(&lisp, &mut eval, "(identity 80)"), 0);
assert_eq!(eval_to_num(&lisp, &mut eval, "(identity 90)"), 0);
assert_eq!(eval_to_num(&lisp, &mut eval, "(identity 10)"), 0);
}
#[test]
fn test_strict_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(+ 1 2 3)"), 6);
eval.eval_str("(define (add x y) (+ x y))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(add 10 20)"), 30);
}
#[test]
fn test_strict_cons_evaluates_args() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define p (cons (+ 1 2) (+ 3 4)))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(car p)"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(cdr p)"), 7);
eval.eval_str("(define lst (list (* 2 3) (* 4 5) (* 6 7)))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(car lst)"), 6);
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (cdr lst))"), 20);
}
#[test]
fn test_strict_side_effects_immediate() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define count 0)").unwrap();
eval.eval_str("(define (side-effect! x) (set! count (+ count 1)) x)").unwrap();
eval.eval_str("(define lst (cons (side-effect! 1) (cons (side-effect! 2) '())))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "count"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(car lst)"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "count"), 2);
}
#[test]
fn test_strict_if_branches() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (safe-div x y) (if (= y 0) 0 (/ x y)))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(safe-div 10 0)"), 0);
assert_eq!(eval_to_num(&lisp, &mut eval, "(safe-div 10 2)"), 5);
}
#[test]
fn test_strict_if_unevaluated_branch() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(if #t 42 undefined-var)"), 42);
eval.eval_str("(define count 0)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(if #t 1 (begin (set! count 99) 2))"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "count"), 0); }
#[test]
fn test_strict_lambda_args_evaluated() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (first x y) x)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(first (+ 1 1) (+ 2 2))"), 2);
eval.eval_str("(define effect-count 0)").unwrap();
eval.eval_str("(define (with-effect x) (set! effect-count (+ effect-count 1)) x)").unwrap();
eval.eval_str("(define (ignore-second a b) a)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(ignore-second (with-effect 1) (with-effect 2))"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "effect-count"), 2);
}
#[test]
fn test_strict_tco_works() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (count n) (if (= n 0) 0 (count (- n 1))))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(count 50)"), 0); }
#[test]
fn test_strict_variable_binding() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (square x) (* x x))").unwrap();
eval.eval_str("(define val (square 5))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "val"), 25);
assert_eq!(eval_to_num(&lisp, &mut eval, "val"), 25);
assert_eq!(eval_to_num(&lisp, &mut eval, "val"), 25);
}
#[test]
fn test_strict_closure() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (make-adder n) (lambda (x) (+ x n)))").unwrap();
eval.eval_str("(define add5 (make-adder 5))").unwrap();
eval.eval_str("(define add10 (make-adder 10))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(add5 3)"), 8);
assert_eq!(eval_to_num(&lisp, &mut eval, "(add10 3)"), 13);
}
#[test]
fn test_predicates() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(null? '())"));
assert!(eval_is_false(&lisp, &mut eval, "(null? '(1))"));
assert!(eval_is_true(&lisp, &mut eval, "(pair? '(1 . 2))"));
assert!(eval_is_false(&lisp, &mut eval, "(pair? 42)"));
assert!(eval_is_true(&lisp, &mut eval, "(number? 42)"));
assert!(eval_is_false(&lisp, &mut eval, "(number? 'x)"));
assert!(eval_is_true(&lisp, &mut eval, "(boolean? #t)"));
assert!(eval_is_true(&lisp, &mut eval, "(boolean? #f)"));
assert!(eval_is_false(&lisp, &mut eval, "(boolean? 1)"));
assert!(eval_is_true(&lisp, &mut eval, "(symbol? 'x)"));
assert!(eval_is_false(&lisp, &mut eval, "(symbol? 42)"));
assert!(eval_is_true(&lisp, &mut eval, "(procedure? +)"));
assert!(eval_is_true(&lisp, &mut eval, "(procedure? (lambda (x) x))"));
assert!(eval_is_false(&lisp, &mut eval, "(procedure? 42)"));
}
#[test]
fn test_not() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(not #f)"));
assert!(eval_is_false(&lisp, &mut eval, "(not #t)"));
assert!(eval_is_false(&lisp, &mut eval, "(not '())")); assert!(eval_is_false(&lisp, &mut eval, "(not 0)")); }
#[test]
fn test_cond() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval_to_num(&lisp, &mut eval,
"(cond ((< 5 3) 1) ((> 5 3) 2) (else 3))");
assert_eq!(result, 2);
}
#[test]
fn test_and_or() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(and #t #t)"));
assert!(eval_is_false(&lisp, &mut eval, "(and #t #f)"));
assert!(eval_is_true(&lisp, &mut eval, "(and)"));
assert!(eval_is_true(&lisp, &mut eval, "(or #f #t)"));
assert!(eval_is_false(&lisp, &mut eval, "(or #f #f)"));
assert!(eval_is_false(&lisp, &mut eval, "(or)")); }
#[test]
fn test_gc_during_eval() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define x 42)").unwrap();
let _stats = eval.gc();
assert_eq!(eval_to_num(&lisp, &mut eval, "x"), 42);
}
#[test]
fn test_tco_deep_recursion() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (sum-tail n acc) (if (= n 0) acc (sum-tail (- n 1) (+ acc n))))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(sum-tail 100 0)"), 5050);
eval.eval_str("(define (countdown n) (if (= n 0) 'done (countdown (- n 1))))").unwrap();
let result = eval.eval_str("(countdown 100)").unwrap();
assert!(lisp.get(result).unwrap().is_symbol());
}
#[test]
fn test_tco_mutual_recursion() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (my-even n) (if (= n 0) #t (my-odd (- n 1))))").unwrap();
eval.eval_str("(define (my-odd n) (if (= n 0) #f (my-even (- n 1))))").unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(my-even 0)"));
assert!(eval_is_false(&lisp, &mut eval, "(my-odd 0)"));
assert!(eval_is_true(&lisp, &mut eval, "(my-even 20)"));
assert!(eval_is_false(&lisp, &mut eval, "(my-odd 20)"));
assert!(eval_is_false(&lisp, &mut eval, "(my-even 19)"));
assert!(eval_is_true(&lisp, &mut eval, "(my-odd 19)"));
}
#[test]
fn test_tco_accumulator_pattern() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (fact-tail n acc) (if (= n 0) acc (fact-tail (- n 1) (* n acc))))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(fact-tail 10 1)"), 3628800);
eval.eval_str("(define (len-tail lst acc) (if (null? lst) acc (len-tail (cdr lst) (+ acc 1))))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(len-tail '(a b c d e) 0)"), 5);
eval.eval_str("(define (rev-tail lst acc) (if (null? lst) acc (rev-tail (cdr lst) (cons (car lst) acc))))").unwrap();
let _result = eval.eval_str("(rev-tail '(1 2 3) '())").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (rev-tail '(1 2 3) '()))"), 3);
}
#[test]
fn test_tco_in_cond() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str(r#"
(define (classify n)
(cond ((< n 0) (classify (- 0 n)))
((= n 0) 'zero)
((< n 10) 'small)
((< n 100) 'medium)
(else (classify (/ n 10)))))
"#).unwrap();
let result = eval.eval_str("(classify -42)").unwrap();
assert!(lisp.get(result).unwrap().is_symbol());
let result = eval.eval_str("(classify 999)").unwrap();
assert!(lisp.get(result).unwrap().is_symbol());
}
#[test]
fn test_and_or_short_circuit() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_false(&lisp, &mut eval, "(and #f undefined-error)"));
assert!(eval_is_true(&lisp, &mut eval, "(and #t #t #t)"));
assert!(eval_is_true(&lisp, &mut eval, "(or #t undefined-error)"));
assert!(eval_is_false(&lisp, &mut eval, "(or #f #f #f)"));
}
#[test]
fn test_let_bindings() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(let ((x 10) (y 20)) (+ x y))"), 30);
assert_eq!(eval_to_num(&lisp, &mut eval, "(let* ((x 5) (y (* x 2))) (+ x y))"), 15);
assert_eq!(eval_to_num(&lisp, &mut eval,
"(let ((x 1)) (let ((y 2)) (let ((z 3)) (+ x y z))))"), 6);
}
#[test]
fn test_petrofsky_let() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(let - ((n (- 1))) n)"), -1);
assert_eq!(eval_to_num(&lisp, &mut eval,
"(let + ((n (+ 2 3))) (if (= n 0) 100 (+ (- n 1))))"), 100);
assert_eq!(eval_to_num(&lisp, &mut eval,
"(let * ((x (* 2 3))) (if (= x 0) 42 (* (- x 1))))"), 42);
}
#[test]
fn test_named_let() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
"(let fact ((n 5)) (if (= n 0) 1 (* n (fact (- n 1)))))"), 120);
assert_eq!(eval_to_num(&lisp, &mut eval,
"(let sum ((n 10) (acc 0)) (if (= n 0) acc (sum (- n 1) (+ acc n))))"), 55);
assert_eq!(eval_to_num(&lisp, &mut eval, "(let loop () 42)"), 42);
}
#[test]
fn test_cons_with_expressions() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define p (cons (+ 1 2) (+ 3 4)))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(car p)"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(cdr p)"), 7);
}
#[test]
fn test_nested_structures() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define deep (cons (cons (cons 1 2) 3) 4))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(cdr deep)"), 4);
assert_eq!(eval_to_num(&lisp, &mut eval, "(cdr (car deep))"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(cdr (car (car deep)))"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (car (car deep)))"), 1);
}
#[test]
fn test_nested_calls() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (double x) (* x 2))").unwrap();
eval.eval_str("(define (quad x) (double (double x)))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(quad 5)"), 20);
assert_eq!(eval_to_num(&lisp, &mut eval, "(quad (quad 2))"), 32);
}
#[test]
fn test_higher_order() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (apply-twice f x) (f (f x)))").unwrap();
eval.eval_str("(define (inc x) (+ x 1))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(apply-twice inc 0)"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(apply-twice (lambda (x) (* x 2)) 3)"), 12);
}
#[test]
fn test_currying() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (curry-add a) (lambda (b) (lambda (c) (+ a b c))))").unwrap();
eval.eval_str("(define add1 (curry-add 1))").unwrap();
eval.eval_str("(define add1-2 (add1 2))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(add1-2 3)"), 6);
assert_eq!(eval_to_num(&lisp, &mut eval, "(((curry-add 10) 20) 30)"), 60);
}
#[test]
fn test_repeated_access() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define p (cons (* 111 111) 0))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(car p)"), 12321);
assert_eq!(eval_to_num(&lisp, &mut eval, "(car p)"), 12321);
assert_eq!(eval_to_num(&lisp, &mut eval, "(car p)"), 12321);
}
#[test]
fn test_case_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
"(case 2 ((1) 10) ((2) 20) ((3) 30))"), 20);
}
#[test]
fn test_case_multiple_datums() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
"(case 'b ((a) 1) ((b c) 2) ((d) 3))"), 2);
}
#[test]
fn test_case_else() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
"(case 99 ((1) 10) ((2) 20) (else 0))"), 0);
}
#[test]
fn test_case_no_match() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(case 5 ((1) 10) ((2) 20))").unwrap();
assert!(lisp.get(result).unwrap().is_false());
}
#[test]
fn test_do_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
"(do ((i 5 (- i 1))) ((= i 0) 42))"), 42);
}
#[test]
fn test_do_accumulator() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
"(do ((i 1 (+ i 1)) (sum 0 (+ sum i))) ((> i 5) sum))"), 15);
}
#[test]
fn test_do_factorial() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
"(do ((n 5 (- n 1)) (result 1 (* result n))) ((= n 0) result))"), 120);
}
#[test]
fn test_quasiquote_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define x 42)").unwrap();
let result = eval.eval_str("(quasiquote (a b (unquote x)))").unwrap();
let third = lisp.car(lisp.cdr(lisp.cdr(result).unwrap()).unwrap()).unwrap();
assert_eq!(lisp.get(third).unwrap().as_number().unwrap(), 42);
}
#[test]
fn test_quasiquote_nested() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define y 10)").unwrap();
let result = eval.eval_str("(quasiquote ((unquote y) 2 (unquote (+ y 1))))").unwrap();
let first = lisp.car(result).unwrap();
let third = lisp.car(lisp.cdr(lisp.cdr(result).unwrap()).unwrap()).unwrap();
assert_eq!(lisp.get(first).unwrap().as_number().unwrap(), 10);
assert_eq!(lisp.get(third).unwrap().as_number().unwrap(), 11);
}
#[test]
fn test_eval_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(eval '(+ 1 2))"), 3);
}
#[test]
fn test_eval_symbol() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define x 99)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(eval 'x)"), 99);
}
#[test]
fn test_eval_computed() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define expr (list '+ 10 20))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(eval expr)"), 30);
}
#[test]
fn test_apply_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(apply + '(1 2 3))"), 6);
}
#[test]
fn test_apply_lambda() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (sum3 a b c) (+ a b c))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(apply sum3 '(10 20 30))"), 60);
}
#[test]
fn test_values_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(values 1 2 3)").unwrap();
assert_eq!(lisp.get(lisp.car(result).unwrap()).unwrap().as_number().unwrap(), 1);
}
#[test]
fn test_call_with_values_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
"(call-with-values (lambda () (values 4 5)) (lambda (a b) (+ a b)))"), 9);
assert_eq!(eval_to_num(&lisp, &mut eval,
"(call-with-values (lambda () (values 4 5)) (lambda (a b) b))"), 5);
}
#[test]
fn test_call_with_values_single() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
"(call-with-values (lambda () 42) (lambda (x) (* x 2)))"), 84);
}
#[test]
fn test_call_with_values_no_values() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(call-with-values (lambda () (values)) (lambda () 'no-values))").unwrap();
assert!(lisp.symbol_matches(result, "no-values").unwrap());
}
#[test]
fn test_call_with_values_many() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
"(call-with-values (lambda () (values 1 2 3)) (lambda (x y z) (+ x y z)))"), 6);
}
#[test]
fn test_let_values_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
"(let-values (((a b) (values 1 2))) (+ a b))"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(let-values () 42)"), 42);
}
#[test]
fn test_let_values_multiple_bindings() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
"(let-values (((a b) (values 1 2)) ((c) (values 3))) (+ a b c))"), 6);
}
#[test]
fn test_let_star_values() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
"(let*-values (((a b) (values 1 2)) ((c) (values (+ a b)))) c)"), 3);
}
#[test]
fn test_define_values_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define-values (dv-x dv-y) (values 10 20))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "dv-x"), 10);
assert_eq!(eval_to_num(&lisp, &mut eval, "dv-y"), 20);
}
#[test]
fn test_define_values_single() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define-values (dv-single) (values 42))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "dv-single"), 42);
}
#[test]
fn test_define_values_three() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define-values (dv-a dv-b dv-c) (values 1 2 3))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "dv-a"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "dv-b"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "dv-c"), 3);
}
#[test]
fn test_define_values_many() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define-values (v1 v2 v3 v4 v5 v6 v7) (values 10 20 30 40 50 60 70))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "v1"), 10);
assert_eq!(eval_to_num(&lisp, &mut eval, "v2"), 20);
assert_eq!(eval_to_num(&lisp, &mut eval, "v3"), 30);
assert_eq!(eval_to_num(&lisp, &mut eval, "v4"), 40);
assert_eq!(eval_to_num(&lisp, &mut eval, "v5"), 50);
assert_eq!(eval_to_num(&lisp, &mut eval, "v6"), 60);
assert_eq!(eval_to_num(&lisp, &mut eval, "v7"), 70);
}
#[test]
fn test_delay_force_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define promise (delay (+ 1 2)))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(force promise)"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(force promise)"), 3);
}
#[test]
fn test_delay_memoization() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define counter 0)").unwrap();
eval.eval_str("(define lazy-inc (delay (begin (set! counter (+ counter 1)) counter)))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(force lazy-inc)"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(force lazy-inc)"), 1); assert_eq!(eval_to_num(&lisp, &mut eval, "counter"), 1); }
#[test]
fn test_car_comprehensive() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(car '(1 2 3))"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (cons 42 99))"), 42);
let result = eval.eval_str("(car '())");
assert!(result.is_err());
}
#[test]
fn test_cdr_comprehensive() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(cdr '(1 2 3))").unwrap();
assert_eq!(lisp.get(lisp.car(result).unwrap()).unwrap().as_number().unwrap(), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(cdr (cons 1 2))"), 2);
let result = eval.eval_str("(cdr '())");
assert!(result.is_err());
}
#[test]
fn test_cons_comprehensive() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(cons 1 2)").unwrap();
assert!(lisp.get(result).unwrap().is_cons());
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (cons 1 '()))"), 1);
}
#[test]
fn test_list_comprehensive() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(list)").unwrap();
assert!(lisp.get(result).unwrap().is_nil());
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (list 42))"), 42);
eval.eval_str("(define my-list (list 1 2 3 4 5))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(car my-list)"), 1);
}
#[test]
fn test_eq_comprehensive() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(eq? 1 1)"));
assert!(eval_is_false(&lisp, &mut eval, "(eq? 1 2)"));
assert!(eval_is_true(&lisp, &mut eval, "(eq? 'a 'a)"));
assert!(eval_is_false(&lisp, &mut eval, "(eq? 'a 'b)"));
assert!(eval_is_true(&lisp, &mut eval, "(eq? '() '())"));
assert!(eval_is_true(&lisp, &mut eval, "(eq? #t #t)"));
assert!(eval_is_true(&lisp, &mut eval, "(eq? #f #f)"));
assert!(eval_is_false(&lisp, &mut eval, "(eq? #t #f)"));
assert!(eval_is_true(&lisp, &mut eval, "(eqv? 1 1)"));
assert!(eval_is_false(&lisp, &mut eval, "(eqv? 1 2)"));
assert!(eval_is_true(&lisp, &mut eval, "(equal? '(1 2 3) '(1 2 3))"));
assert!(eval_is_false(&lisp, &mut eval, "(equal? '(1 2 3) '(1 2 4))"));
}
#[test]
fn test_null_comprehensive() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(null? '())"));
assert!(eval_is_false(&lisp, &mut eval, "(null? 0)"));
assert!(eval_is_false(&lisp, &mut eval, "(null? #f)"));
assert!(eval_is_false(&lisp, &mut eval, "(null? '(1))"));
}
#[test]
fn test_pair_comprehensive() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(pair? '(1 2))"));
assert!(eval_is_true(&lisp, &mut eval, "(pair? (cons 1 2))"));
assert!(eval_is_false(&lisp, &mut eval, "(pair? '())"));
assert!(eval_is_false(&lisp, &mut eval, "(pair? 42)"));
assert!(eval_is_false(&lisp, &mut eval, "(pair? 'x)"));
}
#[test]
fn test_number_comprehensive() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(number? 42)"));
assert!(eval_is_true(&lisp, &mut eval, "(number? -10)"));
assert!(eval_is_true(&lisp, &mut eval, "(number? 0)"));
assert!(eval_is_false(&lisp, &mut eval, "(number? 'x)"));
assert!(eval_is_false(&lisp, &mut eval, "(number? #t)"));
assert!(eval_is_false(&lisp, &mut eval, "(number? '())"));
}
#[test]
fn test_boolean_comprehensive() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(boolean? #t)"));
assert!(eval_is_true(&lisp, &mut eval, "(boolean? #f)"));
assert!(eval_is_false(&lisp, &mut eval, "(boolean? 1)"));
assert!(eval_is_false(&lisp, &mut eval, "(boolean? '())"));
assert!(eval_is_false(&lisp, &mut eval, "(boolean? 'true)"));
}
#[test]
fn test_symbol_comprehensive() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(symbol? 'x)"));
assert!(eval_is_true(&lisp, &mut eval, "(symbol? 'hello-world)"));
assert!(eval_is_false(&lisp, &mut eval, "(symbol? 42)"));
assert!(eval_is_false(&lisp, &mut eval, "(symbol? #t)"));
assert!(eval_is_false(&lisp, &mut eval, "(symbol? '(a b))"));
}
#[test]
fn test_procedure_comprehensive() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(procedure? +)"));
assert!(eval_is_true(&lisp, &mut eval, "(procedure? car)"));
assert!(eval_is_true(&lisp, &mut eval, "(procedure? (lambda (x) x))"));
assert!(eval_is_false(&lisp, &mut eval, "(procedure? 42)"));
assert!(eval_is_false(&lisp, &mut eval, "(procedure? 'lambda)"));
}
#[test]
fn test_add_comprehensive() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(+)"), 0);
assert_eq!(eval_to_num(&lisp, &mut eval, "(+ 5)"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(+ 1 2)"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(+ 1 2 3 4 5)"), 15);
assert_eq!(eval_to_num(&lisp, &mut eval, "(+ -5 10)"), 5);
}
#[test]
fn test_sub_comprehensive() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(- 10)"), -10);
assert_eq!(eval_to_num(&lisp, &mut eval, "(- 10 3)"), 7);
assert_eq!(eval_to_num(&lisp, &mut eval, "(- 100 20 30)"), 50);
assert_eq!(eval_to_num(&lisp, &mut eval, "(- 5 10)"), -5);
}
#[test]
fn test_mul_comprehensive() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(*)"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(* 5)"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(* 2 3)"), 6);
assert_eq!(eval_to_num(&lisp, &mut eval, "(* 2 3 4)"), 24);
assert_eq!(eval_to_num(&lisp, &mut eval, "(* -2 3)"), -6);
}
#[test]
fn test_div_comprehensive() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(/ 10 2)"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(/ 100 2 5)"), 10);
assert_eq!(eval_to_num(&lisp, &mut eval, "(/ -10 2)"), -5);
}
#[test]
fn test_modulo_comprehensive() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(modulo 10 3)"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(modulo 15 5)"), 0);
assert_eq!(eval_to_num(&lisp, &mut eval, "(modulo 7 2)"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(modulo -13 4)"), 3); assert_eq!(eval_to_num(&lisp, &mut eval, "(modulo 13 -4)"), -3); }
#[test]
fn test_remainder_comprehensive() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(remainder 10 3)"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(remainder 15 5)"), 0);
assert_eq!(eval_to_num(&lisp, &mut eval, "(remainder -13 4)"), -1); assert_eq!(eval_to_num(&lisp, &mut eval, "(remainder 13 -4)"), 1); }
#[test]
fn test_comparisons_comprehensive() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(< 1 2)"));
assert!(eval_is_false(&lisp, &mut eval, "(< 2 1)"));
assert!(eval_is_false(&lisp, &mut eval, "(< 2 2)"));
assert!(eval_is_true(&lisp, &mut eval, "(> 2 1)"));
assert!(eval_is_false(&lisp, &mut eval, "(> 1 2)"));
assert!(eval_is_false(&lisp, &mut eval, "(> 2 2)"));
assert!(eval_is_true(&lisp, &mut eval, "(<= 1 2)"));
assert!(eval_is_true(&lisp, &mut eval, "(<= 2 2)"));
assert!(eval_is_false(&lisp, &mut eval, "(<= 3 2)"));
assert!(eval_is_true(&lisp, &mut eval, "(>= 2 1)"));
assert!(eval_is_true(&lisp, &mut eval, "(>= 2 2)"));
assert!(eval_is_false(&lisp, &mut eval, "(>= 1 2)"));
assert!(eval_is_true(&lisp, &mut eval, "(= 5 5)"));
assert!(eval_is_false(&lisp, &mut eval, "(= 5 6)"));
}
#[test]
fn test_not_comprehensive() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(not #f)"));
assert!(eval_is_false(&lisp, &mut eval, "(not #t)"));
assert!(eval_is_false(&lisp, &mut eval, "(not 0)"));
assert!(eval_is_false(&lisp, &mut eval, "(not '())"));
assert!(eval_is_false(&lisp, &mut eval, "(not 'x)"));
}
#[test]
fn test_and_comprehensive() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(and)"));
assert!(eval_is_true(&lisp, &mut eval, "(and #t)"));
assert!(eval_is_false(&lisp, &mut eval, "(and #f)"));
assert!(eval_is_true(&lisp, &mut eval, "(and #t #t #t)"));
assert!(eval_is_false(&lisp, &mut eval, "(and #t #f #t)"));
assert!(eval_is_false(&lisp, &mut eval, "(and #f undefined-var)"));
}
#[test]
fn test_or_comprehensive() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_false(&lisp, &mut eval, "(or)"));
assert!(eval_is_true(&lisp, &mut eval, "(or #t)"));
assert!(eval_is_false(&lisp, &mut eval, "(or #f)"));
assert!(eval_is_true(&lisp, &mut eval, "(or #f #t #f)"));
assert!(eval_is_true(&lisp, &mut eval, "(or #t undefined-var)"));
}
#[test]
fn test_display() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(display 99)").unwrap();
assert!(lisp.get(result).unwrap().is_void());
}
#[test]
fn test_newline() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(newline)").unwrap();
assert!(lisp.get(result).unwrap().is_void());
}
#[test]
fn test_error() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(error 'test-error)");
assert!(result.is_err());
}
#[test]
fn test_closure_counter() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (make-counter init) (lambda (delta) (+ init delta)))").unwrap();
eval.eval_str("(define counter (make-counter 10))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(counter 5)"), 15);
assert_eq!(eval_to_num(&lisp, &mut eval, "(counter 10)"), 20);
}
#[test]
fn test_closure_nested() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (f a) (lambda (b) (lambda (c) (+ a b c))))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(((f 1) 2) 3)"), 6);
}
#[test]
fn test_closure_captures_correct_env() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define x 1)").unwrap();
eval.eval_str("(define (get-x) x)").unwrap();
eval.eval_str("(define (call-with-x val f) (let ((x val)) (f)))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(call-with-x 100 get-x)"), 1);
}
#[test]
fn test_filter_multiples() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (filter-multiples n stream) (cond ((null? stream) '()) ((= (modulo (car stream) n) 0) (filter-multiples n (cdr stream))) (else (cons (car stream) (filter-multiples n (cdr stream))))))").unwrap();
eval.eval_str("(define nums '(2 3 4 5 6 7 8 9 10))").unwrap();
eval.eval_str("(define filtered (filter-multiples 2 nums))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(car filtered)"), 3);
}
#[test]
fn test_mutation_set() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define x 10)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "x"), 10);
eval.eval_str("(set! x 20)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "x"), 20);
eval.eval_str("(set! x 30)").unwrap();
eval.eval_str("(set! x 40)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "x"), 40);
}
#[test]
fn test_mutation_set_in_closure() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define counter 0)").unwrap();
eval.eval_str("(define (inc!) (set! counter (+ counter 1)))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "counter"), 0);
eval.eval_str("(inc!)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "counter"), 1);
eval.eval_str("(inc!)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "counter"), 2);
}
#[test]
fn test_mutation_set_car_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define p (cons 1 2))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(car p)"), 1);
eval.eval_str("(set-car! p 10)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(car p)"), 10);
assert_eq!(eval_to_num(&lisp, &mut eval, "(cdr p)"), 2); }
#[test]
fn test_mutation_set_cdr_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define p (cons 1 2))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(cdr p)"), 2);
eval.eval_str("(set-cdr! p 20)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(cdr p)"), 20);
assert_eq!(eval_to_num(&lisp, &mut eval, "(car p)"), 1); }
#[test]
fn test_mutation_build_list() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define lst (cons 1 '()))").unwrap();
eval.eval_str("(set-cdr! lst (cons 2 '()))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(car lst)"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (cdr lst))"), 2);
}
#[test]
fn test_mutation_with_gc() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define x (cons 1 2))").unwrap();
eval.eval_str("(set-car! x 100)").unwrap();
let _stats = eval.gc();
assert_eq!(eval_to_num(&lisp, &mut eval, "(car x)"), 100);
}
#[test]
fn test_equal_circular_cdr() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define x (list 1 2))").unwrap();
eval.eval_str("(set-cdr! (cdr x) x)").unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(equal? x x)"));
}
#[test]
fn test_equal_circular_car() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define y (cons 0 '()))").unwrap();
eval.eval_str("(set-car! y y)").unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(equal? y y)"));
}
#[test]
fn test_equal_two_different_circular_lists() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define a (list 1))").unwrap();
eval.eval_str("(set-cdr! a a)").unwrap();
eval.eval_str("(define b (list 1))").unwrap();
eval.eval_str("(set-cdr! b b)").unwrap();
assert!(eval_is_false(&lisp, &mut eval, "(equal? a b)"));
}
#[test]
fn test_stdlib_length() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(length '())"), 0);
assert_eq!(eval_to_num(&lisp, &mut eval, "(length '(1))"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(length '(1 2 3))"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(length '(a b c d e))"), 5);
}
#[test]
fn test_stdlib_fold() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(fold + 0 '(1 2 3 4 5))"), 15);
assert_eq!(eval_to_num(&lisp, &mut eval, "(fold * 1 '(1 2 3 4 5))"), 120);
assert_eq!(eval_to_num(&lisp, &mut eval, "(fold + 42 '())"), 42);
}
#[test]
fn test_stdlib_nth() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(nth 0 '(10 20 30))"), 10);
assert_eq!(eval_to_num(&lisp, &mut eval, "(nth 1 '(10 20 30))"), 20);
assert_eq!(eval_to_num(&lisp, &mut eval, "(nth 2 '(10 20 30))"), 30);
}
#[test]
fn test_stdlib_range() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(length (range 0 5))"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (range 0 5))"), 0);
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (cdr (range 0 5)))"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(length (range 0 0))"), 0);
}
#[test]
fn test_stdlib_identity_and_constantly() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(identity 42)"), 42);
eval.eval_str("(define always-5 (constantly 5))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(always-5 1)"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(always-5 100)"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(always-5 'foo)"), 5);
}
#[test]
fn test_stdlib_curry() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define add5 (curry + 5))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(add5 10)"), 15);
assert_eq!(eval_to_num(&lisp, &mut eval, "(add5 20)"), 25);
}
#[test]
fn test_stdlib_cadr_caddr_cddr() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(cadr '(1 2 3))"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(caddr '(1 2 3))"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (cddr '(1 2 3 4)))"), 3);
}
#[test]
fn test_stdlib_member() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(member 2 '(1 2 3))").unwrap();
assert!(!lisp.get(result).unwrap().is_false()); assert_eq!(lisp.get(lisp.car(result).unwrap()).unwrap().as_number().unwrap(), 2);
assert!(eval_is_false(&lisp, &mut eval, "(member 5 '(1 2 3))"));
assert!(eval_is_false(&lisp, &mut eval, "(member 1 '())"));
let result = eval.eval_str("(member '(a) '((x) (a) (b)))").unwrap();
assert!(!lisp.get(result).unwrap().is_false());
}
#[test]
fn test_memq_memv_differences() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(memq 'a '(b a c))").unwrap();
assert!(!lisp.get(result).unwrap().is_false());
assert!(lisp.symbol_matches(lisp.car(result).unwrap(), "a").unwrap());
assert!(eval_is_false(&lisp, &mut eval, "(memq '(a) '((b) (a) (c)))"));
let result = eval.eval_str("(memv 2 '(1 2 3))").unwrap();
assert!(!lisp.get(result).unwrap().is_false());
assert_eq!(lisp.get(lisp.car(result).unwrap()).unwrap().as_number().unwrap(), 2);
}
#[test]
fn test_assoc_variants() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(assoc '(a) '(((x) 1) ((a) 2) ((b) 3)))").unwrap();
assert!(!lisp.get(result).unwrap().is_false());
let value = lisp.car(lisp.cdr(result).unwrap()).unwrap();
assert_eq!(lisp.get(value).unwrap().as_number().unwrap(), 2);
assert!(eval_is_false(&lisp, &mut eval, "(assoc 'x '((a 1) (b 2)))"));
let result = eval.eval_str("(assq 'b '((a 1) (b 2) (c 3)))").unwrap();
assert!(!lisp.get(result).unwrap().is_false());
let value = lisp.car(lisp.cdr(result).unwrap()).unwrap();
assert_eq!(lisp.get(value).unwrap().as_number().unwrap(), 2);
let result = eval.eval_str("(assv 5 '((2 a) (5 b) (7 c)))").unwrap();
assert!(!lisp.get(result).unwrap().is_false());
}
#[test]
fn test_gc_preserves_closures() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (make-counter) (define n 0) (lambda () (set! n (+ n 1)) n))").unwrap();
eval.eval_str("(define counter (make-counter))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(counter)"), 1);
let _stats = eval.gc();
assert_eq!(eval_to_num(&lisp, &mut eval, "(counter)"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(counter)"), 3);
}
#[test]
fn test_gc_collects_unreachable() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(cons 1 2)").unwrap();
eval.eval_str("(cons 3 4)").unwrap();
eval.eval_str("(cons 5 6)").unwrap();
let stats_before = lisp.stats();
let gc_stats = eval.gc();
let stats_after = lisp.stats();
assert!(gc_stats.collected > 0);
assert!(stats_after.allocated <= stats_before.allocated);
}
#[test]
fn test_gc_intern_table_survives() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define a 1)").unwrap();
eval.eval_str("(define b 2)").unwrap();
eval.eval_str("(define c 3)").unwrap();
eval.gc();
assert_eq!(eval_to_num(&lisp, &mut eval, "(+ a b c)"), 6);
}
#[test]
fn test_pitfall_empty_list_is_truthy() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(if '() 1 2)"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(if #f 1 2)"), 2); }
#[test]
fn test_pitfall_zero_is_truthy() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(if 0 1 2)"), 1); }
#[test]
fn test_stdlib_parses_on_each_call() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(length '(1))"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(length '(1 2))"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(length '(1 2 3))"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(fold + 0 (range 0 10))"), 45);
}
#[test]
fn test_stdlib_recursion_works() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(length '(a b c d e f g h i j))"), 10);
assert_eq!(eval_to_num(&lisp, &mut eval, "(fold + 0 '(1 2 3 4 5 6 7 8 9 10))"), 55);
}
#[test]
fn test_gc_builtin() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(cons 1 2)").unwrap();
eval.eval_str("(cons 3 4)").unwrap();
let result = eval.eval_str("(gc)").unwrap();
assert!(lisp.get(result).unwrap().is_cons());
let marked = lisp.car(result).unwrap();
assert!(lisp.get(marked).unwrap().is_number());
}
#[test]
fn test_gc_enable_disable() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(gc-enabled?)"));
let result = eval.eval_str("(gc-disable)").unwrap();
assert!(lisp.get(result).unwrap().is_false());
assert!(eval_is_false(&lisp, &mut eval, "(gc-enabled?)"));
let result = eval.eval_str("(gc-enable)").unwrap();
assert!(lisp.get(result).unwrap().is_true());
assert!(eval_is_true(&lisp, &mut eval, "(gc-enabled?)"));
}
#[test]
fn test_arena_stats_builtin() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(arena-stats)").unwrap();
assert!(lisp.get(result).unwrap().is_cons());
let capacity = lisp.car(result).unwrap();
assert_eq!(lisp.get(capacity).unwrap().as_number(), Some(20000));
let rest = lisp.cdr(result).unwrap();
let allocated = lisp.car(rest).unwrap();
assert!(lisp.get(allocated).unwrap().is_number());
assert!(lisp.get(allocated).unwrap().as_number().unwrap() > 0);
}
#[test]
fn test_gc_disabled_no_collect() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(gc-disable)").unwrap();
let before = eval.eval_str("(arena-stats)").unwrap();
let _before_allocated = lisp.get(lisp.car(lisp.cdr(before).unwrap()).unwrap())
.unwrap().as_number().unwrap();
eval.eval_str("(cons 1 2)").unwrap();
eval.eval_str("(cons 3 4)").unwrap();
let gc_result = eval.eval_str("(gc)").unwrap();
let marked = lisp.get(lisp.car(gc_result).unwrap()).unwrap().as_number().unwrap();
let collected = lisp.get(lisp.car(lisp.cdr(gc_result).unwrap()).unwrap())
.unwrap().as_number().unwrap();
assert_eq!(marked, 0);
assert_eq!(collected, 0);
eval.eval_str("(gc-enable)").unwrap();
}
#[test]
fn test_gc_disabled_memory_grows() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let stats_before = eval.eval_str("(arena-stats)").unwrap();
let allocated_before = lisp.get(lisp.car(lisp.cdr(stats_before).unwrap()).unwrap())
.unwrap().as_number().unwrap();
eval.eval_str("(gc-disable)").unwrap();
for _ in 0..100 {
eval.eval_str("(cons 'garbage 'value)").unwrap();
}
let gc_result = eval.eval_str("(gc)").unwrap();
let collected = lisp.get(lisp.car(lisp.cdr(gc_result).unwrap()).unwrap())
.unwrap().as_number().unwrap();
assert_eq!(collected, 0, "GC should not collect anything when disabled");
let stats_after = eval.eval_str("(arena-stats)").unwrap();
let allocated_after = lisp.get(lisp.car(lisp.cdr(stats_after).unwrap()).unwrap())
.unwrap().as_number().unwrap();
assert!(allocated_after > allocated_before,
"Memory should grow when GC is disabled: before={}, after={}",
allocated_before, allocated_after);
eval.eval_str("(gc-enable)").unwrap();
let gc_result = eval.eval_str("(gc)").unwrap();
let collected = lisp.get(lisp.car(lisp.cdr(gc_result).unwrap()).unwrap())
.unwrap().as_number().unwrap();
assert!(collected > 0, "GC should collect garbage when re-enabled");
}
#[test]
fn test_gc_disabled_trampoline_respects_flag() {
let lisp: Lisp<30000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(gc-disable)").unwrap();
let stats_before = eval.eval_str("(arena-stats)").unwrap();
let allocated_before = lisp.get(lisp.car(lisp.cdr(stats_before).unwrap()).unwrap())
.unwrap().as_number().unwrap();
eval.eval_str("
(define (make-garbage n)
(if (<= n 0)
'done
(begin
(cons n (cons n (cons n '()))) ; create garbage
(make-garbage (- n 1)))))
").unwrap();
eval.eval_str("(make-garbage 100)").unwrap();
let stats_after = eval.eval_str("(arena-stats)").unwrap();
let allocated_after = lisp.get(lisp.car(lisp.cdr(stats_after).unwrap()).unwrap())
.unwrap().as_number().unwrap();
assert!(allocated_after > allocated_before,
"Trampoline should NOT run GC when disabled: before={}, after={}",
allocated_before, allocated_after);
eval.eval_str("(gc-enable)").unwrap();
let gc_result = eval.eval_str("(gc)").unwrap();
let collected = lisp.get(lisp.car(lisp.cdr(gc_result).unwrap()).unwrap())
.unwrap().as_number().unwrap();
assert!(collected > 0, "GC should work after re-enable");
}
#[test]
fn test_gc_disabled_via_arena_respected_by_lisp() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(gc-enabled?)"));
eval.eval_str("(cons 1 2)").unwrap();
eval.eval_str("(cons 3 4)").unwrap();
eval.eval_str("(gc-disable)").unwrap();
assert!(eval_is_false(&lisp, &mut eval, "(gc-enabled?)"));
let gc_result = eval.eval_str("(gc)").unwrap();
let marked = lisp.get(lisp.car(gc_result).unwrap()).unwrap().as_number().unwrap();
let collected = lisp.get(lisp.car(lisp.cdr(gc_result).unwrap()).unwrap())
.unwrap().as_number().unwrap();
assert_eq!(marked, 0, "Marked should be 0 when GC is disabled");
assert_eq!(collected, 0, "Collected should be 0 when GC is disabled");
eval.eval_str("(gc-enable)").unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(gc-enabled?)"));
}
#[test]
fn test_gc_unconditional_ignores_disabled_flag() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(cons 'a 'b)").unwrap();
eval.eval_str("(cons 'c 'd)").unwrap();
eval.eval_str("(gc-disable)").unwrap();
let gc_result = eval.eval_str("(gc)").unwrap();
let collected = lisp.get(lisp.car(lisp.cdr(gc_result).unwrap()).unwrap())
.unwrap().as_number().unwrap();
assert_eq!(collected, 0, "Regular gc should not collect when disabled");
eval.eval_str("(gc-enable)").unwrap();
let gc_result = eval.eval_str("(gc)").unwrap();
let collected = lisp.get(lisp.car(lisp.cdr(gc_result).unwrap()).unwrap())
.unwrap().as_number().unwrap();
assert!(collected >= 0, "GC should work when enabled");
}
#[test]
fn test_letrec_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
"(letrec ((fact (lambda (n) (if (= n 0) 1 (* n (fact (- n 1))))))) (fact 5))"),
120);
}
#[test]
fn test_letrec_mutual_recursion() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval,
"(letrec ((even? (lambda (n) (if (zero? n) #t (odd? (- n 1)))))
(odd? (lambda (n) (if (zero? n) #f (even? (- n 1))))))
(even? 88))"));
assert!(eval_is_false(&lisp, &mut eval,
"(letrec ((even? (lambda (n) (if (zero? n) #t (odd? (- n 1)))))
(odd? (lambda (n) (if (zero? n) #f (even? (- n 1))))))
(even? 7))"));
}
#[test]
fn test_letrec_star_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
"(letrec* ((x 1) (y (+ x 2))) (+ x y))"),
4);
}
#[test]
fn test_letrec_star_mutual_recursion() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval,
"(letrec* ((even? (lambda (n) (if (zero? n) #t (odd? (- n 1)))))
(odd? (lambda (n) (if (zero? n) #f (even? (- n 1))))))
(even? 10))"));
}
#[test]
fn test_when_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define counter 0)").unwrap();
eval.eval_str("(when #t (set! counter (+ counter 1)))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "counter"), 1);
eval.eval_str("(when #f (set! counter (+ counter 10)))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "counter"), 1);
}
#[test]
fn test_when_multiple_expressions() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define a 0)").unwrap();
eval.eval_str("(define b 0)").unwrap();
eval.eval_str("(when (= 1 1) (set! a 1) (set! b 2))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "a"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "b"), 2);
}
#[test]
fn test_unless_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define counter 0)").unwrap();
eval.eval_str("(unless #t (set! counter (+ counter 1)))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "counter"), 0);
eval.eval_str("(unless #f (set! counter (+ counter 10)))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "counter"), 10);
}
#[test]
fn test_unless_multiple_expressions() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define a 0)").unwrap();
eval.eval_str("(define b 0)").unwrap();
eval.eval_str("(unless (= 1 2) (set! a 5) (set! b 6))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "a"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "b"), 6);
}
#[test]
fn test_rounding_operations() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(floor 5)"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(floor -5)"), -5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(ceiling 5)"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(ceiling -5)"), -5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(truncate 5)"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(truncate -5)"), -5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(round 5)"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(round -5)"), -5);
}
#[test]
fn test_exact_integer_predicate() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(exact-integer? 42)"));
assert!(eval_is_true(&lisp, &mut eval, "(exact-integer? 0)"));
assert!(eval_is_true(&lisp, &mut eval, "(exact-integer? -100)"));
assert!(eval_is_false(&lisp, &mut eval, "(exact-integer? 'symbol)"));
}
#[test]
fn test_make_list_stdlib() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(length (make-list 3 0))"), 3);
let result = eval.eval_str("(car (make-list 3 0))").unwrap();
assert_eq!(lisp.get(result).unwrap().as_number().unwrap(), 0);
}
#[test]
fn test_last_and_last_pair() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(last '(1 2 3))"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(last '(5))"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (last-pair '(1 2 3)))"), 3);
}
#[test]
fn test_any_and_every() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(any positive? '(1 -2 -3))"));
assert!(eval_is_false(&lisp, &mut eval, "(any positive? '(-1 -2 -3))"));
assert!(eval_is_true(&lisp, &mut eval, "(every positive? '(1 2 3))"));
assert!(eval_is_false(&lisp, &mut eval, "(every positive? '(1 -2 3))"));
}
#[test]
fn test_find() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(find even? '(1 3 4 5 6))"), 4);
assert!(eval_is_false(&lisp, &mut eval, "(find even? '(1 3 5 7))"));
}
#[test]
fn test_remove_and_delete() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(length (remove even? '(1 2 3 4 5)))"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(length (delete 2 '(1 2 3 2 4)))"), 3);
}
#[test]
fn test_fold_right() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(fold-right cons '() '(1 2 3))").unwrap();
let first = lisp.car(result).unwrap();
assert_eq!(lisp.get(first).unwrap().as_number().unwrap(), 1);
}
#[test]
fn test_reduce() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(reduce + 0 '(1 2 3 4))"), 10);
}
#[test]
fn test_cddddr() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(cddddr '(1 2 3 4 5 6))").unwrap();
let first = lisp.car(result).unwrap();
assert_eq!(lisp.get(first).unwrap().as_number().unwrap(), 5);
}
#[test]
fn test_partition() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define result (partition even? '(1 2 3 4 5 6)))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(length (car result))"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(length (cdr result))"), 3);
}
#[test]
fn test_filter_map() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (pos-or-false x) (if (positive? x) x #f))").unwrap();
eval.eval_str("(define result (filter-map pos-or-false '(-1 2 -3 4 -5)))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(length result)"), 2);
}
#[test]
fn test_boolean_eq() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(boolean-eq #t #t)"));
assert!(eval_is_true(&lisp, &mut eval, "(boolean-eq #f #f)"));
assert!(eval_is_false(&lisp, &mut eval, "(boolean-eq #t #f)"));
assert!(eval_is_false(&lisp, &mut eval, "(boolean-eq #f #t)"));
}
#[test]
fn test_member_equal_and_assoc_equal() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(member-equal '(a) '(x (a) y))").unwrap();
assert!(!lisp.get(result).unwrap().is_false());
let result = eval.eval_str("(assoc-equal '(a) '(((a) 1) ((b) 2)))").unwrap();
assert!(!lisp.get(result).unwrap().is_false()); }
#[test]
fn test_list_set() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define my-list (list 1 2 3 4 5))").unwrap();
eval.eval_str("(list-set! my-list 2 99)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(list-ref my-list 2)"), 99);
}
#[test]
fn test_make_list_edge_cases() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(make-list 0 'x)").unwrap();
assert!(lisp.get(result).unwrap().is_nil());
let result = eval.eval_str("(make-list -5 'x)");
assert!(result.is_err());
}
fn eval_to_char<const N: usize>(lisp: &Lisp<N>, eval: &mut Evaluator<N>, input: &str) -> char {
let result = eval.eval_str(input).unwrap();
lisp.get(result).unwrap().as_char().unwrap()
}
fn eval_string_matches<const N: usize>(lisp: &Lisp<N>, eval: &mut Evaluator<N>, input: &str, expected: &str) -> bool {
let result = eval.eval_str(input).unwrap();
lisp.string_matches(result, expected).unwrap()
}
#[test]
fn test_char_literal_simple() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_char(&lisp, &mut eval, r"#\a"), 'a');
assert_eq!(eval_to_char(&lisp, &mut eval, r"#\A"), 'A');
assert_eq!(eval_to_char(&lisp, &mut eval, r"#\z"), 'z');
assert_eq!(eval_to_char(&lisp, &mut eval, r"#\0"), '0');
assert_eq!(eval_to_char(&lisp, &mut eval, r"#\("), '(');
}
#[test]
fn test_char_literal_named() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_char(&lisp, &mut eval, r"#\newline"), '\n');
assert_eq!(eval_to_char(&lisp, &mut eval, r"#\space"), ' ');
assert_eq!(eval_to_char(&lisp, &mut eval, r"#\tab"), '\t');
assert_eq!(eval_to_char(&lisp, &mut eval, r"#\return"), '\r');
assert_eq!(eval_to_char(&lisp, &mut eval, r"#\null"), '\0');
assert_eq!(eval_to_char(&lisp, &mut eval, r"#\alarm"), '\x07');
assert_eq!(eval_to_char(&lisp, &mut eval, r"#\backspace"), '\x08');
assert_eq!(eval_to_char(&lisp, &mut eval, r"#\delete"), '\x7F');
assert_eq!(eval_to_char(&lisp, &mut eval, r"#\escape"), '\x1B');
}
#[test]
fn test_char_literal_hex() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_char(&lisp, &mut eval, r"#\x41"), 'A');
assert_eq!(eval_to_char(&lisp, &mut eval, r"#\x61"), 'a');
assert_eq!(eval_to_char(&lisp, &mut eval, r"#\x20"), ' ');
}
#[test]
fn test_char_predicate() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, r"(char? #\a)"));
assert!(eval_is_true(&lisp, &mut eval, r"(char? #\space)"));
assert!(eval_is_false(&lisp, &mut eval, "(char? 42)"));
assert!(eval_is_false(&lisp, &mut eval, r#"(char? "hello")"#));
}
#[test]
fn test_char_comparison() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, r"(char=? #\a #\a)"));
assert!(eval_is_false(&lisp, &mut eval, r"(char=? #\a #\b)"));
assert!(eval_is_true(&lisp, &mut eval, r"(char=? #\a #\a #\a)"));
assert!(eval_is_true(&lisp, &mut eval, r"(char<? #\a #\b)"));
assert!(eval_is_true(&lisp, &mut eval, r"(char<? #\a #\b #\c)"));
assert!(eval_is_false(&lisp, &mut eval, r"(char<? #\b #\a)"));
assert!(eval_is_true(&lisp, &mut eval, r"(char>? #\b #\a)"));
assert!(eval_is_false(&lisp, &mut eval, r"(char>? #\a #\b)"));
assert!(eval_is_true(&lisp, &mut eval, r"(char<=? #\a #\a)"));
assert!(eval_is_true(&lisp, &mut eval, r"(char<=? #\a #\b)"));
assert!(eval_is_false(&lisp, &mut eval, r"(char<=? #\b #\a)"));
assert!(eval_is_true(&lisp, &mut eval, r"(char>=? #\a #\a)"));
assert!(eval_is_true(&lisp, &mut eval, r"(char>=? #\b #\a)"));
assert!(eval_is_false(&lisp, &mut eval, r"(char>=? #\a #\b)"));
}
#[test]
fn test_char_conversion() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, r"(char->integer #\A)"), 65);
assert_eq!(eval_to_num(&lisp, &mut eval, r"(char->integer #\a)"), 97);
assert_eq!(eval_to_num(&lisp, &mut eval, r"(char->integer #\space)"), 32);
assert_eq!(eval_to_char(&lisp, &mut eval, "(integer->char 65)"), 'A');
assert_eq!(eval_to_char(&lisp, &mut eval, "(integer->char 97)"), 'a');
assert_eq!(eval_to_char(&lisp, &mut eval, "(integer->char 32)"), ' ');
}
#[test]
fn test_char_case_conversion() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_char(&lisp, &mut eval, r"(char-upcase #\a)"), 'A');
assert_eq!(eval_to_char(&lisp, &mut eval, r"(char-upcase #\z)"), 'Z');
assert_eq!(eval_to_char(&lisp, &mut eval, r"(char-upcase #\A)"), 'A'); assert_eq!(eval_to_char(&lisp, &mut eval, r"(char-upcase #\0)"), '0');
assert_eq!(eval_to_char(&lisp, &mut eval, r"(char-downcase #\A)"), 'a');
assert_eq!(eval_to_char(&lisp, &mut eval, r"(char-downcase #\Z)"), 'z');
assert_eq!(eval_to_char(&lisp, &mut eval, r"(char-downcase #\a)"), 'a'); assert_eq!(eval_to_char(&lisp, &mut eval, r"(char-downcase #\0)"), '0'); }
#[test]
fn test_string_literal_simple() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_string_matches(&lisp, &mut eval, r#""hello""#, "hello"));
assert!(eval_string_matches(&lisp, &mut eval, r#""world""#, "world"));
assert!(eval_string_matches(&lisp, &mut eval, r#""""#, "")); }
#[test]
fn test_string_literal_escapes() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_string_matches(&lisp, &mut eval, r#""hello\nworld""#, "hello\nworld"));
assert!(eval_string_matches(&lisp, &mut eval, r#""tab\there""#, "tab\there"));
assert!(eval_string_matches(&lisp, &mut eval, r#""quote\"here""#, "quote\"here"));
assert!(eval_string_matches(&lisp, &mut eval, r#""back\\slash""#, "back\\slash"));
}
#[test]
fn test_string_literal_hex_escape() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_string_matches(&lisp, &mut eval, r#""\x41;bc""#, "Abc"));
assert!(eval_string_matches(&lisp, &mut eval, r#""a\x42;c""#, "aBc"));
}
#[test]
fn test_string_predicate() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, r#"(string? "hello")"#));
assert!(eval_is_true(&lisp, &mut eval, r#"(string? "")"#));
assert!(eval_is_false(&lisp, &mut eval, "(string? 42)"));
assert!(eval_is_false(&lisp, &mut eval, r"(string? #\a)"));
}
#[test]
fn test_string_length() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, r#"(string-length "hello")"#), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, r#"(string-length "")"#), 0);
assert_eq!(eval_to_num(&lisp, &mut eval, r#"(string-length "a")"#), 1);
}
#[test]
fn test_string_ref() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_char(&lisp, &mut eval, r#"(string-ref "hello" 0)"#), 'h');
assert_eq!(eval_to_char(&lisp, &mut eval, r#"(string-ref "hello" 4)"#), 'o');
assert_eq!(eval_to_char(&lisp, &mut eval, r#"(string-ref "abc" 1)"#), 'b');
}
#[test]
fn test_make_string() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_string_matches(&lisp, &mut eval, r#"(make-string 5 #\x)"#, "xxxxx"));
assert!(eval_string_matches(&lisp, &mut eval, r#"(make-string 3 #\a)"#, "aaa"));
assert!(eval_string_matches(&lisp, &mut eval, r#"(make-string 0 #\x)"#, ""));
}
#[test]
fn test_string_constructor() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_string_matches(&lisp, &mut eval, r#"(string #\h #\e #\l #\l #\o)"#, "hello"));
assert!(eval_string_matches(&lisp, &mut eval, r"(string)", "")); }
#[test]
fn test_string_comparison() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, r#"(string=? "abc" "abc")"#));
assert!(eval_is_false(&lisp, &mut eval, r#"(string=? "abc" "abd")"#));
assert!(eval_is_true(&lisp, &mut eval, r#"(string<? "abc" "abd")"#));
assert!(eval_is_true(&lisp, &mut eval, r#"(string<? "ab" "abc")"#));
assert!(eval_is_false(&lisp, &mut eval, r#"(string<? "abc" "abc")"#));
assert!(eval_is_true(&lisp, &mut eval, r#"(string>? "abd" "abc")"#));
assert!(eval_is_false(&lisp, &mut eval, r#"(string>? "abc" "abc")"#));
assert!(eval_is_true(&lisp, &mut eval, r#"(string<=? "abc" "abc")"#));
assert!(eval_is_true(&lisp, &mut eval, r#"(string<=? "abc" "abd")"#));
assert!(eval_is_true(&lisp, &mut eval, r#"(string>=? "abc" "abc")"#));
assert!(eval_is_true(&lisp, &mut eval, r#"(string>=? "abd" "abc")"#));
}
#[test]
fn test_string_append() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_string_matches(&lisp, &mut eval, r#"(string-append "hello" " " "world")"#, "hello world"));
assert!(eval_string_matches(&lisp, &mut eval, r#"(string-append "a" "b" "c")"#, "abc"));
assert!(eval_string_matches(&lisp, &mut eval, r#"(string-append)"#, ""));
assert!(eval_string_matches(&lisp, &mut eval, r#"(string-append "solo")"#, "solo"));
}
#[test]
fn test_string_to_list() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, r#"(length (string->list "hello"))"#), 5);
assert_eq!(eval_to_char(&lisp, &mut eval, r#"(car (string->list "hello"))"#), 'h');
assert!(eval_string_matches(&lisp, &mut eval, r#"(list->string (list #\a #\b #\c))"#, "abc"));
assert!(eval_string_matches(&lisp, &mut eval, r"(list->string '())", ""));
}
#[test]
fn test_substring() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_string_matches(&lisp, &mut eval, r#"(substring "hello" 1 4)"#, "ell"));
assert!(eval_string_matches(&lisp, &mut eval, r#"(substring "hello" 0 5)"#, "hello"));
assert!(eval_string_matches(&lisp, &mut eval, r#"(substring "hello" 2 2)"#, "")); }
#[test]
fn test_string_copy() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_string_matches(&lisp, &mut eval, r#"(string-copy "hello")"#, "hello"));
assert!(eval_string_matches(&lisp, &mut eval, r#"(string-copy "")"#, ""));
}
#[test]
fn test_string_set() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str(r#"(define s (string-copy "hello"))"#).unwrap();
eval.eval_str(r#"(string-set! s 0 #\H)"#).unwrap();
assert!(eval_string_matches(&lisp, &mut eval, "s", "Hello"));
}
#[test]
fn test_string_equal_structural() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, r#"(equal? "hello" "hello")"#));
assert!(eval_is_false(&lisp, &mut eval, r#"(equal? "hello" "world")"#));
assert!(eval_is_true(&lisp, &mut eval, r#"(equal? "" "")"#));
assert!(eval_is_false(&lisp, &mut eval, r#"(equal? "hi" "hello")"#));
}
#[test]
fn test_string_interning_eq() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval,
r#"(let ((s1 "hello") (s2 "hello")) (eq? s1 s2))"#));
assert!(eval_is_false(&lisp, &mut eval,
r#"(let ((s1 "hello") (s2 "world")) (eq? s1 s2))"#));
}
#[test]
fn test_string_mutation_isolation() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, r#"
(let ((s1 "test") (s2 "test"))
(string-set! s1 0 #\T)
(and (equal? s1 "Test")
(equal? s2 "test")))
"#));
}
#[test]
fn test_string_cow_breaks_eq() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_false(&lisp, &mut eval, r#"
(let ((s1 "test") (s2 "test"))
(string-set! s1 0 #\T)
(eq? s1 s2))
"#));
}
#[test]
fn test_string_direct_ref_shares_mutation() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, r#"
(let* ((s1 "shared") (s2 s1))
(string-set! s1 0 #\S)
(and (equal? s2 "Shared")
(eq? s1 s2)))
"#));
}
#[test]
fn test_string_nested_interning_cow() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, r#"
(let ((s1 "nested"))
(let ((s2 "nested"))
(string-set! s1 0 #\N)
(let ((s3 "nested"))
(string-set! s3 1 #\E)
(and (equal? s1 "Nested")
(equal? s2 "nested")
(equal? s3 "nEsted")))))
"#));
}
#[test]
fn test_number_to_string() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_string_matches(&lisp, &mut eval, r#"(number->string 42)"#, "42"));
assert!(eval_string_matches(&lisp, &mut eval, r#"(number->string -10)"#, "-10"));
assert!(eval_string_matches(&lisp, &mut eval, r#"(number->string 0)"#, "0"));
}
#[test]
fn test_string_to_number() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, r#"(string->number "123")"#), 123);
assert_eq!(eval_to_num(&lisp, &mut eval, r#"(string->number "-42")"#), -42);
assert!(eval_is_false(&lisp, &mut eval, r#"(string->number "abc")"#));
assert!(eval_is_false(&lisp, &mut eval, r#"(string->number "")"#));
}
#[test]
fn test_char_alphabetic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, r"(char-alphabetic? #\a)"));
assert!(eval_is_true(&lisp, &mut eval, r"(char-alphabetic? #\Z)"));
assert!(eval_is_false(&lisp, &mut eval, r"(char-alphabetic? #\0)"));
assert!(eval_is_false(&lisp, &mut eval, r"(char-alphabetic? #\space)"));
}
#[test]
fn test_char_numeric() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, r"(char-numeric? #\0)"));
assert!(eval_is_true(&lisp, &mut eval, r"(char-numeric? #\9)"));
assert!(eval_is_false(&lisp, &mut eval, r"(char-numeric? #\a)"));
assert!(eval_is_false(&lisp, &mut eval, r"(char-numeric? #\space)"));
}
#[test]
fn test_char_whitespace() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, r"(char-whitespace? #\space)"));
assert!(eval_is_true(&lisp, &mut eval, r"(char-whitespace? #\tab)"));
assert!(eval_is_true(&lisp, &mut eval, r"(char-whitespace? #\newline)"));
assert!(eval_is_false(&lisp, &mut eval, r"(char-whitespace? #\a)"));
}
#[test]
fn test_char_upper_lower_case() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, r"(char-upper-case? #\A)"));
assert!(eval_is_true(&lisp, &mut eval, r"(char-upper-case? #\Z)"));
assert!(eval_is_false(&lisp, &mut eval, r"(char-upper-case? #\a)"));
assert!(eval_is_true(&lisp, &mut eval, r"(char-lower-case? #\a)"));
assert!(eval_is_true(&lisp, &mut eval, r"(char-lower-case? #\z)"));
assert!(eval_is_false(&lisp, &mut eval, r"(char-lower-case? #\A)"));
}
#[test]
fn test_digit_value() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, r"(digit-value #\0)"), 0);
assert_eq!(eval_to_num(&lisp, &mut eval, r"(digit-value #\5)"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, r"(digit-value #\9)"), 9);
assert!(eval_is_false(&lisp, &mut eval, r"(digit-value #\a)"));
}
#[test]
fn test_char_ci_comparisons() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, r"(char-ci=? #\a #\A)"));
assert!(eval_is_true(&lisp, &mut eval, r"(char-ci=? #\Z #\z)"));
assert!(eval_is_true(&lisp, &mut eval, r"(char-ci<? #\a #\B)"));
assert!(eval_is_true(&lisp, &mut eval, r"(char-ci>? #\B #\a)"));
}
#[test]
fn test_string_case_conversion() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_string_matches(&lisp, &mut eval, r#"(string-upcase "hello")"#, "HELLO"));
assert!(eval_string_matches(&lisp, &mut eval, r#"(string-downcase "HELLO")"#, "hello"));
assert!(eval_string_matches(&lisp, &mut eval, r#"(string-downcase "HeLLo")"#, "hello"));
}
#[test]
fn test_string_ci_equals() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, r#"(string-ci=? "hello" "HELLO")"#));
assert!(eval_is_true(&lisp, &mut eval, r#"(string-ci=? "ABC" "abc")"#));
assert!(eval_is_false(&lisp, &mut eval, r#"(string-ci=? "abc" "abd")"#));
}
#[test]
fn test_iota_functions() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(length (iota1 5))"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (iota1 5))"), 0);
assert_eq!(eval_to_num(&lisp, &mut eval, "(last (iota1 5))"), 4);
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (iota2 5 10))"), 10);
assert_eq!(eval_to_num(&lisp, &mut eval, "(last (iota2 5 10))"), 14);
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (iota3 5 0 2))"), 0);
assert_eq!(eval_to_num(&lisp, &mut eval, "(second (iota3 5 0 2))"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(last (iota3 5 0 2))"), 8);
}
#[test]
fn test_list_tabulate() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (list-tabulate 5 (lambda (x) x)))"), 0);
assert_eq!(eval_to_num(&lisp, &mut eval, "(last (list-tabulate 5 (lambda (x) x)))"), 4);
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (list-tabulate 5 square))"), 0);
assert_eq!(eval_to_num(&lisp, &mut eval, "(second (list-tabulate 5 square))"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(third (list-tabulate 5 square))"), 4);
}
#[test]
fn test_list_accessors() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define lst '(1 2 3 4 5 6 7 8 9 10))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(first lst)"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(second lst)"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(third lst)"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(fourth lst)"), 4);
assert_eq!(eval_to_num(&lisp, &mut eval, "(fifth lst)"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(sixth lst)"), 6);
assert_eq!(eval_to_num(&lisp, &mut eval, "(seventh lst)"), 7);
assert_eq!(eval_to_num(&lisp, &mut eval, "(eighth lst)"), 8);
assert_eq!(eval_to_num(&lisp, &mut eval, "(ninth lst)"), 9);
assert_eq!(eval_to_num(&lisp, &mut eval, "(tenth lst)"), 10);
}
#[test]
fn test_list_utilities() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(length (concatenate '((1 2) (3 4) (5 6))))"), 6);
assert_eq!(eval_to_num(&lisp, &mut eval, "(length (flatten '(1 (2 3) ((4 5) 6))))"), 6);
assert_eq!(eval_to_num(&lisp, &mut eval, "(first (flatten '(1 (2 3))))"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(count positive? '(-2 -1 0 1 2))"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(count even? '(1 2 3 4 5 6))"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(sum '(1 2 3 4 5))"), 15);
assert_eq!(eval_to_num(&lisp, &mut eval, "(product '(1 2 3 4 5))"), 120);
assert_eq!(eval_to_num(&lisp, &mut eval, "(average '(2 4 6 8))"), 5);
}
#[test]
fn test_string_utilities() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, r#"(string-null? "")"#));
assert!(eval_is_false(&lisp, &mut eval, r#"(string-null? "x")"#));
assert!(eval_string_matches(&lisp, &mut eval, r#"(string-reverse "hello")"#, "olleh"));
assert_eq!(eval_to_num(&lisp, &mut eval, r#"(string-contains "hello world" "wor")"#), 6);
assert!(eval_is_false(&lisp, &mut eval, r#"(string-contains "hello" "xyz")"#));
assert!(eval_string_matches(&lisp, &mut eval, r#"(string-join '("a" "b" "c") "-")"#, "a-b-c"));
assert!(eval_string_matches(&lisp, &mut eval, r#"(string-trim " hello ")"#, "hello"));
}
#[test]
fn test_string_map_and_for_each() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_string_matches(&lisp, &mut eval, r#"(string-map char-upcase "hello")"#, "HELLO"));
}
#[test]
fn test_string_split() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, r#"(length (string-split "a-b-c" #\-))"#), 3);
assert!(eval_string_matches(&lisp, &mut eval, r#"(car (string-split "a-b-c" #\-))"#, "a"));
assert!(eval_string_matches(&lisp, &mut eval, r#"(second (string-split "a-b-c" #\-))"#, "b"));
assert!(eval_string_matches(&lisp, &mut eval, r#"(third (string-split "a-b-c" #\-))"#, "c"));
}
#[test]
fn test_doc_truthiness_semantics() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(if '() 1 2)"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(if 0 1 2)"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(if #f 1 2)"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(if #t 1 2)"), 1);
}
#[test]
fn test_doc_arithmetic_builtins() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(+ 1 2 3 4)"), 10);
assert_eq!(eval_to_num(&lisp, &mut eval, "(- 10 3)"), 7);
assert_eq!(eval_to_num(&lisp, &mut eval, "(* 2 3 4)"), 24);
assert_eq!(eval_to_num(&lisp, &mut eval, "(/ 100 5)"), 20);
assert_eq!(eval_to_num(&lisp, &mut eval, "(modulo 17 5)"), 2);
}
#[test]
fn test_doc_comparison_builtins() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(< 1 2)"));
assert!(eval_is_true(&lisp, &mut eval, "(= 5 5)"));
assert!(eval_is_true(&lisp, &mut eval, "(> 3 1)"));
assert!(eval_is_true(&lisp, &mut eval, "(<= 1 1)"));
assert!(eval_is_true(&lisp, &mut eval, "(>= 5 5)"));
}
#[test]
fn test_doc_equality_builtins() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(eq? 'a 'a)"));
assert!(eval_is_true(&lisp, &mut eval, "(eqv? 5 5)"));
assert!(eval_is_true(&lisp, &mut eval, "(equal? '(1 2) '(1 2))"));
}
#[test]
fn test_doc_list_operations() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(car '(1 2 3))"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (cdr '(1 2 3)))"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (cons 1 '(2 3)))"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (list 1 2 3))"), 1);
}
#[test]
fn test_doc_predicates() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(null? '())"));
assert!(eval_is_false(&lisp, &mut eval, "(null? '(1))"));
assert!(eval_is_true(&lisp, &mut eval, "(pair? '(1 . 2))"));
assert!(eval_is_true(&lisp, &mut eval, "(number? 42)"));
assert!(eval_is_true(&lisp, &mut eval, "(symbol? 'foo)"));
assert!(eval_is_true(&lisp, &mut eval, "(procedure? car)"));
}
#[test]
fn test_doc_gc_operations() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(gc)").unwrap();
assert!(lisp.get(result).unwrap().is_cons());
let result = eval.eval_str("(gc-enabled?)").unwrap();
let val = lisp.get(result).unwrap();
assert!(val.is_true() || val.is_false());
eval.eval_str("(gc-disable)").unwrap();
assert!(eval_is_false(&lisp, &mut eval, "(gc-enabled?)"));
eval.eval_str("(gc-enable)").unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(gc-enabled?)"));
let result = eval.eval_str("(arena-stats)").unwrap();
assert!(lisp.get(result).unwrap().is_cons());
}
#[test]
fn test_doc_vector_operations() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define vec (make-vector 5 0))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-length vec)"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref vec 2)"), 0);
eval.eval_str("(vector-set! vec 2 42)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref vec 2)"), 42);
assert!(eval_is_true(&lisp, &mut eval, "(vector? vec)"));
assert!(eval_is_false(&lisp, &mut eval, "(vector? 42)"));
}
#[test]
fn test_doc_special_forms() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("'hello").unwrap();
assert!(lisp.symbol_matches(result, "hello").unwrap());
assert_eq!(eval_to_num(&lisp, &mut eval, "(if #t 1 2)"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(if #f 1 2)"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(cond (#f 1) (#t 2) (else 3))"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(case 2 ((1) 10) ((2) 20) (else 30))"), 20);
assert_eq!(eval_to_num(&lisp, &mut eval, "((lambda (x) (* x x)) 5)"), 25);
eval.eval_str("(define x 42)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "x"), 42);
eval.eval_str("(set! x 100)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "x"), 100);
assert_eq!(eval_to_num(&lisp, &mut eval, "(let ((a 1) (b 2)) (+ a b))"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(let* ((a 1) (b (+ a 1))) b)"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(letrec ((f (lambda (n) (if (= n 0) 1 (* n (f (- n 1))))))) (f 5))"), 120);
assert_eq!(eval_to_num(&lisp, &mut eval, "(letrec* ((a 1) (b (+ a 1))) b)"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(begin 1 2 3)"), 3);
assert!(eval_is_false(&lisp, &mut eval, "(and #t #f)"));
assert!(eval_is_true(&lisp, &mut eval, "(or #f #t)"));
eval.eval_str("(define when-test 0)").unwrap();
eval.eval_str("(when #t (set! when-test 1))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "when-test"), 1);
eval.eval_str("(define unless-test 0)").unwrap();
eval.eval_str("(unless #f (set! unless-test 1))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "unless-test"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(do ((i 0 (+ i 1)) (sum 0 (+ sum i))) ((= i 5) sum))"), 10);
assert_eq!(eval_to_num(&lisp, &mut eval, "(eval '(+ 1 2))"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(apply + '(1 2 3))"), 6);
let result = eval.eval_str("(values 1 2 3)").unwrap();
assert!(lisp.get(result).unwrap().is_cons());
}
#[test]
fn test_doc_mutation_operations() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define pair (cons 1 2))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(car pair)"), 1);
eval.eval_str("(set-car! pair 10)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(car pair)"), 10);
eval.eval_str("(set-cdr! pair 20)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(cdr pair)"), 20);
}
#[test]
fn test_doc_stdlib_higher_order() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (map (lambda (x) (* x x)) '(1 2 3 4 5)))"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (cdr (map (lambda (x) (* x x)) '(1 2 3 4 5))))"), 4);
assert_eq!(eval_to_num(&lisp, &mut eval, "(length (filter (lambda (x) (> x 0)) '(-1 2 -3 4)))"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(fold + 0 '(1 2 3 4 5))"), 15);
}
#[test]
fn test_doc_stdlib_list_utilities() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(length '(a b c))"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(length (append '(1 2) '(3 4)))"), 4);
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (reverse '(1 2 3)))"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(nth 2 '(10 20 30 40))"), 30);
assert_eq!(eval_to_num(&lisp, &mut eval, "(length (range 0 5))"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (range 0 5))"), 0);
assert_eq!(eval_to_num(&lisp, &mut eval, "(length (take 3 '(a b c d e)))"), 3);
}
#[test]
fn test_doc_stdlib_identity_constantly() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(identity 42)"), 42);
eval.eval_str("(define always-5 (constantly 5))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(always-5 100)"), 5);
}
#[test]
fn test_doc_string_operations() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, r#"(string-length "hello")"#), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, r#"(string-length (string-append "a" "b"))"#), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, r#"(string-length (substring "hello" 1 3))"#), 2);
}
#[test]
fn test_doc_character_operations() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(char->integer #\\A)"), 65);
assert!(eval_is_true(&lisp, &mut eval, "(char=? (integer->char 65) #\\A)"));
assert!(eval_is_true(&lisp, &mut eval, "(char=? (char-upcase #\\a) #\\A)"));
assert!(eval_is_true(&lisp, &mut eval, "(char=? (char-downcase #\\A) #\\a)"));
}
#[test]
fn test_doc_tco_no_stack_overflow() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (sum n acc) (if (= n 0) acc (sum (- n 1) (+ acc n))))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(sum 100 0)"), 5050);
}
#[test]
fn test_doc_quasiquote() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define x 5)").unwrap();
let result = eval.eval_str("(quasiquote (a b (unquote x)))").unwrap();
let third = lisp.car(lisp.cdr(lisp.cdr(result).unwrap()).unwrap()).unwrap();
assert_eq!(lisp.get(third).unwrap().as_number().unwrap(), 5);
}
#[test]
fn test_doc_numeric_operations() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(abs -5)"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(abs 5)"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(max 1 5 3)"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(min 1 5 3)"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(expt 2 10)"), 1024);
assert_eq!(eval_to_num(&lisp, &mut eval, "(square 5)"), 25);
assert_eq!(eval_to_num(&lisp, &mut eval, "(gcd 12 8)"), 4);
assert_eq!(eval_to_num(&lisp, &mut eval, "(lcm 4 6)"), 12);
assert_eq!(eval_to_num(&lisp, &mut eval, "(quotient 17 5)"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(remainder 17 5)"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(floor 3)"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(ceiling 3)"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(truncate 3)"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(round 3)"), 3);
}
#[test]
fn test_doc_number_predicates() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(zero? 0)"));
assert!(eval_is_false(&lisp, &mut eval, "(zero? 1)"));
assert!(eval_is_true(&lisp, &mut eval, "(positive? 5)"));
assert!(eval_is_true(&lisp, &mut eval, "(negative? -5)"));
assert!(eval_is_true(&lisp, &mut eval, "(odd? 5)"));
assert!(eval_is_true(&lisp, &mut eval, "(even? 4)"));
assert!(eval_is_true(&lisp, &mut eval, "(integer? 5)"));
assert!(eval_is_true(&lisp, &mut eval, "(exact? 5)"));
assert!(eval_is_false(&lisp, &mut eval, "(inexact? 5)")); }
#[test]
fn test_vector_predicate() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(vector? (vector 1 2 3))"));
assert!(eval_is_true(&lisp, &mut eval, "(vector? (make-vector 5))"));
assert!(eval_is_false(&lisp, &mut eval, "(vector? '(1 2 3))"));
assert!(eval_is_false(&lisp, &mut eval, "(vector? 42)"));
assert!(eval_is_false(&lisp, &mut eval, "(vector? \"hello\")"));
}
#[test]
fn test_make_vector() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-length (make-vector 5))"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref (make-vector 3 42) 0)"), 42);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref (make-vector 3 42) 1)"), 42);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref (make-vector 3 42) 2)"), 42);
}
#[test]
fn test_vector_constructor() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-length (vector))"), 0);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-length (vector 1 2 3))"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref (vector 10 20 30) 0)"), 10);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref (vector 10 20 30) 1)"), 20);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref (vector 10 20 30) 2)"), 30);
}
#[test]
fn test_vector_length() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-length (vector))"), 0);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-length (vector 'a))"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-length (vector 'a 'b 'c 'd 'e))"), 5);
}
#[test]
fn test_vector_ref() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref (vector 1 1 2 3 5 8 13 21) 5)"), 8);
}
#[test]
fn test_vector_set() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
"(let ((vec (vector 0 1 2)))
(vector-set! vec 1 42)
(vector-ref vec 1))"), 42);
}
#[test]
fn test_vector_to_list() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (vector->list (vector 1 2 3)))"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(cadr (vector->list (vector 1 2 3)))"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(caddr (vector->list (vector 1 2 3)))"), 3);
assert!(eval_is_true(&lisp, &mut eval, "(null? (vector->list (vector)))"));
}
#[test]
fn test_list_to_vector() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref (list->vector '(1 2 3)) 0)"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref (list->vector '(1 2 3)) 1)"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref (list->vector '(1 2 3)) 2)"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-length (list->vector '(1 2 3)))"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-length (list->vector '()))"), 0);
}
#[test]
fn test_vector_fill() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
"(let ((vec (vector 1 2 3)))
(vector-fill! vec 0)
(+ (vector-ref vec 0) (vector-ref vec 1) (vector-ref vec 2)))"), 0);
}
#[test]
fn test_vector_copy() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
"(let ((vec1 (vector 1 2 3)))
(let ((vec2 (vector-copy vec1)))
(vector-set! vec2 0 100)
(+ (vector-ref vec1 0) (vector-ref vec2 0))))"), 101);
}
#[test]
fn test_vector_literal() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(vector? #())"));
assert!(eval_is_true(&lisp, &mut eval, "(vector? #(1 2 3))"));
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-length #())"), 0);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-length #(1 2 3))"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref #(10 20 30) 1)"), 20);
}
#[test]
fn test_vector_with_mixed_types() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref (vector 0 '(2 2 2 2) \"Anna\") 0)"), 0);
assert!(eval_is_true(&lisp, &mut eval, "(pair? (vector-ref (vector 0 '(2 2 2 2) \"Anna\") 1))"));
assert!(eval_is_true(&lisp, &mut eval, "(string? (vector-ref (vector 0 '(2 2 2 2) \"Anna\") 2))"));
}
#[test]
fn test_vector_nested_literal() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(vector? (vector-ref #(1 #(2 3) 4) 1))"));
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref (vector-ref #(1 #(2 3) 4) 1) 0)"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref (vector-ref #(1 #(2 3) 4) 1) 1)"), 3);
}
#[test]
fn test_list_to_vector_improper_list_error() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(list->vector 42)");
assert!(result.is_err());
let result = eval.eval_str("(list->vector (cons 1 2))");
assert!(result.is_err());
}
#[test]
fn test_vector_make_vector_negative_length() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(make-vector -1)");
assert!(result.is_err());
}
#[test]
fn test_nested_stdlib_calls() {
let lisp: Lisp<30000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(+ 1 (abs -2))").unwrap();
let val = lisp.get(result).unwrap().as_number().unwrap();
assert_eq!(val, 3);
let result = eval.eval_str("(+ (abs -2) (abs -3))").unwrap();
let val = lisp.get(result).unwrap().as_number().unwrap();
assert_eq!(val, 5);
let result = eval.eval_str("(+ 1 (* 2 (abs -2)))").unwrap();
let val = lisp.get(result).unwrap().as_number().unwrap();
assert_eq!(val, 5);
}
#[test]
fn test_size_check() {
use std::mem::{size_of, align_of};
use grift_eval::TrampolineState;
use grift_parser::{Builtin, StdLib};
println!("\n╔════════════════════════════════════════════════════════════╗");
println!("║ TYPE SIZE ANALYSIS ║");
println!("╠════════════════════════════════════════════════════════════╣");
println!("║ Core Arena Types: ║");
println!("║ ArenaIndex: {:>3} bytes (align: {:>2}) ║",
size_of::<ArenaIndex>(), align_of::<ArenaIndex>());
println!("║ Value: {:>3} bytes (align: {:>2}) ║",
size_of::<Value>(), align_of::<Value>());
println!("╠════════════════════════════════════════════════════════════╣");
println!("║ Continuation Types: ║");
println!("║ (Continuations are now arena-based, not stack-based) ║");
println!("║ TrampolineState: {:>3} bytes (align: {:>2}) ║",
size_of::<TrampolineState>(), align_of::<TrampolineState>());
println!("╠════════════════════════════════════════════════════════════╣");
println!("║ Function Types: ║");
println!("║ Builtin: {:>3} bytes (align: {:>2}, {} variants) ║",
size_of::<Builtin>(), align_of::<Builtin>(), Builtin::ALL.len());
println!("║ StdLib: {:>3} bytes (align: {:>2}, {} variants) ║",
size_of::<StdLib>(), align_of::<StdLib>(), StdLib::ALL.len());
println!("╠════════════════════════════════════════════════════════════╣");
println!("║ Rust Primitives (for reference): ║");
println!("║ usize: {:>3} bytes ║", size_of::<usize>());
println!("║ isize: {:>3} bytes ║", size_of::<isize>());
println!("║ char: {:>3} bytes ║", size_of::<char>());
println!("║ bool: {:>3} bytes ║", size_of::<bool>());
println!("╠════════════════════════════════════════════════════════════╣");
println!("║ Analysis: ║");
let value_slots = size_of::<Value>() / size_of::<usize>();
println!("║ Value = {} usizes = discriminant + {} usizes payload ║",
value_slots, value_slots - 1);
let values_per_cache_line = 64 / size_of::<Value>();
println!("║ Values per 64-byte cache line: {} ║", values_per_cache_line);
println!("╚════════════════════════════════════════════════════════════╝\n");
assert!(size_of::<Value>() <= 32, "Value enum grew beyond 32 bytes!");
assert!(size_of::<ArenaIndex>() == 8, "ArenaIndex should be exactly 8 bytes");
}
#[test]
fn test_quasiquote_reader_syntax_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("`42").unwrap();
assert_eq!(lisp.get(result).unwrap().as_number().unwrap(), 42);
let result = eval.eval_str("`(a b c)").unwrap();
assert!(lisp.get(result).unwrap().is_cons());
}
#[test]
fn test_quasiquote_reader_with_unquote() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define x 42)").unwrap();
let result = eval.eval_str("`(a b ,x)").unwrap();
let third = lisp.car(lisp.cdr(lisp.cdr(result).unwrap()).unwrap()).unwrap();
assert_eq!(lisp.get(third).unwrap().as_number().unwrap(), 42);
}
#[test]
fn test_quasiquote_reader_with_expression_unquote() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define x 5)").unwrap();
let result = eval.eval_str("`(1 ,(+ x 3) 10)").unwrap();
let second = lisp.car(lisp.cdr(result).unwrap()).unwrap();
assert_eq!(lisp.get(second).unwrap().as_number().unwrap(), 8);
}
#[test]
fn test_quasiquote_reader_multiple_unquotes() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define a 1)").unwrap();
eval.eval_str("(define b 2)").unwrap();
eval.eval_str("(define c 3)").unwrap();
let result = eval.eval_str("`(,a ,b ,c)").unwrap();
let first = lisp.car(result).unwrap();
let second = lisp.car(lisp.cdr(result).unwrap()).unwrap();
let third = lisp.car(lisp.cdr(lisp.cdr(result).unwrap()).unwrap()).unwrap();
assert_eq!(lisp.get(first).unwrap().as_number().unwrap(), 1);
assert_eq!(lisp.get(second).unwrap().as_number().unwrap(), 2);
assert_eq!(lisp.get(third).unwrap().as_number().unwrap(), 3);
}
#[test]
fn test_unquote_splicing_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define lst '(2 3 4))").unwrap();
let result = eval.eval_str("`(1 ,@lst 5)").unwrap();
let n1 = lisp.car(result).unwrap();
let n2 = lisp.car(lisp.cdr(result).unwrap()).unwrap();
let n3 = lisp.car(lisp.cdr(lisp.cdr(result).unwrap()).unwrap()).unwrap();
let n4 = lisp.car(lisp.cdr(lisp.cdr(lisp.cdr(result).unwrap()).unwrap()).unwrap()).unwrap();
let n5 = lisp.car(lisp.cdr(lisp.cdr(lisp.cdr(lisp.cdr(result).unwrap()).unwrap()).unwrap()).unwrap()).unwrap();
assert_eq!(lisp.get(n1).unwrap().as_number().unwrap(), 1);
assert_eq!(lisp.get(n2).unwrap().as_number().unwrap(), 2);
assert_eq!(lisp.get(n3).unwrap().as_number().unwrap(), 3);
assert_eq!(lisp.get(n4).unwrap().as_number().unwrap(), 4);
assert_eq!(lisp.get(n5).unwrap().as_number().unwrap(), 5);
}
#[test]
fn test_unquote_splicing_at_start() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define xs '(a b))").unwrap();
let result = eval.eval_str("`(,@xs c d)").unwrap();
let mut count = 0;
let mut current = result;
loop {
match lisp.get(current).unwrap() {
Value::Nil => break,
Value::Cons { cdr, .. } => {
count += 1;
current = cdr;
}
_ => panic!("Expected list"),
}
}
assert_eq!(count, 4);
}
#[test]
fn test_unquote_splicing_empty_list() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define empty '())").unwrap();
let result = eval.eval_str("`(1 ,@empty 2)").unwrap();
let n1 = lisp.car(result).unwrap();
let n2 = lisp.car(lisp.cdr(result).unwrap()).unwrap();
let tail = lisp.cdr(lisp.cdr(result).unwrap()).unwrap();
assert_eq!(lisp.get(n1).unwrap().as_number().unwrap(), 1);
assert_eq!(lisp.get(n2).unwrap().as_number().unwrap(), 2);
assert!(lisp.get(tail).unwrap().is_nil());
}
#[test]
fn test_unquote_splicing_with_computed_list() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("`(a ,@(list 1 2 3) b)").unwrap();
let mut count = 0;
let mut current = result;
loop {
match lisp.get(current).unwrap() {
Value::Nil => break,
Value::Cons { cdr, .. } => {
count += 1;
current = cdr;
}
_ => panic!("Expected list"),
}
}
assert_eq!(count, 5);
}
#[test]
fn test_quasiquote_nested_lists() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define x 10)").unwrap();
let result = eval.eval_str("`((a ,x) (b ,(+ x 1)))").unwrap();
let first_list = lisp.car(result).unwrap();
let first_second = lisp.car(lisp.cdr(first_list).unwrap()).unwrap();
assert_eq!(lisp.get(first_second).unwrap().as_number().unwrap(), 10);
let second_list = lisp.car(lisp.cdr(result).unwrap()).unwrap();
let second_second = lisp.car(lisp.cdr(second_list).unwrap()).unwrap();
assert_eq!(lisp.get(second_second).unwrap().as_number().unwrap(), 11);
}
#[test]
fn test_quasiquote_deeply_nested_unquote() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define val 99)").unwrap();
let result = eval.eval_str("`(a (b (c ,val)))").unwrap();
let inner1 = lisp.car(lisp.cdr(result).unwrap()).unwrap(); let inner2 = lisp.car(lisp.cdr(inner1).unwrap()).unwrap(); let inner_val = lisp.car(lisp.cdr(inner2).unwrap()).unwrap();
assert_eq!(lisp.get(inner_val).unwrap().as_number().unwrap(), 99);
}
#[test]
fn test_quasiquote_mixed_quote_and_unquote() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define x 5)").unwrap();
let result = eval.eval_str("`(a 'b ,x)").unwrap();
let third = lisp.car(lisp.cdr(lisp.cdr(result).unwrap()).unwrap()).unwrap();
assert_eq!(lisp.get(third).unwrap().as_number().unwrap(), 5);
}
#[test]
fn test_quasiquote_equivalence_to_long_form() {
let lisp: Lisp<30000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define n 7)").unwrap();
let short = eval.eval_str("`(a ,n b)").unwrap();
let long = eval.eval_str("(quasiquote (a (unquote n) b))").unwrap();
let short_second = lisp.car(lisp.cdr(short).unwrap()).unwrap();
let long_second = lisp.car(lisp.cdr(long).unwrap()).unwrap();
assert_eq!(lisp.get(short_second).unwrap().as_number().unwrap(), 7);
assert_eq!(lisp.get(long_second).unwrap().as_number().unwrap(), 7);
}
#[test]
fn test_quasiquote_in_function_body() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (make-pair a b) `(,a . ,b))").unwrap();
let result = eval.eval_str("(make-pair 1 2)").unwrap();
let car = lisp.car(result).unwrap();
let cdr = lisp.cdr(result).unwrap();
assert_eq!(lisp.get(car).unwrap().as_number().unwrap(), 1);
assert_eq!(lisp.get(cdr).unwrap().as_number().unwrap(), 2);
}
#[test]
fn test_quasiquote_function_building_list() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (wrap-in-if test body) `(if ,test ,body #f))").unwrap();
let result = eval.eval_str("(wrap-in-if '#t '(+ 1 2))").unwrap();
let first = lisp.car(result).unwrap();
assert!(lisp.symbol_matches(first, "if").unwrap());
}
#[test]
fn test_nested_quasiquote_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define x 10)").unwrap();
let result = eval.eval_str("``(a b c)").unwrap();
let car = lisp.car(result).unwrap();
assert!(lisp.symbol_matches(car, "quasiquote").unwrap());
}
#[test]
fn test_multiple_splices() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define xs '(1 2))").unwrap();
eval.eval_str("(define ys '(3 4))").unwrap();
let result = eval.eval_str("`(,@xs ,@ys)").unwrap();
let mut count = 0;
let mut current = result;
loop {
match lisp.get(current).unwrap() {
Value::Nil => break,
Value::Cons { cdr, .. } => {
count += 1;
current = cdr;
}
_ => panic!("Expected list"),
}
}
assert_eq!(count, 4);
}
#[test]
fn test_quasiquote_preserves_structure() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let quoted = eval.eval_str("'(a (b c) d)").unwrap();
let quasiquoted = eval.eval_str("`(a (b c) d)").unwrap();
fn count_elements<const N: usize>(lisp: &Lisp<N>, idx: ArenaIndex) -> usize {
let mut count = 0;
let mut current = idx;
loop {
match lisp.get(current).unwrap() {
Value::Nil => return count,
Value::Cons { cdr, .. } => {
count += 1;
current = cdr;
}
_ => return count,
}
}
}
assert_eq!(count_elements(&lisp, quoted), count_elements(&lisp, quasiquoted));
}
#[test]
fn test_quasiquote_with_lambda() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define body '(+ x 1))").unwrap();
let result = eval.eval_str("`(lambda (x) ,body)").unwrap();
let first = lisp.car(result).unwrap();
assert!(lisp.symbol_matches(first, "lambda").unwrap());
}
#[test]
fn test_quasiquote_code_generation() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define op '+)").unwrap();
eval.eval_str("(define a 10)").unwrap();
eval.eval_str("(define b 20)").unwrap();
let result = eval.eval_str("(eval `(,op ,a ,b))").unwrap();
assert_eq!(lisp.get(result).unwrap().as_number().unwrap(), 30);
}
#[test]
fn test_quasiquote_symbols_stay_symbols() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("`(define x 5)").unwrap();
let first = lisp.car(result).unwrap();
assert!(lisp.get(first).unwrap().is_symbol());
assert!(lisp.symbol_matches(first, "define").unwrap());
}
#[test]
fn test_quasiquote_with_vectors() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define x 42)").unwrap();
let result = eval.eval_str("`(#(1 2 3) ,x)").unwrap();
let first = lisp.car(result).unwrap();
assert!(lisp.get(first).unwrap().is_array());
let second = lisp.car(lisp.cdr(result).unwrap()).unwrap();
assert_eq!(lisp.get(second).unwrap().as_number().unwrap(), 42);
}
#[test]
fn test_quasiquote_dotted_pairs() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define x 1)").unwrap();
eval.eval_str("(define y 2)").unwrap();
let result = eval.eval_str("`(,x . ,y)").unwrap();
let car = lisp.car(result).unwrap();
let cdr = lisp.cdr(result).unwrap();
assert_eq!(lisp.get(car).unwrap().as_number().unwrap(), 1);
assert_eq!(lisp.get(cdr).unwrap().as_number().unwrap(), 2);
}
#[test]
fn test_quasiquote_splice_only_in_list_context() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define items '(a b c))").unwrap();
let result = eval.eval_str("`(,@items)").unwrap();
let first = lisp.car(result).unwrap();
assert!(lisp.symbol_matches(first, "a").unwrap());
}
#[test]
fn test_quasiquote_with_conditionals() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("`(if #t ,(+ 1 2) ,(+ 3 4))").unwrap();
let then_val = lisp.car(lisp.cdr(lisp.cdr(result).unwrap()).unwrap()).unwrap();
let else_val = lisp.car(lisp.cdr(lisp.cdr(lisp.cdr(result).unwrap()).unwrap()).unwrap()).unwrap();
assert_eq!(lisp.get(then_val).unwrap().as_number().unwrap(), 3);
assert_eq!(lisp.get(else_val).unwrap().as_number().unwrap(), 7);
}
#[test]
fn test_doc_reserved_slots() {
let lisp: Lisp<1000> = Lisp::new();
let nil = lisp.nil().unwrap();
assert!(lisp.get(nil).unwrap().is_nil());
let true_val = lisp.true_val().unwrap();
assert!(lisp.get(true_val).unwrap().is_true());
let false_val = lisp.false_val().unwrap();
assert!(lisp.get(false_val).unwrap().is_false());
let foo1 = lisp.symbol("foo").unwrap();
let foo2 = lisp.symbol("foo").unwrap();
assert_eq!(foo1, foo2, "Interned symbols should have same index");
}
#[test]
fn test_doc_only_false_is_false() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(if #f 1 2)"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(if '() 1 2)"), 1, "Empty list should be truthy");
assert_eq!(eval_to_num(&lisp, &mut eval, "(if 0 1 2)"), 1, "Zero should be truthy");
assert_eq!(eval_to_num(&lisp, &mut eval, "(if \"\" 1 2)"), 1, "Empty string should be truthy");
assert_eq!(eval_to_num(&lisp, &mut eval, "(if #t 1 2)"), 1);
}
#[test]
fn test_doc_tail_call_optimization() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (sum n acc) (if (= n 0) acc (sum (- n 1) (+ acc n))))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(sum 100 0)"), 5050);
}
#[test]
fn test_doc_gc_control() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(arena-stats)").unwrap();
assert!(matches!(lisp.get(result).unwrap(), Value::Cons { .. }));
let result = eval.eval_str("(gc-enabled?)").unwrap();
assert!(lisp.get(result).unwrap().is_boolean());
eval.eval_str("(gc-disable)").unwrap();
assert!(eval_is_false(&lisp, &mut eval, "(gc-enabled?)"));
eval.eval_str("(gc-enable)").unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(gc-enabled?)"));
let result = eval.eval_str("(gc)").unwrap();
assert!(matches!(lisp.get(result).unwrap(), Value::Cons { .. }));
}
#[test]
fn test_doc_rounding_operations() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(floor 42)"), 42);
assert_eq!(eval_to_num(&lisp, &mut eval, "(ceiling 42)"), 42);
assert_eq!(eval_to_num(&lisp, &mut eval, "(truncate 42)"), 42);
assert_eq!(eval_to_num(&lisp, &mut eval, "(round 42)"), 42);
assert_eq!(eval_to_num(&lisp, &mut eval, "(floor -7)"), -7);
assert_eq!(eval_to_num(&lisp, &mut eval, "(ceiling -7)"), -7);
}
#[test]
fn test_doc_lexical_closures() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (make-adder n) (lambda (x) (+ x n)))").unwrap();
eval.eval_str("(define add5 (make-adder 5))").unwrap();
eval.eval_str("(define add10 (make-adder 10))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(add5 3)"), 8);
assert_eq!(eval_to_num(&lisp, &mut eval, "(add10 3)"), 13);
}
#[test]
fn test_doc_strict_evaluation() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define count 0)").unwrap();
eval.eval_str("(define lst (cons (begin (set! count 1) 'a) '()))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "count"), 1);
}
#[test]
fn test_doc_car_cdr_compositions() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define nested '((1 2) (3 4) (5 6)))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (cadr nested))"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (caddr nested))"), 5);
let _result = eval.eval_str("(cddr nested)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (car (cddr nested)))"), 5);
}
#[test]
fn test_nested_ellipsis_bug() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define-syntax nest-test (lambda (x) (syntax-case x () ((nest-test ((a) ...)) (syntax (quote (a ...))))))))").unwrap();
let result = eval.eval_str("(nest-test ((1) (2) (3)))").unwrap();
let first = lisp.car(result).unwrap();
assert_eq!(lisp.get(first).unwrap().as_number(), Some(1), "First element should be 1");
let second = lisp.car(lisp.cdr(result).unwrap()).unwrap();
assert_eq!(lisp.get(second).unwrap().as_number(), Some(2),
"Second element should be 2, not (2) - nested ellipsis bug");
}
#[test]
fn test_check_pattern_binding_value() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define-syntax test3 (lambda (x) (syntax-case x () ((test3 ((a) ...)) (syntax (quote (a ...))))))))").unwrap();
let result = eval.eval_str("(test3 ((1) (2) (3)))").unwrap();
eprintln!("Result for ((1) (2) (3)):");
let mut curr = result;
let mut idx = 0;
while let Value::Cons { .. } = lisp.get(curr).unwrap() {
let elem = lisp.car(curr).unwrap();
eprintln!(" [{}] = {:?}", idx, lisp.get(elem));
curr = lisp.cdr(curr).unwrap();
idx += 1;
}
let v1 = lisp.car(result).unwrap();
assert_eq!(lisp.get(v1).unwrap().as_number(), Some(1), "First should be 1");
let v2 = lisp.car(lisp.cdr(result).unwrap()).unwrap();
match lisp.get(v2).unwrap() {
Value::Number(n) => assert_eq!(n, 2, "Second should be 2"),
Value::Cons { .. } => {
let inner = lisp.car(v2).unwrap();
eprintln!("BUG: Second element is ({:?}) instead of just the number", lisp.get(inner).unwrap().as_number());
panic!("Nested ellipsis bug: second element should be 2, not (2)");
}
other => panic!("Unexpected: {:?}", other),
}
}
#[test]
fn test_symbol_interning() {
let lisp: Lisp<20000> = Lisp::new();
let a1 = lisp.symbol("a").unwrap();
let a2 = lisp.symbol("a").unwrap();
let a3 = lisp.symbol("a").unwrap();
eprintln!("a1 = {:?}", a1);
eprintln!("a2 = {:?}", a2);
eprintln!("a3 = {:?}", a3);
assert_eq!(a1, a2, "Symbols should be interned to same index");
assert_eq!(a2, a3, "Symbols should be interned to same index");
assert!(lisp.symbol_eq(a1, a2).unwrap());
assert!(lisp.symbol_eq(a2, a3).unwrap());
}
#[test]
fn test_nested_ellipsis_single_var() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define-syntax extract (lambda (x) (syntax-case x () ((extract ((a) ...)) (syntax (quote (a ...))))))))").unwrap();
let result = eval.eval_str("(extract ((1)))").unwrap();
let v1 = lisp.car(result).unwrap();
assert_eq!(lisp.get(v1).unwrap().as_number(), Some(1));
let result = eval.eval_str("(extract ((1) (2)))").unwrap();
let v1 = lisp.car(result).unwrap();
let v2 = lisp.car(lisp.cdr(result).unwrap()).unwrap();
assert_eq!(lisp.get(v1).unwrap().as_number(), Some(1));
assert_eq!(lisp.get(v2).unwrap().as_number(), Some(2));
let result = eval.eval_str("(extract ((1) (2) (3)))").unwrap();
let v1 = lisp.car(result).unwrap();
let v2 = lisp.car(lisp.cdr(result).unwrap()).unwrap();
let v3 = lisp.car(lisp.cdr(lisp.cdr(result).unwrap()).unwrap()).unwrap();
assert_eq!(lisp.get(v1).unwrap().as_number(), Some(1));
assert_eq!(lisp.get(v2).unwrap().as_number(), Some(2));
assert_eq!(lisp.get(v3).unwrap().as_number(), Some(3));
}
#[test]
fn test_nested_ellipsis_multiple_vars() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define-syntax pair-extract (lambda (x) (syntax-case x () ((pair-extract ((a b) ...)) (syntax (quote ((a ...) (b ...))))))))").unwrap();
let result = eval.eval_str("(pair-extract ((1 2) (3 4)))").unwrap();
let first = lisp.car(result).unwrap();
let a1 = lisp.car(first).unwrap();
let a2 = lisp.car(lisp.cdr(first).unwrap()).unwrap();
assert_eq!(lisp.get(a1).unwrap().as_number(), Some(1));
assert_eq!(lisp.get(a2).unwrap().as_number(), Some(3));
let second = lisp.car(lisp.cdr(result).unwrap()).unwrap();
let b1 = lisp.car(second).unwrap();
let b2 = lisp.car(lisp.cdr(second).unwrap()).unwrap();
assert_eq!(lisp.get(b1).unwrap().as_number(), Some(2));
assert_eq!(lisp.get(b2).unwrap().as_number(), Some(4));
}
#[test]
fn test_let_style_bindings() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define-syntax extract-bindings (lambda (x) (syntax-case x () ((extract-bindings ((name val) ...)) (syntax (quote ((name ...) (val ...))))))))").unwrap();
let result = eval.eval_str("(extract-bindings ((x 1)))").unwrap();
let names = lisp.car(result).unwrap();
let vals = lisp.car(lisp.cdr(result).unwrap()).unwrap();
let name = lisp.car(names).unwrap();
let val = lisp.car(vals).unwrap();
assert!(lisp.symbol_matches(name, "x").unwrap());
assert_eq!(lisp.get(val).unwrap().as_number(), Some(1));
let result = eval.eval_str("(extract-bindings ((a 10) (b 20)))").unwrap();
let names = lisp.car(result).unwrap();
let vals = lisp.car(lisp.cdr(result).unwrap()).unwrap();
let n1 = lisp.car(names).unwrap();
let n2 = lisp.car(lisp.cdr(names).unwrap()).unwrap();
let v1 = lisp.car(vals).unwrap();
let v2 = lisp.car(lisp.cdr(vals).unwrap()).unwrap();
assert!(lisp.symbol_matches(n1, "a").unwrap());
assert!(lisp.symbol_matches(n2, "b").unwrap());
assert_eq!(lisp.get(v1).unwrap().as_number(), Some(10));
assert_eq!(lisp.get(v2).unwrap().as_number(), Some(20));
}
#[test]
fn test_ellipsis_parsing() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(list? '(a ...))").unwrap();
assert!(lisp.get(result).unwrap().is_true(), "(a ...) should be a proper list");
let result = eval.eval_str("(cadr '(a ...))").unwrap();
assert!(lisp.symbol_matches(result, "...").unwrap());
let result = eval.eval_str("(list? '((a) ...))").unwrap();
assert!(lisp.get(result).unwrap().is_true(), "((a) ...) should be a proper list");
}
#[test]
fn test_empty_ellipsis() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define-syntax zero-or-more (lambda (x) (syntax-case x () ((zero-or-more a ...) (syntax (quote (a ...)))))))").unwrap();
let result = eval.eval_str("(zero-or-more)").unwrap();
assert!(lisp.get(result).unwrap().is_nil(), "zero-or-more with no args should be ()");
let result = eval.eval_str("(zero-or-more 1)").unwrap();
let first = lisp.car(result).unwrap();
assert_eq!(lisp.get(first).unwrap().as_number(), Some(1));
}
#[test]
fn check_eval_error_size() {
use core::mem::size_of;
use grift_eval::{EvalError, ErrorKind, StackFrame, ArgCountInfo};
use grift_eval::ParseError;
println!("\n=== Type Sizes (stack efficiency check) ===");
println!("EvalError: {} bytes", size_of::<EvalError>());
println!("ErrorKind: {} bytes", size_of::<ErrorKind>());
println!("StackFrame: {} bytes", size_of::<StackFrame>());
println!("ArgCountInfo: {} bytes", size_of::<ArgCountInfo>());
println!("ParseError: {} bytes", size_of::<ParseError>());
println!("Option<ParseError>: {} bytes", size_of::<Option<ParseError>>());
println!("Option<&str>: {} bytes", size_of::<Option<&'static str>>());
let error_size = size_of::<EvalError>();
assert!(
error_size <= 96,
"EvalError is {} bytes, expected <= 96 bytes for stack efficiency",
error_size
);
assert_eq!(size_of::<ErrorKind>(), 1, "ErrorKind should be 1 byte (repr(u8))");
assert_eq!(size_of::<ArgCountInfo>(), 4, "ArgCountInfo should be 4 bytes (2 × u16)");
}
#[test]
fn test_keyword_shadowing_cond() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(cond (#t 42))"), 42);
eval.eval_str("(define (cond) 32)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(cond)"), 32);
}
#[test]
fn test_keyword_shadowing_let() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(let ((x 5)) x)"), 5);
eval.eval_str("(define (let x) (+ x 10))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(let 7)"), 17);
}
#[test]
fn test_keyword_shadowing_and() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(and 1 2 3)"), 3);
eval.eval_str("(define and 999)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "and"), 999);
}
#[test]
fn test_keyword_shadowing_or() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(or #f 42)"), 42);
eval.eval_str("(define (or a b) (* a b))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(or 3 4)"), 12);
}
#[test]
fn test_keyword_shadowing_when() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(when #t 100)"), 100);
eval.eval_str("(define (when) 77)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(when)"), 77);
}
#[test]
fn test_case_lambda_single_clause() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define add1 (case-lambda ((x) (+ x 1))))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(add1 5)"), 6);
}
#[test]
fn test_case_lambda_two_clauses() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let define_result = eval.eval_str("
(define identity-or-sum
(case-lambda
((x) x)
((x y) (+ x y))))
");
assert!(define_result.is_ok(), "define failed: {:?}", define_result);
assert_eq!(eval_to_num(&lisp, &mut eval, "(identity-or-sum 5)"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(identity-or-sum 3 4)"), 7);
}
#[test]
fn test_case_lambda_three_clauses() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("
(define multi-arity
(case-lambda
(() 0)
((x) x)
((x y) (+ x y))))
").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(multi-arity)"), 0);
assert_eq!(eval_to_num(&lisp, &mut eval, "(multi-arity 42)"), 42);
assert_eq!(eval_to_num(&lisp, &mut eval, "(multi-arity 3 4)"), 7);
let result = eval.eval_str("(multi-arity 1 2 3)");
assert!(result.is_err(), "Expected error for 3 args");
}
#[test]
fn test_case_lambda_range_example() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("
(define range
(case-lambda
((e) (range 0 e))
((b e)
(do ((r '() (cons e r))
(e (- e 1) (- e 1)))
((< e b) r)))))
").unwrap();
let result = eval.eval_str("(range 3)").unwrap();
let first = lisp.get(lisp.car(result).unwrap()).unwrap().as_number().unwrap();
assert_eq!(first, 0);
let result = eval.eval_str("(range 3 5)").unwrap();
let first = lisp.get(lisp.car(result).unwrap()).unwrap().as_number().unwrap();
assert_eq!(first, 3);
}
#[test]
fn test_case_lambda_zero_args() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define zero-only (case-lambda (() 100)))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(zero-only)"), 100);
eval.eval_str("(define zero-or-one (case-lambda (() 100) ((x) x)))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(zero-or-one)"), 100);
assert_eq!(eval_to_num(&lisp, &mut eval, "(zero-or-one 42)"), 42);
}
#[test]
fn test_case_lambda_variadic_clause() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("
(define add-all
(case-lambda
(() 0)
((x) x)
((x y) (+ x y))
(args (apply + args))))
").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(add-all)"), 0);
assert_eq!(eval_to_num(&lisp, &mut eval, "(add-all 5)"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(add-all 3 4)"), 7);
assert_eq!(eval_to_num(&lisp, &mut eval, "(add-all 1 2 3)"), 6);
assert_eq!(eval_to_num(&lisp, &mut eval, "(add-all 1 2 3 4 5)"), 15);
}
#[test]
fn test_cond_expand_grift() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(cond-expand (grift 42) (else 0))"), 42);
}
#[test]
fn test_cond_expand_r7rs() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(cond-expand (r7rs 100) (else 0))"), 100);
}
#[test]
fn test_cond_expand_else() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(cond-expand (unknown-feature 1) (else 99))"), 99);
}
#[test]
fn test_cond_expand_and() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(cond-expand ((and r7rs grift) 55) (else 0))"), 55);
assert_eq!(eval_to_num(&lisp, &mut eval, "(cond-expand ((and r7rs ieee-float) 1) (else 66))"), 66);
}
#[test]
fn test_cond_expand_or() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(cond-expand ((or ieee-float ratios grift) 77) (else 0))"), 77);
assert_eq!(eval_to_num(&lisp, &mut eval, "(cond-expand ((or ieee-float ratios) 1) (else 88))"), 88);
}
#[test]
fn test_cond_expand_not() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(cond-expand ((not ieee-float) 33) (else 0))"), 33);
assert_eq!(eval_to_num(&lisp, &mut eval, "(cond-expand ((not grift) 1) (else 44))"), 44);
}
#[test]
fn test_cond_expand_multiple_expressions() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define x 0)").unwrap();
eval.eval_str("(cond-expand (grift (set! x 1) (set! x (+ x 10))))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "x"), 11);
}
#[test]
fn test_delay_force_simple() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define p (delay-force (+ 1 2 3)))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(force p)"), 6);
}
#[test]
fn test_delay_force_memo() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define counter 0)").unwrap();
eval.eval_str("(define p (delay-force (begin (set! counter (+ counter 1)) counter)))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(force p)"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(force p)"), 1); assert_eq!(eval_to_num(&lisp, &mut eval, "counter"), 1);
}
#[test]
fn test_delay_force_chains() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define p1 (delay 42))").unwrap();
eval.eval_str("(define p2 (delay-force (force p1)))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(force p2)"), 42);
}
#[test]
fn test_promise_predicate() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define p (delay 1))").unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(promise? p)"));
assert!(eval_is_false(&lisp, &mut eval, "(promise? 42)"));
assert!(eval_is_false(&lisp, &mut eval, "(promise? '(1 2 3))"));
assert!(eval_is_false(&lisp, &mut eval, "(promise? \"hello\")"));
}
#[test]
fn test_make_promise() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define p (make-promise 42))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(force p)"), 42);
}
#[test]
fn test_make_promise_already_promise() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define p1 (delay 99))").unwrap();
eval.eval_str("(define p2 (make-promise p1))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(force p1)"), 99);
assert_eq!(eval_to_num(&lisp, &mut eval, "(force p2)"), 99);
}
#[test]
fn test_rest_lambda_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define list-all (lambda args args))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(length (list-all 1 2 3))"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (list-all 1 2 3))"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(cadr (list-all 1 2 3))"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(caddr (list-all 1 2 3))"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(length (list-all 42))"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (list-all 42))"), 42);
let result = eval.eval_str("(list-all)").unwrap();
assert!(lisp.get(result).unwrap().is_nil());
}
#[test]
fn test_rest_lambda_with_computation() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define sum-all (lambda args (fold + 0 args)))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(sum-all 1 2 3 4 5)"), 15);
assert_eq!(eval_to_num(&lisp, &mut eval, "(sum-all (* 2 3) (* 4 5))"), 26);
}
#[test]
fn test_dotted_lambda_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define f (lambda (a b . rest) rest))").unwrap();
let result = eval.eval_str("(f 1 2)").unwrap();
assert!(lisp.get(result).unwrap().is_nil());
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (f 1 2 3))"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(length (f 1 2 3 4 5))"), 3);
}
#[test]
fn test_dotted_lambda_use_all_params() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define f (lambda (first . rest) (cons first rest)))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (f 1 2 3))"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(cadr (f 1 2 3))"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(caddr (f 1 2 3))"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(car (f 42))"), 42);
let result = eval.eval_str("(cdr (f 42))").unwrap();
assert!(lisp.get(result).unwrap().is_nil());
}
#[test]
fn test_dotted_lambda_sum_with_base() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define sum-with-base (lambda (base . nums) (fold + base nums)))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(sum-with-base 100 1 2 3)"), 106);
assert_eq!(eval_to_num(&lisp, &mut eval, "(sum-with-base 0)"), 0);
}
#[test]
fn test_promise_lazy_evaluation() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define side-effect 0)").unwrap();
eval.eval_str("(define p (delay (set! side-effect (+ side-effect 1))))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "side-effect"), 0);
eval.eval_str("(force p)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "side-effect"), 1);
eval.eval_str("(force p)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "side-effect"), 1);
}
#[test]
fn test_multiple_promises() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define p1 (delay 10))").unwrap();
eval.eval_str("(define p2 (delay 20))").unwrap();
eval.eval_str("(define p3 (delay (+ (force p1) (force p2))))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(force p3)"), 30);
}
#[test]
fn test_promise_chain() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define p1 (delay 5))").unwrap();
eval.eval_str("(define p2 (delay (* 2 (force p1))))").unwrap();
eval.eval_str("(define p3 (delay (* 3 (force p2))))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(force p3)"), 30);
}
#[test]
fn test_recursive_helper_accumulator_pattern() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str(r#"
(define-syntax collect-first
(lambda (x)
(syntax-case x ()
((collect-first () (acc ...))
(syntax (quote (acc ...))))
((collect-first (first . rest) (acc ...))
(syntax (collect-first rest (acc ... first)))))))
"#).unwrap();
let result = eval.eval_str("(collect-first () ())").unwrap();
assert!(lisp.get(result).unwrap().is_nil());
let result = eval.eval_str("(collect-first (a) ())").unwrap();
let first = lisp.car(result).unwrap();
assert!(lisp.symbol_matches(first, "a").unwrap());
let result = eval.eval_str("(collect-first (a b c) ())").unwrap();
let first = lisp.car(result).unwrap();
let second = lisp.car(lisp.cdr(result).unwrap()).unwrap();
let third = lisp.car(lisp.cdr(lisp.cdr(result).unwrap()).unwrap()).unwrap();
assert!(lisp.symbol_matches(first, "a").unwrap());
assert!(lisp.symbol_matches(second, "b").unwrap());
assert!(lisp.symbol_matches(third, "c").unwrap());
}
#[test]
fn test_dual_accumulator_pattern() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str(r#"
(define-syntax extract-pairs
(lambda (x)
(syntax-case x ()
((extract-pairs () (firsts ...) (seconds ...))
(syntax (list (quote (firsts ...)) (quote (seconds ...)))))
((extract-pairs ((a b) . rest) (firsts ...) (seconds ...))
(syntax (extract-pairs rest (firsts ... a) (seconds ... b)))))))
"#).unwrap();
let result = eval.eval_str("(extract-pairs ((x 1)) () ())").unwrap();
let firsts = lisp.car(result).unwrap();
let seconds = lisp.car(lisp.cdr(result).unwrap()).unwrap();
let x = lisp.car(firsts).unwrap();
let one = lisp.car(seconds).unwrap();
assert!(lisp.symbol_matches(x, "x").unwrap());
assert_eq!(lisp.get(one).unwrap().as_number(), Some(1));
let result = eval.eval_str("(extract-pairs ((a 1) (b 2) (c 3)) () ())").unwrap();
let firsts = lisp.car(result).unwrap();
let seconds = lisp.car(lisp.cdr(result).unwrap()).unwrap();
assert!(lisp.symbol_matches(lisp.car(firsts).unwrap(), "a").unwrap());
assert!(lisp.symbol_matches(lisp.car(lisp.cdr(firsts).unwrap()).unwrap(), "b").unwrap());
assert!(lisp.symbol_matches(lisp.car(lisp.cdr(lisp.cdr(firsts).unwrap()).unwrap()).unwrap(), "c").unwrap());
assert_eq!(lisp.get(lisp.car(seconds).unwrap()).unwrap().as_number(), Some(1));
assert_eq!(lisp.get(lisp.car(lisp.cdr(seconds).unwrap()).unwrap()).unwrap().as_number(), Some(2));
}
#[test]
fn test_recursive_macro_hygiene() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str(r#"
(define-syntax sum-list
(lambda (x)
(syntax-case x ()
((sum-list () acc) (syntax acc))
((sum-list (x . rest) acc)
(syntax (sum-list rest (+ acc x)))))))
"#).unwrap();
eval.eval_str("(define acc 1000)").unwrap();
let result = eval.eval_str("(sum-list (1 2 3) 0)").unwrap();
assert_eq!(lisp.get(result).unwrap().as_number(), Some(6));
let user_acc = eval.eval_str("acc").unwrap();
assert_eq!(lisp.get(user_acc).unwrap().as_number(), Some(1000));
}
#[test]
fn test_do_loop_with_accumulator() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str(r#"
(do ((i 1 (+ i 1))
(sum 0 (+ sum i)))
((> i 10) sum))
"#).unwrap();
assert_eq!(lisp.get(result).unwrap().as_number(), Some(55));
}
#[test]
fn test_do_loop_default_step() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str(r#"
(do ((count 0 (+ count 1))
(limit 5))
((>= count limit) 'done))
"#).unwrap();
assert!(lisp.symbol_matches(result, "done").unwrap());
}
#[test]
fn test_pattern_alternatives() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str(r#"
(define-syntax process-item
(lambda (x)
(syntax-case x ()
((process-item ()) (syntax (quote empty)))
((process-item (a)) (syntax (quote one)))
((process-item (a b)) (syntax (quote two)))
((process-item (a b c)) (syntax (quote three)))
((process-item other) (syntax (quote many))))))
"#).unwrap();
let result = eval.eval_str("(process-item ())").unwrap();
assert!(lisp.symbol_matches(result, "empty").unwrap());
let result = eval.eval_str("(process-item (1))").unwrap();
assert!(lisp.symbol_matches(result, "one").unwrap());
let result = eval.eval_str("(process-item (1 2))").unwrap();
assert!(lisp.symbol_matches(result, "two").unwrap());
let result = eval.eval_str("(process-item (1 2 3))").unwrap();
assert!(lisp.symbol_matches(result, "three").unwrap());
let result = eval.eval_str("(process-item (1 2 3 4))").unwrap();
assert!(lisp.symbol_matches(result, "many").unwrap());
}
#[test]
fn test_literal_keywords() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str(r#"
(define-syntax my-cond
(lambda (x)
(syntax-case x (else =>)
((my-cond (else result)) (syntax result))
((my-cond (test => proc))
(syntax (let ((temp test))
(if temp (proc temp) #f))))
((my-cond (test result))
(syntax (if test result #f))))))
"#).unwrap();
let result = eval.eval_str("(my-cond (else 42))").unwrap();
assert_eq!(lisp.get(result).unwrap().as_number(), Some(42));
let result = eval.eval_str("(my-cond (5 => (lambda (x) (* x 2))))").unwrap();
assert_eq!(lisp.get(result).unwrap().as_number(), Some(10));
let result = eval.eval_str("(my-cond (#t 100))").unwrap();
assert_eq!(lisp.get(result).unwrap().as_number(), Some(100));
}
#[test]
fn test_nested_pattern_decomposition() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str(r#"
(define-syntax flatten-pairs
(lambda (x)
(syntax-case x ()
((flatten-pairs ()) (syntax (quote ())))
((flatten-pairs ((a b) . rest))
(syntax (cons a (cons b (flatten-pairs rest))))))))
"#).unwrap();
let result = eval.eval_str("(flatten-pairs ((1 2) (3 4)))").unwrap();
let v1 = lisp.car(result).unwrap();
let v2 = lisp.car(lisp.cdr(result).unwrap()).unwrap();
let v3 = lisp.car(lisp.cdr(lisp.cdr(result).unwrap()).unwrap()).unwrap();
let v4 = lisp.car(lisp.cdr(lisp.cdr(lisp.cdr(result).unwrap()).unwrap()).unwrap()).unwrap();
assert_eq!(lisp.get(v1).unwrap().as_number(), Some(1));
assert_eq!(lisp.get(v2).unwrap().as_number(), Some(2));
assert_eq!(lisp.get(v3).unwrap().as_number(), Some(3));
assert_eq!(lisp.get(v4).unwrap().as_number(), Some(4));
}
#[test]
fn test_syntax_object_with_marks() {
let lisp: Lisp<20000> = Lisp::new();
let _eval = Evaluator::new(&lisp).unwrap();
let sym = lisp.symbol("x").unwrap();
let nil = lisp.nil().unwrap();
let stx = lisp.syntax(sym, nil, nil).unwrap();
let (expr, marks, subst) = lisp.syntax_parts(stx).unwrap();
assert!(lisp.symbol_matches(expr, "x").unwrap());
assert!(lisp.get(marks).unwrap().is_nil());
assert!(lisp.get(subst).unwrap().is_nil());
let datum = lisp.syntax_to_datum(stx).unwrap();
assert!(lisp.symbol_matches(datum, "x").unwrap());
let num = lisp.number(42).unwrap();
let passed = lisp.syntax_to_datum(num).unwrap();
assert_eq!(lisp.get(passed).unwrap().as_number(), Some(42));
}
#[test]
fn test_mark_syntax_applies_marks() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let sym = lisp.symbol("foo").unwrap();
let nil = lisp.nil().unwrap();
let stx1 = lisp.syntax(sym, nil, nil).unwrap();
let stx2 = eval.mark_syntax(stx1).unwrap();
let (_expr, marks, _subst) = lisp.syntax_parts(stx2).unwrap();
assert!(!lisp.get(marks).unwrap().is_nil(), "marks should be non-empty after mark_syntax");
let first_mark = lisp.car(marks).unwrap();
match lisp.get(first_mark).unwrap() {
Value::Symbol(_) => {
let mut buf = [0u8; 32];
let len = lisp.symbol_to_bytes(first_mark, &mut buf).unwrap();
let name = core::str::from_utf8(&buf[..len]).unwrap();
assert!(name.starts_with("#:"), "mark should be a gensym starting with #:");
}
_ => panic!("mark should be a symbol"),
}
let stx3 = eval.mark_syntax(stx2).unwrap();
let (_expr, marks2, _subst) = lisp.syntax_parts(stx3).unwrap();
let mark1 = lisp.car(marks2).unwrap();
let mark2 = lisp.car(lisp.cdr(marks2).unwrap()).unwrap();
assert!(!lisp.symbol_eq(mark1, mark2).unwrap(), "successive marks should be different");
}
#[test]
fn test_bound_identifier_eq_same() {
let lisp: Lisp<20000> = Lisp::new();
let eval = Evaluator::new(&lisp).unwrap();
let sym = lisp.symbol("x").unwrap();
let nil = lisp.nil().unwrap();
let stx1 = lisp.syntax(sym, nil, nil).unwrap();
let stx2 = lisp.syntax(sym, nil, nil).unwrap();
assert!(eval.bound_identifier_eq(stx1, stx2).unwrap());
let sym_a = lisp.symbol("a").unwrap();
let sym_a2 = lisp.symbol("a").unwrap();
assert!(eval.bound_identifier_eq(sym_a, sym_a2).unwrap());
}
#[test]
fn test_bound_identifier_eq_different_names() {
let lisp: Lisp<20000> = Lisp::new();
let eval = Evaluator::new(&lisp).unwrap();
let sym_x = lisp.symbol("x").unwrap();
let sym_y = lisp.symbol("y").unwrap();
let nil = lisp.nil().unwrap();
let stx_x = lisp.syntax(sym_x, nil, nil).unwrap();
let stx_y = lisp.syntax(sym_y, nil, nil).unwrap();
assert!(!eval.bound_identifier_eq(stx_x, stx_y).unwrap());
}
#[test]
fn test_bound_identifier_eq_different_marks() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let sym = lisp.symbol("x").unwrap();
let nil = lisp.nil().unwrap();
let stx1 = lisp.syntax(sym, nil, nil).unwrap();
let stx2 = eval.mark_syntax(stx1).unwrap();
assert!(!eval.bound_identifier_eq(stx1, stx2).unwrap());
}
#[test]
fn test_free_identifier_eq_unbound() {
let lisp: Lisp<20000> = Lisp::new();
let eval = Evaluator::new(&lisp).unwrap();
let sym = lisp.symbol("unbound_x").unwrap();
let nil = lisp.nil().unwrap();
let stx1 = lisp.syntax(sym, nil, nil).unwrap();
let stx2 = lisp.syntax(sym, nil, nil).unwrap();
assert!(eval.free_identifier_eq(stx1, stx2).unwrap());
let sym_y = lisp.symbol("unbound_y").unwrap();
let stx3 = lisp.syntax(sym_y, nil, nil).unwrap();
assert!(!eval.free_identifier_eq(stx1, stx3).unwrap());
}
#[test]
fn test_hygiene_no_capture() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define temp 42)").unwrap();
eval.eval_str(r#"
(define-syntax swap
(lambda (x)
(syntax-case x ()
((swap a b)
(syntax (let ((temp a))
(set! a b)
(set! b temp)))))))
"#).unwrap();
eval.eval_str("(define x 1)").unwrap();
eval.eval_str("(define y 2)").unwrap();
eval.eval_str("(swap x y)").unwrap();
let x = eval.eval_str("x").unwrap();
let y = eval.eval_str("y").unwrap();
assert_eq!(lisp.get(x).unwrap().as_number(), Some(2));
assert_eq!(lisp.get(y).unwrap().as_number(), Some(1));
let temp = eval.eval_str("temp").unwrap();
assert_eq!(lisp.get(temp).unwrap().as_number(), Some(42));
}
#[test]
fn test_syntax_case_simple_pattern() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str(r#"
(syntax-case '(hello world) ()
((a b) (list 'matched (quote a) (quote b))))
"#).unwrap();
let car = lisp.car(result).unwrap();
assert!(lisp.symbol_matches(car, "matched").unwrap());
}
#[test]
fn test_syntax_case_multiple_clauses() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str(r#"
(syntax-case '(a b c) ()
((x) 'one-element)
((x y) 'two-elements)
((x y z) 'three-elements)
(_ 'other))
"#).unwrap();
assert!(lisp.symbol_matches(result, "three-elements").unwrap());
}
#[test]
fn test_syntax_case_pattern_binding() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str(r#"
(syntax-case '(1 2 3) ()
((a b c) (+ a b c)))
"#).unwrap();
assert_eq!(lisp.get(result).unwrap().as_number(), Some(6));
}
#[test]
fn test_with_syntax_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str(r#"
(with-syntax ((x 1) (y 2))
(+ x y))
"#).unwrap();
assert_eq!(lisp.get(result).unwrap().as_number(), Some(3));
}
#[test]
fn test_with_syntax_empty() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(with-syntax () 42)").unwrap();
assert_eq!(lisp.get(result).unwrap().as_number(), Some(42));
}
#[test]
fn test_with_syntax_in_procedural_macro() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str(r#"
(define-syntax add-one
(lambda (x)
(syntax-case x ()
((_ n) (with-syntax ((result (+ 1 n)))
(syntax result))))))
"#).unwrap();
let result = eval.eval_str("(add-one 99)").unwrap();
assert_eq!(lisp.get(result).unwrap().as_number(), Some(100));
}
#[test]
fn test_with_syntax_multiple_bindings() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str(r#"
(define-syntax sum-triple
(lambda (x)
(syntax-case x ()
((_ a b c)
(with-syntax ((total (+ a b c)))
(syntax total))))))
"#).unwrap();
let result = eval.eval_str("(sum-triple 10 20 30)").unwrap();
assert_eq!(lisp.get(result).unwrap().as_number(), Some(60));
}
#[test]
fn test_with_syntax_complex_expression() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str(r#"
(define-syntax double-val
(lambda (x)
(syntax-case x ()
((_ n)
(with-syntax ((result (+ n n)))
(syntax result))))))
"#).unwrap();
let result = eval.eval_str("(double-val 7)").unwrap();
assert_eq!(lisp.get(result).unwrap().as_number(), Some(14));
}
#[test]
fn test_with_syntax_sequential_binding() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str(r#"
(with-syntax ((x 5))
(with-syntax ((y (+ x 3)))
(+ x y)))
"#).unwrap();
assert_eq!(lisp.get(result).unwrap().as_number(), Some(13));
}
#[test]
fn test_syntax_template_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(syntax (a b c))").unwrap();
let car = lisp.car(result).unwrap();
assert!(lisp.symbol_matches(car, "a").unwrap());
let cadr = lisp.car(lisp.cdr(result).unwrap()).unwrap();
assert!(lisp.symbol_matches(cadr, "b").unwrap());
}
#[test]
fn test_syntax_template_with_pattern_variables() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str(r#"
(syntax-case '(hello world) ()
((a b) (syntax (list a b))))
"#).unwrap();
let car = lisp.car(result).unwrap();
assert!(lisp.symbol_matches(car, "list").unwrap());
let cadr = lisp.car(lisp.cdr(result).unwrap()).unwrap();
assert!(lisp.symbol_matches(cadr, "hello").unwrap());
let caddr = lisp.car(lisp.cdr(lisp.cdr(result).unwrap()).unwrap()).unwrap();
assert!(lisp.symbol_matches(caddr, "world").unwrap());
}
#[test]
fn test_syntax_template_nested_substitution() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str(r#"
(syntax-case '(x y) ()
((a b) (syntax ((a) (b) (a b)))))
"#).unwrap();
let first = lisp.car(result).unwrap();
let first_car = lisp.car(first).unwrap();
assert!(lisp.symbol_matches(first_car, "x").unwrap());
let second = lisp.car(lisp.cdr(result).unwrap()).unwrap();
let second_car = lisp.car(second).unwrap();
assert!(lisp.symbol_matches(second_car, "y").unwrap());
let third = lisp.car(lisp.cdr(lisp.cdr(result).unwrap()).unwrap()).unwrap();
let third_car = lisp.car(third).unwrap();
let third_cadr = lisp.car(lisp.cdr(third).unwrap()).unwrap();
assert!(lisp.symbol_matches(third_car, "x").unwrap());
assert!(lisp.symbol_matches(third_cadr, "y").unwrap());
}
#[test]
fn test_syntax_template_with_numbers() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str(r#"
(syntax-case '(1 2 3) ()
((a b c) (syntax (a b c))))
"#).unwrap();
let car = lisp.car(result).unwrap();
assert_eq!(lisp.get(car).unwrap().as_number(), Some(1));
let cadr = lisp.car(lisp.cdr(result).unwrap()).unwrap();
assert_eq!(lisp.get(cadr).unwrap().as_number(), Some(2));
let caddr = lisp.car(lisp.cdr(lisp.cdr(result).unwrap()).unwrap()).unwrap();
assert_eq!(lisp.get(caddr).unwrap().as_number(), Some(3));
}
#[test]
fn test_syntax_case_with_literals() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str(r#"
(syntax-case '(if x y) (if)
((if cond then) (list 'conditional cond then))
(_ 'no-match))
"#).unwrap();
let car = lisp.car(result).unwrap();
assert!(lisp.symbol_matches(car, "conditional").unwrap());
}
#[test]
fn test_identifier_predicate_symbol() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(identifier? 'foo)").unwrap();
assert!(matches!(lisp.get(result).unwrap(), Value::True));
let result = eval.eval_str("(identifier? 42)").unwrap();
assert!(matches!(lisp.get(result).unwrap(), Value::False));
let result = eval.eval_str("(identifier? '(a b c))").unwrap();
assert!(matches!(lisp.get(result).unwrap(), Value::False));
}
#[test]
fn test_bound_identifier_eq_builtin() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(bound-identifier=? 'x 'x)").unwrap();
assert!(matches!(lisp.get(result).unwrap(), Value::True));
let result = eval.eval_str("(bound-identifier=? 'x 'y)").unwrap();
assert!(matches!(lisp.get(result).unwrap(), Value::False));
}
#[test]
fn test_free_identifier_eq_builtin() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(free-identifier=? 'x 'x)").unwrap();
assert!(matches!(lisp.get(result).unwrap(), Value::True));
let result = eval.eval_str("(free-identifier=? 'x 'y)").unwrap();
assert!(matches!(lisp.get(result).unwrap(), Value::False));
}
#[test]
fn test_syntax_to_datum_builtin() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(syntax->datum 'hello)").unwrap();
assert!(lisp.symbol_matches(result, "hello").unwrap());
let result = eval.eval_str("(syntax->datum 42)").unwrap();
assert_eq!(lisp.get(result).unwrap().as_number(), Some(42));
let result = eval.eval_str("(syntax->datum '(a b c))").unwrap();
let car = lisp.car(result).unwrap();
assert!(lisp.symbol_matches(car, "a").unwrap());
}
#[test]
fn test_datum_to_syntax_builtin() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let _result = eval.eval_str("(datum->syntax 'template 'new-name)").unwrap();
let unwrapped = eval.eval_str("(syntax->datum (datum->syntax 'template 'new-name))").unwrap();
assert!(lisp.symbol_matches(unwrapped, "new-name").unwrap());
}
#[test]
fn test_generate_temporaries_builtin() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(generate-temporaries '(a b c))").unwrap();
assert!(matches!(lisp.get(result).unwrap(), Value::Cons { .. }));
let len = lisp.list_len(result).unwrap();
assert_eq!(len, 3);
let _first = lisp.car(result).unwrap();
let first_is_id = eval.eval_str(&format!("(let ((x (car (generate-temporaries '(a))))) (identifier? x))")).unwrap();
assert!(matches!(lisp.get(first_is_id).unwrap(), Value::True));
}
#[test]
fn test_generate_temporaries_empty() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(generate-temporaries '())").unwrap();
assert!(lisp.get(result).unwrap().is_nil());
}
#[test]
fn test_syntax_case_fender_with_function() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str(r#"
(syntax-case '(foo bar) ()
((a b) (identifier? 'a) 'has-identifier)
(_ 'no-match))
"#).unwrap();
assert!(lisp.symbol_matches(result, "has-identifier").unwrap());
}
#[test]
fn test_syntax_case_fender_false() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str(r#"
(syntax-case '(1 2 3) ()
((a b c) (eq? a 'foo) 'first-clause)
((a b c) #t 'second-clause))
"#).unwrap();
assert!(lisp.symbol_matches(result, "second-clause").unwrap());
}
#[test]
fn test_syntax_case_fender_arithmetic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str(r#"
(syntax-case '(5 10) ()
((a b) (< a b) 'ascending)
((a b) (> a b) 'descending)
(_ 'equal))
"#).unwrap();
assert!(lisp.symbol_matches(result, "ascending").unwrap());
}
#[test]
fn test_syntax_case_fender_complex() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str(r#"
(syntax-case '(3 4 5) ()
((a b c) (and (> b a) (> c b)) 'strictly-increasing)
(_ 'not-increasing))
"#).unwrap();
assert!(lisp.symbol_matches(result, "strictly-increasing").unwrap());
}
#[test]
fn test_procedural_macro_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str(r#"
(define-syntax my-first
(lambda (x)
(syntax-case x ()
((_ a . rest) (syntax a)))))
"#).unwrap();
let result = eval.eval_str("(my-first 1 2 3)").unwrap();
assert_eq!(lisp.get(result).unwrap().as_number(), Some(1));
}
#[test]
fn test_procedural_macro_pattern_match() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str(r#"
(define-syntax my-add
(lambda (x)
(syntax-case x ()
((_ a b) (syntax (+ a b))))))
"#).unwrap();
let result = eval.eval_str("(my-add 2 3)").unwrap();
assert_eq!(lisp.get(result).unwrap().as_number(), Some(5));
}
#[test]
fn test_procedural_macro_multiple_clauses() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str(r#"
(define-syntax count-args
(lambda (x)
(syntax-case x ()
((_) (syntax 0))
((_ a) (syntax 1))
((_ a b) (syntax 2))
((_ a b c) (syntax 3)))))
"#).unwrap();
let result = eval.eval_str("(count-args a b c)").unwrap();
assert_eq!(lisp.get(result).unwrap().as_number(), Some(3));
}
#[test]
fn test_procedural_macro_with_fender() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str(r#"
(define-syntax check-positive
(lambda (x)
(syntax-case x ()
((_ n) (> n 0) (syntax 'positive))
((_ n) (syntax 'not-positive)))))
"#).unwrap();
let result = eval.eval_str("(check-positive 5)").unwrap();
assert!(lisp.symbol_matches(result, "positive").unwrap());
}
#[test]
fn test_procedural_macro_nested() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str(r#"
(define-syntax make-pair
(lambda (x)
(syntax-case x ()
((_ a b) (syntax (cons a b))))))
"#).unwrap();
let result = eval.eval_str("(make-pair 'left 'right)").unwrap();
let car = lisp.car(result).unwrap();
let cdr = lisp.cdr(result).unwrap();
assert!(lisp.symbol_matches(car, "left").unwrap());
assert!(lisp.symbol_matches(cdr, "right").unwrap());
}
#[test]
fn test_procedural_quasiquote_macro_helper() {
let lisp: Lisp<30000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define x 42)").unwrap();
let result = eval.eval_str("(%qq-expand atom (d z))").unwrap();
assert!(lisp.symbol_matches(result, "atom").unwrap());
let result = eval.eval_str("(%qq-expand (unquote x) (d z))").unwrap();
assert_eq!(lisp.get(result).unwrap().as_number(), Some(42));
let result = eval.eval_str("(%qq-expand (a (unquote x) c) (d z))").unwrap();
let car = lisp.car(result).unwrap();
assert!(lisp.symbol_matches(car, "a").unwrap());
let cadr = lisp.car(lisp.cdr(result).unwrap()).unwrap();
assert_eq!(lisp.get(cadr).unwrap().as_number(), Some(42));
}
#[test]
fn test_procedural_quasiquote_macro_splice() {
let lisp: Lisp<30000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define xs '(1 2 3))").unwrap();
let result = eval.eval_str("(%qq-expand (a (unquote-splicing xs) b) (d z))").unwrap();
let mut count = 0;
let mut current = result;
loop {
match lisp.get(current).unwrap() {
Value::Nil => break,
Value::Cons { cdr, .. } => {
count += 1;
current = cdr;
}
_ => break,
}
}
assert_eq!(count, 5, "Should have 5 elements: a, 1, 2, 3, b");
}
#[test]
fn test_procedural_quasiquote_macro_nested() {
let lisp: Lisp<30000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define x 42)").unwrap();
let result = eval.eval_str("(%qq-expand (quasiquote (unquote x)) (d z))").unwrap();
let car = lisp.car(result).unwrap();
assert!(lisp.symbol_matches(car, "quasiquote").unwrap());
let inner = lisp.car(lisp.cdr(result).unwrap()).unwrap();
let inner_car = lisp.car(inner).unwrap();
assert!(lisp.symbol_matches(inner_car, "unquote").unwrap());
}
#[test]
fn test_procedural_quasiquote_matches_special_form() {
let lisp: Lisp<30000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define x 10)").unwrap();
eval.eval_str("(define y 20)").unwrap();
let proc_result = eval.eval_str("(%qq-expand (a (unquote x) c) (d z))").unwrap();
let sf_result = eval.eval_str("`(a ,x c)").unwrap();
let proc_car = lisp.car(proc_result).unwrap();
let sf_car = lisp.car(sf_result).unwrap();
assert!(lisp.symbol_matches(proc_car, "a").unwrap());
assert!(lisp.symbol_matches(sf_car, "a").unwrap());
let proc_cadr = lisp.car(lisp.cdr(proc_result).unwrap()).unwrap();
let sf_cadr = lisp.car(lisp.cdr(sf_result).unwrap()).unwrap();
assert_eq!(lisp.get(proc_cadr).unwrap().as_number(), Some(10));
assert_eq!(lisp.get(sf_cadr).unwrap().as_number(), Some(10));
}
#[test]
fn test_call_cc_basic_escape() {
let lisp: Lisp<30000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(+ 1 (call/cc (lambda (k) (+ 2 (k 3)))))").unwrap();
assert_eq!(lisp.get(result).unwrap().as_number(), Some(4));
}
#[test]
fn test_call_cc_no_escape() {
let lisp: Lisp<30000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(+ 1 (call/cc (lambda (k) 3)))").unwrap();
assert_eq!(lisp.get(result).unwrap().as_number(), Some(4)); }
#[test]
fn test_call_cc_stored_continuation() {
let lisp: Lisp<30000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define saved-k #f)").unwrap();
let result1 = eval.eval_str("(+ 1 (call/cc (lambda (k) (set! saved-k k) 4)))").unwrap();
assert_eq!(lisp.get(result1).unwrap().as_number(), Some(5));
let result2 = eval.eval_str("(saved-k 9)").unwrap();
assert_eq!(lisp.get(result2).unwrap().as_number(), Some(10));
}
#[test]
fn test_call_cc_nested() {
let lisp: Lisp<30000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str(
"(call/cc (lambda (outer) (call/cc (lambda (inner) (outer 'done))) 'never-reached))"
).unwrap();
assert!(lisp.symbol_matches(result, "done").unwrap());
}
#[test]
fn test_call_with_current_continuation() {
let lisp: Lisp<30000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(+ 1 (call-with-current-continuation (lambda (k) (k 5))))").unwrap();
assert_eq!(lisp.get(result).unwrap().as_number(), Some(6)); }
#[test]
fn test_call_cc_multiple_returns() {
let lisp: Lisp<30000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define counter 0)").unwrap();
eval.eval_str("(define saved-k #f)").unwrap();
let result1 = eval.eval_str(
"(call/cc (lambda (k) (set! saved-k k) 1))"
).unwrap();
assert_eq!(lisp.get(result1).unwrap().as_number(), Some(1));
let result2 = eval.eval_str("(saved-k 2)").unwrap();
assert_eq!(lisp.get(result2).unwrap().as_number(), Some(2));
let result3 = eval.eval_str("(saved-k 3)").unwrap();
assert_eq!(lisp.get(result3).unwrap().as_number(), Some(3));
}
#[test]
fn test_call_cc_continuation_is_procedure() {
let lisp: Lisp<30000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define k #f)").unwrap();
eval.eval_str("(call/cc (lambda (c) (set! k c) 1))").unwrap();
let result = eval.eval_str("k").unwrap();
assert!(lisp.get(result).unwrap().is_continuation());
}
#[test]
fn test_call_cc_arithmetic_context() {
let lisp: Lisp<30000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(* 2 (+ 3 (call/cc (lambda (k) (k 5)))))").unwrap();
assert_eq!(lisp.get(result).unwrap().as_number(), Some(16)); }
#[test]
fn test_call_cc_continuation_wrong_args() {
let lisp: Lisp<30000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define k #f)").unwrap();
eval.eval_str("(call/cc (lambda (c) (set! k c) 1))").unwrap();
let result = eval.eval_str("(k)");
assert!(result.is_err());
let result2 = eval.eval_str("(k 1 2)");
assert!(result2.is_err());
}
#[test]
fn test_call_cc_wrong_args() {
let lisp: Lisp<30000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(call/cc)");
assert!(result.is_err());
let result2 = eval.eval_str("(call/cc (lambda (k) k) extra)");
assert!(result2.is_err());
}
#[test]
fn test_dynamic_wind_basic() {
let lisp: Lisp<30000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define log '())").unwrap();
let result = eval.eval_str(
"(dynamic-wind
(lambda () (set! log (cons 'before log)))
(lambda () (set! log (cons 'body log)) 42)
(lambda () (set! log (cons 'after log))))"
).unwrap();
assert_eq!(lisp.get(result).unwrap().as_number(), Some(42));
let log = eval.eval_str("log").unwrap();
let first = lisp.car(log).unwrap();
assert!(lisp.symbol_matches(first, "after").unwrap());
let second = lisp.car(lisp.cdr(log).unwrap()).unwrap();
assert!(lisp.symbol_matches(second, "body").unwrap());
let third = lisp.car(lisp.cdr(lisp.cdr(log).unwrap()).unwrap()).unwrap();
assert!(lisp.symbol_matches(third, "before").unwrap());
}
#[test]
fn test_dynamic_wind_escape() {
let lisp: Lisp<30000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define log '())").unwrap();
let result = eval.eval_str(
"(call/cc (lambda (escape)
(dynamic-wind
(lambda () (set! log (cons 'before log)))
(lambda () (escape 'escaped))
(lambda () (set! log (cons 'after log))))))"
).unwrap();
assert!(lisp.symbol_matches(result, "escaped").unwrap());
let log = eval.eval_str("log").unwrap();
let first = lisp.car(log).unwrap();
assert!(lisp.symbol_matches(first, "after").unwrap());
let second = lisp.car(lisp.cdr(log).unwrap()).unwrap();
assert!(lisp.symbol_matches(second, "before").unwrap());
}
#[test]
fn test_dynamic_wind_returns_body() {
let lisp: Lisp<30000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str(
"(dynamic-wind
(lambda () 'ignored)
(lambda () (+ 1 2 3))
(lambda () 'also-ignored))"
).unwrap();
assert_eq!(lisp.get(result).unwrap().as_number(), Some(6));
}
#[test]
fn test_dynamic_wind_nested() {
let lisp: Lisp<30000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define log '())").unwrap();
let result = eval.eval_str(
"(dynamic-wind
(lambda () (set! log (cons 'before1 log)))
(lambda ()
(dynamic-wind
(lambda () (set! log (cons 'before2 log)))
(lambda () (set! log (cons 'body log)) 99)
(lambda () (set! log (cons 'after2 log)))))
(lambda () (set! log (cons 'after1 log))))"
).unwrap();
assert_eq!(lisp.get(result).unwrap().as_number(), Some(99));
let log = eval.eval_str("log").unwrap();
let e1 = lisp.car(log).unwrap();
assert!(lisp.symbol_matches(e1, "after1").unwrap());
let rest1 = lisp.cdr(log).unwrap();
let e2 = lisp.car(rest1).unwrap();
assert!(lisp.symbol_matches(e2, "after2").unwrap());
let rest2 = lisp.cdr(rest1).unwrap();
let e3 = lisp.car(rest2).unwrap();
assert!(lisp.symbol_matches(e3, "body").unwrap());
}
#[test]
fn test_dynamic_wind_wrong_args() {
let lisp: Lisp<30000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(dynamic-wind (lambda () 1) (lambda () 2))");
assert!(result.is_err());
let result2 = eval.eval_str("(dynamic-wind (lambda () 1) (lambda () 2) (lambda () 3) (lambda () 4))");
assert!(result2.is_err());
let result3 = eval.eval_str("(dynamic-wind)");
assert!(result3.is_err());
}
#[test]
fn test_dynamic_wind_reenter() {
let lisp: Lisp<30000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define log '())").unwrap();
eval.eval_str("(define saved-k #f)").unwrap();
let result1 = eval.eval_str(
"(dynamic-wind
(lambda () (set! log (cons 'before log)))
(lambda ()
(call/cc (lambda (k)
(set! saved-k k)
'first-time)))
(lambda () (set! log (cons 'after log))))"
).unwrap();
assert!(lisp.symbol_matches(result1, "first-time").unwrap());
let log1 = eval.eval_str("log").unwrap();
let e1 = lisp.car(log1).unwrap();
assert!(lisp.symbol_matches(e1, "after").unwrap());
eval.eval_str("(set! log '())").unwrap();
let result2 = eval.eval_str("(saved-k 'second-time)").unwrap();
assert!(lisp.symbol_matches(result2, "second-time").unwrap());
let log2 = eval.eval_str("log").unwrap();
let e2_1 = lisp.car(log2).unwrap();
assert!(lisp.symbol_matches(e2_1, "after").unwrap());
let e2_2 = lisp.car(lisp.cdr(log2).unwrap()).unwrap();
assert!(lisp.symbol_matches(e2_2, "before").unwrap());
}
#[test]
fn test_eval_block_comment() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(+ 1 #| this is a comment |# 2)"), 3);
}
#[test]
fn test_eval_datum_comment() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(+ 1 #; 99 2)"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(+ #; (+ 10 20) 1 2)"), 3);
}
#[test]
fn test_eval_bytevector_self_evaluating() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("#u8(1 2 3)").unwrap();
assert!(lisp.get(result).unwrap().is_bytevector());
assert_eq!(lisp.bytevector_len(result).unwrap(), 3);
}
#[test]
fn test_eval_block_comment_in_eval() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "#| comment |# 42"), 42);
}
#[test]
fn test_syntax_error_raises() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(syntax-error \"bad syntax\")");
assert!(result.is_err());
}
#[test]
fn test_with_exception_handler_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
"(with-exception-handler
(lambda (e) 42)
(lambda () (raise 99)))"),
42);
}
#[test]
fn test_with_exception_handler_no_exception() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
"(with-exception-handler
(lambda (e) 0)
(lambda () 42))"),
42);
}
#[test]
fn test_raise_passes_exception_object() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
"(with-exception-handler
(lambda (e) (+ e 1))
(lambda () (raise 41)))"),
42);
}
#[test]
fn test_raise_without_handler() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(raise 42)");
assert!(result.is_err());
}
#[test]
fn test_guard_catches_exception() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
"(guard (exn
((number? exn) (+ exn 1)))
(raise 41))"),
42);
}
#[test]
fn test_guard_else_clause() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
"(guard (exn
(else 99))
(raise \"error\"))"),
99);
}
#[test]
fn test_guard_no_exception() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
"(guard (exn
((number? exn) 0))
42)"),
42);
}
#[test]
fn test_guard_multiple_clauses() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
"(guard (exn
((string? exn) 1)
((number? exn) 2)
(else 3))
(raise 42))"),
2);
}
#[test]
fn test_error_object_predicate() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval,
r#"(guard (e (#t (error-object? e)))
(error "test"))"#));
}
#[test]
fn test_error_object_predicate_false() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_false(&lisp, &mut eval,
"(error-object? 42)"));
assert!(eval_is_false(&lisp, &mut eval,
r#"(error-object? "hello")"#));
assert!(eval_is_false(&lisp, &mut eval,
"(error-object? #t)"));
}
#[test]
fn test_error_object_message() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval,
r#"(guard (e (#t (string? (error-object-message e))))
(error "test message" 1 2 3))"#));
}
#[test]
fn test_error_object_irritants() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
r#"(guard (e (#t (car (error-object-irritants e))))
(error "test" 42))"#),
42);
}
#[test]
fn test_error_object_irritants_list() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
r#"(guard (e (#t (+ (car (error-object-irritants e))
(car (cdr (error-object-irritants e))))))
(error "test" 10 32))"#),
42);
}
#[test]
fn test_error_object_no_irritants() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval,
r#"(guard (e (#t (null? (error-object-irritants e))))
(error "test"))"#));
}
#[test]
fn test_error_object_type() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval,
r#"(guard (e (#t (null? (error-object-type e))))
(error "test"))"#));
}
#[test]
fn test_error_raises_through_handler() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
r#"(with-exception-handler
(lambda (e) (if (error-object? e) 42 0))
(lambda () (error "boom")))"#),
42);
}
#[test]
fn test_error_without_handler() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str(r#"(error "unhandled")"#);
assert!(result.is_err());
}
#[test]
fn test_guard_catches_error() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
r#"(guard (exn
((error-object? exn)
(car (error-object-irritants exn))))
(error "test" 42))"#),
42);
}
#[test]
fn test_raise_continuable_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
"(with-exception-handler
(lambda (e) (+ e 1))
(lambda () (raise-continuable 41)))"),
42);
}
#[test]
fn test_guard_catches_type_error() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval,
r#"(guard (exn (#t #t))
(car 42))"#));
}
#[test]
fn test_guard_catches_type_error_as_error_object() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval,
r#"(guard (exn ((error-object? exn) #t))
(car 42))"#));
}
#[test]
fn test_guard_catches_unbound_variable() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval,
r#"(guard (exn (#t #t))
undefined-variable-xyz)"#));
}
#[test]
fn test_guard_catches_wrong_arg_count() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define (f x) x)").unwrap();
assert!(eval_is_true(&lisp, &mut eval,
r#"(guard (exn (#t #t))
(f 1 2 3))"#));
}
#[test]
fn test_guard_catches_division_by_zero() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval,
r#"(guard (exn (#t #t))
(/ 1 0))"#));
}
#[test]
fn test_with_exception_handler_catches_type_error() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
r#"(with-exception-handler
(lambda (e) 99)
(lambda () (car 42)))"#),
99);
}
#[test]
fn test_error_object_message_from_type_error() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval,
r#"(guard (exn
((error-object? exn)
(string? (error-object-message exn))))
(car 42))"#));
}
#[test]
fn test_uncaught_type_error_still_errors() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval.eval_str("(car 42)").is_err());
}
#[test]
fn test_guard_catches_not_a_function() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval,
r#"(guard (exn (#t #t))
(42 1 2))"#));
}
#[test]
fn test_make_parameter_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define p (make-parameter 10))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(p)"), 10);
}
#[test]
fn test_parameterize_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define p (make-parameter 10))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval,
"(parameterize ((p 20)) (p))"),
20);
assert_eq!(eval_to_num(&lisp, &mut eval, "(p)"), 10);
}
#[test]
fn test_parameterize_restores_on_exception() {
let lisp: Lisp<40000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define p (make-parameter 10))").unwrap();
eval.eval_str(
"(guard (exn (else #f))
(parameterize ((p 20))
(raise 'err)))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(p)"), 10);
}
#[test]
fn test_define_record_type_basic() {
let lisp: Lisp<40000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define-record-type <point> (make-point x y) point? (x point-x) (y point-y))").unwrap();
eval.eval_str("(define p (make-point 3 4))").unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(point? p)"));
assert_eq!(eval_to_num(&lisp, &mut eval, "(point-x p)"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(point-y p)"), 4);
}
#[test]
fn test_define_record_type_predicate_false() {
let lisp: Lisp<40000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define-record-type <point> (make-point x y) point? (x point-x) (y point-y))").unwrap();
assert!(eval_is_false(&lisp, &mut eval, "(point? 42)"));
assert!(eval_is_false(&lisp, &mut eval, "(point? '())"));
}
#[test]
fn test_define_record_type_with_mutator() {
let lisp: Lisp<40000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define-record-type <point> (make-point x y) point? (x point-x) (y point-y point-set-y!))").unwrap();
eval.eval_str("(define p (make-point 3 4))").unwrap();
eval.eval_str("(point-set-y! p 99)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(point-y p)"), 99);
assert_eq!(eval_to_num(&lisp, &mut eval, "(point-x p)"), 3);
}
#[test]
fn test_port_predicates() {
let lisp: Lisp<20000> = Lisp::new();
let mut io = grift_std::StdIoProvider::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.set_io_provider(&mut io);
assert!(eval_is_true(&lisp, &mut eval, "(port? (current-input-port))"));
assert!(eval_is_true(&lisp, &mut eval, "(port? (current-output-port))"));
assert!(eval_is_true(&lisp, &mut eval, "(port? (current-error-port))"));
assert!(eval_is_false(&lisp, &mut eval, "(port? 42)"));
assert!(eval_is_false(&lisp, &mut eval, "(port? \"hello\")"));
}
#[test]
fn test_input_output_port_predicates() {
let lisp: Lisp<20000> = Lisp::new();
let mut io = grift_std::StdIoProvider::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.set_io_provider(&mut io);
assert!(eval_is_true(&lisp, &mut eval, "(input-port? (current-input-port))"));
assert!(eval_is_false(&lisp, &mut eval, "(input-port? (current-output-port))"));
assert!(eval_is_true(&lisp, &mut eval, "(output-port? (current-output-port))"));
assert!(eval_is_true(&lisp, &mut eval, "(output-port? (current-error-port))"));
assert!(eval_is_false(&lisp, &mut eval, "(output-port? (current-input-port))"));
}
#[test]
fn test_eof_object() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(eof-object? (eof-object))"));
assert!(eval_is_false(&lisp, &mut eval, "(eof-object? 42)"));
assert!(eval_is_false(&lisp, &mut eval, "(eof-object? #f)"));
assert!(eval_is_false(&lisp, &mut eval, "(eof-object? '())"));
}
#[test]
fn test_string_ports_read_char() {
let lisp: Lisp<20000> = Lisp::new();
let mut io = grift_std::StdIoProvider::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.set_io_provider(&mut io);
eval.eval_str("(define p (open-input-string \"abc\"))").unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(input-port? p)"));
assert!(eval_is_false(&lisp, &mut eval, "(output-port? p)"));
let result = eval.eval_str("(read-char p)").unwrap();
assert_eq!(lisp.get(result).unwrap(), Value::Char('a'));
let result = eval.eval_str("(read-char p)").unwrap();
assert_eq!(lisp.get(result).unwrap(), Value::Char('b'));
let result = eval.eval_str("(read-char p)").unwrap();
assert_eq!(lisp.get(result).unwrap(), Value::Char('c'));
assert!(eval_is_true(&lisp, &mut eval, "(eof-object? (read-char p))"));
}
#[test]
fn test_string_ports_peek_char() {
let lisp: Lisp<20000> = Lisp::new();
let mut io = grift_std::StdIoProvider::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.set_io_provider(&mut io);
eval.eval_str("(define p (open-input-string \"xy\"))").unwrap();
let result = eval.eval_str("(peek-char p)").unwrap();
assert_eq!(lisp.get(result).unwrap(), Value::Char('x'));
let result = eval.eval_str("(peek-char p)").unwrap();
assert_eq!(lisp.get(result).unwrap(), Value::Char('x'));
let result = eval.eval_str("(read-char p)").unwrap();
assert_eq!(lisp.get(result).unwrap(), Value::Char('x'));
let result = eval.eval_str("(read-char p)").unwrap();
assert_eq!(lisp.get(result).unwrap(), Value::Char('y'));
}
#[test]
fn test_output_string_port() {
let lisp: Lisp<20000> = Lisp::new();
let mut io = grift_std::StdIoProvider::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.set_io_provider(&mut io);
eval.eval_str("(define p (open-output-string))").unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(output-port? p)"));
assert!(eval_is_false(&lisp, &mut eval, "(input-port? p)"));
eval.eval_str("(write-char #\\H p)").unwrap();
eval.eval_str("(write-char #\\i p)").unwrap();
let result = eval.eval_str("(get-output-string p)").unwrap();
assert!(matches!(lisp.get(result).unwrap(), Value::String { len: 2, .. }));
assert_eq!(lisp.string_char_at(result, 0).unwrap(), 'H');
assert_eq!(lisp.string_char_at(result, 1).unwrap(), 'i');
}
#[test]
fn test_write_to_string_port() {
let lisp: Lisp<20000> = Lisp::new();
let mut io = grift_std::StdIoProvider::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.set_io_provider(&mut io);
eval.eval_str("(define p (open-output-string))").unwrap();
eval.eval_str("(write 42 p)").unwrap();
let result = eval.eval_str("(get-output-string p)").unwrap();
assert!(matches!(lisp.get(result).unwrap(), Value::String { len: 2, .. }));
assert_eq!(lisp.string_char_at(result, 0).unwrap(), '4');
assert_eq!(lisp.string_char_at(result, 1).unwrap(), '2');
}
#[test]
fn test_read_from_string_port() {
let lisp: Lisp<20000> = Lisp::new();
let mut io = grift_std::StdIoProvider::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.set_io_provider(&mut io);
eval.eval_str("(define p (open-input-string \"42\"))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(read p)"), 42);
}
#[test]
fn test_read_list_from_string_port() {
let lisp: Lisp<20000> = Lisp::new();
let mut io = grift_std::StdIoProvider::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.set_io_provider(&mut io);
eval.eval_str("(define p (open-input-string \"(+ 1 2)\"))").unwrap();
let result = eval.eval_str("(read p)").unwrap();
assert!(matches!(lisp.get(result).unwrap(), Value::Cons { .. }));
}
#[test]
fn test_close_port() {
let lisp: Lisp<20000> = Lisp::new();
let mut io = grift_std::StdIoProvider::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.set_io_provider(&mut io);
eval.eval_str("(define p (open-input-string \"hello\"))").unwrap();
eval.eval_str("(close-port p)").unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(port? p)"));
assert!(eval_is_true(&lisp, &mut eval, "(input-port? p)"));
}
#[test]
fn test_read_line() {
let lisp: Lisp<20000> = Lisp::new();
let mut io = grift_std::StdIoProvider::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.set_io_provider(&mut io);
eval.eval_str("(define p (open-input-string \"hello\\nworld\"))").unwrap();
assert_eq!(eval_to_string(&lisp, &mut eval, "(read-line p)"), "\"hello\"");
assert_eq!(eval_to_string(&lisp, &mut eval, "(read-line p)"), "\"world\"");
assert!(eval_is_true(&lisp, &mut eval, "(eof-object? (read-line p))"));
}
#[test]
fn test_read_line_no_newline() {
let lisp: Lisp<20000> = Lisp::new();
let mut io = grift_std::StdIoProvider::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.set_io_provider(&mut io);
eval.eval_str("(define p (open-input-string \"single\"))").unwrap();
assert_eq!(eval_to_string(&lisp, &mut eval, "(read-line p)"), "\"single\"");
assert!(eval_is_true(&lisp, &mut eval, "(eof-object? (read-line p))"));
}
#[test]
fn test_read_string() {
let lisp: Lisp<20000> = Lisp::new();
let mut io = grift_std::StdIoProvider::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.set_io_provider(&mut io);
eval.eval_str("(define p (open-input-string \"hello world\"))").unwrap();
assert_eq!(eval_to_string(&lisp, &mut eval, "(read-string 5 p)"), "\"hello\"");
assert_eq!(eval_to_string(&lisp, &mut eval, "(read-string 1 p)"), "\" \"");
assert_eq!(eval_to_string(&lisp, &mut eval, "(read-string 10 p)"), "\"world\"");
assert!(eval_is_true(&lisp, &mut eval, "(eof-object? (read-string 1 p))"));
}
#[test]
fn test_write_shared_and_simple() {
let lisp: Lisp<20000> = Lisp::new();
let mut io = grift_std::StdIoProvider::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.set_io_provider(&mut io);
eval.eval_str("(define p (open-output-string))").unwrap();
eval.eval_str("(write-shared '(1 2 3) p)").unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(equal? (get-output-string p) \"(1 2 3)\")"));
eval.eval_str("(define q (open-output-string))").unwrap();
eval.eval_str("(write-simple '(a b) q)").unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(equal? (get-output-string q) \"(a b)\")"));
}
#[test]
fn test_textual_port_predicate() {
let lisp: Lisp<20000> = Lisp::new();
let mut io = grift_std::StdIoProvider::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.set_io_provider(&mut io);
assert!(eval_is_true(&lisp, &mut eval, "(textual-port? (current-input-port))"));
assert!(eval_is_true(&lisp, &mut eval, "(textual-port? (current-output-port))"));
assert!(eval_is_false(&lisp, &mut eval, "(textual-port? 42)"));
}
#[test]
fn test_binary_port_predicate() {
let lisp: Lisp<20000> = Lisp::new();
let mut io = grift_std::StdIoProvider::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.set_io_provider(&mut io);
assert!(eval_is_false(&lisp, &mut eval, "(binary-port? (current-input-port))"));
assert!(eval_is_false(&lisp, &mut eval, "(binary-port? 42)"));
}
#[test]
fn test_input_port_open_predicate() {
let lisp: Lisp<20000> = Lisp::new();
let mut io = grift_std::StdIoProvider::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.set_io_provider(&mut io);
eval.eval_str("(define p (open-input-string \"test\"))").unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(input-port-open? p)"));
eval.eval_str("(close-port p)").unwrap();
assert!(eval_is_false(&lisp, &mut eval, "(input-port-open? p)"));
assert!(eval_is_true(&lisp, &mut eval, "(input-port-open? (current-input-port))"));
}
#[test]
fn test_output_port_open_predicate() {
let lisp: Lisp<20000> = Lisp::new();
let mut io = grift_std::StdIoProvider::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.set_io_provider(&mut io);
eval.eval_str("(define p (open-output-string))").unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(output-port-open? p)"));
eval.eval_str("(close-port p)").unwrap();
assert!(eval_is_false(&lisp, &mut eval, "(output-port-open? p)"));
assert!(eval_is_true(&lisp, &mut eval, "(output-port-open? (current-output-port))"));
assert!(eval_is_true(&lisp, &mut eval, "(output-port-open? (current-error-port))"));
}
#[test]
fn test_define_library_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("
(define-library (test math)
(export square-num)
(begin
(define (square-num x) (* x x))))
").unwrap();
eval.eval_str("(import (test math))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(square-num 5)"), 25);
}
#[test]
fn test_define_library_multiple_exports() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("
(define-library (test utils)
(export double triple)
(begin
(define (double x) (* x 2))
(define (triple x) (* x 3))))
").unwrap();
eval.eval_str("(import (test utils))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(double 5)"), 10);
assert_eq!(eval_to_num(&lisp, &mut eval, "(triple 5)"), 15);
}
#[test]
fn test_define_library_private_bindings() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("
(define-library (test private)
(export public-fn)
(begin
(define (helper x) (+ x 10))
(define (public-fn x) (helper (* x 2)))))
").unwrap();
eval.eval_str("(import (test private))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(public-fn 5)"), 20);
}
#[test]
fn test_library_import_chain() {
let lisp: Lisp<30000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("
(define-library (base lib)
(export base-fn)
(begin
(define (base-fn x) (+ x 100))))
").unwrap();
eval.eval_str("
(define-library (derived lib)
(export derived-fn)
(import (base lib))
(begin
(define (derived-fn x) (base-fn (* x 2)))))
").unwrap();
eval.eval_str("(import (derived lib))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(derived-fn 5)"), 110);
}
#[test]
fn test_define_library_no_exports() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("
(define-library (test all)
(begin
(define val1 42)
(define val2 99)))
").unwrap();
eval.eval_str("(import (test all))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "val1"), 42);
assert_eq!(eval_to_num(&lisp, &mut eval, "val2"), 99);
}
#[test]
fn test_binary_prefix() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "#b1010"), 10);
assert_eq!(eval_to_num(&lisp, &mut eval, "#b1111"), 15);
assert_eq!(eval_to_num(&lisp, &mut eval, "#b0"), 0);
assert_eq!(eval_to_num(&lisp, &mut eval, "#b-101"), -5);
assert_eq!(eval_to_num(&lisp, &mut eval, "#B1010"), 10);
}
#[test]
fn test_octal_prefix() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "#o777"), 511);
assert_eq!(eval_to_num(&lisp, &mut eval, "#o10"), 8);
assert_eq!(eval_to_num(&lisp, &mut eval, "#o-12"), -10);
assert_eq!(eval_to_num(&lisp, &mut eval, "#O10"), 8);
}
#[test]
fn test_hex_prefix() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "#xFF"), 255);
assert_eq!(eval_to_num(&lisp, &mut eval, "#x10"), 16);
assert_eq!(eval_to_num(&lisp, &mut eval, "#x-1A"), -26);
assert_eq!(eval_to_num(&lisp, &mut eval, "#xa"), 10);
assert_eq!(eval_to_num(&lisp, &mut eval, "#XFF"), 255);
}
#[test]
fn test_decimal_prefix() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "#d123"), 123);
assert_eq!(eval_to_num(&lisp, &mut eval, "#d-456"), -456);
}
#[test]
fn test_exactness_prefix() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "#e10"), 10);
let result = eval.eval_str("#i10").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert_eq!(f, 10.0),
_ => panic!("expected float for #i10"),
}
}
#[test]
fn test_combined_prefixes() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "#e#xff"), 255);
assert_eq!(eval_to_num(&lisp, &mut eval, "#x#eff"), 255);
let result = eval.eval_str("#i#b1010").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert_eq!(f, 10.0),
_ => panic!("expected float for #i#b1010"),
}
let result = eval.eval_str("#b#i1010").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert_eq!(f, 10.0),
_ => panic!("expected float for #b#i1010"),
}
assert_eq!(eval_to_num(&lisp, &mut eval, "#e#d123"), 123);
assert_eq!(eval_to_num(&lisp, &mut eval, "#d#e123"), 123);
}
#[test]
fn test_invalid_radix_digits() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval.eval_str("#b2").is_err());
assert!(eval.eval_str("#o8").is_err());
}
#[test]
fn test_radix_in_expressions() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(+ #b1010 #o10)"), 18);
assert_eq!(eval_to_num(&lisp, &mut eval, "(* #xff 2)"), 510);
}
#[test]
fn test_vector_copy_to_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define v1 (vector 1 2 3 4 5))").unwrap();
eval.eval_str("(define v2 (vector 10 20 30 40 50))").unwrap();
eval.eval_str("(vector-copy! v2 1 v1 0 2)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref v2 0)"), 10);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref v2 1)"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref v2 2)"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref v2 3)"), 40);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref v2 4)"), 50);
}
#[test]
fn test_vector_copy_to_overlapping() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define v (vector 1 2 3 4 5))").unwrap();
eval.eval_str("(vector-copy! v 2 v 0 3)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref v 0)"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref v 1)"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref v 2)"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref v 3)"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref v 4)"), 3);
}
#[test]
fn test_vector_copy_to_defaults() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define v1 (vector 1 2 3))").unwrap();
eval.eval_str("(define v2 (vector 10 20 30 40 50))").unwrap();
eval.eval_str("(vector-copy! v2 0 v1)").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref v2 0)"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref v2 1)"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref v2 2)"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(vector-ref v2 3)"), 40);
}
#[test]
fn test_vector_append() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_string(&lisp, &mut eval, "(vector-append #(1 2) #(3 4 5))"), "#(1 2 3 4 5)");
assert_eq!(eval_to_string(&lisp, &mut eval, "(vector-append #(a b) #())"), "#(a b)");
assert_eq!(eval_to_string(&lisp, &mut eval, "(vector-append)"), "#()");
assert_eq!(eval_to_string(&lisp, &mut eval, "(vector-append #(1) #(2) #(3))"), "#(1 2 3)");
}
#[test]
fn test_vector_map_single() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_string(&lisp, &mut eval, "(vector-map (lambda (x) (* x x)) #(1 2 3 4))"), "#(1 4 9 16)");
}
#[test]
fn test_vector_map_multiple() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_string(&lisp, &mut eval, "(vector-map + #(1 2 3) #(4 5 6))"), "#(5 7 9)");
}
#[test]
fn test_vector_for_each() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str("(define result 0)").unwrap();
eval.eval_str("(vector-for-each (lambda (x) (set! result (+ result x))) #(1 2 3 4 5))").unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "result"), 15);
}
#[test]
fn test_string_copy_to_basic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str(r#"(define s2 (string-copy "world!"))"#).unwrap();
eval.eval_str(r#"(string-copy! s2 1 "hello" 1 5)"#).unwrap();
assert!(eval_string_matches(&lisp, &mut eval, "s2", "wello!"));
}
#[test]
fn test_string_copy_to_overlapping() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str(r#"(define s (string-copy "abcde"))"#).unwrap();
eval.eval_str("(string-copy! s 2 s 0 3)").unwrap();
assert!(eval_string_matches(&lisp, &mut eval, "s", "ababc"));
}
#[test]
fn test_string_copy_to_defaults() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str(r#"(define s (string-copy "hello"))"#).unwrap();
eval.eval_str(r#"(string-copy! s 0 "xyz")"#).unwrap();
assert!(eval_string_matches(&lisp, &mut eval, "s", "xyzlo"));
}
#[test]
fn test_string_fill_entire() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str(r#"(define s (string-copy "hello"))"#).unwrap();
eval.eval_str(r#"(string-fill! s #\*)"#).unwrap();
assert!(eval_string_matches(&lisp, &mut eval, "s", "*****"));
}
#[test]
fn test_string_fill_subrange() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
eval.eval_str(r#"(define s (string-copy "abcdefgh"))"#).unwrap();
eval.eval_str(r#"(string-fill! s #\- 2 5)"#).unwrap();
assert!(eval_string_matches(&lisp, &mut eval, "s", "ab---fgh"));
}
#[test]
fn test_string_ci_lt() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, r#"(string-ci<? "apple" "Banana")"#));
assert!(eval_is_false(&lisp, &mut eval, r#"(string-ci<? "apple" "Apple")"#));
assert!(eval_is_true(&lisp, &mut eval, r#"(string-ci<? "apple" "Banana" "CHERRY")"#));
}
#[test]
fn test_string_ci_gt() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, r#"(string-ci>? "zebra" "YELL" "apple")"#));
assert!(eval_is_false(&lisp, &mut eval, r#"(string-ci>? "apple" "banana")"#));
}
#[test]
fn test_string_ci_le() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, r#"(string-ci<=? "apple" "Apple" "banana")"#));
assert!(eval_is_true(&lisp, &mut eval, r#"(string-ci<=? "apple" "apple")"#));
assert!(eval_is_false(&lisp, &mut eval, r#"(string-ci<=? "banana" "apple")"#));
}
#[test]
fn test_string_ci_ge() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, r#"(string-ci>=? "zebra" "zebra" "apple")"#));
assert!(eval_is_true(&lisp, &mut eval, r#"(string-ci>=? "BANANA" "banana")"#));
assert!(eval_is_false(&lisp, &mut eval, r#"(string-ci>=? "apple" "banana")"#));
}
#[test]
fn test_symbol_eq() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(symbol=? 'foo 'foo)"));
assert!(eval_is_false(&lisp, &mut eval, "(symbol=? 'foo 'bar)"));
assert!(eval_is_true(&lisp, &mut eval, "(symbol=? 'foo 'foo 'foo)"));
assert!(eval_is_false(&lisp, &mut eval, "(symbol=? 'foo 'foo 'bar)"));
assert!(eval_is_false(&lisp, &mut eval, r#"(symbol=? 'foo "foo")"#));
}
#[test]
fn test_boolean_eq_r7rs() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(boolean=? #t #t)"));
assert!(eval_is_true(&lisp, &mut eval, "(boolean=? #f #f)"));
assert!(eval_is_false(&lisp, &mut eval, "(boolean=? #t #f)"));
assert!(eval_is_true(&lisp, &mut eval, "(boolean=? #t #t #t)"));
assert!(eval_is_false(&lisp, &mut eval, "(boolean=? #t 1)"));
assert!(eval_is_false(&lisp, &mut eval, "(boolean=? #f '())"));
}
#[test]
fn test_vector_copy_with_range() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_string(&lisp, &mut eval, "(vector-copy #(1 2 3 4 5) 1 3)"), "#(2 3)");
assert_eq!(eval_to_string(&lisp, &mut eval, "(vector-copy #(1 2 3 4 5) 0 5)"), "#(1 2 3 4 5)");
assert_eq!(eval_to_string(&lisp, &mut eval, "(vector-copy #(1 2 3 4 5))"), "#(1 2 3 4 5)");
}
#[test]
fn test_string_copy_with_range() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_string_matches(&lisp, &mut eval, r#"(string-copy "hello" 1 3)"#, "el"));
assert!(eval_string_matches(&lisp, &mut eval, r#"(string-copy "hello")"#, "hello"));
}
#[test]
fn test_float_literal_parsing() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("3.14").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert!((f - 3.14).abs() < 0.001),
v => panic!("expected Float, got {:?}", v),
}
let result = eval.eval_str("-2.5").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert!((f - (-2.5)).abs() < 0.001),
v => panic!("expected Float, got {:?}", v),
}
let result = eval.eval_str("1e3").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert!((f - 1000.0).abs() < 0.001),
v => panic!("expected Float, got {:?}", v),
}
let result = eval.eval_str("1.5e2").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert!((f - 150.0).abs() < 0.001),
v => panic!("expected Float, got {:?}", v),
}
}
#[test]
fn test_special_float_constants() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("+inf.0").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert!(f.is_infinite() && f > 0.0),
v => panic!("expected +inf.0, got {:?}", v),
}
let result = eval.eval_str("-inf.0").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert!(f.is_infinite() && f < 0.0),
v => panic!("expected -inf.0, got {:?}", v),
}
let result = eval.eval_str("+nan.0").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert!(f.is_nan()),
v => panic!("expected +nan.0, got {:?}", v),
}
}
#[test]
fn test_mixed_arithmetic() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(+ 1 2.0)").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert!((f - 3.0).abs() < 0.001),
v => panic!("expected Float for (+ 1 2.0), got {:?}", v),
}
let result = eval.eval_str("(+ 1.5 2)").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert!((f - 3.5).abs() < 0.001),
v => panic!("expected Float for (+ 1.5 2), got {:?}", v),
}
let result = eval.eval_str("(+ 1 2)").unwrap();
match lisp.get(result).unwrap() {
Value::Number(n) => assert_eq!(n, 3),
v => panic!("expected Number for (+ 1 2), got {:?}", v),
}
let result = eval.eval_str("(- 10.5 3)").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert!((f - 7.5).abs() < 0.001),
v => panic!("expected Float, got {:?}", v),
}
let result = eval.eval_str("(* 3 2.5)").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert!((f - 7.5).abs() < 0.001),
v => panic!("expected Float, got {:?}", v),
}
let result = eval.eval_str("(/ 7.0 2)").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert!((f - 3.5).abs() < 0.001),
v => panic!("expected Float, got {:?}", v),
}
}
#[test]
fn test_float_comparisons() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(< 1 2.5)"));
assert!(eval_is_true(&lisp, &mut eval, "(< 1.5 2)"));
assert!(eval_is_false(&lisp, &mut eval, "(< 3.0 2)"));
assert!(eval_is_true(&lisp, &mut eval, "(= 3.0 3)"));
assert!(eval_is_true(&lisp, &mut eval, "(= 3 3.0)"));
assert!(eval_is_true(&lisp, &mut eval, "(>= 3.5 3)"));
assert!(eval_is_true(&lisp, &mut eval, "(<= 2.5 3)"));
}
#[test]
fn test_numeric_predicates_with_floats() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(number? 42)"));
assert!(eval_is_true(&lisp, &mut eval, "(number? 3.14)"));
assert!(eval_is_true(&lisp, &mut eval, "(integer? 42)"));
assert!(eval_is_false(&lisp, &mut eval, "(integer? 3.14)"));
assert!(eval_is_true(&lisp, &mut eval, "(exact? 42)"));
assert!(eval_is_false(&lisp, &mut eval, "(exact? 3.14)"));
assert!(eval_is_false(&lisp, &mut eval, "(inexact? 42)"));
assert!(eval_is_true(&lisp, &mut eval, "(inexact? 3.14)"));
assert!(eval_is_true(&lisp, &mut eval, "(exact-integer? 42)"));
assert!(eval_is_false(&lisp, &mut eval, "(exact-integer? 3.14)"));
assert!(eval_is_true(&lisp, &mut eval, "(zero? 0.0)"));
assert!(eval_is_true(&lisp, &mut eval, "(positive? 1.5)"));
assert!(eval_is_true(&lisp, &mut eval, "(negative? -1.5)"));
assert!(eval_is_true(&lisp, &mut eval, "(real? 42)"));
assert!(eval_is_true(&lisp, &mut eval, "(real? 3.14)"));
assert!(eval_is_true(&lisp, &mut eval, "(complex? 42)"));
assert!(eval_is_true(&lisp, &mut eval, "(finite? 42)"));
assert!(eval_is_true(&lisp, &mut eval, "(finite? 3.14)"));
assert!(eval_is_false(&lisp, &mut eval, "(finite? +inf.0)"));
assert!(eval_is_true(&lisp, &mut eval, "(infinite? +inf.0)"));
assert!(eval_is_true(&lisp, &mut eval, "(infinite? -inf.0)"));
assert!(eval_is_false(&lisp, &mut eval, "(infinite? 42)"));
assert!(eval_is_true(&lisp, &mut eval, "(nan? +nan.0)"));
assert!(eval_is_false(&lisp, &mut eval, "(nan? 42)"));
}
#[test]
fn test_exactness_conversion() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(exact->inexact 42)").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert_eq!(f, 42.0),
v => panic!("expected Float, got {:?}", v),
}
let result = eval.eval_str("(inexact->exact 42.0)").unwrap();
match lisp.get(result).unwrap() {
Value::Number(n) => assert_eq!(n, 42),
v => panic!("expected Number, got {:?}", v),
}
let result = eval.eval_str("(inexact 42)").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert_eq!(f, 42.0),
v => panic!("expected Float, got {:?}", v),
}
let result = eval.eval_str("(exact 42.0)").unwrap();
match lisp.get(result).unwrap() {
Value::Number(n) => assert_eq!(n, 42),
v => panic!("expected Number, got {:?}", v),
}
}
#[test]
fn test_rounding_operations_with_floats() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(floor 3.7)").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert_eq!(f, 3.0),
v => panic!("expected Float, got {:?}", v),
}
let result = eval.eval_str("(floor -3.3)").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert_eq!(f, -4.0),
v => panic!("expected Float, got {:?}", v),
}
let result = eval.eval_str("(ceiling 3.3)").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert_eq!(f, 4.0),
v => panic!("expected Float, got {:?}", v),
}
let result = eval.eval_str("(truncate 3.7)").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert_eq!(f, 3.0),
v => panic!("expected Float, got {:?}", v),
}
let result = eval.eval_str("(truncate -3.7)").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert_eq!(f, -3.0),
v => panic!("expected Float, got {:?}", v),
}
let result = eval.eval_str("(round 3.5)").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert_eq!(f, 4.0),
v => panic!("expected Float, got {:?}", v),
}
assert_eq!(eval_to_num(&lisp, &mut eval, "(floor 5)"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(ceiling 5)"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(truncate 5)"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(round 5)"), 5);
}
#[test]
fn test_sqrt_builtin() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(sqrt 4)"), 2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(sqrt 9)"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(sqrt 0)"), 0);
assert_eq!(eval_to_num(&lisp, &mut eval, "(sqrt 1)"), 1);
let result = eval.eval_str("(sqrt 2)").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert!((f - 1.4142135).abs() < 0.001),
v => panic!("expected Float for (sqrt 2), got {:?}", v),
}
let result = eval.eval_str("(sqrt 2.0)").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert!((f - 1.4142135).abs() < 0.001),
v => panic!("expected Float for (sqrt 2.0), got {:?}", v),
}
}
#[test]
fn test_expt_with_floats() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(expt 2 10)"), 1024);
let result = eval.eval_str("(expt 2.0 3)").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert!((f - 8.0).abs() < 0.001),
v => panic!("expected Float, got {:?}", v),
}
let result = eval.eval_str("(expt 2 -1)").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert!((f - 0.5).abs() < 0.001),
v => panic!("expected Float for (expt 2 -1), got {:?}", v),
}
}
#[test]
fn test_float_display() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_string(&lisp, &mut eval, "(exact->inexact 42)"), "42.0");
assert_eq!(eval_to_string(&lisp, &mut eval, "+inf.0"), "+inf.0");
assert_eq!(eval_to_string(&lisp, &mut eval, "-inf.0"), "-inf.0");
assert_eq!(eval_to_string(&lisp, &mut eval, "+nan.0"), "+nan.0");
}
#[test]
fn test_number_string_conversions_with_floats() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str(r#"(number->string 3.14)"#).unwrap();
let s = format!("{}", lisp.display(result));
assert!(s.contains("3.14"), "expected '3.14' in {}", s);
let result = eval.eval_str(r#"(string->number "3.14")"#).unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert!((f - 3.14).abs() < 0.001),
v => panic!("expected Float, got {:?}", v),
}
let result = eval.eval_str(r#"(string->number "+inf.0")"#).unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert!(f.is_infinite() && f > 0.0),
v => panic!("expected +inf, got {:?}", v),
}
}
#[test]
fn test_float_eq_eqv_equal() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(eqv? 3.0 3.0)"));
assert!(eval_is_false(&lisp, &mut eval, "(eqv? 3 3.0)"));
assert!(eval_is_false(&lisp, &mut eval, "(eqv? 3.0 3)"));
assert!(eval_is_true(&lisp, &mut eval, "(equal? 3.0 3.0)"));
}
#[test]
fn test_inexact_prefix() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("#i42").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert_eq!(f, 42.0),
v => panic!("expected Float for #i42, got {:?}", v),
}
assert_eq!(eval_to_num(&lisp, &mut eval, "#e42"), 42);
}
#[test]
fn test_abs_with_floats() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(abs -3.5)").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert_eq!(f, 3.5),
v => panic!("expected Float, got {:?}", v),
}
assert_eq!(eval_to_num(&lisp, &mut eval, "(abs -5)"), 5);
}
#[test]
fn test_max_min_with_floats() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(max 1 2.5 3)").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert_eq!(f, 3.0),
v => panic!("expected Float for max, got {:?}", v),
}
let result = eval.eval_str("(min 1 2.5 -3.0)").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert_eq!(f, -3.0),
v => panic!("expected Float for min, got {:?}", v),
}
}
#[test]
fn test_square_with_floats() {
let lisp: Lisp<20000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(square 2.5)").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert!((f - 6.25).abs() < 0.001),
v => panic!("expected Float, got {:?}", v),
}
assert_eq!(eval_to_num(&lisp, &mut eval, "(square 5)"), 25);
}
#[test]
fn test_rational_construction() {
let lisp: Lisp<80000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(rational? (make-rat 3 4))"));
assert_eq!(eval_to_num(&lisp, &mut eval, "(rat-numer (make-rat 3 4))"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(rat-denom (make-rat 3 4))"), 4);
assert_eq!(eval_to_num(&lisp, &mut eval, "(rat-numer (make-rat 6 8))"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(rat-denom (make-rat 6 8))"), 4);
assert_eq!(eval_to_num(&lisp, &mut eval, "(rat-numer (make-rat 3 -4))"), -3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(rat-denom (make-rat 3 -4))"), 4);
assert_eq!(eval_to_num(&lisp, &mut eval, "(rat-numer (make-rat 5 1))"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(rat-denom (make-rat 5 1))"), 1);
}
#[test]
fn test_rational_macro() {
let lisp: Lisp<80000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(rat-numer (rat 3 / 4))"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(rat-denom (rat 3 / 4))"), 4);
assert_eq!(eval_to_num(&lisp, &mut eval, "(rat-numer (rat 5))"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(rat-denom (rat 5))"), 1);
}
#[test]
fn test_rational_arithmetic() {
let lisp: Lisp<80000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval,
r#"(equal? (rat->string (rat+ (rat 3 / 4) (rat 2 / 3))) "17/12")"#));
assert!(eval_is_true(&lisp, &mut eval,
r#"(equal? (rat->string (rat- (rat 3 / 4) (rat 2 / 3))) "1/12")"#));
assert!(eval_is_true(&lisp, &mut eval,
r#"(equal? (rat->string (rat* (rat 3 / 4) (rat 2 / 3))) "1/2")"#));
assert!(eval_is_true(&lisp, &mut eval,
r#"(equal? (rat->string (rat/ (rat 3 / 4) (rat 2 / 3))) "9/8")"#));
}
#[test]
fn test_rational_comparison() {
let lisp: Lisp<80000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(rat= (rat 1 / 2) (rat 2 / 4))"));
assert!(eval_is_false(&lisp, &mut eval, "(rat= (rat 1 / 2) (rat 1 / 3))"));
assert!(eval_is_true(&lisp, &mut eval, "(rat< (rat 1 / 3) (rat 1 / 2))"));
assert!(eval_is_false(&lisp, &mut eval, "(rat< (rat 1 / 2) (rat 1 / 3))"));
}
#[test]
fn test_rational_conversion() {
let lisp: Lisp<80000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval,
r#"(equal? (rat->string (rat 3 / 4)) "3/4")"#));
assert!(eval_is_true(&lisp, &mut eval,
r#"(equal? (rat->string (rat 5)) "5")"#));
let result = eval.eval_str("(rat->float (rat 3 / 4))").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert!((f - 0.75).abs() < 0.001),
v => panic!("expected Float for rat->float, got {:?}", v),
}
}
#[test]
fn test_complex_construction() {
let lisp: Lisp<80000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval, "(complex? (make-complex-rect 3 4))"));
assert_eq!(eval_to_num(&lisp, &mut eval, "(complex-real (make-complex-rect 3 4))"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(complex-imag (make-complex-rect 3 4))"), 4);
}
#[test]
fn test_complex_macro() {
let lisp: Lisp<80000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert_eq!(eval_to_num(&lisp, &mut eval, "(complex-real (cpx 3 + 4 i))"), 3);
assert_eq!(eval_to_num(&lisp, &mut eval, "(complex-imag (cpx 3 + 4 i))"), 4);
assert_eq!(eval_to_num(&lisp, &mut eval, "(complex-real (cpx 1 - 2 i))"), 1);
assert_eq!(eval_to_num(&lisp, &mut eval, "(complex-imag (cpx 1 - 2 i))"), -2);
assert_eq!(eval_to_num(&lisp, &mut eval, "(complex-real (cpx 5))"), 5);
assert_eq!(eval_to_num(&lisp, &mut eval, "(complex-imag (cpx 5))"), 0);
}
#[test]
fn test_complex_arithmetic() {
let lisp: Lisp<80000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval,
r#"(equal? (complex->string (complex+ (cpx 3 + 4 i) (cpx 1 - 2 i))) "4+2i")"#));
assert!(eval_is_true(&lisp, &mut eval,
r#"(equal? (complex->string (complex- (cpx 3 + 4 i) (cpx 1 - 2 i))) "2+6i")"#));
assert!(eval_is_true(&lisp, &mut eval,
r#"(equal? (complex->string (complex* (cpx 3 + 4 i) (cpx 1 - 2 i))) "11-2i")"#));
assert!(eval_is_true(&lisp, &mut eval, "(complex= (cpx 3 + 4 i) (cpx 3 + 4 i))"));
assert!(eval_is_false(&lisp, &mut eval, "(complex= (cpx 3 + 4 i) (cpx 1 + 2 i))"));
}
#[test]
fn test_complex_conjugate() {
let lisp: Lisp<80000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval,
r#"(equal? (complex->string (complex-conjugate (cpx 3 + 4 i))) "3-4i")"#));
}
#[test]
fn test_complex_magnitude() {
let lisp: Lisp<80000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
let result = eval.eval_str("(complex-mag (cpx 3 + 4 i))").unwrap();
match lisp.get(result).unwrap() {
Value::Float(f) => assert!((f - 5.0).abs() < 0.01),
Value::Number(n) => assert_eq!(n, 5),
v => panic!("expected number for complex-mag, got {:?}", v),
}
}
#[test]
fn test_complex_to_string() {
let lisp: Lisp<80000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval,
r#"(equal? (complex->string (cpx 5)) "5")"#));
assert!(eval_is_true(&lisp, &mut eval,
r#"(equal? (complex->string (cpx 3 + 4 i)) "3+4i")"#));
assert!(eval_is_true(&lisp, &mut eval,
r#"(equal? (complex->string (cpx 3 - 4 i)) "3-4i")"#));
}
#[test]
fn test_rat_complex_construction() {
let lisp: Lisp<80000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval,
"(rat-complex? (make-rat-complex (make-rat 1 3) (make-rat 2 5)))"));
assert!(eval_is_true(&lisp, &mut eval,
r#"(equal? (rat->string (rat-complex-real (make-rat-complex (make-rat 1 3) (make-rat 2 5)))) "1/3")"#));
assert!(eval_is_true(&lisp, &mut eval,
r#"(equal? (rat->string (rat-complex-imag (make-rat-complex (make-rat 1 3) (make-rat 2 5)))) "2/5")"#));
}
#[test]
fn test_rat_complex_macro() {
let lisp: Lisp<80000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval,
r#"(equal? (rat->string (rat-complex-real (rat-cpx (1 / 3) + (2 / 5) i))) "1/3")"#));
assert!(eval_is_true(&lisp, &mut eval,
r#"(equal? (rat->string (rat-complex-imag (rat-cpx (1 / 3) + (2 / 5) i))) "2/5")"#));
assert!(eval_is_true(&lisp, &mut eval,
r#"(equal? (rat->string (rat-complex-real (rat-cpx (1 / 3)))) "1/3")"#));
assert!(eval_is_true(&lisp, &mut eval,
r#"(equal? (rat->string (rat-complex-imag (rat-cpx (1 / 3)))) "0")"#));
}
#[test]
fn test_rat_complex_arithmetic() {
let lisp: Lisp<80000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval,
r#"(equal? (rat-complex->string (rat-complex+ (rat-cpx (1 / 3) + (2 / 5) i) (rat-cpx (1 / 6) + (1 / 5) i))) "1/2+3/5i")"#));
assert!(eval_is_true(&lisp, &mut eval,
r#"(equal? (rat-complex->string (rat-complex- (rat-cpx (1 / 2) + (3 / 5) i) (rat-cpx (1 / 6) + (1 / 5) i))) "1/3+2/5i")"#));
}
#[test]
fn test_rat_complex_multiplication() {
let lisp: Lisp<80000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval,
r#"(equal? (rat-complex->string (rat-complex* (rat-cpx (1 / 2)) (rat-cpx (1 / 3)))) "1/6")"#));
}
#[test]
fn test_rat_complex_equality() {
let lisp: Lisp<80000> = Lisp::new();
let mut eval = Evaluator::new(&lisp).unwrap();
assert!(eval_is_true(&lisp, &mut eval,
"(rat-complex= (rat-cpx (1 / 3) + (2 / 5) i) (rat-cpx (1 / 3) + (2 / 5) i))"));
assert!(eval_is_false(&lisp, &mut eval,
"(rat-complex= (rat-cpx (1 / 3) + (2 / 5) i) (rat-cpx (1 / 2) + (2 / 5) i))"));
}