use std::collections::BTreeSet;
use crate::env::Env;
use crate::heap::{Address, Heap};
use crate::value::Value;
enum Lookup<'a> {
Dangling,
Found(&'a Value),
}
fn lookup(heap: &Heap, address: Address) -> Lookup<'_> {
heap.load(address).map_or(Lookup::Dangling, Lookup::Found)
}
#[must_use]
pub fn reachable(roots: &[&Value], env: &Env, heap: &Heap) -> BTreeSet<Address> {
let from_env = mark_env(env, heap, BTreeSet::new());
roots
.iter()
.fold(from_env, |acc, &value| mark_value(value, heap, acc))
}
#[must_use]
pub fn collect(roots: &[&Value], env: &Env, heap: &Heap) -> Heap {
heap.retain(&reachable(roots, env, heap))
}
fn mark_env(env: &Env, heap: &Heap, live: BTreeSet<Address>) -> BTreeSet<Address> {
match env {
Env::Empty => live,
Env::Cons { value, rest, .. } => {
let live = mark_value(value, heap, live);
mark_env(rest, heap, live)
}
}
}
fn mark_value(value: &Value, heap: &Heap, live: BTreeSet<Address>) -> BTreeSet<Address> {
match value {
Value::Closure { env: capture, .. } => mark_env(capture, heap, live),
Value::Ref(address) => mark_reference(*address, heap, live),
}
}
fn mark_reference(address: Address, heap: &Heap, live: BTreeSet<Address>) -> BTreeSet<Address> {
if live.contains(&address) {
live
} else {
let extended: BTreeSet<Address> =
live.into_iter().chain(std::iter::once(address)).collect();
match lookup(heap, address) {
Lookup::Dangling => extended,
Lookup::Found(value) => mark_value(value, heap, extended),
}
}
}