Add an explain plugin ; improve printers (#489)

This commit is contained in:
Louis Gesbert 2023-07-12 11:50:06 +02:00 committed by GitHub
commit 56eaf77a5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 2091 additions and 305 deletions

View File

@ -20,7 +20,8 @@ type 'a pos = ('a, Pos.t) ed
let add m e = e, m
let remove (x, _) = x
let get (_, x) = x
let get (_, m) = m
let set m (x, _) = x, m
let map f (x, m) = f x, m
let map_mark f (a, m) = a, f m
let copy (_, m) x = x, m

View File

@ -28,6 +28,7 @@ type 'a pos = ('a, Pos.t) ed
val add : 'm -> 'a -> ('a, 'm) ed
val remove : ('a, 'm) ed -> 'a
val get : ('a, 'm) ed -> 'm
val set : 'm -> ('a, _) ed -> ('a, 'm) ed
val map : ('a -> 'b) -> ('a, 'm) ed -> ('b, 'm) ed
val map_mark : ('m1 -> 'm2) -> ('a, 'm1) ed -> ('a, 'm2) ed
val copy : ('b, 'm) ed -> 'a -> ('a, 'm) ed

View File

@ -54,8 +54,6 @@ let remove_prefix ~prefix s =
sub s plen (length s - plen)
else s
let format_t = Format.pp_print_string
(* Note: this should do, but remains incorrect for combined unicode characters
that display as one (e.g. `e` + postfix `'`). We should switch to Uuseg at
some poing *)
@ -68,5 +66,7 @@ let width s =
in
aux 0 0
let format_t ppf s = Format.pp_print_as ppf (width s) s
module Set = Set.Make (Stdlib.String)
module Map = Map.Make (Stdlib.String)

View File

@ -72,7 +72,7 @@ module MarkedString = struct
type info = string Mark.pos
let to_string (s, _) = s
let format fmt i = Format.pp_print_string fmt (to_string i)
let format fmt i = String.format_t fmt (to_string i)
let equal = Mark.equal String.equal
let compare = Mark.compare String.compare
end

View File

@ -104,6 +104,7 @@ end
(** Various helpers *)
val modname_of_file : string -> string
val get_lang : Cli.options -> Cli.input_file -> Cli.backend_lang
(** API available to plugins for their own registration *)

View File

