简体   繁体   English

如何将简单的命令行OCaml脚本编译成Javascript

[英]How to compile a simple Command-line OCaml script into Javascript

I have a simple command line OCaml application that performs a computation on Sys.argv.(1) and outputs the result to stdout. 我有一个简单的命令行OCaml应用程序,它在Sys.argv.(1)上执行计算并将结果输出到stdout。 I can compile it to Javascript with js_of_ocaml , but it gives me a lot of errors about caml_ml_output_char being undefined. 我可以使用js_of_ocaml其编译为Javascript,但它给了我很多关于caml_ml_output_char未定义的错误。 I fixed those errors by stubbing out the printfs, so it runs, but it freezes firefox while running. 我通过删除printfs修复了这些错误,因此它运行,但它在运行时冻结了firefox。

How can I cleanly compile simple OCaml command-line script into a Javascript based webpage; 如何将简单的OCaml命令行脚本干净地编译到基于Javascript的网页中; without maintaining a forked version or freezing the browser? 没有维护分叉版本或冻结浏览器?

You will probably want to use webworkers, as running software not designed around Javascript's co-operative multi-tasking in the UI thread can cause the browser to lock up. 您可能希望使用Webworkers,因为运行的软件不是围绕Javascript在UI线程中的合作多任务设计的,可能会导致浏览器锁定。 You can add the following header to the top of your OCaml file to overload the normal OCaml Sys and print implementations 您可以将以下标头添加到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

The most natural way to pass in commandline arguments is through the URL sent to the web worker. 传递命令行参数的最自然方式是通过发送给Web worker的URL。 We can override the Ocaml Sys module to instead read ?argv as a sequence of null terminated strings. 我们可以覆盖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

Now that we have entered the header we can enter a simple OCaml Command Line program: 现在我们已经输入了标题,我们可以输入一个简单的OCaml命令行程序:

(* cli.ml *)
let _ = print_string (Array.fold_left (^) "" (Array.make 40 (String.lowercase (Sys.argv.(1)^"\n"))) )

This command line program relies on the OS to flush the output, but we will have to manually flush the output. 此命令行程序依赖于OS来刷新输出,但我们必须手动刷新输出。 You may also want to send a null character so the Javascript knows that the command has finished. 您可能还想发送一个空字符,以便Javascript知道该命令已完成。 This can be achieved by appending the following footer. 这可以通过附加以下页脚来实现。

(* JsFooter.ml *)
let _ = flush stdout; print_endline "\x00" 

We can join the files and compile them as follows: 我们可以加入文件并按如下方式编译它们:

 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

Now that we have created the file merged.js we can wrap the javascript in a simple web page such as the following: 现在我们已经创建了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>

See http://www.dansted.org/app/bctl-plain.html for an example of this approach in action, and https://github.com/gmatht/TimeLogicUnify/blob/master/ATL/js/webworker/ml2js.sh for a script that appends the appropriate headers, footers etc. http://www.dansted.org/app/bctl-plain.html在行动这种做法的一个例子,和https://github.com/gmatht/TimeLogicUnify/blob/master/ATL/js/webworker/ ml2js.sh用于附加相应页眉,页脚等的脚本。

What js_of_ocaml's version are you using ? 您使用的是什么js_of_ocaml的版本? You should not get errors with caml_ml_output_char. 你不应该使用caml_ml_output_char获得错误。 When running on node, you should have sys.argv set correctly. 在节点上运行时,应该正确设置sys.argv。 In the browser, Sys.argv is set to [|"a.out"|]. 在浏览器中,Sys.argv设置为[|“a.out”|]。 Please open a GitHub issue on https://github.com/ocsigen/js_of_ocaml/issues/new if you still have an issue with this. 如果您仍然遇到此问题,请在https://github.com/ocsigen/js_of_ocaml/issues/new上打开GitHub问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM