Crates.io | ocaml-gen |
lib.rs | ocaml-gen |
version | 0.1.5 |
source | src |
created_at | 2022-09-12 23:50:44.576118 |
updated_at | 2023-06-21 19:34:50.386207 |
description | A library for generating OCaml code |
homepage | |
repository | https://github.com/o1-labs/ocaml-gen |
max_upload_size | |
id | 664051 |
size | 23,720 |
This crate provides automatic generation of OCaml bindings. Refer to the rustdoc for more information.
Here's an example of generating some bindings. Create a main.rs
as:
use ocaml_gen::prelude::*;
// Some Rust code you have:
#[derive(ocaml::FromValue, ocaml::IntoValue, ocaml_gen::CustomType)]
pub struct SomeType {
pub a: u8,
}
#[ocaml_gen::func]
#[ocaml::func]
fn create_some_type() -> SomeType {
SomeType { a: 42 }
}
fn main() {
// initialize your environment
let env = &mut Env::default();
// choose where you want to write the bindings
let w = &mut std::io::stdout();
// you can declare modules
decl_module!(w, env, "Types", {
// and types
decl_type!(w, env, SomeType => "t");
});
decl_module!(w, env, "Functions", {
// and functions
decl_func!(w, env, create_some_type => "create");
});
}
Note that the underlying function imported by decl_func!
is actually caml_of_numeral_to_ocaml
, which is created by the annotated macro ocaml_gen::func
.
So either your function is in scope, or you import everything (e.g. use path::*
), or you import the derived function directly (e.g. use path::caml_of_numeral_to_ocaml
).
The OCaml bindings generated should look like this:
module Types = struct
type nonrec t
end
module Functions = struct
val create : unit -> Types.t
end
In general, you can use this library by following these steps:
ocaml-rs
and ocaml_gen
macros.main.rs
file to generate your binding file bindings.ml
as shown above.ocaml-rs
crate to export the static library that OCaml will use.dune
file to your crate to build the Rust static library as well as the OCaml bindings. (protip: use (mode promote)
in the dune file to have the resulting binding file live within the folder.)You can see an example of these steps in the test/ folder. (Although we don't "promote" the file with dune for testing purposes.)
To allow ocaml-gen to understand how to generate OCaml bindings from your types and functions, you must annotate them using ocaml-gen's macros.
To allow generation of bindings on structs, use ocaml_gen::Struct
:
#[ocaml_gen::Struct]
struct MyType {
// ...
}
To allow generation of bindings on enums, use ocaml_gen::Enum
:
#[ocaml_gen::Enum]
enum MyType {
// ...
}
To allow generation of bindings on functions, use ocaml_gen::func
:
#[ocaml_gen::func]
#[ocaml::func] // if you use the crate ocaml-rs' macro, it must appear after
pub fn your_function(arg1: String) {
//...
}
To allow generation of bindings on custom types, use ocaml_gen::CustomType
:
#[ocaml_gen::CustomType]
struct MyCustomType {
// ...
}
To generate bindings, you must create a main.rs
file that uses the ocaml_gen
crate functions to layout what the bindings .ml
file will look like.
The first thing to do is to import the types and functions you want to generate bindings for, as well as the ocaml_gen
macros:
use ocaml_gen::prelude::*;
use your_crate::*;
You can then use decl_module!
to declare modules:
let env = &mut Env::default();
let w = &mut std::io::stdout();
decl_module!(w, env, "T1", {
decl_module!(w, env, "T2", {
decl_type!(w, env, SomeType);
});
});
decl_module!(w, env, "T3", {
decl_type!(w, env, SomeOtherType);
});
You can rename types and functions by simply adding an arrow:
decl_type!(w, env, SomeType => "t");
You can also declare generic types by first declaring the generic type parameters (that you must reuse for all generic types):
decl_fake_generic!(T1, 0); // danger:
decl_fake_generic!(T2, 1); // make sure you
decl_fake_generic!(T3, 2); // increment these correctly
decl_type!(w, env, TypeWithOneGenericType<T1>);
decl_type!(w, env, ThisOneHasTwo<T1, T2>);
decl_type!(w, env, ThisOneHasThreeOfThem<T1, T2, T3>);
You can also create type aliases with the decl_type_alias!
macro but it is highly experimental.
It has a number of issues:
Thing<usize>
to t1
, eventhough t1
was an alias to Thing<String>
(this is the main danger, see this tracking issue)t1
is the alias of Thing<usize>
and t2
is the alias of Thing<String>
)