mirror of
https://github.com/CatalaLang/catala.git
synced 2024-09-19 16:28:12 +03:00
Small ocaml/js output rehaul (#582)
This commit is contained in:
commit
c78a6b62c9
14
INSTALL.md
14
INSTALL.md
@ -137,19 +137,17 @@ To uninstall, use
|
||||
#### Updating [`@catala-lang/catala-web-assets`](https://www.npmjs.com/package/@catala-lang/catala-web-assets)
|
||||
|
||||
The Catala website features assets generated by the Catala compiler. They are
|
||||
needed to build the website. To produce them, simply run
|
||||
needed to build the website. To produce them, run
|
||||
|
||||
make website-assets DUNE_PROFILE=release
|
||||
make website-assets.tar
|
||||
|
||||
Then, use a helper script to copy them over to the `assets` directory of the
|
||||
[`catala-web-assets`](https://github.com/CatalaLang/catala-web-assets)
|
||||
repository.
|
||||
Then, get the base [`catala-web-assets`](https://github.com/CatalaLang/catala-web-assets)
|
||||
repository, and untar the generated file within the `assets/` subdirectory:
|
||||
|
||||
./generate_website_assets.sh <path-to-catala-web-assets>/assets
|
||||
tar xf website-assets.tar -C <path-to-catala-web-assets>/assets
|
||||
|
||||
> You will need the `groff` executable to generate the HTML versions of the man
|
||||
pages, as well as the `rsync` executable to transfer files (preferred to `cp`)
|
||||
because it also works with a remote server.
|
||||
pages.
|
||||
|
||||
## Syntax highlighting
|
||||
|
||||
|
65
Makefile
65
Makefile
@ -217,15 +217,27 @@ tests/%: .FORCE
|
||||
# (and therefore the doc target here)
|
||||
WEBSITE_ASSETS = grammar.html catala.html clerk.html
|
||||
|
||||
$(addprefix _build/default/,$(WEBSITE_ASSETS)):
|
||||
dune build $@
|
||||
WEBSITE_ASSETS_EXAMPLES = \
|
||||
tutorial_en/tutorial_en.html \
|
||||
tutoriel_fr/tutoriel_fr.html \
|
||||
us_tax_code/us_tax_code.html \
|
||||
allocations_familiales/allocations_familiales.html \
|
||||
allocations_familiales/allocations_familiales_schema.json \
|
||||
aides_logement/aides_logement.html \
|
||||
aides_logement/aides_logement_schema.json
|
||||
|
||||
WEBSITE_ASSETS_ALL = $(WEBSITE_ASSETS) $(addprefix catala-examples.tmp/,$(WEBSITE_ASSETS_EXAMPLES))
|
||||
|
||||
website-assets-base: build
|
||||
dune build $(WEBSITE_ASSETS)
|
||||
$(call local_tmp_clone,catala-examples) && \
|
||||
dune build $(addprefix _build/default/,$(WEBSITE_ASSETS_ALL))
|
||||
|
||||
website-assets.tar:
|
||||
# $(MAKE) DUNE_PROFILE=release website-assets-base
|
||||
tar cf $@ $(foreach file,$(WEBSITE_ASSETS_ALL),-C $(CURDIR)/$(dir _build/default/$(file)) $(notdir $(file)))
|
||||
|
||||
#> website-assets : Builds all the assets necessary for the Catala website
|
||||
website-assets:
|
||||
$(MAKE) DUNE_PROFILE=release website-assets-base
|
||||
website-assets: website-assets.tar
|
||||
|
||||
##########################################
|
||||
# Miscellaneous
|
||||
@ -238,32 +250,35 @@ all: \
|
||||
runtimes \
|
||||
plugins
|
||||
|
||||
BRANCH=$(shell git branch --show-current 2>/dev/null || echo master)
|
||||
BRANCH = $(shell git branch --show-current 2>/dev/null || echo master)
|
||||
|
||||
# Attempt a clone of the named CatalaLang repo into <name>.tmp, using local git
|
||||
# objects in ../<name> if available, the branch with the same name as the
|
||||
# current branch if it exists (master otherwise), and falling back to a local
|
||||
# clone of ../<name> if the network is not available. The temp dir is removed
|
||||
# when the shell terminates, so this must be run in the same "Makefile line" as
|
||||
# its usage.
|
||||
local_tmp_clone = { \
|
||||
rm -rf $1.tmp && \
|
||||
trap "rm -rf $1.tmp" EXIT && \
|
||||
git clone https://github.com/CatalaLang/$1 \
|
||||
--depth 1 --reference-if-able ../$1 \
|
||||
$1.tmp -b $(BRANCH) || \
|
||||
git clone https://github.com/CatalaLang/$1 \
|
||||
--depth 1 --reference-if-able ../$1 \
|
||||
$1.tmp || \
|
||||
git clone -s ../$1 $1.tmp $(BRANCH) || \
|
||||
git clone -s ../$1 $1.tmp master; \
|
||||
}
|
||||
|
||||
#> alltest : Runs more extensive tests, including the examples and french-law. Use before push!
|
||||
alltest: dependencies-python
|
||||
@export DUNE_PROFILE=check && \
|
||||
dune build @update-parser-messages @install @runtest && \
|
||||
$(CLERK_BIN) test tests && \
|
||||
rm -rf catala-examples.tmp french-law.tmp && \
|
||||
trap "rm -rf catala-examples.tmp french-law.tmp $$TMP" EXIT && \
|
||||
{ git clone https://github.com/CatalaLang/catala-examples \
|
||||
--depth 1 --reference-if-able ../catala-examples \
|
||||
catala-examples.tmp -b $(BRANCH) || \
|
||||
git clone https://github.com/CatalaLang/catala-examples \
|
||||
--depth 1 --reference-if-able ../catala-examples \
|
||||
catala-examples.tmp || \
|
||||
git clone -s ../catala-examples catala-examples.tmp $(BRANCH) || \
|
||||
git clone -s ../catala-examples catala-examples.tmp master; } && \
|
||||
$(call local_tmp_clone,catala-examples) && \
|
||||
$(CLERK_BIN) test catala-examples.tmp && \
|
||||
{ git clone https://github.com/CatalaLang/french-law \
|
||||
--depth 1 --reference-if-able ../french-law \
|
||||
french-law.tmp -b $(BRANCH) || \
|
||||
git clone https://github.com/CatalaLang/french-law \
|
||||
--depth 1 --reference-if-able ../french-law \
|
||||
french-law.tmp || \
|
||||
git clone -s ../french-law french-law.tmp $(BRANCH) || \
|
||||
git clone -s ../french-law french-law.tmp master; } && \
|
||||
$(call local_tmp_clone,french-law) && \
|
||||
make -C french-law.tmp all PY_VENV_DIR=$(ROOT_DIR)/_python_venv
|
||||
|
||||
#> clean : Clean build artifacts
|
||||
@ -288,4 +303,4 @@ help_catala:
|
||||
.PHONY: inspect clean all english allocations_familiales \
|
||||
pygments install build_dev build doc format dependencies \
|
||||
dependencies-ocaml catala.html help parser-messages plugins \
|
||||
website-assets website-assets-base
|
||||
website-assets.tar website-assets-base
|
||||
|
@ -347,9 +347,7 @@ module Poll = struct
|
||||
|
||||
let ocaml_link_flags : string list Lazy.t =
|
||||
lazy
|
||||
(let link_libs =
|
||||
["yojson"; "ppx_yojson_conv_lib"; "zarith"; "dates_calc"]
|
||||
in
|
||||
(let link_libs = ["zarith"; "dates_calc"] in
|
||||
let link_libs_flags =
|
||||
List.concat_map
|
||||
(fun lib ->
|
||||
@ -493,6 +491,10 @@ let[@ocamlformat "disable"] static_base_rules =
|
||||
]
|
||||
~description:["<ocaml>"; "⇒"; !output];
|
||||
|
||||
Nj.rule "python"
|
||||
~command:[!catala_exe; "python"; !catala_flags; !input; "-o"; !output]
|
||||
~description:["<catala>"; "python"; "⇒"; !output];
|
||||
|
||||
Nj.rule "out-test"
|
||||
~command: [
|
||||
!catala_exe; !test_command; "--plugin-dir="; "-o -"; !catala_flags; !input;
|
||||
@ -591,21 +593,30 @@ let gen_build_statements
|
||||
~outputs:[modd m])
|
||||
item.module_def
|
||||
in
|
||||
let ml_file =
|
||||
let target_file ext =
|
||||
match item.module_def with
|
||||
| Some m -> (!Var.builddir / src /../ m) ^ ".ml"
|
||||
| None -> (!Var.builddir / !Var.src) ^ ".ml"
|
||||
| Some m -> (!Var.builddir / src /../ m) ^ "." ^ ext
|
||||
| None -> (!Var.builddir / !Var.src) ^ "." ^ ext
|
||||
in
|
||||
let ocaml =
|
||||
let ml_file = target_file "ml" in
|
||||
let py_file = target_file "py" in
|
||||
let ocaml, python =
|
||||
if item.extrnal then
|
||||
Nj.build "copy"
|
||||
~implicit_in:[inc srcv]
|
||||
~inputs:[src -.- "ml"]
|
||||
~outputs:[ml_file]
|
||||
( Nj.build "copy"
|
||||
~implicit_in:[inc srcv]
|
||||
~inputs:[src -.- "ml"]
|
||||
~outputs:[ml_file],
|
||||
Nj.build "copy"
|
||||
~implicit_in:[inc srcv]
|
||||
~inputs:[src -.- "py"]
|
||||
~outputs:[py_file] )
|
||||
else
|
||||
Nj.build "catala-ocaml"
|
||||
~inputs:[inc srcv]
|
||||
~implicit_in:[!Var.catala_exe] ~outputs:[ml_file]
|
||||
( Nj.build "catala-ocaml"
|
||||
~inputs:[inc srcv]
|
||||
~implicit_in:[!Var.catala_exe] ~outputs:[ml_file],
|
||||
Nj.build "python"
|
||||
~inputs:[inc srcv]
|
||||
~implicit_in:[!Var.catala_exe] ~outputs:[py_file] )
|
||||
in
|
||||
let ocamlopt =
|
||||
let implicit_out_exts = ["cmi"; "cmx"; "cmt"; "o"] in
|
||||
@ -774,6 +785,7 @@ let gen_build_statements
|
||||
Option.to_seq expose_module;
|
||||
Seq.return ocaml;
|
||||
Seq.return ocamlopt;
|
||||
Seq.return python;
|
||||
List.to_seq tests;
|
||||
Seq.return interpret;
|
||||
]
|
||||
|
@ -31,8 +31,6 @@ depends: [
|
||||
"ocaml" {>= "4.14.0"}
|
||||
"ocamlfind" {!= "1.9.5"}
|
||||
"ocamlgraph" {>= "1.8.8"}
|
||||
"yojson" {>= "2.0" }
|
||||
"ppx_yojson_conv" {>= "0.14.0"}
|
||||
"re" {>= "1.9.0"}
|
||||
"sedlex" {>= "2.4"}
|
||||
"uutf" {>= "1.0.3"}
|
||||
|
@ -462,7 +462,7 @@ let format_struct_embedding
|
||||
format_struct_name struct_name format_to_module_name (`Sname struct_name)
|
||||
else
|
||||
Format.fprintf fmt
|
||||
"@[<hov 2>let embed_%a (x: %a.t) : runtime_value =@ Struct([\"%a\"],@ \
|
||||
"@[<hov 2>let embed_%a (x: %a.t) : runtime_value =@ Struct(\"%a\",@ \
|
||||
@[<hov 2>[%a]@])@]@\n\
|
||||
@\n"
|
||||
format_struct_name struct_name format_to_module_name (`Sname struct_name)
|
||||
@ -485,7 +485,7 @@ let format_enum_embedding
|
||||
else
|
||||
Format.fprintf fmt
|
||||
"@[<hv 2>@[<hov 2>let embed_%a@ @[<hov 2>(x:@ %a.t)@]@ : runtime_value \
|
||||
=@]@ Enum([\"%a\"],@ @[<hov 2>match x with@ %a@])@]@\n\
|
||||
=@]@ Enum(\"%a\",@ @[<hov 2>match x with@ %a@])@]@\n\
|
||||
@\n"
|
||||
format_enum_name enum_name format_to_module_name (`Ename enum_name)
|
||||
EnumName.format enum_name
|
||||
|
@ -994,7 +994,7 @@ let interpret_program_dcalc p s : (Uid.MarkedString.info * ('a, 'm) gexpr) list
|
||||
built-in interpreter does not have a way to retrieve input \
|
||||
values from the command line, so it cannot execute this scope. \
|
||||
Please create another scope that provides the input arguments \
|
||||
to this one and execute it instead. ")
|
||||
to this one and execute it instead.")
|
||||
taus
|
||||
in
|
||||
let to_interpret =
|
||||
|
@ -1,25 +0,0 @@
|
||||
#! /usr/bin/env sh
|
||||
|
||||
set -eux
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
echo "USAGE: \$1 DST where DST is the directory in which files have to be copied"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BUILD=_build/default
|
||||
|
||||
rsync $BUILD/examples/allocations_familiales/allocations_familiales.html $1/
|
||||
rsync $BUILD/examples/aides_logement/aides_logement.html $1/
|
||||
rsync $BUILD/examples/us_tax_code/us_tax_code.html $1/
|
||||
rsync $BUILD/examples/tutorial_en/tutorial_en.html $1/
|
||||
rsync $BUILD/examples/tutoriel_fr/tutoriel_fr.html $1/
|
||||
|
||||
rsync $BUILD/grammar.html $1/
|
||||
rsync $BUILD/catala.html $1/
|
||||
rsync $BUILD/clerk.html $1/
|
||||
|
||||
rsync $BUILD/examples/allocations_familiales/allocations_familiales_schema.json $1/
|
||||
rsync $BUILD/examples/aides_logement/aides_logement_schema.json $1/
|
@ -84,83 +84,75 @@ let event_manager : event_manager Js.t =
|
||||
method resetLog = Js.wrap_meth_callback R_ocaml.reset_log
|
||||
|
||||
method retrieveEvents =
|
||||
Js.wrap_meth_callback (fun () ->
|
||||
Js.array
|
||||
(Array.of_list
|
||||
(R_ocaml.retrieve_log ()
|
||||
|> R_ocaml.EventParser.parse_raw_events
|
||||
|> List.map (fun event ->
|
||||
object%js
|
||||
val mutable data =
|
||||
event
|
||||
|> R_ocaml.yojson_of_event
|
||||
|> Yojson.Safe.to_string
|
||||
|> Js.string
|
||||
end))))
|
||||
Js.wrap_meth_callback
|
||||
@@ fun () ->
|
||||
R_ocaml.retrieve_log ()
|
||||
|> R_ocaml.EventParser.parse_raw_events
|
||||
|> List.map (fun event ->
|
||||
object%js
|
||||
val mutable data = event |> R_ocaml.Json.event |> Js.string
|
||||
end)
|
||||
|> Array.of_list
|
||||
|> Js.array
|
||||
|
||||
method retrieveRawEvents =
|
||||
Js.wrap_meth_callback (fun () ->
|
||||
Js.array
|
||||
(Array.of_list
|
||||
(List.map
|
||||
(fun evt ->
|
||||
object%js
|
||||
val mutable eventType =
|
||||
Js.string
|
||||
(match evt with
|
||||
| R_ocaml.BeginCall _ -> "Begin call"
|
||||
| EndCall _ -> "End call"
|
||||
| VariableDefinition _ -> "Variable definition"
|
||||
| DecisionTaken _ -> "Decision taken")
|
||||
let evt_to_js evt =
|
||||
(* FIXME: ideally this could be just a Json.parse (R_ocaml.Json.event
|
||||
foo) ? *)
|
||||
object%js
|
||||
val mutable eventType =
|
||||
(match evt with
|
||||
| R_ocaml.BeginCall _ -> "Begin call"
|
||||
| EndCall _ -> "End call"
|
||||
| VariableDefinition _ -> "Variable definition"
|
||||
| DecisionTaken _ -> "Decision taken")
|
||||
|> Js.string
|
||||
|
||||
val mutable information =
|
||||
Js.array
|
||||
(Array.of_list
|
||||
(match evt with
|
||||
| BeginCall info
|
||||
| EndCall info
|
||||
| VariableDefinition (info, _, _) ->
|
||||
List.map Js.string info
|
||||
| DecisionTaken _ -> []))
|
||||
val mutable information =
|
||||
(match evt with
|
||||
| BeginCall info | EndCall info | VariableDefinition (info, _, _) ->
|
||||
List.map Js.string info
|
||||
| DecisionTaken _ -> [])
|
||||
|> Array.of_list
|
||||
|> Js.array
|
||||
|
||||
val mutable loggedIOJson =
|
||||
match evt with
|
||||
| VariableDefinition (_, io, _) ->
|
||||
io
|
||||
|> R_ocaml.yojson_of_io_log
|
||||
|> Yojson.Safe.to_string
|
||||
|> Js.string
|
||||
| EndCall _ | BeginCall _ | DecisionTaken _ ->
|
||||
"unavailable" |> Js.string
|
||||
val mutable loggedIOJson =
|
||||
match evt with
|
||||
| VariableDefinition (_, io, _) ->
|
||||
io |> R_ocaml.Json.io_log |> Js.string
|
||||
| EndCall _ | BeginCall _ | DecisionTaken _ ->
|
||||
"unavailable" |> Js.string
|
||||
|
||||
val mutable loggedValueJson =
|
||||
(match evt with
|
||||
| VariableDefinition (_, _, v) -> v
|
||||
| EndCall _ | BeginCall _ | DecisionTaken _ ->
|
||||
R_ocaml.unembeddable ())
|
||||
|> R_ocaml.yojson_of_runtime_value
|
||||
|> Yojson.Safe.to_string
|
||||
|> Js.string
|
||||
val mutable loggedValueJson =
|
||||
(match evt with
|
||||
| VariableDefinition (_, _, v) -> v
|
||||
| EndCall _ | BeginCall _ | DecisionTaken _ ->
|
||||
R_ocaml.unembeddable ())
|
||||
|> R_ocaml.Json.runtime_value
|
||||
|> Js.string
|
||||
|
||||
val mutable sourcePosition =
|
||||
match evt with
|
||||
| DecisionTaken pos ->
|
||||
Js.def
|
||||
(object%js
|
||||
val mutable fileName = Js.string pos.filename
|
||||
val mutable startLine = pos.start_line
|
||||
val mutable endLine = pos.end_line
|
||||
val mutable startColumn = pos.start_column
|
||||
val mutable endColumn = pos.end_column
|
||||
val mutable sourcePosition =
|
||||
match evt with
|
||||
| DecisionTaken pos ->
|
||||
Js.def
|
||||
(object%js
|
||||
val mutable fileName = Js.string pos.filename
|
||||
val mutable startLine = pos.start_line
|
||||
val mutable endLine = pos.end_line
|
||||
val mutable startColumn = pos.start_column
|
||||
val mutable endColumn = pos.end_column
|
||||
|
||||
val mutable lawHeadings =
|
||||
Js.array
|
||||
(Array.of_list
|
||||
(List.map Js.string pos.law_headings))
|
||||
end)
|
||||
| _ -> Js.undefined
|
||||
end)
|
||||
(R_ocaml.retrieve_log ()))))
|
||||
val mutable lawHeadings =
|
||||
List.map Js.string pos.law_headings
|
||||
|> Array.of_list
|
||||
|> Js.array
|
||||
end)
|
||||
| _ -> Js.undefined
|
||||
end
|
||||
in
|
||||
Js.wrap_meth_callback
|
||||
@@ fun () ->
|
||||
R_ocaml.retrieve_log () |> List.map evt_to_js |> Array.of_list |> Js.array
|
||||
end
|
||||
|
||||
let execute_or_throw_error f =
|
||||
|
@ -1,7 +1,5 @@
|
||||
(library
|
||||
(name runtime_ocaml)
|
||||
(public_name catala.runtime_ocaml)
|
||||
(preprocess
|
||||
(pps ppx_yojson_conv))
|
||||
(libraries dates_calc zarith zarith_stubs_js)
|
||||
(modules runtime))
|
||||
|
@ -13,8 +13,6 @@
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations under
|
||||
the License. *)
|
||||
open Ppx_yojson_conv_lib.Yojson_conv.Primitives
|
||||
|
||||
type nonrec unit = unit
|
||||
type nonrec bool = bool
|
||||
|
||||
@ -30,8 +28,8 @@ module Eoption = struct
|
||||
type 'a t = ENone of unit | ESome of 'a
|
||||
end
|
||||
|
||||
type io_input = NoInput | OnlyInput | Reentrant [@@deriving yojson_of]
|
||||
type io_log = { io_input : io_input; io_output : bool } [@@deriving yojson_of]
|
||||
type io_input = NoInput | OnlyInput | Reentrant
|
||||
type io_log = { io_input : io_input; io_output : bool }
|
||||
|
||||
type source_position = {
|
||||
filename : string;
|
||||
@ -41,7 +39,6 @@ type source_position = {
|
||||
end_column : int;
|
||||
law_headings : string list;
|
||||
}
|
||||
[@@deriving yojson_of]
|
||||
|
||||
exception EmptyError
|
||||
exception AssertionFailed of source_position
|
||||
@ -190,12 +187,6 @@ let duration_to_string (d : duration) : string =
|
||||
let duration_to_years_months_days (d : duration) : int * int * int =
|
||||
Dates_calc.Dates.period_to_ymds d
|
||||
|
||||
let yojson_of_money (m : money) = `Float (money_to_float m)
|
||||
let yojson_of_integer (i : integer) = `Int (integer_to_int i)
|
||||
let yojson_of_decimal (d : decimal) = `Float (decimal_to_float d)
|
||||
let yojson_of_date (d : date) = `String (date_to_string d)
|
||||
let yojson_of_duration (d : duration) = `String (duration_to_string d)
|
||||
|
||||
type runtime_value =
|
||||
| Unit
|
||||
| Bool of bool
|
||||
@ -204,11 +195,10 @@ type runtime_value =
|
||||
| Decimal of decimal
|
||||
| Date of date
|
||||
| Duration of duration
|
||||
| Enum of string list * (string * runtime_value)
|
||||
| Struct of string list * (string * runtime_value) list
|
||||
| Enum of string * (string * runtime_value)
|
||||
| Struct of string * (string * runtime_value) list
|
||||
| Array of runtime_value array
|
||||
| Unembeddable
|
||||
[@@deriving yojson_of]
|
||||
|
||||
let unembeddable _ = Unembeddable
|
||||
let embed_unit () = Unit
|
||||
@ -220,7 +210,7 @@ let embed_date x = Date x
|
||||
let embed_duration x = Duration x
|
||||
let embed_array f x = Array (Array.map f x)
|
||||
|
||||
type information = string list [@@deriving yojson_of]
|
||||
type information = string list
|
||||
|
||||
type raw_event =
|
||||
| BeginCall of information
|
||||
@ -236,7 +226,6 @@ type event =
|
||||
inputs : var_def list;
|
||||
body : event list;
|
||||
}
|
||||
[@@deriving yojson_of]
|
||||
|
||||
and var_def = {
|
||||
pos : source_position option;
|
||||
@ -253,6 +242,108 @@ and fun_call = {
|
||||
output : var_def;
|
||||
}
|
||||
|
||||
module BufferedJson = struct
|
||||
let rec list f buf = function
|
||||
| [] -> ()
|
||||
| [x] -> f buf x
|
||||
| x :: r ->
|
||||
f buf x;
|
||||
Buffer.add_char buf ',';
|
||||
list f buf r
|
||||
|
||||
let quote buf str =
|
||||
Buffer.add_char buf '"';
|
||||
String.iter
|
||||
(function
|
||||
| ('"' | '\\') as c ->
|
||||
Buffer.add_char buf '\\';
|
||||
Buffer.add_char buf c
|
||||
| '\n' -> Buffer.add_string buf "\\n"
|
||||
| '\t' -> Buffer.add_string buf "\\t"
|
||||
| '\r' -> Buffer.add_string buf "\\r"
|
||||
| '\x00' .. '\x1F' as c -> Printf.bprintf buf "\\u%04x" (int_of_char c)
|
||||
| c -> Buffer.add_char buf c)
|
||||
str;
|
||||
Buffer.add_char buf '"'
|
||||
|
||||
(* Note: the output format is made for transition with what Yojson gave us,
|
||||
but we could change it to something nicer (e.g. objects for structures) *)
|
||||
let rec runtime_value buf = function
|
||||
| Unit -> Buffer.add_string buf {|"Unit"|}
|
||||
| Bool b -> Buffer.add_string buf (string_of_bool b)
|
||||
| Money m -> Buffer.add_string buf (money_to_string m)
|
||||
| Integer i -> Buffer.add_string buf (integer_to_string i)
|
||||
| Decimal d ->
|
||||
Buffer.add_string buf (decimal_to_string ~max_prec_digits:10 d)
|
||||
| Date d -> quote buf (date_to_string d)
|
||||
| Duration d -> quote buf (duration_to_string d)
|
||||
| Enum (name, (constr, v)) ->
|
||||
Printf.bprintf buf {|[["%s"],["%s",%a]]|} name constr runtime_value v
|
||||
| Struct (name, elts) ->
|
||||
Printf.bprintf buf {|["%s",[%a]]|} name
|
||||
(list (fun buf (cstr, v) ->
|
||||
Printf.bprintf buf {|"%s":%a|} cstr runtime_value v))
|
||||
elts
|
||||
| Array elts ->
|
||||
Printf.bprintf buf "[%a]" (list runtime_value) (Array.to_list elts)
|
||||
| Unembeddable -> Buffer.add_string buf {|"unembeddable"|}
|
||||
|
||||
let information buf info = Printf.bprintf buf "[%a]" (list quote) info
|
||||
|
||||
let source_position buf pos =
|
||||
Printf.bprintf buf {|{"filename":%a|} quote pos.filename;
|
||||
Printf.bprintf buf {|,"start_line":%d|} pos.start_line;
|
||||
Printf.bprintf buf {|,"start_column":%d|} pos.start_column;
|
||||
Printf.bprintf buf {|,"end_line":%d|} pos.end_line;
|
||||
Printf.bprintf buf {|,"end_column":%d|} pos.end_column;
|
||||
Printf.bprintf buf {|,"law_headings":[%a]}|} (list quote) pos.law_headings
|
||||
|
||||
let io_input buf = function
|
||||
| NoInput -> quote buf "NoInput"
|
||||
| OnlyInput -> quote buf "OnlyInput"
|
||||
| Reentrant -> quote buf "Reentrant"
|
||||
|
||||
let io_log buf iol =
|
||||
Printf.bprintf buf {|{"io_input":%a|} io_input iol.io_input;
|
||||
Printf.bprintf buf {|,"io_output":%b}|} iol.io_output
|
||||
|
||||
let rec event buf = function
|
||||
| VarComputation vd ->
|
||||
Printf.bprintf buf {|"VarComputation",%a]|} var_def vd
|
||||
| FunCall fc -> Printf.bprintf buf {|"FunCall",%a]|} fun_call fc
|
||||
| SubScopeCall { name; inputs; body } ->
|
||||
Printf.bprintf buf {|{"name":%a,"inputs":[%a],"body":[%a]}|} information
|
||||
name (list var_def) inputs (list event) body
|
||||
|
||||
and var_def buf def =
|
||||
Option.iter (Printf.bprintf buf {|{"pos":%a|} source_position) def.pos;
|
||||
Printf.bprintf buf {|,"name":%a|} information def.name;
|
||||
Printf.bprintf buf {|,"io":%a|} io_log def.io;
|
||||
Printf.bprintf buf {|,"value":%a|} runtime_value def.value;
|
||||
Option.iter
|
||||
(Printf.bprintf buf {|,"fun_calls":[%a]}|} (list fun_call))
|
||||
def.fun_calls
|
||||
|
||||
and fun_call buf fc =
|
||||
Printf.bprintf buf {|{"fun_name":%a|} information fc.fun_name;
|
||||
Printf.bprintf buf {|,"fun_inputs":[%a]|} (list var_def) fc.fun_inputs;
|
||||
Printf.bprintf buf {|,"body":[%a]|} (list event) fc.body;
|
||||
Printf.bprintf buf {|,"output":%a}|} var_def fc.output
|
||||
end
|
||||
|
||||
module Json = struct
|
||||
let str f x =
|
||||
let buf = Buffer.create 800 in
|
||||
f buf x;
|
||||
Buffer.contents buf
|
||||
|
||||
open BufferedJson
|
||||
|
||||
let runtime_value = str runtime_value
|
||||
let io_log = str io_log
|
||||
let event = str event
|
||||
end
|
||||
|
||||
let log_ref : raw_event list ref = ref []
|
||||
let reset_log () = log_ref := []
|
||||
let retrieve_log () = List.rev !log_ref
|
||||
@ -313,8 +404,7 @@ let rec pp_events ?(is_first_call = true) ppf events =
|
||||
| Duration x -> Format.fprintf ppf "%s" (duration_to_string x)
|
||||
| Enum (_, (name, _)) -> Format.fprintf ppf "%s" name
|
||||
| Struct (name, attrs) ->
|
||||
Format.fprintf ppf "@[<hv 2>%s = {@ %a@;<1 -2>}@]"
|
||||
(String.concat "." name)
|
||||
Format.fprintf ppf "@[<hv 2>%s = {@ %a@;<1 -2>}@]" name
|
||||
(Format.pp_print_list
|
||||
~pp_sep:(fun ppf () -> Format.fprintf ppf ",@,")
|
||||
(fun fmt (name, value) ->
|
||||
@ -414,7 +504,7 @@ module EventParser = struct
|
||||
| VariableDefinition (name, io, value) ->
|
||||
Printf.sprintf "VariableDefinition([ %s ], %s, %s)"
|
||||
(String.concat ", " name) (io_log_to_string io)
|
||||
(yojson_of_runtime_value value |> Yojson.Safe.to_string)
|
||||
(Json.runtime_value value)
|
||||
| DecisionTaken pos ->
|
||||
Printf.sprintf "DecisionTaken(%s:%d.%d-%d.%d)" pos.filename pos.start_line
|
||||
pos.start_column pos.end_line pos.end_column
|
||||
|
@ -57,13 +57,11 @@ type io_input =
|
||||
| Reentrant
|
||||
(** For variables defined in the scope that can also be redefined by the
|
||||
caller as they appear in the input. *)
|
||||
[@@deriving yojson_of]
|
||||
|
||||
type io_log = {
|
||||
io_input : io_input;
|
||||
io_output : bool; (** [true] if the variable is an output *)
|
||||
}
|
||||
[@@deriving yojson_of]
|
||||
|
||||
(** {1 Exceptions} *)
|
||||
|
||||
@ -85,11 +83,10 @@ type runtime_value =
|
||||
| Decimal of decimal
|
||||
| Date of date
|
||||
| Duration of duration
|
||||
| Enum of string list * (string * runtime_value)
|
||||
| Struct of string list * (string * runtime_value) list
|
||||
| Enum of string * (string * runtime_value)
|
||||
| Struct of string * (string * runtime_value) list
|
||||
| Array of runtime_value Array.t
|
||||
| Unembeddable
|
||||
[@@deriving yojson_of]
|
||||
|
||||
val unembeddable : 'a -> runtime_value
|
||||
val embed_unit : unit -> runtime_value
|
||||
@ -115,7 +112,7 @@ val embed_array : ('a -> runtime_value) -> 'a Array.t -> runtime_value
|
||||
|
||||
(** {2 Data structures} *)
|
||||
|
||||
type information = string list [@@deriving yojson_of]
|
||||
type information = string list
|
||||
(** Represents information about a name in the code -- i.e. variable name,
|
||||
subscope name, etc...
|
||||
|
||||
@ -188,7 +185,6 @@ type event =
|
||||
inputs : var_def list;
|
||||
body : event list;
|
||||
}
|
||||
[@@deriving yojson_of]
|
||||
|
||||
and var_def = {
|
||||
pos : source_position option;
|
||||
@ -230,6 +226,16 @@ val log_decision_taken : source_position -> bool -> bool
|
||||
|
||||
(** {3 Pretty printers} *)
|
||||
|
||||
(** {4 Conversions to JSON} *)
|
||||
module Json : sig
|
||||
(* val io_input: io_input -> string *)
|
||||
val io_log : io_log -> string
|
||||
val runtime_value : runtime_value -> string
|
||||
|
||||
(* val information: information -> string *)
|
||||
val event : event -> string
|
||||
end
|
||||
|
||||
val pp_events : ?is_first_call:bool -> Format.formatter -> event list -> unit
|
||||
(** [pp_events ~is_first_call ppf events] pretty prints in [ppf] the string
|
||||
representation of [events].
|
||||
|
Loading…
Reference in New Issue
Block a user