use grift_parser::*;
#[test]
fn test_reserved_slots_are_singletons() {
let lisp: Lisp<100> = Lisp::new();
let nil1 = lisp.nil().unwrap();
let nil2 = lisp.nil().unwrap();
let nil3 = lisp.nil().unwrap();
assert_eq!(nil1, nil2);
assert_eq!(nil2, nil3);
let true1 = lisp.true_val().unwrap();
let true2 = lisp.true_val().unwrap();
let true3 = lisp.true_val().unwrap();
assert_eq!(true1, true2);
assert_eq!(true2, true3);
let false1 = lisp.false_val().unwrap();
let false2 = lisp.false_val().unwrap();
let false3 = lisp.false_val().unwrap();
assert_eq!(false1, false2);
assert_eq!(false2, false3);
assert_ne!(nil1, true1);
assert_ne!(nil1, false1);
assert_ne!(true1, false1);
}
#[test]
fn test_reserved_slots_have_correct_values() {
let lisp: Lisp<100> = Lisp::new();
assert_eq!(lisp.get(lisp.nil().unwrap()).unwrap(), Value::Nil);
assert_eq!(lisp.get(lisp.true_val().unwrap()).unwrap(), Value::True);
assert_eq!(lisp.get(lisp.false_val().unwrap()).unwrap(), Value::False);
}
#[test]
fn test_reserved_slots_occupy_first_slots() {
let lisp: Lisp<100> = Lisp::new();
assert_eq!(lisp.nil().unwrap().raw(), 0);
assert_eq!(lisp.void_val().unwrap().raw(), 1);
assert_eq!(lisp.true_val().unwrap().raw(), 2);
assert_eq!(lisp.false_val().unwrap().raw(), 3);
assert_eq!(lisp.get(lisp.nil().unwrap()).unwrap(), Value::Nil);
assert_eq!(lisp.get(lisp.void_val().unwrap()).unwrap(), Value::Void);
assert_eq!(lisp.get(lisp.true_val().unwrap()).unwrap(), Value::True);
assert_eq!(lisp.get(lisp.false_val().unwrap()).unwrap(), Value::False);
}
#[test]
fn test_reserved_slots_not_reallocated() {
let lisp: Lisp<100> = Lisp::new();
assert_eq!(lisp.arena().len(), 6);
let _ = lisp.nil();
let _ = lisp.void_val();
let _ = lisp.true_val();
let _ = lisp.false_val();
assert_eq!(lisp.arena().len(), 6);
for _ in 0..100 {
let _ = lisp.nil();
let _ = lisp.void_val();
let _ = lisp.true_val();
let _ = lisp.false_val();
}
assert_eq!(lisp.arena().len(), 6);
}
#[test]
fn test_boolean_uses_reserved_slots() {
let lisp: Lisp<100> = Lisp::new();
let b_true = lisp.boolean(true).unwrap();
let b_false = lisp.boolean(false).unwrap();
assert_eq!(b_true, lisp.true_val().unwrap());
assert_eq!(b_false, lisp.false_val().unwrap());
}
#[test]
fn test_reserved_slots_survive_gc() {
let lisp: Lisp<100> = Lisp::new();
let nil = lisp.nil().unwrap();
let void_val = lisp.void_val().unwrap();
let true_val = lisp.true_val().unwrap();
let false_val = lisp.false_val().unwrap();
let _ = lisp.number(1);
let _ = lisp.number(2);
let _ = lisp.number(3);
let stats = lisp.gc(&[nil, void_val, true_val, false_val]);
assert_eq!(stats.collected, 3);
assert_eq!(lisp.get(nil).unwrap(), Value::Nil);
assert_eq!(lisp.get(void_val).unwrap(), Value::Void);
assert_eq!(lisp.get(true_val).unwrap(), Value::True);
assert_eq!(lisp.get(false_val).unwrap(), Value::False);
}
#[test]
fn test_regular_allocation_starts_after_reserved_slots() {
let lisp: Lisp<100> = Lisp::new();
let num = lisp.number(42).unwrap();
assert_eq!(num.raw(), 6);
let num2 = lisp.number(43).unwrap();
assert_eq!(num2.raw(), 7);
}
#[test]
fn test_parse_number() {
let lisp: Lisp<100> = Lisp::new();
let idx = parse(&lisp, "42").unwrap();
assert_eq!(lisp.get(idx).unwrap(), Value::Number(42));
let idx = parse(&lisp, "-123").unwrap();
assert_eq!(lisp.get(idx).unwrap(), Value::Number(-123));
}
#[test]
fn test_parse_symbol() {
let lisp: Lisp<100> = Lisp::new();
let idx = parse(&lisp, "hello").unwrap();
assert!(lisp.symbol_matches(idx, "hello").unwrap());
}
#[test]
fn test_parse_empty_list() {
let lisp: Lisp<100> = Lisp::new();
let idx = parse(&lisp, "()").unwrap();
assert_eq!(lisp.get(idx).unwrap(), Value::Nil);
let idx = parse(&lisp, "nil").unwrap();
assert!(lisp.get(idx).unwrap().is_symbol());
}
#[test]
fn test_parse_booleans() {
let lisp: Lisp<100> = Lisp::new();
let idx = parse(&lisp, "#t").unwrap();
assert_eq!(lisp.get(idx).unwrap(), Value::True);
let idx = parse(&lisp, "#f").unwrap();
assert_eq!(lisp.get(idx).unwrap(), Value::False);
let idx = parse(&lisp, "#T").unwrap();
assert_eq!(lisp.get(idx).unwrap(), Value::True);
let idx = parse(&lisp, "#F").unwrap();
assert_eq!(lisp.get(idx).unwrap(), Value::False);
}
#[test]
fn test_parse_list() {
let lisp: Lisp<100> = Lisp::new();
let idx = parse(&lisp, "(1 2 3)").unwrap();
let val = lisp.get(idx).unwrap();
assert!(val.is_cons());
let car = lisp.car(idx).unwrap();
assert_eq!(lisp.get(car).unwrap(), Value::Number(1));
}
#[test]
fn test_parse_nested() {
let lisp: Lisp<100> = Lisp::new();
let idx = parse(&lisp, "(+ 1 (- 3 2))").unwrap();
assert!(lisp.get(idx).unwrap().is_cons());
}
#[test]
fn test_parse_quote() {
let lisp: Lisp<100> = Lisp::new();
let idx = parse(&lisp, "'x").unwrap();
let car = lisp.car(idx).unwrap();
assert!(lisp.symbol_matches(car, "quote").unwrap());
}
#[test]
fn test_symbol_equality() {
let lisp: Lisp<100> = Lisp::new();
let a = lisp.symbol("hello").unwrap();
let b = lisp.symbol("hello").unwrap();
let c = lisp.symbol("world").unwrap();
assert!(lisp.symbol_eq(a, b).unwrap());
assert!(!lisp.symbol_eq(a, c).unwrap());
}
#[test]
fn test_gc() {
let lisp: Lisp<100> = Lisp::new();
let root = parse(&lisp, "(1 2 3)").unwrap();
for i in 0..20 {
lisp.number(i * 1000).unwrap();
}
let stats = lisp.gc(&[root]);
assert!(stats.collected > 0);
}
#[test]
fn test_string_basic() {
let lisp: Lisp<1000> = Lisp::new();
let hello = lisp.string("hello").unwrap();
assert_eq!(lisp.string_len(hello).unwrap(), 5);
assert_eq!(lisp.string_char_at(hello, 0).unwrap(), 'h');
assert_eq!(lisp.string_char_at(hello, 1).unwrap(), 'e');
assert_eq!(lisp.string_char_at(hello, 2).unwrap(), 'l');
assert_eq!(lisp.string_char_at(hello, 3).unwrap(), 'l');
assert_eq!(lisp.string_char_at(hello, 4).unwrap(), 'o');
}
#[test]
fn test_string_empty() {
let lisp: Lisp<100> = Lisp::new();
let empty = lisp.string("").unwrap();
assert_eq!(lisp.string_len(empty).unwrap(), 0);
assert!(lisp.string_char_at(empty, 0).is_err());
}
#[test]
fn test_string_single_char() {
let lisp: Lisp<100> = Lisp::new();
let single = lisp.string("x").unwrap();
assert_eq!(lisp.string_len(single).unwrap(), 1);
assert_eq!(lisp.string_char_at(single, 0).unwrap(), 'x');
assert!(lisp.string_char_at(single, 1).is_err());
}
#[test]
fn test_string_unicode() {
let lisp: Lisp<100> = Lisp::new();
let unicode = lisp.string("héllo").unwrap();
assert_eq!(lisp.string_len(unicode).unwrap(), 5);
assert_eq!(lisp.string_char_at(unicode, 0).unwrap(), 'h');
assert_eq!(lisp.string_char_at(unicode, 1).unwrap(), 'é');
assert_eq!(lisp.string_char_at(unicode, 2).unwrap(), 'l');
}
#[test]
fn test_string_matches() {
let lisp: Lisp<1000> = Lisp::new();
let hello = lisp.string("hello").unwrap();
assert!(lisp.string_matches(hello, "hello").unwrap());
assert!(!lisp.string_matches(hello, "Hello").unwrap());
assert!(!lisp.string_matches(hello, "hello!").unwrap());
assert!(!lisp.string_matches(hello, "hell").unwrap());
assert!(!lisp.string_matches(hello, "").unwrap());
}
#[test]
fn test_string_eq_contiguous() {
let lisp: Lisp<1000> = Lisp::new();
let hello1 = lisp.string("hello").unwrap();
let hello2 = lisp.string("hello").unwrap();
let world = lisp.string("world").unwrap();
assert!(lisp.string_eq_contiguous(hello1, hello2).unwrap());
assert!(lisp.string_eq_contiguous(hello1, hello1).unwrap());
assert!(!lisp.string_eq_contiguous(hello1, world).unwrap());
}
#[test]
fn test_string_to_bytes() {
let lisp: Lisp<1000> = Lisp::new();
let hello = lisp.string("hello").unwrap();
let mut buf = [0u8; 10];
let len = lisp.string_to_bytes(hello, &mut buf).unwrap();
assert_eq!(len, 5);
assert_eq!(&buf[..5], b"hello");
}
#[test]
fn test_string_to_bytes_truncated() {
let lisp: Lisp<1000> = Lisp::new();
let hello = lisp.string("hello").unwrap();
let mut buf = [0u8; 3];
let len = lisp.string_to_bytes(hello, &mut buf).unwrap();
assert_eq!(len, 3);
assert_eq!(&buf[..3], b"hel");
}
#[test]
fn test_string_to_bytes_skips_non_ascii() {
let lisp: Lisp<1000> = Lisp::new();
let mixed = lisp.string("héllo").unwrap();
let mut buf = [0u8; 10];
let len = lisp.string_to_bytes(mixed, &mut buf).unwrap();
assert_eq!(len, 4); assert_eq!(&buf[..4], b"hllo");
}
#[test]
fn test_string_free() {
let lisp: Lisp<100> = Lisp::new();
let initial_allocated = lisp.stats().allocated;
let hello = lisp.string("hello").unwrap();
let after_alloc = lisp.stats().allocated;
assert_eq!(after_alloc - initial_allocated, 6);
lisp.string_free(hello).unwrap();
let after_free = lisp.stats().allocated;
assert_eq!(after_free, initial_allocated);
}
#[test]
fn test_string_multiple() {
let lisp: Lisp<1000> = Lisp::new();
let s1 = lisp.string("foo").unwrap();
let s2 = lisp.string("bar").unwrap();
let s3 = lisp.string("baz").unwrap();
assert!(lisp.string_matches(s1, "foo").unwrap());
assert!(lisp.string_matches(s2, "bar").unwrap());
assert!(lisp.string_matches(s3, "baz").unwrap());
lisp.string_free(s2).unwrap();
assert!(lisp.string_matches(s1, "foo").unwrap());
assert!(lisp.string_matches(s3, "baz").unwrap());
}
#[test]
fn test_string_memory_layout() {
let lisp: Lisp<1000> = Lisp::new();
let hello = lisp.string("hello").unwrap();
match lisp.get(hello).unwrap() {
Value::String { len, data } => {
assert_eq!(len, 5);
let idx0 = lisp.arena().index_at_offset(data, 0).unwrap();
let idx1 = lisp.arena().index_at_offset(data, 1).unwrap();
assert_eq!(lisp.get(idx0).unwrap(), Value::Char('h'));
assert_eq!(lisp.get(idx1).unwrap(), Value::Char('e'));
}
_ => panic!("Expected Value::String"),
}
}
#[test]
fn test_string_char_out_of_bounds() {
let lisp: Lisp<100> = Lisp::new();
let hello = lisp.string("hi").unwrap();
assert!(lisp.string_char_at(hello, 0).is_ok());
assert!(lisp.string_char_at(hello, 1).is_ok());
assert!(lisp.string_char_at(hello, 2).is_err());
assert!(lisp.string_char_at(hello, 100).is_err());
}
#[test]
fn test_set_car() {
let lisp: Lisp<100> = Lisp::new();
let a = lisp.number(1).unwrap();
let b = lisp.number(2).unwrap();
let c = lisp.number(3).unwrap();
let pair = lisp.cons(a, b).unwrap();
assert_eq!(lisp.car(pair).unwrap(), a);
lisp.set_car(pair, c).unwrap();
assert_eq!(lisp.car(pair).unwrap(), c);
assert_eq!(lisp.cdr(pair).unwrap(), b);
}
#[test]
fn test_set_cdr() {
let lisp: Lisp<100> = Lisp::new();
let a = lisp.number(1).unwrap();
let b = lisp.number(2).unwrap();
let c = lisp.number(3).unwrap();
let pair = lisp.cons(a, b).unwrap();
assert_eq!(lisp.cdr(pair).unwrap(), b);
lisp.set_cdr(pair, c).unwrap();
assert_eq!(lisp.cdr(pair).unwrap(), c);
assert_eq!(lisp.car(pair).unwrap(), a);
}
#[test]
fn test_set_car_on_non_pair_fails() {
let lisp: Lisp<100> = Lisp::new();
let num = lisp.number(42).unwrap();
let new_val = lisp.number(99).unwrap();
assert!(lisp.set_car(num, new_val).is_err());
}
#[test]
fn test_set_cdr_on_non_pair_fails() {
let lisp: Lisp<100> = Lisp::new();
let num = lisp.number(42).unwrap();
let new_val = lisp.number(99).unwrap();
assert!(lisp.set_cdr(num, new_val).is_err());
}
#[test]
fn test_symbol_interning_same_name_returns_same_index() {
let lisp: Lisp<1000> = Lisp::new();
let sym1 = lisp.symbol("foo").unwrap();
let sym2 = lisp.symbol("foo").unwrap();
let sym3 = lisp.symbol("foo").unwrap();
assert_eq!(sym1, sym2);
assert_eq!(sym2, sym3);
}
#[test]
fn test_symbol_interning_different_names_return_different_indices() {
let lisp: Lisp<1000> = Lisp::new();
let foo = lisp.symbol("foo").unwrap();
let bar = lisp.symbol("bar").unwrap();
let baz = lisp.symbol("baz").unwrap();
assert_ne!(foo, bar);
assert_ne!(bar, baz);
assert_ne!(foo, baz);
}
#[test]
fn test_symbol_interning_from_bytes() {
let lisp: Lisp<1000> = Lisp::new();
let sym1 = lisp.symbol("test").unwrap();
let sym2 = lisp.symbol_from_bytes(b"test").unwrap();
assert_eq!(sym1, sym2);
}
#[test]
fn test_symbol_interning_preserves_content() {
let lisp: Lisp<1000> = Lisp::new();
let sym = lisp.symbol("hello").unwrap();
assert!(lisp.symbol_matches(sym, "hello").unwrap());
assert!(!lisp.symbol_matches(sym, "world").unwrap());
}
#[test]
fn test_intern_table_is_gc_root() {
let lisp: Lisp<1000> = Lisp::new();
let sym1 = lisp.symbol("a").unwrap();
let sym2 = lisp.symbol("b").unwrap();
let sym3 = lisp.symbol("c").unwrap();
for i in 0..50 {
let _ = lisp.number(i);
}
let empty_roots: &[ArenaIndex] = &[];
lisp.gc(empty_roots);
assert!(lisp.get(sym1).is_ok());
assert!(lisp.get(sym2).is_ok());
assert!(lisp.get(sym3).is_ok());
assert!(lisp.symbol_matches(sym1, "a").unwrap());
assert!(lisp.symbol_matches(sym2, "b").unwrap());
assert!(lisp.symbol_matches(sym3, "c").unwrap());
}
#[test]
fn test_symbol_len() {
let lisp: Lisp<1000> = Lisp::new();
let empty = lisp.symbol("").unwrap();
let short = lisp.symbol("hi").unwrap();
let longer = lisp.symbol("hello world").unwrap();
assert_eq!(lisp.symbol_len(empty).unwrap(), 0);
assert_eq!(lisp.symbol_len(short).unwrap(), 2);
assert_eq!(lisp.symbol_len(longer).unwrap(), 11);
}
#[test]
fn test_symbol_char_at() {
let lisp: Lisp<1000> = Lisp::new();
let hello = lisp.symbol("hello").unwrap();
assert_eq!(lisp.symbol_char_at(hello, 0).unwrap(), Some('h'));
assert_eq!(lisp.symbol_char_at(hello, 1).unwrap(), Some('e'));
assert_eq!(lisp.symbol_char_at(hello, 2).unwrap(), Some('l'));
assert_eq!(lisp.symbol_char_at(hello, 3).unwrap(), Some('l'));
assert_eq!(lisp.symbol_char_at(hello, 4).unwrap(), Some('o'));
assert_eq!(lisp.symbol_char_at(hello, 5).unwrap(), None);
assert_eq!(lisp.symbol_char_at(hello, 100).unwrap(), None);
}
#[test]
fn test_symbol_to_bytes() {
let lisp: Lisp<1000> = Lisp::new();
let hello = lisp.symbol("hello").unwrap();
let mut buf = [0u8; 32];
let len = lisp.symbol_to_bytes(hello, &mut buf).unwrap();
assert_eq!(len, 5);
assert_eq!(&buf[..len], b"hello");
}
#[test]
fn test_contiguous_symbol_uses_less_memory() {
let lisp: Lisp<20000> = Lisp::new();
let initial = lisp.arena().len();
let _sym = lisp.symbol("factorial").unwrap();
let after_symbol = lisp.arena().len();
let slots_used = after_symbol - initial;
assert!(slots_used < 19, "Expected fewer than 19 slots, got {}", slots_used);
}
#[test]
fn test_array_basic() {
let lisp: Lisp<1000> = Lisp::new();
let nil = lisp.nil().unwrap();
let arr = lisp.make_array(5, nil).unwrap();
assert_eq!(lisp.array_len(arr).unwrap(), 5);
for i in 0..5 {
let elem = lisp.array_get(arr, i).unwrap();
assert_eq!(lisp.get(elem).unwrap(), Value::Nil);
}
}
#[test]
fn test_array_empty() {
let lisp: Lisp<1000> = Lisp::new();
let nil = lisp.nil().unwrap();
let arr = lisp.make_array(0, nil).unwrap();
assert_eq!(lisp.array_len(arr).unwrap(), 0);
assert!(lisp.get(arr).unwrap().is_array());
}
#[test]
fn test_array_get_set() {
let lisp: Lisp<1000> = Lisp::new();
let nil = lisp.nil().unwrap();
let arr = lisp.make_array(3, nil).unwrap();
let val1 = lisp.number(42).unwrap();
let val2 = lisp.number(100).unwrap();
lisp.array_set(arr, 0, val1).unwrap();
lisp.array_set(arr, 2, val2).unwrap();
let elem0 = lisp.array_get(arr, 0).unwrap();
let elem1 = lisp.array_get(arr, 1).unwrap();
let elem2 = lisp.array_get(arr, 2).unwrap();
assert_eq!(lisp.get(elem0).unwrap(), Value::Number(42));
assert_eq!(lisp.get(elem1).unwrap(), Value::Nil); assert_eq!(lisp.get(elem2).unwrap(), Value::Number(100));
}
#[test]
fn test_array_out_of_bounds() {
let lisp: Lisp<1000> = Lisp::new();
let nil = lisp.nil().unwrap();
let arr = lisp.make_array(3, nil).unwrap();
assert!(lisp.array_get(arr, 2).is_ok());
assert!(lisp.array_get(arr, 3).is_err());
assert!(lisp.array_get(arr, 100).is_err());
assert!(lisp.array_set(arr, 3, nil).is_err());
}
#[test]
fn test_array_with_different_value_types() {
let lisp: Lisp<1000> = Lisp::new();
let nil = lisp.nil().unwrap();
let arr = lisp.make_array(4, nil).unwrap();
let num = lisp.number(123).unwrap();
let b = lisp.true_val().unwrap();
let sym = lisp.symbol("foo").unwrap();
let pair = lisp.cons(lisp.number(1).unwrap(), lisp.number(2).unwrap()).unwrap();
lisp.array_set(arr, 0, num).unwrap();
lisp.array_set(arr, 1, b).unwrap();
lisp.array_set(arr, 2, sym).unwrap();
lisp.array_set(arr, 3, pair).unwrap();
let elem0 = lisp.array_get(arr, 0).unwrap();
let elem1 = lisp.array_get(arr, 1).unwrap();
let elem2 = lisp.array_get(arr, 2).unwrap();
let elem3 = lisp.array_get(arr, 3).unwrap();
assert!(lisp.get(elem0).unwrap().is_number());
assert!(lisp.get(elem1).unwrap().is_true());
assert!(lisp.get(elem2).unwrap().is_symbol());
assert!(lisp.get(elem3).unwrap().is_cons());
}
#[test]
fn test_array_is_array_predicate() {
let lisp: Lisp<1000> = Lisp::new();
let nil = lisp.nil().unwrap();
let arr = lisp.make_array(3, nil).unwrap();
let num = lisp.number(42).unwrap();
let pair = lisp.cons(nil, nil).unwrap();
assert!(lisp.get(arr).unwrap().is_array());
assert!(!lisp.get(num).unwrap().is_array());
assert!(!lisp.get(pair).unwrap().is_array());
assert!(!lisp.get(nil).unwrap().is_array());
}
#[test]
fn test_array_len_on_non_array() {
let lisp: Lisp<1000> = Lisp::new();
let nil = lisp.nil().unwrap();
assert!(lisp.array_len(nil).is_err());
}
#[test]
fn test_array_free() {
let lisp: Lisp<1000> = Lisp::new();
let nil = lisp.nil().unwrap();
let initial = lisp.arena().len();
let arr = lisp.make_array(5, nil).unwrap();
let after_alloc = lisp.arena().len();
assert_eq!(after_alloc - initial, 6);
lisp.array_free(arr).unwrap();
let after_free = lisp.arena().len();
assert_eq!(after_free, initial);
}
#[test]
fn test_array_memory_layout() {
let lisp: Lisp<1000> = Lisp::new();
let nil = lisp.nil().unwrap();
let initial = lisp.arena().len();
let _arr = lisp.make_array(10, nil).unwrap();
let after = lisp.arena().len();
assert_eq!(after - initial, 11);
}
#[test]
fn test_array_type_name() {
let arr = Value::Array { len: 0, data: ArenaIndex::NIL };
assert_eq!(arr.type_name(), "array");
}
#[test]
fn test_string_type_name() {
let s = Value::String { len: 0, data: ArenaIndex::NIL };
assert_eq!(s.type_name(), "string");
}
#[test]
fn test_string_is_string_predicate() {
let lisp: Lisp<1000> = Lisp::new();
let nil = lisp.nil().unwrap();
let s = lisp.string("hello").unwrap();
let arr = lisp.make_array(3, nil).unwrap();
let num = lisp.number(42).unwrap();
assert!(lisp.get(s).unwrap().is_string());
assert!(!lisp.get(arr).unwrap().is_string());
assert!(!lisp.get(num).unwrap().is_string());
assert!(!lisp.get(nil).unwrap().is_string());
}
#[test]
fn test_string_and_array_consistent_layout() {
let lisp: Lisp<1000> = Lisp::new();
let nil = lisp.nil().unwrap();
let initial = lisp.arena().len();
let s = lisp.string("hello").unwrap();
let after_string = lisp.arena().len();
assert_eq!(after_string - initial, 6);
let arr = lisp.make_array(5, nil).unwrap();
let after_array = lisp.arena().len();
assert_eq!(after_array - after_string, 6);
match lisp.get(s).unwrap() {
Value::String { .. } => assert_eq!(lisp.string_len(s).unwrap(), 5),
_ => panic!("Expected String"),
}
match lisp.get(arr).unwrap() {
Value::Array { .. } => assert_eq!(lisp.array_len(arr).unwrap(), 5),
_ => panic!("Expected Array"),
}
}
#[test]
fn test_string_gc_trace() {
let lisp: Lisp<200> = Lisp::new();
let s = lisp.string("hello world").unwrap();
for i in 0..50 {
lisp.number(i * 1000).unwrap();
}
let stats = lisp.gc(&[s]);
assert!(stats.collected > 0);
assert!(lisp.get(s).unwrap().is_string());
assert_eq!(lisp.string_len(s).unwrap(), 11);
assert!(lisp.string_matches(s, "hello world").unwrap());
}
#[test]
fn test_syntax_object_creation() {
let lisp: Lisp<1000> = Lisp::new();
let x = lisp.symbol("x").unwrap();
let nil = lisp.nil().unwrap();
let stx = lisp.syntax(x, nil, nil).unwrap();
assert!(lisp.get(stx).unwrap().is_syntax());
assert_eq!(lisp.get(stx).unwrap().type_name(), "syntax");
}
#[test]
fn test_syntax_object_parts() {
let lisp: Lisp<1000> = Lisp::new();
let expr = lisp.symbol("test").unwrap();
let mark1 = lisp.symbol("m1").unwrap();
let marks = lisp.cons(mark1, lisp.nil().unwrap()).unwrap();
let subst = lisp.nil().unwrap();
let stx = lisp.syntax(expr, marks, subst).unwrap();
let (extracted_expr, extracted_marks, extracted_subst) = lisp.syntax_parts(stx).unwrap();
assert_eq!(extracted_expr, expr);
assert_eq!(extracted_marks, marks);
assert_eq!(extracted_subst, subst);
}
#[test]
fn test_syntax_to_datum() {
let lisp: Lisp<1000> = Lisp::new();
let expr = lisp.number(42).unwrap();
let nil = lisp.nil().unwrap();
let stx = lisp.syntax(expr, nil, nil).unwrap();
let datum = lisp.syntax_to_datum(stx).unwrap();
assert_eq!(datum, expr);
assert_eq!(lisp.get(datum).unwrap().as_number(), Some(42));
}
#[test]
fn test_syntax_to_datum_passthrough() {
let lisp: Lisp<1000> = Lisp::new();
let num = lisp.number(100).unwrap();
let result = lisp.syntax_to_datum(num).unwrap();
assert_eq!(result, num);
let sym = lisp.symbol("hello").unwrap();
let result = lisp.syntax_to_datum(sym).unwrap();
assert_eq!(result, sym);
}
#[test]
fn test_syntax_object_with_list() {
let lisp: Lisp<1000> = Lisp::new();
let plus = lisp.symbol("+").unwrap();
let one = lisp.number(1).unwrap();
let two = lisp.number(2).unwrap();
let list = lisp.list([plus, one, two]).unwrap();
let nil = lisp.nil().unwrap();
let stx = lisp.syntax(list, nil, nil).unwrap();
let datum = lisp.syntax_to_datum(stx).unwrap();
assert!(lisp.get(datum).unwrap().is_cons());
let first = lisp.car(datum).unwrap();
assert!(lisp.symbol_matches(first, "+").unwrap());
}
#[test]
fn test_syntax_object_gc() {
let lisp: Lisp<1000> = Lisp::new();
let expr = lisp.symbol("test-gc").unwrap();
let mark = lisp.symbol("mark1").unwrap();
let marks = lisp.cons(mark, lisp.nil().unwrap()).unwrap();
let subst = lisp.nil().unwrap();
let stx = lisp.syntax(expr, marks, subst).unwrap();
for i in 0..50 {
lisp.number(i * 100).unwrap();
}
let stats = lisp.gc(&[stx]);
assert!(stats.collected > 0);
assert!(lisp.get(stx).unwrap().is_syntax());
let (extracted_expr, _, _) = lisp.syntax_parts(stx).unwrap();
assert!(lisp.symbol_matches(extracted_expr, "test-gc").unwrap());
}
#[test]
fn test_cont_frame_creation() {
let lisp: Lisp<100> = Lisp::new();
let nil = lisp.nil().unwrap();
let env = lisp.nil().unwrap();
let cont = lisp.cont_frame(0, nil, nil, env).unwrap();
assert!(lisp.get(cont).unwrap().is_cont_frame());
}
#[test]
fn test_cont_frame_parts_extraction() {
let lisp: Lisp<100> = Lisp::new();
let nil = lisp.nil().unwrap();
let env = lisp.symbol("test-env").unwrap();
let cont = lisp.cont_frame(0, nil, nil, env).unwrap();
let (cont_type, data, parent, extracted_env) = lisp.cont_frame_parts(cont).unwrap();
assert_eq!(cont_type, 0);
assert_eq!(data, nil);
assert_eq!(parent, nil);
assert_eq!(extracted_env, env);
}
#[test]
fn test_cont_frame_with_data() {
let lisp: Lisp<100> = Lisp::new();
let nil = lisp.nil().unwrap();
let then_expr = lisp.symbol("then").unwrap();
let else_expr = lisp.symbol("else").unwrap();
let data = lisp.cons(then_expr, else_expr).unwrap();
let env = lisp.nil().unwrap();
let cont = lisp.cont_frame(2, data, nil, env).unwrap();
let (cont_type, extracted_data, parent, _) = lisp.cont_frame_parts(cont).unwrap();
assert_eq!(cont_type, 2);
assert_eq!(extracted_data, data);
assert_eq!(parent, nil);
let extracted_then = lisp.car(extracted_data).unwrap();
let extracted_else = lisp.cdr(extracted_data).unwrap();
assert!(lisp.symbol_matches(extracted_then, "then").unwrap());
assert!(lisp.symbol_matches(extracted_else, "else").unwrap());
}
#[test]
fn test_cont_frame_linked_list() {
let lisp: Lisp<100> = Lisp::new();
let nil = lisp.nil().unwrap();
let env = lisp.nil().unwrap();
let done_cont = lisp.cont_frame(0, nil, nil, env).unwrap();
let apply_cont = lisp.cont_frame(1, nil, done_cont, env).unwrap();
let if_cont = lisp.cont_frame(2, nil, apply_cont, env).unwrap();
let (if_type, _, if_parent, _) = lisp.cont_frame_parts(if_cont).unwrap();
assert_eq!(if_type, 2);
assert_eq!(if_parent, apply_cont);
let (apply_type, _, apply_parent, _) = lisp.cont_frame_parts(if_parent).unwrap();
assert_eq!(apply_type, 1);
assert_eq!(apply_parent, done_cont);
let (done_type, _, done_parent, _) = lisp.cont_frame_parts(apply_parent).unwrap();
assert_eq!(done_type, 0);
assert_eq!(done_parent, nil); }
#[test]
fn test_cont_frame_parent_helper() {
let lisp: Lisp<100> = Lisp::new();
let nil = lisp.nil().unwrap();
let env = lisp.nil().unwrap();
let done_cont = lisp.cont_frame(0, nil, nil, env).unwrap();
let child_cont = lisp.cont_frame(1, nil, done_cont, env).unwrap();
let parent = lisp.cont_frame_parent(child_cont).unwrap();
assert_eq!(parent, done_cont);
let done_parent = lisp.cont_frame_parent(done_cont).unwrap();
assert_eq!(done_parent, nil);
}
#[test]
fn test_cont_frame_gc_survival() {
let lisp: Lisp<500> = Lisp::new();
let nil = lisp.nil().unwrap();
let env = lisp.nil().unwrap();
let done_cont = lisp.cont_frame(0, nil, nil, env).unwrap();
let child_cont = lisp.cont_frame(1, nil, done_cont, env).unwrap();
for i in 0..50 {
let _ = lisp.number(i * 100).unwrap();
}
let stats = lisp.gc(&[child_cont]);
assert!(stats.collected > 0);
assert!(lisp.get(child_cont).unwrap().is_cont_frame());
assert!(lisp.get(done_cont).unwrap().is_cont_frame());
let parent = lisp.cont_frame_parent(child_cont).unwrap();
assert_eq!(parent, done_cont);
}
#[test]
fn test_cont_frame_type_name() {
let lisp: Lisp<100> = Lisp::new();
let nil = lisp.nil().unwrap();
let cont = lisp.cont_frame(0, nil, nil, nil).unwrap();
assert_eq!(lisp.get(cont).unwrap().type_name(), "cont-frame");
}
#[test]
fn test_continuation_creation() {
let lisp: Lisp<200> = Lisp::new();
let nil = lisp.nil().unwrap();
let env = nil;
let cont = lisp.continuation(nil, env, nil).unwrap();
assert!(lisp.get(cont).unwrap().is_continuation());
assert_eq!(lisp.get(cont).unwrap().type_name(), "continuation");
}
#[test]
fn test_continuation_parts_extraction() {
let lisp: Lisp<200> = Lisp::new();
let nil = lisp.nil().unwrap();
let cont_chain = nil; let sym_a = lisp.symbol("a").unwrap();
let num_42 = lisp.number(42).unwrap();
let capture_env = lisp.cons(sym_a, num_42).unwrap();
let dw_chain = nil;
let cont = lisp.continuation(cont_chain, capture_env, dw_chain).unwrap();
let (extracted_chain, extracted_env, extracted_dw) = lisp.continuation_parts(cont).unwrap();
assert_eq!(extracted_chain, cont_chain);
assert_eq!(extracted_env, capture_env);
assert_eq!(extracted_dw, dw_chain);
}
#[test]
fn test_continuation_with_cont_frame_chain() {
let lisp: Lisp<500> = Lisp::new();
let nil = lisp.nil().unwrap();
let env = nil;
let frame1 = lisp.cont_frame(0, nil, nil, env).unwrap(); let frame2 = lisp.cont_frame(1, nil, frame1, env).unwrap(); let frame3 = lisp.cont_frame(2, nil, frame2, env).unwrap();
let cont = lisp.continuation(frame3, env, nil).unwrap();
let (extracted_chain, _, _) = lisp.continuation_parts(cont).unwrap();
assert_eq!(extracted_chain, frame3);
let (type3, _, parent3, _) = lisp.cont_frame_parts(extracted_chain).unwrap();
assert_eq!(type3, 2);
let (type2, _, parent2, _) = lisp.cont_frame_parts(parent3).unwrap();
assert_eq!(type2, 1);
let (type1, _, parent1, _) = lisp.cont_frame_parts(parent2).unwrap();
assert_eq!(type1, 0);
assert!(parent1.is_nil()); }
#[test]
fn test_continuation_gc_survival() {
let lisp: Lisp<500> = Lisp::new();
let nil = lisp.nil().unwrap();
let sym_x = lisp.symbol("x").unwrap();
let num_1 = lisp.number(1).unwrap();
let env = lisp.cons(sym_x, num_1).unwrap();
let frame = lisp.cont_frame(0, nil, nil, env).unwrap();
let cont = lisp.continuation(frame, env, nil).unwrap();
for _ in 0..50 {
lisp.cons(nil, nil).unwrap();
}
let stats = lisp.gc(&[cont]);
assert!(stats.collected > 0);
assert!(lisp.get(cont).unwrap().is_continuation());
let (chain, _, _) = lisp.continuation_parts(cont).unwrap();
assert!(lisp.get(chain).unwrap().is_cont_frame());
}
#[test]
fn test_block_comment_basic() {
let lisp: Lisp<100> = Lisp::new();
let idx = parse(&lisp, "#| comment |# 42").unwrap();
assert_eq!(lisp.get(idx).unwrap(), Value::Number(42));
}
#[test]
fn test_block_comment_nested() {
let lisp: Lisp<100> = Lisp::new();
let idx = parse(&lisp, "#| outer #| inner |# still outer |# 7").unwrap();
assert_eq!(lisp.get(idx).unwrap(), Value::Number(7));
}
#[test]
fn test_block_comment_inline() {
let lisp: Lisp<200> = Lisp::new();
let idx = parse(&lisp, "(+ 1 #| skip |# 2)").unwrap();
let (car, cdr) = lisp.car_cdr(idx).unwrap();
assert!(lisp.symbol_matches(car, "+").unwrap());
let (one, rest) = lisp.car_cdr(cdr).unwrap();
assert_eq!(lisp.get(one).unwrap(), Value::Number(1));
let (two, nil) = lisp.car_cdr(rest).unwrap();
assert_eq!(lisp.get(two).unwrap(), Value::Number(2));
assert!(lisp.get(nil).unwrap().is_nil());
}
#[test]
fn test_block_comment_multiline() {
let lisp: Lisp<100> = Lisp::new();
let idx = parse(&lisp, "#|\nmultiline\ncomment\n|# 99").unwrap();
assert_eq!(lisp.get(idx).unwrap(), Value::Number(99));
}
#[test]
fn test_datum_comment_number() {
let lisp: Lisp<200> = Lisp::new();
let idx = parse(&lisp, "#; 42 99").unwrap();
assert_eq!(lisp.get(idx).unwrap(), Value::Number(99));
}
#[test]
fn test_datum_comment_list() {
let lisp: Lisp<200> = Lisp::new();
let idx = parse(&lisp, "#; (a b c) 55").unwrap();
assert_eq!(lisp.get(idx).unwrap(), Value::Number(55));
}
#[test]
fn test_datum_comment_symbol() {
let lisp: Lisp<200> = Lisp::new();
let idx = parse(&lisp, "#; foo bar").unwrap();
assert!(lisp.symbol_matches(idx, "bar").unwrap());
}
#[test]
fn test_datum_comment_in_list() {
let lisp: Lisp<500> = Lisp::new();
let idx = parse(&lisp, "(1 #; 2 3)").unwrap();
let (car, cdr) = lisp.car_cdr(idx).unwrap();
assert_eq!(lisp.get(car).unwrap(), Value::Number(1));
let (car2, nil) = lisp.car_cdr(cdr).unwrap();
assert_eq!(lisp.get(car2).unwrap(), Value::Number(3));
assert!(lisp.get(nil).unwrap().is_nil());
}
#[test]
fn test_bytevector_empty() {
let lisp: Lisp<200> = Lisp::new();
let idx = parse(&lisp, "#u8()").unwrap();
assert!(lisp.get(idx).unwrap().is_bytevector());
assert_eq!(lisp.bytevector_len(idx).unwrap(), 0);
}
#[test]
fn test_bytevector_basic() {
let lisp: Lisp<200> = Lisp::new();
let idx = parse(&lisp, "#u8(0 10 5)").unwrap();
assert!(lisp.get(idx).unwrap().is_bytevector());
assert_eq!(lisp.bytevector_len(idx).unwrap(), 3);
let b0 = lisp.bytevector_get(idx, 0).unwrap();
assert_eq!(lisp.get(b0).unwrap(), Value::Number(0));
let b1 = lisp.bytevector_get(idx, 1).unwrap();
assert_eq!(lisp.get(b1).unwrap(), Value::Number(10));
let b2 = lisp.bytevector_get(idx, 2).unwrap();
assert_eq!(lisp.get(b2).unwrap(), Value::Number(5));
}
#[test]
fn test_bytevector_display() {
let lisp: Lisp<200> = Lisp::new();
let idx = parse(&lisp, "#u8(1 2 3)").unwrap();
let display = format!("{}", lisp.display(idx));
assert_eq!(display, "#u8(1 2 3)");
}
#[test]
fn test_bytevector_empty_display() {
let lisp: Lisp<200> = Lisp::new();
let idx = parse(&lisp, "#u8()").unwrap();
let display = format!("{}", lisp.display(idx));
assert_eq!(display, "#u8()");
}
#[test]
fn test_fold_case_default() {
let lisp: Lisp<100> = Lisp::new();
let idx = parse(&lisp, "Hello").unwrap();
assert!(lisp.symbol_matches(idx, "hello").unwrap());
}
#[test]
fn test_no_fold_case() {
let lisp: Lisp<100> = Lisp::new();
let idx = parse(&lisp, "#!no-fold-case Hello").unwrap();
assert!(lisp.symbol_matches(idx, "Hello").unwrap());
}
#[test]
fn test_fold_case_restore() {
let lisp: Lisp<200> = Lisp::new();
let idx = parse(&lisp, "#!no-fold-case #!fold-case Hello").unwrap();
assert!(lisp.symbol_matches(idx, "hello").unwrap());
}