Skip to content

Instantly share code, notes, and snippets.

@larsr
Last active April 23, 2020 23:33
Show Gist options
  • Save larsr/6b3cd6f62d54d56e3f9a to your computer and use it in GitHub Desktop.
Save larsr/6b3cd6f62d54d56e3f9a to your computer and use it in GitHub Desktop.
Ocaml to Javascript

Compiling ocaml into javascript.

From http://toss.sourceforge.net/ocaml.html

This OCaml code example uses the menhir parser, and the js_of_ocaml library together. I've installed them with opam.

type formula =
| Var of string
| Not of formula
| Or of formula * formula
| And of formula * formula
let rec str = function
| Var s -> s
| Not f -> "(not " ^ (str f) ^ ")"
| Or (f, g) -> "(" ^ (str f) ^ " or " ^ (str g) ^ ")"
| And (f, g) -> "(" ^ (str f) ^ " and " ^ (str g) ^ ")"
let rec nnf ?(negate=false) = function
| Var s -> if not negate then Var s else Not (Var s)
| Not f -> nnf ~negate:(not negate) f
| Or (f, g) -> if not negate then Or (nnf f, nnf g) else
And (nnf ~negate:true f, nnf ~negate:true g)
| And (f, g) -> if not negate then And (nnf f, nnf g) else
Or (nnf ~negate:true f, nnf ~negate:true g)
let _ = print_endline (str (nnf (Not (And (Var "X", Not (Var "Y"))))))
<html>
<head>
<meta http-equiv="Content-Type" content="text/xhtml+xml; charset=UTF-8" />
<title>Tutorial</title>
<script type="text/javascript">
<!--
var worker = new Worker ("JsClient.js");
var worker_handler = new Object ();
worker.onmessage = function (m) {
if (typeof m.data == 'string') {
console.log("" + m.data);
} else {
console.log ("[ASYNCH] back from " + m.data.fname);
var handler = worker_handler[m.data.fname];
handler (m.data.result);
}
}
function ASYNCH (action_name, action_args, cont) {
worker_handler[action_name] = cont;
worker.postMessage ({fname: action_name, args: action_args});
console.log ("[ASYNCH] " + action_name + " (" + action_args + ")");
}
function convert_nnf () {
var txt = document.getElementById ("formula").value;
ASYNCH ("nnf", [txt], function (resp) { alert (resp) })
}
//-->
</script>
</head>
<body>
<textarea id="formula" rows="2" cols="40">
not (X and not Y)</textarea>
<button onclick="convert_nnf()">Convert to NNF</button>
</body>
</html>
open Formula
(* Boilerplate code for calling OCaml in the worker thread. *)
let js_object = Js.Unsafe.variable "Object"
let js_handler = jsnew js_object ()
let postMessage = Js.Unsafe.variable "postMessage"
let log s = ignore (Js.Unsafe.call postMessage (Js.Unsafe.variable "self")
[|Js.Unsafe.inject (Js.string s)|])
let onmessage event =
let fname = event##data##fname in
let args = event##data##args in
let handle = Js.Unsafe.get js_handler fname in
let result = Js.Unsafe.fun_call handle (Js.to_array args) in
let response = jsnew js_object () in
Js.Unsafe.set response (Js.string "fname") fname;
Js.Unsafe.set response (Js.string "result") result;
Js.Unsafe.call postMessage (Js.Unsafe.variable "self") [|Js.Unsafe.inject response|]
let _ = Js.Unsafe.set (Js.Unsafe.variable "self") (Js.string "onmessage") onmessage
(* The NNF conversion and registration in JS. *)
let formula_of_string s = Parser.parse_formula Lexer.lex (Lexing.from_string s)
let js_nnf s =
log ("computing nnf of " ^ (Js.to_string s));
Js.string (str (nnf (formula_of_string (Js.to_string s))))
let _ = Js.Unsafe.set js_handler (Js.string "nnf") (Js.wrap_callback js_nnf)
{
type token =
| ID of (string)
| AND
| OR
| NOT
| OP
| CL
| EOF
}
rule lex = parse
| [' ' '\t'] { lex lexbuf }
| "and" { AND }
| "or" { OR }
| "not" { NOT }
| "(" { OP }
| ")" { CL }
| ['A'-'Z' 'a'-'z' '_']['0'-'9' 'A'-'Z' 'a'-'z' '_']* as s { ID (s) }
| eof { EOF }
open Formula
open Lexer
let formula_of_string s = Parser.parse_formula Lexer.lex (Lexing.from_string s)
let _ = print_endline (str (nnf (formula_of_string "not (X and not Y)")))
OCAMLC = ocamlfind ocamlc $(PACKAGES) $(OFLAGS)
PACKAGES += js_of_ocaml
PACKAGES += js_of_ocaml.syntax
PACKAGES := $(addprefix -package , $(PACKAGES))
OFLAGS += -syntax camlp4o
all: Main.js JsClient.js
%.mli: %.ml
$(OCAMLC) -i $< > $@
%.cmi: %.mli
$(OCAMLC) -c $< -o $@
%.cmo: %.ml
$(OCAMLC) -c $< -o $@
%.byte:
$(OCAMLC) -linkpkg $^ -o $@
%.js: %.byte
js_of_ocaml $<
Lexer.ml: Lexer.mll
ocamllex $<
Parser.ml: Lexer.cmi
menhir --external-tokens Lexer Parser.mly
Parser.cmo: Formula.cmo Parser.cmi
JsClient.byte: Formula.cmo Lexer.cmo Parser.cmo JsClient.cmo
Main.byte: Formula.cmo Lexer.cmo Parser.cmo Main.cmo
clean:
git clean -f -d
%token <string> ID
%token AND OR NOT OP CL EOF
%left OR AND
%nonassoc NOT
%{
open Lexer
open Formula
%}
%start parse_formula
%type <Formula.formula> parse_formula formula_expr
%%
%public formula_expr:
| ID { Var ($1) }
| NOT formula_expr { Not ($2) }
| formula_expr AND formula_expr { And ($1, $3) }
| f = formula_expr OR g = formula_expr { Or (f, g) }
| OP f = formula_expr CL { f }
parse_formula:
| formula_expr EOF { $1 }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment