catala
catala copied to clipboard
Use ppxlib instead of printers to generate ocaml code
Currently we use custom printers when doing code generation. This is quite error-prone since it makes it possible to generate nonsense code. For example, ocaml code is printed in two different places: to_ocaml and web_api
We could use ppxlib to build simpler code generator for ocaml. Indeed, ppxlib gives an interface for dealing with code generation.
Here is an exemple for the to_ocaml.ml file
Before:
let tlit (fmt : Format.formatter) (l : typ_lit) : unit =
base_type fmt
(match l with
| TUnit -> "unit"
| TBool -> "bool"
| TInt -> "integer"
| TRat -> "decimal"
| TMoney -> "money"
| TDuration -> "duration"
| TDate -> "date")
let rec format_typ (fmt : Format.formatter) (typ : typ) : unit =
let format_typ_with_parens (fmt : Format.formatter) (t : typ) =
if typ_needs_parens t then Format.fprintf fmt "(%a)" format_typ t
else Format.fprintf fmt "%a" format_typ t
in
match Marked.unmark typ with
| TLit l -> Format.fprintf fmt "%a" Print.tlit l
| TTuple ts ->
Format.fprintf fmt "@[<hov 2>(%a)@]"
(Format.pp_print_list
~pp_sep:(fun fmt () -> Format.fprintf fmt "@ *@ ")
format_typ_with_parens)
ts
| TStruct s -> Format.fprintf fmt "%a.t" format_to_module_name (`Sname s)
| TOption t ->
Format.fprintf fmt "@[<hov 2>(%a)@] %a" format_typ_with_parens t
format_enum_name Ast.option_enum
| TEnum e -> Format.fprintf fmt "%a.t" format_to_module_name (`Ename e)
| TArrow (t1, t2) ->
Format.fprintf fmt "@[<hov 2>%a@]"
(Format.pp_print_list
~pp_sep:(fun fmt () -> Format.fprintf fmt " ->@ ")
format_typ_with_parens)
(t1 @ [t2])
| TArray t1 -> Format.fprintf fmt "@[%a@ array@]" format_typ_with_parens t1
| TAny -> Format.fprintf fmt "_"
After
let rec typ_lit (loc : Location.t) (ty : typ_lit) : Parsetree.core_type =
match ty with
| TBool -> [%type: bool]
| TUnit -> [%type: unit]
| TInt -> [%type: integer]
| TRat -> [%type: rational]
| TMoney -> [%type: money]
| TDate -> [%type: date]
| TDuration -> [%type: duration]
let rec typ (loc : Location.t) (ty : typ) : Parsetree.core_type =
match Marked.unmark ty with
| TLit l -> typ_lit loc l
| TTuple ts -> ptyp_tuple ~loc (List.map (typ loc) ts)
| TStruct n -> ptyp_constr ~loc (struct_name loc n) []
| TEnum n -> ptyp_constr ~loc (enum_name loc n) []
| TOption t -> ptyp_constr ~loc (enum_name loc Ast.option_enum) [typ loc t]
| TArrow (ts, t) ->
List.fold_right (ptyp_arrow ~loc Nolabel)
(List.map (typ loc) ts)
(typ loc t)
| TArray t -> [%type: [%t typ loc t] array]
| TAny -> [%type: _]
That's indeed incredibly nicer !
My only conern is how much of the OCaml compiler internals will end up having to statically link ; but that might not be that much of an issue. (And heh, if we actually need to link it all we could take the opportunity to add options to run evaluation through an OCaml toplevel straight away, or even spit out native code… :thinking: )
Note that what you need is actualy metaquot, but nowadays that is a subpart of ppxlib I think.