ocaml_gen/
lib.rs

1#![deny(missing_docs)]
2#![doc = include_str!("../README.md")]
3#![no_std]
4
5extern crate alloc;
6extern crate ocaml_gen_derive;
7use alloc::collections::btree_map::Entry;
8use alloc::collections::BTreeMap;
9use alloc::format;
10use alloc::string::{String, ToString};
11use alloc::{vec, vec::Vec};
12
13pub use const_random::const_random;
14pub use ocaml_gen_derive::*;
15pub use paste::paste;
16
17pub mod conv;
18
19//
20// User-friendly prologue
21//
22
23/// To use the library, you can simply import the prelude as in:
24///
25/// ```
26/// use ocaml_gen::prelude::*;
27/// ```
28///
29pub mod prelude {
30    pub use super::{decl_fake_generic, decl_func, decl_module, decl_type, decl_type_alias, Env};
31}
32
33//
34// Structs
35//
36
37/// The environment at some point in time during the declaration of OCaml
38/// bindings.
39/// It ensures that types cannot be declared twice, and that types that are
40/// renamed and/or relocated into module are referenced correctly.
41#[derive(Debug)]
42pub struct Env {
43    /// every type (their path and their name) is stored here at declaration
44    locations: BTreeMap<u128, (Vec<&'static str>, &'static str)>,
45
46    /// the current path we're in (e.g. `ModA.ModB`)
47    current_module: Vec<&'static str>,
48
49    /// list of aliases. When entering a module, the vec is extended.
50    /// When exiting a module, the vec is poped.
51    /// This way, aliases are kept within their own modules.
52    aliases: Vec<BTreeMap<u128, &'static str>>,
53}
54
55impl Drop for Env {
56    /// This makes sure that we close our OCaml modules (with the keyword
57    /// `end`).
58    fn drop(&mut self) {
59        assert!(self.current_module.is_empty(), "you must call .root() on the environment to finalize the generation. You are currently still nested: {:?}", self.current_module);
60    }
61}
62
63impl Default for Env {
64    fn default() -> Self {
65        Self::new()
66    }
67}
68
69impl Env {
70    /// Creates a new environment.
71    #[must_use]
72    pub fn new() -> Self {
73        Self {
74            locations: BTreeMap::new(),
75            current_module: Vec::new(),
76            aliases: vec![BTreeMap::new()],
77        }
78    }
79
80    /// Declares a new type. If the type was already declared, this will panic.
81    ///
82    /// # Panics
83    /// The function will panic if the type was already declared.
84    pub fn new_type(&mut self, ty: u128, name: &'static str) {
85        match self.locations.entry(ty) {
86            Entry::Occupied(_) => panic!("ocaml-gen: cannot re-declare the same type twice"),
87            Entry::Vacant(v) => v.insert((self.current_module.clone(), name)),
88        };
89    }
90
91    /// Retrieves a type that was declared previously.
92    /// A boolean indicates if the type is being aliased.
93    ///
94    /// # Panics
95    /// The function will panic if the type was not declared previously.
96    #[must_use]
97    pub fn get_type(&self, ty: u128, name: &str) -> (String, bool) {
98        // first, check if we have an alias for this type
99        if let Some(alias) = self
100            .aliases
101            .last()
102            .expect("ocaml-gen bug: bad initialization of aliases")
103            .get(&ty)
104        {
105            return ((*alias).to_string(), true);
106        }
107
108        // otherwise, check where the type is declared
109        let (type_path, type_name) = self
110            .locations
111            .get(&ty)
112            .unwrap_or_else(|| panic!("ocaml-gen: the type {name} hasn't been declared"));
113
114        // path resolution
115        let mut current = self.current_module.clone();
116        current.reverse();
117        let path: Vec<&str> = type_path
118            .iter()
119            .skip_while(|&p| Some(*p) == current.pop())
120            .copied()
121            .collect();
122
123        let name = if path.is_empty() {
124            (*type_name).to_string()
125        } else {
126            format!("{}.{}", path.join("."), type_name)
127        };
128
129        (name, false)
130    }
131
132    /// Adds a new alias for the current scope (module).
133    ///
134    /// # Panics
135    /// The function will panic if the alias was already declared.
136    pub fn add_alias(&mut self, ty: u128, alias: &'static str) {
137        let res = self
138            .aliases
139            .last_mut()
140            .expect("bug in ocaml-gen: the Env initializer is broken")
141            .insert(ty, alias);
142        assert!(
143            res.is_none(),
144            "ocaml-gen: cannot re-declare the same alias twice"
145        );
146    }
147
148    /// Create a module and enters it.
149    ///
150    /// # Panics
151    /// This function will panic if the module was already declared,
152    /// or if the module name is not following the OCaml guidelines.
153    pub fn new_module(&mut self, mod_name: &'static str) -> String {
154        let first_letter = mod_name
155            .chars()
156            .next()
157            .expect("module name cannot be empty");
158        assert!(
159            first_letter.to_uppercase().to_string() == first_letter.to_string(),
160            "ocaml-gen: OCaml module names start with an uppercase, you provided: {mod_name}"
161        );
162
163        // nest into the aliases vector
164        self.aliases.push(BTreeMap::new());
165
166        // create a module
167        self.current_module.push(mod_name);
168
169        format!("module {mod_name} = struct ")
170    }
171
172    /// how deeply nested are we currently? (default is 0)
173    #[must_use]
174    pub fn nested(&self) -> usize {
175        self.current_module.len()
176    }
177
178    /// called when we exit a module
179    pub fn parent(&mut self) -> String {
180        // destroy any aliases
181        self.aliases.pop();
182
183        // go back up one module
184        self.current_module
185            .pop()
186            .expect("ocaml-gen: you are already at the root");
187        "end".to_string()
188    }
189
190    /// you can call this to go back to the root and finalize the generation
191    pub fn root(&mut self) -> String {
192        let mut res = String::new();
193        for _ in &self.current_module {
194            res.push_str("end\n");
195        }
196        res
197    }
198}
199
200//
201// Traits
202//
203
204/// `OCamlBinding` is the trait implemented by types to generate their OCaml bindings.
205/// It is usually derived automatically via the [Struct] macro,
206/// or the [`CustomType`] macro for custom types.
207/// For functions, refer to the [func] macro.
208pub trait OCamlBinding {
209    /// will generate the OCaml bindings for a type (called root type).
210    /// It takes the current environment [Env],
211    /// as well as an optional name (if you wish to rename the type in OCaml).
212    fn ocaml_binding(env: &mut Env, rename: Option<&'static str>, new_type: bool) -> String;
213}
214
215/// `OCamlDesc` is the trait implemented by types to facilitate generation of
216/// their OCaml bindings.
217/// It is usually derived automatically via the [Struct] macro,
218/// or the [`CustomType`] macro for custom types.
219pub trait OCamlDesc {
220    /// describes the type in OCaml, given the current environment [Env]
221    /// and the list of generic type parameters of the root type
222    /// (the type that makes use of this type)
223    fn ocaml_desc(env: &Env, generics: &[&str]) -> String;
224
225    /// Returns a unique ID for the type. This ID will not change if concrete
226    /// type parameters are used.
227    fn unique_id() -> u128;
228}
229
230//
231// Func-like macros
232//
233
234/// Creates a module
235#[macro_export]
236macro_rules! decl_module {
237    ($w:expr, $env:expr, $name:expr, $b:block) => {{
238        use std::io::Write;
239        write!($w, "\n{}{}\n", format_args!("{: >1$}", "", $env.nested() * 2), $env.new_module($name)).unwrap();
240        $b
241        write!($w, "{}{}\n\n", format_args!("{: >1$}", "", $env.nested() * 2 - 2), $env.parent()).unwrap();
242    }}
243}
244
245/// Declares the binding for a given function
246#[macro_export]
247macro_rules! decl_func {
248    ($w:expr, $env:expr, $func:ident) => {{
249        use std::io::Write;
250        ::ocaml_gen::paste! {
251            let binding = [<$func _to_ocaml>]($env, None);
252        }
253        write!(
254            $w,
255            "{}{}\n",
256            format_args!("{: >1$}", "", $env.nested() * 2),
257            binding,
258        )
259        .unwrap();
260    }};
261    // rename
262    ($w:expr, $env:expr, $func:ident => $new:expr) => {{
263        use std::io::Write;
264        ::ocaml_gen::paste! {
265            let binding = [<$func _to_ocaml>]($env, Some($new));
266        }
267        write!(
268            $w,
269            "{}{}\n",
270            format_args!("{: >1$}", "", $env.nested() * 2),
271            binding,
272        )
273        .unwrap();
274    }};
275}
276
277/// Declares the binding for a given type
278#[macro_export]
279macro_rules! decl_type {
280    ($w:expr, $env:expr, $ty:ty) => {{
281        use std::io::Write;
282        let res = <$ty as ::ocaml_gen::OCamlBinding>::ocaml_binding($env, None, true);
283        write!(
284            $w,
285            "{}{}\n",
286            format_args!("{: >1$}", "", $env.nested() * 2),
287            res,
288        )
289        .unwrap();
290    }};
291    // rename
292    ($w:expr, $env:expr, $ty:ty => $new:expr) => {{
293        use std::io::Write;
294        let res = <$ty as ::ocaml_gen::OCamlBinding>::ocaml_binding($env, Some($new), true);
295        write!(
296            $w,
297            "{}{}\n",
298            format_args!("{: >1$}", "", $env.nested() * 2),
299            res,
300        )
301        .unwrap();
302    }};
303}
304
305/// Declares a new OCaml type that is made of other types
306#[macro_export]
307macro_rules! decl_type_alias {
308    ($w:expr, $env:expr, $new:expr => $ty:ty) => {{
309        use std::io::Write;
310        let res = <$ty as ::ocaml_gen::OCamlBinding>::ocaml_binding($env, Some($new), false);
311        write!(
312            $w,
313            "{}{}\n",
314            format_args!("{: >1$}", "", $env.nested() * 2),
315            res,
316        )
317        .unwrap();
318    }};
319}
320
321/// Creates a fake generic. This is a necessary hack, at the moment, to declare
322/// types (with the [`decl_type`] macro) that have generic parameters.
323#[macro_export]
324macro_rules! decl_fake_generic {
325    ($name:ident, $i:expr) => {
326        pub struct $name;
327
328        impl ::ocaml_gen::OCamlDesc for $name {
329            fn ocaml_desc(_env: &::ocaml_gen::Env, generics: &[&str]) -> String {
330                format!("'{}", generics[$i])
331            }
332
333            fn unique_id() -> u128 {
334                ::ocaml_gen::const_random!(u128)
335            }
336        }
337    };
338}