【问题标题】:How to compile a simple Command-line OCaml script into Javascript如何将一个简单的命令行 OCaml 脚本编译成 Javascript
【发布时间】:2016-05-28 06:19:01
【问题描述】:

我有一个简单的命令行 OCaml 应用程序,它对 Sys.argv.(1) 执行计算并将结果输出到标准输出。我可以使用js_of_ocaml 将其编译为Javascript,但它给了我很多关于caml_ml_output_char 未定义的错误。我通过删除 printfs 修复了这些错误,因此它运行了,但它在运行时冻结了 firefox。

如何将简单的 OCaml 命令行脚本干净地编译成基于 Javascript 的网页;无需维护分叉版本或冻结浏览器?

【问题讨论】:

    标签: javascript ocaml js-of-ocaml


    【解决方案1】:

    您可能希望使用 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 了解附加适当页眉、页脚等的脚本。

    【讨论】:

      【解决方案2】:

      你用的是什么 js_of_ocaml 的版本? caml_ml_output_char 不应出现错误。在节点上运行时,您应该正确设置 sys.argv。在浏览器中,Sys.argv 设置为 [|"a.out"|]。 如果您对此仍有疑问,请在 https://github.com/ocsigen/js_of_ocaml/issues/new 上打开 GitHub 问题。

      【讨论】:

      • 我在 Ubuntu 上使用 1.4-1build1。即使js_of_ocaml 的更高版本在排除例如Sys.argv 大概我仍然需要像上面一样包装我的 ml 脚本以使其在浏览器中真正有用? (顺便说一句,这可能比作为一个答案更好的评论?)
      • 我已经修改了我的问题,提到我对网页特别感兴趣。
      猜你喜欢
      • 2016-07-18
      • 2011-01-27
      • 1970-01-01
      • 1970-01-01
      • 2015-08-08
      • 2017-06-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多