Skip to main content

oxilean_codegen/ocaml_backend/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use std::collections::{HashMap, HashSet, VecDeque};
6
7/// The OCaml code generation backend.
8pub struct OcamlBackend {
9    pub(super) module: OcamlModule,
10}
11impl OcamlBackend {
12    /// Create a new OCaml backend for the given module name.
13    pub fn new(module_name: &str) -> Self {
14        OcamlBackend {
15            module: OcamlModule::new(module_name),
16        }
17    }
18    /// Add a definition to the generated module.
19    pub fn add_definition(&mut self, def: OcamlDefinition) {
20        self.module.add(def);
21    }
22    /// Build a variant type definition for an algebraic data type.
23    ///
24    /// Example:
25    /// ```text
26    /// type expr =
27    ///   | Lit of int
28    ///   | Add of expr * expr
29    ///   | Mul of expr * expr
30    /// ```
31    pub fn make_adt(
32        &self,
33        name: &str,
34        type_params: Vec<&str>,
35        variants: Vec<(&str, Vec<OcamlType>)>,
36    ) -> OcamlTypeDef {
37        OcamlTypeDef {
38            name: name.to_string(),
39            type_params: type_params.iter().map(|s| s.to_string()).collect(),
40            decl: OcamlTypeDecl::Variant(
41                variants
42                    .into_iter()
43                    .map(|(n, ts)| (n.to_string(), ts))
44                    .collect(),
45            ),
46        }
47    }
48    /// Build a tail-recursive list fold (common OCaml pattern).
49    ///
50    /// Generates: `let rec fold_left f acc = function | [] -> acc | x::xs -> fold_left f (f acc x) xs`
51    pub fn make_fold_left(&self, name: &str) -> OcamlLetBinding {
52        OcamlLetBinding {
53            is_rec: true,
54            name: name.to_string(),
55            params: vec![("f".to_string(), None), ("acc".to_string(), None)],
56            body: OcamlExpr::Match(
57                Box::new(OcamlExpr::Var("lst".to_string())),
58                vec![
59                    (
60                        OcamlPattern::List(vec![]),
61                        OcamlExpr::Var("acc".to_string()),
62                    ),
63                    (
64                        OcamlPattern::Cons(
65                            Box::new(OcamlPattern::Var("x".to_string())),
66                            Box::new(OcamlPattern::Var("xs".to_string())),
67                        ),
68                        OcamlExpr::App(
69                            Box::new(OcamlExpr::Var(name.to_string())),
70                            vec![
71                                OcamlExpr::Var("f".to_string()),
72                                OcamlExpr::App(
73                                    Box::new(OcamlExpr::Var("f".to_string())),
74                                    vec![
75                                        OcamlExpr::Var("acc".to_string()),
76                                        OcamlExpr::Var("x".to_string()),
77                                    ],
78                                ),
79                                OcamlExpr::Var("xs".to_string()),
80                            ],
81                        ),
82                    ),
83                ],
84            ),
85            type_annotation: None,
86        }
87    }
88    /// Emit the full `.ml` implementation.
89    pub fn emit_module(&self) -> std::string::String {
90        self.module.emit()
91    }
92    /// Emit the `.mli` interface file.
93    pub fn emit_mli(&self) -> std::string::String {
94        self.module.emit_mli()
95    }
96}
97/// OCaml Bigarray layout.
98#[allow(dead_code)]
99#[derive(Debug, Clone, Copy, PartialEq, Eq)]
100pub enum BigarrayLayout {
101    CLayout,
102    FortranLayout,
103}
104#[allow(dead_code)]
105impl BigarrayLayout {
106    /// Return the layout constant name.
107    pub fn layout_name(&self) -> &'static str {
108        match self {
109            BigarrayLayout::CLayout => "Bigarray.c_layout",
110            BigarrayLayout::FortranLayout => "Bigarray.fortran_layout",
111        }
112    }
113}
114/// A single item in an OCaml module signature.
115#[derive(Debug, Clone)]
116pub enum OcamlSigItem {
117    /// `val name : ty`
118    Val(std::string::String, OcamlType),
119    /// `type t` or full type decl
120    Type(OcamlTypeDef),
121    /// `module M : S`
122    Module(std::string::String, std::string::String),
123    /// `exception E of ty`
124    Exception(std::string::String, Option<OcamlType>),
125}
126#[allow(dead_code)]
127pub struct OCamlPassRegistry {
128    pub(super) configs: Vec<OCamlPassConfig>,
129    pub(super) stats: std::collections::HashMap<String, OCamlPassStats>,
130}
131impl OCamlPassRegistry {
132    #[allow(dead_code)]
133    pub fn new() -> Self {
134        OCamlPassRegistry {
135            configs: Vec::new(),
136            stats: std::collections::HashMap::new(),
137        }
138    }
139    #[allow(dead_code)]
140    pub fn register(&mut self, config: OCamlPassConfig) {
141        self.stats
142            .insert(config.pass_name.clone(), OCamlPassStats::new());
143        self.configs.push(config);
144    }
145    #[allow(dead_code)]
146    pub fn enabled_passes(&self) -> Vec<&OCamlPassConfig> {
147        self.configs.iter().filter(|c| c.enabled).collect()
148    }
149    #[allow(dead_code)]
150    pub fn get_stats(&self, name: &str) -> Option<&OCamlPassStats> {
151        self.stats.get(name)
152    }
153    #[allow(dead_code)]
154    pub fn total_passes(&self) -> usize {
155        self.configs.len()
156    }
157    #[allow(dead_code)]
158    pub fn enabled_count(&self) -> usize {
159        self.enabled_passes().len()
160    }
161    #[allow(dead_code)]
162    pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
163        if let Some(stats) = self.stats.get_mut(name) {
164            stats.record_run(changes, time_ms, iter);
165        }
166    }
167}
168/// OCaml ppx attribute node.
169#[allow(dead_code)]
170#[derive(Debug, Clone)]
171pub struct OcamlPpxAttr {
172    /// Attribute name (e.g. "deriving", "inline", "unrolled").
173    pub name: std::string::String,
174    /// Attribute payload (optional).
175    pub payload: Option<std::string::String>,
176}
177#[allow(dead_code)]
178impl OcamlPpxAttr {
179    /// Create a new ppx attribute.
180    pub fn new(name: &str) -> Self {
181        OcamlPpxAttr {
182            name: name.to_string(),
183            payload: None,
184        }
185    }
186    /// Set the payload.
187    pub fn with_payload(mut self, payload: &str) -> Self {
188        self.payload = Some(payload.to_string());
189        self
190    }
191    /// Deriving attribute: `[@deriving show, eq]`
192    pub fn deriving(traits: &[&str]) -> Self {
193        OcamlPpxAttr {
194            name: "deriving".to_string(),
195            payload: Some(traits.join(", ")),
196        }
197    }
198    /// Emit the attribute.
199    pub fn emit(&self) -> std::string::String {
200        match &self.payload {
201            None => format!("[@{}]", self.name),
202            Some(p) => format!("[@{} {}]", self.name, p),
203        }
204    }
205    /// Emit as a double-bracket attribute.
206    pub fn emit_double(&self) -> std::string::String {
207        match &self.payload {
208            None => format!("[@@{}]", self.name),
209            Some(p) => format!("[@@{} {}]", self.name, p),
210        }
211    }
212}
213/// An OCaml 5.x effect declaration.
214#[allow(dead_code)]
215#[derive(Debug, Clone)]
216pub struct OcamlEffect {
217    /// Effect name (e.g. "State", "Async", "Fail").
218    pub name: std::string::String,
219    /// Effect continuation type.
220    pub params: Vec<OcamlType>,
221    /// Return type of the effect continuation.
222    pub ret: OcamlType,
223}
224#[allow(dead_code)]
225impl OcamlEffect {
226    /// Create a new effect declaration.
227    pub fn new(name: &str, params: Vec<OcamlType>, ret: OcamlType) -> Self {
228        OcamlEffect {
229            name: name.to_string(),
230            params,
231            ret,
232        }
233    }
234    /// Emit the effect type declaration.
235    pub fn emit_decl(&self) -> std::string::String {
236        if self.params.is_empty() {
237            format!("type _ Effect.t += {} : {}", self.name, self.ret)
238        } else {
239            let params_str: Vec<std::string::String> =
240                self.params.iter().map(|t| t.to_string()).collect();
241            format!(
242                "type _ Effect.t += {} : {} -> {}",
243                self.name,
244                params_str.join(" -> "),
245                self.ret
246            )
247        }
248    }
249    /// Emit an `Effect.perform` call.
250    pub fn emit_perform(&self, args: &[&str]) -> std::string::String {
251        if args.is_empty() {
252            format!("Effect.perform {}", self.name)
253        } else {
254            format!("Effect.perform ({} {})", self.name, args.join(" "))
255        }
256    }
257    /// Emit a handler for this effect.
258    pub fn emit_handler_arm(&self, handler_body: &str) -> std::string::String {
259        format!(
260            "| Effect ({name} v), k -> {body}",
261            name = self.name,
262            body = handler_body
263        )
264    }
265}
266/// A Dune library stanza.
267#[allow(dead_code)]
268#[derive(Debug, Clone)]
269pub struct DuneLibrary {
270    /// Library name.
271    pub name: std::string::String,
272    /// Public name (for installed packages).
273    pub public_name: Option<std::string::String>,
274    /// Module names.
275    pub modules: Vec<std::string::String>,
276    /// Library dependencies.
277    pub libraries: Vec<std::string::String>,
278    /// Preprocessor directives.
279    pub preprocess: Vec<std::string::String>,
280    /// Flags passed to ocamlopt/ocamlc.
281    pub ocamlopt_flags: Vec<std::string::String>,
282    /// Whether to wrap modules.
283    pub wrapped: bool,
284    /// Inline tests.
285    pub inline_tests: bool,
286}
287#[allow(dead_code)]
288impl DuneLibrary {
289    /// Create a new Dune library stanza.
290    pub fn new(name: &str) -> Self {
291        DuneLibrary {
292            name: name.to_string(),
293            public_name: None,
294            modules: vec![],
295            libraries: vec![],
296            preprocess: vec![],
297            ocamlopt_flags: vec![],
298            wrapped: true,
299            inline_tests: false,
300        }
301    }
302    /// Set public name.
303    pub fn public_name(mut self, name: &str) -> Self {
304        self.public_name = Some(name.to_string());
305        self
306    }
307    /// Add a module.
308    pub fn add_module(mut self, module: &str) -> Self {
309        self.modules.push(module.to_string());
310        self
311    }
312    /// Add a library dependency.
313    pub fn add_dep(mut self, dep: &str) -> Self {
314        self.libraries.push(dep.to_string());
315        self
316    }
317    /// Add ppx preprocessor.
318    pub fn add_ppx(mut self, ppx: &str) -> Self {
319        self.preprocess.push(format!("(pps {})", ppx));
320        self
321    }
322    /// Disable module wrapping.
323    pub fn unwrapped(mut self) -> Self {
324        self.wrapped = false;
325        self
326    }
327    /// Enable inline tests.
328    pub fn with_inline_tests(mut self) -> Self {
329        self.inline_tests = true;
330        self
331    }
332    /// Emit the Dune library stanza.
333    pub fn emit(&self) -> std::string::String {
334        let mut lines = vec!["(library".to_string()];
335        lines.push(format!(" (name {})", self.name));
336        if let Some(pub_name) = &self.public_name {
337            lines.push(format!(" (public_name {})", pub_name));
338        }
339        if !self.modules.is_empty() {
340            lines.push(format!(" (modules {})", self.modules.join(" ")));
341        }
342        if !self.libraries.is_empty() {
343            lines.push(format!(" (libraries {})", self.libraries.join(" ")));
344        }
345        if !self.preprocess.is_empty() {
346            lines.push(format!(" (preprocess {})", self.preprocess.join(" ")));
347        }
348        if !self.wrapped {
349            lines.push(" (wrapped false)".to_string());
350        }
351        if self.inline_tests {
352            lines.push(" (inline_tests)".to_string());
353        }
354        lines.push(")".to_string());
355        lines.join("\n")
356    }
357}
358/// A field in an OCaml record type.
359#[derive(Debug, Clone)]
360pub struct OcamlRecordField {
361    pub name: std::string::String,
362    pub ty: OcamlType,
363    pub mutable: bool,
364}
365/// OCaml type representation.
366#[derive(Debug, Clone, PartialEq, Eq, Hash)]
367pub enum OcamlType {
368    /// `int`
369    Int,
370    /// `float`
371    Float,
372    /// `bool`
373    Bool,
374    /// `char`
375    Char,
376    /// `string`
377    String,
378    /// `unit`
379    Unit,
380    /// Bottom type (represented as `'a` with no instances in practice)
381    Never,
382    /// `t list`
383    List(Box<OcamlType>),
384    /// `t array`
385    Array(Box<OcamlType>),
386    /// `t1 * t2 * ...` (product/tuple type)
387    Tuple(Vec<OcamlType>),
388    /// `t option`
389    Option(Box<OcamlType>),
390    /// `(t, e) result`
391    Result(Box<OcamlType>, Box<OcamlType>),
392    /// `a -> b` (function type)
393    Fun(Box<OcamlType>, Box<OcamlType>),
394    /// A named type: `my_type`, `Tree.t`
395    Custom(std::string::String),
396    /// A type variable: `'a`, `'b`
397    Polymorphic(std::string::String),
398    /// A module path type: `MyModule.t`
399    Module(std::string::String),
400}
401/// A top-level OCaml let binding.
402#[derive(Debug, Clone)]
403pub struct OcamlLetBinding {
404    pub is_rec: bool,
405    pub name: std::string::String,
406    pub params: Vec<(std::string::String, Option<OcamlType>)>,
407    pub body: OcamlExpr,
408    pub type_annotation: Option<OcamlType>,
409}
410#[allow(dead_code)]
411#[derive(Debug, Clone)]
412pub struct OCamlDominatorTree {
413    pub idom: Vec<Option<u32>>,
414    pub dom_children: Vec<Vec<u32>>,
415    pub dom_depth: Vec<u32>,
416}
417impl OCamlDominatorTree {
418    #[allow(dead_code)]
419    pub fn new(size: usize) -> Self {
420        OCamlDominatorTree {
421            idom: vec![None; size],
422            dom_children: vec![Vec::new(); size],
423            dom_depth: vec![0; size],
424        }
425    }
426    #[allow(dead_code)]
427    pub fn set_idom(&mut self, node: usize, idom: u32) {
428        self.idom[node] = Some(idom);
429    }
430    #[allow(dead_code)]
431    pub fn dominates(&self, a: usize, b: usize) -> bool {
432        if a == b {
433            return true;
434        }
435        let mut cur = b;
436        loop {
437            match self.idom[cur] {
438                Some(parent) if parent as usize == a => return true,
439                Some(parent) if parent as usize == cur => return false,
440                Some(parent) => cur = parent as usize,
441                None => return false,
442            }
443        }
444    }
445    #[allow(dead_code)]
446    pub fn depth(&self, node: usize) -> u32 {
447        self.dom_depth.get(node).copied().unwrap_or(0)
448    }
449}
450/// A full OCaml type definition.
451#[derive(Debug, Clone)]
452pub struct OcamlTypeDef {
453    pub name: std::string::String,
454    /// Type parameters: `'a`, `'b`, etc.
455    pub type_params: Vec<std::string::String>,
456    pub decl: OcamlTypeDecl,
457}
458/// An OUnit2 test suite.
459#[allow(dead_code)]
460#[derive(Debug, Clone)]
461pub struct OcamlTestSuite {
462    /// Suite name.
463    pub name: std::string::String,
464    /// Test cases.
465    pub cases: Vec<OcamlTestCase>,
466}
467#[allow(dead_code)]
468impl OcamlTestSuite {
469    /// Create a new test suite.
470    pub fn new(name: &str) -> Self {
471        OcamlTestSuite {
472            name: name.to_string(),
473            cases: vec![],
474        }
475    }
476    /// Add a test case.
477    pub fn add(mut self, case: OcamlTestCase) -> Self {
478        self.cases.push(case);
479        self
480    }
481    /// Emit OUnit2 test suite.
482    pub fn emit_ounit(&self) -> std::string::String {
483        let mut lines = vec![];
484        lines.push("open OUnit2".to_string());
485        lines.push(std::string::String::new());
486        let cases_str: Vec<std::string::String> = self
487            .cases
488            .iter()
489            .map(|c| format!("  {}", c.emit_ounit()))
490            .collect();
491        lines.push(format!("let suite = \"{}\" >::: [", self.name));
492        lines.push(cases_str.join(";\n"));
493        lines.push("]".to_string());
494        lines.push(std::string::String::new());
495        lines.push("let () = run_test_tt_main suite".to_string());
496        lines.join("\n")
497    }
498    /// Emit Alcotest test suite.
499    pub fn emit_alcotest(&self) -> std::string::String {
500        let mut lines = vec![];
501        lines.push("let () =".to_string());
502        lines.push("  Alcotest.run \"tests\" [".to_string());
503        let cases_str: Vec<std::string::String> = self
504            .cases
505            .iter()
506            .map(|c| format!("    {}", c.emit_alcotest("Quick")))
507            .collect();
508        lines.push(format!("    \"{}\", [", self.name));
509        lines.push(cases_str.join(";\n"));
510        lines.push("    ]".to_string());
511        lines.push("  ]".to_string());
512        lines.join("\n")
513    }
514}
515/// An OCaml Generalized Algebraic Data Type (GADT) variant.
516#[allow(dead_code)]
517#[derive(Debug, Clone)]
518pub struct OcamlGadtVariant {
519    /// Constructor name.
520    pub name: std::string::String,
521    /// Parameter types.
522    pub params: Vec<OcamlType>,
523    /// The resulting index type (the right-hand side of `:`).
524    pub result_type: std::string::String,
525}
526/// An OCaml functor definition.
527#[allow(dead_code)]
528#[derive(Debug, Clone)]
529pub struct OcamlFunctor {
530    /// Functor name.
531    pub name: std::string::String,
532    /// Parameters.
533    pub params: Vec<OcamlFunctorParam>,
534    /// Body definitions.
535    pub body: Vec<OcamlDefinition>,
536    /// Optional result signature constraint.
537    pub sig_constraint: Option<std::string::String>,
538}
539#[allow(dead_code)]
540impl OcamlFunctor {
541    /// Create a new functor.
542    pub fn new(name: &str) -> Self {
543        OcamlFunctor {
544            name: name.to_string(),
545            params: vec![],
546            body: vec![],
547            sig_constraint: None,
548        }
549    }
550    /// Add a functor parameter.
551    pub fn add_param(mut self, name: &str, module_type: &str) -> Self {
552        self.params.push(OcamlFunctorParam {
553            name: name.to_string(),
554            module_type: module_type.to_string(),
555        });
556        self
557    }
558    /// Add a body definition.
559    pub fn add_def(mut self, def: OcamlDefinition) -> Self {
560        self.body.push(def);
561        self
562    }
563    /// Set a result signature constraint.
564    pub fn with_sig(mut self, sig: &str) -> Self {
565        self.sig_constraint = Some(sig.to_string());
566        self
567    }
568    /// Emit the functor definition.
569    pub fn emit(&self) -> std::string::String {
570        let params_str: Vec<std::string::String> = self
571            .params
572            .iter()
573            .map(|p| format!("({} : {})", p.name, p.module_type))
574            .collect();
575        let sig_str = self
576            .sig_constraint
577            .as_ref()
578            .map(|s| format!(" : {}", s))
579            .unwrap_or_default();
580        let mut lines = vec![format!(
581            "module {} {}{}= struct",
582            self.name,
583            params_str.join(" "),
584            sig_str
585        )];
586        for def in &self.body {
587            for line in def.to_string().lines() {
588                lines.push(format!("  {}", line));
589            }
590        }
591        lines.push("end".to_string());
592        lines.join("\n")
593    }
594}
595/// A Dune executable stanza.
596#[allow(dead_code)]
597#[derive(Debug, Clone)]
598pub struct DuneExecutable {
599    /// Executable name.
600    pub name: std::string::String,
601    /// Public name (installed as).
602    pub public_name: Option<std::string::String>,
603    /// Library dependencies.
604    pub libraries: Vec<std::string::String>,
605    /// Preprocessors.
606    pub preprocess: Vec<std::string::String>,
607    /// Extra OCaml flags.
608    pub flags: Vec<std::string::String>,
609}
610#[allow(dead_code)]
611impl DuneExecutable {
612    /// Create a new Dune executable stanza.
613    pub fn new(name: &str) -> Self {
614        DuneExecutable {
615            name: name.to_string(),
616            public_name: None,
617            libraries: vec![],
618            preprocess: vec![],
619            flags: vec![],
620        }
621    }
622    /// Add a dependency.
623    pub fn add_dep(mut self, dep: &str) -> Self {
624        self.libraries.push(dep.to_string());
625        self
626    }
627    /// Add ppx.
628    pub fn add_ppx(mut self, ppx: &str) -> Self {
629        self.preprocess.push(format!("(pps {})", ppx));
630        self
631    }
632    /// Emit the stanza.
633    pub fn emit(&self) -> std::string::String {
634        let mut lines = vec!["(executable".to_string()];
635        lines.push(format!(" (name {})", self.name));
636        if let Some(pub_name) = &self.public_name {
637            lines.push(format!(" (public_name {})", pub_name));
638        }
639        if !self.libraries.is_empty() {
640            lines.push(format!(" (libraries {})", self.libraries.join(" ")));
641        }
642        if !self.preprocess.is_empty() {
643            lines.push(format!(" (preprocess {})", self.preprocess.join(" ")));
644        }
645        lines.push(")".to_string());
646        lines.join("\n")
647    }
648}
649#[allow(dead_code)]
650#[derive(Debug, Clone)]
651pub struct OCamlAnalysisCache {
652    pub(super) entries: std::collections::HashMap<String, OCamlCacheEntry>,
653    pub(super) max_size: usize,
654    pub(super) hits: u64,
655    pub(super) misses: u64,
656}
657impl OCamlAnalysisCache {
658    #[allow(dead_code)]
659    pub fn new(max_size: usize) -> Self {
660        OCamlAnalysisCache {
661            entries: std::collections::HashMap::new(),
662            max_size,
663            hits: 0,
664            misses: 0,
665        }
666    }
667    #[allow(dead_code)]
668    pub fn get(&mut self, key: &str) -> Option<&OCamlCacheEntry> {
669        if self.entries.contains_key(key) {
670            self.hits += 1;
671            self.entries.get(key)
672        } else {
673            self.misses += 1;
674            None
675        }
676    }
677    #[allow(dead_code)]
678    pub fn insert(&mut self, key: String, data: Vec<u8>) {
679        if self.entries.len() >= self.max_size {
680            if let Some(oldest) = self.entries.keys().next().cloned() {
681                self.entries.remove(&oldest);
682            }
683        }
684        self.entries.insert(
685            key.clone(),
686            OCamlCacheEntry {
687                key,
688                data,
689                timestamp: 0,
690                valid: true,
691            },
692        );
693    }
694    #[allow(dead_code)]
695    pub fn invalidate(&mut self, key: &str) {
696        if let Some(entry) = self.entries.get_mut(key) {
697            entry.valid = false;
698        }
699    }
700    #[allow(dead_code)]
701    pub fn clear(&mut self) {
702        self.entries.clear();
703    }
704    #[allow(dead_code)]
705    pub fn hit_rate(&self) -> f64 {
706        let total = self.hits + self.misses;
707        if total == 0 {
708            return 0.0;
709        }
710        self.hits as f64 / total as f64
711    }
712    #[allow(dead_code)]
713    pub fn size(&self) -> usize {
714        self.entries.len()
715    }
716}
717/// An OCaml module parameter (functor argument).
718#[allow(dead_code)]
719#[derive(Debug, Clone)]
720pub struct OcamlFunctorParam {
721    /// Parameter name (e.g. "M").
722    pub name: std::string::String,
723    /// Module type (e.g. "Map.OrderedType").
724    pub module_type: std::string::String,
725}
726#[allow(dead_code)]
727#[derive(Debug, Clone)]
728pub struct OCamlDepGraph {
729    pub(super) nodes: Vec<u32>,
730    pub(super) edges: Vec<(u32, u32)>,
731}
732impl OCamlDepGraph {
733    #[allow(dead_code)]
734    pub fn new() -> Self {
735        OCamlDepGraph {
736            nodes: Vec::new(),
737            edges: Vec::new(),
738        }
739    }
740    #[allow(dead_code)]
741    pub fn add_node(&mut self, id: u32) {
742        if !self.nodes.contains(&id) {
743            self.nodes.push(id);
744        }
745    }
746    #[allow(dead_code)]
747    pub fn add_dep(&mut self, dep: u32, dependent: u32) {
748        self.add_node(dep);
749        self.add_node(dependent);
750        self.edges.push((dep, dependent));
751    }
752    #[allow(dead_code)]
753    pub fn dependents_of(&self, node: u32) -> Vec<u32> {
754        self.edges
755            .iter()
756            .filter(|(d, _)| *d == node)
757            .map(|(_, dep)| *dep)
758            .collect()
759    }
760    #[allow(dead_code)]
761    pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
762        self.edges
763            .iter()
764            .filter(|(_, dep)| *dep == node)
765            .map(|(d, _)| *d)
766            .collect()
767    }
768    #[allow(dead_code)]
769    pub fn topological_sort(&self) -> Vec<u32> {
770        let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
771        for &n in &self.nodes {
772            in_degree.insert(n, 0);
773        }
774        for (_, dep) in &self.edges {
775            *in_degree.entry(*dep).or_insert(0) += 1;
776        }
777        let mut queue: std::collections::VecDeque<u32> = self
778            .nodes
779            .iter()
780            .filter(|&&n| in_degree[&n] == 0)
781            .copied()
782            .collect();
783        let mut result = Vec::new();
784        while let Some(node) = queue.pop_front() {
785            result.push(node);
786            for dep in self.dependents_of(node) {
787                let cnt = in_degree.entry(dep).or_insert(0);
788                *cnt = cnt.saturating_sub(1);
789                if *cnt == 0 {
790                    queue.push_back(dep);
791                }
792            }
793        }
794        result
795    }
796    #[allow(dead_code)]
797    pub fn has_cycle(&self) -> bool {
798        self.topological_sort().len() < self.nodes.len()
799    }
800}
801/// An OUnit2-style test case.
802#[allow(dead_code)]
803#[derive(Debug, Clone)]
804pub struct OcamlTestCase {
805    /// Test name.
806    pub name: std::string::String,
807    /// Test body code.
808    pub body: std::string::String,
809    /// Expected result (for assert_equal).
810    pub expected: Option<std::string::String>,
811    /// Actual expression to evaluate.
812    pub actual: Option<std::string::String>,
813}
814#[allow(dead_code)]
815impl OcamlTestCase {
816    /// Create a new test case.
817    pub fn new(name: &str, body: &str) -> Self {
818        OcamlTestCase {
819            name: name.to_string(),
820            body: body.to_string(),
821            expected: None,
822            actual: None,
823        }
824    }
825    /// Create an equality assertion test.
826    pub fn assert_equal(name: &str, expected: &str, actual: &str) -> Self {
827        OcamlTestCase {
828            name: name.to_string(),
829            body: format!("assert_equal ({}) ({})", expected, actual),
830            expected: Some(expected.to_string()),
831            actual: Some(actual.to_string()),
832        }
833    }
834    /// Emit as an OUnit2 test case.
835    pub fn emit_ounit(&self) -> std::string::String {
836        format!("\"{}\" >:: (fun _ -> {})", self.name, self.body)
837    }
838    /// Emit as an inline Alcotest test.
839    pub fn emit_alcotest(&self, test_type: &str) -> std::string::String {
840        format!(
841            "Alcotest.test_case \"{}\" `{} (fun () -> {})",
842            self.name, test_type, self.body
843        )
844    }
845}
846/// A top-level definition in an OCaml module.
847#[derive(Debug, Clone)]
848pub enum OcamlDefinition {
849    TypeDef(OcamlTypeDef),
850    Let(OcamlLetBinding),
851    Signature(OcamlSignature),
852    /// `exception Name of ty`
853    Exception(std::string::String, Option<OcamlType>),
854    /// `open Module`
855    Open(std::string::String),
856    /// A nested module: `module M = struct ... end`
857    SubModule(OcamlModule),
858    /// A raw comment
859    Comment(std::string::String),
860}
861/// OCaml Bigarray kind.
862#[allow(dead_code)]
863#[derive(Debug, Clone, Copy, PartialEq, Eq)]
864pub enum BigarrayKind {
865    Float32,
866    Float64,
867    Int32,
868    Int64,
869    Int,
870    Complex32,
871    Complex64,
872}
873#[allow(dead_code)]
874impl BigarrayKind {
875    /// Return the Bigarray kind constant name.
876    pub fn kind_name(&self) -> &'static str {
877        match self {
878            BigarrayKind::Float32 => "Bigarray.float32",
879            BigarrayKind::Float64 => "Bigarray.float64",
880            BigarrayKind::Int32 => "Bigarray.int32",
881            BigarrayKind::Int64 => "Bigarray.int64",
882            BigarrayKind::Int => "Bigarray.int",
883            BigarrayKind::Complex32 => "Bigarray.complex32",
884            BigarrayKind::Complex64 => "Bigarray.complex64",
885        }
886    }
887    /// Return the corresponding OCaml element type name.
888    pub fn element_type(&self) -> &'static str {
889        match self {
890            BigarrayKind::Float32 => "float",
891            BigarrayKind::Float64 => "float",
892            BigarrayKind::Int32 => "int32",
893            BigarrayKind::Int64 => "int64",
894            BigarrayKind::Int => "int",
895            BigarrayKind::Complex32 => "Complex.t",
896            BigarrayKind::Complex64 => "Complex.t",
897        }
898    }
899}
900/// OCaml pattern for match arms.
901#[derive(Debug, Clone, PartialEq)]
902pub enum OcamlPattern {
903    /// `_` (wildcard)
904    Wildcard,
905    /// `x` (bind to variable)
906    Var(std::string::String),
907    /// A constant pattern: `42`, `true`, `'a'`, `"hello"`
908    Const(OcamlLit),
909    /// A tuple pattern: `(p1, p2, ...)`
910    Tuple(Vec<OcamlPattern>),
911    /// A cons pattern: `h :: t`
912    Cons(Box<OcamlPattern>, Box<OcamlPattern>),
913    /// A list pattern: `[p1; p2; ...]`
914    List(Vec<OcamlPattern>),
915    /// A constructor pattern: `Some p`, `Error e`, `Leaf`, `Node (l, v, r)`
916    Ctor(std::string::String, Vec<OcamlPattern>),
917    /// A record pattern: `{ field1 = p1; field2 = p2; ... }`
918    Record(Vec<(std::string::String, OcamlPattern)>),
919    /// An or-pattern: `p1 | p2`
920    Or(Box<OcamlPattern>, Box<OcamlPattern>),
921    /// An as-pattern: `p as x`
922    As(Box<OcamlPattern>, std::string::String),
923}
924#[allow(dead_code)]
925#[derive(Debug, Clone)]
926pub struct OCamlLivenessInfo {
927    pub live_in: Vec<std::collections::HashSet<u32>>,
928    pub live_out: Vec<std::collections::HashSet<u32>>,
929    pub defs: Vec<std::collections::HashSet<u32>>,
930    pub uses: Vec<std::collections::HashSet<u32>>,
931}
932impl OCamlLivenessInfo {
933    #[allow(dead_code)]
934    pub fn new(block_count: usize) -> Self {
935        OCamlLivenessInfo {
936            live_in: vec![std::collections::HashSet::new(); block_count],
937            live_out: vec![std::collections::HashSet::new(); block_count],
938            defs: vec![std::collections::HashSet::new(); block_count],
939            uses: vec![std::collections::HashSet::new(); block_count],
940        }
941    }
942    #[allow(dead_code)]
943    pub fn add_def(&mut self, block: usize, var: u32) {
944        if block < self.defs.len() {
945            self.defs[block].insert(var);
946        }
947    }
948    #[allow(dead_code)]
949    pub fn add_use(&mut self, block: usize, var: u32) {
950        if block < self.uses.len() {
951            self.uses[block].insert(var);
952        }
953    }
954    #[allow(dead_code)]
955    pub fn is_live_in(&self, block: usize, var: u32) -> bool {
956        self.live_in
957            .get(block)
958            .map(|s| s.contains(&var))
959            .unwrap_or(false)
960    }
961    #[allow(dead_code)]
962    pub fn is_live_out(&self, block: usize, var: u32) -> bool {
963        self.live_out
964            .get(block)
965            .map(|s| s.contains(&var))
966            .unwrap_or(false)
967    }
968}
969/// OCaml literal values.
970#[derive(Debug, Clone, PartialEq)]
971pub enum OcamlLit {
972    Int(i64),
973    Float(f64),
974    Bool(bool),
975    Char(char),
976    Str(std::string::String),
977    Unit,
978}
979/// An OCaml type declaration body.
980#[derive(Debug, Clone)]
981pub enum OcamlTypeDecl {
982    /// A type alias: `type t = int`
983    Alias(OcamlType),
984    /// A record type: `type t = { field: ty; ... }`
985    Record(Vec<OcamlRecordField>),
986    /// A variant type: `type t = A | B of ty1 * ty2 | ...`
987    Variant(Vec<(std::string::String, Vec<OcamlType>)>),
988    /// An abstract type (signature only): `type t`
989    Abstract,
990}
991/// An OCaml GADT type definition.
992#[allow(dead_code)]
993#[derive(Debug, Clone)]
994pub struct OcamlGadt {
995    /// GADT type name.
996    pub name: std::string::String,
997    /// Type parameter names.
998    pub type_params: Vec<std::string::String>,
999    /// Variants.
1000    pub variants: Vec<OcamlGadtVariant>,
1001}
1002#[allow(dead_code)]
1003impl OcamlGadt {
1004    /// Create a new GADT.
1005    pub fn new(name: &str, type_params: Vec<&str>) -> Self {
1006        OcamlGadt {
1007            name: name.to_string(),
1008            type_params: type_params.iter().map(|s| s.to_string()).collect(),
1009            variants: vec![],
1010        }
1011    }
1012    /// Add a variant.
1013    pub fn add_variant(mut self, name: &str, params: Vec<OcamlType>, result: &str) -> Self {
1014        self.variants.push(OcamlGadtVariant {
1015            name: name.to_string(),
1016            params,
1017            result_type: result.to_string(),
1018        });
1019        self
1020    }
1021    /// Emit the GADT definition.
1022    pub fn emit(&self) -> std::string::String {
1023        let type_params_str = if self.type_params.is_empty() {
1024            std::string::String::new()
1025        } else {
1026            format!(
1027                "({}) ",
1028                self.type_params
1029                    .iter()
1030                    .map(|p| format!("'{}", p))
1031                    .collect::<Vec<_>>()
1032                    .join(", ")
1033            )
1034        };
1035        let mut lines = vec![format!("type {}{}=", type_params_str, self.name)];
1036        for v in &self.variants {
1037            if v.params.is_empty() {
1038                lines.push(format!("  | {} : {}", v.name, v.result_type));
1039            } else {
1040                let params_str: Vec<std::string::String> =
1041                    v.params.iter().map(|t| t.to_string()).collect();
1042                lines.push(format!(
1043                    "  | {} : {} -> {}",
1044                    v.name,
1045                    params_str.join(" * "),
1046                    v.result_type
1047                ));
1048            }
1049        }
1050        lines.join("\n")
1051    }
1052}
1053#[allow(dead_code)]
1054#[derive(Debug, Clone)]
1055pub struct OCamlCacheEntry {
1056    pub key: String,
1057    pub data: Vec<u8>,
1058    pub timestamp: u64,
1059    pub valid: bool,
1060}
1061#[allow(dead_code)]
1062pub struct OCamlConstantFoldingHelper;
1063impl OCamlConstantFoldingHelper {
1064    #[allow(dead_code)]
1065    pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
1066        a.checked_add(b)
1067    }
1068    #[allow(dead_code)]
1069    pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
1070        a.checked_sub(b)
1071    }
1072    #[allow(dead_code)]
1073    pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
1074        a.checked_mul(b)
1075    }
1076    #[allow(dead_code)]
1077    pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
1078        if b == 0 {
1079            None
1080        } else {
1081            a.checked_div(b)
1082        }
1083    }
1084    #[allow(dead_code)]
1085    pub fn fold_add_f64(a: f64, b: f64) -> f64 {
1086        a + b
1087    }
1088    #[allow(dead_code)]
1089    pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
1090        a * b
1091    }
1092    #[allow(dead_code)]
1093    pub fn fold_neg_i64(a: i64) -> Option<i64> {
1094        a.checked_neg()
1095    }
1096    #[allow(dead_code)]
1097    pub fn fold_not_bool(a: bool) -> bool {
1098        !a
1099    }
1100    #[allow(dead_code)]
1101    pub fn fold_and_bool(a: bool, b: bool) -> bool {
1102        a && b
1103    }
1104    #[allow(dead_code)]
1105    pub fn fold_or_bool(a: bool, b: bool) -> bool {
1106        a || b
1107    }
1108    #[allow(dead_code)]
1109    pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
1110        a.checked_shl(b)
1111    }
1112    #[allow(dead_code)]
1113    pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
1114        a.checked_shr(b)
1115    }
1116    #[allow(dead_code)]
1117    pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
1118        if b == 0 {
1119            None
1120        } else {
1121            Some(a % b)
1122        }
1123    }
1124    #[allow(dead_code)]
1125    pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
1126        a & b
1127    }
1128    #[allow(dead_code)]
1129    pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
1130        a | b
1131    }
1132    #[allow(dead_code)]
1133    pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
1134        a ^ b
1135    }
1136    #[allow(dead_code)]
1137    pub fn fold_bitnot_i64(a: i64) -> i64 {
1138        !a
1139    }
1140}
1141#[allow(dead_code)]
1142#[derive(Debug, Clone, PartialEq)]
1143pub enum OCamlPassPhase {
1144    Analysis,
1145    Transformation,
1146    Verification,
1147    Cleanup,
1148}
1149impl OCamlPassPhase {
1150    #[allow(dead_code)]
1151    pub fn name(&self) -> &str {
1152        match self {
1153            OCamlPassPhase::Analysis => "analysis",
1154            OCamlPassPhase::Transformation => "transformation",
1155            OCamlPassPhase::Verification => "verification",
1156            OCamlPassPhase::Cleanup => "cleanup",
1157        }
1158    }
1159    #[allow(dead_code)]
1160    pub fn is_modifying(&self) -> bool {
1161        matches!(
1162            self,
1163            OCamlPassPhase::Transformation | OCamlPassPhase::Cleanup
1164        )
1165    }
1166}
1167/// An OCaml module (`.ml` file or nested `module M = struct ... end`).
1168#[derive(Debug, Clone)]
1169pub struct OcamlModule {
1170    pub name: std::string::String,
1171    pub definitions: Vec<OcamlDefinition>,
1172    /// Whether this is a top-level file module (affects emit format).
1173    pub is_top_level: bool,
1174}
1175impl OcamlModule {
1176    /// Create a new top-level module.
1177    pub fn new(name: &str) -> Self {
1178        OcamlModule {
1179            name: name.to_string(),
1180            definitions: Vec::new(),
1181            is_top_level: true,
1182        }
1183    }
1184    /// Add a definition to the module.
1185    pub fn add(&mut self, def: OcamlDefinition) {
1186        self.definitions.push(def);
1187    }
1188    /// Emit the module as OCaml source code (`.ml`).
1189    pub fn emit(&self) -> std::string::String {
1190        if self.is_top_level {
1191            let mut out = std::string::String::new();
1192            for def in &self.definitions {
1193                out.push_str(&format!("{}\n\n", def));
1194            }
1195            out
1196        } else {
1197            let mut out = format!("module {} = struct\n", self.name);
1198            for def in &self.definitions {
1199                let text = def.to_string();
1200                for line in text.lines() {
1201                    out.push_str("  ");
1202                    out.push_str(line);
1203                    out.push('\n');
1204                }
1205                out.push('\n');
1206            }
1207            out.push_str("end");
1208            out
1209        }
1210    }
1211    /// Emit the module interface as OCaml source code (`.mli`).
1212    pub fn emit_mli(&self) -> std::string::String {
1213        let mut out = std::string::String::new();
1214        for def in &self.definitions {
1215            match def {
1216                OcamlDefinition::TypeDef(td) => {
1217                    out.push_str(&format!("{}\n\n", td));
1218                }
1219                OcamlDefinition::Let(lb) => {
1220                    if let Some(ret_ty) = &lb.type_annotation {
1221                        if lb.params.is_empty() {
1222                            out.push_str(&format!("val {} : {}\n\n", lb.name, ret_ty));
1223                        } else {
1224                            let mut ty = ret_ty.clone();
1225                            for (_, param_ty) in lb.params.iter().rev() {
1226                                let domain = param_ty
1227                                    .clone()
1228                                    .unwrap_or(OcamlType::Custom("_".to_string()));
1229                                ty = OcamlType::Fun(Box::new(domain), Box::new(ty));
1230                            }
1231                            out.push_str(&format!("val {} : {}\n\n", lb.name, ty));
1232                        }
1233                    }
1234                }
1235                OcamlDefinition::Signature(sig) => {
1236                    out.push_str(&format!("{}\n\n", sig));
1237                }
1238                OcamlDefinition::Exception(name, ty) => {
1239                    if let Some(t) = ty {
1240                        out.push_str(&format!("exception {} of {}\n\n", name, t));
1241                    } else {
1242                        out.push_str(&format!("exception {}\n\n", name));
1243                    }
1244                }
1245                OcamlDefinition::Open(m) => {
1246                    out.push_str(&format!("open {}\n\n", m));
1247                }
1248                OcamlDefinition::Comment(text) => {
1249                    out.push_str(&format!("(* {} *)\n\n", text));
1250                }
1251                OcamlDefinition::SubModule(_) => {}
1252            }
1253        }
1254        out
1255    }
1256}
1257#[allow(dead_code)]
1258#[derive(Debug, Clone)]
1259pub struct OCamlWorklist {
1260    pub(super) items: std::collections::VecDeque<u32>,
1261    pub(super) in_worklist: std::collections::HashSet<u32>,
1262}
1263impl OCamlWorklist {
1264    #[allow(dead_code)]
1265    pub fn new() -> Self {
1266        OCamlWorklist {
1267            items: std::collections::VecDeque::new(),
1268            in_worklist: std::collections::HashSet::new(),
1269        }
1270    }
1271    #[allow(dead_code)]
1272    pub fn push(&mut self, item: u32) -> bool {
1273        if self.in_worklist.insert(item) {
1274            self.items.push_back(item);
1275            true
1276        } else {
1277            false
1278        }
1279    }
1280    #[allow(dead_code)]
1281    pub fn pop(&mut self) -> Option<u32> {
1282        let item = self.items.pop_front()?;
1283        self.in_worklist.remove(&item);
1284        Some(item)
1285    }
1286    #[allow(dead_code)]
1287    pub fn is_empty(&self) -> bool {
1288        self.items.is_empty()
1289    }
1290    #[allow(dead_code)]
1291    pub fn len(&self) -> usize {
1292        self.items.len()
1293    }
1294    #[allow(dead_code)]
1295    pub fn contains(&self, item: u32) -> bool {
1296        self.in_worklist.contains(&item)
1297    }
1298}
1299/// OCaml expression AST node.
1300#[derive(Debug, Clone, PartialEq)]
1301pub enum OcamlExpr {
1302    /// A literal value.
1303    Lit(OcamlLit),
1304    /// A variable or module path: `x`, `List.map`
1305    Var(std::string::String),
1306    /// A binary operation: `a + b`, `a && b`
1307    BinOp(std::string::String, Box<OcamlExpr>, Box<OcamlExpr>),
1308    /// A unary operation: `not b`, `- n`
1309    UnaryOp(std::string::String, Box<OcamlExpr>),
1310    /// Function application: `f x` or `f x y` (curried)
1311    App(Box<OcamlExpr>, Vec<OcamlExpr>),
1312    /// A lambda: `fun x -> body`
1313    Lambda(Vec<std::string::String>, Box<OcamlExpr>),
1314    /// A let binding: `let x = e1 in e2`
1315    Let(std::string::String, Box<OcamlExpr>, Box<OcamlExpr>),
1316    /// A recursive let binding: `let rec f x = e1 in e2`
1317    LetRec(
1318        std::string::String,
1319        Vec<std::string::String>,
1320        Box<OcamlExpr>,
1321        Box<OcamlExpr>,
1322    ),
1323    /// If-then-else: `if cond then e1 else e2`
1324    IfThenElse(Box<OcamlExpr>, Box<OcamlExpr>, Box<OcamlExpr>),
1325    /// Match expression: `match e with | p1 -> e1 | p2 -> e2`
1326    Match(Box<OcamlExpr>, Vec<(OcamlPattern, OcamlExpr)>),
1327    /// Tuple: `(e1, e2, ...)`
1328    Tuple(Vec<OcamlExpr>),
1329    /// List literal: `[e1; e2; ...]`
1330    List(Vec<OcamlExpr>),
1331    /// Record literal: `{ field1 = e1; field2 = e2; ... }`
1332    Record(Vec<(std::string::String, OcamlExpr)>),
1333    /// Record field access: `e.field`
1334    Field(Box<OcamlExpr>, std::string::String),
1335    /// Module expression: `Module.expr`
1336    Module(std::string::String, Box<OcamlExpr>),
1337    /// `begin ... end` block (sequence of expressions)
1338    Begin(Vec<OcamlExpr>),
1339}
1340#[allow(dead_code)]
1341#[derive(Debug, Clone)]
1342pub struct OCamlPassConfig {
1343    pub phase: OCamlPassPhase,
1344    pub enabled: bool,
1345    pub max_iterations: u32,
1346    pub debug_output: bool,
1347    pub pass_name: String,
1348}
1349impl OCamlPassConfig {
1350    #[allow(dead_code)]
1351    pub fn new(name: impl Into<String>, phase: OCamlPassPhase) -> Self {
1352        OCamlPassConfig {
1353            phase,
1354            enabled: true,
1355            max_iterations: 10,
1356            debug_output: false,
1357            pass_name: name.into(),
1358        }
1359    }
1360    #[allow(dead_code)]
1361    pub fn disabled(mut self) -> Self {
1362        self.enabled = false;
1363        self
1364    }
1365    #[allow(dead_code)]
1366    pub fn with_debug(mut self) -> Self {
1367        self.debug_output = true;
1368        self
1369    }
1370    #[allow(dead_code)]
1371    pub fn max_iter(mut self, n: u32) -> Self {
1372        self.max_iterations = n;
1373        self
1374    }
1375}
1376/// An OCaml module signature (`sig ... end`).
1377#[derive(Debug, Clone)]
1378pub struct OcamlSignature {
1379    pub name: std::string::String,
1380    pub items: Vec<OcamlSigItem>,
1381}
1382#[allow(dead_code)]
1383#[derive(Debug, Clone, Default)]
1384pub struct OCamlPassStats {
1385    pub total_runs: u32,
1386    pub successful_runs: u32,
1387    pub total_changes: u64,
1388    pub time_ms: u64,
1389    pub iterations_used: u32,
1390}
1391impl OCamlPassStats {
1392    #[allow(dead_code)]
1393    pub fn new() -> Self {
1394        Self::default()
1395    }
1396    #[allow(dead_code)]
1397    pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
1398        self.total_runs += 1;
1399        self.successful_runs += 1;
1400        self.total_changes += changes;
1401        self.time_ms += time_ms;
1402        self.iterations_used = iterations;
1403    }
1404    #[allow(dead_code)]
1405    pub fn average_changes_per_run(&self) -> f64 {
1406        if self.total_runs == 0 {
1407            return 0.0;
1408        }
1409        self.total_changes as f64 / self.total_runs as f64
1410    }
1411    #[allow(dead_code)]
1412    pub fn success_rate(&self) -> f64 {
1413        if self.total_runs == 0 {
1414            return 0.0;
1415        }
1416        self.successful_runs as f64 / self.total_runs as f64
1417    }
1418    #[allow(dead_code)]
1419    pub fn format_summary(&self) -> String {
1420        format!(
1421            "Runs: {}/{}, Changes: {}, Time: {}ms",
1422            self.successful_runs, self.total_runs, self.total_changes, self.time_ms
1423        )
1424    }
1425}