copied from js_of_ocaml/toplevel/examples/lwt_toplevel
This commit is contained in:
82
b64.ml
Normal file
82
b64.ml
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
(*
|
||||||
|
* Copyright (c) 2006-2009 Citrix Systems Inc.
|
||||||
|
* Copyright (c) 2010 Thomas Gazagnaire <thomas@gazagnaire.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*)
|
||||||
|
|
||||||
|
let default_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||||
|
|
||||||
|
let uri_safe_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||||
|
|
||||||
|
let padding = '='
|
||||||
|
|
||||||
|
let of_char ?(alphabet = default_alphabet) x =
|
||||||
|
if x = padding then 0 else String.index alphabet x
|
||||||
|
|
||||||
|
let to_char ?(alphabet = default_alphabet) x = alphabet.[x]
|
||||||
|
|
||||||
|
let decode ?alphabet input =
|
||||||
|
let length = String.length input in
|
||||||
|
let input =
|
||||||
|
if length mod 4 = 0 then input else input ^ String.make (4 - (length mod 4)) padding
|
||||||
|
in
|
||||||
|
let length = String.length input in
|
||||||
|
let words = length / 4 in
|
||||||
|
let padding =
|
||||||
|
match length with
|
||||||
|
| 0 -> 0
|
||||||
|
| _ when input.[length - 2] = padding -> 2
|
||||||
|
| _ when input.[length - 1] = padding -> 1
|
||||||
|
| _ -> 0
|
||||||
|
in
|
||||||
|
let output = Bytes.make ((words * 3) - padding) '\000' in
|
||||||
|
for i = 0 to words - 1 do
|
||||||
|
let a = of_char ?alphabet input.[(4 * i) + 0]
|
||||||
|
and b = of_char ?alphabet input.[(4 * i) + 1]
|
||||||
|
and c = of_char ?alphabet input.[(4 * i) + 2]
|
||||||
|
and d = of_char ?alphabet input.[(4 * i) + 3] in
|
||||||
|
let n = (a lsl 18) lor (b lsl 12) lor (c lsl 6) lor d in
|
||||||
|
let x = (n lsr 16) land 255 and y = (n lsr 8) land 255 and z = n land 255 in
|
||||||
|
Bytes.set output ((3 * i) + 0) (char_of_int x);
|
||||||
|
if i <> words - 1 || padding < 2 then Bytes.set output ((3 * i) + 1) (char_of_int y);
|
||||||
|
if i <> words - 1 || padding < 1 then Bytes.set output ((3 * i) + 2) (char_of_int z)
|
||||||
|
done;
|
||||||
|
Bytes.unsafe_to_string output
|
||||||
|
|
||||||
|
let encode ?(pad = true) ?alphabet input =
|
||||||
|
let length = String.length input in
|
||||||
|
let words = (length + 2) / 3 in
|
||||||
|
(* rounded up *)
|
||||||
|
let padding_len = if length mod 3 = 0 then 0 else 3 - (length mod 3) in
|
||||||
|
let output = Bytes.make (words * 4) '\000' in
|
||||||
|
let get i = if i >= length then 0 else int_of_char input.[i] in
|
||||||
|
for i = 0 to words - 1 do
|
||||||
|
let x = get ((3 * i) + 0) and y = get ((3 * i) + 1) and z = get ((3 * i) + 2) in
|
||||||
|
let n = (x lsl 16) lor (y lsl 8) lor z in
|
||||||
|
let a = (n lsr 18) land 63
|
||||||
|
and b = (n lsr 12) land 63
|
||||||
|
and c = (n lsr 6) land 63
|
||||||
|
and d = n land 63 in
|
||||||
|
Bytes.set output ((4 * i) + 0) (to_char ?alphabet a);
|
||||||
|
Bytes.set output ((4 * i) + 1) (to_char ?alphabet b);
|
||||||
|
Bytes.set output ((4 * i) + 2) (to_char ?alphabet c);
|
||||||
|
Bytes.set output ((4 * i) + 3) (to_char ?alphabet d)
|
||||||
|
done;
|
||||||
|
for i = 1 to padding_len do
|
||||||
|
Bytes.set output (Bytes.length output - i) padding
|
||||||
|
done;
|
||||||
|
if pad
|
||||||
|
then Bytes.unsafe_to_string output
|
||||||
|
else Bytes.sub_string output 0 (Bytes.length output - padding_len)
|
||||||
40
b64.mli
Normal file
40
b64.mli
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
(*
|
||||||
|
* Copyright (c) 2006-2009 Citrix Systems Inc.
|
||||||
|
* Copyright (c) 2010 Thomas Gazagnaire <thomas@gazagnaire.com>
|
||||||
|
* Copyright (c) 2014-2016 Anil Madhavapeddy <anil@recoil.org>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*)
|
||||||
|
|
||||||
|
(** Base64 RFC4648 implementation.
|
||||||
|
|
||||||
|
Base64 is a group of similar binary-to-text encoding schemes that represent binary
|
||||||
|
data in an ASCII string format by translating it into a radix-64 representation. It
|
||||||
|
is specified in RFC 4648. *)
|
||||||
|
|
||||||
|
val default_alphabet : string
|
||||||
|
(** A 64-character string specifying the regular Base64 alphabet. *)
|
||||||
|
|
||||||
|
val uri_safe_alphabet : string
|
||||||
|
(** A 64-character string specifying the URI- and filename-safe Base64 alphabet. *)
|
||||||
|
|
||||||
|
val decode : ?alphabet:string -> string -> string
|
||||||
|
(** [decode s] decodes the string [s] that is encoded in Base64 format. Will leave
|
||||||
|
trailing NULLs on the string, padding it out to a multiple of 3 characters.
|
||||||
|
[alphabet] defaults to {!default_alphabet}.
|
||||||
|
@raise Not_found if [s] is not a valid Base64 string. *)
|
||||||
|
|
||||||
|
val encode : ?pad:bool -> ?alphabet:string -> string -> string
|
||||||
|
(** [encode s] encodes the string [s] into base64. If [pad] is false, no trailing padding
|
||||||
|
is added. [pad] defaults to [true], and [alphabet] to {!default_alphabet}. *)
|
||||||
28
colorize.fake.ml
Normal file
28
colorize.fake.ml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
open Js_of_ocaml
|
||||||
|
open Js_of_ocaml_tyxml
|
||||||
|
|
||||||
|
let text ~a_class:cl s = Tyxml_js.Html.(span ~a:[ a_class [ cl ] ] [ txt s ])
|
||||||
|
|
||||||
|
let ocaml = text
|
||||||
|
|
||||||
|
let highlight from_ to_ e =
|
||||||
|
match Js.Opt.to_option e##.textContent with
|
||||||
|
| None -> assert false
|
||||||
|
| Some x ->
|
||||||
|
let x = Js.to_string x in
|
||||||
|
let (`Pos from_) = from_ in
|
||||||
|
let to_ =
|
||||||
|
match to_ with
|
||||||
|
| `Pos n -> n
|
||||||
|
| `Last -> String.length x - 1
|
||||||
|
in
|
||||||
|
e##.innerHTML := Js.string "";
|
||||||
|
let span kind s =
|
||||||
|
if s <> ""
|
||||||
|
then
|
||||||
|
let span = Tyxml_js.Html.(span ~a:[ a_class [ kind ] ] [ txt s ]) in
|
||||||
|
Dom.appendChild e (Tyxml_js.To_dom.of_element span)
|
||||||
|
in
|
||||||
|
span "normal" (String.sub x 0 from_);
|
||||||
|
span "errorloc" (String.sub x from_ (to_ - from_));
|
||||||
|
span "normal" (String.sub x to_ (String.length x - to_))
|
||||||
39
colorize.higlo.ml
Normal file
39
colorize.higlo.ml
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
open Js_of_ocaml
|
||||||
|
open Js_of_ocaml_tyxml
|
||||||
|
|
||||||
|
let text ~a_class:cl s = Tyxml_js.Html.(span ~a:[ a_class [ cl ] ] [ txt s ])
|
||||||
|
|
||||||
|
let ocaml ~a_class:cl s =
|
||||||
|
let tks = Higlo.Lang.parse ~lang:"ocaml" s in
|
||||||
|
let span' cl (s, _) = Tyxml_js.Html.(span ~a:[ a_class [ cl ] ] [ txt s ]) in
|
||||||
|
let make_span = function
|
||||||
|
| Higlo.Lang.Bcomment s -> span' "comment" s
|
||||||
|
| Higlo.Lang.Constant s -> span' "constant" s
|
||||||
|
| Higlo.Lang.Directive s -> span' "directive" s
|
||||||
|
| Higlo.Lang.Escape s -> span' "escape" s
|
||||||
|
| Higlo.Lang.Id s -> span' "id" s
|
||||||
|
| Higlo.Lang.Keyword (level, s) -> span' (Printf.sprintf "kw%d" level) s
|
||||||
|
| Higlo.Lang.Lcomment s -> span' "comment" s
|
||||||
|
| Higlo.Lang.Numeric s -> span' "numeric" s
|
||||||
|
| Higlo.Lang.String s -> span' "string" s
|
||||||
|
| Higlo.Lang.Symbol (level, s) -> span' (Printf.sprintf "sym%d" level) s
|
||||||
|
| Higlo.Lang.Text s -> span' "text" s
|
||||||
|
| Higlo.Lang.Title (_, s) -> span' "text" s
|
||||||
|
in
|
||||||
|
Tyxml_js.Html.(div ~a:[ a_class [ cl ] ] (List.map make_span tks))
|
||||||
|
|
||||||
|
let highlight (`Pos from_) to_ e =
|
||||||
|
let _ =
|
||||||
|
List.fold_left
|
||||||
|
(fun pos e ->
|
||||||
|
match Js.Opt.to_option (Dom_html.CoerceTo.element e) with
|
||||||
|
| None -> pos
|
||||||
|
| Some e ->
|
||||||
|
let size = Js.Opt.case e##.textContent (fun () -> 0) (fun t -> t##.length) in
|
||||||
|
if pos + size > from_ && (to_ = `Last || `Pos pos < to_)
|
||||||
|
then e##.classList##add (Js.string "errorloc");
|
||||||
|
pos + size)
|
||||||
|
0
|
||||||
|
(Dom.list_of_nodeList e##.childNodes)
|
||||||
|
in
|
||||||
|
()
|
||||||
9
colorize.mli
Normal file
9
colorize.mli
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
open Js_of_ocaml
|
||||||
|
open Js_of_ocaml_tyxml
|
||||||
|
|
||||||
|
val text : a_class:string -> string -> [> Html_types.div_content ] Tyxml_js.Html.elt
|
||||||
|
|
||||||
|
val ocaml : a_class:string -> string -> [> Html_types.div_content ] Tyxml_js.Html.elt
|
||||||
|
|
||||||
|
val highlight :
|
||||||
|
[ `Pos of int ] -> [ `Last | `Pos of int ] -> Dom_html.element Js.t -> unit
|
||||||
150
dune
Normal file
150
dune
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
(executables
|
||||||
|
(names toplevel)
|
||||||
|
(libraries
|
||||||
|
js_of_ocaml-compiler
|
||||||
|
js_of_ocaml-tyxml
|
||||||
|
js_of_ocaml-toplevel
|
||||||
|
lwt
|
||||||
|
js_of_ocaml-lwt
|
||||||
|
;; not used directly
|
||||||
|
graphics
|
||||||
|
js_of_ocaml.deriving
|
||||||
|
react
|
||||||
|
reactiveData
|
||||||
|
str
|
||||||
|
dynlink
|
||||||
|
(select
|
||||||
|
ocp_indent.ml
|
||||||
|
from
|
||||||
|
(ocp-indent.lib -> ocp_indent.ok.ml)
|
||||||
|
(-> ocp_indent.fake.ml))
|
||||||
|
(select
|
||||||
|
colorize.ml
|
||||||
|
from
|
||||||
|
(higlo -> colorize.higlo.ml)
|
||||||
|
(!higlo -> colorize.fake.ml))
|
||||||
|
(select
|
||||||
|
graphics_support.ml
|
||||||
|
from
|
||||||
|
(js_of_ocaml-lwt.graphics -> graphics_support.enabled.ml)
|
||||||
|
(-> graphics_support.disabled.ml))
|
||||||
|
(select
|
||||||
|
ppx_support.ml
|
||||||
|
from
|
||||||
|
(js_of_ocaml-ppx -> ppx_support.enabled.ml)
|
||||||
|
(-> ppx_support.disabled.ml)))
|
||||||
|
(flags
|
||||||
|
(:standard -rectypes))
|
||||||
|
(link_flags
|
||||||
|
(:standard -linkall))
|
||||||
|
(modes byte js)
|
||||||
|
(js_of_ocaml
|
||||||
|
(link_flags (:standard))
|
||||||
|
(build_runtime_flags
|
||||||
|
(:standard
|
||||||
|
+toplevel.js
|
||||||
|
+dynlink.js
|
||||||
|
--file
|
||||||
|
%{dep:examples.ml}
|
||||||
|
--file
|
||||||
|
%{dep:test_dynlink.cmo}
|
||||||
|
--file
|
||||||
|
%{dep:test_dynlink.js}))
|
||||||
|
(flags
|
||||||
|
(:standard
|
||||||
|
--toplevel
|
||||||
|
(:include effects_flags.sexp))))
|
||||||
|
(modules
|
||||||
|
(:standard \ test_dynlink examples effects_flags))
|
||||||
|
(preprocess
|
||||||
|
(pps js_of_ocaml-ppx)))
|
||||||
|
|
||||||
|
(rule
|
||||||
|
(targets test_dynlink.cmo test_dynlink.cmi)
|
||||||
|
(action
|
||||||
|
(run ocamlc -c %{dep:test_dynlink.ml})))
|
||||||
|
|
||||||
|
(rule
|
||||||
|
(targets test_dynlink.js)
|
||||||
|
(action
|
||||||
|
(run %{bin:js_of_ocaml} --pretty --toplevel %{dep:test_dynlink.cmo})))
|
||||||
|
|
||||||
|
(rule
|
||||||
|
(targets export.txt)
|
||||||
|
(deps
|
||||||
|
(package js_of_ocaml-ppx)
|
||||||
|
(package js_of_ocaml)
|
||||||
|
(package js_of_ocaml-compiler)
|
||||||
|
(package js_of_ocaml-lwt)
|
||||||
|
(package js_of_ocaml-tyxml)
|
||||||
|
(package js_of_ocaml-toplevel))
|
||||||
|
(action
|
||||||
|
(run
|
||||||
|
jsoo_listunits
|
||||||
|
-o
|
||||||
|
%{targets}
|
||||||
|
stdlib
|
||||||
|
graphics
|
||||||
|
str
|
||||||
|
dynlink
|
||||||
|
js_of_ocaml-compiler.runtime
|
||||||
|
js_of_ocaml-lwt.graphics
|
||||||
|
js_of_ocaml-ppx.as-lib
|
||||||
|
js_of_ocaml.deriving
|
||||||
|
lwt
|
||||||
|
tyxml.functor
|
||||||
|
tyxml.functor:html_types.cmi
|
||||||
|
react
|
||||||
|
reactiveData
|
||||||
|
js_of_ocaml
|
||||||
|
js_of_ocaml-lwt
|
||||||
|
js_of_ocaml-tyxml
|
||||||
|
js_of_ocaml-toplevel)))
|
||||||
|
|
||||||
|
(executable
|
||||||
|
(name effects_flags)
|
||||||
|
(modules effects_flags))
|
||||||
|
|
||||||
|
(rule
|
||||||
|
(target effects_flags.sexp)
|
||||||
|
(action
|
||||||
|
(with-stdout-to
|
||||||
|
%{target}
|
||||||
|
(run ./effects_flags.exe sexp))))
|
||||||
|
|
||||||
|
(rule
|
||||||
|
(target effects_flags.txt)
|
||||||
|
(action
|
||||||
|
(with-stdout-to
|
||||||
|
%{target}
|
||||||
|
(run ./effects_flags.exe txt))))
|
||||||
|
|
||||||
|
(rule
|
||||||
|
(targets toplevel.js)
|
||||||
|
(action
|
||||||
|
(run
|
||||||
|
%{bin:js_of_ocaml}
|
||||||
|
compile
|
||||||
|
--pretty
|
||||||
|
%{read-strings:effects_flags.txt}
|
||||||
|
--Werror
|
||||||
|
--target-env
|
||||||
|
browser
|
||||||
|
--file
|
||||||
|
%{dep:examples.ml}
|
||||||
|
--file
|
||||||
|
%{dep:test_dynlink.cmo}
|
||||||
|
--file
|
||||||
|
%{dep:test_dynlink.js}
|
||||||
|
--export
|
||||||
|
%{dep:export.txt}
|
||||||
|
--toplevel
|
||||||
|
--disable
|
||||||
|
shortvar
|
||||||
|
%{dep:toplevel.bc}
|
||||||
|
-o
|
||||||
|
%{targets})))
|
||||||
|
|
||||||
|
(alias
|
||||||
|
(name default)
|
||||||
|
(deps toplevel.js toplevel.bc.js index.html))
|
||||||
2
dune-project
Normal file
2
dune-project
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
(lang dune 3.4)
|
||||||
|
(name oplevel)
|
||||||
15
effects_flags.ml
Normal file
15
effects_flags.ml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
let enable b n =
|
||||||
|
let f = if b then "--enable" else "--disable" in
|
||||||
|
[ f; n ]
|
||||||
|
|
||||||
|
let () =
|
||||||
|
let major = String.split_on_char '.' Sys.ocaml_version |> List.hd |> int_of_string in
|
||||||
|
let has_effect = major >= 5 in
|
||||||
|
let l = enable has_effect "effects" in
|
||||||
|
match Sys.argv |> Array.to_list |> List.tl with
|
||||||
|
| "txt" :: [] -> List.iter print_endline l
|
||||||
|
| "sexp" :: [] ->
|
||||||
|
print_endline "(";
|
||||||
|
List.iter print_endline l;
|
||||||
|
print_endline ")"
|
||||||
|
| _ -> assert false
|
||||||
231
examples.ml
Normal file
231
examples.ml
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
(** Overview *)
|
||||||
|
|
||||||
|
let x = 10 + 10
|
||||||
|
|
||||||
|
let y = x * 3
|
||||||
|
|
||||||
|
let c = String.make x 'a'
|
||||||
|
|
||||||
|
let sin1 = sin 1.
|
||||||
|
|
||||||
|
let rec fact n = if n = 0 then 1. else float n *. fact (n - 1)
|
||||||
|
|
||||||
|
let _ = Printf.printf "fact 20 = %f\n" (fact 20)
|
||||||
|
|
||||||
|
let _ = "abc" < "def"
|
||||||
|
|
||||||
|
(** Mutually recursive function *)
|
||||||
|
|
||||||
|
let rec even n =
|
||||||
|
match n with
|
||||||
|
| 0 -> true
|
||||||
|
| x -> odd (x - 1)
|
||||||
|
|
||||||
|
and odd n =
|
||||||
|
match n with
|
||||||
|
| 0 -> false
|
||||||
|
| x -> even (x - 1)
|
||||||
|
|
||||||
|
(** Mutually recursive module *)
|
||||||
|
|
||||||
|
module rec Odd : sig
|
||||||
|
val odd : int -> bool
|
||||||
|
end = struct
|
||||||
|
let odd x = if x = 0 then false else Even.even (pred x)
|
||||||
|
end
|
||||||
|
|
||||||
|
and Even : sig
|
||||||
|
val even : int -> bool
|
||||||
|
end = struct
|
||||||
|
let even x = if x = 0 then true else Odd.odd (pred x)
|
||||||
|
end
|
||||||
|
|
||||||
|
(** Reactive dom *)
|
||||||
|
|
||||||
|
open Js_of_ocaml
|
||||||
|
open Js_of_ocaml_lwt
|
||||||
|
open Js_of_ocaml_tyxml
|
||||||
|
|
||||||
|
let display x =
|
||||||
|
Dom.appendChild (Dom_html.getElementById "output") (Tyxml_js.To_dom.of_element x)
|
||||||
|
|
||||||
|
module RList = ReactiveData.RList
|
||||||
|
|
||||||
|
let rl, rhandle = RList.create []
|
||||||
|
|
||||||
|
let li_rl = RList.map (fun x -> Tyxml_js.Html.(li [ txt x ])) rl
|
||||||
|
|
||||||
|
let ul_elt = Tyxml_js.R.Html.ul li_rl
|
||||||
|
|
||||||
|
let init =
|
||||||
|
let _ = RList.snoc "# cons \"some string\"" rhandle in
|
||||||
|
let _ = RList.snoc "# snoc \"some other\"" rhandle in
|
||||||
|
let _ = RList.snoc "# insert \"anywhere\" 1" rhandle in
|
||||||
|
let _ = RList.snoc "# remove 1" rhandle in
|
||||||
|
()
|
||||||
|
|
||||||
|
let snoc s = RList.snoc s rhandle
|
||||||
|
|
||||||
|
let cons s = RList.cons s rhandle
|
||||||
|
|
||||||
|
let insert s pos = RList.insert s pos rhandle
|
||||||
|
|
||||||
|
let remove pos = RList.remove pos rhandle
|
||||||
|
|
||||||
|
let time_signal =
|
||||||
|
let s, set = React.S.create (Sys.time ()) in
|
||||||
|
let rec loop () : unit Lwt.t =
|
||||||
|
set (Sys.time ());
|
||||||
|
Lwt.bind (Lwt_js.sleep 1.) loop
|
||||||
|
in
|
||||||
|
Lwt.async loop;
|
||||||
|
s
|
||||||
|
|
||||||
|
let div_elt =
|
||||||
|
Tyxml_js.(
|
||||||
|
Html.(
|
||||||
|
div
|
||||||
|
[ h4
|
||||||
|
[ txt "Uptime is "
|
||||||
|
; R.Html.txt
|
||||||
|
(React.S.map (fun s -> string_of_int (int_of_float s)) time_signal)
|
||||||
|
; txt " s"
|
||||||
|
]
|
||||||
|
; ul_elt
|
||||||
|
]))
|
||||||
|
|
||||||
|
let _ = display div_elt
|
||||||
|
|
||||||
|
(** Graphics: Draw *)
|
||||||
|
|
||||||
|
open Graphics_js
|
||||||
|
|
||||||
|
let () =
|
||||||
|
loop [ Mouse_motion ] (function { mouse_x = x; mouse_y = y } -> fill_circle x y 5)
|
||||||
|
|
||||||
|
(** Graphics: Draw chars*)
|
||||||
|
|
||||||
|
open Graphics_js
|
||||||
|
|
||||||
|
let () =
|
||||||
|
loop [ Mouse_motion; Key_pressed ] (function
|
||||||
|
| { key = '\000'; _ } -> ()
|
||||||
|
| { mouse_x = x; mouse_y = y; key } ->
|
||||||
|
moveto x y;
|
||||||
|
draw_char key)
|
||||||
|
|
||||||
|
(** Graphics: PingPong *)
|
||||||
|
|
||||||
|
open Js_of_ocaml_lwt
|
||||||
|
open Graphics_js
|
||||||
|
|
||||||
|
let c = 3
|
||||||
|
|
||||||
|
let x0 = 0
|
||||||
|
|
||||||
|
and x1 = size_x ()
|
||||||
|
|
||||||
|
and y0 = 0
|
||||||
|
|
||||||
|
and y1 = size_y ()
|
||||||
|
|
||||||
|
let draw_ball x y =
|
||||||
|
set_color foreground;
|
||||||
|
fill_circle x y c
|
||||||
|
|
||||||
|
let state = ref (Lwt.task ())
|
||||||
|
|
||||||
|
let wait () = fst !state
|
||||||
|
|
||||||
|
let rec pong_aux x y dx dy =
|
||||||
|
draw_ball x y;
|
||||||
|
let new_x = x + dx and new_y = y + dy in
|
||||||
|
let new_dx = if new_x - c <= x0 || new_x + c >= x1 then -dx else dx
|
||||||
|
and new_dy = if new_y - c <= y0 || new_y + c >= y1 then -dy else dy in
|
||||||
|
Lwt.bind (wait ()) (fun () -> pong_aux new_x new_y new_dx new_dy)
|
||||||
|
|
||||||
|
let rec start () =
|
||||||
|
let t = Lwt.task () in
|
||||||
|
let _, w = !state in
|
||||||
|
state := t;
|
||||||
|
clear_graph ();
|
||||||
|
Lwt.wakeup w ();
|
||||||
|
Lwt.bind (Lwt_js.sleep (1. /. 60.)) start
|
||||||
|
|
||||||
|
let pong x y dx dy = pong_aux x y dx dy
|
||||||
|
|
||||||
|
let _ = pong 111 87 2 3
|
||||||
|
|
||||||
|
let _ = pong 28 57 5 3
|
||||||
|
|
||||||
|
let _ = start ()
|
||||||
|
|
||||||
|
(** Effect handler *)
|
||||||
|
|
||||||
|
module Txn : sig
|
||||||
|
type 'a t
|
||||||
|
|
||||||
|
val atomically : (unit -> unit) -> unit
|
||||||
|
|
||||||
|
val ref : 'a -> 'a t
|
||||||
|
|
||||||
|
val ( ! ) : 'a t -> 'a
|
||||||
|
|
||||||
|
val ( := ) : 'a t -> 'a -> unit
|
||||||
|
end = struct
|
||||||
|
open Effect
|
||||||
|
open Effect.Deep
|
||||||
|
|
||||||
|
type 'a t = 'a ref
|
||||||
|
|
||||||
|
type _ Effect.t += Update : 'a t * 'a -> unit Effect.t
|
||||||
|
|
||||||
|
let atomically f =
|
||||||
|
let comp =
|
||||||
|
match_with
|
||||||
|
f
|
||||||
|
()
|
||||||
|
{ retc = (fun x _ -> x)
|
||||||
|
; exnc =
|
||||||
|
(fun e rb ->
|
||||||
|
rb ();
|
||||||
|
raise e)
|
||||||
|
; effc =
|
||||||
|
(fun (type a) (e : a Effect.t) ->
|
||||||
|
match e with
|
||||||
|
| Update (r, v) ->
|
||||||
|
Some
|
||||||
|
(fun (k : (a, _) continuation) rb ->
|
||||||
|
let old_v = !r in
|
||||||
|
r := v;
|
||||||
|
continue k () (fun () ->
|
||||||
|
r := old_v;
|
||||||
|
rb ()))
|
||||||
|
| _ -> None)
|
||||||
|
}
|
||||||
|
in
|
||||||
|
comp (fun () -> ())
|
||||||
|
|
||||||
|
let ref = ref
|
||||||
|
|
||||||
|
let ( ! ) = ( ! )
|
||||||
|
|
||||||
|
let ( := ) r v = perform (Update (r, v))
|
||||||
|
end
|
||||||
|
|
||||||
|
let example () =
|
||||||
|
let open Txn in
|
||||||
|
let exception Res of int in
|
||||||
|
let r = ref 10 in
|
||||||
|
Printf.printf "T0: %d\n" !r;
|
||||||
|
try
|
||||||
|
atomically (fun () ->
|
||||||
|
r := 20;
|
||||||
|
r := 21;
|
||||||
|
Printf.printf "T1: Before abort %d\n" !r;
|
||||||
|
raise (Res !r) |> ignore;
|
||||||
|
Printf.printf "T1: After abort %d\n" !r;
|
||||||
|
r := 30)
|
||||||
|
with Res v ->
|
||||||
|
Printf.printf "T0: T1 aborted with %d\n" v;
|
||||||
|
Printf.printf "T0: %d\n" !r
|
||||||
1
graphics_support.disabled.ml
Normal file
1
graphics_support.disabled.ml
Normal file
@ -0,0 +1 @@
|
|||||||
|
let init _ = ()
|
||||||
1
graphics_support.enabled.ml
Normal file
1
graphics_support.enabled.ml
Normal file
@ -0,0 +1 @@
|
|||||||
|
let init elt = Graphics_js.open_canvas elt
|
||||||
43
indent.ml
Normal file
43
indent.ml
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
open Js_of_ocaml
|
||||||
|
|
||||||
|
let textarea (textbox : Dom_html.textAreaElement Js.t) : unit =
|
||||||
|
let rec loop s acc (i, pos') =
|
||||||
|
try
|
||||||
|
let pos = String.index_from s pos' '\n' in
|
||||||
|
loop s ((i, (pos', pos)) :: acc) (succ i, succ pos)
|
||||||
|
with _ -> List.rev ((i, (pos', String.length s)) :: acc)
|
||||||
|
in
|
||||||
|
let rec find (l : (int * (int * int)) list) c =
|
||||||
|
match l with
|
||||||
|
| [] -> assert false
|
||||||
|
| (i, (lo, up)) :: _ when up >= c -> c, i, lo, up
|
||||||
|
| (_, (_lo, _up)) :: rem -> find rem c
|
||||||
|
in
|
||||||
|
let v = textbox##.value in
|
||||||
|
let pos =
|
||||||
|
let c1 = textbox##.selectionStart and c2 = textbox##.selectionEnd in
|
||||||
|
if Js.Opt.test (Js.Opt.return c1) && Js.Opt.test (Js.Opt.return c2)
|
||||||
|
then
|
||||||
|
let l = loop (Js.to_string v) [] (0, 0) in
|
||||||
|
Some (find l c1, find l c2)
|
||||||
|
else None
|
||||||
|
in
|
||||||
|
let f =
|
||||||
|
match pos with
|
||||||
|
| None -> fun _ -> true
|
||||||
|
| Some ((_c1, line1, _lo1, _up1), (_c2, line2, _lo2, _up2)) ->
|
||||||
|
fun l -> l >= line1 + 1 && l <= line2 + 1
|
||||||
|
in
|
||||||
|
let v = Ocp_indent.indent (Js.to_string v) f in
|
||||||
|
textbox##.value := Js.string v;
|
||||||
|
match pos with
|
||||||
|
| Some ((c1, line1, _lo1, up1), (c2, line2, _lo2, up2)) ->
|
||||||
|
let l = loop v [] (0, 0) in
|
||||||
|
let lo1'', up1'' = List.assoc line1 l in
|
||||||
|
let lo2'', up2'' = List.assoc line2 l in
|
||||||
|
let n1 = max (c1 + up1'' - up1) lo1'' in
|
||||||
|
let n2 = max (c2 + up2'' - up2) lo2'' in
|
||||||
|
let () = (Obj.magic textbox)##setSelectionRange n1 n2 in
|
||||||
|
textbox##focus;
|
||||||
|
()
|
||||||
|
| None -> ()
|
||||||
3
indent.mli
Normal file
3
indent.mli
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
open Js_of_ocaml
|
||||||
|
|
||||||
|
val textarea : Dom_html.textAreaElement Js.t -> unit
|
||||||
233
index.html
Normal file
233
index.html
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
|
||||||
|
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<title>OCaml toplevel</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" />
|
||||||
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" />
|
||||||
|
<style>
|
||||||
|
|
||||||
|
code, kbd, pre, samp {
|
||||||
|
font-family: Menlo,Monaco,Consolas,monospace;
|
||||||
|
}
|
||||||
|
body,html {
|
||||||
|
height: 100%;
|
||||||
|
background-color:#eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toplevel-container {
|
||||||
|
width: 50%;
|
||||||
|
background-color: black;
|
||||||
|
color: #ccc;
|
||||||
|
overflow: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
height: 100%;
|
||||||
|
float:left;
|
||||||
|
padding:10px;
|
||||||
|
padding-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toplevel-container pre#output {
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toplevel-container #output {
|
||||||
|
background-color:transparent;
|
||||||
|
color: #ccc;
|
||||||
|
border: none;
|
||||||
|
line-height:18px;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toplevel-container textarea {
|
||||||
|
width:90%;
|
||||||
|
line-height:18px;
|
||||||
|
font-size: 12px;
|
||||||
|
background-color: transparent;
|
||||||
|
color: #fff;
|
||||||
|
border: 0;
|
||||||
|
resize: none;
|
||||||
|
outline: none;
|
||||||
|
font-family: Menlo,Monaco,Consolas,monospace;
|
||||||
|
font-weight: bold;
|
||||||
|
float:left;
|
||||||
|
margin: 0px;
|
||||||
|
padding:0px;
|
||||||
|
}
|
||||||
|
#toplevel-container #sharp {
|
||||||
|
float: left;
|
||||||
|
line-height:18px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-family: Menlo,Monaco,Consolas,monospace;
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
.sharp:before{
|
||||||
|
content:"# ";
|
||||||
|
line-height:18px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-family: Menlo,Monaco,Consolas,monospace;
|
||||||
|
}
|
||||||
|
.caml{
|
||||||
|
color:rgb(110, 110, 201);
|
||||||
|
}
|
||||||
|
#toplevel-side{
|
||||||
|
position:relative;
|
||||||
|
width:45%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
text-align:justify;
|
||||||
|
float:left;
|
||||||
|
margin-left:30px;
|
||||||
|
}
|
||||||
|
#toplevel-side ul{
|
||||||
|
padding: 0px;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stderr {
|
||||||
|
color: #d9534f;
|
||||||
|
}
|
||||||
|
.stdout {
|
||||||
|
|
||||||
|
}
|
||||||
|
.errorloc{
|
||||||
|
border-bottom-width: 3px;
|
||||||
|
border-bottom-style: solid;
|
||||||
|
border-bottom-color: red;
|
||||||
|
}
|
||||||
|
canvas {
|
||||||
|
border: 1px dashed black;
|
||||||
|
float: left;
|
||||||
|
margin: 7px;
|
||||||
|
}
|
||||||
|
#output canvas {
|
||||||
|
background-color: #464646;
|
||||||
|
float: none;
|
||||||
|
display: block;
|
||||||
|
border: 1px dashed while;
|
||||||
|
margin: 7px;
|
||||||
|
}
|
||||||
|
#output img {
|
||||||
|
display:block;
|
||||||
|
}
|
||||||
|
#toplevel-examples {
|
||||||
|
width: 270px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
#toplevel-examples .list-group-item{
|
||||||
|
padding: 5px 15px;
|
||||||
|
}
|
||||||
|
#btn-share {
|
||||||
|
float:right;
|
||||||
|
margin-top:-20px;
|
||||||
|
background-color:rgb(92, 129, 184);
|
||||||
|
border-color: rgb(70, 75, 128);
|
||||||
|
padding: 1px 5px;
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
.clear { clear:both; }
|
||||||
|
|
||||||
|
.sharp .id { color: #59B65C ; font-style: italic }
|
||||||
|
.sharp .kw0 { color: rgb(64, 75, 190); font-weight: bold ;}
|
||||||
|
.sharp .kw1 { color: rgb(150, 0, 108); font-weight: bold ;}
|
||||||
|
.sharp .kw2 { color: rgb(23, 100, 42); font-weight: bold ;}
|
||||||
|
.sharp .kw3 { color: #59B65C; font-weight: bold ;}
|
||||||
|
.sharp .kw4 { color: #59B65C; font-weight: bold ;}
|
||||||
|
.sharp .comment { color: green ; font-style: italic ; }
|
||||||
|
.sharp .string { color: #6B6B6B; font-weight: bold ; }
|
||||||
|
.sharp .text { }
|
||||||
|
.sharp .numeric { color: #729AAF; }
|
||||||
|
.sharp .directive { font-style: italic ; color : #EB00FF; } ;
|
||||||
|
.sharp .escape { color: #409290 ; }
|
||||||
|
.sharp .symbol0 { color: orange ; font-weight: bold ; }
|
||||||
|
.sharp .symbol1 { color: #993300 ; font-weight: bold ; }
|
||||||
|
.sharp .constant { color: rgb(0, 152, 255); }
|
||||||
|
</style>
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
window.onhashchange = function() { window.location.reload() }
|
||||||
|
var hash = window.location.hash.replace(/^#/,"");
|
||||||
|
var fields = hash.split(/&/);
|
||||||
|
var prefix = "";
|
||||||
|
var version = "";
|
||||||
|
var main = "toplevel.bc.js";
|
||||||
|
for(var f in fields){
|
||||||
|
var data = fields[f].split(/=/);
|
||||||
|
if(data[0] == "version"){
|
||||||
|
version = data[1].replace(/%20|%2B/g,"+");
|
||||||
|
}
|
||||||
|
else if (data[0] == "separate"){
|
||||||
|
main = "toplevel.bc.js";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function load_script(url){
|
||||||
|
var fileref=document.createElement('script');
|
||||||
|
fileref.setAttribute("type","text/javascript");
|
||||||
|
fileref.setAttribute("src", prefix+(version==""?"":(version+"/"))+url);
|
||||||
|
document.getElementsByTagName("head")[0].appendChild(fileref);
|
||||||
|
}
|
||||||
|
load_script("exported-unit.cmis.js");
|
||||||
|
load_script(main);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="toplevel-container">
|
||||||
|
<pre id="output"></pre>
|
||||||
|
<div>
|
||||||
|
<div id="sharp" class="sharp"></div>
|
||||||
|
<textarea id="userinput">Loading ...</textarea>
|
||||||
|
<button type="button" class="btn btn-default"
|
||||||
|
id="btn-share">Share</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="toplevel-side">
|
||||||
|
<h3>Js_of_ocaml</h3>
|
||||||
|
<h4>A compiler from OCaml bytecode to Javascript.</h4>
|
||||||
|
<p>It makes OCaml programs that run on Web browsers. It is
|
||||||
|
easy to install as it works with an existing installation of OCaml,
|
||||||
|
with no need to recompile any library. It comes with bindings for a
|
||||||
|
large part of the browser APIs.</p>
|
||||||
|
<p>This web-based OCaml toplevel is compiled using Js_of_ocaml.</p>
|
||||||
|
<h4>Command</h4>
|
||||||
|
<table class="table table-striped table-condensed">
|
||||||
|
<tbody class>
|
||||||
|
<tr>
|
||||||
|
<td>Enter/Return</td>
|
||||||
|
<td>Submit code</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Ctrl + Enter</td>
|
||||||
|
<td>Newline</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Up / Down</td>
|
||||||
|
<td>Browse history</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Ctrl + l</td>
|
||||||
|
<td>Clear display</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Ctrl + k</td>
|
||||||
|
<td>Reset toplevel</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Tab</td>
|
||||||
|
<td>Indent code</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<h4>Try to execute samples</h4>
|
||||||
|
<div id="toplevel-examples" class="list-group"></div>
|
||||||
|
<canvas width=200 height=200 id="test-canvas"></canvas>
|
||||||
|
<h4 class="clear">See the generated javascript code</h4>
|
||||||
|
<pre id="last-js">
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
3
ocp_indent.fake.ml
Normal file
3
ocp_indent.fake.ml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
let indent s _in_lines = s
|
||||||
|
|
||||||
|
(* ocp-indent not available *)
|
||||||
14
ocp_indent.ok.ml
Normal file
14
ocp_indent.ok.ml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
let _ = Approx_lexer.enable_extension "lwt"
|
||||||
|
|
||||||
|
let indent s in_lines =
|
||||||
|
let output =
|
||||||
|
{ IndentPrinter.debug = false
|
||||||
|
; config = IndentConfig.default
|
||||||
|
; in_lines
|
||||||
|
; indent_empty = true
|
||||||
|
; adaptive = true
|
||||||
|
; kind = IndentPrinter.Print (fun s acc -> acc ^ s)
|
||||||
|
}
|
||||||
|
in
|
||||||
|
let stream = Nstream.of_string s in
|
||||||
|
IndentPrinter.proceed output stream IndentBlock.empty ""
|
||||||
1
ppx_support.disabled.ml
Normal file
1
ppx_support.disabled.ml
Normal file
@ -0,0 +1 @@
|
|||||||
|
let init () = ()
|
||||||
1
ppx_support.enabled.ml
Normal file
1
ppx_support.enabled.ml
Normal file
@ -0,0 +1 @@
|
|||||||
|
let init () = Ast_mapper.register "js_of_ocaml" (fun _ -> Ppx_js.mapper)
|
||||||
3
test_dynlink.ml
Normal file
3
test_dynlink.ml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
let _ = print_endline "Dynlink OK"
|
||||||
|
|
||||||
|
let f () = print_endline "Test_dynlink.f Ok"
|
||||||
492
toplevel.ml
Normal file
492
toplevel.ml
Normal file
@ -0,0 +1,492 @@
|
|||||||
|
(* Js_of_ocaml toplevel
|
||||||
|
* http://www.ocsigen.org/js_of_ocaml/
|
||||||
|
* Copyright (C) 2011 Jérôme Vouillon
|
||||||
|
* Laboratoire PPS - CNRS Université Paris Diderot
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, with linking exception;
|
||||||
|
* either version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*)
|
||||||
|
|
||||||
|
open Js_of_ocaml
|
||||||
|
open Js_of_ocaml_lwt
|
||||||
|
open Js_of_ocaml_tyxml
|
||||||
|
open Js_of_ocaml_toplevel
|
||||||
|
open Lwt
|
||||||
|
|
||||||
|
let compiler_name = "OCaml"
|
||||||
|
|
||||||
|
let by_id s = Dom_html.getElementById s
|
||||||
|
|
||||||
|
let by_id_coerce s f =
|
||||||
|
Js.Opt.get (f (Dom_html.getElementById s)) (fun () -> raise Not_found)
|
||||||
|
|
||||||
|
let do_by_id s f = try f (Dom_html.getElementById s) with Not_found -> ()
|
||||||
|
|
||||||
|
(* load file using a synchronous XMLHttpRequest *)
|
||||||
|
let load_resource_aux filename url =
|
||||||
|
Js_of_ocaml_lwt.XmlHttpRequest.perform_raw ~response_type:XmlHttpRequest.ArrayBuffer url
|
||||||
|
>|= fun frame ->
|
||||||
|
if frame.Js_of_ocaml_lwt.XmlHttpRequest.code = 200
|
||||||
|
then
|
||||||
|
Js.Opt.case
|
||||||
|
frame.Js_of_ocaml_lwt.XmlHttpRequest.content
|
||||||
|
(fun () -> Printf.eprintf "Could not load %s\n" filename)
|
||||||
|
(fun b ->
|
||||||
|
Sys_js.update_file ~name:filename ~content:(Typed_array.String.of_arrayBuffer b))
|
||||||
|
else ()
|
||||||
|
|
||||||
|
let load_resource scheme ~prefix ~path:suffix =
|
||||||
|
let url = scheme ^ suffix in
|
||||||
|
let filename = Filename.concat prefix suffix in
|
||||||
|
Lwt.async (fun () -> load_resource_aux filename url);
|
||||||
|
Some ""
|
||||||
|
|
||||||
|
let setup_pseudo_fs ~load_cmis_from_server =
|
||||||
|
Sys_js.mount ~path:"/dev/" (fun ~prefix:_ ~path:_ -> None);
|
||||||
|
Sys_js.mount ~path:"/http/" (load_resource "http://");
|
||||||
|
Sys_js.mount ~path:"/https/" (load_resource "https://");
|
||||||
|
Sys_js.mount ~path:"/ftp/" (load_resource "ftp://");
|
||||||
|
if load_cmis_from_server then Sys_js.mount ~path:"/home/" (load_resource "filesys/")
|
||||||
|
|
||||||
|
let exec' s =
|
||||||
|
let res : bool = JsooTop.use Format.std_formatter s in
|
||||||
|
if not res then Format.eprintf "error while evaluating %s@." s
|
||||||
|
|
||||||
|
module Version = struct
|
||||||
|
type t = int list
|
||||||
|
|
||||||
|
let split_char ~sep p =
|
||||||
|
let len = String.length p in
|
||||||
|
let rec split beg cur =
|
||||||
|
if cur >= len
|
||||||
|
then if cur - beg > 0 then [ String.sub p beg (cur - beg) ] else []
|
||||||
|
else if sep p.[cur]
|
||||||
|
then String.sub p beg (cur - beg) :: split (cur + 1) (cur + 1)
|
||||||
|
else split beg (cur + 1)
|
||||||
|
in
|
||||||
|
split 0 0
|
||||||
|
|
||||||
|
let split v =
|
||||||
|
match
|
||||||
|
split_char
|
||||||
|
~sep:(function
|
||||||
|
| '+' | '-' | '~' -> true
|
||||||
|
| _ -> false)
|
||||||
|
v
|
||||||
|
with
|
||||||
|
| [] -> assert false
|
||||||
|
| x :: _ ->
|
||||||
|
List.map
|
||||||
|
int_of_string
|
||||||
|
(split_char
|
||||||
|
~sep:(function
|
||||||
|
| '.' -> true
|
||||||
|
| _ -> false)
|
||||||
|
x)
|
||||||
|
|
||||||
|
let current : t = split Sys.ocaml_version
|
||||||
|
|
||||||
|
let compint (a : int) b = compare a b
|
||||||
|
|
||||||
|
let rec compare v v' =
|
||||||
|
match v, v' with
|
||||||
|
| [ x ], [ y ] -> compint x y
|
||||||
|
| [], [] -> 0
|
||||||
|
| [], y :: _ -> compint 0 y
|
||||||
|
| x :: _, [] -> compint x 0
|
||||||
|
| x :: xs, y :: ys -> (
|
||||||
|
match compint x y with
|
||||||
|
| 0 -> compare xs ys
|
||||||
|
| n -> n)
|
||||||
|
end
|
||||||
|
|
||||||
|
let setup_toplevel () =
|
||||||
|
Clflags.debug := true;
|
||||||
|
JsooTop.initialize ();
|
||||||
|
Sys.interactive := false;
|
||||||
|
if Version.compare Version.current [ 4; 07 ] >= 0 then exec' "open Stdlib";
|
||||||
|
exec'
|
||||||
|
"module Lwt_main = struct\n\
|
||||||
|
\ let run t = match Lwt.state t with\n\
|
||||||
|
\ | Lwt.Return x -> x\n\
|
||||||
|
\ | Lwt.Fail e -> raise e\n\
|
||||||
|
\ | Lwt.Sleep -> failwith \"Lwt_main.run: thread didn't return\"\n\
|
||||||
|
\ end";
|
||||||
|
let header1 = Printf.sprintf " %s version %%s" compiler_name in
|
||||||
|
let header2 =
|
||||||
|
Printf.sprintf " Compiled with Js_of_ocaml version %s" Sys_js.js_of_ocaml_version
|
||||||
|
in
|
||||||
|
exec' (Printf.sprintf "Format.printf \"%s@.\" Sys.ocaml_version;;" header1);
|
||||||
|
exec' (Printf.sprintf "Format.printf \"%s@.\";;" header2);
|
||||||
|
exec' "#enable \"pretty\";;";
|
||||||
|
exec' "#disable \"shortvar\";;";
|
||||||
|
Ppx_support.init ();
|
||||||
|
Toploop.add_directive
|
||||||
|
"load_js"
|
||||||
|
(Toploop.Directive_string (fun name -> Js.Unsafe.global##load_script_ name))
|
||||||
|
{ section = "js_of_ocaml-toplevel-example"; doc = "Load the given javascript file" };
|
||||||
|
Sys.interactive := true;
|
||||||
|
()
|
||||||
|
|
||||||
|
let resize ~container ~textbox () =
|
||||||
|
Lwt.pause ()
|
||||||
|
>>= fun () ->
|
||||||
|
textbox##.style##.height := Js.string "auto";
|
||||||
|
textbox##.style##.height
|
||||||
|
:= Js.string (Printf.sprintf "%dpx" (max 18 textbox##.scrollHeight));
|
||||||
|
container##.scrollTop := container##.scrollHeight;
|
||||||
|
Lwt.return ()
|
||||||
|
|
||||||
|
let setup_printers () =
|
||||||
|
exec'
|
||||||
|
"let _print_error fmt e = Format.pp_print_string fmt (Js_of_ocaml.Js_error.to_string \
|
||||||
|
e)";
|
||||||
|
Topdirs.dir_install_printer Format.std_formatter Longident.(Lident "_print_error");
|
||||||
|
exec' "let _print_unit fmt (_ : 'a) : 'a = Format.pp_print_string fmt \"()\"";
|
||||||
|
Topdirs.dir_install_printer Format.std_formatter Longident.(Lident "_print_unit")
|
||||||
|
|
||||||
|
let setup_examples ~container ~textbox =
|
||||||
|
let r = Regexp.regexp "^\\(\\*+(.*)\\*+\\)$" in
|
||||||
|
let all = ref [] in
|
||||||
|
(try
|
||||||
|
let ic = open_in "/static/examples.ml" in
|
||||||
|
while true do
|
||||||
|
let line = input_line ic in
|
||||||
|
match Regexp.string_match r line 0 with
|
||||||
|
| Some res ->
|
||||||
|
let name =
|
||||||
|
match Regexp.matched_group res 1 with
|
||||||
|
| Some s -> s
|
||||||
|
| None -> assert false
|
||||||
|
in
|
||||||
|
all := `Title name :: !all
|
||||||
|
| None -> all := `Content line :: !all
|
||||||
|
done;
|
||||||
|
assert false
|
||||||
|
with _ -> ());
|
||||||
|
let example_container = by_id "toplevel-examples" in
|
||||||
|
let _ =
|
||||||
|
List.fold_left
|
||||||
|
(fun acc tok ->
|
||||||
|
match tok with
|
||||||
|
| `Content line -> line ^ "\n" ^ acc
|
||||||
|
| `Title name ->
|
||||||
|
let a =
|
||||||
|
Tyxml_js.Html.(
|
||||||
|
a
|
||||||
|
~a:
|
||||||
|
[ a_class [ "list-group-item" ]
|
||||||
|
; a_onclick (fun _ ->
|
||||||
|
textbox##.value := (Js.string acc)##trim;
|
||||||
|
Lwt.async (fun () ->
|
||||||
|
resize ~container ~textbox ()
|
||||||
|
>>= fun () ->
|
||||||
|
textbox##focus;
|
||||||
|
Lwt.return_unit);
|
||||||
|
true)
|
||||||
|
]
|
||||||
|
[ txt name ])
|
||||||
|
in
|
||||||
|
Dom.appendChild example_container (Tyxml_js.To_dom.of_a a);
|
||||||
|
"")
|
||||||
|
""
|
||||||
|
!all
|
||||||
|
in
|
||||||
|
()
|
||||||
|
|
||||||
|
(* we need to compute the hash form href to avoid different encoding behavior
|
||||||
|
across browser. see Url.get_fragment *)
|
||||||
|
let parse_hash () =
|
||||||
|
let frag = Url.Current.get_fragment () in
|
||||||
|
Url.decode_arguments frag
|
||||||
|
|
||||||
|
let rec iter_on_sharp ~f x =
|
||||||
|
Js.Opt.iter (Dom_html.CoerceTo.element x) (fun e ->
|
||||||
|
if Js.to_bool (e##.classList##contains (Js.string "sharp")) then f e);
|
||||||
|
match Js.Opt.to_option x##.nextSibling with
|
||||||
|
| None -> ()
|
||||||
|
| Some n -> iter_on_sharp ~f n
|
||||||
|
|
||||||
|
let setup_share_button ~output =
|
||||||
|
do_by_id "btn-share" (fun e ->
|
||||||
|
e##.style##.display := Js.string "block";
|
||||||
|
e##.onclick :=
|
||||||
|
Dom_html.handler (fun _ ->
|
||||||
|
(* get all ocaml code *)
|
||||||
|
let code = ref [] in
|
||||||
|
Js.Opt.iter
|
||||||
|
output##.firstChild
|
||||||
|
(iter_on_sharp ~f:(fun e ->
|
||||||
|
code :=
|
||||||
|
Js.Opt.case e##.textContent (fun () -> "") Js.to_string :: !code));
|
||||||
|
let code_encoded = B64.encode (String.concat "" (List.rev !code)) in
|
||||||
|
let url, is_file =
|
||||||
|
match Url.Current.get () with
|
||||||
|
| Some (Url.Http url) -> Url.Http { url with Url.hu_fragment = "" }, false
|
||||||
|
| Some (Url.Https url) -> Url.Https { url with Url.hu_fragment = "" }, false
|
||||||
|
| Some (Url.File url) -> Url.File { url with Url.fu_fragment = "" }, true
|
||||||
|
| _ -> assert false
|
||||||
|
in
|
||||||
|
let frag =
|
||||||
|
let frags = parse_hash () in
|
||||||
|
let frags = List.remove_assoc "code" frags @ [ "code", code_encoded ] in
|
||||||
|
Url.encode_arguments frags
|
||||||
|
in
|
||||||
|
let uri = Url.string_of_url url ^ "#" ^ frag in
|
||||||
|
let append_url str =
|
||||||
|
let dom =
|
||||||
|
Tyxml_js.Html.(
|
||||||
|
p [ txt "Share this url : "; a ~a:[ a_href str ] [ txt str ] ])
|
||||||
|
in
|
||||||
|
Dom.appendChild output (Tyxml_js.To_dom.of_element dom)
|
||||||
|
in
|
||||||
|
Lwt.async (fun () ->
|
||||||
|
Lwt.catch
|
||||||
|
(fun () ->
|
||||||
|
if is_file
|
||||||
|
then failwith "Cannot shorten url with file scheme"
|
||||||
|
else
|
||||||
|
let uri =
|
||||||
|
Printf.sprintf
|
||||||
|
"http://is.gd/create.php?format=json&url=%s"
|
||||||
|
(Url.urlencode uri)
|
||||||
|
in
|
||||||
|
Lwt.bind (Js_of_ocaml_lwt.Jsonp.call uri) (fun o ->
|
||||||
|
let str = Js.to_string o##.shorturl in
|
||||||
|
append_url str;
|
||||||
|
Lwt.return_unit))
|
||||||
|
(fun exn ->
|
||||||
|
Format.eprintf
|
||||||
|
"Could not generate short url. reason: %s@."
|
||||||
|
(Printexc.to_string exn);
|
||||||
|
append_url uri;
|
||||||
|
Lwt.return_unit));
|
||||||
|
Js._false))
|
||||||
|
|
||||||
|
let setup_js_preview () =
|
||||||
|
let ph = by_id "last-js" in
|
||||||
|
let runcode : string -> 'a = Js.Unsafe.global##.toplevelEval in
|
||||||
|
Js.Unsafe.global##.toplevelEval
|
||||||
|
:= fun bc ->
|
||||||
|
ph##.innerHTML := Js.string bc;
|
||||||
|
runcode bc
|
||||||
|
|
||||||
|
let current_position = ref 0
|
||||||
|
|
||||||
|
let highlight_location loc =
|
||||||
|
let x = ref 0 in
|
||||||
|
let output = by_id "output" in
|
||||||
|
let first =
|
||||||
|
Js.Opt.get (output##.childNodes##item !current_position) (fun _ -> assert false)
|
||||||
|
in
|
||||||
|
iter_on_sharp first ~f:(fun e ->
|
||||||
|
incr x;
|
||||||
|
let _file1, line1, col1 = Location.get_pos_info loc.Location.loc_start in
|
||||||
|
let _file2, line2, col2 = Location.get_pos_info loc.Location.loc_end in
|
||||||
|
if !x >= line1 && !x <= line2
|
||||||
|
then
|
||||||
|
let from_ = if !x = line1 then `Pos col1 else `Pos 0 in
|
||||||
|
let to_ = if !x = line2 then `Pos col2 else `Last in
|
||||||
|
Colorize.highlight from_ to_ e)
|
||||||
|
|
||||||
|
let append colorize output cl s =
|
||||||
|
Dom.appendChild output (Tyxml_js.To_dom.of_element (colorize ~a_class:cl s))
|
||||||
|
|
||||||
|
module History = struct
|
||||||
|
let data = ref [| "" |]
|
||||||
|
|
||||||
|
let idx = ref 0
|
||||||
|
|
||||||
|
let get_storage () =
|
||||||
|
match Js.Optdef.to_option Dom_html.window##.localStorage with
|
||||||
|
| exception _ -> raise Not_found
|
||||||
|
| None -> raise Not_found
|
||||||
|
| Some t -> t
|
||||||
|
|
||||||
|
let setup () =
|
||||||
|
try
|
||||||
|
let s = get_storage () in
|
||||||
|
match Js.Opt.to_option (s##getItem (Js.string "history")) with
|
||||||
|
| None -> raise Not_found
|
||||||
|
| Some s ->
|
||||||
|
let a = Json.unsafe_input s in
|
||||||
|
data := a;
|
||||||
|
idx := Array.length a - 1
|
||||||
|
with _ -> ()
|
||||||
|
|
||||||
|
let push text =
|
||||||
|
let l = Array.length !data in
|
||||||
|
let n = Array.make (l + 1) "" in
|
||||||
|
!data.(l - 1) <- text;
|
||||||
|
Array.blit !data 0 n 0 l;
|
||||||
|
data := n;
|
||||||
|
idx := l;
|
||||||
|
try
|
||||||
|
let s = get_storage () in
|
||||||
|
let str = Json.output !data in
|
||||||
|
s##setItem (Js.string "history") str
|
||||||
|
with Not_found -> ()
|
||||||
|
|
||||||
|
let current text = !data.(!idx) <- text
|
||||||
|
|
||||||
|
let previous textbox =
|
||||||
|
if !idx > 0
|
||||||
|
then (
|
||||||
|
decr idx;
|
||||||
|
textbox##.value := Js.string !data.(!idx))
|
||||||
|
|
||||||
|
let next textbox =
|
||||||
|
if !idx < Array.length !data - 1
|
||||||
|
then (
|
||||||
|
incr idx;
|
||||||
|
textbox##.value := Js.string !data.(!idx))
|
||||||
|
end
|
||||||
|
|
||||||
|
let run _ =
|
||||||
|
let container = by_id "toplevel-container" in
|
||||||
|
let output = by_id "output" in
|
||||||
|
let textbox : 'a Js.t = by_id_coerce "userinput" Dom_html.CoerceTo.textarea in
|
||||||
|
let sharp_chan = open_out "/dev/null0" in
|
||||||
|
let sharp_ppf = Format.formatter_of_out_channel sharp_chan in
|
||||||
|
let caml_chan = open_out "/dev/null1" in
|
||||||
|
let caml_ppf = Format.formatter_of_out_channel caml_chan in
|
||||||
|
let execute () =
|
||||||
|
let content = Js.to_string textbox##.value##trim in
|
||||||
|
let content' =
|
||||||
|
let len = String.length content in
|
||||||
|
if try content <> "" && content.[len - 1] <> ';' && content.[len - 2] <> ';'
|
||||||
|
with _ -> true
|
||||||
|
then content ^ ";;"
|
||||||
|
else content
|
||||||
|
in
|
||||||
|
current_position := output##.childNodes##.length;
|
||||||
|
textbox##.value := Js.string "";
|
||||||
|
History.push content;
|
||||||
|
JsooTop.execute true ~pp_code:sharp_ppf ~highlight_location caml_ppf content';
|
||||||
|
resize ~container ~textbox ()
|
||||||
|
>>= fun () ->
|
||||||
|
container##.scrollTop := container##.scrollHeight;
|
||||||
|
textbox##focus;
|
||||||
|
Lwt.return_unit
|
||||||
|
in
|
||||||
|
let history_down _e =
|
||||||
|
let txt = Js.to_string textbox##.value in
|
||||||
|
let pos = textbox##.selectionStart in
|
||||||
|
try
|
||||||
|
if String.length txt = pos then raise Not_found;
|
||||||
|
let _ = String.index_from txt pos '\n' in
|
||||||
|
Js._true
|
||||||
|
with Not_found ->
|
||||||
|
History.current txt;
|
||||||
|
History.next textbox;
|
||||||
|
Js._false
|
||||||
|
in
|
||||||
|
let history_up _e =
|
||||||
|
let txt = Js.to_string textbox##.value in
|
||||||
|
let pos = textbox##.selectionStart - 1 in
|
||||||
|
try
|
||||||
|
if pos < 0 then raise Not_found;
|
||||||
|
let _ = String.rindex_from txt pos '\n' in
|
||||||
|
Js._true
|
||||||
|
with Not_found ->
|
||||||
|
History.current txt;
|
||||||
|
History.previous textbox;
|
||||||
|
Js._false
|
||||||
|
in
|
||||||
|
let meta e =
|
||||||
|
let b = Js.to_bool in
|
||||||
|
b e##.ctrlKey || b e##.altKey || b e##.metaKey
|
||||||
|
in
|
||||||
|
let shift e = Js.to_bool e##.shiftKey in
|
||||||
|
(* setup handlers *)
|
||||||
|
textbox##.onkeyup :=
|
||||||
|
Dom_html.handler (fun _ ->
|
||||||
|
Lwt.async (resize ~container ~textbox);
|
||||||
|
Js._true);
|
||||||
|
textbox##.onchange :=
|
||||||
|
Dom_html.handler (fun _ ->
|
||||||
|
Lwt.async (resize ~container ~textbox);
|
||||||
|
Js._true);
|
||||||
|
textbox##.onkeydown :=
|
||||||
|
Dom_html.handler (fun e ->
|
||||||
|
match e##.keyCode with
|
||||||
|
| 13 when not (meta e || shift e) ->
|
||||||
|
Lwt.async execute;
|
||||||
|
Js._false
|
||||||
|
| 13 ->
|
||||||
|
Lwt.async (resize ~container ~textbox);
|
||||||
|
Js._true
|
||||||
|
| 09 ->
|
||||||
|
Indent.textarea textbox;
|
||||||
|
Js._false
|
||||||
|
| 76 when meta e ->
|
||||||
|
output##.innerHTML := Js.string "";
|
||||||
|
Js._true
|
||||||
|
| 75 when meta e ->
|
||||||
|
setup_toplevel ();
|
||||||
|
Js._false
|
||||||
|
| 38 -> history_up e
|
||||||
|
| 40 -> history_down e
|
||||||
|
| _ -> Js._true);
|
||||||
|
(Lwt.async_exception_hook :=
|
||||||
|
fun exc ->
|
||||||
|
Format.eprintf "exc during Lwt.async: %s@." (Printexc.to_string exc);
|
||||||
|
match exc with
|
||||||
|
| Js_error.Exn e ->
|
||||||
|
let e = Js_error.to_error e in
|
||||||
|
Firebug.console##log e##.stack
|
||||||
|
| _ -> ());
|
||||||
|
Lwt.async (fun () ->
|
||||||
|
resize ~container ~textbox ()
|
||||||
|
>>= fun () ->
|
||||||
|
textbox##focus;
|
||||||
|
Lwt.return_unit);
|
||||||
|
Graphics_support.init (by_id_coerce "test-canvas" Dom_html.CoerceTo.canvas);
|
||||||
|
Sys_js.set_channel_flusher caml_chan (append Colorize.ocaml output "caml");
|
||||||
|
Sys_js.set_channel_flusher sharp_chan (append Colorize.ocaml output "sharp");
|
||||||
|
Sys_js.set_channel_flusher stdout (append Colorize.text output "stdout");
|
||||||
|
Sys_js.set_channel_flusher stderr (append Colorize.text output "stderr");
|
||||||
|
let readline () =
|
||||||
|
Js.Opt.case
|
||||||
|
(Dom_html.window##prompt (Js.string "The toplevel expects inputs:") (Js.string ""))
|
||||||
|
(fun () -> "")
|
||||||
|
(fun s -> Js.to_string s ^ "\n")
|
||||||
|
in
|
||||||
|
Sys_js.set_channel_filler stdin readline;
|
||||||
|
setup_share_button ~output;
|
||||||
|
setup_examples ~container ~textbox;
|
||||||
|
setup_pseudo_fs ~load_cmis_from_server:false;
|
||||||
|
setup_toplevel ();
|
||||||
|
setup_js_preview ();
|
||||||
|
setup_printers ();
|
||||||
|
History.setup ();
|
||||||
|
textbox##.value := Js.string "";
|
||||||
|
(* Run initial code if any *)
|
||||||
|
try
|
||||||
|
let code = List.assoc "code" (parse_hash ()) in
|
||||||
|
textbox##.value := Js.string (B64.decode code);
|
||||||
|
Lwt.async execute
|
||||||
|
with
|
||||||
|
| Not_found -> ()
|
||||||
|
| exc ->
|
||||||
|
Firebug.console##log_3
|
||||||
|
(Js.string "exception")
|
||||||
|
(Js.string (Printexc.to_string exc))
|
||||||
|
exc
|
||||||
|
|
||||||
|
let _ =
|
||||||
|
Dom_html.window##.onload :=
|
||||||
|
Dom_html.handler (fun _ ->
|
||||||
|
run ();
|
||||||
|
Js._false)
|
||||||
Reference in New Issue
Block a user