#[cfg(test)]
mod tests {
use crate::scheme::environment::Environment;
use crate::scheme::evaluator::Evaluator;
use crate::scheme::parser::Parser;
use crate::scheme::value::Value;
fn eval_string(code: &str) -> Value {
let mut parser = Parser::new(code);
let expr = parser.parse().expect("Parse error");
let env = Environment::new_global();
let mut evaluator = Evaluator::new();
evaluator.eval(expr, env).expect("Eval error")
}
#[test]
fn test_eval_integer() {
let result = eval_string("42");
assert!(matches!(result, Value::Integer(42)));
}
#[test]
fn test_eval_string() {
let result = eval_string(r#""hello""#);
assert!(result.is_string());
}
#[test]
fn test_eval_boolean() {
let result = eval_string("#t");
assert!(result.is_true());
let result = eval_string("#f");
assert!(!result.is_true());
}
#[test]
fn test_eval_hex_numbers() {
let result = eval_string("#xff");
assert!(matches!(result, Value::Integer(255)));
let result = eval_string("#x10");
assert!(matches!(result, Value::Integer(16)));
let result = eval_string("(+ #x10 #x20)");
assert!(matches!(result, Value::Integer(48))); }
#[test]
fn test_eval_octal_numbers() {
let result = eval_string("#o77");
assert!(matches!(result, Value::Integer(63)));
let result = eval_string("#o10");
assert!(matches!(result, Value::Integer(8)));
let result = eval_string("(+ #o10 #o20)");
assert!(matches!(result, Value::Integer(24))); }
#[test]
fn test_eval_binary_numbers() {
let result = eval_string("#b1010");
assert!(matches!(result, Value::Integer(10)));
let result = eval_string("#b1111");
assert!(matches!(result, Value::Integer(15)));
let result = eval_string("(+ #b10 #b100)");
assert!(matches!(result, Value::Integer(6))); }
#[test]
fn test_eval_quote() {
let result = eval_string("(quote (1 2 3))");
assert!(result.is_list());
}
#[test]
fn test_eval_quote_shorthand() {
let result = eval_string("'(1 2 3)");
assert!(result.is_list());
}
#[test]
fn test_eval_define_and_lookup() {
let mut parser = Parser::new("(define x 42)");
let define_expr = parser.parse().unwrap();
let env = Environment::new_global();
let mut evaluator = Evaluator::new();
evaluator.eval(define_expr, env.clone()).unwrap();
let mut parser = Parser::new("x");
let lookup_expr = parser.parse().unwrap();
let result = evaluator.eval(lookup_expr, env).unwrap();
assert!(matches!(result, Value::Integer(42)));
}
#[test]
fn test_eval_car() {
let result = eval_string("(car '(1 2 3))");
assert!(matches!(result, Value::Integer(1)));
}
#[test]
fn test_eval_cdr() {
let result = eval_string("(cdr '(1 2 3))");
assert!(result.is_list());
}
#[test]
fn test_eval_cons() {
let result = eval_string("(cons 1 2)");
assert!(result.is_pair());
}
#[test]
fn test_eval_list() {
let result = eval_string("(list 1 2 3)");
assert!(result.is_list());
}
#[test]
fn test_eval_length() {
let result = eval_string("(length '(1 2 3 4 5))");
assert!(matches!(result, Value::Integer(5)));
}
#[test]
fn test_eval_append() {
let result = eval_string("(append '(1 2) '(3 4))");
assert!(result.is_list());
let code = "(length (append '(1 2) '(3 4)))";
let result = eval_string(code);
assert!(matches!(result, Value::Integer(4)));
}
#[test]
fn test_eval_reverse() {
let result = eval_string("(car (reverse '(1 2 3)))");
assert!(matches!(result, Value::Integer(3)));
}
#[test]
fn test_eval_list_ref() {
let result = eval_string("(list-ref '(10 20 30) 1)");
assert!(matches!(result, Value::Integer(20)));
}
#[test]
fn test_eval_null_p() {
let result = eval_string("(null? '())");
assert!(result.is_true());
let result = eval_string("(null? '(1 2 3))");
assert!(!result.is_true());
}
#[test]
fn test_eval_pair_p() {
let result = eval_string("(pair? '(1 2))");
assert!(result.is_true());
let result = eval_string("(pair? '())");
assert!(!result.is_true());
}
#[test]
fn test_eval_list_p() {
let result = eval_string("(list? '(1 2 3))");
assert!(result.is_true());
let result = eval_string("(list? '())");
assert!(result.is_true());
}
#[test]
fn test_eval_if() {
let result = eval_string("(if #t 1 2)");
assert!(matches!(result, Value::Integer(1)));
let result = eval_string("(if #f 1 2)");
assert!(matches!(result, Value::Integer(2)));
}
#[test]
fn test_eval_cond() {
let code = r#"
(cond
(#f 1)
(#t 2)
(else 3))
"#;
let result = eval_string(code);
assert!(matches!(result, Value::Integer(2)));
}
#[test]
fn test_eval_case() {
let code = r#"
(case 'b
((a) 1)
((b) 2)
((c) 3))
"#;
let result = eval_string(code);
assert!(matches!(result, Value::Integer(2)));
let code = r#"
(case 3
((1 2) 'first)
((3 4) 'second)
((5 6) 'third))
"#;
let result = eval_string(code);
if let Value::Symbol(ref sym) = result {
assert_eq!(&**sym, "second");
} else {
panic!("Expected symbol 'second");
}
let code = r#"
(case 99
((1 2) 'first)
((3 4) 'second)
(else 'default))
"#;
let result = eval_string(code);
if let Value::Symbol(ref sym) = result {
assert_eq!(&**sym, "default");
} else {
panic!("Expected symbol 'default");
}
let code = r#"
(case 99
((1 2) 'first)
((3 4) 'second))
"#;
let env = Environment::new_global();
let mut evaluator = Evaluator::new();
let mut parser = Parser::new(code);
let expr = parser.parse().unwrap();
let result = evaluator.eval(expr, env);
assert!(result.is_err(), "Expected error when no case clause matches");
let code = r#"
(case (car '(a b c))
((x y) 'not-this)
((a b) 'this-one)
(else 'fallback))
"#;
let result = eval_string(code);
if let Value::Symbol(ref sym) = result {
assert_eq!(&**sym, "this-one");
} else {
panic!("Expected symbol 'this-one");
}
}
#[test]
fn test_eval_let() {
let code = r#"
(let ((x 1)
(y 2))
(cons x y))
"#;
let result = eval_string(code);
assert!(result.is_pair());
}
#[test]
fn test_eval_let_star() {
let code = r#"
(let* ((x 1)
(y (car '(2 3))))
(cons x y))
"#;
let result = eval_string(code);
assert!(result.is_pair());
}
#[test]
fn test_eval_nested_list_operations() {
let result = eval_string("(car (cdr (cdr '(1 2 3 4))))");
assert!(matches!(result, Value::Integer(3)));
}
#[test]
fn test_eval_multiline_let_the_critical_test() {
let code = r#"
(let ((x 1)
(y 2)
(z 3))
(list x y z))
"#;
let result = eval_string(code);
assert!(result.is_list());
let len_code = r#"
(length (let ((x 1)
(y 2)
(z 3))
(list x y z)))
"#;
let len_result = eval_string(len_code);
assert!(matches!(len_result, Value::Integer(3)));
}
#[test]
fn test_eval_begin() {
let code = r#"
(begin
(cons 1 2)
(list 1 2 3)
42)
"#;
let result = eval_string(code);
assert!(matches!(result, Value::Integer(42)));
}
#[test]
fn test_eval_arithmetic() {
let result = eval_string("(+ 1 2 3)");
assert!(matches!(result, Value::Integer(6)));
let result = eval_string("(- 10 3)");
assert!(matches!(result, Value::Integer(7)));
let result = eval_string("(* 2 3 4)");
assert!(matches!(result, Value::Integer(24)));
let result = eval_string("(/ 10 2)");
assert!(result.is_real());
}
#[test]
fn test_eval_comparison() {
let result = eval_string("(< 1 2 3)");
assert!(result.is_true());
let result = eval_string("(> 3 2 1)");
assert!(result.is_true());
let result = eval_string("(= 5 5)");
assert!(result.is_true());
let result = eval_string("(<= 1 2 2)");
assert!(result.is_true());
let result = eval_string("(>= 3 2 2)");
assert!(result.is_true());
}
#[test]
fn test_eval_number_predicates() {
let result = eval_string("(number? 42)");
assert!(result.is_true());
let result = eval_string("(integer? 42)");
assert!(result.is_true());
let result = eval_string("(zero? 0)");
assert!(result.is_true());
let result = eval_string("(positive? 5)");
assert!(result.is_true());
let result = eval_string("(negative? -5)");
assert!(result.is_true());
let result = eval_string("(odd? 3)");
assert!(result.is_true());
let result = eval_string("(even? 4)");
assert!(result.is_true());
}
#[test]
fn test_eval_math_functions() {
let result = eval_string("(abs -5)");
assert!(matches!(result, Value::Integer(5)));
let result = eval_string("(max 1 5 3)");
assert!(matches!(result, Value::Integer(5)));
let result = eval_string("(min 5 1 3)");
assert!(matches!(result, Value::Integer(1)));
let result = eval_string("(floor 3.7)");
assert!(matches!(result, Value::Integer(3)));
let result = eval_string("(ceiling 3.2)");
assert!(matches!(result, Value::Integer(4)));
}
#[test]
fn test_eval_complex_arithmetic() {
let result = eval_string("(+ (* 2 3) (- 10 5))");
assert!(matches!(result, Value::Integer(11)));
let code = r#"
(let ((x 10)
(y 5))
(+ x y))
"#;
let result = eval_string(code);
assert!(matches!(result, Value::Integer(15)));
let code = "(if (< 1 2) 100 200)";
let result = eval_string(code);
assert!(matches!(result, Value::Integer(100)));
}
#[test]
fn test_eval_arithmetic_with_lists() {
let code = "(+ (length '(1 2 3)) (length '(4 5)))";
let result = eval_string(code);
assert!(matches!(result, Value::Integer(5)));
let code = "(list-ref '(10 20 30) (- 2 1))";
let result = eval_string(code);
assert!(matches!(result, Value::Integer(20))); }
#[test]
fn test_eval_string_operations() {
let result = eval_string(r#"(string-length "hello")"#);
assert!(matches!(result, Value::Integer(5)));
let result = eval_string(r#"(string-append "hello" " " "world")"#);
assert!(result.is_string());
let result = eval_string(r#"(substring "hello" 1 4)"#);
assert!(result.is_string());
}
#[test]
fn test_eval_string_comparison() {
let result = eval_string(r#"(string=? "hello" "hello")"#);
assert!(result.is_true());
let result = eval_string(r#"(string<? "abc" "def")"#);
assert!(result.is_true());
let result = eval_string(r#"(string>? "xyz" "abc")"#);
assert!(result.is_true());
}
#[test]
fn test_eval_string_predicates() {
let result = eval_string(r#"(string? "hello")"#);
assert!(result.is_true());
let result = eval_string(r#"(symbol? 'foo)"#);
assert!(result.is_true());
let result = eval_string(r#"(char? #\a)"#);
assert!(result.is_true());
}
#[test]
fn test_eval_string_conversions() {
let result = eval_string(r#"(symbol->string 'foo)"#);
assert!(result.is_string());
let result = eval_string(r#"(string->symbol "bar")"#);
assert!(matches!(result, Value::Symbol(_)));
let result = eval_string(r#"(string->list "hi")"#);
assert!(result.is_list());
let result = eval_string(r#"(list->string '(#\h #\i))"#);
assert!(result.is_string());
}
#[test]
fn test_eval_char_operations() {
let result = eval_string(r#"(char=? #\a #\a)"#);
assert!(result.is_true());
let result = eval_string(r#"(char<? #\a #\b)"#);
assert!(result.is_true());
let result = eval_string(r#"(char-upcase #\a)"#);
assert!(matches!(result, Value::Char('A')));
let result = eval_string(r#"(char-downcase #\Z)"#);
assert!(matches!(result, Value::Char('z')));
}
#[test]
fn test_eval_complex_string_operations() {
let code = r#"
(let ((s1 "hello")
(s2 "world"))
(string-append s1 " " s2))
"#;
let result = eval_string(code);
assert!(result.is_string());
let code = r#"(< (string-length "hi") (string-length "hello"))"#;
let result = eval_string(code);
assert!(result.is_true());
let code = r#"(list->string (string->list "test"))"#;
let result = eval_string(code);
assert!(result.is_string());
}
#[test]
fn test_eval_lambda_simple() {
let code = "((lambda (x) x) 42)";
let result = eval_string(code);
assert!(matches!(result, Value::Integer(42)));
}
#[test]
fn test_eval_lambda_with_operations() {
let code = "((lambda (x y) (+ x y)) 10 20)";
let result = eval_string(code);
assert!(matches!(result, Value::Integer(30)));
}
#[test]
fn test_eval_lambda_closure() {
let code = r#"
(let ((x 10))
((lambda (y) (+ x y)) 20))
"#;
let result = eval_string(code);
assert!(matches!(result, Value::Integer(30)));
}
#[test]
fn test_eval_define_function_shorthand() {
let code = r#"
(begin
(define (add x y) (+ x y))
(add 5 7))
"#;
let result = eval_string(code);
assert!(matches!(result, Value::Integer(12)));
}
#[test]
fn test_eval_lambda_nested_closures() {
let code = r#"
(let ((make-adder (lambda (x) (lambda (y) (+ x y)))))
(let ((add5 (make-adder 5)))
(add5 10)))
"#;
let result = eval_string(code);
assert!(matches!(result, Value::Integer(15)));
}
#[test]
fn test_eval_lambda_with_list_operations() {
let code = "((lambda (lst) (car (cdr lst))) '(1 2 3))";
let result = eval_string(code);
assert!(matches!(result, Value::Integer(2)));
}
#[test]
fn test_eval_lambda_multiple_body_expressions() {
let code = r#"
((lambda (x)
(+ x 1)
(+ x 2)
(+ x 3))
10)
"#;
let result = eval_string(code);
assert!(matches!(result, Value::Integer(13))); }
#[test]
fn test_eval_higher_order_function() {
let code = r#"
(begin
(define (apply-twice f x)
(f (f x)))
(define (inc x) (+ x 1))
(apply-twice inc 10))
"#;
let result = eval_string(code);
assert!(matches!(result, Value::Integer(12)));
}
#[test]
fn test_eval_lambda_with_conditionals() {
let code = r#"
((lambda (x)
(if (< x 10)
"small"
"large"))
5)
"#;
let result = eval_string(code);
assert!(result.is_string());
}
#[test]
fn test_eval_recursive_function() {
let code = r#"
(begin
(define (factorial n)
(if (<= n 1)
1
(* n (factorial (- n 1)))))
(factorial 5))
"#;
let result = eval_string(code);
assert!(matches!(result, Value::Integer(120)));
}
#[test]
fn test_eval_letrec_simple() {
let code = r#"
(letrec ((fact (lambda (n)
(if (<= n 1)
1
(* n (fact (- n 1)))))))
(fact 5))
"#;
let result = eval_string(code);
assert!(matches!(result, Value::Integer(120)));
}
#[test]
fn test_eval_letrec_mutually_recursive() {
let code = r#"
(letrec ((even? (lambda (n)
(if (= n 0)
#t
(odd? (- n 1)))))
(odd? (lambda (n)
(if (= n 0)
#f
(even? (- n 1))))))
(even? 10))
"#;
let result = eval_string(code);
assert!(result.is_true());
}
#[test]
fn test_eval_map_simple() {
let code = "(map (lambda (x) (* x 2)) '(1 2 3))";
let result = eval_string(code);
assert!(result.is_list());
let code = "(car (map (lambda (x) (* x 2)) '(1 2 3)))";
let result = eval_string(code);
assert!(matches!(result, Value::Integer(2)));
}
#[test]
fn test_eval_map_with_defined_function() {
let code = r#"
(begin
(define (square x) (* x x))
(map square '(1 2 3 4)))
"#;
let result = eval_string(code);
assert!(result.is_list());
let code = r#"
(begin
(define (square x) (* x x))
(length (map square '(1 2 3 4))))
"#;
let result = eval_string(code);
assert!(matches!(result, Value::Integer(4)));
}
#[test]
fn test_eval_apply_with_list() {
let code = "(apply + '(1 2 3 4 5))";
let result = eval_string(code);
assert!(matches!(result, Value::Integer(15)));
}
#[test]
fn test_eval_apply_with_lambda() {
let code = "(apply (lambda (x y) (+ x y)) '(10 20))";
let result = eval_string(code);
assert!(matches!(result, Value::Integer(30)));
}
#[test]
fn test_eval_for_each() {
let code = "(for-each (lambda (x) (+ x 1)) '(1 2 3))";
let result = eval_string(code);
assert!(matches!(result, Value::Unspecified));
}
#[test]
fn test_eval_map_nested() {
let code = r#"
(map (lambda (x) (+ x 1))
(map (lambda (x) (* x 2))
'(1 2 3)))
"#;
let result = eval_string(code);
assert!(result.is_list());
let code = r#"
(car (map (lambda (x) (+ x 1))
(map (lambda (x) (* x 2))
'(1 2 3))))
"#;
let result = eval_string(code);
assert!(matches!(result, Value::Integer(3)));
}
#[test]
fn test_eval_complex_higher_order() {
let code = r#"
(begin
(define (compose f g)
(lambda (x) (f (g x))))
(define (inc x) (+ x 1))
(define (double x) (* x 2))
(define inc-then-double (compose double inc))
(map inc-then-double '(1 2 3)))
"#;
let result = eval_string(code);
assert!(result.is_list());
let code = r#"
(begin
(define (compose f g)
(lambda (x) (f (g x))))
(define (inc x) (+ x 1))
(define (double x) (* x 2))
(define inc-then-double (compose double inc))
(car (map inc-then-double '(1 2 3))))
"#;
let result = eval_string(code);
assert!(matches!(result, Value::Integer(4)));
}
#[test]
fn test_eval_not() {
let result = eval_string("(not #t)");
assert!(!result.is_true());
let result = eval_string("(not #f)");
assert!(result.is_true());
let result = eval_string("(not 0)");
assert!(!result.is_true());
let result = eval_string(r#"(not "")"#);
assert!(!result.is_true());
let result = eval_string("(not '())");
assert!(!result.is_true());
}
#[test]
fn test_eval_boolean_p() {
let result = eval_string("(boolean? #t)");
assert!(result.is_true());
let result = eval_string("(boolean? #f)");
assert!(result.is_true());
let result = eval_string("(boolean? 42)");
assert!(!result.is_true());
let result = eval_string(r#"(boolean? "hello")"#);
assert!(!result.is_true());
let result = eval_string("(boolean? '())");
assert!(!result.is_true());
}
#[test]
fn test_eval_equal_p() {
let result = eval_string("(equal? 42 42)");
assert!(result.is_true());
let result = eval_string("(equal? 42 43)");
assert!(!result.is_true());
let result = eval_string(r#"(equal? "hello" "hello")"#);
assert!(result.is_true());
let result = eval_string(r#"(equal? "hello" "world")"#);
assert!(!result.is_true());
let result = eval_string("(equal? '(1 2 3) '(1 2 3))");
assert!(result.is_true());
let result = eval_string("(equal? '(1 2 3) '(1 2 4))");
assert!(!result.is_true());
let result = eval_string("(equal? '(1 (2 3) 4) '(1 (2 3) 4))");
assert!(result.is_true());
let result = eval_string("(equal? '(1 (2 3) 4) '(1 (2 4) 4))");
assert!(!result.is_true());
}
#[test]
fn test_eval_eqv_p() {
let result = eval_string("(eqv? 42 42)");
assert!(result.is_true());
let result = eval_string("(eqv? 42 43)");
assert!(!result.is_true());
let result = eval_string("(eqv? 'foo 'foo)");
assert!(result.is_true());
let result = eval_string("(eqv? 'foo 'bar)");
assert!(!result.is_true());
let result = eval_string("(eqv? #t #t)");
assert!(result.is_true());
let result = eval_string("(eqv? #t #f)");
assert!(!result.is_true());
let result = eval_string(r#"(eqv? 42 "42")"#);
assert!(!result.is_true());
}
#[test]
fn test_eval_eq_p() {
let result = eval_string("(eq? 42 42)");
assert!(result.is_true());
let result = eval_string("(eq? 'foo 'foo)");
assert!(result.is_true());
let result = eval_string(r#"
(let ((x '(1 2 3)))
(eq? x x))
"#);
assert!(result.is_true());
let result = eval_string("(eq? '(1 2 3) '(1 2 3))");
assert!(!result.is_true());
}
#[test]
fn test_eval_procedure_p() {
let result = eval_string("(procedure? +)");
assert!(result.is_true());
let result = eval_string("(procedure? car)");
assert!(result.is_true());
let result = eval_string("(procedure? (lambda (x) x))");
assert!(result.is_true());
let result = eval_string("(procedure? 42)");
assert!(!result.is_true());
let result = eval_string(r#"(procedure? "hello")"#);
assert!(!result.is_true());
let result = eval_string("(procedure? '(1 2 3))");
assert!(!result.is_true());
}
#[test]
fn test_eval_boolean_logic_combinations() {
let result = eval_string("(not (< 5 3))");
assert!(result.is_true());
let result = eval_string("(equal? (+ 2 2) (* 2 2))");
assert!(result.is_true());
let code = r#"
(if (and (not (null? '(1 2 3)))
(equal? (car '(1 2 3)) 1))
"yes"
"no")
"#;
let result = eval_string(code);
assert!(result.is_string());
}
#[test]
fn test_eval_display() {
let result = eval_string(r#"(display "hello")"#);
assert!(matches!(result, Value::Unspecified));
let result = eval_string("(display 42)");
assert!(matches!(result, Value::Unspecified));
}
#[test]
fn test_eval_newline() {
let result = eval_string("(newline)");
assert!(matches!(result, Value::Unspecified));
}
#[test]
fn test_eval_write() {
let result = eval_string(r#"(write "test")"#);
assert!(matches!(result, Value::Unspecified));
let result = eval_string("(write '(1 2 3))");
assert!(matches!(result, Value::Unspecified));
}
#[test]
fn test_eval_display_sequence() {
let code = r#"
(begin
(display "Hello, ")
(display "World!")
(newline))
"#;
let result = eval_string(code);
assert!(matches!(result, Value::Unspecified));
}
#[test]
fn test_load_simple_file() {
use std::fs;
use tempfile::TempDir;
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("test.scm");
fs::write(&file_path, "(+ 10 32)").unwrap();
let code = format!(r#"(load "{}")"#, file_path.display());
let result = eval_string(&code);
assert!(matches!(result, Value::Integer(42)));
}
#[test]
fn test_load_multiple_expressions() {
use std::fs;
use tempfile::TempDir;
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("multi.scm");
let content = r#"
(define x 10)
(define y 20)
(+ x y)
"#;
fs::write(&file_path, content).unwrap();
let code = format!(r#"(load "{}")"#, file_path.display());
let result = eval_string(&code);
assert!(matches!(result, Value::Integer(30)));
}
#[test]
fn test_load_with_definitions() {
use std::fs;
use tempfile::TempDir;
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("defs.scm");
let content = r#"
(define (square x) (* x x))
(square 7)
"#;
fs::write(&file_path, content).unwrap();
let code = format!(r#"(load "{}")"#, file_path.display());
let result = eval_string(&code);
assert!(matches!(result, Value::Integer(49)));
}
#[test]
fn test_load_file_not_found() {
let result = std::panic::catch_unwind(|| {
eval_string(r#"(load "/nonexistent/file.scm")"#)
});
assert!(result.is_err());
}
#[test]
fn test_load_invalid_argument() {
let result = std::panic::catch_unwind(|| {
eval_string("(load 123)")
});
assert!(result.is_err());
}
#[test]
fn test_load_empty_file() {
use std::fs;
use tempfile::TempDir;
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("empty.scm");
fs::write(&file_path, "").unwrap();
let code = format!(r#"(load "{}")"#, file_path.display());
let result = eval_string(&code);
assert!(matches!(result, Value::Unspecified));
}
#[test]
#[ignore] fn bench_vm_vs_tree_walker() {
use std::time::Instant;
let code = r#"
(begin
(define (factorial n)
(if (<= n 1)
1
(* n (factorial (- n 1)))))
(define (fib n)
(if (<= n 1)
n
(+ (fib (- n 1)) (fib (- n 2)))))
;; Run multiple operations
(begin
(factorial 10)
(fib 15)
(+ (factorial 5) (fib 10))
(* (factorial 7) (fib 12))))
"#;
let mut parser = Parser::new(code);
let expr = parser.parse().expect("Parse error");
println!("\n=== VM vs Tree-Walker Performance Benchmark ===\n");
println!("Running tree-walker benchmark (10 iterations)...");
let tree_walker_start = Instant::now();
for _ in 0..10 {
let mut evaluator = Evaluator::new();
evaluator.disable_vm(); let env = Environment::new_global();
evaluator.eval(expr.clone(), env).expect("Eval error");
}
let tree_walker_duration = tree_walker_start.elapsed();
let tree_walker_avg = tree_walker_duration.as_micros() / 10;
println!("Tree-walker: {} μs total, {} μs avg per run",
tree_walker_duration.as_micros(), tree_walker_avg);
println!("\nRunning VM benchmark (10 iterations)...");
let vm_start = Instant::now();
for _ in 0..10 {
let mut evaluator = Evaluator::new();
evaluator.enable_vm(); let env = Environment::new_global();
evaluator.eval(expr.clone(), env).expect("Eval error");
}
let vm_duration = vm_start.elapsed();
let vm_avg = vm_duration.as_micros() / 10;
println!("VM: {} μs total, {} μs avg per run",
vm_duration.as_micros(), vm_avg);
let speedup = tree_walker_avg as f64 / vm_avg as f64;
println!("\n=== Results ===");
println!("Speedup: {:.2}x", speedup);
if speedup > 1.0 {
println!("✓ VM is {:.2}x faster than tree-walker", speedup);
} else {
println!("✗ VM is {:.2}x slower than tree-walker (overhead: {:.2}x)",
1.0 / speedup, 1.0 / speedup);
}
let mut eval_tree = Evaluator::new();
eval_tree.disable_vm();
let result_tree = eval_tree.eval(expr.clone(), Environment::new_global()).unwrap();
let mut eval_vm = Evaluator::new();
eval_vm.enable_vm();
let result_vm = eval_vm.eval(expr, Environment::new_global()).unwrap();
assert!(matches!(result_tree, Value::Integer(_)));
assert!(matches!(result_vm, Value::Integer(_)));
println!("\n✓ Both implementations produce identical results");
}
#[test]
#[ignore] fn bench_vm_simple_arithmetic() {
use std::time::Instant;
let code = "(+ (* 2 3) (- 10 5) (* 4 (+ 1 2)))";
let mut parser = Parser::new(code);
let expr = parser.parse().expect("Parse error");
println!("\n=== Simple Arithmetic Benchmark ===\n");
let tree_walker_start = Instant::now();
for _ in 0..1000 {
let mut evaluator = Evaluator::new();
evaluator.disable_vm();
let env = Environment::new_global();
evaluator.eval(expr.clone(), env).expect("Eval error");
}
let tree_walker_duration = tree_walker_start.elapsed();
let tree_walker_avg = tree_walker_duration.as_nanos() / 1000;
println!("Tree-walker: {} ns avg per eval", tree_walker_avg);
let vm_start = Instant::now();
for _ in 0..1000 {
let mut evaluator = Evaluator::new();
evaluator.enable_vm();
let env = Environment::new_global();
evaluator.eval(expr.clone(), env).expect("Eval error");
}
let vm_duration = vm_start.elapsed();
let vm_avg = vm_duration.as_nanos() / 1000;
println!("VM: {} ns avg per eval", vm_avg);
let speedup = tree_walker_avg as f64 / vm_avg as f64;
println!("\nSpeedup: {:.2}x", speedup);
}
#[test]
#[ignore] fn bench_vm_list_operations() {
use std::time::Instant;
let code = r#"
(begin
(define lst '(1 2 3 4 5 6 7 8 9 10))
(define (sum lst)
(if (null? lst)
0
(+ (car lst) (sum (cdr lst)))))
(+ (sum lst)
(length (append lst lst))
(car (reverse lst))))
"#;
let mut parser = Parser::new(code);
let expr = parser.parse().expect("Parse error");
println!("\n=== List Operations Benchmark ===\n");
let tree_walker_start = Instant::now();
for _ in 0..100 {
let mut evaluator = Evaluator::new();
evaluator.disable_vm();
let env = Environment::new_global();
evaluator.eval(expr.clone(), env).expect("Eval error");
}
let tree_walker_duration = tree_walker_start.elapsed();
let tree_walker_avg = tree_walker_duration.as_micros() / 100;
println!("Tree-walker: {} μs avg per eval", tree_walker_avg);
let vm_start = Instant::now();
for _ in 0..100 {
let mut evaluator = Evaluator::new();
evaluator.enable_vm();
let env = Environment::new_global();
evaluator.eval(expr.clone(), env).expect("Eval error");
}
let vm_duration = vm_start.elapsed();
let vm_avg = vm_duration.as_micros() / 100;
println!("VM: {} μs avg per eval", vm_avg);
let speedup = tree_walker_avg as f64 / vm_avg as f64;
println!("\nSpeedup: {:.2}x", speedup);
}
}