2022-03-04 20:32:39 +03:00
|
|
|
(* This file is part of the Catala compiler, a specification language for tax
|
2022-07-21 17:18:36 +03:00
|
|
|
and social benefits computation rules. Copyright (C) 2020 Inria,
|
|
|
|
contributors: Emile Rolley <emile.rolley@tuta.io>, Louis Gesbert
|
|
|
|
<louis.gesbert@inria.fr>.
|
2022-03-04 20:32:39 +03:00
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
|
|
use this file except in compliance with the License. You may obtain a copy of
|
|
|
|
the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
|
|
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
|
License for the specific language governing permissions and limitations under
|
|
|
|
the License. *)
|
|
|
|
|
2022-07-29 14:40:43 +03:00
|
|
|
(** Catala plugin for generating web APIs. It generates OCaml code before the
|
|
|
|
the associated [js_of_ocaml] wrapper. *)
|
2022-03-04 20:32:39 +03:00
|
|
|
|
2022-11-21 12:46:17 +03:00
|
|
|
open Catala_utils
|
2022-08-12 23:42:39 +03:00
|
|
|
open Shared_ast
|
2022-07-19 18:59:45 +03:00
|
|
|
open Lcalc
|
2022-07-18 20:19:56 +03:00
|
|
|
open Lcalc.Ast
|
2022-07-19 18:59:45 +03:00
|
|
|
open Lcalc.To_ocaml
|
2022-07-18 20:19:56 +03:00
|
|
|
module D = Dcalc.Ast
|
|
|
|
|
2022-07-29 14:39:33 +03:00
|
|
|
let name = "api_web"
|
2022-07-22 13:34:46 +03:00
|
|
|
let extension = ".ml"
|
2022-07-20 15:20:21 +03:00
|
|
|
|
2022-07-27 17:09:37 +03:00
|
|
|
(** Contains all format functions used to generating the [js_of_ocaml] wrapper
|
|
|
|
of the corresponding Catala program. *)
|
2022-07-18 20:19:56 +03:00
|
|
|
module To_jsoo = struct
|
2022-07-29 14:39:33 +03:00
|
|
|
let to_camel_case (s : string) : string =
|
|
|
|
String.split_on_char '_' s
|
|
|
|
|> (function
|
|
|
|
| hd :: tl -> hd :: List.map String.capitalize_ascii tl | l -> l)
|
|
|
|
|> String.concat ""
|
|
|
|
|
|
|
|
let format_struct_field_name_camel_case
|
|
|
|
(fmt : Format.formatter)
|
2022-11-21 12:12:45 +03:00
|
|
|
(v : StructField.t) : unit =
|
2022-07-29 14:39:33 +03:00
|
|
|
let s =
|
2022-11-21 12:12:45 +03:00
|
|
|
Format.asprintf "%a" StructField.format_t v
|
2022-11-21 13:17:42 +03:00
|
|
|
|> String.to_ascii
|
|
|
|
|> String.to_snake_case
|
2022-08-03 18:02:13 +03:00
|
|
|
|> avoid_keywords
|
|
|
|
|> to_camel_case
|
2022-07-29 14:39:33 +03:00
|
|
|
in
|
|
|
|
Format.fprintf fmt "%s" s
|
|
|
|
|
2022-08-12 23:42:39 +03:00
|
|
|
let format_tlit (fmt : Format.formatter) (l : typ_lit) : unit =
|
2022-08-17 17:14:14 +03:00
|
|
|
Print.base_type fmt
|
2022-07-19 18:59:45 +03:00
|
|
|
(match l with
|
2022-07-22 20:26:56 +03:00
|
|
|
| TUnit -> "unit"
|
2022-07-19 18:59:45 +03:00
|
|
|
| TInt -> "int"
|
2022-07-27 17:09:37 +03:00
|
|
|
| TRat | TMoney -> "Js.number Js.t"
|
2022-07-22 20:26:56 +03:00
|
|
|
| TDuration -> "Runtime_jsoo.Runtime.duration Js.t"
|
2022-07-19 18:59:45 +03:00
|
|
|
| TBool -> "bool Js.t"
|
2022-07-26 18:02:00 +03:00
|
|
|
| TDate -> "Js.js_string Js.t")
|
2022-07-18 20:19:56 +03:00
|
|
|
|
2022-08-25 18:29:00 +03:00
|
|
|
let rec format_typ (fmt : Format.formatter) (typ : typ) : unit =
|
|
|
|
let format_typ_with_parens (fmt : Format.formatter) (t : typ) =
|
2022-07-18 20:19:56 +03:00
|
|
|
if typ_needs_parens t then Format.fprintf fmt "(%a)" format_typ t
|
|
|
|
else Format.fprintf fmt "%a" format_typ t
|
|
|
|
in
|
2023-05-17 16:44:57 +03:00
|
|
|
match Mark.remove typ with
|
2022-07-18 20:19:56 +03:00
|
|
|
| TLit l -> Format.fprintf fmt "%a" format_tlit l
|
2022-08-23 16:23:52 +03:00
|
|
|
| TStruct s -> Format.fprintf fmt "%a Js.t" format_struct_name s
|
|
|
|
| TTuple _ ->
|
2022-07-18 20:19:56 +03:00
|
|
|
(* Tuples are encoded as an javascript polymorphic array. *)
|
|
|
|
Format.fprintf fmt "Js.Unsafe.any_js_array Js.t "
|
2022-08-23 16:23:52 +03:00
|
|
|
| TOption t ->
|
2022-07-18 20:19:56 +03:00
|
|
|
Format.fprintf fmt "@[<hov 2>(%a)@] %a" format_typ_with_parens t
|
2023-05-17 14:26:47 +03:00
|
|
|
format_enum_name Expr.option_enum
|
2022-08-23 16:23:52 +03:00
|
|
|
| TEnum e -> Format.fprintf fmt "%a Js.t" format_enum_name e
|
2022-07-18 20:19:56 +03:00
|
|
|
| TArray t1 ->
|
|
|
|
Format.fprintf fmt "@[%a@ Js.js_array Js.t@]" format_typ_with_parens t1
|
|
|
|
| TAny -> Format.fprintf fmt "Js.Unsafe.any Js.t"
|
|
|
|
| TArrow (t1, t2) ->
|
2023-02-20 19:21:44 +03:00
|
|
|
Format.fprintf fmt "(@[<hov 2>unit, @ %a -> %a@]) Js.meth_callback"
|
|
|
|
(Format.pp_print_list
|
|
|
|
~pp_sep:(fun fmt () -> Format.pp_print_string fmt " -> ")
|
|
|
|
format_typ_with_parens)
|
|
|
|
t1 format_typ_with_parens t2
|
2022-07-21 16:52:35 +03:00
|
|
|
|
|
|
|
let rec format_typ_to_jsoo fmt typ =
|
2023-05-17 16:44:57 +03:00
|
|
|
match Mark.remove typ with
|
2022-08-12 23:42:39 +03:00
|
|
|
| TLit TBool -> Format.fprintf fmt "Js.bool"
|
|
|
|
| TLit TInt -> Format.fprintf fmt "integer_to_int"
|
2022-07-23 19:00:04 +03:00
|
|
|
| TLit TRat -> Format.fprintf fmt "Js.number_of_float %@%@ decimal_to_float"
|
|
|
|
| TLit TMoney -> Format.fprintf fmt "Js.number_of_float %@%@ money_to_float"
|
2022-08-12 23:42:39 +03:00
|
|
|
| TLit TDuration -> Format.fprintf fmt "duration_to_jsoo"
|
|
|
|
| TLit TDate -> Format.fprintf fmt "date_to_jsoo"
|
2022-08-23 16:23:52 +03:00
|
|
|
| TEnum ename -> Format.fprintf fmt "%a_to_jsoo" format_enum_name ename
|
|
|
|
| TStruct sname -> Format.fprintf fmt "%a_to_jsoo" format_struct_name sname
|
2022-08-12 23:42:39 +03:00
|
|
|
| TArray t ->
|
2022-07-23 19:00:04 +03:00
|
|
|
Format.fprintf fmt "Js.array %@%@ Array.map (fun x -> %a x)"
|
|
|
|
format_typ_to_jsoo t
|
2022-08-23 16:23:52 +03:00
|
|
|
| TAny | TTuple _ -> Format.fprintf fmt "Js.Unsafe.inject"
|
2022-07-21 16:52:35 +03:00
|
|
|
| _ -> Format.fprintf fmt ""
|
|
|
|
|
|
|
|
let rec format_typ_of_jsoo fmt typ =
|
2023-05-17 16:44:57 +03:00
|
|
|
match Mark.remove typ with
|
2022-08-12 23:42:39 +03:00
|
|
|
| TLit TBool -> Format.fprintf fmt "Js.to_bool"
|
|
|
|
| TLit TInt -> Format.fprintf fmt "integer_of_int"
|
2022-07-23 19:00:04 +03:00
|
|
|
| TLit TRat -> Format.fprintf fmt "decimal_of_float %@%@ Js.float_of_number"
|
2022-08-12 23:42:39 +03:00
|
|
|
| TLit TMoney ->
|
2022-07-23 19:00:04 +03:00
|
|
|
Format.fprintf fmt
|
|
|
|
"money_of_decimal %@%@ decimal_of_float %@%@ Js.float_of_number"
|
2022-08-12 23:42:39 +03:00
|
|
|
| TLit TDuration -> Format.fprintf fmt "duration_of_jsoo"
|
|
|
|
| TLit TDate -> Format.fprintf fmt "date_of_jsoo"
|
2022-08-23 16:23:52 +03:00
|
|
|
| TEnum ename -> Format.fprintf fmt "%a_of_jsoo" format_enum_name ename
|
|
|
|
| TStruct sname -> Format.fprintf fmt "%a_of_jsoo" format_struct_name sname
|
2022-08-12 23:42:39 +03:00
|
|
|
| TArray t ->
|
2022-07-23 19:00:04 +03:00
|
|
|
Format.fprintf fmt "Array.map (fun x -> %a x) %@%@ Js.to_array"
|
|
|
|
format_typ_of_jsoo t
|
2022-07-21 16:52:35 +03:00
|
|
|
| _ -> Format.fprintf fmt ""
|
2022-07-18 20:19:56 +03:00
|
|
|
|
2022-08-22 19:53:30 +03:00
|
|
|
let format_var_camel_case (fmt : Format.formatter) (v : 'm Var.t) : unit =
|
2022-07-18 20:19:56 +03:00
|
|
|
let lowercase_name =
|
2022-08-03 18:02:13 +03:00
|
|
|
Bindlib.name_of v
|
2022-11-21 13:17:42 +03:00
|
|
|
|> String.to_ascii
|
|
|
|
|> String.to_snake_case
|
2022-07-19 18:59:45 +03:00
|
|
|
|> Re.Pcre.substitute ~rex:(Re.Pcre.regexp "\\.") ~subst:(fun _ ->
|
|
|
|
"_dot_")
|
2022-11-21 13:17:42 +03:00
|
|
|
|> String.to_ascii
|
2022-07-19 18:59:45 +03:00
|
|
|
|> avoid_keywords
|
|
|
|
|> to_camel_case
|
2022-07-18 20:19:56 +03:00
|
|
|
in
|
|
|
|
if
|
|
|
|
List.mem lowercase_name ["handle_default"; "handle_default_opt"]
|
2022-11-21 13:17:42 +03:00
|
|
|
|| String.begins_with_uppercase (Bindlib.name_of v)
|
2022-07-18 20:19:56 +03:00
|
|
|
then Format.fprintf fmt "%s" lowercase_name
|
|
|
|
else if lowercase_name = "_" then Format.fprintf fmt "%s" lowercase_name
|
|
|
|
else Format.fprintf fmt "%s_" lowercase_name
|
|
|
|
|
|
|
|
let format_ctx
|
|
|
|
(type_ordering : Scopelang.Dependency.TVertex.t list)
|
|
|
|
(fmt : Format.formatter)
|
2022-08-12 23:42:39 +03:00
|
|
|
(ctx : decl_ctx) : unit =
|
2022-08-25 18:29:00 +03:00
|
|
|
let format_prop_or_meth fmt (struct_field_type : typ) =
|
2023-05-17 16:44:57 +03:00
|
|
|
match Mark.remove struct_field_type with
|
2022-08-12 23:42:39 +03:00
|
|
|
| TArrow _ -> Format.fprintf fmt "Js.meth"
|
2022-07-18 20:19:56 +03:00
|
|
|
| _ -> Format.fprintf fmt "Js.readonly_prop"
|
|
|
|
in
|
|
|
|
let format_struct_decl fmt (struct_name, struct_fields) =
|
2022-07-20 19:25:41 +03:00
|
|
|
let fmt_struct_name fmt _ = format_struct_name fmt struct_name in
|
|
|
|
let fmt_module_struct_name fmt _ =
|
|
|
|
To_ocaml.format_to_module_name fmt (`Sname struct_name)
|
|
|
|
in
|
2022-07-21 16:52:35 +03:00
|
|
|
let fmt_to_jsoo fmt _ =
|
|
|
|
Format.fprintf fmt "%a"
|
|
|
|
(Format.pp_print_list
|
|
|
|
~pp_sep:(fun fmt () -> Format.fprintf fmt "@\n")
|
|
|
|
(fun fmt (struct_field, struct_field_type) ->
|
2023-05-17 16:44:57 +03:00
|
|
|
match Mark.remove struct_field_type with
|
2022-08-12 23:42:39 +03:00
|
|
|
| TArrow (t1, t2) ->
|
2023-02-20 19:21:44 +03:00
|
|
|
let args_names =
|
|
|
|
ListLabels.mapi t1 ~f:(fun i _ ->
|
|
|
|
"function_input" ^ string_of_int i)
|
|
|
|
in
|
2022-07-21 16:52:35 +03:00
|
|
|
Format.fprintf fmt
|
2022-08-04 17:42:50 +03:00
|
|
|
"@[<hov 2>method %a =@ Js.wrap_meth_callback@ @[<hv 2>(@,\
|
2023-02-20 19:21:44 +03:00
|
|
|
fun _ %a ->@ %a (%a.%a %a))@]@]"
|
2023-02-21 18:19:17 +03:00
|
|
|
format_struct_field_name_camel_case struct_field
|
|
|
|
(Format.pp_print_list (fun fmt (arg_i, ti) ->
|
|
|
|
Format.fprintf fmt "(%s: %a)" arg_i format_typ ti))
|
|
|
|
(List.combine args_names t1)
|
2022-07-21 16:52:35 +03:00
|
|
|
format_typ_to_jsoo t2 fmt_struct_name ()
|
|
|
|
format_struct_field_name (None, struct_field)
|
2023-02-20 19:21:44 +03:00
|
|
|
(Format.pp_print_list (fun fmt (i, ti) ->
|
|
|
|
Format.fprintf fmt "@[<hv 2>(%a@ %a)@]"
|
2023-02-21 18:19:17 +03:00
|
|
|
format_typ_of_jsoo ti Format.pp_print_string i))
|
2023-02-20 19:21:44 +03:00
|
|
|
(List.combine args_names t1)
|
2022-07-21 16:52:35 +03:00
|
|
|
| _ ->
|
2022-08-04 17:42:50 +03:00
|
|
|
Format.fprintf fmt "@[<hov 2>val %a =@ %a %a.%a@]"
|
2022-07-21 16:52:35 +03:00
|
|
|
format_struct_field_name_camel_case struct_field
|
|
|
|
format_typ_to_jsoo struct_field_type fmt_struct_name ()
|
|
|
|
format_struct_field_name (None, struct_field)))
|
2022-11-21 12:12:45 +03:00
|
|
|
(StructField.Map.bindings struct_fields)
|
2022-07-21 16:52:35 +03:00
|
|
|
in
|
|
|
|
let fmt_of_jsoo fmt _ =
|
|
|
|
Format.fprintf fmt "%a"
|
|
|
|
(Format.pp_print_list
|
|
|
|
~pp_sep:(fun fmt () -> Format.fprintf fmt ";@\n")
|
|
|
|
(fun fmt (struct_field, struct_field_type) ->
|
2023-05-17 16:44:57 +03:00
|
|
|
match Mark.remove struct_field_type with
|
2022-08-12 23:42:39 +03:00
|
|
|
| TArrow _ ->
|
2022-07-21 16:52:35 +03:00
|
|
|
Format.fprintf fmt
|
2022-07-21 17:18:36 +03:00
|
|
|
"%a = failwith \"The function '%a' translation isn't yet \
|
|
|
|
supported...\""
|
2022-07-21 16:52:35 +03:00
|
|
|
format_struct_field_name (None, struct_field)
|
|
|
|
format_struct_field_name (None, struct_field)
|
|
|
|
| _ ->
|
2022-08-04 17:42:50 +03:00
|
|
|
Format.fprintf fmt
|
|
|
|
"@[<hv 2>%a =@ @[<hov 2>%a@ @[<hov>%a@,##.%a@]@]@]"
|
|
|
|
format_struct_field_name (None, struct_field)
|
|
|
|
format_typ_of_jsoo struct_field_type fmt_struct_name ()
|
|
|
|
format_struct_field_name_camel_case struct_field))
|
2022-11-21 12:12:45 +03:00
|
|
|
(StructField.Map.bindings struct_fields)
|
2022-07-21 16:52:35 +03:00
|
|
|
in
|
|
|
|
let fmt_conv_funs fmt _ =
|
|
|
|
Format.fprintf fmt
|
2022-08-04 17:42:50 +03:00
|
|
|
"@[<hov 2>let %a_to_jsoo@ (%a@ : %a.t)@ : %a Js.t =@ @[<hv \
|
|
|
|
2>object%%js@\n\
|
|
|
|
%a@\n\
|
|
|
|
@]@]end@\n\
|
|
|
|
@[<hov 2>let %a_of_jsoo@ @[<hov 2>(%a@ : %a Js.t)@] :@ %a.t =@ \
|
|
|
|
@[<hv 2>{@,\
|
|
|
|
%a@]@\n\
|
|
|
|
}@]"
|
2022-07-21 16:52:35 +03:00
|
|
|
fmt_struct_name () fmt_struct_name () fmt_module_struct_name ()
|
|
|
|
fmt_struct_name () fmt_to_jsoo () fmt_struct_name () fmt_struct_name
|
|
|
|
() fmt_struct_name () fmt_module_struct_name () fmt_of_jsoo ()
|
|
|
|
in
|
|
|
|
|
2022-11-21 12:12:45 +03:00
|
|
|
if StructField.Map.is_empty struct_fields then
|
2022-07-20 19:25:41 +03:00
|
|
|
Format.fprintf fmt
|
|
|
|
"class type %a =@ object end@\n\
|
2022-07-21 16:52:35 +03:00
|
|
|
let %a_to_jsoo (_ : %a.t) : %a Js.t = object%%js end@\n\
|
2022-07-20 19:25:41 +03:00
|
|
|
let %a_of_jsoo (_ : %a Js.t) : %a.t = ()" fmt_struct_name ()
|
|
|
|
fmt_struct_name () fmt_module_struct_name () fmt_struct_name ()
|
|
|
|
fmt_struct_name () fmt_struct_name () fmt_module_struct_name ()
|
2022-07-18 20:19:56 +03:00
|
|
|
else
|
|
|
|
Format.fprintf fmt
|
2022-08-04 17:42:50 +03:00
|
|
|
"@[<hv 2>class type %a =@ @[<hov 2>object@ %a@]@,end@\n%a@]@\n"
|
2022-07-20 19:25:41 +03:00
|
|
|
fmt_struct_name ()
|
2022-07-18 20:19:56 +03:00
|
|
|
(Format.pp_print_list
|
|
|
|
~pp_sep:(fun fmt () -> Format.fprintf fmt "@\n")
|
2022-07-21 17:18:36 +03:00
|
|
|
(fun fmt (struct_field, struct_field_type) ->
|
2022-08-04 17:42:50 +03:00
|
|
|
Format.fprintf fmt "@[<hov 2>method %a:@ %a %a@]"
|
2022-07-21 17:18:36 +03:00
|
|
|
format_struct_field_name_camel_case struct_field format_typ
|
|
|
|
struct_field_type format_prop_or_meth struct_field_type))
|
2022-11-21 12:12:45 +03:00
|
|
|
(StructField.Map.bindings struct_fields)
|
2022-11-17 19:13:35 +03:00
|
|
|
fmt_conv_funs ()
|
2022-07-18 20:19:56 +03:00
|
|
|
in
|
2022-11-21 12:12:45 +03:00
|
|
|
let format_enum_decl fmt (enum_name, (enum_cons : typ EnumConstructor.Map.t))
|
2022-11-17 19:13:35 +03:00
|
|
|
=
|
2022-07-21 16:52:35 +03:00
|
|
|
let fmt_enum_name fmt _ = format_enum_name fmt enum_name in
|
|
|
|
let fmt_module_enum_name fmt _ =
|
|
|
|
To_ocaml.format_to_module_name fmt (`Ename enum_name)
|
|
|
|
in
|
|
|
|
let fmt_to_jsoo fmt _ =
|
|
|
|
Format.fprintf fmt "%a"
|
2022-07-18 20:19:56 +03:00
|
|
|
(Format.pp_print_list
|
|
|
|
~pp_sep:(fun fmt () -> Format.fprintf fmt "@\n")
|
2022-07-21 16:52:35 +03:00
|
|
|
(fun fmt (cname, typ) ->
|
2023-05-17 16:44:57 +03:00
|
|
|
match Mark.remove typ with
|
2022-08-23 16:23:52 +03:00
|
|
|
| TTuple _ ->
|
2022-07-21 17:18:36 +03:00
|
|
|
Cli.error_print
|
|
|
|
"Tuples aren't supported yet in the conversion to JS"
|
|
|
|
| _ ->
|
2022-07-21 16:52:35 +03:00
|
|
|
Format.fprintf fmt
|
2022-08-04 17:42:50 +03:00
|
|
|
"@[<v 2>@[<v 4>| %a arg -> object%%js@\n\
|
2022-07-21 16:52:35 +03:00
|
|
|
val kind = Js.string \"%a\"@\n\
|
2022-07-21 17:18:36 +03:00
|
|
|
val payload = Js.Unsafe.coerce (Js.Unsafe.inject (%a \
|
|
|
|
arg))@]@\n\
|
2022-08-04 17:42:50 +03:00
|
|
|
end@]"
|
2022-07-21 16:52:35 +03:00
|
|
|
format_enum_cons_name cname format_enum_cons_name cname
|
2022-07-21 17:18:36 +03:00
|
|
|
format_typ_to_jsoo typ))
|
2022-11-21 12:12:45 +03:00
|
|
|
(EnumConstructor.Map.bindings enum_cons)
|
2022-07-21 16:52:35 +03:00
|
|
|
in
|
|
|
|
let fmt_of_jsoo fmt _ =
|
|
|
|
Format.fprintf fmt
|
2022-08-04 17:42:50 +03:00
|
|
|
"@[<hov 2>match@ %a##.kind@ |> Js.to_string@ with@]@\n\
|
|
|
|
@[<hv>%a@\n\
|
|
|
|
@[<hv 2>| cons ->@ @[<hov 2>failwith@ @[<hov 2>(Printf.sprintf@ \
|
|
|
|
\"Unexpected '%%s' kind for the enumeration '%a.t'\"@ cons)@]@]@]@]"
|
2022-07-21 16:52:35 +03:00
|
|
|
fmt_enum_name ()
|
|
|
|
(Format.pp_print_list
|
|
|
|
~pp_sep:(fun fmt () -> Format.fprintf fmt "@\n")
|
|
|
|
(fun fmt (cname, typ) ->
|
2023-05-17 16:44:57 +03:00
|
|
|
match Mark.remove typ with
|
2022-08-23 16:23:52 +03:00
|
|
|
| TTuple _ ->
|
2022-07-21 17:18:36 +03:00
|
|
|
Cli.error_print
|
|
|
|
"Tuples aren't yet supported in the conversion to JS..."
|
2022-08-12 23:42:39 +03:00
|
|
|
| TLit TUnit ->
|
2022-08-04 17:42:50 +03:00
|
|
|
Format.fprintf fmt "@[<hv 2>| \"%a\" ->@ %a.%a ()@]"
|
2022-07-21 17:18:36 +03:00
|
|
|
format_enum_cons_name cname fmt_module_enum_name ()
|
|
|
|
format_enum_cons_name cname
|
|
|
|
| _ ->
|
2022-07-21 16:52:35 +03:00
|
|
|
Format.fprintf fmt
|
2022-07-23 19:00:04 +03:00
|
|
|
"| \"%a\" ->@\n%a.%a (%a (Js.Unsafe.coerce %a##.payload))"
|
2022-07-21 17:18:36 +03:00
|
|
|
format_enum_cons_name cname fmt_module_enum_name ()
|
2022-07-22 20:02:09 +03:00
|
|
|
format_enum_cons_name cname format_typ_of_jsoo typ
|
2022-07-21 17:18:36 +03:00
|
|
|
fmt_enum_name ()))
|
2022-11-21 12:12:45 +03:00
|
|
|
(EnumConstructor.Map.bindings enum_cons)
|
2022-11-17 19:13:35 +03:00
|
|
|
fmt_module_enum_name ()
|
2022-07-21 16:52:35 +03:00
|
|
|
in
|
|
|
|
|
|
|
|
let fmt_conv_funs fmt _ =
|
|
|
|
Format.fprintf fmt
|
2022-08-04 17:42:50 +03:00
|
|
|
"@[<hov 2>let %a_to_jsoo@ : %a.t -> %a Js.t@ = function@\n\
|
|
|
|
%a@]@\n\
|
|
|
|
@\n\
|
|
|
|
@[<hov 2>let %a_of_jsoo@ @[<hov 2>(%a@ : %a Js.t)@]@ : %a.t =@ %a@]@\n"
|
|
|
|
fmt_enum_name () fmt_module_enum_name () fmt_enum_name () fmt_to_jsoo
|
|
|
|
() fmt_enum_name () fmt_enum_name () fmt_enum_name ()
|
2022-07-21 16:52:35 +03:00
|
|
|
fmt_module_enum_name () fmt_of_jsoo ()
|
|
|
|
in
|
|
|
|
Format.fprintf fmt
|
2022-08-04 17:42:50 +03:00
|
|
|
"@[<v 2>class type %a =@ @[<v 2>object@ @[<hov 2>method kind :@ \
|
|
|
|
Js.js_string Js.t Js.readonly_prop@\n\
|
2022-07-21 16:52:35 +03:00
|
|
|
@[<v 2>(** Expects one of:@\n\
|
2022-08-04 17:42:50 +03:00
|
|
|
%a *)@]@]@\n\
|
2022-07-21 16:52:35 +03:00
|
|
|
@\n\
|
2022-08-04 17:42:50 +03:00
|
|
|
@[<hov 2>method payload :@ Js.Unsafe.any Js.t Js.readonly_prop@]@]@\n\
|
2022-07-21 16:52:35 +03:00
|
|
|
end@]@\n\
|
2022-08-04 17:42:50 +03:00
|
|
|
@\n\
|
2022-07-21 16:52:35 +03:00
|
|
|
%a@\n"
|
|
|
|
format_enum_name enum_name
|
|
|
|
(Format.pp_print_list
|
|
|
|
~pp_sep:(fun fmt () -> Format.fprintf fmt "@\n")
|
2022-07-21 17:18:36 +03:00
|
|
|
(fun fmt (enum_cons, _) ->
|
2022-07-21 16:52:35 +03:00
|
|
|
Format.fprintf fmt "- \"%a\"" format_enum_cons_name enum_cons))
|
2022-11-21 12:12:45 +03:00
|
|
|
(EnumConstructor.Map.bindings enum_cons)
|
2022-11-17 19:13:35 +03:00
|
|
|
fmt_conv_funs ()
|
2022-07-18 20:19:56 +03:00
|
|
|
in
|
|
|
|
let is_in_type_ordering s =
|
|
|
|
List.exists
|
|
|
|
(fun struct_or_enum ->
|
|
|
|
match struct_or_enum with
|
|
|
|
| Scopelang.Dependency.TVertex.Enum _ -> false
|
|
|
|
| Scopelang.Dependency.TVertex.Struct s' -> s = s')
|
|
|
|
type_ordering
|
|
|
|
in
|
|
|
|
let scope_structs =
|
|
|
|
List.map
|
|
|
|
(fun (s, _) -> Scopelang.Dependency.TVertex.Struct s)
|
2022-11-21 12:12:45 +03:00
|
|
|
(StructName.Map.bindings
|
|
|
|
(StructName.Map.filter
|
2022-07-18 20:19:56 +03:00
|
|
|
(fun s _ -> not (is_in_type_ordering s))
|
|
|
|
ctx.ctx_structs))
|
|
|
|
in
|
|
|
|
List.iter
|
|
|
|
(fun struct_or_enum ->
|
|
|
|
match struct_or_enum with
|
|
|
|
| Scopelang.Dependency.TVertex.Struct s ->
|
|
|
|
Format.fprintf fmt "%a@\n" format_struct_decl (s, find_struct s ctx)
|
|
|
|
| Scopelang.Dependency.TVertex.Enum e ->
|
|
|
|
Format.fprintf fmt "%a@\n" format_enum_decl (e, find_enum e ctx))
|
|
|
|
(type_ordering @ scope_structs)
|
|
|
|
|
2023-01-23 14:19:36 +03:00
|
|
|
let fmt_input_struct_name fmt (scope_body : 'a expr scope_body) =
|
|
|
|
format_struct_name fmt scope_body.scope_body_input_struct
|
2022-07-21 17:18:36 +03:00
|
|
|
|
2023-01-23 14:19:36 +03:00
|
|
|
let fmt_output_struct_name fmt (scope_body : 'a expr scope_body) =
|
|
|
|
format_struct_name fmt scope_body.scope_body_output_struct
|
2022-07-21 17:18:36 +03:00
|
|
|
|
2023-01-23 14:19:36 +03:00
|
|
|
let format_scopes_to_fun
|
2023-02-13 17:00:23 +03:00
|
|
|
(_ctx : decl_ctx)
|
2022-07-19 18:59:45 +03:00
|
|
|
(fmt : Format.formatter)
|
2023-01-23 14:19:36 +03:00
|
|
|
(scopes : 'e code_item_list) =
|
|
|
|
Scope.fold_left
|
|
|
|
~f:(fun () code_item var ->
|
|
|
|
match code_item with
|
|
|
|
| Topdef _ -> ()
|
2023-02-13 17:00:23 +03:00
|
|
|
| ScopeDef (_name, body) ->
|
2023-01-23 14:19:36 +03:00
|
|
|
let fmt_fun_call fmt _ =
|
|
|
|
Format.fprintf fmt "@[<hv>%a@ |> %a_of_jsoo@ |> %a@ |> %a_to_jsoo@]"
|
|
|
|
fmt_input_struct_name body fmt_input_struct_name body format_var
|
|
|
|
var fmt_output_struct_name body
|
|
|
|
in
|
|
|
|
Format.fprintf fmt
|
|
|
|
"@\n@\n@[<hov 2>let %a@ (%a : %a Js.t)@ : %a Js.t =@\n%a@]@\n"
|
|
|
|
format_var var fmt_input_struct_name body fmt_input_struct_name body
|
|
|
|
fmt_output_struct_name body fmt_fun_call ())
|
|
|
|
~init:() scopes
|
2022-07-21 17:18:36 +03:00
|
|
|
|
2023-01-23 14:19:36 +03:00
|
|
|
let format_scopes_to_callbacks
|
2023-02-13 17:00:23 +03:00
|
|
|
(_ctx : decl_ctx)
|
2022-07-21 17:18:36 +03:00
|
|
|
(fmt : Format.formatter)
|
2023-01-23 14:19:36 +03:00
|
|
|
(scopes : 'e code_item_list) : unit =
|
|
|
|
Scope.fold_left
|
|
|
|
~f:(fun () code_item var ->
|
|
|
|
match code_item with
|
|
|
|
| Topdef _ -> ()
|
2023-02-13 17:00:23 +03:00
|
|
|
| ScopeDef (_name, body) ->
|
2023-01-23 14:19:36 +03:00
|
|
|
let fmt_meth_name fmt _ =
|
|
|
|
Format.fprintf fmt "method %a : (%a Js.t -> %a Js.t) Js.callback"
|
|
|
|
format_var_camel_case var fmt_input_struct_name body
|
|
|
|
fmt_output_struct_name body
|
|
|
|
in
|
|
|
|
Format.fprintf fmt "@,@[<hov 2>%a =@ Js.wrap_callback@ %a@]@,"
|
|
|
|
fmt_meth_name () format_var var)
|
|
|
|
~init:() scopes
|
2022-07-19 18:59:45 +03:00
|
|
|
|
2022-07-18 20:19:56 +03:00
|
|
|
let format_program
|
|
|
|
(fmt : Format.formatter)
|
2022-07-29 14:39:33 +03:00
|
|
|
(module_name : string option)
|
2022-07-20 13:00:33 +03:00
|
|
|
(prgm : 'm Lcalc.Ast.program)
|
2022-07-18 20:19:56 +03:00
|
|
|
(type_ordering : Scopelang.Dependency.TVertex.t list) =
|
2022-07-21 17:18:36 +03:00
|
|
|
let fmt_lib_name fmt _ =
|
|
|
|
Format.fprintf fmt "%sLib"
|
2022-07-29 14:39:33 +03:00
|
|
|
(Option.fold ~none:""
|
|
|
|
~some:(fun name ->
|
|
|
|
List.nth (String.split_on_char ' ' name) 1
|
|
|
|
|> String.split_on_char '_'
|
|
|
|
|> List.map String.capitalize_ascii
|
|
|
|
|> String.concat "")
|
|
|
|
module_name)
|
2022-07-21 17:18:36 +03:00
|
|
|
in
|
|
|
|
|
2022-07-20 17:51:28 +03:00
|
|
|
Cli.call_unstyled (fun _ ->
|
|
|
|
Format.fprintf fmt
|
|
|
|
"(** This file has been generated by the Catala compiler, do not \
|
|
|
|
edit! *)@\n\
|
|
|
|
@\n\
|
|
|
|
open Runtime_ocaml.Runtime@\n\
|
|
|
|
open Runtime_jsoo.Runtime@\n\
|
|
|
|
open Js_of_ocaml@\n\
|
|
|
|
%s@\n\
|
|
|
|
@\n\
|
|
|
|
[@@@@@@ocaml.warning \"-4-26-27-32-41-42\"]@\n\
|
|
|
|
@\n\
|
2022-08-04 17:42:50 +03:00
|
|
|
(* Generated API *)@\n\
|
|
|
|
@\n\
|
2022-07-20 17:51:28 +03:00
|
|
|
%a@\n\
|
2022-07-21 17:18:36 +03:00
|
|
|
%a@\n\
|
2022-08-04 17:42:50 +03:00
|
|
|
@\n\
|
|
|
|
@[<v 2>let _ =@ @[<hov 2> Js.export \"%a\"@\n\
|
|
|
|
@[<v 2>(object%%js@ %a@]@\n\
|
|
|
|
end)@]@]@?"
|
2022-07-29 14:39:33 +03:00
|
|
|
(Option.fold ~none:"" ~some:(fun name -> name) module_name)
|
|
|
|
(format_ctx type_ordering) prgm.decl_ctx
|
2022-07-21 17:18:36 +03:00
|
|
|
(format_scopes_to_fun prgm.decl_ctx)
|
2023-02-13 17:00:23 +03:00
|
|
|
prgm.code_items fmt_lib_name ()
|
2022-07-21 17:18:36 +03:00
|
|
|
(format_scopes_to_callbacks prgm.decl_ctx)
|
2023-02-13 17:00:23 +03:00
|
|
|
prgm.code_items)
|
2022-07-18 20:19:56 +03:00
|
|
|
end
|
|
|
|
|
|
|
|
let apply
|
2022-07-29 14:39:33 +03:00
|
|
|
~(source_file : Pos.input_file)
|
2022-07-26 18:27:42 +03:00
|
|
|
~(output_file : string option)
|
2022-07-29 14:39:33 +03:00
|
|
|
~scope
|
2022-07-20 13:00:33 +03:00
|
|
|
(prgm : 'm Lcalc.Ast.program)
|
2022-07-18 20:19:56 +03:00
|
|
|
(type_ordering : Scopelang.Dependency.TVertex.t list) =
|
2022-07-29 14:39:33 +03:00
|
|
|
ignore scope;
|
2022-07-29 14:40:43 +03:00
|
|
|
File.with_formatter_of_opt_file output_file (fun fmt ->
|
2022-07-27 17:09:37 +03:00
|
|
|
Cli.trace_flag := true;
|
2022-07-29 14:39:33 +03:00
|
|
|
Cli.debug_print "Writing OCaml code to %s..."
|
|
|
|
(Option.value ~default:"stdout" output_file);
|
2022-08-04 17:15:48 +03:00
|
|
|
To_ocaml.format_program fmt prgm type_ordering);
|
2022-07-29 14:39:33 +03:00
|
|
|
|
2022-07-29 14:40:43 +03:00
|
|
|
let output_file, filename_without_ext =
|
2022-07-29 14:39:33 +03:00
|
|
|
match output_file with
|
2022-07-29 14:40:43 +03:00
|
|
|
| Some "-" -> output_file, output_file
|
|
|
|
| Some f ->
|
|
|
|
output_file, Some (Filename.basename f |> Filename.remove_extension)
|
|
|
|
| None -> Some "-", None
|
2022-07-27 17:09:37 +03:00
|
|
|
in
|
2022-07-29 14:39:33 +03:00
|
|
|
let jsoo_output_file, with_formatter =
|
|
|
|
File.get_formatter_of_out_channel ~source_file
|
|
|
|
~output_file:
|
|
|
|
(Option.map
|
|
|
|
(fun name ->
|
|
|
|
if "-" = name then "-"
|
|
|
|
else Filename.remove_extension name ^ "_api_web.ml")
|
|
|
|
output_file)
|
|
|
|
~ext:"_api_web.ml" ()
|
|
|
|
in
|
|
|
|
let module_name =
|
2022-07-27 17:09:37 +03:00
|
|
|
Option.map
|
2022-07-29 14:39:33 +03:00
|
|
|
(fun name -> Printf.sprintf "open %s" (String.capitalize_ascii name))
|
|
|
|
filename_without_ext
|
2022-07-27 17:09:37 +03:00
|
|
|
in
|
2022-07-29 14:39:33 +03:00
|
|
|
with_formatter (fun fmt ->
|
2022-07-27 17:09:37 +03:00
|
|
|
Cli.debug_print "Writing JSOO API code to %s..."
|
2022-07-29 14:39:33 +03:00
|
|
|
(Option.value ~default:"stdout" jsoo_output_file);
|
2022-08-04 17:15:48 +03:00
|
|
|
To_jsoo.format_program fmt module_name prgm type_ordering)
|
2022-07-18 20:19:56 +03:00
|
|
|
|
2022-07-20 15:20:21 +03:00
|
|
|
let () = Driver.Plugin.register_lcalc ~name ~extension apply
|