From 6518dbd81644e864214a947643eacd092b50db20 Mon Sep 17 00:00:00 2001 From: Etienne MARAIS Date: Wed, 27 May 2020 16:07:58 +0200 Subject: [PATCH 01/20] Embedded Graphical code Allow to use Vg library in Toplevel and Exercices --- learn-ocaml.opam | 1 + learn-ocaml.opam.locked | 1 + src/grader/dune | 9 +++++++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/learn-ocaml.opam b/learn-ocaml.opam index a2f1dc610..b790c7665 100644 --- a/learn-ocaml.opam +++ b/learn-ocaml.opam @@ -50,6 +50,7 @@ depends: [ "ppx_cstruct" "ppx_tools" "uutf" {>= "1.0" } + "vg" "yojson" {>= "1.4.0" } "asak" {>= "0.1"} ] diff --git a/learn-ocaml.opam.locked b/learn-ocaml.opam.locked index eabfb195c..8676fe078 100644 --- a/learn-ocaml.opam.locked +++ b/learn-ocaml.opam.locked @@ -111,6 +111,7 @@ depends: [ "uri" {= "1.9.7"} "uutf" {= "1.0.2"} "yojson" {= "1.7.0"} + "vg" {= "0.9.3"} ] build: [ [make "static"] diff --git a/src/grader/dune b/src/grader/dune index 9096f8eaf..d7094c58a 100644 --- a/src/grader/dune +++ b/src/grader/dune @@ -46,6 +46,8 @@ (run odoc html %{dep:test_lib.odoc} -o %{workspace_root}/_doc/_html))) ) + + (rule (targets embedded_cmis.ml) (deps %{ocaml-config:standard_library}/array.cmi @@ -83,7 +85,10 @@ %{ocaml-config:standard_library}/string.cmi %{ocaml-config:standard_library}/sys.cmi %{ocaml-config:standard_library}/uchar.cmi - %{ocaml-config:standard_library}/weak.cmi) + %{ocaml-config:standard_library}/weak.cmi + %{lib:gg:gg.cmi} + %{lib:vg:vg.cmi} + %{lib:vg:vgr_svg.cmi}) (action (with-stdout-to %{targets} (run ocp-ocamlres -format ocamlres %{deps}))) ) @@ -92,7 +97,7 @@ (wrapped false) (modes byte) (modules Embedded_cmis) - (libraries ocplib-ocamlres.runtime bigarray) + (libraries ocplib-ocamlres.runtime bigarray vg gg vg.svg) ) (rule From c04713f636f2e0449d8cd0ed5ee329bdc319ab53 Mon Sep 17 00:00:00 2001 From: Etienne MARAIS Date: Mon, 1 Jun 2020 11:27:17 +0200 Subject: [PATCH 02/20] Add function to print_svg --- src/grader/dune | 4 +++- src/toplevel/learnocaml_toplevel_output.ml | 12 ++++++++++++ src/toplevel/learnocaml_toplevel_output.mli | 2 ++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/grader/dune b/src/grader/dune index d7094c58a..5f71e6a88 100644 --- a/src/grader/dune +++ b/src/grader/dune @@ -86,6 +86,8 @@ %{ocaml-config:standard_library}/sys.cmi %{ocaml-config:standard_library}/uchar.cmi %{ocaml-config:standard_library}/weak.cmi + + %{lib:stringext:stringext.cmi} %{lib:gg:gg.cmi} %{lib:vg:vg.cmi} %{lib:vg:vgr_svg.cmi}) @@ -97,7 +99,7 @@ (wrapped false) (modes byte) (modules Embedded_cmis) - (libraries ocplib-ocamlres.runtime bigarray vg gg vg.svg) + (libraries ocplib-ocamlres.runtime bigarray str vg gg vg.svg stringext) ) (rule diff --git a/src/toplevel/learnocaml_toplevel_output.ml b/src/toplevel/learnocaml_toplevel_output.ml index fa10aaf24..9e39a6468 100644 --- a/src/toplevel/learnocaml_toplevel_output.ml +++ b/src/toplevel/learnocaml_toplevel_output.ml @@ -180,6 +180,18 @@ let output_html ?phrase output html = Js_utils.Manip.appendChild output.container div ; insert output ?phrase (Html (html, div)) div + + + +let output_svg ?phrase output svg = + let svg = + let pattern = "l:href" in + let with_ = "href" in + Stringext.replace_all svg ~pattern ~with_ + in + output_html ?phrase output svg + + let output_code ?phrase output code = let snapshot = let blocks = match phrase with diff --git a/src/toplevel/learnocaml_toplevel_output.mli b/src/toplevel/learnocaml_toplevel_output.mli index 4985a8cde..e30d7bff7 100644 --- a/src/toplevel/learnocaml_toplevel_output.mli +++ b/src/toplevel/learnocaml_toplevel_output.mli @@ -77,6 +77,8 @@ val output_stderr : ?phrase: phrase -> output -> string -> unit (** Output HTML in a [div] element with class [toplevel-html-block]. *) val output_html : ?phrase: phrase -> output -> string -> unit +val output_svg : ?phrase: phrase -> output -> string -> unit + (** Output ocaml code in a [pre] element with class [toplevel-code]. Code tokens are wrapped in [span] elements with classes as documented in {!Ocaml_mode.token_type}. An intermediate level of From 05d7952d20b1cd63ee4019b7efd14caed3e06f70 Mon Sep 17 00:00:00 2001 From: Etienne MARAIS Date: Mon, 1 Jun 2020 15:53:27 +0200 Subject: [PATCH 03/20] Load pretty_printer for Vg.image --- src/grader/dune | 4 ++- src/toplevel/learnocaml_toplevel.ml | 43 +++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/grader/dune b/src/grader/dune index 5f71e6a88..96ff109f8 100644 --- a/src/grader/dune +++ b/src/grader/dune @@ -99,7 +99,9 @@ (wrapped false) (modes byte) (modules Embedded_cmis) - (libraries ocplib-ocamlres.runtime bigarray str vg gg vg.svg stringext) + (libraries ocplib-ocamlres.runtime bigarray + vg gg vg.svg + stringext) ) (rule diff --git a/src/toplevel/learnocaml_toplevel.ml b/src/toplevel/learnocaml_toplevel.ml index 79b7f525f..eed3601fd 100644 --- a/src/toplevel/learnocaml_toplevel.ml +++ b/src/toplevel/learnocaml_toplevel.ml @@ -380,6 +380,22 @@ let wrap_flusher_to_prevent_flood top name hook real = flooded := total end +let load_pp err top pps = + let build_pp name ty code = + Format.sprintf + "let %s fmt (obj : %s) = %s;; #install_printer %s" name ty code name + in + let rec loading err = function + | [] -> err + | (pp_name, pp_type, pp_code) :: pps -> + err + >>= + (fun _ -> + load ~print_outcome:false top (build_pp pp_name pp_type pp_code)) + >>= (fun _ -> loading err pps) + in + err >>= (fun _ -> loading err pps) + let welcome_phrase () = [%i"Printf.printf \"Welcome to OCaml %s\\n%!\" (Sys.ocaml_version);\n\ print_endline \" - type your OCaml phrase in the box below and press [Enter]\";\n\ @@ -389,6 +405,23 @@ let welcome_phrase () = to navigate through history\" ;;"] (* U+2191 upwards arrow, U+2193 downwards arrow*) + +(* List of pretty printer with name, type and code to execute. + Produce a name fmt obj function. *) +let pretty_printers = [ +"print_image", +"Vg.image", +" +let coeff = 1.0 in +let b = Buffer.create 2048 in +let size = Gg.Size2.v (coeff *. 100.) (coeff *. 100.) in +let view = Gg.Box2.v Gg.P2.o (Gg.Size2.v coeff coeff) in +let r = Vg.Vgr.create (Vgr_svg.target ()) (`Buffer b) in +ignore (Vg.Vgr.render r (`Image (size, view, obj))); +ignore (Vg.Vgr.render r `End); + print_svg (Buffer.contents b)"; +] + let create ?worker_js_file ?(timeout_delay = 5.) @@ -495,10 +528,16 @@ let create else first_time := false ; Learnocaml_toplevel_worker_caller.register_callback worker "print_html" - (Learnocaml_toplevel_output.output_html output) >>= fun _ -> + (Learnocaml_toplevel_output.output_html output) + >>= fun _ -> + Learnocaml_toplevel_worker_caller.register_callback worker "print_svg" + (Learnocaml_toplevel_output.output_svg output) + >>= fun err -> load_pp (Lwt.return err) top pretty_printers + >>= fun _ -> match after_init with | None -> Lwt.return_unit - | Some f -> f top in + | Some f -> f top + in after_init top >>= fun () -> Learnocaml_toplevel_worker_caller.set_after_init top.worker (fun _ -> after_init top); Lwt.return top From 7f62d7a3ad503a4f0a14988008ee2bce85cac67d Mon Sep 17 00:00:00 2001 From: Etienne MARAIS Date: Tue, 2 Jun 2020 16:18:54 +0200 Subject: [PATCH 04/20] Update svg formatter with re Allow print_svg to display svg file in the toplevel. Use Re to update the svg. --- learn-ocaml.opam | 1 + src/grader/dune | 4 +-- src/toplevel/learnocaml_toplevel_output.ml | 42 ++++++++++++++++++---- 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/learn-ocaml.opam b/learn-ocaml.opam index b790c7665..cb758e86e 100644 --- a/learn-ocaml.opam +++ b/learn-ocaml.opam @@ -49,6 +49,7 @@ depends: [ "pprint" "ppx_cstruct" "ppx_tools" + "re" "uutf" {>= "1.0" } "vg" "yojson" {>= "1.4.0" } diff --git a/src/grader/dune b/src/grader/dune index 96ff109f8..972c55a3a 100644 --- a/src/grader/dune +++ b/src/grader/dune @@ -87,7 +87,7 @@ %{ocaml-config:standard_library}/uchar.cmi %{ocaml-config:standard_library}/weak.cmi - %{lib:stringext:stringext.cmi} + %{lib:re:re.cmi} %{lib:gg:gg.cmi} %{lib:vg:vg.cmi} %{lib:vg:vgr_svg.cmi}) @@ -101,7 +101,7 @@ (modules Embedded_cmis) (libraries ocplib-ocamlres.runtime bigarray vg gg vg.svg - stringext) + re) ) (rule diff --git a/src/toplevel/learnocaml_toplevel_output.ml b/src/toplevel/learnocaml_toplevel_output.ml index 9e39a6468..6aeff821e 100644 --- a/src/toplevel/learnocaml_toplevel_output.ml +++ b/src/toplevel/learnocaml_toplevel_output.ml @@ -181,17 +181,45 @@ let output_html ?phrase output html = insert output ?phrase (Html (html, div)) div - +(* Module to generate new id *) +module Id_generator : sig + val get_fresh_id : unit -> int + val reset_ids : unit -> unit +end = +struct + let id = ref 0 + + let get_fresh_id () = + let idx = !id in + id := !id + 1; + idx + + let reset_ids () = + id := 0 +end + +let replace_markup idx markup svg = + let open Re in + let f g = Format.sprintf " %s=\"%s-%d\"" markup (Group.get g 1) idx in + let regexp = Format.sprintf "[ ]+%s=\"([#A-Za-z0-9]+)\"" markup in + let regexp = Posix.compile_pat regexp in + replace ~f regexp svg + +let replace_link svg = + let open Re in + let regexp = Posix.compile_pat "l:href" in + replace_string regexp ~by:"href" svg + +let rewrite_svg svg = + let idx = Id_generator.get_fresh_id () in + replace_markup idx "id" svg + |> replace_markup idx "l:href" + |> replace_link let output_svg ?phrase output svg = - let svg = - let pattern = "l:href" in - let with_ = "href" in - Stringext.replace_all svg ~pattern ~with_ - in + let svg = rewrite_svg svg in output_html ?phrase output svg - let output_code ?phrase output code = let snapshot = let blocks = match phrase with From 2d463d59d3bc4992da5b979b17e26d1ddcf08288 Mon Sep 17 00:00:00 2001 From: Etienne MARAIS Date: Tue, 2 Jun 2020 16:34:41 +0200 Subject: [PATCH 05/20] Clear output --- src/toplevel/learnocaml_toplevel_output.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/toplevel/learnocaml_toplevel_output.ml b/src/toplevel/learnocaml_toplevel_output.ml index 6aeff821e..a411acc4e 100644 --- a/src/toplevel/learnocaml_toplevel_output.ml +++ b/src/toplevel/learnocaml_toplevel_output.ml @@ -362,6 +362,7 @@ let output_warning ?phrase output warning = insert output ~phrase (Warning (phrase.warnings, warning, pre)) pre let clear output = + Id_generator.reset_ids (); Js_utils.Manip.removeChildren output.container ; output.blocks <- [] From b1d66f679e0c0ff3ddc49bdea7555ca000f86a79 Mon Sep 17 00:00:00 2001 From: Maiste Date: Wed, 3 Jun 2020 14:45:25 +0200 Subject: [PATCH 06/20] Correct regular expression MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Yann RĂ©gis Gianas --- src/toplevel/learnocaml_toplevel_output.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/toplevel/learnocaml_toplevel_output.ml b/src/toplevel/learnocaml_toplevel_output.ml index a411acc4e..11a7c29d7 100644 --- a/src/toplevel/learnocaml_toplevel_output.ml +++ b/src/toplevel/learnocaml_toplevel_output.ml @@ -201,7 +201,7 @@ end let replace_markup idx markup svg = let open Re in let f g = Format.sprintf " %s=\"%s-%d\"" markup (Group.get g 1) idx in - let regexp = Format.sprintf "[ ]+%s=\"([#A-Za-z0-9]+)\"" markup in + let regexp = Format.sprintf "[ ]+%s=\"(#?[A-Za-z0-9]+)\"" markup in let regexp = Posix.compile_pat regexp in replace ~f regexp svg From e82ee38b5bca075fef5c0f591545355b9efae8c2 Mon Sep 17 00:00:00 2001 From: Etienne MARAIS Date: Thu, 4 Jun 2020 15:10:35 +0200 Subject: [PATCH 07/20] Move to unique pretty printer file --- src/toplevel/dune | 11 ++++++++++ src/toplevel/learnocaml_toplevel_pp.ml | 28 ++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 src/toplevel/learnocaml_toplevel_pp.ml diff --git a/src/toplevel/dune b/src/toplevel/dune index f13b126ac..67b1005a6 100644 --- a/src/toplevel/dune +++ b/src/toplevel/dune @@ -34,6 +34,14 @@ (modules Learnocaml_toplevel_history) ) +(library + (name learnocaml_toplevel_pp) + (wrapped false) + (modes byte) + (libraries vg gg vg.svg) + (modules Learnocaml_toplevel_pp) +) + (library (name learnocaml_toplevel) (wrapped false) @@ -48,6 +56,7 @@ ocplib-json-typed learnocaml_toplevel_history learnocaml_toplevel_worker_messages + learnocaml_toplevel_pp ocplib_i18n) (modules Learnocaml_toplevel_worker_caller Learnocaml_toplevel_output @@ -56,6 +65,8 @@ (preprocess (pps ppx_ocplib_i18n js_of_ocaml.ppx)) ) + + (install (package learn-ocaml) (section share) diff --git a/src/toplevel/learnocaml_toplevel_pp.ml b/src/toplevel/learnocaml_toplevel_pp.ml new file mode 100644 index 000000000..7b32857e3 --- /dev/null +++ b/src/toplevel/learnocaml_toplevel_pp.ml @@ -0,0 +1,28 @@ +(* This file is part of Learn-OCaml. + * + * Copyright (C) 2019 OCaml Software Foundation. + * Copyright (C) 2016-2018 OCamlPro. + * + * Learn-OCaml is distributed under the terms of the MIT license. See the + * included LICENSE file for details. *) + +(* Code to build an image *) +let construct_image i = + let coeff = 1.0 in + let b = Buffer.create 2048 in + let size = Gg.Size2.v (coeff *. 100.) (coeff *. 100.) in + let view = Gg.Box2.v Gg.P2.o (Gg.Size2.v coeff coeff) in + let r = Vg.Vgr.create (Vgr_svg.target ()) (`Buffer b) in + ignore (Vg.Vgr.render r (`Image (size, view, i))); + ignore (Vg.Vgr.render r `End); + Buffer.contents b + +(* Prelude for pretty printer *) +let prelude_pp = " +let pp_svg _ i = construct_image i |> print_svg;;" + + +(* List of pretty printer to deploy in toplevel *) +let pp_list = [ + "pp_svg"; +] From f91cbb09a194a96033f74ba732df77d5c2e0a020 Mon Sep 17 00:00:00 2001 From: Etienne MARAIS Date: Thu, 4 Jun 2020 15:11:03 +0200 Subject: [PATCH 08/20] Include local libraries in embedded dune --- src/grader/dune | 94 +++++++++++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 42 deletions(-) diff --git a/src/grader/dune b/src/grader/dune index 972c55a3a..a9e662866 100644 --- a/src/grader/dune +++ b/src/grader/dune @@ -50,48 +50,57 @@ (rule (targets embedded_cmis.ml) - (deps %{ocaml-config:standard_library}/array.cmi - %{ocaml-config:standard_library}/arrayLabels.cmi - %{ocaml-config:standard_library}/buffer.cmi - %{ocaml-config:standard_library}/bytes.cmi - %{ocaml-config:standard_library}/bigarray.cmi - %{ocaml-config:standard_library}/camlinternalFormatBasics.cmi - %{ocaml-config:standard_library}/camlinternalFormat.cmi - %{ocaml-config:standard_library}/camlinternalLazy.cmi - %{ocaml-config:standard_library}/camlinternalMod.cmi - %{ocaml-config:standard_library}/camlinternalOO.cmi - %{ocaml-config:standard_library}/compiler-libs/topdirs.cmi - %{ocaml-config:standard_library}/char.cmi - %{ocaml-config:standard_library}/complex.cmi - %{ocaml-config:standard_library}/digest.cmi - %{ocaml-config:standard_library}/filename.cmi - %{ocaml-config:standard_library}/format.cmi - %{ocaml-config:standard_library}/hashtbl.cmi - %{ocaml-config:standard_library}/int32.cmi - %{ocaml-config:standard_library}/int64.cmi - %{ocaml-config:standard_library}/lazy.cmi - %{ocaml-config:standard_library}/lexing.cmi - %{ocaml-config:standard_library}/list.cmi - %{ocaml-config:standard_library}/map.cmi - %{ocaml-config:standard_library}/marshal.cmi - %{ocaml-config:standard_library}/pervasives.cmi - %{ocaml-config:standard_library}/printexc.cmi - %{ocaml-config:standard_library}/printf.cmi - %{ocaml-config:standard_library}/queue.cmi - %{ocaml-config:standard_library}/random.cmi - %{ocaml-config:standard_library}/scanf.cmi - %{ocaml-config:standard_library}/set.cmi - %{ocaml-config:standard_library}/stack.cmi - %{ocaml-config:standard_library}/string.cmi - %{ocaml-config:standard_library}/sys.cmi - %{ocaml-config:standard_library}/uchar.cmi - %{ocaml-config:standard_library}/weak.cmi - - %{lib:re:re.cmi} - %{lib:gg:gg.cmi} - %{lib:vg:vg.cmi} - %{lib:vg:vgr_svg.cmi}) - (action (with-stdout-to %{targets} (run ocp-ocamlres -format ocamlres %{deps}))) + (deps + (:stdlib_cmis + %{ocaml-config:standard_library}/array.cmi + %{ocaml-config:standard_library}/arrayLabels.cmi + %{ocaml-config:standard_library}/buffer.cmi + %{ocaml-config:standard_library}/bytes.cmi + %{ocaml-config:standard_library}/bigarray.cmi + %{ocaml-config:standard_library}/camlinternalFormatBasics.cmi + %{ocaml-config:standard_library}/camlinternalFormat.cmi + %{ocaml-config:standard_library}/camlinternalLazy.cmi + %{ocaml-config:standard_library}/camlinternalMod.cmi + %{ocaml-config:standard_library}/camlinternalOO.cmi + %{ocaml-config:standard_library}/compiler-libs/topdirs.cmi + %{ocaml-config:standard_library}/char.cmi + %{ocaml-config:standard_library}/complex.cmi + %{ocaml-config:standard_library}/digest.cmi + %{ocaml-config:standard_library}/filename.cmi + %{ocaml-config:standard_library}/format.cmi + %{ocaml-config:standard_library}/hashtbl.cmi + %{ocaml-config:standard_library}/int32.cmi + %{ocaml-config:standard_library}/int64.cmi + %{ocaml-config:standard_library}/lazy.cmi + %{ocaml-config:standard_library}/lexing.cmi + %{ocaml-config:standard_library}/list.cmi + %{ocaml-config:standard_library}/map.cmi + %{ocaml-config:standard_library}/marshal.cmi + %{ocaml-config:standard_library}/pervasives.cmi + %{ocaml-config:standard_library}/printexc.cmi + %{ocaml-config:standard_library}/printf.cmi + %{ocaml-config:standard_library}/queue.cmi + %{ocaml-config:standard_library}/random.cmi + %{ocaml-config:standard_library}/scanf.cmi + %{ocaml-config:standard_library}/set.cmi + %{ocaml-config:standard_library}/stack.cmi + %{ocaml-config:standard_library}/string.cmi + %{ocaml-config:standard_library}/sys.cmi + %{ocaml-config:standard_library}/uchar.cmi + %{ocaml-config:standard_library}/weak.cmi) + + (:local_cmis + ../toplevel/.learnocaml_toplevel_pp.objs/byte/learnocaml_toplevel_pp.cmi) + + (:lib_cmis + %{lib:re:re.cmi} + %{lib:gg:gg.cmi} + %{lib:vg:vg.cmi} + %{lib:vg:vgr_svg.cmi})) + (action + (with-stdout-to %{targets} + (run ocp-ocamlres -format ocamlres %{stdlib_cmis} %{local_cmis} %{lib_cmis})) + ) ) (library @@ -100,6 +109,7 @@ (modes byte) (modules Embedded_cmis) (libraries ocplib-ocamlres.runtime bigarray + learnocaml_toplevel_pp vg gg vg.svg re) ) From 14f7acfea2dbeefb9ff83a4503af768d132d01fa Mon Sep 17 00:00:00 2001 From: Etienne MARAIS Date: Thu, 4 Jun 2020 15:11:26 +0200 Subject: [PATCH 09/20] Include pretty_printers in toplevel --- src/toplevel/learnocaml_toplevel.ml | 40 +++++++++-------------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/src/toplevel/learnocaml_toplevel.ml b/src/toplevel/learnocaml_toplevel.ml index eed3601fd..12e16d7bf 100644 --- a/src/toplevel/learnocaml_toplevel.ml +++ b/src/toplevel/learnocaml_toplevel.ml @@ -381,20 +381,21 @@ let wrap_flusher_to_prevent_flood top name hook real = end let load_pp err top pps = - let build_pp name ty code = - Format.sprintf - "let %s fmt (obj : %s) = %s;; #install_printer %s" name ty code name + let prelude_pp = + Format.sprintf "open Learnocaml_toplevel_pp;; %s" + Learnocaml_toplevel_pp.prelude_pp in let rec loading err = function | [] -> err - | (pp_name, pp_type, pp_code) :: pps -> - err - >>= - (fun _ -> - load ~print_outcome:false top (build_pp pp_name pp_type pp_code)) - >>= (fun _ -> loading err pps) + | pp :: pps -> + let pp = Format.sprintf "#install_printer %s;;" pp in + err + >>= (fun _ -> load ~print_outcome:false top pp) + >>= (fun _ -> loading err pps) in - err >>= (fun _ -> loading err pps) + err + >>= (fun _ -> load ~print_outcome:false top prelude_pp) + >>= (fun _ -> loading err pps) let welcome_phrase () = [%i"Printf.printf \"Welcome to OCaml %s\\n%!\" (Sys.ocaml_version);\n\ @@ -405,23 +406,6 @@ let welcome_phrase () = to navigate through history\" ;;"] (* U+2191 upwards arrow, U+2193 downwards arrow*) - -(* List of pretty printer with name, type and code to execute. - Produce a name fmt obj function. *) -let pretty_printers = [ -"print_image", -"Vg.image", -" -let coeff = 1.0 in -let b = Buffer.create 2048 in -let size = Gg.Size2.v (coeff *. 100.) (coeff *. 100.) in -let view = Gg.Box2.v Gg.P2.o (Gg.Size2.v coeff coeff) in -let r = Vg.Vgr.create (Vgr_svg.target ()) (`Buffer b) in -ignore (Vg.Vgr.render r (`Image (size, view, obj))); -ignore (Vg.Vgr.render r `End); - print_svg (Buffer.contents b)"; -] - let create ?worker_js_file ?(timeout_delay = 5.) @@ -532,7 +516,7 @@ let create >>= fun _ -> Learnocaml_toplevel_worker_caller.register_callback worker "print_svg" (Learnocaml_toplevel_output.output_svg output) - >>= fun err -> load_pp (Lwt.return err) top pretty_printers + >>= fun err -> load_pp (Lwt.return err) top Learnocaml_toplevel_pp.pp_list >>= fun _ -> match after_init with | None -> Lwt.return_unit From 44b4236e31ffeef226c69f3f43b6314698d64a11 Mon Sep 17 00:00:00 2001 From: Etienne MARAIS Date: Fri, 5 Jun 2020 12:07:47 +0200 Subject: [PATCH 10/20] Include link flag --linkall Allow the builder to use different libraries from stdlib in the exercises. --- src/grader/dune | 1 + 1 file changed, 1 insertion(+) diff --git a/src/grader/dune b/src/grader/dune index a9e662866..aae924a91 100644 --- a/src/grader/dune +++ b/src/grader/dune @@ -195,6 +195,7 @@ (name grader_jsoo_worker) (modes byte) (flags :standard -warn-error -9-27) + (link_flags :standard -linkall) (libraries toploop_jsoo grading ezjsonm From e20e9a4602047fe9639b36fdde3c0db809814d53 Mon Sep 17 00:00:00 2001 From: Etienne MARAIS Date: Mon, 27 Jul 2020 12:43:02 +0200 Subject: [PATCH 11/20] Support Dockerfile --- learn-ocaml-client.opam | 2 ++ 1 file changed, 2 insertions(+) diff --git a/learn-ocaml-client.opam b/learn-ocaml-client.opam index f4e06fffc..15c72e796 100644 --- a/learn-ocaml-client.opam +++ b/learn-ocaml-client.opam @@ -19,6 +19,8 @@ depends: [ "cmdliner" "omd" "asak" + "gg" + "vg" "cohttp" {>= "1.0.0" & < "2.0.0"} "cohttp-lwt-unix" {>= "1.0.0" & < "2.0.0"} "ssl" {= "0.5.5"} From 2d1924c9e980deca5bcb00500485fb0953d2b9b1 Mon Sep 17 00:00:00 2001 From: Etienne MARAIS Date: Wed, 22 Jul 2020 16:45:41 +0200 Subject: [PATCH 12/20] Update pp to avoid warning --- src/toplevel/learnocaml_toplevel_pp.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/toplevel/learnocaml_toplevel_pp.ml b/src/toplevel/learnocaml_toplevel_pp.ml index 7b32857e3..d27211378 100644 --- a/src/toplevel/learnocaml_toplevel_pp.ml +++ b/src/toplevel/learnocaml_toplevel_pp.ml @@ -18,8 +18,7 @@ let construct_image i = Buffer.contents b (* Prelude for pretty printer *) -let prelude_pp = " -let pp_svg _ i = construct_image i |> print_svg;;" +let prelude_pp = "let pp_svg _ i = construct_image i |> print_svg;;" (* List of pretty printer to deploy in toplevel *) From d99919ab64a295beb32cebb08ffe71d180a0706d Mon Sep 17 00:00:00 2001 From: Etienne MARAIS Date: Wed, 22 Jul 2020 16:46:20 +0200 Subject: [PATCH 13/20] Update .opam to follow vg.dev --- learn-ocaml.opam | 3 +++ 1 file changed, 3 insertions(+) diff --git a/learn-ocaml.opam b/learn-ocaml.opam index cb758e86e..50ca58a2f 100644 --- a/learn-ocaml.opam +++ b/learn-ocaml.opam @@ -55,6 +55,9 @@ depends: [ "yojson" {>= "1.4.0" } "asak" {>= "0.1"} ] +pin-depends: [ + "vg.dev" "git+https://github.com/maiste/vg#ocaml_rendering" +] build: [ [make "static"] ["dune" "build" "-p" name "-j" jobs] From bee0b1780e3c90ad9c4b2c0001786019905a5d7c Mon Sep 17 00:00:00 2001 From: Etienne MARAIS Date: Wed, 22 Jul 2020 16:50:30 +0200 Subject: [PATCH 14/20] Vg basic tests --- src/grader/dune | 6 ++- src/grader/test_lib.ml | 107 ++++++++++++++++++++++++++++++++++++++++ src/grader/test_lib.mli | 22 +++++++++ 3 files changed, 134 insertions(+), 1 deletion(-) diff --git a/src/grader/dune b/src/grader/dune index aae924a91..0abe9c30a 100644 --- a/src/grader/dune +++ b/src/grader/dune @@ -24,7 +24,11 @@ learnocaml_ppx_metaquot_lib ocplib-json-typed learnocaml_report - learnocaml_repository) + learnocaml_repository + vg + gg + bigarray + vg.bigarray) (modules Introspection_intf Introspection Test_lib diff --git a/src/grader/test_lib.ml b/src/grader/test_lib.ml index c6b8b44df..2a0cb85b6 100644 --- a/src/grader/test_lib.ml +++ b/src/grader/test_lib.ml @@ -145,6 +145,17 @@ module type S = sig end + (*-----------------------------------------------------------------------------*) + + module Test_functions_vg_var : sig + + val test_vg : + int -> int -> Vg.image -> Vg.image -> Learnocaml_report.t + + val test_vg_against_solution: + int -> int -> string -> Learnocaml_report.t + end + (*----------------------------------------------------------------------------*) module Test_functions_types : sig @@ -441,6 +452,7 @@ module type S = sig include (module type of Sampler) include (module type of Test_functions_types) include (module type of Test_functions_ref_var) + include (module type of Test_functions_vg_var) include (module type of Test_functions_function) include (module type of Test_functions_generic) @@ -1408,6 +1420,100 @@ module Make (*----------------------------------------------------------------------------*) + module Test_functions_vg_var = struct + open Gg + open Vg + open Bigarray + open Test_functions_generic + + let render_array w h img = + let size = Size2.v (float_of_int w) (float_of_int h) in + let view = Box2.v P2.o size in + let stride = 24 * w in + let data = + Array1.create int8_unsigned c_layout (stride * h) + in + let target = Vgr_bigarray.target data in + let r = Vgr.create target `Other in + ignore (Vgr.render r (`Image (size, view, img))); + ignore (Vgr.render r `End ); + data + + let compute_diff_array f_dist w h got exp = + let got = render_array w h got in + let exp = render_array w h exp in + let size = Array1.dim exp in + let kind = Array1.kind exp in + let layout = Array1.layout exp in + let output = Array1.create kind layout size in + let rec compute_array k = + if k >= size then output + else + let p_exp = + Array1.get exp k, Array1.get exp (k+1), Array1.get exp (k+2) + in + let p_got = + Array1.get got k, Array1.get got (k+1), Array1.get got (k+2) + in + let r, g, b = f_dist p_exp p_got in + Array1.set output k r; + Array1.set output (k+1) g; + Array1.set output (k+2) b; + compute_array (k+3) + in compute_array 0 + + let compute_dist p_exp p_got = + let r_exp, g_exp, b_exp = p_exp in + let r_got, g_got, b_got = p_got in + let partial_dist c_exp c_got = + (c_exp - c_got |> float_of_int) ** 2.0 + in + let r = partial_dist r_exp r_got in + let g = partial_dist g_exp g_got in + let b = partial_dist b_exp b_got in + let d = sqrt (r +. g +. b) |> int_of_float in + let gray = (d * 255) / (3. *. (255. ** 2.) |> sqrt |> int_of_float) in + gray, gray, gray + + let show a = + let dim = Array1.dim a in + let rec loop acc k = + if k < dim then + let px = a.{k} |> string_of_int in + let acc = acc ^ ", " ^ px in + loop acc (k+1) + else acc + in loop "" 0 + + + let test_vg w h got exp = + let open Learnocaml_report in + let diff = compute_diff_array compute_dist w h got exp in + let size = Array1.dim diff in + let rec check_image k = + if k >= size then + [Message ([Text "Nice answer"], Success 1)] + else + let r, g, b = + Array1.get diff k, Array1.get diff (k+1), Array1.get diff (k+2) + in + if r != 0 || g != 0 || b != 0 then + let txt = show diff in + [Message ([Text "Wrong answer" ; Text txt], Failure)] + else + check_image (k+3) + in check_image 0 + + let test_vg_against_solution w h name = + let ty = [%ty: Vg.image ] in + test_value (lookup_solution ty name) @@ fun sol -> + test_value (lookup_student ty name) @@ fun got -> + test_vg w h got sol + + end + + (*----------------------------------------------------------------------------*) + module Test_functions_function = struct open Test_functions_generic @@ -1805,6 +1911,7 @@ module Make include Sampler include Test_functions_types include Test_functions_ref_var + include Test_functions_vg_var include Test_functions_function include Test_functions_generic diff --git a/src/grader/test_lib.mli b/src/grader/test_lib.mli index 0bede3698..7e66e14b3 100644 --- a/src/grader/test_lib.mli +++ b/src/grader/test_lib.mli @@ -534,6 +534,27 @@ module type S = sig end + + (** {1 Grading functions for vg images} *) + + (** Grading function for vg images. *) + module Test_functions_vg_var : sig + + (** [test_var w h got exp] returns {!LearnOcaml_report.Success 1} + report if vg image [got] is equal to [exp] with [w] and [h] dimensions + and {!LearnOcaml_report.Failure} report otherwise. *) + val test_vg : + int -> int -> Vg.image -> Vg.image -> Learnocaml_report.t + + (** [test_variable_against_solution w h name] returns {!LearnOcaml_report.Success + 1} report if image of dimension [w] and [h] named [name] exists and is equal to + image with the same name defined in solution. Otherwise returns + {!LearnOcaml_report.Failure} report.*) + val test_vg_against_solution: + int -> int -> string -> Learnocaml_report.t + + end + (** {1 Grading functions for types} *) (** Grading function for types. *) @@ -1233,6 +1254,7 @@ module type S = sig include (module type of Sampler) include (module type of Test_functions_types) include (module type of Test_functions_ref_var) + include (module type of Test_functions_vg_var) include (module type of Test_functions_function) include (module type of Test_functions_generic) end From 6f6475e548013a55d20d5d7ade1f278a1b3afe9d Mon Sep 17 00:00:00 2001 From: Etienne MARAIS Date: Sun, 26 Jul 2020 15:37:02 +0200 Subject: [PATCH 15/20] Build learnocaml png web --- src/app/learnocaml_png.ml | 35 +++++++++++++++++++++++++++++++++++ src/server/learnocaml_png.ml | 2 ++ src/utils/dune | 10 +++++++++- src/utils/learnocaml_png.mli | 15 +++++++++++++++ 4 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 src/app/learnocaml_png.ml create mode 100644 src/server/learnocaml_png.ml create mode 100644 src/utils/learnocaml_png.mli diff --git a/src/app/learnocaml_png.ml b/src/app/learnocaml_png.ml new file mode 100644 index 000000000..07a0cab65 --- /dev/null +++ b/src/app/learnocaml_png.ml @@ -0,0 +1,35 @@ +(* This file is part of Learn-OCaml. + * + * Copyright (C) 2019 OCaml Software Foundation. + * Copyright (C) 2016-2018 OCamlPro. + * + * Learn-OCaml is distributed under the terms of the MIT license. See the + * included LICENSE file for details. *) + +open Js_of_ocaml +open Bigarray + +let fill big_data data = + let dim = Array1.dim big_data in + let rec loop i j = + if i < dim then + begin + Dom_html.pixel_set data j big_data.{i}; + Dom_html.pixel_set data (j+1) big_data.{i+1}; + Dom_html.pixel_set data (j+2) big_data.{i+2}; + Dom_html.pixel_set data (j+3) 255; + loop (i+3) (j+4) + end + else () + in loop 0 0 + +let to_png_data big_data w h = + let canvas = Dom_html.createCanvas Dom_html.document in + canvas ##. width := w; + canvas ##. height := h; + let context = canvas ## getContext Dom_html._2d_ in + let image_data = context ## createImageData w h in + let data = image_data ##.data in + fill big_data data; + context ## putImageData image_data 0. 0.; + canvas##toDataURL_type (Js.string "image/png") |> Js.to_string diff --git a/src/server/learnocaml_png.ml b/src/server/learnocaml_png.ml new file mode 100644 index 000000000..248ed4717 --- /dev/null +++ b/src/server/learnocaml_png.ml @@ -0,0 +1,2 @@ +let to_png_data _data _w _h = + "TODO" diff --git a/src/utils/dune b/src/utils/dune index 51daffeac..5a8e29b5b 100644 --- a/src/utils/dune +++ b/src/utils/dune @@ -41,6 +41,14 @@ (modules Learnocaml_xor) ) +(library + (name learnocaml_png) + (wrapped false) + (flags :standard -warn-error A-4-42-44-45-48) + (modules Learnocaml_png) + (virtual_modules Learnocaml_png) + ) + (library (name sha) (wrapped false) @@ -55,4 +63,4 @@ (flags :standard -warn-error A-4-42-44-45-48) (libraries asak lwt learnocaml_store learnocaml_data) (modules learnocaml_partition_create) -) \ No newline at end of file +) diff --git a/src/utils/learnocaml_png.mli b/src/utils/learnocaml_png.mli new file mode 100644 index 000000000..569c4aabc --- /dev/null +++ b/src/utils/learnocaml_png.mli @@ -0,0 +1,15 @@ +(* This file is part of Learn-OCaml. + * + * Copyright (C) 2019 OCaml Software Foundation. + * Copyright (C) 2016-2018 OCamlPro. + * + * Learn-OCaml is distributed under the terms of the MIT license. See the + * included LICENSE file for details. *) + +open Bigarray + +(** [to_png_data data w h] generates a string containing the png image + [data] as a string. The image as dimension [w] * [h]. *) +val to_png_data: + (int, int8_unsigned_elt, c_layout) Array1.t -> int -> int -> string + From 9db5f525e21460b12637757b738ccc579b9a1fa2 Mon Sep 17 00:00:00 2001 From: Etienne MARAIS Date: Sun, 26 Jul 2020 15:40:03 +0200 Subject: [PATCH 16/20] Display image with learnocaml_png --- src/app/dune | 22 +++++++++++++---- src/grader/dune | 6 ++++- src/grader/learnocaml_report.ml | 41 ++++++++++++++++++++++++++++++++ src/grader/learnocaml_report.mli | 6 +++++ src/grader/test_lib.ml | 25 +++++-------------- src/main/dune | 13 ++++++---- src/main/learnocaml_client.ml | 2 ++ src/server/dune | 9 +++++-- 8 files changed, 93 insertions(+), 31 deletions(-) diff --git a/src/app/dune b/src/app/dune index f82e580e3..97a6e0a80 100644 --- a/src/app/dune +++ b/src/app/dune @@ -25,16 +25,25 @@ ocplib_i18n) ) +(library + (name learnocaml_png_web) + (modules Learnocaml_png) + (modes byte) + (implements learnocaml_png) + (libraries js_of_ocaml) + (preprocess (pps js_of_ocaml.ppx))) + (executable (name learnocaml_index_main) (modes byte) (flags :standard -warn-error -6-9-27-33-39) (libraries ezjsonm ace - sha + sha learnocaml_repository learnocaml_app_common learnocaml_toplevel + learnocaml_png_web js_of_ocaml.ppx ocplib_i18n) (modules Learnocaml_teacher_tab @@ -52,10 +61,11 @@ (libraries ezjsonm grading_jsoo ace - sha + sha learnocaml_repository learnocaml_app_common learnocaml_toplevel + learnocaml_png_web js_of_ocaml.ppx ocplib_i18n) (modules Learnocaml_exercise_main) @@ -71,10 +81,11 @@ (flags :standard -warn-error -9-27-33) (libraries ezjsonm ace - sha + sha learnocaml_repository learnocaml_app_common learnocaml_toplevel + learnocaml_png_web js_of_ocaml.ppx ocplib_i18n) (modules Learnocaml_playground_main) @@ -93,6 +104,7 @@ ace learnocaml_repository learnocaml_app_common + learnocaml_png_web lwt_react js_of_ocaml.ppx ocplib_i18n) @@ -111,6 +123,7 @@ ace learnocaml_repository learnocaml_app_common + learnocaml_png_web js_of_ocaml.ppx ocplib_i18n) (modules Learnocaml_description_main) @@ -126,11 +139,12 @@ (modes byte) (flags :standard -warn-error -9-27-33) (libraries asak - ezjsonm + ezjsonm grading_jsoo ace learnocaml_repository learnocaml_app_common + learnocaml_png_web lwt_react js_of_ocaml.ppx ocplib_i18n) diff --git a/src/grader/dune b/src/grader/dune index 0abe9c30a..60f4496cc 100644 --- a/src/grader/dune +++ b/src/grader/dune @@ -4,7 +4,10 @@ (flags :standard -w -37-41 -warn-error -27-39) (modules Learnocaml_report) (preprocess (pps ppx_ocplib_i18n)) - (libraries ocplib-json-typed ocplib_i18n) + (libraries ocplib-json-typed + ocplib_i18n + vg + learnocaml_png) ) (rule @@ -209,6 +212,7 @@ ocplib_i18n learnocaml_repository learnocaml_report + learnocaml_png_web jsutils js_of_ocaml.ppx grader_jsoo_messages) diff --git a/src/grader/learnocaml_report.ml b/src/grader/learnocaml_report.ml index 95b979340..5377800b7 100644 --- a/src/grader/learnocaml_report.ml +++ b/src/grader/learnocaml_report.ml @@ -8,6 +8,8 @@ (* -- minimal HTML producer ------------------------------------------------- *) +open Bigarray + type html = elt list and elt = | C of string @@ -82,10 +84,14 @@ and status = and text = inline list +and image_struct = +(int, int8_unsigned_elt, c_layout) Array1.t * int * int + and inline = | Text of string | Break | Code of string + | Image of image_struct | Output of string let result items = @@ -109,6 +115,22 @@ let result items = let (n, b) = do_report items in (max n 0, b) +let marshal data = + let dim = Array1.dim data in + let rec loop k acc = + if k < dim then + let acc = data.{k} :: acc in + loop (k+1) acc + else acc |> List.rev + in loop 0 [] + +let unmarshall int_data = + let dim = List.length int_data in + let data = Array1.create int8_unsigned c_layout dim in + List.iteri (fun i elt -> data.{i} <- elt) int_data; + data + + let rec scale ?(penalties = true) factor items = List.map (scale_item penalties factor) items and scale_item penalties factor = function @@ -145,6 +167,20 @@ let enc = | (text, `Normal) -> Text text | (text, `Code) -> Code text | (text, `Output) -> Output text) ; + case + (obj3 + (req "data" (list int)) + (req "width" int) + (req "height" int)) + (function + | Image (data, w, h) -> + let str_data = marshal data in + Some (str_data, w, h) + | _ -> None) + (function + | (int_data, w, h) -> + let data = unmarshall int_data in + Image (data, w, h)) ; case empty (function Break -> Some () | _ -> None) @@ -274,6 +310,9 @@ let format items = S ("br", []) | Code s when String.contains s '\n' -> E ("code", ["class", "code-block" ], [ T s ]) + | Image (data, w, h) -> + let data = Learnocaml_png.to_png_data data w h in + S ("img", ["class", "vg-image"; "src", data]) | Output s -> E ("code", ["class", "output-block" ], [ T s ]) | Code s -> @@ -581,6 +620,8 @@ let print ppf items = Format.fprintf ppf "@,%a%a" print_code s print_text rest | Output s :: rest -> Format.fprintf ppf "@,%a%a" print_code s print_text rest + | Image _ :: rest -> + Format.fprintf ppf "@,Image data code%a" print_text rest | [] -> () and print_code ppf s = let s = String.trim s in diff --git a/src/grader/learnocaml_report.mli b/src/grader/learnocaml_report.mli index 0427cfcdd..b0a59fca8 100644 --- a/src/grader/learnocaml_report.mli +++ b/src/grader/learnocaml_report.mli @@ -8,6 +8,8 @@ (** {2 Formatted report output} *) +open Bigarray + type t = item list and item = @@ -25,10 +27,14 @@ and status = and text = inline list +and image_struct = +(int, int8_unsigned_elt, c_layout) Array1.t * int * int + and inline = | Text of string (** A word *) | Break (** Line separator *) | Code of string (** For expressions *) + | Image of image_struct (* For Vg image *) | Output of string (** For output *) (** Gets the total successes of a report, and tells if a failure happened *) diff --git a/src/grader/test_lib.ml b/src/grader/test_lib.ml index 2a0cb85b6..54be79385 100644 --- a/src/grader/test_lib.ml +++ b/src/grader/test_lib.ml @@ -1429,7 +1429,7 @@ module Make let render_array w h img = let size = Size2.v (float_of_int w) (float_of_int h) in let view = Box2.v P2.o size in - let stride = 24 * w in + let stride = 3 * w in let data = Array1.create int8_unsigned c_layout (stride * h) in @@ -1460,7 +1460,7 @@ module Make Array1.set output (k+1) g; Array1.set output (k+2) b; compute_array (k+3) - in compute_array 0 + in got, (compute_array 0) let compute_dist p_exp p_got = let r_exp, g_exp, b_exp = p_exp in @@ -1475,33 +1475,20 @@ module Make let gray = (d * 255) / (3. *. (255. ** 2.) |> sqrt |> int_of_float) in gray, gray, gray - let show a = - let dim = Array1.dim a in - let rec loop acc k = - if k < dim then - let px = a.{k} |> string_of_int in - let acc = acc ^ ", " ^ px in - loop acc (k+1) - else acc - in loop "" 0 - - let test_vg w h got exp = let open Learnocaml_report in - let diff = compute_diff_array compute_dist w h got exp in + let got, diff = compute_diff_array compute_dist w h got exp in let size = Array1.dim diff in let rec check_image k = if k >= size then - [Message ([Text "Nice answer"], Success 1)] + [Message ([Text "Correct value" ; Break; Image (got, w, h)], Success 1)] else let r, g, b = Array1.get diff k, Array1.get diff (k+1), Array1.get diff (k+2) in if r != 0 || g != 0 || b != 0 then - let txt = show diff in - [Message ([Text "Wrong answer" ; Text txt], Failure)] - else - check_image (k+3) + [Message ([Text "Wrong value" ; Break ; Image (diff, w, h)], Failure)] + else check_image (k+3) in check_image 0 let test_vg_against_solution w h name = diff --git a/src/main/dune b/src/main/dune index a5ca480fc..efb11cf45 100644 --- a/src/main/dune +++ b/src/main/dune @@ -18,7 +18,8 @@ learnocaml_process_repository_lib learnocaml_server_lib learnocaml_server_args - learnocaml_report) + learnocaml_report + learnocaml_png_server) ) (executable @@ -30,14 +31,15 @@ (flags :standard -linkall) (modules Learnocaml_client) (libraries cmdliner - sha + sha lwt.unix lwt_utils cohttp.lwt grading_cli learnocaml_data - learnocaml_store - learnocaml_api) + learnocaml_store + learnocaml_api + learnocaml_png_server) ) (executable @@ -45,5 +47,6 @@ (public_name learn-ocaml-server) (name learnocaml_server_main) (modules learnocaml_server_main) - (libraries learnocaml_server_args) + (libraries learnocaml_server_args + learnocaml_png_server) ) diff --git a/src/main/learnocaml_client.ml b/src/main/learnocaml_client.ml index 5e32a5abb..4105e5a5a 100644 --- a/src/main/learnocaml_client.ml +++ b/src/main/learnocaml_client.ml @@ -380,6 +380,8 @@ let console_report ?(verbose=false) ex report = let format_text t = String.concat " " @@ List.map (function | Text w -> w + | Image (data, w, h) -> + Learnocaml_png.to_png_data data w h | Break -> "\n" | Code s when String.contains s '\n' -> "\n"^block ~border_color:[`Cyan] s | Code s -> color [`Cyan] s diff --git a/src/server/dune b/src/server/dune index 6925512d6..2774f07f6 100644 --- a/src/server/dune +++ b/src/server/dune @@ -9,12 +9,17 @@ lwt_utils cohttp.lwt magic-mime - sha + sha checkseum.c decompress learnocaml_report learnocaml_data learnocaml_api learnocaml_store - learnocaml_partition_create) + learnocaml_partition_create) ) + +(library + (name learnocaml_png_server) + (modules Learnocaml_png) + (implements learnocaml_png)) From f5b7d7ae5b9004507f6795dac29d0e586936f41e Mon Sep 17 00:00:00 2001 From: Etienne MARAIS Date: Sun, 26 Jul 2020 15:59:56 +0200 Subject: [PATCH 17/20] Produice ppm string --- src/server/learnocaml_png.ml | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/server/learnocaml_png.ml b/src/server/learnocaml_png.ml index 248ed4717..be136d8b1 100644 --- a/src/server/learnocaml_png.ml +++ b/src/server/learnocaml_png.ml @@ -1,2 +1,27 @@ -let to_png_data _data _w _h = - "TODO" +(* This file is part of Learn-OCaml. + * + * Copyright (C) 2019 OCaml Software Foundation. + * Copyright (C) 2016-2018 OCamlPro. + * + * Learn-OCaml is distributed under the terms of the MIT license. See the + * included LICENSE file for details. *) + +open Bigarray + +let to_png_data data w h = + let dim = Array1.dim data in + let header = [ + "\n" ; "255" ; "\n" ; + string_of_int h ; " " ; string_of_int w ; "\n" ; + "P3" ] + in + let rec loop k acc = + if k < dim then + let r = data.{k} |> string_of_int in + let g = data.{k+1} |> string_of_int in + let b = data.{k+2} |> string_of_int in + let px = r ^ " " ^ g ^ " " ^ b ^ " " in + let acc = px :: acc in + loop (k+3) acc + else List.rev acc |> String.concat "" + in loop 0 header From f47aff437e6512b7ecbe478070f7579515ce5c6e Mon Sep 17 00:00:00 2001 From: Etienne MARAIS Date: Mon, 27 Jul 2020 11:41:47 +0200 Subject: [PATCH 18/20] Clean dune files --- src/app/dune | 8 ++++---- src/server/dune | 4 ++-- src/utils/dune | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/app/dune b/src/app/dune index 97a6e0a80..803f5d4f0 100644 --- a/src/app/dune +++ b/src/app/dune @@ -27,10 +27,10 @@ (library (name learnocaml_png_web) - (modules Learnocaml_png) (modes byte) (implements learnocaml_png) (libraries js_of_ocaml) + (modules Learnocaml_png) (preprocess (pps js_of_ocaml.ppx))) (executable @@ -61,7 +61,7 @@ (libraries ezjsonm grading_jsoo ace - sha + sha learnocaml_repository learnocaml_app_common learnocaml_toplevel @@ -81,7 +81,7 @@ (flags :standard -warn-error -9-27-33) (libraries ezjsonm ace - sha + sha learnocaml_repository learnocaml_app_common learnocaml_toplevel @@ -139,7 +139,7 @@ (modes byte) (flags :standard -warn-error -9-27-33) (libraries asak - ezjsonm + ezjsonm grading_jsoo ace learnocaml_repository diff --git a/src/server/dune b/src/server/dune index 2774f07f6..e31d737c4 100644 --- a/src/server/dune +++ b/src/server/dune @@ -9,14 +9,14 @@ lwt_utils cohttp.lwt magic-mime - sha + sha checkseum.c decompress learnocaml_report learnocaml_data learnocaml_api learnocaml_store - learnocaml_partition_create) + learnocaml_partition_create) ) (library diff --git a/src/utils/dune b/src/utils/dune index 5a8e29b5b..2a64eacc1 100644 --- a/src/utils/dune +++ b/src/utils/dune @@ -45,8 +45,8 @@ (name learnocaml_png) (wrapped false) (flags :standard -warn-error A-4-42-44-45-48) - (modules Learnocaml_png) (virtual_modules Learnocaml_png) + (modules Learnocaml_png) ) (library From d3eaac65899b3ac98d04ca430a42c401c22c7349 Mon Sep 17 00:00:00 2001 From: Etienne MARAIS Date: Mon, 27 Jul 2020 11:56:51 +0200 Subject: [PATCH 19/20] Clean code --- src/app/learnocaml_png.ml | 17 +++++++++-------- src/grader/learnocaml_report.ml | 22 +++++++++++++--------- src/grader/learnocaml_report.mli | 2 +- src/grader/test_lib.ml | 32 ++++++++++++++------------------ src/server/learnocaml_png.ml | 4 +++- src/utils/learnocaml_png.mli | 2 +- 6 files changed, 41 insertions(+), 38 deletions(-) diff --git a/src/app/learnocaml_png.ml b/src/app/learnocaml_png.ml index 07a0cab65..5b6c6c2bc 100644 --- a/src/app/learnocaml_png.ml +++ b/src/app/learnocaml_png.ml @@ -11,14 +11,14 @@ open Bigarray let fill big_data data = let dim = Array1.dim big_data in - let rec loop i j = - if i < dim then + let rec loop big_count data_count = + if big_count < dim then begin - Dom_html.pixel_set data j big_data.{i}; - Dom_html.pixel_set data (j+1) big_data.{i+1}; - Dom_html.pixel_set data (j+2) big_data.{i+2}; - Dom_html.pixel_set data (j+3) 255; - loop (i+3) (j+4) + Dom_html.pixel_set data data_count big_data.{big_count}; + Dom_html.pixel_set data (data_count+1) big_data.{big_count+1}; + Dom_html.pixel_set data (data_count+2) big_data.{big_count+2}; + Dom_html.pixel_set data (data_count+3) 255; + loop (big_count+3) (data_count+4) end else () in loop 0 0 @@ -32,4 +32,5 @@ let to_png_data big_data w h = let data = image_data ##.data in fill big_data data; context ## putImageData image_data 0. 0.; - canvas##toDataURL_type (Js.string "image/png") |> Js.to_string + canvas##toDataURL_type (Js.string "image/png") + |> Js.to_string diff --git a/src/grader/learnocaml_report.ml b/src/grader/learnocaml_report.ml index 5377800b7..b93f39d07 100644 --- a/src/grader/learnocaml_report.ml +++ b/src/grader/learnocaml_report.ml @@ -117,17 +117,21 @@ let result items = let marshal data = let dim = Array1.dim data in - let rec loop k acc = + let rec loop k int_data = if k < dim then - let acc = data.{k} :: acc in - loop (k+1) acc - else acc |> List.rev + let int_data = data.{k} :: int_data in + loop (k+1) int_data + else + int_data + |> List.rev in loop 0 [] let unmarshall int_data = let dim = List.length int_data in let data = Array1.create int8_unsigned c_layout dim in - List.iteri (fun i elt -> data.{i} <- elt) int_data; + List.iteri ( + fun i elt -> data.{i} <- elt + ) int_data; data @@ -174,8 +178,8 @@ let enc = (req "height" int)) (function | Image (data, w, h) -> - let str_data = marshal data in - Some (str_data, w, h) + let int_data = marshal data in + Some (int_data, w, h) | _ -> None) (function | (int_data, w, h) -> @@ -312,7 +316,7 @@ let format items = E ("code", ["class", "code-block" ], [ T s ]) | Image (data, w, h) -> let data = Learnocaml_png.to_png_data data w h in - S ("img", ["class", "vg-image"; "src", data]) + S ("img", ["src", data]) | Output s -> E ("code", ["class", "output-block" ], [ T s ]) | Code s -> @@ -621,7 +625,7 @@ let print ppf items = | Output s :: rest -> Format.fprintf ppf "@,%a%a" print_code s print_text rest | Image _ :: rest -> - Format.fprintf ppf "@,Image data code%a" print_text rest + Format.fprintf ppf "Vg image%a" print_text rest | [] -> () and print_code ppf s = let s = String.trim s in diff --git a/src/grader/learnocaml_report.mli b/src/grader/learnocaml_report.mli index b0a59fca8..8c4135928 100644 --- a/src/grader/learnocaml_report.mli +++ b/src/grader/learnocaml_report.mli @@ -27,7 +27,7 @@ and status = and text = inline list -and image_struct = +and image_struct = (** An array with its width and its height *) (int, int8_unsigned_elt, c_layout) Array1.t * int * int and inline = diff --git a/src/grader/test_lib.ml b/src/grader/test_lib.ml index 54be79385..a5e6fb887 100644 --- a/src/grader/test_lib.ml +++ b/src/grader/test_lib.ml @@ -1445,20 +1445,16 @@ module Make let size = Array1.dim exp in let kind = Array1.kind exp in let layout = Array1.layout exp in - let output = Array1.create kind layout size in + let data = Array1.create kind layout size in let rec compute_array k = - if k >= size then output + if k >= size then data else - let p_exp = - Array1.get exp k, Array1.get exp (k+1), Array1.get exp (k+2) - in - let p_got = - Array1.get got k, Array1.get got (k+1), Array1.get got (k+2) - in + let p_exp = exp.{k}, exp.{k+1}, exp.{k+2} in + let p_got = got.{k}, got.{k+1}, got.{k+2} in let r, g, b = f_dist p_exp p_got in - Array1.set output k r; - Array1.set output (k+1) g; - Array1.set output (k+2) b; + data.{k} <- r; + data.{k+1} <- g; + data.{k+2} <- b; compute_array (k+3) in got, (compute_array 0) @@ -1481,14 +1477,14 @@ module Make let size = Array1.dim diff in let rec check_image k = if k >= size then - [Message ([Text "Correct value" ; Break; Image (got, w, h)], Success 1)] + [Message ([Text "Correct value" ; Break; Image (got, w, h)], + Success 1)] else - let r, g, b = - Array1.get diff k, Array1.get diff (k+1), Array1.get diff (k+2) - in - if r != 0 || g != 0 || b != 0 then - [Message ([Text "Wrong value" ; Break ; Image (diff, w, h)], Failure)] - else check_image (k+3) + if diff.{k} != 0 || diff.{k+1} != 0 || diff.{k+2} != 0 then + [Message ([Text "Wrong value" ; Break ; Image (diff, w, h)], + Failure)] + else + check_image (k+3) in check_image 0 let test_vg_against_solution w h name = diff --git a/src/server/learnocaml_png.ml b/src/server/learnocaml_png.ml index be136d8b1..25c5b66b1 100644 --- a/src/server/learnocaml_png.ml +++ b/src/server/learnocaml_png.ml @@ -23,5 +23,7 @@ let to_png_data data w h = let px = r ^ " " ^ g ^ " " ^ b ^ " " in let acc = px :: acc in loop (k+3) acc - else List.rev acc |> String.concat "" + else + List.rev acc + |> String.concat "" in loop 0 header diff --git a/src/utils/learnocaml_png.mli b/src/utils/learnocaml_png.mli index 569c4aabc..5cbe22168 100644 --- a/src/utils/learnocaml_png.mli +++ b/src/utils/learnocaml_png.mli @@ -9,7 +9,7 @@ open Bigarray (** [to_png_data data w h] generates a string containing the png image - [data] as a string. The image as dimension [w] * [h]. *) + [data] as a string. The image has dimensions [w] * [h]. *) val to_png_data: (int, int8_unsigned_elt, c_layout) Array1.t -> int -> int -> string From ccda29f8cb2cc80ac7d9a0cdf8602255062969c0 Mon Sep 17 00:00:00 2001 From: Etienne MARAIS Date: Wed, 29 Jul 2020 10:38:35 +0200 Subject: [PATCH 20/20] Update lock file --- learn-ocaml-client.opam | 3 +++ learn-ocaml.opam.locked | 10 +++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/learn-ocaml-client.opam b/learn-ocaml-client.opam index 15c72e796..b5878056c 100644 --- a/learn-ocaml-client.opam +++ b/learn-ocaml-client.opam @@ -40,6 +40,9 @@ depends: [ "ppx_sexp_conv" {= "v0.9.0"} "ppx_fields_conv" {= "v0.9.0"} ] +pin-depends: [ + "vg.dev" "git+https://github.com/maiste/vg#ocaml_rendering" +] build: [ ["dune" "build" "@install" "-p" name "-j" jobs] ] diff --git a/learn-ocaml.opam.locked b/learn-ocaml.opam.locked index 8676fe078..c46813980 100644 --- a/learn-ocaml.opam.locked +++ b/learn-ocaml.opam.locked @@ -25,6 +25,7 @@ depends: [ "base64" {= "2.3.0"} "bigarray-compat" {= "1.0.0"} "biniou" {= "1.2.1"} + "cairo2" {= "0.6.1"} "checkseum" {= "0.1.0"} "cmdliner" {= "1.0.4"} "cohttp" {= "1.1.1"} @@ -33,6 +34,7 @@ depends: [ "conduit" {= "1.3.0"} "conduit-lwt" {= "1.3.0"} "conduit-lwt-unix" {= "1.3.0"} + "conf-cairo" {= "1"} "conf-git" {= "1.0"} "conf-libssl" {= "1"} "conf-m4" {= "1"} @@ -43,12 +45,14 @@ depends: [ "decompress" {= "0.8.1"} "digestif" {= "0.8.0-1"} "dune" {= "2.0.1"} + "dune-configurator" {= "1.11.4"} "easy-format" {= "1.3.2"} "eqaf" {= "0.7"} "ezjsonm" {= "1.1.0"} "fieldslib" {= "v0.9.0"} "fmt" {= "0.8.8"} "fpath" {= "0.7.2"} + "gg" {= "0.9.3"} "hex" {= "1.4.0"} "ipaddr" {= "2.8.0"} "jbuilder" {= "1.0+beta20.2"} @@ -61,6 +65,7 @@ depends: [ "jsonm" {= "1.0.1"} "logs" {= "0.7.0"} "lwt" {= "4.2.1"} + "lwt_log" {= "1.1.1"} "lwt_react" {= "1.1.3"} "lwt_ssl" {= "1.1.3"} "magic-mime" {= "1.1.2"} @@ -110,8 +115,11 @@ depends: [ "uchar" {= "0.0.2"} "uri" {= "1.9.7"} "uutf" {= "1.0.2"} + "vg" {= "dev"} "yojson" {= "1.7.0"} - "vg" {= "0.9.3"} +] +pin-depends: [ + "vg.dev" "git+https://github.com/maiste/vg#ocaml_rendering" ] build: [ [make "static"]