我有一个简单的命令行 OCaml 应用程序,它执行计算Sys.argv.(1)
并将结果输出到标准输出。我可以用 将它编译成 Javascript js_of_ocaml
,但它给了我很多关于caml_ml_output_char
未定义的错误。我通过删除 printfs 修复了这些错误,因此它运行,但它在运行时冻结了 firefox。
如何将简单的 OCaml 命令行脚本干净地编译成基于 Javascript 的网页;不维护分叉版本或冻结浏览器?
我有一个简单的命令行 OCaml 应用程序,它执行计算Sys.argv.(1)
并将结果输出到标准输出。我可以用 将它编译成 Javascript js_of_ocaml
,但它给了我很多关于caml_ml_output_char
未定义的错误。我通过删除 printfs 修复了这些错误,因此它运行,但它在运行时冻结了 firefox。
如何将简单的 OCaml 命令行脚本干净地编译成基于 Javascript 的网页;不维护分叉版本或冻结浏览器?
您可能希望使用 webworkers,因为在 UI 线程中运行不是围绕 Javascript 的协作多任务设计的软件可能会导致浏览器锁定。您可以将以下标头添加到 OCaml 文件的顶部,以重载正常的 OCaml Sys 和打印实现
(* JsHeader.ml *)
let output_buffer_ = Buffer.create 1000
let flush x=let module J = Js.Unsafe in let () = J.call
(J.variable "postMessage") (J.variable "self")
[|J.inject (Js.string (Buffer.contents output_buffer_))|]
in Buffer.clear output_buffer_
let print_string = Buffer.add_string output_buffer_
let print_char = Buffer.add_char output_buffer_
let print_newline () = print_char '\n'
let print_endline s = print_string (s^"\n"); flush ()
let caml_ml_output_char = print_char
let printf fmt = Printf.bprintf output_buffer_ fmt
module Printf = struct
include Printf
let printf fmt = Printf.bprintf output_buffer_ fmt
end
传递命令行参数最自然的方式是通过发送给 web worker 的 URL。我们可以覆盖 Ocaml Sys 模块,改为读取?argv
为以空字符结尾的字符串序列。
module Sys = struct
let char_split delim s = (*Str.split is overkill*)
let hd = ref "" in let l = ref [] in
String.iter (fun c ->
if c = delim
then (l := (!hd)::(!l); hd := "")
else hd := (!hd) ^ (String.make 1 c)
) s;
List.rev ((!hd)::(!l))
let getenv x = List.assoc x Url.Current.arguments
let argv = Array.of_list (char_split '\x00' (getenv "?argv"))
let executable_name = argv.(0)
end
现在我们已经输入了标题,我们可以输入一个简单的 OCaml 命令行程序:
(* cli.ml *)
let _ = print_string (Array.fold_left (^) "" (Array.make 40 (String.lowercase (Sys.argv.(1)^"\n"))) )
这个命令行程序依赖于操作系统来刷新输出,但我们必须手动刷新输出。您可能还想发送一个空字符,以便 Javascript 知道该命令已完成。这可以通过附加以下页脚来实现。
(* JsFooter.ml *)
let _ = flush stdout; print_endline "\x00"
我们可以加入文件并编译它们,如下所示:
cat JsHeader.ml cli.ml JsFooter.ml > merged.ml
ocamlbuild -use-menhir -menhir "menhir" \
-pp "camlp4o -I /opt/local/lib/ocaml/site-lib js_of_ocaml/pa_js.cmo" \
-cflags -I,+js_of_ocaml,-I,+site-lib/js_of_ocaml -libs js_of_ocaml \
-lflags -I,+js_of_ocaml,-I,+site-lib/js_of_ocaml merged.byte
js_of_ocaml merged.byte
现在我们已经创建了文件merged.js,我们可以将javascript包装在一个简单的网页中,如下所示:
<html>
<head>
<meta http-equiv="Content-Type" content="text/xhtml+xml; charset=UTF-8" />
<title>ml2js sample_cli</title>
<script type="text/javascript">
<!--
var worker;
function go () {
var output=document.getElementById ("output");
var argv = encodeURIComponent("/bin/sample_cli\0"+document.getElementById ("input").value);
if (worker) {
worker.terminate();
}
worker = new Worker ("sample_cli.js?argv="+argv);
document.getElementById ("output").value="";
worker.onmessage = function (m) {
if (typeof m.data == 'string') {
if (m.data == "\0\n") {
output.scrollTop = output.scrollHeight
} else {
output.value+=m.data;
}
}
}
}
//-->
</script>
</head>
<body onload=go()>
<textarea id="input" rows="2" cols="60" onkeyup="go()" onchange="go()" style="width:90%">SAMPLE_INPUT</textarea>
<button onclick="go()">go</button><br>
<textarea id="output" rows="0" cols="60" style="width:100%;height:90%" readonly onload=go()>
Your browser does not seem to support Webworkers.
Try Firefox, Chrome or IE10+.
</textarea>
</body>
</html>
有关此方法的实际示例,请参见http://www.dansted.org/app/bctl-plain.html ,以及https://github.com/gmatht/TimeLogicUnify/blob/master/ATL/js/webworker/ ml2js.sh用于附加适当页眉、页脚等的脚本。
你用的是什么 js_of_ocaml 的版本?caml_ml_output_char 不应出现错误。在节点上运行时,您应该正确设置 sys.argv。在浏览器中,Sys.argv 设置为 [|"a.out"|]。如果您对此仍有疑问,请在https://github.com/ocsigen/js_of_ocaml/issues/new上打开 GitHub 问题。