Compiles OCaml bytecode programs to Lua 5.1, inspired by js_of_ocaml.
Note: this project was written in a heavily guided, multi-hour chat session with Claude Code. Most of it with Model: Deepseek v4.0, and some Opus 4.7 — nothing agentic.
opam install js_of_ocaml-compiler
git clone git@github.com:maltasea/lua_of_ocaml.git
cd lua_of_ocaml
dune build
echo 'let () = print_endline "hello from lua"' > hello.ml
make hello
hello from lua
Edit hello.ml, then make hello.
./loo.sh prog.ml # .ml → stdout
./loo.sh prog.ml out.lua # .ml → out.lua
lua out.lua
./loo.sh is generated by make — no install
needed.
ocaml source
→ ocamlc → bytecode
→ jsoo parser
(reused)
→ IR (Code.program)
→ generate_lua.ml → Lua
AST
→ output_lua.ml → .lua text
+
runtime/lua/*.lua
→ luajit hello.lua
The IR from js_of_ocaml is target-agnostic. lua_of_ocaml reuses the bytecode parser and optimizations, and provides a structural CFG code generator modeled on js_of_ocaml’s generate.ml.
| file | what |
|---|---|
compiler/lib/lua.ml |
lua 5.1 ast |
compiler/lib/output_lua.ml |
lua pretty printer |
compiler/lib/generate_lua.ml |
ir → lua |
compiler/bin-lua_of_ocaml/ |
cli tool |
runtime/lua/*.lua |
lua runtime |
test/ |
tests |
external becomes a direct lua call. dummy c stub for
linking.
(* mylib.ml *)
external lua_add : int -> int -> int = "lua_add"
/* mylib_stubs.c — just so ocamlc links */
#include <caml/mlvalues.h>
CAMLprim value lua_add(value a, value b) { return Val_int(0); }
ocamlc -c mylib_stubs.c
ocamlc -c mylib.ml
ocamlc -custom -o mylib.byte mylib_stubs.o mylib.cmo
./loo.sh mylib.byte -o mylib.lua
ocaml values in lua: ints tagged n*2, floats boxed
{253, v}, strings plain, blocks
{tag, f1, f2, ...}.
real example: example-game/ — löve2d chicken jump.
generated lua has --# file:line markers. use
ocamlc -g.
make test # 26 tests
bash test/behavioral/run.sh # 5 behavioral tests
lgpl-2.1-or-later with ocaml-lgpl-linking-exception (same as jsoo)