Embedding the OCaml bytecode compiler in C

Has anyone tried to embed the OCaml bytecode compiler in C?

What was your experience?

Is this so difficult that I shouldn’t bother?

I think this is just a matter of embedding the OCaml toplevel.

Hi,

something like this might work,
I haven’t tested this exact code,
I’m pasting it from a bigger project:

caml.ml:

let _OUTSZ = 1024 * 500
let outbuf = ref (Buffer.create 0)
let run_caml phrase =
  let () = Buffer.clear !outbuf in
  let errbuf = Buffer.create 2048 in
  let ppf = Format.formatter_of_buffer errbuf in
  let input = Toploop.String phrase in
  let old_warning_formatter = !Location.formatter_for_warnings in
  let new_formatter = Format.formatter_of_buffer errbuf in
  let () = Location.formatter_for_warnings := new_formatter in
  let () =
    try
      if Toploop.prepare ppf ~input:input () then begin
          if Toploop.run_script ppf input [||] then begin
              let () = Format.pp_print_flush new_formatter () in
              let () = Location.formatter_for_warnings := old_warning_formatter in
              flush_all ()
            end;
        end;
    with
    | Compenv.Exit_with_status status ->
       Format.pp_print_flush Format.err_formatter () in

  if 0 < Buffer.length errbuf then
    let contents = Buffer.contents errbuf in
    let () = print_endline contents in
    contents
  else
    Buffer.contents !outbuf

let _ = Callback.register "run_caml" run_caml

let wout str =
  let bytes = Bytes.of_string str in
  let addlen = Bytes.length bytes in
  let () = if _OUTSZ < (Buffer.length !outbuf) + addlen then failwith "output buffer full" in
  Buffer.add_bytes !outbuf bytes

caml.c:

#include <caml/alloc.h>
#include <caml/callback.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>

#define BUFSZ 1024 * 500

void run_caml(const char *phrase,
              char *out) {
    static bool started = false;
    if (!started) {
        char_os *args = NULL;
        caml_startup(&args);
        started = true;
        printf("%d OCaml startup complete\n", getpid());
        fflush(stdout);
    }

    CAMLparam0();
    CAMLlocal2(caml_phrase, res);
    caml_phrase = caml_copy_string(phrase);
    res = caml_callback(
        *caml_named_value("run_caml"),
        caml_phrase);
    memset(out, 0, BUFSZ);
    mlsize_t len = caml_string_length(res);
    if (0 < len) {
        memcpy(out, Bytes_val(res), len <= BUFSZ ? len : BUFSZ);
    }
}

build.sh:

ocamlc -custom -output-obj -o caml_byte.o \
-I +compiler-libs \
compilerlibs/ocamlcommon.cma \
compilerlibs/ocamlbytecomp.cma \
compilerlibs/ocamltoplevel.cma \
caml.ml

cc -c -o caml.o -I lib/ocaml src/caml.c
cp ocaml/runtime/libcamlrun_pic.a libcamlrun.a
ar r libcamlrun.a caml.o caml_byte.o

If you get Error: Unbound module errors, make a directory,
for example ocamllibs and set the environment variable OCAMLLIB to it:
setenv("OCAMLLIB", "ocamllibs", 1);, and then copy necessary *.cmi files into it.

1 Like