use gc_lang::{Gc, Heap, Trace, Tracer};
enum Value {
Number(f64),
Pair(Gc<Value>, Gc<Value>),
Closure { captures: Vec<Gc<Value>> },
}
impl Trace for Value {
fn trace(&self, tracer: &mut Tracer<'_>) {
match self {
Value::Number(_) => {}
Value::Pair(car, cdr) => {
tracer.mark(*car);
tracer.mark(*cdr);
}
Value::Closure { captures } => {
for &capture in captures {
tracer.mark(capture);
}
}
}
}
}
fn build_list(heap: &mut Heap<Value>) -> Gc<Value> {
let nil = heap.alloc(Value::Number(0.0)); let three = heap.alloc(Value::Number(3.0));
let two = heap.alloc(Value::Number(2.0));
let one = heap.alloc(Value::Number(1.0));
let tail = heap.alloc(Value::Pair(three, nil));
let mid = heap.alloc(Value::Pair(two, tail));
heap.alloc(Value::Pair(one, mid))
}
fn sum_list(heap: &Heap<Value>, mut node: Gc<Value>) -> f64 {
let mut total = 0.0;
while let Some(Value::Pair(car, cdr)) = heap.get(node) {
if let Some(Value::Number(n)) = heap.get(*car) {
total += n;
}
node = *cdr;
}
total
}
fn main() {
let mut heap: Heap<Value> = Heap::with_capacity(64);
let mut stack: Vec<Gc<Value>> = Vec::new();
let list = build_list(&mut heap);
println!("list (1 2 3) sums to {}", sum_list(&heap, list));
stack.push(list);
let closure = heap.alloc(Value::Closure {
captures: vec![list],
});
stack.push(closure);
for n in 0..100 {
let _ = heap.alloc(Value::Number(f64::from(n)));
}
println!("before gc: {} objects live", heap.len());
let stats = heap.collect(stack.iter().copied());
println!(
"after gc: {} objects live, {} reclaimed",
stats.live, stats.freed
);
assert!(heap.get(list).is_some());
assert!(heap.get(closure).is_some());
let _ = stack.pop();
let stats = heap.collect(stack.iter().copied());
println!("dropped the closure, reclaimed {} more", stats.freed);
assert!(heap.get(closure).is_none());
assert!(heap.get(list).is_some());
stack.clear();
let stats = heap.collect(stack.iter().copied());
println!("cleared the stack, reclaimed {} more", stats.freed);
assert!(heap.is_empty());
}