use crate::object::RtObject;
use std::collections::{BTreeSet, HashMap, VecDeque};
#[allow(dead_code)]
pub struct LambdaLifter {
next_id: u32,
lifted: Vec<LiftedLambda>,
}
#[allow(dead_code)]
impl LambdaLifter {
pub fn new() -> Self {
Self {
next_id: 0,
lifted: Vec::new(),
}
}
pub fn lift(&mut self, free_vars: Vec<String>, arity: u32) -> LiftedLambda {
let id = self.next_id;
self.next_id += 1;
let lifted = LiftedLambda {
id,
free_vars,
arity,
};
self.lifted.push(lifted.clone());
lifted
}
pub fn len(&self) -> usize {
self.lifted.len()
}
pub fn is_empty(&self) -> bool {
self.lifted.is_empty()
}
pub fn all(&self) -> &[LiftedLambda] {
&self.lifted
}
pub fn total_free_vars(&self) -> usize {
self.lifted.iter().map(|l| l.free_vars.len()).sum()
}
}
#[derive(Clone, Debug)]
pub enum PapResult {
UnderApplied(Pap),
Saturated {
closure: Closure,
args: Vec<RtObject>,
},
OverApplied {
closure: Closure,
args: Vec<RtObject>,
remaining_args: Vec<RtObject>,
},
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct BatchForceResult {
pub forced: usize,
pub failed: usize,
pub already_evaluated: usize,
}
#[allow(dead_code)]
pub struct ClosureArena {
closures: Vec<Option<Closure>>,
free: Vec<usize>,
alloc_count: u64,
free_count: u64,
}
#[allow(dead_code)]
impl ClosureArena {
pub fn new() -> Self {
Self {
closures: Vec::new(),
free: Vec::new(),
alloc_count: 0,
free_count: 0,
}
}
pub fn alloc(&mut self, c: Closure) -> ClosureHandle {
self.alloc_count += 1;
if let Some(idx) = self.free.pop() {
self.closures[idx] = Some(c);
ClosureHandle(idx)
} else {
let idx = self.closures.len();
self.closures.push(Some(c));
ClosureHandle(idx)
}
}
pub fn free(&mut self, h: ClosureHandle) {
if let Some(slot) = self.closures.get_mut(h.0) {
if slot.take().is_some() {
self.free.push(h.0);
self.free_count += 1;
}
}
}
pub fn get(&self, h: ClosureHandle) -> Option<&Closure> {
self.closures.get(h.0)?.as_ref()
}
pub fn get_mut(&mut self, h: ClosureHandle) -> Option<&mut Closure> {
self.closures.get_mut(h.0)?.as_mut()
}
pub fn live_count(&self) -> usize {
self.closures.iter().filter(|s| s.is_some()).count()
}
pub fn capacity(&self) -> usize {
self.closures.len()
}
pub fn alloc_count(&self) -> u64 {
self.alloc_count
}
pub fn free_count(&self) -> u64 {
self.free_count
}
pub fn iter(&self) -> impl Iterator<Item = (ClosureHandle, &Closure)> {
self.closures
.iter()
.enumerate()
.filter_map(|(i, s)| s.as_ref().map(|c| (ClosureHandle(i), c)))
}
}
#[derive(Clone, Debug)]
pub struct CallInfo {
pub convention: CallConvention,
pub fn_ptr: FnPtr,
pub num_args: u16,
pub is_recursive: bool,
pub caller_name: Option<String>,
pub callee_name: Option<String>,
}
impl CallInfo {
pub fn new(convention: CallConvention, fn_ptr: FnPtr, num_args: u16) -> Self {
CallInfo {
convention,
fn_ptr,
num_args,
is_recursive: false,
caller_name: None,
callee_name: None,
}
}
}
#[allow(dead_code)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ClosureHandle(pub usize);
#[derive(Clone, Debug)]
pub struct ClosureConversionResult {
pub closure: Closure,
pub captured_vars: Vec<String>,
pub eta_expanded: bool,
pub has_mutual_rec: bool,
}
impl ClosureConversionResult {
pub fn new(closure: Closure) -> Self {
ClosureConversionResult {
closure,
captured_vars: Vec::new(),
eta_expanded: false,
has_mutual_rec: false,
}
}
pub fn add_captured(&mut self, var: String) {
self.captured_vars.push(var);
}
}
#[allow(dead_code)]
pub struct EnvGraph {
edges: HashMap<u32, Vec<u32>>,
node_count: u32,
}
#[allow(dead_code)]
impl EnvGraph {
pub fn new() -> Self {
Self {
edges: HashMap::new(),
node_count: 0,
}
}
pub fn add_node(&mut self) -> u32 {
let id = self.node_count;
self.node_count += 1;
self.edges.insert(id, Vec::new());
id
}
pub fn add_edge(&mut self, from: u32, to: u32) {
self.edges.entry(from).or_default().push(to);
}
pub fn captures(&self, id: u32) -> &[u32] {
self.edges.get(&id).map(|v| v.as_slice()).unwrap_or(&[])
}
pub fn transitive_captures(&self, id: u32) -> Vec<u32> {
let mut visited = std::collections::HashSet::new();
let mut stack = vec![id];
let mut result = Vec::new();
while let Some(curr) = stack.pop() {
for &dep in self.captures(curr) {
if visited.insert(dep) {
result.push(dep);
stack.push(dep);
}
}
}
result
}
pub fn has_cycle(&self) -> bool {
for &start in self.edges.keys() {
if self.transitive_captures(start).contains(&start) {
return true;
}
}
false
}
pub fn node_count(&self) -> u32 {
self.node_count
}
pub fn edge_count(&self) -> usize {
self.edges.values().map(|v| v.len()).sum()
}
}
pub struct CallStack {
frames: Vec<StackFrame>,
max_depth: usize,
}
impl CallStack {
pub fn new() -> Self {
CallStack {
frames: Vec::new(),
max_depth: 10_000,
}
}
pub fn with_max_depth(max_depth: usize) -> Self {
CallStack {
frames: Vec::new(),
max_depth,
}
}
pub fn push(&mut self, frame: StackFrame) -> Result<(), StackOverflow> {
if self.frames.len() >= self.max_depth {
return Err(StackOverflow {
depth: self.frames.len(),
max_depth: self.max_depth,
});
}
self.frames.push(frame);
Ok(())
}
pub fn pop(&mut self) -> Option<StackFrame> {
self.frames.pop()
}
pub fn current(&self) -> Option<&StackFrame> {
self.frames.last()
}
pub fn current_mut(&mut self) -> Option<&mut StackFrame> {
self.frames.last_mut()
}
pub fn depth(&self) -> usize {
self.frames.len()
}
pub fn is_empty(&self) -> bool {
self.frames.is_empty()
}
pub fn trace(&self) -> Vec<String> {
self.frames
.iter()
.rev()
.map(|f| f.name.clone().unwrap_or_else(|| format!("{}", f.fn_ptr)))
.collect()
}
pub fn set_max_depth(&mut self, depth: usize) {
self.max_depth = depth;
}
}
pub struct ClosureBuilder {
fn_ptr: FnPtr,
arity: u16,
env: Vec<RtObject>,
name: Option<String>,
is_recursive: bool,
is_known: bool,
}
impl ClosureBuilder {
pub fn new(fn_ptr: FnPtr, arity: u16) -> Self {
ClosureBuilder {
fn_ptr,
arity,
env: Vec::new(),
name: None,
is_recursive: false,
is_known: false,
}
}
pub fn name(mut self, name: String) -> Self {
self.name = Some(name);
self
}
pub fn capture(mut self, value: RtObject) -> Self {
self.env.push(value);
self
}
pub fn capture_many(mut self, values: Vec<RtObject>) -> Self {
self.env.extend(values);
self
}
pub fn recursive(mut self) -> Self {
self.is_recursive = true;
self
}
pub fn known(mut self) -> Self {
self.is_known = true;
self
}
pub fn build(self) -> Closure {
Closure {
fn_ptr: self.fn_ptr,
arity: self.arity,
env: self.env,
name: self.name,
is_recursive: self.is_recursive,
is_known: self.is_known,
}
}
}
#[allow(dead_code)]
pub struct PapQueue {
queue: std::collections::VecDeque<Pap>,
max_size: usize,
enqueue_count: u64,
saturated_count: u64,
}
#[allow(dead_code)]
impl PapQueue {
pub fn new(max_size: usize) -> Self {
Self {
queue: std::collections::VecDeque::new(),
max_size,
enqueue_count: 0,
saturated_count: 0,
}
}
pub fn enqueue(&mut self, pap: Pap) -> bool {
if self.queue.len() >= self.max_size {
return false;
}
self.queue.push_back(pap);
self.enqueue_count += 1;
true
}
pub fn dequeue(&mut self) -> Option<Pap> {
self.queue.pop_front()
}
pub fn apply_front(&mut self, args: Vec<RtObject>) -> Option<PapResult> {
let pap = self.dequeue()?;
let result = pap.apply(&args);
if matches!(result, PapResult::Saturated { .. }) {
self.saturated_count += 1;
}
Some(result)
}
pub fn len(&self) -> usize {
self.queue.len()
}
pub fn is_empty(&self) -> bool {
self.queue.is_empty()
}
pub fn enqueue_count(&self) -> u64 {
self.enqueue_count
}
pub fn saturated_count(&self) -> u64 {
self.saturated_count
}
}
#[derive(Clone, Debug)]
pub struct Pap {
pub closure: Closure,
pub args: Vec<RtObject>,
}
impl Pap {
pub fn new(closure: Closure, args: Vec<RtObject>) -> Self {
Pap { closure, args }
}
pub fn remaining_arity(&self) -> u16 {
self.closure.arity.saturating_sub(self.args.len() as u16)
}
pub fn total_arity(&self) -> u16 {
self.closure.arity
}
pub fn num_applied(&self) -> usize {
self.args.len()
}
pub fn is_saturated(&self) -> bool {
self.args.len() >= self.closure.arity as usize
}
pub fn apply(&self, new_args: &[RtObject]) -> PapResult {
let total_args = self.args.len() + new_args.len();
let arity = self.closure.arity as usize;
if total_args < arity {
let mut all_args = self.args.clone();
all_args.extend_from_slice(new_args);
PapResult::UnderApplied(Pap::new(self.closure.clone(), all_args))
} else if total_args == arity {
let mut all_args = self.args.clone();
all_args.extend_from_slice(new_args);
PapResult::Saturated {
closure: self.closure.clone(),
args: all_args,
}
} else {
let mut exact_args = self.args.clone();
let needed = arity - self.args.len();
exact_args.extend_from_slice(&new_args[..needed]);
let remaining = new_args[needed..].to_vec();
PapResult::OverApplied {
closure: self.closure.clone(),
args: exact_args,
remaining_args: remaining,
}
}
}
pub fn all_args(&self) -> Vec<RtObject> {
let mut args = self.closure.env.clone();
args.extend(self.args.iter().cloned());
args
}
}
#[allow(dead_code)]
#[derive(Clone, Debug, Default)]
pub struct ClosureOptReport {
pub inlined_paps: usize,
pub specialized_arities: usize,
pub env_reduced_vars: usize,
pub removed_dead_closures: usize,
}
#[allow(dead_code)]
impl ClosureOptReport {
pub fn has_changes(&self) -> bool {
self.inlined_paps > 0
|| self.specialized_arities > 0
|| self.env_reduced_vars > 0
|| self.removed_dead_closures > 0
}
pub fn total(&self) -> usize {
self.inlined_paps
+ self.specialized_arities
+ self.env_reduced_vars
+ self.removed_dead_closures
}
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct InlineCandidate {
pub fn_id: u32,
pub call_site_count: u32,
pub env_size: usize,
pub arity: u32,
}
#[allow(dead_code)]
impl InlineCandidate {
pub fn inline_score(&self) -> f64 {
self.env_size as f64 / (self.call_site_count as f64 + 1.0)
}
pub fn is_leaf_candidate(&self) -> bool {
self.env_size <= 4 && self.arity <= 3
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct FnPtr {
pub index: u32,
pub module_id: u16,
}
impl FnPtr {
pub fn new(index: u32) -> Self {
FnPtr {
index,
module_id: 0,
}
}
pub fn with_module(index: u32, module_id: u16) -> Self {
FnPtr { index, module_id }
}
pub fn null() -> Self {
FnPtr {
index: u32::MAX,
module_id: 0,
}
}
pub fn is_null(&self) -> bool {
self.index == u32::MAX
}
}
#[derive(Clone, Debug)]
pub struct StackFrame {
pub fn_ptr: FnPtr,
pub locals: Vec<RtObject>,
pub args: Vec<RtObject>,
pub env: Vec<RtObject>,
pub return_ip: u32,
pub name: Option<String>,
}
impl StackFrame {
pub fn new(fn_ptr: FnPtr, args: Vec<RtObject>, env: Vec<RtObject>, frame_size: usize) -> Self {
StackFrame {
fn_ptr,
locals: vec![RtObject::unit(); frame_size],
args,
env,
return_ip: 0,
name: None,
}
}
pub fn get_local(&self, index: usize) -> Option<&RtObject> {
self.locals.get(index)
}
pub fn set_local(&mut self, index: usize, value: RtObject) -> bool {
if index < self.locals.len() {
self.locals[index] = value;
true
} else {
false
}
}
pub fn get_arg(&self, index: usize) -> Option<&RtObject> {
self.args.get(index)
}
pub fn get_env(&self, index: usize) -> Option<&RtObject> {
self.env.get(index)
}
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct LiftedLambda {
pub id: u32,
pub free_vars: Vec<String>,
pub arity: u32,
}
#[allow(dead_code)]
pub struct CallInliner {
candidates: Vec<InlineCandidate>,
inlined_count: u64,
}
#[allow(dead_code)]
impl CallInliner {
pub fn new() -> Self {
Self {
candidates: Vec::new(),
inlined_count: 0,
}
}
pub fn register(&mut self, candidate: InlineCandidate) {
self.candidates.push(candidate);
}
pub fn top_candidates(&self, n: usize) -> Vec<&InlineCandidate> {
let mut sorted: Vec<&InlineCandidate> = self.candidates.iter().collect();
sorted.sort_by(|a, b| {
a.inline_score()
.partial_cmp(&b.inline_score())
.unwrap_or(std::cmp::Ordering::Equal)
});
sorted.truncate(n);
sorted
}
pub fn record_inline(&mut self, fn_id: u32) {
self.candidates.retain(|c| c.fn_id != fn_id);
self.inlined_count += 1;
}
pub fn inlined_count(&self) -> u64 {
self.inlined_count
}
pub fn candidate_count(&self) -> usize {
self.candidates.len()
}
}
#[derive(Clone, Debug)]
pub struct FunctionEntry {
pub name: String,
pub arity: u16,
pub env_size: u16,
pub convention: CallConvention,
pub is_recursive: bool,
pub is_builtin: bool,
pub is_inlined: bool,
pub frame_size: u16,
pub param_names: Vec<String>,
}
impl FunctionEntry {
pub fn new(name: String, arity: u16) -> Self {
FunctionEntry {
name,
arity,
env_size: 0,
convention: CallConvention::ClosureCall,
is_recursive: false,
is_builtin: false,
is_inlined: false,
frame_size: 0,
param_names: Vec::new(),
}
}
pub fn builtin(name: String, arity: u16) -> Self {
FunctionEntry {
name,
arity,
env_size: 0,
convention: CallConvention::BuiltinCall,
is_recursive: false,
is_builtin: true,
is_inlined: false,
frame_size: 0,
param_names: Vec::new(),
}
}
}
#[derive(Clone, Debug)]
pub struct StackOverflow {
pub depth: usize,
pub max_depth: usize,
}
pub struct FunctionTable {
pub(super) entries: Vec<FunctionEntry>,
name_index: HashMap<String, u32>,
}
impl FunctionTable {
pub fn new() -> Self {
FunctionTable {
entries: Vec::new(),
name_index: HashMap::new(),
}
}
pub fn with_capacity(cap: usize) -> Self {
FunctionTable {
entries: Vec::with_capacity(cap),
name_index: HashMap::with_capacity(cap),
}
}
pub fn register(&mut self, entry: FunctionEntry) -> FnPtr {
let index = self.entries.len() as u32;
self.name_index.insert(entry.name.clone(), index);
self.entries.push(entry);
FnPtr::new(index)
}
pub fn get(&self, ptr: FnPtr) -> Option<&FunctionEntry> {
self.entries.get(ptr.index as usize)
}
pub fn get_by_name(&self, name: &str) -> Option<(FnPtr, &FunctionEntry)> {
self.name_index
.get(name)
.and_then(|&idx| self.entries.get(idx as usize).map(|e| (FnPtr::new(idx), e)))
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = (FnPtr, &FunctionEntry)> {
self.entries
.iter()
.enumerate()
.map(|(i, e)| (FnPtr::new(i as u32), e))
}
pub fn register_builtins(&mut self) {
self.register(FunctionEntry::builtin("Nat.add".to_string(), 2));
self.register(FunctionEntry::builtin("Nat.sub".to_string(), 2));
self.register(FunctionEntry::builtin("Nat.mul".to_string(), 2));
self.register(FunctionEntry::builtin("Nat.div".to_string(), 2));
self.register(FunctionEntry::builtin("Nat.mod".to_string(), 2));
self.register(FunctionEntry::builtin("Nat.beq".to_string(), 2));
self.register(FunctionEntry::builtin("Nat.ble".to_string(), 2));
self.register(FunctionEntry::builtin("Bool.and".to_string(), 2));
self.register(FunctionEntry::builtin("Bool.or".to_string(), 2));
self.register(FunctionEntry::builtin("Bool.not".to_string(), 1));
self.register(FunctionEntry::builtin("String.append".to_string(), 2));
self.register(FunctionEntry::builtin("String.length".to_string(), 1));
self.register(FunctionEntry::builtin("String.mk".to_string(), 1));
self.register(FunctionEntry::builtin("IO.println".to_string(), 2));
self.register(FunctionEntry::builtin("IO.getLine".to_string(), 1));
self.register(FunctionEntry::builtin("IO.pure".to_string(), 2));
self.register(FunctionEntry::builtin("IO.bind".to_string(), 4));
self.register(FunctionEntry::builtin("Array.mk".to_string(), 1));
self.register(FunctionEntry::builtin("Array.push".to_string(), 2));
self.register(FunctionEntry::builtin("Array.get!".to_string(), 2));
self.register(FunctionEntry::builtin("Array.set!".to_string(), 3));
self.register(FunctionEntry::builtin("Array.size".to_string(), 1));
}
}
#[allow(dead_code)]
pub struct ClosureSerializer;
#[allow(dead_code)]
impl ClosureSerializer {
pub fn serialize_env(env: &HashMap<String, u64>) -> Vec<u8> {
let mut out = Vec::new();
let count = env.len() as u32;
out.extend_from_slice(&count.to_le_bytes());
let mut pairs: Vec<(&String, &u64)> = env.iter().collect();
pairs.sort_by_key(|(k, _)| k.as_str());
for (key, val) in pairs {
let key_bytes = key.as_bytes();
out.extend_from_slice(&(key_bytes.len() as u32).to_le_bytes());
out.extend_from_slice(key_bytes);
out.extend_from_slice(&val.to_le_bytes());
}
out
}
pub fn deserialize_env(data: &[u8]) -> Option<HashMap<String, u64>> {
if data.len() < 4 {
return None;
}
let count = u32::from_le_bytes(data[0..4].try_into().ok()?) as usize;
let mut pos = 4;
let mut env = HashMap::new();
for _ in 0..count {
if pos + 4 > data.len() {
return None;
}
let key_len = u32::from_le_bytes(data[pos..pos + 4].try_into().ok()?) as usize;
pos += 4;
if pos + key_len > data.len() {
return None;
}
let key = std::str::from_utf8(&data[pos..pos + key_len])
.ok()?
.to_string();
pos += key_len;
if pos + 8 > data.len() {
return None;
}
let val = u64::from_le_bytes(data[pos..pos + 8].try_into().ok()?);
pos += 8;
env.insert(key, val);
}
Some(env)
}
}
#[allow(dead_code)]
pub struct ClosureOptimizer {
inline_threshold: usize,
}
#[allow(dead_code)]
impl ClosureOptimizer {
pub fn new(inline_threshold: usize) -> Self {
Self { inline_threshold }
}
pub fn reduce_env(
&self,
closure: &mut Closure,
used_names: &std::collections::HashSet<String>,
) -> usize {
let before = closure.env.len();
closure.env.retain(|obj| {
let _ = obj;
let _ = used_names;
true
});
before - closure.env.len()
}
pub fn optimize_pap(&self, pap: &Pap) -> Option<Closure> {
if pap.args.len() == pap.closure.arity as usize {
Some(Closure {
fn_ptr: pap.closure.fn_ptr,
arity: 0,
env: pap
.closure
.env
.iter()
.chain(pap.args.iter())
.cloned()
.collect(),
name: pap.closure.name.clone(),
is_recursive: pap.closure.is_recursive,
is_known: pap.closure.is_known,
})
} else {
None
}
}
pub fn inline_paps(&self, table: &mut FunctionTable) -> usize {
let _ = table;
0
}
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct FlatClosure {
pub fn_index: u32,
pub arity: u32,
pub env: Vec<u64>,
}
#[allow(dead_code)]
impl FlatClosure {
pub fn new(fn_index: u32, arity: u32, env: Vec<u64>) -> Self {
Self {
fn_index,
arity,
env,
}
}
pub fn serialize(&self) -> Vec<u8> {
let mut out = Vec::new();
out.extend_from_slice(&self.fn_index.to_le_bytes());
out.extend_from_slice(&self.arity.to_le_bytes());
let env_len = self.env.len() as u32;
out.extend_from_slice(&env_len.to_le_bytes());
for v in &self.env {
out.extend_from_slice(&v.to_le_bytes());
}
out
}
pub fn deserialize(data: &[u8]) -> Option<Self> {
if data.len() < 12 {
return None;
}
let fn_index = u32::from_le_bytes(data[0..4].try_into().ok()?);
let arity = u32::from_le_bytes(data[4..8].try_into().ok()?);
let env_len = u32::from_le_bytes(data[8..12].try_into().ok()?) as usize;
if data.len() < 12 + env_len * 8 {
return None;
}
let mut env = Vec::with_capacity(env_len);
for i in 0..env_len {
let off = 12 + i * 8;
let v = u64::from_le_bytes(data[off..off + 8].try_into().ok()?);
env.push(v);
}
Some(Self {
fn_index,
arity,
env,
})
}
pub fn is_thunk(&self) -> bool {
self.arity == 0
}
pub fn env_size(&self) -> usize {
self.env.len()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CallConvention {
ClosureCall,
DirectCall,
TailCall,
IndirectCall,
BuiltinCall,
}
impl CallConvention {
pub fn supports_tco(&self) -> bool {
matches!(self, CallConvention::TailCall | CallConvention::DirectCall)
}
pub fn is_direct(&self) -> bool {
matches!(self, CallConvention::DirectCall | CallConvention::TailCall)
}
}
#[derive(Clone, Debug, Default)]
pub struct ClosureStats {
pub closures_created: u64,
pub paps_created: u64,
pub exact_calls: u64,
pub under_applications: u64,
pub over_applications: u64,
pub tail_calls: u64,
pub direct_calls: u64,
pub builtin_calls: u64,
pub peak_stack_depth: usize,
}
impl ClosureStats {
pub fn new() -> Self {
Self::default()
}
pub fn record_closure_created(&mut self) {
self.closures_created += 1;
}
pub fn record_pap_created(&mut self) {
self.paps_created += 1;
}
pub fn record_exact_call(&mut self) {
self.exact_calls += 1;
}
pub fn record_under_application(&mut self) {
self.under_applications += 1;
}
pub fn record_over_application(&mut self) {
self.over_applications += 1;
}
pub fn record_tail_call(&mut self) {
self.tail_calls += 1;
}
pub fn record_direct_call(&mut self) {
self.direct_calls += 1;
}
pub fn record_builtin_call(&mut self) {
self.builtin_calls += 1;
}
pub fn update_peak_depth(&mut self, depth: usize) {
if depth > self.peak_stack_depth {
self.peak_stack_depth = depth;
}
}
pub fn total_calls(&self) -> u64 {
self.exact_calls
+ self.under_applications
+ self.over_applications
+ self.tail_calls
+ self.direct_calls
+ self.builtin_calls
}
pub fn reset(&mut self) {
*self = Self::default();
}
}
#[allow(dead_code)]
pub struct ClosureRegistry {
map: HashMap<String, Closure>,
access_counts: HashMap<String, u64>,
}
#[allow(dead_code)]
impl ClosureRegistry {
pub fn new() -> Self {
Self {
map: HashMap::new(),
access_counts: HashMap::new(),
}
}
pub fn register(&mut self, name: &str, c: Closure) {
self.map.insert(name.to_string(), c);
self.access_counts.insert(name.to_string(), 0);
}
pub fn lookup(&mut self, name: &str) -> Option<&Closure> {
if let Some(count) = self.access_counts.get_mut(name) {
*count += 1;
}
self.map.get(name)
}
pub fn unregister(&mut self, name: &str) -> Option<Closure> {
self.access_counts.remove(name);
self.map.remove(name)
}
pub fn len(&self) -> usize {
self.map.len()
}
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
pub fn access_count(&self, name: &str) -> u64 {
self.access_counts.get(name).copied().unwrap_or(0)
}
pub fn most_accessed(&self) -> Option<&str> {
self.access_counts
.iter()
.max_by_key(|(_, &c)| c)
.map(|(name, _)| name.as_str())
}
pub fn names(&self) -> impl Iterator<Item = &str> {
self.map.keys().map(|s| s.as_str())
}
}
#[allow(dead_code)]
pub struct ClosureSizeEstimator {
pub(super) ptr_size: usize,
pub(super) object_overhead: usize,
}
#[allow(dead_code)]
impl ClosureSizeEstimator {
pub fn new(ptr_size: usize) -> Self {
Self {
ptr_size,
object_overhead: 16,
}
}
pub fn estimate_closure(&self, c: &Closure) -> usize {
self.object_overhead + self.ptr_size + 4 + 4 + c.env.len() * self.ptr_size
}
pub fn estimate_pap(&self, pap: &Pap) -> usize {
self.object_overhead + self.estimate_closure(&pap.closure) + pap.args.len() * self.ptr_size
}
pub fn estimate_flat(&self, fc: &FlatClosure) -> usize {
self.object_overhead + 4 + 4 + fc.env.len() * 8
}
}
#[allow(dead_code)]
#[derive(Clone, Debug, Default)]
pub struct CaptureSet {
pub(super) vars: std::collections::BTreeSet<String>,
}
#[allow(dead_code)]
impl CaptureSet {
pub fn new() -> Self {
Self {
vars: std::collections::BTreeSet::new(),
}
}
pub fn capture(&mut self, name: &str) {
self.vars.insert(name.to_string());
}
pub fn bind(&mut self, name: &str) {
self.vars.remove(name);
}
pub fn is_captured(&self, name: &str) -> bool {
self.vars.contains(name)
}
pub fn len(&self) -> usize {
self.vars.len()
}
pub fn is_empty(&self) -> bool {
self.vars.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = &str> {
self.vars.iter().map(|s| s.as_str())
}
pub fn union(&mut self, other: &CaptureSet) {
for name in &other.vars {
self.vars.insert(name.clone());
}
}
pub fn difference(&mut self, other: &CaptureSet) {
for name in &other.vars {
self.vars.remove(name);
}
}
}
#[derive(Clone, Debug)]
pub struct Closure {
pub fn_ptr: FnPtr,
pub arity: u16,
pub env: Vec<RtObject>,
pub name: Option<String>,
pub is_recursive: bool,
pub is_known: bool,
}
impl Closure {
pub fn new(fn_ptr: FnPtr, arity: u16, env: Vec<RtObject>) -> Self {
Closure {
fn_ptr,
arity,
env,
name: None,
is_recursive: false,
is_known: false,
}
}
pub fn named(name: String, fn_ptr: FnPtr, arity: u16, env: Vec<RtObject>) -> Self {
Closure {
fn_ptr,
arity,
env,
name: Some(name),
is_recursive: false,
is_known: false,
}
}
pub fn simple(fn_ptr: FnPtr, arity: u16) -> Self {
Closure::new(fn_ptr, arity, Vec::new())
}
pub fn env_size(&self) -> usize {
self.env.len()
}
pub fn get_env(&self, index: usize) -> Option<&RtObject> {
self.env.get(index)
}
pub fn set_env(&mut self, index: usize, value: RtObject) -> bool {
if index < self.env.len() {
self.env[index] = value;
true
} else {
false
}
}
pub fn extend_env(&mut self, values: &[RtObject]) {
self.env.extend_from_slice(values);
}
pub fn set_recursive(&mut self) {
self.is_recursive = true;
}
pub fn set_known(&mut self) {
self.is_known = true;
}
}
#[derive(Clone, Debug)]
pub struct MutualRecGroup {
pub closures: Vec<Closure>,
pub shared_env: Vec<RtObject>,
}
impl MutualRecGroup {
pub fn new(closures: Vec<Closure>, shared_env: Vec<RtObject>) -> Self {
MutualRecGroup {
closures,
shared_env,
}
}
pub fn get_closure(&self, index: usize) -> Option<&Closure> {
self.closures.get(index)
}
pub fn size(&self) -> usize {
self.closures.len()
}
}