@ -46,11 +46,12 @@
(modes native js)
(flags
(:standard
(:include custom_linking.sexp)))
(:include custom_linking.sexp)
-linkall))
(package catala)
(modules catala)
(public_name catala)
(libraries catala.driver))
(libraries catala.driver ocamlgraph))
(documentation
(package catala)

View File

@ -73,20 +73,75 @@ let format_string_list (fmt : Format.formatter) (uids : string list) : unit =
(Re.replace sanitize_quotes ~f:(fun _ -> "\\\"") info)))
uids
(* list taken from
http://caml.inria.fr/pub/docs/manual-ocaml/lex.html#sss:keywords *)
let ocaml_keywords =
[
"and";
"as";
"assert";
"asr";
"begin";
"class";
"constraint";
"do";
"done";
"downto";
"else";
"end";
"exception";
"external";
"false";
"for";
"fun";
"function";
"functor";
"if";
"in";
"include";
"inherit";
"initializer";
"land";
"lazy";
"let";
"lor";
"lsl";
"lsr";
"lxor";
"match";
"method";
"mod";
"module";
"mutable";
"new";
"nonrec";
"object";
"of";
"open";
"or";
"private";
"rec";
"sig";
"struct";
"then";
"to";
"true";
"try";
"type";
"val";
"virtual";
"when";
"while";
"with";
"Stdlib";
"Runtime";
"Oper";
]
let ocaml_keywords_set = String.Set.of_list ocaml_keywords
let avoid_keywords (s : string) : string =
match s with
(* list taken from
http://caml.inria.fr/pub/docs/manual-ocaml/lex.html#sss:keywords *)
| "and" | "as" | "assert" | "asr" | "begin" | "class" | "constraint" | "do"
| "done" | "downto" | "else" | "end" | "exception" | "external" | "false"
| "for" | "fun" | "function" | "functor" | "if" | "in" | "include" | "inherit"
| "initializer" | "land" | "lazy" | "let" | "lor" | "lsl" | "lsr" | "lxor"
| "match" | "method" | "mod" | "module" | "mutable" | "new" | "nonrec"
| "object" | "of" | "open" | "or" | "private" | "rec" | "sig" | "struct"
| "then" | "to" | "true" | "try" | "type" | "val" | "virtual" | "when"
| "while" | "with" | "Stdlib" | "Runtime" | "Oper" ->
s ^ "_user"
| _ -> s
if String.Set.mem s ocaml_keywords_set then s ^ "_user" else s
(* Fixme: this could cause clashes if the user program contains both e.g. [new]
and [new_user] *)
@ -187,7 +242,7 @@ let format_var (fmt : Format.formatter) (v : 'm Var.t) : unit =
~subst:(fun _ -> "_dot_")
lowercase_name
in
let lowercase_name = avoid_keywords (String.to_ascii lowercase_name) in
let lowercase_name = String.to_ascii lowercase_name in
if
List.mem lowercase_name ["handle_default"; "handle_default_opt"]
|| String.begins_with_uppercase (Bindlib.name_of v)
@ -505,6 +560,14 @@ let format_ctx
Format.fprintf fmt "%a@\n" format_enum_decl (e, find_enum e ctx))
(type_ordering @ scope_structs)
let rename_vars e =
Expr.(
unbox
(rename_vars ~exclude:ocaml_keywords ~reset_context_for_closed_terms:true
~skip_constant_binders:true ~constant_binder_name:(Some "_") e))
let format_expr ctx fmt e = format_expr ctx fmt (rename_vars e)
let rec format_scope_body_expr
(ctx : decl_ctx)
(fmt : Format.formatter)

View File

@ -26,7 +26,17 @@
(synopsis
"Catala plugin that implements a different, experimental interpreter, featuring lazy and partial evaluation")
(modules lazy_interp)
(libraries shared_ast catala.driver))
(flags (-linkall))
(libraries shared_ast catala.driver ocamlgraph))
(library
(name explain)
(public_name catala.plugins.explain)
(synopsis
"Experiments for the explanation of computations: generates a graph of the formulas that are used for a given execution of a scope")
(modules explain)
(flags (-linkall))
(libraries shared_ast catala.driver ocamlgraph))
(library
(name modules)

1448
compiler/plugins/explain.ml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -757,6 +757,85 @@ let remove_logging_calls e =
in
f e
module DefaultBindlibCtxRename = struct
(* This code is a copy-paste from Bindlib, they forgot to expose the default
implementation ! *)
type ctxt = int String.Map.t
let empty_ctxt = String.Map.empty
let split_name : string -> string * int =
fun name ->
let len = String.length name in
(* [i] is the index of the first first character of the suffix. *)
let i =
let is_digit c = '0' <= c && c <= '9' in
let first_digit = ref len in
let first_non_0 = ref len in
while !first_digit > 0 && is_digit name.[!first_digit - 1] do
decr first_digit;
if name.[!first_digit] <> '0' then first_non_0 := !first_digit
done;
!first_non_0
in
if i = len then name, 0
else String.sub name 0 i, int_of_string (String.sub name i (len - i))
let get_suffix : string -> int -> ctxt -> int * ctxt =
fun name suffix ctxt ->
let n = try String.Map.find name ctxt with Not_found -> -1 in
let suffix = if suffix > n then suffix else n + 1 in
suffix, String.Map.add name suffix ctxt
let merge_name : string -> int -> string =
fun prefix suffix ->
if suffix > 0 then prefix ^ string_of_int suffix else prefix
let new_name : string -> ctxt -> string * ctxt =
fun name ctxt ->
let prefix, suffix = split_name name in
let suffix, ctxt = get_suffix prefix suffix ctxt in
merge_name prefix suffix, ctxt
let reserve_name : string -> ctxt -> ctxt =
fun name ctxt ->
let prefix, suffix = split_name name in
try
let n = String.Map.find prefix ctxt in
if suffix <= n then ctxt else String.Map.add prefix suffix ctxt
with Not_found -> String.Map.add prefix suffix ctxt
end
let rename_vars
?(exclude = ([] : string list))
?(reset_context_for_closed_terms = false)
?(skip_constant_binders = false)
?(constant_binder_name = None)
e =
let module BindCtx = Bindlib.Ctxt (struct
include DefaultBindlibCtxRename
let reset_context_for_closed_terms = reset_context_for_closed_terms
let skip_constant_binders = skip_constant_binders
let constant_binder_name = constant_binder_name
end) in
let rec aux : type a. BindCtx.ctxt -> (a, 't) gexpr -> (a, 't) gexpr boxed =
fun ctx e ->
match e with
| EAbs { binder; tys }, m ->
let vars, body, ctx = BindCtx.unmbind_in ctx binder in
let body = aux ctx body in
let binder = bind vars body in
eabs binder tys m
| e -> map ~f:(aux ctx) e
in
let ctx =
List.fold_left
(fun ctx name -> DefaultBindlibCtxRename.reserve_name name ctx)
BindCtx.empty_ctxt exclude
in
aux ctx e
let format ppf e = Print.expr ~debug:false () ppf e
let rec size : type a. (a, 't) gexpr -> int =

View File

@ -363,6 +363,17 @@ val remove_logging_calls :
(** Removes all calls to [Log] unary operators in the AST, replacing them by
their argument. *)
val rename_vars :
?exclude:string list ->
?reset_context_for_closed_terms:bool ->
?skip_constant_binders:bool ->
?constant_binder_name:string option ->
('a, 'm) gexpr ->
('a, 'm) boxed_gexpr
(** Disambiguates all variable names in [e]. [exclude] will blacklist the given
names (useful for keywords or built-in names) ; the other flags behave as
defined in the bindlib documentation for module type [Rename] *)
val format : Format.formatter -> ('a, 'm) gexpr -> unit
(** Simple printing without debug, use [Print.expr ()] instead to follow the
command-line debug setting *)

View File

@ -68,10 +68,20 @@ let print_log entry infos pos e =
if Cli.globals.trace then
match entry with
| VarDef _ ->
let module Printer = Print.ExprGen (struct
include Print.ExprConciseParam
let bypass : type a. Format.formatter -> (a, 't) gexpr -> bool =
fun ppf e ->
match e with
| EAbs _, _ ->
Print.op_style ppf "<function>";
true
| _ -> false
end) in
Message.emit_log "%s%a %a: @{<green>%s@}" !indent_str Print.log_entry
entry Print.uid_list infos
(Message.unformat (fun ppf ->
Print.expr ~hide_function_body:true () ppf e))
(Message.unformat (fun ppf -> Printer.expr ppf e))
| PosRecordIfTrueBool -> (
match pos <> Pos.no_pos, Mark.remove e with
| true, ELit (LBool true) ->
@ -176,8 +186,11 @@ let rec evaluate_operator
(Print.expr ()) arg),
Expr.pos arg ))
args)
"Operator applied to the wrong arguments\n\
(should not happen if the term was well-typed)"
"Operator %a applied to the wrong arguments\n\
(should not happen if the term was well-typed)%a"
(Print.operator ~debug:true)
op Expr.format
(EApp { f = EOp { op; tys = [] }, m; args }, m)
in
propagate_empty_error_list args
@@ fun args ->

View File

