Вы, вероятно, захотите использовать веб-воркеры, так как запуск программного обеспечения, не разработанного для совместной многозадачности Javascript в потоке пользовательского интерфейса, может привести к блокировке браузера. Вы можете добавить следующий заголовок в начало файла OCaml, чтобы перегрузить обычные реализации OCaml Sys и print.
(* 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
Самый естественный способ передачи аргументов командной строки — через 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 для скрипта, который добавляет соответствующие заголовки, нижние колонтитулы и т. д.
person
gmatht
schedule
28.05.2016