-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 8580c24
Showing
17 changed files
with
837 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
target | ||
.idea | ||
*.iml | ||
Cargo.lock | ||
*~ | ||
*.bk | ||
_build | ||
*.o | ||
*.cmx | ||
*.cmi | ||
*#* | ||
examples/ocaml/test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
[package] | ||
name = "ocaml" | ||
version = "0.1.0" | ||
authors = ["Zach Shipko <[email protected]>"] | ||
readme = "README.md" | ||
keywords = ["ocaml", "rust", "ffi"] | ||
repository = "https://github.com/zshipko/ocaml-rs" | ||
license = "ISC" | ||
description = "OCaml bindings for Rust" | ||
documentation = "https://docs.rs/raml" | ||
|
||
[dependencies] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
SRC=$(wildcard src/*.rs) | ||
ARGS=-- -Z unstable-options --pretty=expanded | ||
|
||
pretty: $(SRC) | ||
@cargo rustc $(ARGS) | ||
@cd examples/rust && cargo rustc $(ARGS) | ||
@cd examples/ocaml && make pretty | ||
examples/ocaml/test | ||
|
||
test: $(SRC) | ||
@cargo test | ||
@cd examples/rust && cargo build | ||
@cd examples/ocaml && make | ||
examples/ocaml/test | ||
|
||
clean: | ||
cargo clean | ||
cd examples/rust && cargo clean | ||
cd examples/ocaml && make clean | ||
|
||
.PHONE: test pretty clean |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# ocaml-rs - OCaml FFI for Rust | ||
|
||
NOTE: `ocaml-rs` is based on `raml v0.1` | ||
|
||
Direct OCaml bindings without ever leaving Rust - no C stubs! | ||
|
||
(you still have to know how the C ffi bindings work; if you do, the macros are almost identical to the C ones in their naming and purpose) | ||
|
||
Please see the example in `examples` for the Rust code in `rust` for the Rust code that OCaml code will call and the `ocaml` directory for the OCaml code that calls the Rust code. | ||
|
||
Also, please bear with me as I'm trying to add more documentation and examples, but I am very busy; if you see something, don't hesitate to add a PR or issue, thanks :) | ||
|
||
A basic example demonstrates their usage: | ||
|
||
```rust | ||
caml!(ml_beef, |parameter|, <local>, { | ||
let i = int_val!(parameter); | ||
let res = 0xbeef * i; | ||
println!("about to return 0x{:x} to OCaml runtime", res); | ||
local = val_int!(res); | ||
} -> local); | ||
``` | ||
|
||
The macro takes care of _automatically_ declaring `CAMLparam` et. al, as well as `CAMLlocal` and `CAMLreturn`. | ||
|
||
If you need more fine grained control, `caml_body!` and others are available. | ||
|
||
### Documentation | ||
|
||
https://docs.rs/ocaml/ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
## example makefile for building ocaml binary with rust lib | ||
## NB: assumes you use rustup with standard install | ||
|
||
RUSTSRC=$(wildcard ../rust/src/*.rs) | ||
CLIENT=../rust/target/debug/libclient.a | ||
ARGS=-- -Z unstable-options --pretty=expanded | ||
|
||
build: test $(CLIENT) | ||
|
||
$(CLIENT): $(RUSTSRC) | ||
cd ../rust && cargo build | ||
|
||
test: test.ml $(CLIENT) | ||
ocamlopt.opt -verbose -cclib -lclient -ccopt -L../rust/target/debug -ccopt '-L ~/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/' -cclib -ldl -cclib -lpthread test.ml -o test | ||
|
||
pretty: test | ||
cd ../rust && cargo rustc $(ARGS) | ||
|
||
clean: | ||
rm test *.o *.cm* | ||
rm -rf _build | ||
cd ../rust && cargo clean | ||
|
||
.PHONY: build clean pretty |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
external send_int : int -> int = "ml_send_int" | ||
external send_two : int -> string -> unit = "ml_send_two" | ||
external send_tuple : (int * int) -> int = "ml_send_tuple" | ||
external new_tuple : unit -> (int * int * int) = "ml_new_tuple" | ||
|
||
let f x = x land 0x0000ffff | ||
|
||
let _ = | ||
let string = "string thing" in | ||
let deadbeef = 0xdeadbeef in | ||
let res = send_int 0xb1b1eb0b in | ||
Printf.printf "send_int returned: 0x%x\n" res; | ||
flush stdout; | ||
send_two deadbeef string; | ||
send_two (f deadbeef) string; | ||
let res = send_tuple (1, 2) in | ||
Printf.printf "%d\n" res; | ||
let (a, b, c) = new_tuple () in | ||
Printf.printf "%d %d %d\n" a b c |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
[package] | ||
name = "client" | ||
version = "0.1.0" | ||
authors = ["m4b <[email protected]>"] | ||
|
||
[lib] | ||
crate-type = ["staticlib"] | ||
|
||
[dependencies.ocaml] | ||
path = "../../" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
#[macro_use] | ||
extern crate ocaml; | ||
|
||
// use std::io; | ||
// use std::io::Write; | ||
|
||
caml!(ml_send_int, |v, v2|, <l>, { | ||
let x = int_val!(v); | ||
l = val_int!(0xbeef); | ||
println!("send_int 0x{:x}", x); | ||
// io::stdout().flush(); | ||
} -> l); | ||
|
||
caml!(ml_send_two, |v, v2|, { | ||
println!("local root addr: {:p} caml_local_roots: {:#?}, v: {:?}", &ocaml::memory::caml_local_roots, ocaml::memory::caml_local_roots, v); | ||
let x = int_val!(v); | ||
let len = ocaml::mlvalues::caml_string_length(v2); | ||
let ptr = string_val!(v2); | ||
let slice = ::std::slice::from_raw_parts(ptr, len); | ||
let string = ::std::str::from_utf8_unchecked(slice); | ||
println!("got 0x{:x}, {}", x, string); | ||
}); | ||
|
||
caml!(ml_send_tuple, |t|, <dest>, { | ||
let x = int_val!(*field!(t, 0)); | ||
let y = int_val!(*field!(t, 1)); | ||
|
||
dest = val_int!(x + y); | ||
} -> dest); | ||
|
||
caml!(ml_new_tuple, |unit|, <dest, a, b, c>, { | ||
a = val_int!(0); | ||
b = val_int!(1); | ||
c = val_int!(2); | ||
|
||
let mut tuple = ocaml::Tuple::new(3); | ||
tuple.set(0, a).unwrap(); | ||
tuple.set(1, b).unwrap(); | ||
tuple.set(2, c).unwrap(); | ||
dest = ocaml::Value::from(tuple); | ||
} -> dest); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
//! External definitions for allocating values in the OCaml runtime | ||
use mlvalues::{Size, Value, Tag}; | ||
|
||
extern "C" { | ||
pub fn caml_alloc(size: Size, tag: Tag) -> Value; | ||
pub fn caml_alloc_small(size: Size, tag: Tag) -> Value; | ||
pub fn caml_alloc_tuple(size: Size) -> Value; | ||
pub fn caml_alloc_string(size: Size) -> Value; // size in bytes | ||
pub fn caml_copy_string(string: *const u8) -> Value; | ||
pub fn caml_copy_string_array(arr: *const *const u8) -> Value; | ||
|
||
pub fn caml_copy_double(double: f64) -> Value; | ||
pub fn caml_copy_int32(int: i32) -> Value; // defined in [ints.c] | ||
pub fn caml_copy_int64(int: i64) -> Value; // defined in [ints.c] | ||
pub fn caml_copy_nativeint(int: isize) -> Value; // defined in [ints.c] | ||
pub fn caml_alloc_array(value: (unsafe extern "C" fn(*const u8) -> Value), | ||
array: *mut *mut u8) | ||
-> Value; | ||
// CAMLextern value caml_alloc_sprintf( const char * format, ... ); // this is going to be interesting | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
//! Callbacks from C to OCaml | ||
//! This is also where you initialize the OCaml runtime system via `caml_startup` or `caml_main` | ||
//! | ||
use mlvalues::Value; | ||
|
||
extern "C" { | ||
pub fn caml_callback(closure: Value, arg: Value) -> Value; | ||
pub fn caml_callback2(closure: Value, arg1: Value, arg2: Value) -> Value; | ||
pub fn caml_callback3(closure: Value, arg1: Value, arg2: Value, arg3: Value) -> Value; | ||
pub fn caml_callbackN(closure: Value, narg: usize, args: *mut Value) -> Value; | ||
|
||
pub fn caml_callback_exn(closure: Value, arg1: Value) -> Value; | ||
pub fn caml_callback2_exn(closure: Value, arg1: Value, arg2: Value) -> Value; | ||
pub fn caml_callback3_exn(closure: Value, arg1: Value, arg2: Value, arg3: Value) -> Value; | ||
pub fn caml_callbackN_exn(closure: Value, narg: usize, args: *mut Value) -> Value; | ||
|
||
pub fn caml_main(argv: *mut *mut u8); | ||
pub fn caml_startup(argv: *mut *mut u8); | ||
pub fn caml_named_value(name: *const u8) -> *mut Value; | ||
|
||
pub static mut caml_callback_depth: usize; | ||
} | ||
|
||
#[macro_export] | ||
macro_rules! make_exception_result { | ||
($v:ident) => { | ||
(v as usize) | 2 | ||
} | ||
} | ||
|
||
#[macro_export] | ||
macro_rules! is_exception_result { | ||
($v:ident) => { | ||
(v as usize) & 3 == 2 | ||
} | ||
} | ||
|
||
#[macro_export] | ||
macro_rules! extract_exception { | ||
($v:ident) => { | ||
(v as usize) & !3 | ||
} | ||
} | ||
|
||
/* | ||
typedef void (*caml_named_action) (value*, char *); | ||
CAMLextern void caml_iterate_named_values(caml_named_action f); | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
#[derive(Debug)] | ||
pub enum Error { | ||
OutOfBounds | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
//! Raml is a library for directly interacting with the C OCaml runtime, in Rust. | ||
//! Consquently, ocaml is designed for rust shared objects that expose raw C FFI bindings, | ||
//! which are then either statically or dynamically linked against an OCaml binary, which calls into these raw FFI bindings as if they were | ||
//! regular, so-called "C stubs". Similarly, any OCaml runtime functions, such as `caml_string_length`, will get their definition from the | ||
//! final _OCaml_ binary, with its associated runtime. | ||
//! | ||
//! The benefit of this approach is that it removes any bridging C code, and allows in essence, a direct interface between Rust and OCaml. | ||
//! | ||
//! The macro has the format: `(name, |params...|, <local variables..>, { // code body }, -> optional return value listed in local variables`) | ||
//! | ||
//! A basic example demonstrates their usage: | ||
//! | ||
//! ```rust | ||
//! #[macro_use] extern crate ocaml; | ||
//! | ||
//! // linking against this rust static lib, you can access this function in OCaml via: | ||
//! // `external beef : int -> int = "ml_beef"` | ||
//! caml!(ml_beef, |parameter|, <local>, { | ||
//! let i = int_val!(parameter); | ||
//! let res = 0xbeef * i; | ||
//! println!("about to return 0x{:x} to OCaml runtime", res); | ||
//! local = val_int!(res); | ||
//! } -> local); | ||
//! | ||
//! // this is only here to satisfy docs | ||
//! fn main() {} | ||
//! ``` | ||
//! | ||
//! The macro takes care of _automatically_ declaring `CAMLparam` et. al, as well as `CAMLlocal` and `CAMLreturn`. | ||
//! | ||
//! If you need more fine grained control, `caml_body!` and others are available. | ||
//! | ||
//! Some more examples: | ||
//! | ||
//! ```rust | ||
//! #[macro_use] extern crate ocaml; | ||
//! | ||
//! // these are two functions that OCaml code can access via `external val` declarations | ||
//! caml!(ml_send_int, |v, v2|, <l>, { | ||
//! let x = int_val!(v); | ||
//! l = val_int!(0xbeef); | ||
//! println!("send_int 0x{:x}", x); | ||
//! // io::stdout().flush(); | ||
//! } -> l); | ||
//! | ||
//! caml!(ml_send_two, |v, v2|, { | ||
//! println!("local root addr: {:p} caml_local_roots: {:#?}, v: {:?}", &ocaml::memory::caml_local_roots, ocaml::memory::caml_local_roots, v); | ||
//! let x = int_val!(v); | ||
//! let len = ocaml::mlvalues::caml_string_length(v2); | ||
//! let ptr = string_val!(v2); | ||
//! let slice = ::std::slice::from_raw_parts(ptr, len); | ||
//! let string = ::std::str::from_utf8_unchecked(slice); | ||
//! println!("got 0x{:x}, {}", x, string); | ||
//! }); | ||
//! | ||
//! // this is only here to satisfy docs, you will likely want a staticlib, not a rust executable | ||
//! fn main() {} | ||
//! ``` | ||
//! | ||
//! These will be accessible to an OCaml program via `external` declarations, e.g., for the above we could do something like: | ||
//! | ||
//! ```OCaml | ||
//! external send_int : int -> int = "ml_send_int" | ||
//! external send_two : int -> string -> unit = "ml_send_two" | ||
//! | ||
//! let f x = x land 0x0000ffff | ||
//! | ||
//! let _ = | ||
//! let string = "string thing" in | ||
//! let deadbeef = 0xdeadbeef in | ||
//! let res = send_int 0xb1b1eb0b in | ||
//! Printf.printf "send_int returned: 0x%x\n" res; | ||
//! flush stdout; | ||
//! send_two deadbeef string; | ||
//! send_two (f deadbeef) string | ||
//! ``` | ||
//! | ||
mod tag; | ||
mod types; | ||
mod error; | ||
|
||
#[macro_use] | ||
pub mod mlvalues; | ||
#[macro_use] | ||
pub mod memory; | ||
pub mod alloc; | ||
pub mod callback; | ||
|
||
pub use error::Error; | ||
pub use mlvalues::Value; | ||
pub use tag::Tag; | ||
pub use types::{Array, Tuple}; | ||
|
Oops, something went wrong.