1use std::collections::{HashMap, HashSet, VecDeque};
6
7pub struct OcamlBackend {
9 pub(super) module: OcamlModule,
10}
11impl OcamlBackend {
12 pub fn new(module_name: &str) -> Self {
14 OcamlBackend {
15 module: OcamlModule::new(module_name),
16 }
17 }
18 pub fn add_definition(&mut self, def: OcamlDefinition) {
20 self.module.add(def);
21 }
22 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 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 pub fn emit_module(&self) -> std::string::String {
90 self.module.emit()
91 }
92 pub fn emit_mli(&self) -> std::string::String {
94 self.module.emit_mli()
95 }
96}
97#[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 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#[derive(Debug, Clone)]
116pub enum OcamlSigItem {
117 Val(std::string::String, OcamlType),
119 Type(OcamlTypeDef),
121 Module(std::string::String, std::string::String),
123 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#[allow(dead_code)]
170#[derive(Debug, Clone)]
171pub struct OcamlPpxAttr {
172 pub name: std::string::String,
174 pub payload: Option<std::string::String>,
176}
177#[allow(dead_code)]
178impl OcamlPpxAttr {
179 pub fn new(name: &str) -> Self {
181 OcamlPpxAttr {
182 name: name.to_string(),
183 payload: None,
184 }
185 }
186 pub fn with_payload(mut self, payload: &str) -> Self {
188 self.payload = Some(payload.to_string());
189 self
190 }
191 pub fn deriving(traits: &[&str]) -> Self {
193 OcamlPpxAttr {
194 name: "deriving".to_string(),
195 payload: Some(traits.join(", ")),
196 }
197 }
198 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 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#[allow(dead_code)]
215#[derive(Debug, Clone)]
216pub struct OcamlEffect {
217 pub name: std::string::String,
219 pub params: Vec<OcamlType>,
221 pub ret: OcamlType,
223}
224#[allow(dead_code)]
225impl OcamlEffect {
226 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 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 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 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#[allow(dead_code)]
268#[derive(Debug, Clone)]
269pub struct DuneLibrary {
270 pub name: std::string::String,
272 pub public_name: Option<std::string::String>,
274 pub modules: Vec<std::string::String>,
276 pub libraries: Vec<std::string::String>,
278 pub preprocess: Vec<std::string::String>,
280 pub ocamlopt_flags: Vec<std::string::String>,
282 pub wrapped: bool,
284 pub inline_tests: bool,
286}
287#[allow(dead_code)]
288impl DuneLibrary {
289 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 pub fn public_name(mut self, name: &str) -> Self {
304 self.public_name = Some(name.to_string());
305 self
306 }
307 pub fn add_module(mut self, module: &str) -> Self {
309 self.modules.push(module.to_string());
310 self
311 }
312 pub fn add_dep(mut self, dep: &str) -> Self {
314 self.libraries.push(dep.to_string());
315 self
316 }
317 pub fn add_ppx(mut self, ppx: &str) -> Self {
319 self.preprocess.push(format!("(pps {})", ppx));
320 self
321 }
322 pub fn unwrapped(mut self) -> Self {
324 self.wrapped = false;
325 self
326 }
327 pub fn with_inline_tests(mut self) -> Self {
329 self.inline_tests = true;
330 self
331 }
332 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#[derive(Debug, Clone)]
360pub struct OcamlRecordField {
361 pub name: std::string::String,
362 pub ty: OcamlType,
363 pub mutable: bool,
364}
365#[derive(Debug, Clone, PartialEq, Eq, Hash)]
367pub enum OcamlType {
368 Int,
370 Float,
372 Bool,
374 Char,
376 String,
378 Unit,
380 Never,
382 List(Box<OcamlType>),
384 Array(Box<OcamlType>),
386 Tuple(Vec<OcamlType>),
388 Option(Box<OcamlType>),
390 Result(Box<OcamlType>, Box<OcamlType>),
392 Fun(Box<OcamlType>, Box<OcamlType>),
394 Custom(std::string::String),
396 Polymorphic(std::string::String),
398 Module(std::string::String),
400}
401#[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#[derive(Debug, Clone)]
452pub struct OcamlTypeDef {
453 pub name: std::string::String,
454 pub type_params: Vec<std::string::String>,
456 pub decl: OcamlTypeDecl,
457}
458#[allow(dead_code)]
460#[derive(Debug, Clone)]
461pub struct OcamlTestSuite {
462 pub name: std::string::String,
464 pub cases: Vec<OcamlTestCase>,
466}
467#[allow(dead_code)]
468impl OcamlTestSuite {
469 pub fn new(name: &str) -> Self {
471 OcamlTestSuite {
472 name: name.to_string(),
473 cases: vec![],
474 }
475 }
476 pub fn add(mut self, case: OcamlTestCase) -> Self {
478 self.cases.push(case);
479 self
480 }
481 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 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#[allow(dead_code)]
517#[derive(Debug, Clone)]
518pub struct OcamlGadtVariant {
519 pub name: std::string::String,
521 pub params: Vec<OcamlType>,
523 pub result_type: std::string::String,
525}
526#[allow(dead_code)]
528#[derive(Debug, Clone)]
529pub struct OcamlFunctor {
530 pub name: std::string::String,
532 pub params: Vec<OcamlFunctorParam>,
534 pub body: Vec<OcamlDefinition>,
536 pub sig_constraint: Option<std::string::String>,
538}
539#[allow(dead_code)]
540impl OcamlFunctor {
541 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 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 pub fn add_def(mut self, def: OcamlDefinition) -> Self {
560 self.body.push(def);
561 self
562 }
563 pub fn with_sig(mut self, sig: &str) -> Self {
565 self.sig_constraint = Some(sig.to_string());
566 self
567 }
568 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#[allow(dead_code)]
597#[derive(Debug, Clone)]
598pub struct DuneExecutable {
599 pub name: std::string::String,
601 pub public_name: Option<std::string::String>,
603 pub libraries: Vec<std::string::String>,
605 pub preprocess: Vec<std::string::String>,
607 pub flags: Vec<std::string::String>,
609}
610#[allow(dead_code)]
611impl DuneExecutable {
612 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 pub fn add_dep(mut self, dep: &str) -> Self {
624 self.libraries.push(dep.to_string());
625 self
626 }
627 pub fn add_ppx(mut self, ppx: &str) -> Self {
629 self.preprocess.push(format!("(pps {})", ppx));
630 self
631 }
632 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#[allow(dead_code)]
719#[derive(Debug, Clone)]
720pub struct OcamlFunctorParam {
721 pub name: std::string::String,
723 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#[allow(dead_code)]
803#[derive(Debug, Clone)]
804pub struct OcamlTestCase {
805 pub name: std::string::String,
807 pub body: std::string::String,
809 pub expected: Option<std::string::String>,
811 pub actual: Option<std::string::String>,
813}
814#[allow(dead_code)]
815impl OcamlTestCase {
816 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 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 pub fn emit_ounit(&self) -> std::string::String {
836 format!("\"{}\" >:: (fun _ -> {})", self.name, self.body)
837 }
838 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#[derive(Debug, Clone)]
848pub enum OcamlDefinition {
849 TypeDef(OcamlTypeDef),
850 Let(OcamlLetBinding),
851 Signature(OcamlSignature),
852 Exception(std::string::String, Option<OcamlType>),
854 Open(std::string::String),
856 SubModule(OcamlModule),
858 Comment(std::string::String),
860}
861#[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 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 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#[derive(Debug, Clone, PartialEq)]
902pub enum OcamlPattern {
903 Wildcard,
905 Var(std::string::String),
907 Const(OcamlLit),
909 Tuple(Vec<OcamlPattern>),
911 Cons(Box<OcamlPattern>, Box<OcamlPattern>),
913 List(Vec<OcamlPattern>),
915 Ctor(std::string::String, Vec<OcamlPattern>),
917 Record(Vec<(std::string::String, OcamlPattern)>),
919 Or(Box<OcamlPattern>, Box<OcamlPattern>),
921 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#[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#[derive(Debug, Clone)]
981pub enum OcamlTypeDecl {
982 Alias(OcamlType),
984 Record(Vec<OcamlRecordField>),
986 Variant(Vec<(std::string::String, Vec<OcamlType>)>),
988 Abstract,
990}
991#[allow(dead_code)]
993#[derive(Debug, Clone)]
994pub struct OcamlGadt {
995 pub name: std::string::String,
997 pub type_params: Vec<std::string::String>,
999 pub variants: Vec<OcamlGadtVariant>,
1001}
1002#[allow(dead_code)]
1003impl OcamlGadt {
1004 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 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 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#[derive(Debug, Clone)]
1169pub struct OcamlModule {
1170 pub name: std::string::String,
1171 pub definitions: Vec<OcamlDefinition>,
1172 pub is_top_level: bool,
1174}
1175impl OcamlModule {
1176 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 pub fn add(&mut self, def: OcamlDefinition) {
1186 self.definitions.push(def);
1187 }
1188 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 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#[derive(Debug, Clone, PartialEq)]
1301pub enum OcamlExpr {
1302 Lit(OcamlLit),
1304 Var(std::string::String),
1306 BinOp(std::string::String, Box<OcamlExpr>, Box<OcamlExpr>),
1308 UnaryOp(std::string::String, Box<OcamlExpr>),
1310 App(Box<OcamlExpr>, Vec<OcamlExpr>),
1312 Lambda(Vec<std::string::String>, Box<OcamlExpr>),
1314 Let(std::string::String, Box<OcamlExpr>, Box<OcamlExpr>),
1316 LetRec(
1318 std::string::String,
1319 Vec<std::string::String>,
1320 Box<OcamlExpr>,
1321 Box<OcamlExpr>,
1322 ),
1323 IfThenElse(Box<OcamlExpr>, Box<OcamlExpr>, Box<OcamlExpr>),
1325 Match(Box<OcamlExpr>, Vec<(OcamlPattern, OcamlExpr)>),
1327 Tuple(Vec<OcamlExpr>),
1329 List(Vec<OcamlExpr>),
1331 Record(Vec<(std::string::String, OcamlExpr)>),
1333 Field(Box<OcamlExpr>, std::string::String),
1335 Module(std::string::String, Box<OcamlExpr>),
1337 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#[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}