Skip to main content

oxilean_runtime/closure/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::object::RtObject;
6use std::collections::{BTreeSet, HashMap, VecDeque};
7
8/// A simplified lambda lifter that identifies free variables.
9#[allow(dead_code)]
10pub struct LambdaLifter {
11    next_id: u32,
12    lifted: Vec<LiftedLambda>,
13}
14#[allow(dead_code)]
15impl LambdaLifter {
16    /// Create a new lambda lifter.
17    pub fn new() -> Self {
18        Self {
19            next_id: 0,
20            lifted: Vec::new(),
21        }
22    }
23    /// Lift a lambda with the given free variables and arity.
24    pub fn lift(&mut self, free_vars: Vec<String>, arity: u32) -> LiftedLambda {
25        let id = self.next_id;
26        self.next_id += 1;
27        let lifted = LiftedLambda {
28            id,
29            free_vars,
30            arity,
31        };
32        self.lifted.push(lifted.clone());
33        lifted
34    }
35    /// Number of lifted lambdas.
36    pub fn len(&self) -> usize {
37        self.lifted.len()
38    }
39    /// Whether any lambdas have been lifted.
40    pub fn is_empty(&self) -> bool {
41        self.lifted.is_empty()
42    }
43    /// Access all lifted lambdas.
44    pub fn all(&self) -> &[LiftedLambda] {
45        &self.lifted
46    }
47    /// Total free variables across all lifted lambdas.
48    pub fn total_free_vars(&self) -> usize {
49        self.lifted.iter().map(|l| l.free_vars.len()).sum()
50    }
51}
52/// Result of applying arguments to a PAP.
53#[derive(Clone, Debug)]
54pub enum PapResult {
55    /// Still under-applied (need more arguments).
56    UnderApplied(Pap),
57    /// Exactly saturated (ready to call).
58    Saturated {
59        /// The closure to call.
60        closure: Closure,
61        /// All arguments.
62        args: Vec<RtObject>,
63    },
64    /// Over-applied (extra arguments left over).
65    OverApplied {
66        /// The closure to call.
67        closure: Closure,
68        /// Exact arguments for the call.
69        args: Vec<RtObject>,
70        /// Remaining arguments to apply to the result.
71        remaining_args: Vec<RtObject>,
72    },
73}
74/// Result of a batch thunk forcing operation.
75#[allow(dead_code)]
76#[derive(Clone, Debug)]
77pub struct BatchForceResult {
78    pub forced: usize,
79    pub failed: usize,
80    pub already_evaluated: usize,
81}
82/// An arena of closures that supports allocation and reuse.
83#[allow(dead_code)]
84pub struct ClosureArena {
85    closures: Vec<Option<Closure>>,
86    free: Vec<usize>,
87    alloc_count: u64,
88    free_count: u64,
89}
90#[allow(dead_code)]
91impl ClosureArena {
92    /// Create an empty closure arena.
93    pub fn new() -> Self {
94        Self {
95            closures: Vec::new(),
96            free: Vec::new(),
97            alloc_count: 0,
98            free_count: 0,
99        }
100    }
101    /// Insert a closure and return its handle.
102    pub fn alloc(&mut self, c: Closure) -> ClosureHandle {
103        self.alloc_count += 1;
104        if let Some(idx) = self.free.pop() {
105            self.closures[idx] = Some(c);
106            ClosureHandle(idx)
107        } else {
108            let idx = self.closures.len();
109            self.closures.push(Some(c));
110            ClosureHandle(idx)
111        }
112    }
113    /// Release a closure back to the pool.
114    pub fn free(&mut self, h: ClosureHandle) {
115        if let Some(slot) = self.closures.get_mut(h.0) {
116            if slot.take().is_some() {
117                self.free.push(h.0);
118                self.free_count += 1;
119            }
120        }
121    }
122    /// Get a reference to a closure by handle.
123    pub fn get(&self, h: ClosureHandle) -> Option<&Closure> {
124        self.closures.get(h.0)?.as_ref()
125    }
126    /// Get a mutable reference.
127    pub fn get_mut(&mut self, h: ClosureHandle) -> Option<&mut Closure> {
128        self.closures.get_mut(h.0)?.as_mut()
129    }
130    /// Number of live closures.
131    pub fn live_count(&self) -> usize {
132        self.closures.iter().filter(|s| s.is_some()).count()
133    }
134    /// Capacity (live + free slots).
135    pub fn capacity(&self) -> usize {
136        self.closures.len()
137    }
138    /// Total allocations.
139    pub fn alloc_count(&self) -> u64 {
140        self.alloc_count
141    }
142    /// Total frees.
143    pub fn free_count(&self) -> u64 {
144        self.free_count
145    }
146    /// Iterate over live closures with their handles.
147    pub fn iter(&self) -> impl Iterator<Item = (ClosureHandle, &Closure)> {
148        self.closures
149            .iter()
150            .enumerate()
151            .filter_map(|(i, s)| s.as_ref().map(|c| (ClosureHandle(i), c)))
152    }
153}
154/// Information about a function call at runtime.
155#[derive(Clone, Debug)]
156pub struct CallInfo {
157    /// The call convention used.
158    pub convention: CallConvention,
159    /// The function being called.
160    pub fn_ptr: FnPtr,
161    /// Number of arguments.
162    pub num_args: u16,
163    /// Whether this is a recursive call.
164    pub is_recursive: bool,
165    /// Caller's name (for debugging).
166    pub caller_name: Option<String>,
167    /// Callee's name (for debugging).
168    pub callee_name: Option<String>,
169}
170impl CallInfo {
171    /// Create a new call info.
172    pub fn new(convention: CallConvention, fn_ptr: FnPtr, num_args: u16) -> Self {
173        CallInfo {
174            convention,
175            fn_ptr,
176            num_args,
177            is_recursive: false,
178            caller_name: None,
179            callee_name: None,
180        }
181    }
182}
183/// Handle to a closure in the arena.
184#[allow(dead_code)]
185#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
186pub struct ClosureHandle(pub usize);
187/// Result of closure conversion for a function.
188#[derive(Clone, Debug)]
189pub struct ClosureConversionResult {
190    /// The converted closure.
191    pub closure: Closure,
192    /// Free variables that were captured.
193    pub captured_vars: Vec<String>,
194    /// Whether the function was eta-expanded during conversion.
195    pub eta_expanded: bool,
196    /// Whether any mutual recursion was detected.
197    pub has_mutual_rec: bool,
198}
199impl ClosureConversionResult {
200    /// Create a new conversion result.
201    pub fn new(closure: Closure) -> Self {
202        ClosureConversionResult {
203            closure,
204            captured_vars: Vec::new(),
205            eta_expanded: false,
206            has_mutual_rec: false,
207        }
208    }
209    /// Add a captured variable.
210    pub fn add_captured(&mut self, var: String) {
211        self.captured_vars.push(var);
212    }
213}
214/// A directed graph of closure environment dependencies.
215/// Edges mean "closure A captures closure B".
216#[allow(dead_code)]
217pub struct EnvGraph {
218    edges: HashMap<u32, Vec<u32>>,
219    node_count: u32,
220}
221#[allow(dead_code)]
222impl EnvGraph {
223    /// Create an empty environment graph.
224    pub fn new() -> Self {
225        Self {
226            edges: HashMap::new(),
227            node_count: 0,
228        }
229    }
230    /// Add a new node (closure id).
231    pub fn add_node(&mut self) -> u32 {
232        let id = self.node_count;
233        self.node_count += 1;
234        self.edges.insert(id, Vec::new());
235        id
236    }
237    /// Add a capture edge: `from` captures `to`.
238    pub fn add_edge(&mut self, from: u32, to: u32) {
239        self.edges.entry(from).or_default().push(to);
240    }
241    /// Get the direct captures of a closure.
242    pub fn captures(&self, id: u32) -> &[u32] {
243        self.edges.get(&id).map(|v| v.as_slice()).unwrap_or(&[])
244    }
245    /// Compute the transitive closure of captures (all transitively captured closures).
246    pub fn transitive_captures(&self, id: u32) -> Vec<u32> {
247        let mut visited = std::collections::HashSet::new();
248        let mut stack = vec![id];
249        let mut result = Vec::new();
250        while let Some(curr) = stack.pop() {
251            for &dep in self.captures(curr) {
252                if visited.insert(dep) {
253                    result.push(dep);
254                    stack.push(dep);
255                }
256            }
257        }
258        result
259    }
260    /// Detect cycles (a closure transitively capturing itself).
261    pub fn has_cycle(&self) -> bool {
262        for &start in self.edges.keys() {
263            if self.transitive_captures(start).contains(&start) {
264                return true;
265            }
266        }
267        false
268    }
269    /// Number of nodes.
270    pub fn node_count(&self) -> u32 {
271        self.node_count
272    }
273    /// Number of edges.
274    pub fn edge_count(&self) -> usize {
275        self.edges.values().map(|v| v.len()).sum()
276    }
277}
278/// The call stack.
279pub struct CallStack {
280    /// Stack frames.
281    frames: Vec<StackFrame>,
282    /// Maximum stack depth.
283    max_depth: usize,
284}
285impl CallStack {
286    /// Create a new call stack.
287    pub fn new() -> Self {
288        CallStack {
289            frames: Vec::new(),
290            max_depth: 10_000,
291        }
292    }
293    /// Create with a custom maximum depth.
294    pub fn with_max_depth(max_depth: usize) -> Self {
295        CallStack {
296            frames: Vec::new(),
297            max_depth,
298        }
299    }
300    /// Push a frame onto the stack.
301    pub fn push(&mut self, frame: StackFrame) -> Result<(), StackOverflow> {
302        if self.frames.len() >= self.max_depth {
303            return Err(StackOverflow {
304                depth: self.frames.len(),
305                max_depth: self.max_depth,
306            });
307        }
308        self.frames.push(frame);
309        Ok(())
310    }
311    /// Pop a frame from the stack.
312    pub fn pop(&mut self) -> Option<StackFrame> {
313        self.frames.pop()
314    }
315    /// Get the current (top) frame.
316    pub fn current(&self) -> Option<&StackFrame> {
317        self.frames.last()
318    }
319    /// Get the current (top) frame mutably.
320    pub fn current_mut(&mut self) -> Option<&mut StackFrame> {
321        self.frames.last_mut()
322    }
323    /// Current stack depth.
324    pub fn depth(&self) -> usize {
325        self.frames.len()
326    }
327    /// Check if the stack is empty.
328    pub fn is_empty(&self) -> bool {
329        self.frames.is_empty()
330    }
331    /// Get all frame names (for stack traces).
332    pub fn trace(&self) -> Vec<String> {
333        self.frames
334            .iter()
335            .rev()
336            .map(|f| f.name.clone().unwrap_or_else(|| format!("{}", f.fn_ptr)))
337            .collect()
338    }
339    /// Set the maximum depth.
340    pub fn set_max_depth(&mut self, depth: usize) {
341        self.max_depth = depth;
342    }
343}
344/// Builder for constructing closures with captured environments.
345pub struct ClosureBuilder {
346    /// Function pointer.
347    fn_ptr: FnPtr,
348    /// Arity.
349    arity: u16,
350    /// Captured environment values.
351    env: Vec<RtObject>,
352    /// Name.
353    name: Option<String>,
354    /// Whether recursive.
355    is_recursive: bool,
356    /// Whether known.
357    is_known: bool,
358}
359impl ClosureBuilder {
360    /// Create a new closure builder.
361    pub fn new(fn_ptr: FnPtr, arity: u16) -> Self {
362        ClosureBuilder {
363            fn_ptr,
364            arity,
365            env: Vec::new(),
366            name: None,
367            is_recursive: false,
368            is_known: false,
369        }
370    }
371    /// Set the closure name.
372    pub fn name(mut self, name: String) -> Self {
373        self.name = Some(name);
374        self
375    }
376    /// Add a captured value to the environment.
377    pub fn capture(mut self, value: RtObject) -> Self {
378        self.env.push(value);
379        self
380    }
381    /// Add multiple captured values.
382    pub fn capture_many(mut self, values: Vec<RtObject>) -> Self {
383        self.env.extend(values);
384        self
385    }
386    /// Mark as recursive.
387    pub fn recursive(mut self) -> Self {
388        self.is_recursive = true;
389        self
390    }
391    /// Mark as a known function.
392    pub fn known(mut self) -> Self {
393        self.is_known = true;
394        self
395    }
396    /// Build the closure.
397    pub fn build(self) -> Closure {
398        Closure {
399            fn_ptr: self.fn_ptr,
400            arity: self.arity,
401            env: self.env,
402            name: self.name,
403            is_recursive: self.is_recursive,
404            is_known: self.is_known,
405        }
406    }
407}
408/// A queue of partial applications waiting to be saturated.
409#[allow(dead_code)]
410pub struct PapQueue {
411    queue: std::collections::VecDeque<Pap>,
412    max_size: usize,
413    enqueue_count: u64,
414    saturated_count: u64,
415}
416#[allow(dead_code)]
417impl PapQueue {
418    /// Create a queue with a maximum capacity.
419    pub fn new(max_size: usize) -> Self {
420        Self {
421            queue: std::collections::VecDeque::new(),
422            max_size,
423            enqueue_count: 0,
424            saturated_count: 0,
425        }
426    }
427    /// Enqueue a PAP. Returns false if the queue is full.
428    pub fn enqueue(&mut self, pap: Pap) -> bool {
429        if self.queue.len() >= self.max_size {
430            return false;
431        }
432        self.queue.push_back(pap);
433        self.enqueue_count += 1;
434        true
435    }
436    /// Dequeue the next PAP.
437    pub fn dequeue(&mut self) -> Option<Pap> {
438        self.queue.pop_front()
439    }
440    /// Apply additional args to the front PAP.
441    pub fn apply_front(&mut self, args: Vec<RtObject>) -> Option<PapResult> {
442        let pap = self.dequeue()?;
443        let result = pap.apply(&args);
444        if matches!(result, PapResult::Saturated { .. }) {
445            self.saturated_count += 1;
446        }
447        Some(result)
448    }
449    /// Current queue length.
450    pub fn len(&self) -> usize {
451        self.queue.len()
452    }
453    /// Whether the queue is empty.
454    pub fn is_empty(&self) -> bool {
455        self.queue.is_empty()
456    }
457    /// Total PAPs enqueued.
458    pub fn enqueue_count(&self) -> u64 {
459        self.enqueue_count
460    }
461    /// Total saturated PAPs.
462    pub fn saturated_count(&self) -> u64 {
463        self.saturated_count
464    }
465}
466/// A partial application (PAP).
467///
468/// Created when a closure is applied to fewer arguments than its arity.
469/// The PAP stores the original closure and the arguments applied so far.
470#[derive(Clone, Debug)]
471pub struct Pap {
472    /// The original closure being partially applied.
473    pub closure: Closure,
474    /// Arguments applied so far.
475    pub args: Vec<RtObject>,
476}
477impl Pap {
478    /// Create a new PAP.
479    pub fn new(closure: Closure, args: Vec<RtObject>) -> Self {
480        Pap { closure, args }
481    }
482    /// Number of arguments still needed.
483    pub fn remaining_arity(&self) -> u16 {
484        self.closure.arity.saturating_sub(self.args.len() as u16)
485    }
486    /// Total arity of the underlying closure.
487    pub fn total_arity(&self) -> u16 {
488        self.closure.arity
489    }
490    /// Number of arguments applied so far.
491    pub fn num_applied(&self) -> usize {
492        self.args.len()
493    }
494    /// Check if the PAP is fully saturated (should not happen, but defensive).
495    pub fn is_saturated(&self) -> bool {
496        self.args.len() >= self.closure.arity as usize
497    }
498    /// Apply additional arguments. Returns either a new PAP or indicates saturation.
499    pub fn apply(&self, new_args: &[RtObject]) -> PapResult {
500        let total_args = self.args.len() + new_args.len();
501        let arity = self.closure.arity as usize;
502        if total_args < arity {
503            let mut all_args = self.args.clone();
504            all_args.extend_from_slice(new_args);
505            PapResult::UnderApplied(Pap::new(self.closure.clone(), all_args))
506        } else if total_args == arity {
507            let mut all_args = self.args.clone();
508            all_args.extend_from_slice(new_args);
509            PapResult::Saturated {
510                closure: self.closure.clone(),
511                args: all_args,
512            }
513        } else {
514            let mut exact_args = self.args.clone();
515            let needed = arity - self.args.len();
516            exact_args.extend_from_slice(&new_args[..needed]);
517            let remaining = new_args[needed..].to_vec();
518            PapResult::OverApplied {
519                closure: self.closure.clone(),
520                args: exact_args,
521                remaining_args: remaining,
522            }
523        }
524    }
525    /// Get all arguments (environment + applied).
526    pub fn all_args(&self) -> Vec<RtObject> {
527        let mut args = self.closure.env.clone();
528        args.extend(self.args.iter().cloned());
529        args
530    }
531}
532/// Optimization report for a closure.
533#[allow(dead_code)]
534#[derive(Clone, Debug, Default)]
535pub struct ClosureOptReport {
536    pub inlined_paps: usize,
537    pub specialized_arities: usize,
538    pub env_reduced_vars: usize,
539    pub removed_dead_closures: usize,
540}
541#[allow(dead_code)]
542impl ClosureOptReport {
543    /// Whether any optimizations were applied.
544    pub fn has_changes(&self) -> bool {
545        self.inlined_paps > 0
546            || self.specialized_arities > 0
547            || self.env_reduced_vars > 0
548            || self.removed_dead_closures > 0
549    }
550    /// Total optimization actions.
551    pub fn total(&self) -> usize {
552        self.inlined_paps
553            + self.specialized_arities
554            + self.env_reduced_vars
555            + self.removed_dead_closures
556    }
557}
558/// A record of a potential inline candidate.
559#[allow(dead_code)]
560#[derive(Clone, Debug)]
561pub struct InlineCandidate {
562    pub fn_id: u32,
563    pub call_site_count: u32,
564    pub env_size: usize,
565    pub arity: u32,
566}
567#[allow(dead_code)]
568impl InlineCandidate {
569    /// Score: lower is more profitable to inline.
570    pub fn inline_score(&self) -> f64 {
571        self.env_size as f64 / (self.call_site_count as f64 + 1.0)
572    }
573    /// Whether the candidate is a leaf (no recursive calls).
574    pub fn is_leaf_candidate(&self) -> bool {
575        self.env_size <= 4 && self.arity <= 3
576    }
577}
578/// A function pointer in the runtime.
579///
580/// Functions are identified by an index into the global function table.
581/// This allows us to represent function pointers without actual raw pointers.
582#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
583pub struct FnPtr {
584    /// Index into the global function table.
585    pub index: u32,
586    /// Module that defines this function (for multi-module support).
587    pub module_id: u16,
588}
589impl FnPtr {
590    /// Create a new function pointer.
591    pub fn new(index: u32) -> Self {
592        FnPtr {
593            index,
594            module_id: 0,
595        }
596    }
597    /// Create a function pointer with module ID.
598    pub fn with_module(index: u32, module_id: u16) -> Self {
599        FnPtr { index, module_id }
600    }
601    /// Null/invalid function pointer.
602    pub fn null() -> Self {
603        FnPtr {
604            index: u32::MAX,
605            module_id: 0,
606        }
607    }
608    /// Check if this is a null pointer.
609    pub fn is_null(&self) -> bool {
610        self.index == u32::MAX
611    }
612}
613/// A stack frame for function calls.
614#[derive(Clone, Debug)]
615pub struct StackFrame {
616    /// The function being executed.
617    pub fn_ptr: FnPtr,
618    /// Local variables.
619    pub locals: Vec<RtObject>,
620    /// Arguments.
621    pub args: Vec<RtObject>,
622    /// Captured environment (if executing a closure).
623    pub env: Vec<RtObject>,
624    /// Return address (instruction pointer offset).
625    pub return_ip: u32,
626    /// Frame name (for debugging).
627    pub name: Option<String>,
628}
629impl StackFrame {
630    /// Create a new stack frame.
631    pub fn new(fn_ptr: FnPtr, args: Vec<RtObject>, env: Vec<RtObject>, frame_size: usize) -> Self {
632        StackFrame {
633            fn_ptr,
634            locals: vec![RtObject::unit(); frame_size],
635            args,
636            env,
637            return_ip: 0,
638            name: None,
639        }
640    }
641    /// Get a local variable.
642    pub fn get_local(&self, index: usize) -> Option<&RtObject> {
643        self.locals.get(index)
644    }
645    /// Set a local variable.
646    pub fn set_local(&mut self, index: usize, value: RtObject) -> bool {
647        if index < self.locals.len() {
648            self.locals[index] = value;
649            true
650        } else {
651            false
652        }
653    }
654    /// Get an argument.
655    pub fn get_arg(&self, index: usize) -> Option<&RtObject> {
656        self.args.get(index)
657    }
658    /// Get a captured environment value.
659    pub fn get_env(&self, index: usize) -> Option<&RtObject> {
660        self.env.get(index)
661    }
662}
663/// The result of lifting a lambda to a closed form.
664#[allow(dead_code)]
665#[derive(Clone, Debug)]
666pub struct LiftedLambda {
667    /// The lifted function's identifier.
668    pub id: u32,
669    /// Names of free variables captured from the outer scope.
670    pub free_vars: Vec<String>,
671    /// Arity of the function.
672    pub arity: u32,
673}
674/// A simple call inliner that collects candidates.
675#[allow(dead_code)]
676pub struct CallInliner {
677    candidates: Vec<InlineCandidate>,
678    inlined_count: u64,
679}
680#[allow(dead_code)]
681impl CallInliner {
682    /// Create a new inliner.
683    pub fn new() -> Self {
684        Self {
685            candidates: Vec::new(),
686            inlined_count: 0,
687        }
688    }
689    /// Register a candidate.
690    pub fn register(&mut self, candidate: InlineCandidate) {
691        self.candidates.push(candidate);
692    }
693    /// Get the top-N candidates by inline score.
694    pub fn top_candidates(&self, n: usize) -> Vec<&InlineCandidate> {
695        let mut sorted: Vec<&InlineCandidate> = self.candidates.iter().collect();
696        sorted.sort_by(|a, b| {
697            a.inline_score()
698                .partial_cmp(&b.inline_score())
699                .unwrap_or(std::cmp::Ordering::Equal)
700        });
701        sorted.truncate(n);
702        sorted
703    }
704    /// Mark a candidate as inlined.
705    pub fn record_inline(&mut self, fn_id: u32) {
706        self.candidates.retain(|c| c.fn_id != fn_id);
707        self.inlined_count += 1;
708    }
709    /// Total inlines performed.
710    pub fn inlined_count(&self) -> u64 {
711        self.inlined_count
712    }
713    /// Remaining candidate count.
714    pub fn candidate_count(&self) -> usize {
715        self.candidates.len()
716    }
717}
718/// An entry in the global function table.
719///
720/// Each compiled function has an entry that describes its properties.
721#[derive(Clone, Debug)]
722pub struct FunctionEntry {
723    /// The function's name.
724    pub name: String,
725    /// Total arity.
726    pub arity: u16,
727    /// Number of environment variables expected (for closures).
728    pub env_size: u16,
729    /// Preferred call convention.
730    pub convention: CallConvention,
731    /// Whether this function is recursive.
732    pub is_recursive: bool,
733    /// Whether this function is a built-in.
734    pub is_builtin: bool,
735    /// Whether this function has been inlined at all call sites.
736    pub is_inlined: bool,
737    /// Stack frame size (in words) needed for local variables.
738    pub frame_size: u16,
739    /// Parameter names (for debugging).
740    pub param_names: Vec<String>,
741}
742impl FunctionEntry {
743    /// Create a new function entry.
744    pub fn new(name: String, arity: u16) -> Self {
745        FunctionEntry {
746            name,
747            arity,
748            env_size: 0,
749            convention: CallConvention::ClosureCall,
750            is_recursive: false,
751            is_builtin: false,
752            is_inlined: false,
753            frame_size: 0,
754            param_names: Vec::new(),
755        }
756    }
757    /// Create a built-in function entry.
758    pub fn builtin(name: String, arity: u16) -> Self {
759        FunctionEntry {
760            name,
761            arity,
762            env_size: 0,
763            convention: CallConvention::BuiltinCall,
764            is_recursive: false,
765            is_builtin: true,
766            is_inlined: false,
767            frame_size: 0,
768            param_names: Vec::new(),
769        }
770    }
771}
772/// Stack overflow error.
773#[derive(Clone, Debug)]
774pub struct StackOverflow {
775    /// Current depth when overflow occurred.
776    pub depth: usize,
777    /// Maximum allowed depth.
778    pub max_depth: usize,
779}
780/// The global function table.
781///
782/// Maps function indices to their entries. Built during compilation
783/// and used at runtime for function dispatch.
784pub struct FunctionTable {
785    /// Entries indexed by function pointer index.
786    pub(super) entries: Vec<FunctionEntry>,
787    /// Name-to-index lookup.
788    name_index: HashMap<String, u32>,
789}
790impl FunctionTable {
791    /// Create a new empty function table.
792    pub fn new() -> Self {
793        FunctionTable {
794            entries: Vec::new(),
795            name_index: HashMap::new(),
796        }
797    }
798    /// Create with pre-allocated capacity.
799    pub fn with_capacity(cap: usize) -> Self {
800        FunctionTable {
801            entries: Vec::with_capacity(cap),
802            name_index: HashMap::with_capacity(cap),
803        }
804    }
805    /// Register a new function and return its pointer.
806    pub fn register(&mut self, entry: FunctionEntry) -> FnPtr {
807        let index = self.entries.len() as u32;
808        self.name_index.insert(entry.name.clone(), index);
809        self.entries.push(entry);
810        FnPtr::new(index)
811    }
812    /// Look up a function entry by pointer.
813    pub fn get(&self, ptr: FnPtr) -> Option<&FunctionEntry> {
814        self.entries.get(ptr.index as usize)
815    }
816    /// Look up a function entry by name.
817    pub fn get_by_name(&self, name: &str) -> Option<(FnPtr, &FunctionEntry)> {
818        self.name_index
819            .get(name)
820            .and_then(|&idx| self.entries.get(idx as usize).map(|e| (FnPtr::new(idx), e)))
821    }
822    /// Number of registered functions.
823    pub fn len(&self) -> usize {
824        self.entries.len()
825    }
826    /// Check if the table is empty.
827    pub fn is_empty(&self) -> bool {
828        self.entries.is_empty()
829    }
830    /// Iterate over all entries.
831    pub fn iter(&self) -> impl Iterator<Item = (FnPtr, &FunctionEntry)> {
832        self.entries
833            .iter()
834            .enumerate()
835            .map(|(i, e)| (FnPtr::new(i as u32), e))
836    }
837    /// Register built-in functions.
838    pub fn register_builtins(&mut self) {
839        self.register(FunctionEntry::builtin("Nat.add".to_string(), 2));
840        self.register(FunctionEntry::builtin("Nat.sub".to_string(), 2));
841        self.register(FunctionEntry::builtin("Nat.mul".to_string(), 2));
842        self.register(FunctionEntry::builtin("Nat.div".to_string(), 2));
843        self.register(FunctionEntry::builtin("Nat.mod".to_string(), 2));
844        self.register(FunctionEntry::builtin("Nat.beq".to_string(), 2));
845        self.register(FunctionEntry::builtin("Nat.ble".to_string(), 2));
846        self.register(FunctionEntry::builtin("Bool.and".to_string(), 2));
847        self.register(FunctionEntry::builtin("Bool.or".to_string(), 2));
848        self.register(FunctionEntry::builtin("Bool.not".to_string(), 1));
849        self.register(FunctionEntry::builtin("String.append".to_string(), 2));
850        self.register(FunctionEntry::builtin("String.length".to_string(), 1));
851        self.register(FunctionEntry::builtin("String.mk".to_string(), 1));
852        self.register(FunctionEntry::builtin("IO.println".to_string(), 2));
853        self.register(FunctionEntry::builtin("IO.getLine".to_string(), 1));
854        self.register(FunctionEntry::builtin("IO.pure".to_string(), 2));
855        self.register(FunctionEntry::builtin("IO.bind".to_string(), 4));
856        self.register(FunctionEntry::builtin("Array.mk".to_string(), 1));
857        self.register(FunctionEntry::builtin("Array.push".to_string(), 2));
858        self.register(FunctionEntry::builtin("Array.get!".to_string(), 2));
859        self.register(FunctionEntry::builtin("Array.set!".to_string(), 3));
860        self.register(FunctionEntry::builtin("Array.size".to_string(), 1));
861    }
862}
863/// A serializer for closure environments (mapping string names to u64 values).
864#[allow(dead_code)]
865pub struct ClosureSerializer;
866#[allow(dead_code)]
867impl ClosureSerializer {
868    /// Serialize an environment map to bytes.
869    pub fn serialize_env(env: &HashMap<String, u64>) -> Vec<u8> {
870        let mut out = Vec::new();
871        let count = env.len() as u32;
872        out.extend_from_slice(&count.to_le_bytes());
873        let mut pairs: Vec<(&String, &u64)> = env.iter().collect();
874        pairs.sort_by_key(|(k, _)| k.as_str());
875        for (key, val) in pairs {
876            let key_bytes = key.as_bytes();
877            out.extend_from_slice(&(key_bytes.len() as u32).to_le_bytes());
878            out.extend_from_slice(key_bytes);
879            out.extend_from_slice(&val.to_le_bytes());
880        }
881        out
882    }
883    /// Deserialize an environment map from bytes.
884    pub fn deserialize_env(data: &[u8]) -> Option<HashMap<String, u64>> {
885        if data.len() < 4 {
886            return None;
887        }
888        let count = u32::from_le_bytes(data[0..4].try_into().ok()?) as usize;
889        let mut pos = 4;
890        let mut env = HashMap::new();
891        for _ in 0..count {
892            if pos + 4 > data.len() {
893                return None;
894            }
895            let key_len = u32::from_le_bytes(data[pos..pos + 4].try_into().ok()?) as usize;
896            pos += 4;
897            if pos + key_len > data.len() {
898                return None;
899            }
900            let key = std::str::from_utf8(&data[pos..pos + key_len])
901                .ok()?
902                .to_string();
903            pos += key_len;
904            if pos + 8 > data.len() {
905                return None;
906            }
907            let val = u64::from_le_bytes(data[pos..pos + 8].try_into().ok()?);
908            pos += 8;
909            env.insert(key, val);
910        }
911        Some(env)
912    }
913}
914/// A basic closure optimizer.
915#[allow(dead_code)]
916pub struct ClosureOptimizer {
917    inline_threshold: usize,
918}
919#[allow(dead_code)]
920impl ClosureOptimizer {
921    /// Create an optimizer with the given inlining threshold (env size).
922    pub fn new(inline_threshold: usize) -> Self {
923        Self { inline_threshold }
924    }
925    /// Attempt to reduce a closure's environment by removing unused captures.
926    pub fn reduce_env(
927        &self,
928        closure: &mut Closure,
929        used_names: &std::collections::HashSet<String>,
930    ) -> usize {
931        let before = closure.env.len();
932        closure.env.retain(|obj| {
933            let _ = obj;
934            let _ = used_names;
935            true
936        });
937        before - closure.env.len()
938    }
939    /// Specialize a PAP by precomputing partial argument count.
940    pub fn optimize_pap(&self, pap: &Pap) -> Option<Closure> {
941        if pap.args.len() == pap.closure.arity as usize {
942            Some(Closure {
943                fn_ptr: pap.closure.fn_ptr,
944                arity: 0,
945                env: pap
946                    .closure
947                    .env
948                    .iter()
949                    .chain(pap.args.iter())
950                    .cloned()
951                    .collect(),
952                name: pap.closure.name.clone(),
953                is_recursive: pap.closure.is_recursive,
954                is_known: pap.closure.is_known,
955            })
956        } else {
957            None
958        }
959    }
960    /// Inline PAP optimization across the function table.
961    pub fn inline_paps(&self, table: &mut FunctionTable) -> usize {
962        let _ = table;
963        0
964    }
965}
966/// A flat, heap-allocated closure that can be serialized (no dyn fn ptrs).
967#[allow(dead_code)]
968#[derive(Clone, Debug)]
969pub struct FlatClosure {
970    /// Function index (into a global function table).
971    pub fn_index: u32,
972    /// Arity.
973    pub arity: u32,
974    /// Environment values (limited to simple types here: u64).
975    pub env: Vec<u64>,
976}
977#[allow(dead_code)]
978impl FlatClosure {
979    /// Create a new flat closure.
980    pub fn new(fn_index: u32, arity: u32, env: Vec<u64>) -> Self {
981        Self {
982            fn_index,
983            arity,
984            env,
985        }
986    }
987    /// Serialize to bytes.
988    pub fn serialize(&self) -> Vec<u8> {
989        let mut out = Vec::new();
990        out.extend_from_slice(&self.fn_index.to_le_bytes());
991        out.extend_from_slice(&self.arity.to_le_bytes());
992        let env_len = self.env.len() as u32;
993        out.extend_from_slice(&env_len.to_le_bytes());
994        for v in &self.env {
995            out.extend_from_slice(&v.to_le_bytes());
996        }
997        out
998    }
999    /// Deserialize from bytes.
1000    pub fn deserialize(data: &[u8]) -> Option<Self> {
1001        if data.len() < 12 {
1002            return None;
1003        }
1004        let fn_index = u32::from_le_bytes(data[0..4].try_into().ok()?);
1005        let arity = u32::from_le_bytes(data[4..8].try_into().ok()?);
1006        let env_len = u32::from_le_bytes(data[8..12].try_into().ok()?) as usize;
1007        if data.len() < 12 + env_len * 8 {
1008            return None;
1009        }
1010        let mut env = Vec::with_capacity(env_len);
1011        for i in 0..env_len {
1012            let off = 12 + i * 8;
1013            let v = u64::from_le_bytes(data[off..off + 8].try_into().ok()?);
1014            env.push(v);
1015        }
1016        Some(Self {
1017            fn_index,
1018            arity,
1019            env,
1020        })
1021    }
1022    /// Whether this is a thunk (arity == 0).
1023    pub fn is_thunk(&self) -> bool {
1024        self.arity == 0
1025    }
1026    /// Env size.
1027    pub fn env_size(&self) -> usize {
1028        self.env.len()
1029    }
1030}
1031/// Call convention for a function.
1032#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1033pub enum CallConvention {
1034    /// Standard closure call (through the closure mechanism).
1035    ClosureCall,
1036    /// Direct call (function is known at compile time).
1037    DirectCall,
1038    /// Tail call (reuse the current stack frame).
1039    TailCall,
1040    /// Indirect call (through a function pointer).
1041    IndirectCall,
1042    /// Built-in operation (handled specially by the runtime).
1043    BuiltinCall,
1044}
1045impl CallConvention {
1046    /// Check if this convention supports tail call optimization.
1047    pub fn supports_tco(&self) -> bool {
1048        matches!(self, CallConvention::TailCall | CallConvention::DirectCall)
1049    }
1050    /// Check if this is a direct (non-closure) call.
1051    pub fn is_direct(&self) -> bool {
1052        matches!(self, CallConvention::DirectCall | CallConvention::TailCall)
1053    }
1054}
1055/// Statistics about closure operations.
1056#[derive(Clone, Debug, Default)]
1057pub struct ClosureStats {
1058    /// Number of closures created.
1059    pub closures_created: u64,
1060    /// Number of PAPs created.
1061    pub paps_created: u64,
1062    /// Number of exact calls.
1063    pub exact_calls: u64,
1064    /// Number of under-applications.
1065    pub under_applications: u64,
1066    /// Number of over-applications.
1067    pub over_applications: u64,
1068    /// Number of tail calls.
1069    pub tail_calls: u64,
1070    /// Number of direct calls.
1071    pub direct_calls: u64,
1072    /// Number of built-in calls.
1073    pub builtin_calls: u64,
1074    /// Peak stack depth.
1075    pub peak_stack_depth: usize,
1076}
1077impl ClosureStats {
1078    /// Create new empty statistics.
1079    pub fn new() -> Self {
1080        Self::default()
1081    }
1082    /// Record a closure creation.
1083    pub fn record_closure_created(&mut self) {
1084        self.closures_created += 1;
1085    }
1086    /// Record a PAP creation.
1087    pub fn record_pap_created(&mut self) {
1088        self.paps_created += 1;
1089    }
1090    /// Record an exact call.
1091    pub fn record_exact_call(&mut self) {
1092        self.exact_calls += 1;
1093    }
1094    /// Record an under-application.
1095    pub fn record_under_application(&mut self) {
1096        self.under_applications += 1;
1097    }
1098    /// Record an over-application.
1099    pub fn record_over_application(&mut self) {
1100        self.over_applications += 1;
1101    }
1102    /// Record a tail call.
1103    pub fn record_tail_call(&mut self) {
1104        self.tail_calls += 1;
1105    }
1106    /// Record a direct call.
1107    pub fn record_direct_call(&mut self) {
1108        self.direct_calls += 1;
1109    }
1110    /// Record a built-in call.
1111    pub fn record_builtin_call(&mut self) {
1112        self.builtin_calls += 1;
1113    }
1114    /// Update peak stack depth.
1115    pub fn update_peak_depth(&mut self, depth: usize) {
1116        if depth > self.peak_stack_depth {
1117            self.peak_stack_depth = depth;
1118        }
1119    }
1120    /// Total number of calls.
1121    pub fn total_calls(&self) -> u64 {
1122        self.exact_calls
1123            + self.under_applications
1124            + self.over_applications
1125            + self.tail_calls
1126            + self.direct_calls
1127            + self.builtin_calls
1128    }
1129    /// Reset all statistics.
1130    pub fn reset(&mut self) {
1131        *self = Self::default();
1132    }
1133}
1134/// A registry that maps string names to closures.
1135#[allow(dead_code)]
1136pub struct ClosureRegistry {
1137    map: HashMap<String, Closure>,
1138    access_counts: HashMap<String, u64>,
1139}
1140#[allow(dead_code)]
1141impl ClosureRegistry {
1142    /// Create an empty registry.
1143    pub fn new() -> Self {
1144        Self {
1145            map: HashMap::new(),
1146            access_counts: HashMap::new(),
1147        }
1148    }
1149    /// Register a closure with a name.
1150    pub fn register(&mut self, name: &str, c: Closure) {
1151        self.map.insert(name.to_string(), c);
1152        self.access_counts.insert(name.to_string(), 0);
1153    }
1154    /// Look up a closure by name.
1155    pub fn lookup(&mut self, name: &str) -> Option<&Closure> {
1156        if let Some(count) = self.access_counts.get_mut(name) {
1157            *count += 1;
1158        }
1159        self.map.get(name)
1160    }
1161    /// Remove a closure.
1162    pub fn unregister(&mut self, name: &str) -> Option<Closure> {
1163        self.access_counts.remove(name);
1164        self.map.remove(name)
1165    }
1166    /// Number of registered closures.
1167    pub fn len(&self) -> usize {
1168        self.map.len()
1169    }
1170    /// Whether the registry is empty.
1171    pub fn is_empty(&self) -> bool {
1172        self.map.is_empty()
1173    }
1174    /// Access count for a name.
1175    pub fn access_count(&self, name: &str) -> u64 {
1176        self.access_counts.get(name).copied().unwrap_or(0)
1177    }
1178    /// Most accessed closure.
1179    pub fn most_accessed(&self) -> Option<&str> {
1180        self.access_counts
1181            .iter()
1182            .max_by_key(|(_, &c)| c)
1183            .map(|(name, _)| name.as_str())
1184    }
1185    /// Iterate over all registered names.
1186    pub fn names(&self) -> impl Iterator<Item = &str> {
1187        self.map.keys().map(|s| s.as_str())
1188    }
1189}
1190/// Estimates the memory footprint of a closure.
1191#[allow(dead_code)]
1192pub struct ClosureSizeEstimator {
1193    pub(super) ptr_size: usize,
1194    pub(super) object_overhead: usize,
1195}
1196#[allow(dead_code)]
1197impl ClosureSizeEstimator {
1198    /// Create an estimator for a given pointer size.
1199    pub fn new(ptr_size: usize) -> Self {
1200        Self {
1201            ptr_size,
1202            object_overhead: 16,
1203        }
1204    }
1205    /// Estimate closure heap bytes.
1206    pub fn estimate_closure(&self, c: &Closure) -> usize {
1207        self.object_overhead + self.ptr_size + 4 + 4 + c.env.len() * self.ptr_size
1208    }
1209    /// Estimate PAP heap bytes.
1210    pub fn estimate_pap(&self, pap: &Pap) -> usize {
1211        self.object_overhead + self.estimate_closure(&pap.closure) + pap.args.len() * self.ptr_size
1212    }
1213    /// Estimate a flat closure.
1214    pub fn estimate_flat(&self, fc: &FlatClosure) -> usize {
1215        self.object_overhead + 4 + 4 + fc.env.len() * 8
1216    }
1217}
1218/// A set of variable names captured by a closure.
1219#[allow(dead_code)]
1220#[derive(Clone, Debug, Default)]
1221pub struct CaptureSet {
1222    pub(super) vars: std::collections::BTreeSet<String>,
1223}
1224#[allow(dead_code)]
1225impl CaptureSet {
1226    /// Create an empty capture set.
1227    pub fn new() -> Self {
1228        Self {
1229            vars: std::collections::BTreeSet::new(),
1230        }
1231    }
1232    /// Insert a variable into the capture set.
1233    pub fn capture(&mut self, name: &str) {
1234        self.vars.insert(name.to_string());
1235    }
1236    /// Remove a variable (e.g., it's now bound locally).
1237    pub fn bind(&mut self, name: &str) {
1238        self.vars.remove(name);
1239    }
1240    /// Whether a variable is captured.
1241    pub fn is_captured(&self, name: &str) -> bool {
1242        self.vars.contains(name)
1243    }
1244    /// Number of captured variables.
1245    pub fn len(&self) -> usize {
1246        self.vars.len()
1247    }
1248    /// Whether the set is empty.
1249    pub fn is_empty(&self) -> bool {
1250        self.vars.is_empty()
1251    }
1252    /// Iterate over captured variable names in sorted order.
1253    pub fn iter(&self) -> impl Iterator<Item = &str> {
1254        self.vars.iter().map(|s| s.as_str())
1255    }
1256    /// Union with another capture set.
1257    pub fn union(&mut self, other: &CaptureSet) {
1258        for name in &other.vars {
1259            self.vars.insert(name.clone());
1260        }
1261    }
1262    /// Difference: remove all variables in `other` from self.
1263    pub fn difference(&mut self, other: &CaptureSet) {
1264        for name in &other.vars {
1265            self.vars.remove(name);
1266        }
1267    }
1268}
1269/// A runtime closure with captured environment.
1270///
1271/// The closure captures free variables in a flat array. The function body
1272/// is represented by a `FnPtr` into the code table.
1273#[derive(Clone, Debug)]
1274pub struct Closure {
1275    /// Pointer to the function body.
1276    pub fn_ptr: FnPtr,
1277    /// Total arity (number of parameters the function expects).
1278    pub arity: u16,
1279    /// Captured environment values.
1280    pub env: Vec<RtObject>,
1281    /// Name of the closure (for debugging).
1282    pub name: Option<String>,
1283    /// Whether this closure is recursive.
1284    pub is_recursive: bool,
1285    /// Whether this closure has been marked as a known function
1286    /// (can be called directly without going through the closure mechanism).
1287    pub is_known: bool,
1288}
1289impl Closure {
1290    /// Create a new closure.
1291    pub fn new(fn_ptr: FnPtr, arity: u16, env: Vec<RtObject>) -> Self {
1292        Closure {
1293            fn_ptr,
1294            arity,
1295            env,
1296            name: None,
1297            is_recursive: false,
1298            is_known: false,
1299        }
1300    }
1301    /// Create a closure with a name.
1302    pub fn named(name: String, fn_ptr: FnPtr, arity: u16, env: Vec<RtObject>) -> Self {
1303        Closure {
1304            fn_ptr,
1305            arity,
1306            env,
1307            name: Some(name),
1308            is_recursive: false,
1309            is_known: false,
1310        }
1311    }
1312    /// Create a simple closure with no captured environment.
1313    pub fn simple(fn_ptr: FnPtr, arity: u16) -> Self {
1314        Closure::new(fn_ptr, arity, Vec::new())
1315    }
1316    /// Number of captured environment variables.
1317    pub fn env_size(&self) -> usize {
1318        self.env.len()
1319    }
1320    /// Get a captured value by index.
1321    pub fn get_env(&self, index: usize) -> Option<&RtObject> {
1322        self.env.get(index)
1323    }
1324    /// Set a captured value by index.
1325    pub fn set_env(&mut self, index: usize, value: RtObject) -> bool {
1326        if index < self.env.len() {
1327            self.env[index] = value;
1328            true
1329        } else {
1330            false
1331        }
1332    }
1333    /// Extend the environment with additional values.
1334    pub fn extend_env(&mut self, values: &[RtObject]) {
1335        self.env.extend_from_slice(values);
1336    }
1337    /// Mark as recursive.
1338    pub fn set_recursive(&mut self) {
1339        self.is_recursive = true;
1340    }
1341    /// Mark as a known function.
1342    pub fn set_known(&mut self) {
1343        self.is_known = true;
1344    }
1345}
1346/// A group of mutually recursive closures.
1347///
1348/// All closures in the group share a common environment that includes
1349/// references to each other.
1350#[derive(Clone, Debug)]
1351pub struct MutualRecGroup {
1352    /// The closures in the group.
1353    pub closures: Vec<Closure>,
1354    /// Shared environment that all closures can access.
1355    pub shared_env: Vec<RtObject>,
1356}
1357impl MutualRecGroup {
1358    /// Create a new mutual recursive group.
1359    pub fn new(closures: Vec<Closure>, shared_env: Vec<RtObject>) -> Self {
1360        MutualRecGroup {
1361            closures,
1362            shared_env,
1363        }
1364    }
1365    /// Get a specific closure by index.
1366    pub fn get_closure(&self, index: usize) -> Option<&Closure> {
1367        self.closures.get(index)
1368    }
1369    /// Number of closures in the group.
1370    pub fn size(&self) -> usize {
1371        self.closures.len()
1372    }
1373}