@ -39,6 +39,11 @@ let with_color f color fmt x =
let pp_color_string = with_color Format.pp_print_string
(* Cyclic list used to choose nested paren colors *)
let rec colors =
let open Ocolor_types in
blue :: cyan :: green :: yellow :: red :: magenta :: colors
let keyword (fmt : Format.formatter) (s : string) : unit =
pp_color_string Ocolor_types.red fmt s
@ -80,12 +85,12 @@ let enum_constructor (fmt : Format.formatter) (c : EnumConstructor.t) : unit =
let struct_field (fmt : Format.formatter) (c : StructField.t) : unit =
Format.fprintf fmt "@{<magenta>%a@}" StructField.format_t c
let rec typ
let rec typ_gen
(ctx : decl_ctx option)
~(colors : Ocolor_types.color4 list)
(fmt : Format.formatter)
(ty : typ) : unit =
let typ = typ ctx in
let typ = typ_gen ctx in
let typ_with_parens ~colors (fmt : Format.formatter) (t : typ) =
if typ_needs_parens t then (
Format.pp_open_hvbox fmt 1;
@ -164,6 +169,9 @@ let rec typ
| TAny -> base_type fmt "any"
| TClosureEnv -> base_type fmt "closure_env"
let typ_debug = typ_gen None ~colors
let typ ctx = typ_gen (Some ctx) ~colors
let lit (fmt : Format.formatter) (l : lit) : unit =
match l with
| LBool b -> lit_style fmt (string_of_bool b)
@ -453,264 +461,292 @@ module Precedence = struct
| Contained, _ -> false
end
let rec expr_aux :
type a.
hide_function_body:bool ->
debug:bool ->
Bindlib.ctxt ->
Ocolor_types.color4 list ->
Format.formatter ->
(a, 't) gexpr ->
unit =
fun ~hide_function_body ~debug bnd_ctx colors fmt e ->
let exprb bnd_ctx colors e =
expr_aux ~hide_function_body ~debug bnd_ctx colors e
in
let exprc colors e = exprb bnd_ctx colors e in
let expr e = exprc colors e in
let var = if debug then var_debug else var in
let rec skip_log : type a. (a, 't) gexpr -> (a, 't) gexpr = function
| EApp { f = EOp { op = Log _; _ }, _; args = [e] }, _ when not debug ->
skip_log e
| e -> e
in
let e = skip_log e in
let paren ~rhs ?(colors = colors) expr fmt e1 =
if Precedence.needs_parens ~rhs ~context:e (skip_log e1) then (
Format.pp_open_hvbox fmt 1;
pp_color_string (List.hd colors) fmt "(";
expr (List.tl colors) fmt e1;
Format.pp_close_box fmt ();
pp_color_string (List.hd colors) fmt ")")
else expr colors fmt e1
in
let default_punct = with_color (fun fmt -> Format.pp_print_as fmt 1) in
let lhs ?(colors = colors) ex = paren ~colors ~rhs:false ex in
let rhs ex = paren ~rhs:true ex in
match Mark.remove e with
| EVar v -> var fmt v
| EExternal eref -> Qident.format fmt eref
| ETuple es ->
Format.fprintf fmt "@[<hov 2>%a%a%a@]"
(pp_color_string (List.hd colors))
"("
(Format.pp_print_list
~pp_sep:(fun fmt () ->
Format.fprintf fmt "%a@ " (pp_color_string (List.hd colors)) ",")
(fun fmt e -> lhs ~colors:(List.tl colors) exprc fmt e))
es
(pp_color_string (List.hd colors))
")"
| EArray es ->
Format.fprintf fmt "@[<hv 2>%a %a@] %a" punctuation "["
(Format.pp_print_list
~pp_sep:(fun fmt () -> Format.fprintf fmt ";@ ")
(fun fmt e -> lhs exprc fmt e))
es punctuation "]"
| ETupleAccess { e; index; _ } ->
lhs exprc fmt e;
punctuation fmt ".";
Format.pp_print_int fmt index
| ELit l -> lit fmt l
| EApp { f = EAbs _, _; _ } ->
let rec pr bnd_ctx colors fmt = function
| EApp { f = EAbs { binder; tys }, _; args }, _ ->
let xs, body, bnd_ctx = Bindlib.unmbind_in bnd_ctx binder in
let xs_tau = List.mapi (fun i tau -> xs.(i), tau) tys in
let xs_tau_arg =
List.map2 (fun (x, tau) arg -> x, tau, arg) xs_tau args
module type EXPR_PARAM = sig
val bypass : Format.formatter -> ('a, 't) gexpr -> bool
val operator : Format.formatter -> 'a operator -> unit
val var : Format.formatter -> ('a, 't) gexpr Var.t -> unit
val lit : Format.formatter -> lit -> unit
val pre_map : ('a, 't) gexpr -> ('a, 't) gexpr
end
module ExprGen (C : EXPR_PARAM) = struct
let rec expr_aux :
type a.
Bindlib.ctxt ->
Ocolor_types.color4 list ->
Format.formatter ->
(a, 't) gexpr ->
unit =
fun bnd_ctx colors fmt e ->
let exprb bnd_ctx colors e = expr_aux bnd_ctx colors e in
let exprc colors e = exprb bnd_ctx colors e in
let expr e = exprc colors e in
let var = C.var in
let operator = C.operator in
let e = C.pre_map e in
let paren ~rhs ?(colors = colors) expr fmt e1 =
if Precedence.needs_parens ~rhs ~context:e (C.pre_map e1) then (
Format.pp_open_hvbox fmt 1;
pp_color_string (List.hd colors) fmt "(";
expr (List.tl colors) fmt e1;
Format.pp_close_box fmt ();
pp_color_string (List.hd colors) fmt ")")
else expr colors fmt e1
in
let default_punct = with_color (fun fmt -> Format.pp_print_as fmt 1) in
let lhs ?(colors = colors) ex = paren ~colors ~rhs:false ex in
let rhs ex = paren ~rhs:true ex in
if C.bypass fmt e then ()
else
match Mark.remove e with
| EVar v -> var fmt v
| EExternal eref -> Qident.format fmt eref
| ETuple es ->
Format.fprintf fmt "@[<hov 2>%a%a%a@]"
(pp_color_string (List.hd colors))
"("
(Format.pp_print_list
~pp_sep:(fun fmt () ->
Format.fprintf fmt "%a@ " (pp_color_string (List.hd colors)) ",")
(fun fmt e -> lhs ~colors:(List.tl colors) exprc fmt e))
es
(pp_color_string (List.hd colors))
")"
| EArray es ->
Format.fprintf fmt "@[<hov 2>%a %a@] %a" punctuation "["
(Format.pp_print_list
~pp_sep:(fun fmt () -> Format.fprintf fmt ";@ ")
(fun fmt e -> lhs exprc fmt e))
es punctuation "]"
| ETupleAccess { e; index; _ } ->
lhs exprc fmt e;
punctuation fmt ".";
Format.pp_print_int fmt index
| ELit l -> C.lit fmt l
| EApp { f = EAbs _, _; _ } ->
let rec pr bnd_ctx colors fmt = function
| EApp { f = EAbs { binder; tys }, _; args }, _ ->
let xs, body, bnd_ctx = Bindlib.unmbind_in bnd_ctx binder in
let xs_tau = List.mapi (fun i tau -> xs.(i), tau) tys in
let xs_tau_arg =
List.map2 (fun (x, tau) arg -> x, tau, arg) xs_tau args
in
Format.pp_print_list
(fun fmt (x, tau, arg) ->
Format.fprintf fmt
"@[<hv 2>@[<hov 4>%a %a %a@ %a@ %a@]@ %a@;<1 -2>%a@]" keyword
"let" var x punctuation ":" (typ_gen None ~colors) tau
punctuation "=" (exprc colors) arg keyword "in")
fmt xs_tau_arg;
Format.pp_print_cut fmt ();
rhs (pr bnd_ctx) fmt body
| e -> rhs (exprb bnd_ctx) fmt e
in
Format.pp_print_list
(fun fmt (x, tau, arg) ->
Format.fprintf fmt
"@[<hv 2>@[<hov 4>%a %a %a@ %a@ %a@]@ %a@;<1 -2>%a@]" keyword
"let" var x punctuation ":" (typ None ~colors) tau punctuation "="
(exprc colors) arg keyword "in")
fmt xs_tau_arg;
Format.pp_print_cut fmt ();
rhs (pr bnd_ctx) fmt body
| e -> rhs (exprb bnd_ctx) fmt e
in
Format.pp_open_vbox fmt 0;
pr bnd_ctx colors fmt e;
Format.pp_close_box fmt ()
| EAbs { binder; tys } ->
if hide_function_body then Format.fprintf fmt "%a" op_style "<function>"
else
let xs, body, bnd_ctx = Bindlib.unmbind_in bnd_ctx binder in
let expr = exprb bnd_ctx in
let xs_tau = List.mapi (fun i tau -> xs.(i), tau) tys in
Format.fprintf fmt "@[<hv 0>%a @[<hv 2>%a@]@ @]%a@ %a" punctuation "λ"
(Format.pp_print_list ~pp_sep:Format.pp_print_space (fun fmt (x, tau) ->
punctuation fmt "(";
Format.pp_open_hvbox fmt 2;
var fmt x;
punctuation fmt ":";
Format.pp_print_space fmt ();
typ None ~colors fmt tau;
Format.pp_close_box fmt ();
punctuation fmt ")"))
xs_tau punctuation "" (rhs expr) body
| EApp { f = EOp { op = (Map | Filter) as op; _ }, _; args = [arg1; arg2] } ->
Format.fprintf fmt "@[<hv 2>%a %a@ %a@]" (operator ~debug) op (lhs exprc)
arg1 (rhs exprc) arg2
| EApp { f = EOp { op = Log _ as op; _ }, _; args = [arg1] } ->
Format.fprintf fmt "@[<hv 0>%a@ %a@]" (operator ~debug) op (rhs exprc) arg1
| EApp { f = EOp { op = op0; _ }, _; args = [_; _] } ->
let prec = Precedence.expr e in
let rec pr colors fmt = function
(* Flatten sequences of the same associative op *)
| EApp { f = EOp { op; _ }, _; args = [arg1; arg2] }, _ when op = op0 -> (
(match prec with
| Op (And | Or | Mul | Add | Div | Sub) -> lhs pr fmt arg1
| _ -> lhs exprc fmt arg1);
Format.pp_open_vbox fmt 0;
pr bnd_ctx colors fmt e;
Format.pp_close_box fmt ()
| EAbs { binder; tys } ->
let xs, body, bnd_ctx = Bindlib.unmbind_in bnd_ctx binder in
let expr = exprb bnd_ctx in
let xs_tau = List.mapi (fun i tau -> xs.(i), tau) tys in
Format.fprintf fmt "@[<hv 0>%a @[<hv 2>%a@]@ @]%a@ %a" punctuation "λ"
(Format.pp_print_list ~pp_sep:Format.pp_print_space
(fun fmt (x, tau) ->
punctuation fmt "(";
Format.pp_open_hvbox fmt 2;
var fmt x;
punctuation fmt ":";
Format.pp_print_space fmt ();
typ_gen None ~colors fmt tau;
Format.pp_close_box fmt ();
punctuation fmt ")"))
xs_tau punctuation "" (rhs expr) body
| EApp
{ f = EOp { op = (Map | Filter) as op; _ }, _; args = [arg1; arg2] }
->
Format.fprintf fmt "@[<hv 2>%a %a@ %a@]" operator op (lhs exprc) arg1
(rhs exprc) arg2
| EApp { f = EOp { op = Log _ as op; _ }, _; args = [arg1] } ->
Format.fprintf fmt "@[<hv 0>%a@ %a@]" operator op (rhs exprc) arg1
| EApp { f = EOp { op = op0; _ }, _; args = [_; _] } ->
let prec = Precedence.expr e in
let rec pr colors fmt = function
(* Flatten sequences of the same associative op *)
| EApp { f = EOp { op; _ }, _; args = [arg1; arg2] }, _ when op = op0
-> (
(match prec with
| Op (And | Or | Mul | Add | Div | Sub) -> lhs pr fmt arg1
| _ -> lhs exprc fmt arg1);
Format.pp_print_space fmt ();
operator fmt op;
Format.pp_print_char fmt ' ';
match prec with
| Op (And | Or | Mul | Add) -> rhs pr fmt arg2
| _ -> rhs exprc fmt arg2)
| e -> exprc colors fmt e
in
Format.pp_open_hvbox fmt 0;
pr colors fmt e;
Format.pp_close_box fmt ()
| EApp { f = EOp { op; _ }, _; args = [arg1] } ->
Format.fprintf fmt "@[<hv 2>%a@ %a@]" operator op (rhs exprc) arg1
| EApp { f; args } ->
Format.fprintf fmt "@[<hv 2>%a@ %a@]" (lhs exprc) f
(Format.pp_print_list
~pp_sep:(fun fmt () -> Format.fprintf fmt "@ ")
(rhs exprc))
args
| EIfThenElse _ ->
let rec pr els fmt = function
| EIfThenElse { cond; etrue; efalse }, _ ->
Format.fprintf fmt "@[<hv 2>@[<hv 2>%a@ %a@;<1 -2>%a@]@ %a@]@ %a"
keyword
(if els then "else if" else "if")
expr cond keyword "then" expr etrue (pr true) efalse
| e ->
Format.fprintf fmt "@[<hv 2>%a@ %a@]" keyword "else" (rhs exprc) e
in
Format.pp_open_hvbox fmt 0;
pr false fmt e;
Format.pp_close_box fmt ()
| EOp { op; _ } -> operator fmt op
| EDefault { excepts; just; cons } ->
if List.length excepts = 0 then
Format.fprintf fmt "@[<hv 1>%a%a@ %a %a%a@]"
(default_punct (List.hd colors))
""
(exprc (List.tl colors))
just
(default_punct (List.hd colors))
""
(exprc (List.tl colors))
cons
(default_punct (List.hd colors))
""
else
Format.fprintf fmt
"@[<hv 0>@[<hov 2>%a %a@]@ @[<hov 2>%a %a@ %a %a@] %a@]"
(default_punct (List.hd colors))
""
(Format.pp_print_list
~pp_sep:(fun fmt () ->
Format.fprintf fmt "%a@ " (default_punct (List.hd colors)) ",")
(lhs ~colors:(List.tl colors) exprc))
excepts
(default_punct (List.hd colors))
"|"
(exprc (List.tl colors))
just
(default_punct (List.hd colors))
""
(exprc (List.tl colors))
cons
(default_punct (List.hd colors))
""
| EEmptyError -> lit_style fmt ""
| EErrorOnEmpty e' ->
Format.fprintf fmt "@[<hov 2>%a@ %a@]" op_style "error_empty"
(rhs exprc) e'
| EAssert e' ->
Format.fprintf fmt "@[<hov 2>%a@ %a%a%a@]" keyword "assert" punctuation
"(" (rhs exprc) e' punctuation ")"
| ECatch { body; exn; handler } ->
Format.fprintf fmt
"@[<hv 0>@[<hov 2>%a@ %a@]@ @[<hov 2>%a@ %a ->@ %a@]@]" keyword "try"
expr body keyword "with" except exn (rhs exprc) handler
| ERaise exn ->
Format.fprintf fmt "@[<hov 2>%a@ %a@]" keyword "raise" except exn
| ELocation loc -> location fmt loc
| EDStructAccess { e; field; _ } ->
Format.fprintf fmt "@[<hv 2>%a%a@,%a%a%a@]" (lhs exprc) e punctuation
"." punctuation "\"" Ident.format_t field punctuation "\""
| EStruct { name; fields } ->
if StructField.Map.is_empty fields then (
punctuation fmt "{";
StructName.format_t fmt name;
punctuation fmt "}")
else
Format.fprintf fmt "@[<hv 2>%a %a@ %a@;<1 -2>%a@]" punctuation "{"
StructName.format_t name
(Format.pp_print_list ~pp_sep:Format.pp_print_space
(fun fmt (field_name, field_expr) ->
Format.fprintf fmt "@[<hv 2>%a %a@ %a%a@]" struct_field
field_name punctuation "=" (lhs exprc) field_expr punctuation
";"))
(StructField.Map.bindings fields)
punctuation "}"
| EStructAccess { e; field; _ } ->
Format.fprintf fmt "@[<hv 2>%a%a@,%a@]" (lhs exprc) e punctuation "."
struct_field field
| EInj { e; cons; _ } ->
Format.fprintf fmt "@[<hv 2>%a@ %a@]" EnumConstructor.format_t cons
(rhs exprc) e
| EMatch { e; cases; _ } ->
Format.fprintf fmt "@[<v 0>@[<hv 2>%a@ %a@ %a@]@ %a@]" keyword "match"
(lhs exprc) e keyword "with"
(Format.pp_print_list
~pp_sep:(fun fmt () -> Format.fprintf fmt "@\n")
(fun fmt (cons_name, case_expr) ->
match case_expr with
| EAbs { binder; _ }, _ ->
let xs, body, bnd_ctx = Bindlib.unmbind_in bnd_ctx binder in
let expr = exprb bnd_ctx in
Format.fprintf fmt "@[<hov 2>%a %a@ %a@ %a@ %a@]" punctuation
"|" enum_constructor cons_name
(Format.pp_print_seq ~pp_sep:Format.pp_print_space var)
(Array.to_seq xs) punctuation "" (rhs expr) body
| e ->
Format.fprintf fmt "@[<hov 2>%a %a@ %a@ %a@]" punctuation "|"
enum_constructor cons_name punctuation "" (rhs exprc) e))
(EnumConstructor.Map.bindings cases)
| EScopeCall { scope; args } ->
Format.pp_open_hovbox fmt 2;
ScopeName.format_t fmt scope;
Format.pp_print_space fmt ();
(operator ~debug) fmt op;
Format.pp_print_char fmt ' ';
match prec with
| Op (And | Or | Mul | Add) -> rhs pr fmt arg2
| _ -> rhs exprc fmt arg2)
| e -> exprc colors fmt e
in
Format.pp_open_hvbox fmt 0;
pr colors fmt e;
Format.pp_close_box fmt ()
| EApp { f = EOp { op; _ }, _; args = [arg1] } ->
Format.fprintf fmt "@[<hv 2>%a@ %a@]" (operator ~debug) op (rhs exprc) arg1
| EApp { f; args } ->
Format.fprintf fmt "@[<hv 2>%a@ %a@]" (lhs exprc) f
(Format.pp_print_list
~pp_sep:(fun fmt () -> Format.fprintf fmt "@ ")
(rhs exprc))
args
| EIfThenElse _ ->
let rec pr els fmt = function
| EIfThenElse { cond; etrue; efalse }, _ ->
Format.fprintf fmt "@[<hv 2>@[<hv 2>%a@ %a@;<1 -2>%a@]@ %a@]@ %a"
keyword
(if els then "else if" else "if")
expr cond keyword "then" expr etrue (pr true) efalse
| e -> Format.fprintf fmt "@[<hv 2>%a@ %a@]" keyword "else" (rhs exprc) e
in
Format.pp_open_hvbox fmt 0;
pr false fmt e;
Format.pp_close_box fmt ()
| EOp { op; _ } -> operator ~debug fmt op
| EDefault { excepts; just; cons } ->
if List.length excepts = 0 then
Format.fprintf fmt "@[<hv 1>%a%a@ %a %a%a@]"
(default_punct (List.hd colors))
""
(exprc (List.tl colors))
just
(default_punct (List.hd colors))
""
(exprc (List.tl colors))
cons
(default_punct (List.hd colors))
""
else
Format.fprintf fmt
"@[<hv 0>@[<hov 2>%a %a@]@ @[<hov 2>%a %a@ %a %a@] %a@]"
(default_punct (List.hd colors))
""
(Format.pp_print_list
~pp_sep:(fun fmt () ->
Format.fprintf fmt "%a@ " (default_punct (List.hd colors)) ",")
(lhs ~colors:(List.tl colors) exprc))
excepts
(default_punct (List.hd colors))
"|"
(exprc (List.tl colors))
just
(default_punct (List.hd colors))
""
(exprc (List.tl colors))
cons
(default_punct (List.hd colors))
""
| EEmptyError -> lit_style fmt ""
| EErrorOnEmpty e' ->
Format.fprintf fmt "@[<hov 2>%a@ %a@]" op_style "error_empty" (rhs exprc) e'
| EAssert e' ->
Format.fprintf fmt "@[<hov 2>%a@ %a%a%a@]" keyword "assert" punctuation "("
(rhs exprc) e' punctuation ")"
| ECatch { body; exn; handler } ->
Format.fprintf fmt "@[<hv 0>@[<hov 2>%a@ %a@]@ @[<hov 2>%a@ %a ->@ %a@]@]"
keyword "try" expr body keyword "with" except exn (rhs exprc) handler
| ERaise exn ->
Format.fprintf fmt "@[<hov 2>%a@ %a@]" keyword "raise" except exn
| ELocation loc -> location fmt loc
| EDStructAccess { e; field; _ } ->
Format.fprintf fmt "@[<hv 2>%a%a@,%a%a%a@]" (lhs exprc) e punctuation "."
punctuation "\"" Ident.format_t field punctuation "\""
| EStruct { name; fields } ->
if StructField.Map.is_empty fields then (
punctuation fmt "{";
StructName.format_t fmt name;
punctuation fmt "}")
else
Format.fprintf fmt "@[<hv 2>%a %a@ %a@;<1 -2>%a@]" punctuation "{"
StructName.format_t name
(Format.pp_print_list ~pp_sep:Format.pp_print_space
(fun fmt (field_name, field_expr) ->
Format.fprintf fmt "@[<hv 2>%a %a@ %a%a@]" struct_field field_name
punctuation "=" (lhs exprc) field_expr punctuation ";"))
(StructField.Map.bindings fields)
punctuation "}"
| EStructAccess { e; field; _ } ->
Format.fprintf fmt "@[<hv 2>%a%a@,%a@]" (lhs exprc) e punctuation "."
struct_field field
| EInj { e; cons; _ } ->
Format.fprintf fmt "@[<hv 2>%a@ %a@]" EnumConstructor.format_t cons
(rhs exprc) e
| EMatch { e; cases; _ } ->
Format.fprintf fmt "@[<v 0>@[<hv 2>%a@ %a@ %a@]@ %a@]" keyword "match"
(lhs exprc) e keyword "with"
(Format.pp_print_list
~pp_sep:(fun fmt () -> Format.fprintf fmt "@\n")
(fun fmt (cons_name, case_expr) ->
match case_expr with
| EAbs { binder; _ }, _ ->
let xs, body, bnd_ctx = Bindlib.unmbind_in bnd_ctx binder in
let expr = exprb bnd_ctx in
Format.fprintf fmt "@[<hov 2>%a %a@ %a@ %a@ %a@]" punctuation "|"
enum_constructor cons_name
(Format.pp_print_seq ~pp_sep:Format.pp_print_space var)
(Array.to_seq xs) punctuation "" (rhs expr) body
| e ->
Format.fprintf fmt "@[<hov 2>%a %a@ %a@ %a@]" punctuation "|"
enum_constructor cons_name punctuation "" (rhs exprc) e))
(EnumConstructor.Map.bindings cases)
| EScopeCall { scope; args } ->
Format.pp_open_hovbox fmt 2;
ScopeName.format_t fmt scope;
Format.pp_print_space fmt ();
keyword fmt "of";
Format.pp_print_space fmt ();
Format.pp_open_hvbox fmt 2;
punctuation fmt "{";
Format.pp_print_list
~pp_sep:(fun fmt () -> Format.fprintf fmt "%a@ " punctuation ";")
(fun fmt (field_name, field_expr) ->
Format.fprintf fmt "%a%a%a%a@ %a" punctuation "\"" ScopeVar.format_t
field_name punctuation "\"" punctuation "=" (rhs exprc) field_expr)
fmt
(ScopeVar.Map.bindings args);
Format.pp_close_box fmt ();
punctuation fmt "}";
Format.pp_close_box fmt ()
| ECustom _ -> Format.pp_print_string fmt "<obj>"
keyword fmt "of";
Format.pp_print_space fmt ();
Format.pp_open_hvbox fmt 2;
punctuation fmt "{";
Format.pp_print_list
~pp_sep:(fun fmt () -> Format.fprintf fmt "%a@ " punctuation ";")
(fun fmt (field_name, field_expr) ->
Format.fprintf fmt "%a%a%a%a@ %a" punctuation "\"" ScopeVar.format_t
field_name punctuation "\"" punctuation "=" (rhs exprc) field_expr)
fmt
(ScopeVar.Map.bindings args);
Format.pp_close_box fmt ();
punctuation fmt "}";
Format.pp_close_box fmt ()
| ECustom _ -> Format.pp_print_string fmt "<obj>"
let rec colors =
let open Ocolor_types in
blue :: cyan :: green :: yellow :: red :: magenta :: colors
let expr ppf e = expr_aux Bindlib.empty_ctxt colors ppf e
end
let typ_debug = typ None ~colors
let typ ctx = typ (Some ctx) ~colors
module ExprConciseParam = struct
let bypass _ _ = false
let operator o = operator ~debug:false o
let var = var
let lit = lit
let expr ?(hide_function_body = false) ?(debug = Cli.globals.debug) () ppf e =
expr_aux ~hide_function_body ~debug Bindlib.empty_ctxt colors ppf e
let rec pre_map : type a. (a, 't) gexpr -> (a, 't) gexpr = function
| EApp { f = EOp { op = Log _; _ }, _; args = [e] }, _ -> pre_map e
| e -> e
end
module ExprConcise = ExprGen (ExprConciseParam)
module ExprDebugParam = struct
let bypass _ _ = false
let operator o = operator ~debug:true o
let var = var_debug
let lit = lit
let pre_map e = e
end
module ExprDebug = ExprGen (ExprDebugParam)
let expr ?(debug = Cli.globals.debug) () ppf e =
if debug then ExprDebug.expr ppf e else ExprConcise.expr ppf e
let scope_let_kind ?debug:(_debug = true) _ctx fmt k =
match k with
@ -1009,33 +1045,74 @@ module UserFacing = struct
with_color (lit_raw lang) Ocolor_types.yellow ppf lit
let rec value :
type a b.
type a.
?fallback:(Format.formatter -> (a, 't) gexpr -> unit) ->
Cli.backend_lang ->
Format.formatter ->
((a, b) dcalc_lcalc, _) gexpr ->
(a, 't) gexpr ->
unit =
fun lang ppf e ->
fun ?(fallback = fun _ _ -> invalid_arg "UserPrint.value: not a value") lang
ppf e ->
match Mark.remove e with
| ELit l -> lit lang ppf l
| EArray l | ETuple l ->
Format.fprintf ppf "@[<hov 1>[@;<0 1>%a@;<0 -1>]@]"
(Format.pp_print_list
~pp_sep:(fun ppf () -> Format.fprintf ppf ";@ ")
(value lang))
(value ~fallback lang))
l
| EStruct { name; fields } ->
Format.fprintf ppf "@[<hv 2>%a {@ %a@;<1 -2>}@]" StructName.format_t name
(Format.pp_print_list ~pp_sep:Format.pp_print_space (fun ppf (fld, e) ->
Format.fprintf ppf "-- %a: %a" StructField.format_t fld
(value lang) e))
(value ~fallback lang) e))
(StructField.Map.bindings fields)
| EInj { name = _; cons; e } ->
Format.fprintf ppf "%a %a" EnumConstructor.format_t cons (value lang) e
Format.fprintf ppf "%a %a" EnumConstructor.format_t cons
(value ~fallback lang) e
| EEmptyError -> Format.pp_print_string ppf "ø"
| EAbs _ -> Format.pp_print_string ppf "<function>"
| EExternal _ -> Format.pp_print_string ppf "<external>"
| EApp _ | EOp _ | EVar _ | EIfThenElse _ | EMatch _ | ETupleAccess _
| EStructAccess _ | EAssert _ | EDefault _ | EErrorOnEmpty _ | ERaise _
| ECatch _ | ELocation _ ->
invalid_arg "UserPrint.value: not a value"
| ECatch _ | ELocation _ | EScopeCall _ | EDStructAccess _ | ECustom _ ->
fallback ppf e
(* This function is already in module [Expr], but [Expr] depends on this
module *)
let rec skip_wrappers : type a. (a, 'm) gexpr -> (a, 'm) gexpr = function
| EApp { f = EOp { op = Log _; _ }, _; args = [e] }, _ -> skip_wrappers e
| EApp { f = EApp { f = EOp { op = Log _; _ }, _; args = [f] }, _; args }, m
->
skip_wrappers (EApp { f; args }, m)
| EErrorOnEmpty e, _ -> skip_wrappers e
| EDefault { excepts = []; just = ELit (LBool true), _; cons = e }, _ ->
skip_wrappers e
| e -> e
let expr :
type a. Cli.backend_lang -> Format.formatter -> (a, 't) gexpr -> unit =
fun lang ->
let rec aux_value : type a t. Format.formatter -> (a, t) gexpr -> unit =
fun ppf e -> value ~fallback lang ppf e
and fallback : type a t. Format.formatter -> (a, t) gexpr -> unit =
fun ppf e ->
let module E = ExprGen (struct
let bypass : type a t. Format.formatter -> (a, t) gexpr -> bool =
fun ppf e ->
match Mark.remove e with
| EArray _ | ETuple _ | EStruct _ | EInj _ | EEmptyError | EAbs _
| EExternal _ ->
aux_value ppf e;
true
| _ -> false
let operator o = operator ~debug:false o
let var = var
let lit = lit lang
let pre_map = skip_wrappers
end) in
E.expr ppf e
in
aux_value
end

View File

@ -51,15 +51,35 @@ val except : Format.formatter -> except -> unit
val var : Format.formatter -> 'e Var.t -> unit
val var_debug : Format.formatter -> 'e Var.t -> unit
val expr :
?hide_function_body:bool ->
?debug:bool ->
unit ->
Format.formatter ->
('a, 'm) gexpr ->
unit
(** Same as [expr], but with a debug flag that defaults to [!Cli.debug_flag]. If
[~hide_function_body:true], prints "<function>" for [EAbs] nodes *)
val expr : ?debug:bool -> unit -> Format.formatter -> ('a, 'm) gexpr -> unit
(** Expression printer.
@param debug
(default to the global setting) turns on printing of logging nodes,
variable indices and operator suffixes. See the interface below for more
detailed control. *)
(** {2 Generic expression printer interface} *)
module type EXPR_PARAM = sig
val bypass : Format.formatter -> ('a, 't) gexpr -> bool
(** can be used to customise printing of any specific nodes or subtrees: will
cancel normal printing upon returning [true]. *)
val operator : Format.formatter -> 'a operator -> unit
val var : Format.formatter -> ('a, 't) gexpr Var.t -> unit
val lit : Format.formatter -> lit -> unit
val pre_map : ('a, 't) gexpr -> ('a, 't) gexpr
(** pre-processing on expressions: can be used to skip log calls, etc. *)
end
module ExprGen (C : EXPR_PARAM) : sig
val expr : Format.formatter -> ('a, 't) gexpr -> unit
end
module ExprConciseParam : EXPR_PARAM
module ExprDebugParam : EXPR_PARAM
(** {1 Debugging versions that don't require a context} *)
@ -90,11 +110,18 @@ module UserFacing : sig
val lit_to_string : Cli.backend_lang -> lit -> string
val value :
?fallback:(Format.formatter -> ('a, 't) gexpr -> unit) ->
Cli.backend_lang ->
Format.formatter ->
((_, _) dcalc_lcalc, _) gexpr ->
('a, 't) gexpr ->
unit
(** @raise Invalid_argument
if the supplied expression is a custom/external or anything that is not
a value *)
(** Prints a value in a localised format, intended to be read by an end-user.
@param fallback
is called upon non-value expressions (by default, [Invalid_argument] is
raised) *)
val expr : Cli.backend_lang -> Format.formatter -> (_, _) gexpr -> unit
(** This combines the user-facing value printer and the generic expression
printer to handle all AST nodes *)
end

View File

@ -26,7 +26,8 @@ type 'e vars = ('a, 't) naked_gexpr Bindlib.mvar constraint 'e = ('a, 't) gexpr
let make (name : string) : 'e t = Bindlib.new_var (fun x -> EVar x) name
let compare = Bindlib.compare_vars
let eq = Bindlib.eq_vars
let equal = Bindlib.eq_vars
let hash = Bindlib.hash_var
let translate (v : 'e1 t) : 'e2 t =
Bindlib.copy_var v (fun x -> EVar x) (Bindlib.name_of v)
@ -76,6 +77,7 @@ module Set = struct
let of_list l = of_list (List.map t l)
let elements s = elements s |> List.map get
let diff s1 s2 = diff s1 s2
let iter f s = iter (fun x -> f (get x)) s
(* Add more as needed *)
end

View File

@ -26,7 +26,8 @@ type 'e vars = ('a, 't) naked_gexpr Bindlib.mvar constraint 'e = ('a, 't) gexpr
val make : string -> 'e t
val compare : 'e t -> 'e t -> int
val eq : 'e t -> 'e t -> bool
val equal : 'e t -> 'e t -> bool
val hash : 'e t -> int
val translate : 'e1 t -> 'e2 t
(** Needed when converting from one AST type to another. See the note of caution
@ -49,6 +50,7 @@ module Set : sig
val of_list : 'e var list -> 'e t
val elements : 'e t -> 'e var list
val diff : 'e t -> 'e t -> 'e t
val iter : ('e var -> unit) -> 'e t -> unit
end
(** Wrapper over [Map.S] but with a type variable for the AST type parameters.

View File

@ -122,7 +122,7 @@ let match_and_ignore_outer_reentrant_default (ctx : ctx) (e : typed expr) :
cons;
},
_ )
when List.exists (fun x' -> Var.eq x x') ctx.input_vars ->
when List.exists (fun x' -> Var.equal x x') ctx.input_vars ->
(* scope variables*)
cons
| EAbs { binder; tys = [(TLit TUnit, _)] } ->

View File

@ -6,7 +6,8 @@ declaration scope S:
scope S:
definition a equals
let a equals true in
let a equals false in
let a equals a or true in
a
```
@ -21,3 +22,52 @@ $ catala Interpret_Lcalc -s S --avoid_exceptions --optimize
[RESULT] Computation successful! Results:
[RESULT] a = ESome true
```
```catala-test-inline
$ catala ocaml
(** This file has been generated by the Catala compiler, do not edit! *)
open Runtime_ocaml.Runtime
[@@@ocaml.warning "-4-26-27-32-41-42"]
module S = struct
type t = {a: bool}
end
module SIn = struct
type t = {a_in: unit -> bool}
end
let s (s_in: SIn.t) : S.t =
let a_: unit -> bool = s_in.SIn.a_in in
let a_: bool =
try
(handle_default
{filename = ""; start_line=0; start_column=1;
end_line=0; end_column=1; law_headings=[]}
([|(fun (_: unit) -> a_ ())|]) (fun (_: unit) -> true)
(fun (_: unit) ->
handle_default
{filename = ""; start_line=0; start_column=1;
end_line=0; end_column=1; law_headings=[]} ([||])
(fun (_: unit) -> true)
(fun (_: unit) -> (let a_ : bool = false
in
(let a_ : bool = (o_or a_ true) in
a_)))))
with
EmptyError -> (raise (NoValueProvided
{filename = "tests/test_name_resolution/good/let_in2.catala_en";
start_line=5; start_column=18; end_line=5; end_column=19;
law_headings=["Article"]})) in
{S.a = a_}
let () =
Runtime_ocaml.Runtime.register_module "Let_in2"
[ "S", Obj.repr s ]
"todo-module-hash"
```