Merge branch '8-reporting-of-error-messages' into dev
This commit is contained in:
commit
e62178cee6
@ -113,6 +113,22 @@ and matching : type a . (formatter -> a -> unit) -> formatter -> a matching -> u
|
||||
| Match_option {match_none ; match_some = (some, match_some)} ->
|
||||
fprintf ppf "| None -> %a @.| Some %s -> %a" f match_none some f match_some
|
||||
|
||||
(* Shows the type expected for the matched value *)
|
||||
and matching_type ppf m = match m with
|
||||
| Match_tuple _ ->
|
||||
fprintf ppf "tuple"
|
||||
| Match_variant lst ->
|
||||
fprintf ppf "variant %a" (list_sep matching_variant_case_type (tag "@.")) lst
|
||||
| Match_bool _ ->
|
||||
fprintf ppf "boolean"
|
||||
| Match_list _ ->
|
||||
fprintf ppf "list"
|
||||
| Match_option _ ->
|
||||
fprintf ppf "option"
|
||||
|
||||
and matching_variant_case_type ppf ((c,n),_a) =
|
||||
fprintf ppf "| %s %s" c n
|
||||
|
||||
let declaration ppf (d:declaration) = match d with
|
||||
| Declaration_type (type_name , te) ->
|
||||
fprintf ppf "type %s = %a" type_name type_expression te
|
||||
|
@ -204,7 +204,6 @@ let rec assert_type_value_eq (a, b: (type_value * type_value)) : unit result = m
|
||||
@@ Assert.assert_list_same_size sa' sb' in
|
||||
trace (simple_error "sum type") @@
|
||||
bind_list_iter aux (List.combine sa' sb')
|
||||
|
||||
)
|
||||
| T_sum _, _ -> fail @@ different_kinds a b
|
||||
| T_record ra, T_record rb -> (
|
||||
|
@ -4,8 +4,8 @@ open Trace
|
||||
let toplevel x =
|
||||
match x with
|
||||
| Trace.Ok ((), annotations) -> ignore annotations; ()
|
||||
| Errors ss ->
|
||||
Format.printf "Errors: %a\n%!" errors_pp @@ List.map (fun f -> f()) ss
|
||||
| Error ss ->
|
||||
Format.printf "%a%!" error_pp (ss ())
|
||||
|
||||
let main =
|
||||
let term = Term.(const print_endline $ const "Ligo needs a command. Do ligo --help") in
|
||||
|
@ -51,7 +51,7 @@ let transpile_value
|
||||
let%bind f =
|
||||
let open Transpiler in
|
||||
let (f , _) = functionalize e in
|
||||
let%bind main = translate_main f in
|
||||
let%bind main = translate_main f e.location in
|
||||
ok main
|
||||
in
|
||||
|
||||
|
@ -5,7 +5,7 @@ let transpile_value
|
||||
let%bind f =
|
||||
let open Transpiler in
|
||||
let (f , _) = functionalize e in
|
||||
let%bind main = translate_main f in
|
||||
let%bind main = translate_main f e.location in
|
||||
ok main
|
||||
in
|
||||
|
||||
|
@ -94,5 +94,5 @@ let parse_expression (s:string) : AST.expr result =
|
||||
start.pos_fname s
|
||||
in
|
||||
simple_error str
|
||||
) @@ (fun () -> Parser.expr read lexbuf) >>? fun raw ->
|
||||
) @@ (fun () -> Parser.interactive_expr read lexbuf) >>? fun raw ->
|
||||
ok raw
|
||||
|
@ -346,7 +346,7 @@ and conditional = {
|
||||
|
||||
let sprintf = Printf.sprintf
|
||||
|
||||
let region_of_type_expr = function
|
||||
let type_expr_to_region = function
|
||||
TProd {region; _}
|
||||
| TSum {region; _}
|
||||
| TRecord {region; _}
|
||||
@ -355,12 +355,11 @@ let region_of_type_expr = function
|
||||
| TPar {region; _}
|
||||
| TAlias {region; _} -> region
|
||||
|
||||
|
||||
let region_of_list_pattern = function
|
||||
let list_pattern_to_region = function
|
||||
Sugar {region; _} | PCons {region; _} -> region
|
||||
|
||||
let region_of_pattern = function
|
||||
PList p -> region_of_list_pattern p
|
||||
let pattern_to_region = function
|
||||
PList p -> list_pattern_to_region p
|
||||
| PTuple {region;_} | PVar {region;_}
|
||||
| PUnit {region;_} | PInt {region;_}
|
||||
| PTrue region | PFalse region
|
||||
@ -368,38 +367,38 @@ let region_of_pattern = function
|
||||
| PConstr {region; _} | PPar {region;_}
|
||||
| PRecord {region; _} | PTyped {region; _} -> region
|
||||
|
||||
let region_of_bool_expr = function
|
||||
let bool_expr_to_region = function
|
||||
Or {region;_} | And {region;_}
|
||||
| True region | False region
|
||||
| Not {region;_} -> region
|
||||
|
||||
let region_of_comp_expr = function
|
||||
let comp_expr_to_region = function
|
||||
Lt {region;_} | Leq {region;_}
|
||||
| Gt {region;_} | Geq {region;_}
|
||||
| Neq {region;_} | Equal {region;_} -> region
|
||||
|
||||
let region_of_logic_expr = function
|
||||
BoolExpr e -> region_of_bool_expr e
|
||||
| CompExpr e -> region_of_comp_expr e
|
||||
let logic_expr_to_region = function
|
||||
BoolExpr e -> bool_expr_to_region e
|
||||
| CompExpr e -> comp_expr_to_region e
|
||||
|
||||
let region_of_arith_expr = function
|
||||
let arith_expr_to_region = function
|
||||
Add {region;_} | Sub {region;_} | Mult {region;_}
|
||||
| Div {region;_} | Mod {region;_} | Neg {region;_}
|
||||
| Int {region;_} | Mtz {region; _}
|
||||
| Nat {region; _} -> region
|
||||
|
||||
let region_of_string_expr = function
|
||||
let string_expr_to_region = function
|
||||
String {region;_} | Cat {region;_} -> region
|
||||
|
||||
let region_of_list_expr = function
|
||||
let list_expr_to_region = function
|
||||
Cons {region; _} | List {region; _}
|
||||
(* | Append {region; _}*) -> region
|
||||
|
||||
let region_of_expr = function
|
||||
ELogic e -> region_of_logic_expr e
|
||||
| EArith e -> region_of_arith_expr e
|
||||
| EString e -> region_of_string_expr e
|
||||
| EList e -> region_of_list_expr e
|
||||
let expr_to_region = function
|
||||
ELogic e -> logic_expr_to_region e
|
||||
| EArith e -> arith_expr_to_region e
|
||||
| EString e -> string_expr_to_region e
|
||||
| EList e -> list_expr_to_region e
|
||||
| EAnnot {region;_ } | ELetIn {region;_} | EFun {region;_}
|
||||
| ECond {region;_} | ETuple {region;_} | ECase {region;_}
|
||||
| ECall {region;_} | EVar {region; _} | EProj {region; _}
|
||||
|
@ -470,9 +470,9 @@ val print_tokens : (*?undo:bool ->*) ast -> unit
|
||||
(* Projecting regions from sundry nodes of the AST. See the first
|
||||
comment at the beginning of this file. *)
|
||||
|
||||
val region_of_pattern : pattern -> Region.t
|
||||
val region_of_expr : expr -> Region.t
|
||||
val region_of_type_expr : type_expr -> Region.t
|
||||
val pattern_to_region : pattern -> Region.t
|
||||
val expr_to_region : expr -> Region.t
|
||||
val type_expr_to_region : type_expr -> Region.t
|
||||
|
||||
(* Simplifications *)
|
||||
|
||||
|
@ -47,9 +47,9 @@ let rec mk_field_path (rank, tail) =
|
||||
|
||||
(* Entry points *)
|
||||
|
||||
%start program expr
|
||||
%start program interactive_expr
|
||||
%type <AST.t> program
|
||||
%type <AST.expr> expr
|
||||
%type <AST.expr> interactive_expr
|
||||
|
||||
%%
|
||||
|
||||
@ -382,6 +382,9 @@ tail:
|
||||
|
||||
(* Expressions *)
|
||||
|
||||
interactive_expr:
|
||||
expr EOF { $1 }
|
||||
|
||||
expr:
|
||||
base_cond__open(expr) { $1 }
|
||||
| reg(match_expr(base_cond)) { ECase $1 }
|
||||
|
@ -315,8 +315,9 @@ and statement =
|
||||
| Data of data_decl
|
||||
|
||||
and local_decl =
|
||||
LocalLam of lambda_decl
|
||||
| LocalData of data_decl
|
||||
LocalFun of fun_decl reg
|
||||
| LocalProc of proc_decl reg
|
||||
| LocalData of data_decl
|
||||
|
||||
and data_decl =
|
||||
LocalConst of const_decl reg
|
||||
@ -785,9 +786,8 @@ let pattern_to_region = function
|
||||
| PTuple {region; _} -> region
|
||||
|
||||
let local_decl_to_region = function
|
||||
LocalLam FunDecl {region; _}
|
||||
| LocalLam ProcDecl {region; _}
|
||||
| LocalLam EntryDecl {region; _}
|
||||
LocalFun {region; _}
|
||||
| LocalProc {region; _}
|
||||
| LocalData LocalConst {region; _}
|
||||
| LocalData LocalVar {region; _} -> region
|
||||
|
||||
|
@ -299,8 +299,9 @@ and statement =
|
||||
| Data of data_decl
|
||||
|
||||
and local_decl =
|
||||
LocalLam of lambda_decl
|
||||
| LocalData of data_decl
|
||||
LocalFun of fun_decl reg
|
||||
| LocalProc of proc_decl reg
|
||||
| LocalData of data_decl
|
||||
|
||||
and data_decl =
|
||||
LocalConst of const_decl reg
|
||||
|
@ -426,8 +426,9 @@ open_var_decl:
|
||||
in {region; value}}
|
||||
|
||||
local_decl:
|
||||
lambda_decl { LocalLam $1 }
|
||||
| data_decl { LocalData $1 }
|
||||
fun_decl { LocalFun $1 }
|
||||
| proc_decl { LocalProc $1 }
|
||||
| data_decl { LocalData $1 }
|
||||
|
||||
data_decl:
|
||||
const_decl { LocalConst $1 }
|
||||
|
@ -251,7 +251,8 @@ and print_local_decls sequence =
|
||||
List.iter print_local_decl sequence
|
||||
|
||||
and print_local_decl = function
|
||||
LocalLam decl -> print_lambda_decl decl
|
||||
LocalFun decl -> print_fun_decl decl
|
||||
| LocalProc decl -> print_proc_decl decl
|
||||
| LocalData decl -> print_data_decl decl
|
||||
|
||||
and print_data_decl = function
|
||||
|
@ -7,7 +7,7 @@
|
||||
parser
|
||||
ast_simplified
|
||||
operators)
|
||||
(modules ligodity pascaligo camligo simplify)
|
||||
(modules ligodity pascaligo simplify)
|
||||
(preprocess
|
||||
(pps
|
||||
simple-utils.ppx_let_generalized
|
||||
|
@ -17,6 +17,151 @@ let pseq_to_list = function
|
||||
| Some lst -> npseq_to_list lst
|
||||
let get_value : 'a Raw.reg -> 'a = fun x -> x.value
|
||||
|
||||
module Errors = struct
|
||||
let wrong_pattern expected_name actual =
|
||||
let title () = "wrong pattern" in
|
||||
let message () = "" in
|
||||
let data = [
|
||||
("expected", fun () -> expected_name);
|
||||
("actual_loc" , fun () -> Format.asprintf "%a" Location.pp_lift @@ Raw.pattern_to_region actual)
|
||||
] in
|
||||
error ~data title message
|
||||
|
||||
let multiple_patterns construct (patterns: Raw.pattern list) =
|
||||
let title () = "multiple patterns" in
|
||||
let message () =
|
||||
Format.asprintf "multiple patterns in \"%s\" are not supported yet" construct in
|
||||
let patterns_loc =
|
||||
List.fold_left (fun a p -> Region.cover a (Raw.pattern_to_region p))
|
||||
Region.min patterns in
|
||||
let data = [
|
||||
("patterns_loc", fun () -> Format.asprintf "%a" Location.pp_lift @@ patterns_loc)
|
||||
] in
|
||||
error ~data title message
|
||||
|
||||
let unknown_predefined_type name =
|
||||
let title () = "type constants" in
|
||||
let message () =
|
||||
Format.asprintf "unknown predefined type \"%s\"" name.Region.value in
|
||||
let data = [
|
||||
("typename_loc",
|
||||
fun () -> Format.asprintf "%a" Location.pp_lift @@ name.Region.region)
|
||||
] in
|
||||
error ~data title message
|
||||
|
||||
let unsupported_arith_op expr =
|
||||
let title () = "arithmetic expressions" in
|
||||
let message () =
|
||||
Format.asprintf "this arithmetic operator is not supported yet" in
|
||||
let expr_loc = Raw.expr_to_region expr in
|
||||
let data = [
|
||||
("expr_loc",
|
||||
fun () -> Format.asprintf "%a" Location.pp_lift @@ expr_loc)
|
||||
] in
|
||||
error ~data title message
|
||||
|
||||
let unsupported_string_catenation expr =
|
||||
let title () = "string expressions" in
|
||||
let message () =
|
||||
Format.asprintf "string concatenation is not supported yet" in
|
||||
let expr_loc = Raw.expr_to_region expr in
|
||||
let data = [
|
||||
("expr_loc",
|
||||
fun () -> Format.asprintf "%a" Location.pp_lift @@ expr_loc)
|
||||
] in
|
||||
error ~data title message
|
||||
|
||||
let untyped_fun_param var =
|
||||
let title () = "function parameter" in
|
||||
let message () =
|
||||
Format.asprintf "untyped function parameters are not supported yet" in
|
||||
let param_loc = var.Region.region in
|
||||
let data = [
|
||||
("param_loc",
|
||||
fun () -> Format.asprintf "%a" Location.pp_lift @@ param_loc)
|
||||
] in
|
||||
error ~data title message
|
||||
|
||||
let unsupported_tuple_pattern p =
|
||||
let title () = "tuple pattern" in
|
||||
let message () =
|
||||
Format.asprintf "tuple patterns are not supported yet" in
|
||||
let pattern_loc = Raw.pattern_to_region p in
|
||||
let data = [
|
||||
("pattern_loc",
|
||||
fun () -> Format.asprintf "%a" Location.pp_lift @@ pattern_loc)
|
||||
] in
|
||||
error ~data title message
|
||||
|
||||
let unsupported_cst_constr p =
|
||||
let title () = "constant constructor" in
|
||||
let message () =
|
||||
Format.asprintf "constant constructors are not supported yet" in
|
||||
let pattern_loc = Raw.pattern_to_region p in
|
||||
let data = [
|
||||
("pattern_loc",
|
||||
fun () -> Format.asprintf "%a" Location.pp_lift @@ pattern_loc)
|
||||
] in
|
||||
error ~data title message
|
||||
|
||||
let unsupported_non_var_pattern p =
|
||||
let title () = "pattern is not a variable" in
|
||||
let message () =
|
||||
Format.asprintf "non-variable patterns in constructors \
|
||||
are not supported yet" in
|
||||
let pattern_loc = Raw.pattern_to_region p in
|
||||
let data = [
|
||||
("pattern_loc",
|
||||
fun () -> Format.asprintf "%a" Location.pp_lift @@ pattern_loc)
|
||||
] in
|
||||
error ~data title message
|
||||
|
||||
let simplifying_expr t =
|
||||
let title () = "simplifying expression" in
|
||||
let message () = "" in
|
||||
let data = [
|
||||
("expression" ,
|
||||
thunk @@ Format.asprintf "%a" (PP_helpers.printer Raw.print_expr) t)
|
||||
] in
|
||||
error ~data title message
|
||||
|
||||
let only_constructors p =
|
||||
let title () = "constructors in patterns" in
|
||||
let message () =
|
||||
Format.asprintf "currently, only constructors are supported in patterns" in
|
||||
let pattern_loc = Raw.pattern_to_region p in
|
||||
let data = [
|
||||
("pattern_loc",
|
||||
fun () -> Format.asprintf "%a" Location.pp_lift @@ pattern_loc)
|
||||
] in
|
||||
error ~data title message
|
||||
|
||||
let unsupported_sugared_lists region =
|
||||
let title () = "lists in patterns" in
|
||||
let message () =
|
||||
Format.asprintf "currently, only empty lists and constructors (::) \
|
||||
are supported in patterns" in
|
||||
let data = [
|
||||
("pattern_loc",
|
||||
fun () -> Format.asprintf "%a" Location.pp_lift @@ region)
|
||||
] in
|
||||
error ~data title message
|
||||
|
||||
let corner_case ~loc message =
|
||||
let title () = "corner case" in
|
||||
let content () = "We don't have a good error message for this case. \
|
||||
We are striving find ways to better report them and \
|
||||
find the use-cases that generate them. \
|
||||
Please report this to the developers." in
|
||||
let data = [
|
||||
("location" , fun () -> loc) ;
|
||||
("message" , fun () -> message) ;
|
||||
] in
|
||||
error ~data title content
|
||||
end
|
||||
|
||||
open Errors
|
||||
|
||||
open Operators.Simplify.Ligodity
|
||||
|
||||
let r_split = Location.r_split
|
||||
@ -25,7 +170,7 @@ let rec pattern_to_var : Raw.pattern -> _ = fun p ->
|
||||
match p with
|
||||
| Raw.PPar p -> pattern_to_var p.value.inside
|
||||
| Raw.PVar v -> ok v
|
||||
| _ -> simple_fail "not a var"
|
||||
| _ -> fail @@ wrong_pattern "var" p
|
||||
|
||||
let rec pattern_to_typed_var : Raw.pattern -> _ = fun p ->
|
||||
match p with
|
||||
@ -36,7 +181,7 @@ let rec pattern_to_typed_var : Raw.pattern -> _ = fun p ->
|
||||
ok (v , Some tp.type_expr)
|
||||
)
|
||||
| Raw.PVar v -> ok (v , None)
|
||||
| _ -> simple_fail "not a var"
|
||||
| _ -> fail @@ wrong_pattern "typed variable" p
|
||||
|
||||
let rec expr_to_typed_expr : Raw.expr -> _ = fun e ->
|
||||
match e with
|
||||
@ -45,11 +190,13 @@ let rec expr_to_typed_expr : Raw.expr -> _ = fun e ->
|
||||
| _ -> ok (e , None)
|
||||
|
||||
let patterns_to_var : Raw.pattern list -> _ = fun ps ->
|
||||
let%bind () = Assert.assert_list_size ps 1 in
|
||||
pattern_to_var @@ List.hd ps
|
||||
match ps with
|
||||
| [ pattern ] -> pattern_to_var pattern
|
||||
| _ -> fail @@ multiple_patterns "let" ps
|
||||
|
||||
let rec simpl_type_expression : Raw.type_expr -> type_expression result =
|
||||
function
|
||||
let rec simpl_type_expression : Raw.type_expr -> type_expression result = fun te ->
|
||||
trace (simple_info "simplifying this type expression...") @@
|
||||
match te with
|
||||
| TPar x -> simpl_type_expression x.value.inside
|
||||
| TAlias v -> (
|
||||
match List.assoc_opt v.value type_constants with
|
||||
@ -59,26 +206,34 @@ let rec simpl_type_expression : Raw.type_expr -> type_expression result =
|
||||
| TFun x -> (
|
||||
let%bind (a , b) =
|
||||
let (a , _ , b) = x.value in
|
||||
bind_map_pair simpl_type_expression (a , b) in
|
||||
let%bind a = simpl_type_expression a in
|
||||
let%bind b = simpl_type_expression b in
|
||||
ok (a , b)
|
||||
in
|
||||
ok @@ T_function (a , b)
|
||||
)
|
||||
| TApp x ->
|
||||
| TApp x -> (
|
||||
let (name, tuple) = x.value in
|
||||
let lst = npseq_to_list tuple.value.inside in
|
||||
let%bind cst =
|
||||
trace_option (simple_error "unrecognized type constants") @@
|
||||
List.assoc_opt name.value type_constants in
|
||||
let%bind lst' = bind_list @@ List.map simpl_type_expression lst in
|
||||
trace_option (unknown_predefined_type name) @@
|
||||
List.assoc_opt name.value type_constants
|
||||
in
|
||||
let%bind lst' = bind_map_list simpl_type_expression lst in
|
||||
ok @@ T_constant (cst , lst')
|
||||
| TProd p ->
|
||||
let%bind tpl = simpl_list_type_expression
|
||||
@@ npseq_to_list p.value in
|
||||
)
|
||||
| TProd p -> (
|
||||
let%bind tpl = simpl_list_type_expression @@ npseq_to_list p.value in
|
||||
ok tpl
|
||||
)
|
||||
| TRecord r ->
|
||||
let aux = fun (x, y) -> let%bind y = simpl_type_expression y in ok (x, y) in
|
||||
let%bind lst = bind_list
|
||||
let apply (x:Raw.field_decl Raw.reg) =
|
||||
(x.value.field_name.value, x.value.field_type) in
|
||||
let%bind lst =
|
||||
bind_list
|
||||
@@ List.map aux
|
||||
@@ List.map (fun (x:Raw.field_decl Raw.reg) -> (x.value.field_name.value, x.value.field_type))
|
||||
@@ List.map apply
|
||||
@@ pseq_to_list r.value.elements in
|
||||
let m = List.fold_left (fun m (x, y) -> SMap.add x y m) SMap.empty lst in
|
||||
ok @@ T_record m
|
||||
@ -104,7 +259,7 @@ and simpl_list_type_expression (lst:Raw.type_expr list) : type_expression result
|
||||
| [] -> assert false
|
||||
| [hd] -> simpl_type_expression hd
|
||||
| lst ->
|
||||
let%bind lst = bind_list @@ List.map simpl_type_expression lst in
|
||||
let%bind lst = bind_map_list simpl_type_expression lst in
|
||||
ok @@ T_tuple lst
|
||||
|
||||
let rec simpl_expression :
|
||||
@ -128,14 +283,7 @@ let rec simpl_expression :
|
||||
return @@ e_accessor ~loc var path'
|
||||
in
|
||||
|
||||
trace (
|
||||
let title () = "simplifying expression" in
|
||||
let message () = "" in
|
||||
let data = [
|
||||
("expression" , thunk @@ Format.asprintf "%a" (PP_helpers.printer Raw.print_expr) t)
|
||||
] in
|
||||
error ~data title message
|
||||
) @@
|
||||
trace (simplifying_expr t) @@
|
||||
match t with
|
||||
| Raw.ELetIn e -> (
|
||||
let Raw.{binding ; body ; _} = e.value in
|
||||
@ -240,7 +388,8 @@ let rec simpl_expression :
|
||||
let n = Z.to_int @@ snd @@ n in
|
||||
return @@ e_literal ~loc (Literal_tez n)
|
||||
)
|
||||
| EArith _ -> simple_fail "arith: not supported yet"
|
||||
| EArith _ as e ->
|
||||
fail @@ unsupported_arith_op e
|
||||
| EString (String s) -> (
|
||||
let (s , loc) = r_split s in
|
||||
let s' =
|
||||
@ -249,7 +398,8 @@ let rec simpl_expression :
|
||||
in
|
||||
return @@ e_literal ~loc (Literal_string s')
|
||||
)
|
||||
| EString _ -> simple_fail "string: not supported yet"
|
||||
| EString (Cat _) as e ->
|
||||
fail @@ unsupported_string_catenation e
|
||||
| ELogic l -> simpl_logic_expression l
|
||||
| EList l -> simpl_list_expression l
|
||||
| ECase c -> (
|
||||
@ -321,7 +471,7 @@ and simpl_fun lamb' : expr result =
|
||||
| "storage" , None ->
|
||||
ok (var , T_variable "storage")
|
||||
| _ , None ->
|
||||
simple_fail "untyped function parameter"
|
||||
fail @@ untyped_fun_param var
|
||||
| _ , Some ty -> (
|
||||
let%bind ty' = simpl_type_expression ty in
|
||||
ok (var , ty')
|
||||
@ -411,21 +561,24 @@ and simpl_tuple_expression ?loc (lst:Raw.expr list) : expression result =
|
||||
let%bind lst = bind_list @@ List.map simpl_expression lst in
|
||||
return @@ e_tuple ?loc lst
|
||||
|
||||
and simpl_declaration : Raw.declaration -> declaration Location.wrap result = fun t ->
|
||||
and simpl_declaration : Raw.declaration -> declaration Location.wrap result =
|
||||
fun t ->
|
||||
let open! Raw in
|
||||
let loc : 'a . 'a Raw.reg -> _ -> _ = fun x v -> Location.wrap ~loc:(File x.region) v in
|
||||
let loc : 'a . 'a Raw.reg -> _ -> _ =
|
||||
fun x v -> Location.wrap ~loc:(File x.region) v in
|
||||
match t with
|
||||
| TypeDecl x ->
|
||||
let {name;type_expr} : Raw.type_decl = x.value in
|
||||
let%bind type_expression = simpl_type_expression type_expr in
|
||||
ok @@ loc x @@ Declaration_type (name.value , type_expression)
|
||||
| LetEntry x (* -> simple_fail "no entry point yet" *)
|
||||
| LetEntry x
|
||||
| Let x -> (
|
||||
let _ , binding = x.value in
|
||||
let {bindings ; lhs_type ; let_rhs} = binding in
|
||||
let%bind (var , args) =
|
||||
let%bind (hd , tl) = match bindings with
|
||||
| [] -> simple_fail "let without bindgings"
|
||||
let%bind (hd , tl) =
|
||||
match bindings with
|
||||
| [] -> fail @@ corner_case ~loc:__LOC__ "let without bindings"
|
||||
| hd :: tl -> ok (hd , tl)
|
||||
in
|
||||
let%bind var = pattern_to_var hd in
|
||||
@ -452,55 +605,58 @@ and simpl_declaration : Raw.declaration -> declaration Location.wrap result = fu
|
||||
)
|
||||
)
|
||||
|
||||
and simpl_cases : type a . (Raw.pattern * a) list -> a matching result = fun t ->
|
||||
and simpl_cases : type a . (Raw.pattern * a) list -> a matching result =
|
||||
fun t ->
|
||||
let open Raw in
|
||||
let get_var (t:Raw.pattern) = match t with
|
||||
let rec get_var (t:Raw.pattern) =
|
||||
match t with
|
||||
| PVar v -> ok v.value
|
||||
| _ ->
|
||||
let error =
|
||||
let title () = "not a var" in
|
||||
let content () = Format.asprintf "%a" (PP_helpers.printer Raw.print_pattern) t in
|
||||
error title content
|
||||
in
|
||||
fail error
|
||||
| PPar p -> get_var p.value.inside
|
||||
| _ -> fail @@ unsupported_non_var_pattern t
|
||||
in
|
||||
let get_tuple (t:Raw.pattern) = match t with
|
||||
let rec get_tuple (t:Raw.pattern) =
|
||||
match t with
|
||||
| PTuple v -> npseq_to_list v.value
|
||||
| PPar p -> get_tuple p.value.inside
|
||||
| x -> [ x ]
|
||||
in
|
||||
let get_single (t:Raw.pattern) =
|
||||
let t' = get_tuple t in
|
||||
let%bind () =
|
||||
trace_strong (simple_error "not single") @@
|
||||
trace_strong (unsupported_tuple_pattern t) @@
|
||||
Assert.assert_list_size t' 1 in
|
||||
ok (List.hd t') in
|
||||
let get_constr (t:Raw.pattern) = match t with
|
||||
ok (List.hd t')
|
||||
in
|
||||
let rec get_constr (t:Raw.pattern) =
|
||||
match t with
|
||||
| PPar p -> get_constr p.value.inside
|
||||
| PConstr v -> (
|
||||
let (const , pat_opt) = v.value in
|
||||
let%bind pat =
|
||||
trace_option (simple_error "No constructor without variable yet") @@
|
||||
trace_option (unsupported_cst_constr t) @@
|
||||
pat_opt in
|
||||
let%bind single_pat = get_single pat in
|
||||
let%bind var = get_var single_pat in
|
||||
ok (const.value , var)
|
||||
)
|
||||
| _ -> simple_fail "not a constr"
|
||||
| _ -> fail @@ only_constructors t
|
||||
in
|
||||
let%bind patterns =
|
||||
let aux (x , y) =
|
||||
let xs = get_tuple x in
|
||||
trace_strong (simple_error "no tuple in patterns yet") @@
|
||||
trace_strong (unsupported_tuple_pattern x) @@
|
||||
Assert.assert_list_size xs 1 >>? fun () ->
|
||||
ok (List.hd xs , y)
|
||||
in
|
||||
bind_map_list aux t in
|
||||
match patterns with
|
||||
| [(PFalse _ , f) ; (PTrue _ , t)]
|
||||
| [(PTrue _ , t) ; (PFalse _ , f)] -> ok @@ Match_bool {match_true = t ; match_false = f}
|
||||
| [(PTrue _ , t) ; (PFalse _ , f)] ->
|
||||
ok @@ Match_bool {match_true = t ; match_false = f}
|
||||
| [(PList (PCons c) , cons) ; (PList (Sugar sugar_nil) , nil)]
|
||||
| [(PList (Sugar sugar_nil) , nil) ; (PList (PCons c), cons)] -> (
|
||||
let%bind () =
|
||||
trace_strong (simple_error "Only empty list patterns and cons are allowed yet")
|
||||
trace_strong (unsupported_sugared_lists sugar_nil.region)
|
||||
@@ Assert.assert_list_empty
|
||||
@@ pseq_to_list
|
||||
@@ sugar_nil.value.elements in
|
||||
@ -513,7 +669,8 @@ and simpl_cases : type a . (Raw.pattern * a) list -> a matching result = fun t -
|
||||
ok @@ Match_list {match_cons = (a, b, cons) ; match_nil = nil}
|
||||
)
|
||||
| lst -> (
|
||||
trace (simple_error "weird patterns not supported yet") @@
|
||||
trace (simple_info "currently, only booleans, lists and constructors \
|
||||
are supported in patterns") @@
|
||||
let%bind constrs =
|
||||
let aux (x , y) =
|
||||
let error =
|
||||
|
@ -14,6 +14,94 @@ let pseq_to_list = function
|
||||
| Some lst -> npseq_to_list lst
|
||||
let get_value : 'a Raw.reg -> 'a = fun x -> x.value
|
||||
|
||||
module Errors = struct
|
||||
let unsupported_entry_decl decl =
|
||||
let title () = "entry point declarations" in
|
||||
let message () =
|
||||
Format.asprintf "entry points within the contract are not supported yet" in
|
||||
let data = [
|
||||
("declaration",
|
||||
fun () -> Format.asprintf "%a" Location.pp_lift @@ decl.Region.region)
|
||||
] in
|
||||
error ~data title message
|
||||
|
||||
let unsupported_proc_decl decl =
|
||||
let title () = "procedure declarations" in
|
||||
let message () =
|
||||
Format.asprintf "procedures are not supported yet" in
|
||||
let data = [
|
||||
("declaration",
|
||||
fun () -> Format.asprintf "%a" Location.pp_lift @@ decl.Region.region)
|
||||
] in
|
||||
error ~data title message
|
||||
|
||||
let unsupported_local_proc region =
|
||||
let title () = "local procedure declarations" in
|
||||
let message () =
|
||||
Format.asprintf "local procedures are not supported yet" in
|
||||
let data = [
|
||||
("declaration",
|
||||
fun () -> Format.asprintf "%a" Location.pp_lift @@ region)
|
||||
] in
|
||||
error ~data title message
|
||||
|
||||
let corner_case ~loc message =
|
||||
let title () = "corner case" in
|
||||
let content () = "We don't have a good error message for this case. \
|
||||
We are striving find ways to better report them and \
|
||||
find the use-cases that generate them. \
|
||||
Please report this to the developers." in
|
||||
let data = [
|
||||
("location" , fun () -> loc) ;
|
||||
("message" , fun () -> message) ;
|
||||
] in
|
||||
error ~data title content
|
||||
|
||||
let unknown_predefined_type name =
|
||||
let title () = "type constants" in
|
||||
let message () =
|
||||
Format.asprintf "unknown predefined type \"%s\"" name.Region.value in
|
||||
let data = [
|
||||
("typename_loc",
|
||||
fun () -> Format.asprintf "%a" Location.pp_lift @@ name.Region.region)
|
||||
] in
|
||||
error ~data title message
|
||||
|
||||
let unsupported_arith_op expr =
|
||||
let title () = "arithmetic expressions" in
|
||||
let message () =
|
||||
Format.asprintf "this arithmetic operator is not supported yet" in
|
||||
let expr_loc = Raw.expr_to_region expr in
|
||||
let data = [
|
||||
("expr_loc",
|
||||
fun () -> Format.asprintf "%a" Location.pp_lift @@ expr_loc)
|
||||
] in
|
||||
error ~data title message
|
||||
|
||||
let unsupported_string_catenation expr =
|
||||
let title () = "string expressions" in
|
||||
let message () =
|
||||
Format.asprintf "string concatenation is not supported yet" in
|
||||
let expr_loc = Raw.expr_to_region expr in
|
||||
let data = [
|
||||
("expr_loc",
|
||||
fun () -> Format.asprintf "%a" Location.pp_lift @@ expr_loc)
|
||||
] in
|
||||
error ~data title message
|
||||
|
||||
let unsupported_set_expr expr =
|
||||
let title () = "set expressions" in
|
||||
let message () =
|
||||
Format.asprintf "set type is not supported yet" in
|
||||
let expr_loc = Raw.expr_to_region expr in
|
||||
let data = [
|
||||
("expr_loc",
|
||||
fun () -> Format.asprintf "%a" Location.pp_lift @@ expr_loc)
|
||||
] in
|
||||
error ~data title message
|
||||
end
|
||||
|
||||
open Errors
|
||||
open Operators.Simplify.Pascaligo
|
||||
|
||||
let r_split = Location.r_split
|
||||
@ -26,7 +114,7 @@ let return expr = ok @@ fun expr'_opt ->
|
||||
|
||||
let return_let_in ?loc binder rhs = ok @@ fun expr'_opt ->
|
||||
match expr'_opt with
|
||||
| None -> simple_fail "missing return" (* Hard to explain. Shouldn't happen in prod. *)
|
||||
| None -> fail @@ corner_case ~loc:__LOC__ "missing return"
|
||||
| Some expr' -> ok @@ e_let_in ?loc binder rhs expr'
|
||||
|
||||
let rec simpl_type_expression (t:Raw.type_expr) : type_expression result =
|
||||
@ -48,7 +136,7 @@ let rec simpl_type_expression (t:Raw.type_expr) : type_expression result =
|
||||
let lst = npseq_to_list tuple.value.inside in
|
||||
let%bind lst' = bind_list @@ List.map simpl_type_expression lst in
|
||||
let%bind cst =
|
||||
trace_option (simple_error "unrecognized type constants") @@
|
||||
trace_option (unknown_predefined_type name) @@
|
||||
List.assoc_opt name.value type_constants in
|
||||
ok @@ T_constant (cst , lst')
|
||||
| TProd p ->
|
||||
@ -57,9 +145,11 @@ let rec simpl_type_expression (t:Raw.type_expr) : type_expression result =
|
||||
ok tpl
|
||||
| TRecord r ->
|
||||
let aux = fun (x, y) -> let%bind y = simpl_type_expression y in ok (x, y) in
|
||||
let apply =
|
||||
fun (x:Raw.field_decl Raw.reg) -> (x.value.field_name.value, x.value.field_type) in
|
||||
let%bind lst = bind_list
|
||||
@@ List.map aux
|
||||
@@ List.map (fun (x:Raw.field_decl Raw.reg) -> (x.value.field_name.value, x.value.field_type))
|
||||
@@ List.map apply
|
||||
@@ pseq_to_list r.value.elements in
|
||||
let m = List.fold_left (fun m (x, y) -> SMap.add x y m) SMap.empty lst in
|
||||
ok @@ T_record m
|
||||
@ -194,18 +284,20 @@ let rec simpl_expression (t:Raw.expr) : expr result =
|
||||
let n = Z.to_int @@ snd @@ n in
|
||||
return @@ e_literal ~loc (Literal_tez n)
|
||||
)
|
||||
| EArith _ -> simple_fail "arith: not supported yet"
|
||||
| EArith _ as e ->
|
||||
fail @@ unsupported_arith_op e
|
||||
| EString (String s) ->
|
||||
let (s , loc) = r_split s in
|
||||
let s' =
|
||||
(* S contains quotes *)
|
||||
String.(sub s 1 ((length s) - 2))
|
||||
String.(sub s 1 (length s - 2))
|
||||
in
|
||||
return @@ e_literal ~loc (Literal_string s')
|
||||
| EString _ -> simple_fail "string: not supported yet"
|
||||
| EString (Cat _) as e ->
|
||||
fail @@ unsupported_string_catenation e
|
||||
| ELogic l -> simpl_logic_expression l
|
||||
| EList l -> simpl_list_expression l
|
||||
| ESet _ -> simple_fail "set: not supported yet"
|
||||
| ESet _ -> fail @@ unsupported_set_expr t
|
||||
| ECase c -> (
|
||||
let (c , loc) = r_split c in
|
||||
let%bind e = simpl_expression c.expr in
|
||||
@ -224,10 +316,11 @@ let rec simpl_expression (t:Raw.expr) : expr result =
|
||||
let (mi , loc) = r_split mi in
|
||||
let%bind lst =
|
||||
let lst = List.map get_value @@ pseq_to_list mi.elements in
|
||||
let aux : Raw.binding -> (expression * expression) result = fun b ->
|
||||
let%bind src = simpl_expression b.source in
|
||||
let%bind dst = simpl_expression b.image in
|
||||
ok (src, dst) in
|
||||
let aux : Raw.binding -> (expression * expression) result =
|
||||
fun b ->
|
||||
let%bind src = simpl_expression b.source in
|
||||
let%bind dst = simpl_expression b.image in
|
||||
ok (src, dst) in
|
||||
bind_map_list aux lst in
|
||||
return @@ e_map ~loc lst
|
||||
)
|
||||
@ -309,26 +402,20 @@ and simpl_tuple_expression ?loc (lst:Raw.expr list) : expression result =
|
||||
match lst with
|
||||
| [] -> return @@ e_literal Literal_unit
|
||||
| [hd] -> simpl_expression hd
|
||||
| lst -> (
|
||||
| lst ->
|
||||
let%bind lst = bind_list @@ List.map simpl_expression lst in
|
||||
return @@ e_tuple ?loc lst
|
||||
)
|
||||
|
||||
and simpl_local_declaration : Raw.local_decl -> _ result = fun t ->
|
||||
match t with
|
||||
| LocalData d -> simpl_data_declaration d
|
||||
| LocalLam l -> simpl_lambda_declaration l
|
||||
|
||||
and simpl_lambda_declaration : Raw.lambda_decl -> _ result = fun l ->
|
||||
match l with
|
||||
| FunDecl f -> (
|
||||
| LocalData d ->
|
||||
simpl_data_declaration d
|
||||
| LocalFun f ->
|
||||
let (f , loc) = r_split f in
|
||||
let%bind (name , e) = simpl_fun_declaration ~loc f in
|
||||
return_let_in ~loc name e
|
||||
)
|
||||
| ProcDecl _ -> simple_fail "no local procedure yet"
|
||||
| EntryDecl _ -> simple_fail "no local entry-point yet"
|
||||
|
||||
| LocalProc d ->
|
||||
fail @@ unsupported_local_proc d.Region.region
|
||||
and simpl_data_declaration : Raw.data_decl -> _ result = fun t ->
|
||||
match t with
|
||||
| LocalVar x ->
|
||||
@ -344,7 +431,8 @@ and simpl_data_declaration : Raw.data_decl -> _ result = fun t ->
|
||||
let%bind expression = simpl_expression x.init in
|
||||
return_let_in ~loc (name , Some t) expression
|
||||
|
||||
and simpl_param : Raw.param_decl -> (type_name * type_expression) result = fun t ->
|
||||
and simpl_param : Raw.param_decl -> (type_name * type_expression) result =
|
||||
fun t ->
|
||||
match t with
|
||||
| ParamConst c ->
|
||||
let c = c.value in
|
||||
@ -357,11 +445,15 @@ and simpl_param : Raw.param_decl -> (type_name * type_expression) result = fun t
|
||||
let%bind type_expression = simpl_type_expression c.param_type in
|
||||
ok (type_name , type_expression)
|
||||
|
||||
and simpl_fun_declaration : loc:_ -> Raw.fun_decl -> ((name * type_expression option) * expression) result = fun ~loc x ->
|
||||
and simpl_fun_declaration :
|
||||
loc:_ -> Raw.fun_decl -> ((name * type_expression option) * expression) result =
|
||||
fun ~loc x ->
|
||||
let open! Raw in
|
||||
let {name;param;ret_type;local_decls;block;return} : fun_decl = x in
|
||||
(match npseq_to_list param.value.inside with
|
||||
| [] -> simple_fail "function without parameters are not allowed"
|
||||
| [] ->
|
||||
fail @@
|
||||
corner_case ~loc:__LOC__ "parameter-less function should not exist"
|
||||
| [a] -> (
|
||||
let%bind input = simpl_param a in
|
||||
let name = name.value in
|
||||
@ -390,7 +482,7 @@ and simpl_fun_declaration : loc:_ -> Raw.fun_decl -> ((name * type_expression op
|
||||
(arguments_name , type_expression) in
|
||||
let%bind tpl_declarations =
|
||||
let aux = fun i x ->
|
||||
let expr = e_accessor (e_variable arguments_name) [ Access_tuple i ] in
|
||||
let expr = e_accessor (e_variable arguments_name) [Access_tuple i] in
|
||||
let type_ = Some (snd x) in
|
||||
let ass = return_let_in (fst x , type_) expr in
|
||||
ass
|
||||
@ -407,12 +499,14 @@ and simpl_fun_declaration : loc:_ -> Raw.fun_decl -> ((name * type_expression op
|
||||
let%bind result =
|
||||
let aux prec cur = cur (Some prec) in
|
||||
bind_fold_right_list aux result body in
|
||||
let expression = e_lambda ~loc binder (Some input_type) (Some output_type) result in
|
||||
let expression =
|
||||
e_lambda ~loc binder (Some input_type) (Some output_type) result in
|
||||
let type_annotation = Some (T_function (input_type, output_type)) in
|
||||
ok ((name.value , type_annotation) , expression)
|
||||
)
|
||||
)
|
||||
and simpl_declaration : Raw.declaration -> declaration Location.wrap result = fun t ->
|
||||
and simpl_declaration : Raw.declaration -> declaration Location.wrap result =
|
||||
fun t ->
|
||||
let open! Raw in
|
||||
match t with
|
||||
| TypeDecl x -> (
|
||||
@ -434,15 +528,19 @@ and simpl_declaration : Raw.declaration -> declaration Location.wrap result = fu
|
||||
let%bind ((name , ty_opt) , expr) = simpl_fun_declaration ~loc x in
|
||||
ok @@ Location.wrap ~loc (Declaration_constant (name , ty_opt , expr))
|
||||
)
|
||||
| LambdaDecl (ProcDecl _) -> simple_fail "no proc declaration yet"
|
||||
| LambdaDecl (EntryDecl _)-> simple_fail "no entry point yet"
|
||||
| LambdaDecl (ProcDecl decl) ->
|
||||
fail @@ unsupported_proc_decl decl
|
||||
| LambdaDecl (EntryDecl decl) ->
|
||||
fail @@ unsupported_entry_decl decl
|
||||
|
||||
and simpl_statement : Raw.statement -> (_ -> expression result) result = fun s ->
|
||||
and simpl_statement : Raw.statement -> (_ -> expression result) result =
|
||||
fun s ->
|
||||
match s with
|
||||
| Instr i -> simpl_instruction i
|
||||
| Data d -> simpl_data_declaration d
|
||||
|
||||
and simpl_single_instruction : Raw.single_instr -> (_ -> expression result) result = fun t ->
|
||||
and simpl_single_instruction : Raw.single_instr -> (_ -> expression result) result =
|
||||
fun t ->
|
||||
match t with
|
||||
| ProcCall _ -> simple_fail "no proc call"
|
||||
| Fail e -> (
|
||||
|
@ -1,3 +1,2 @@
|
||||
module Pascaligo = Pascaligo
|
||||
module Camligo = Camligo
|
||||
module Ligodity = Ligodity
|
||||
|
@ -8,6 +8,6 @@ let compile_contract_basic () : unit result =
|
||||
in
|
||||
ok ()
|
||||
|
||||
let main = "Bin", [
|
||||
let main = test_suite "Bin" [
|
||||
test "compile contract basic" compile_contract_basic ;
|
||||
]
|
||||
|
@ -229,7 +229,7 @@ let sell () =
|
||||
ok ()
|
||||
|
||||
|
||||
let main = "Coase (End to End)", [
|
||||
let main = test_suite "Coase (End to End)" [
|
||||
test "buy" buy ;
|
||||
test "dispatch buy" dispatch_buy ;
|
||||
test "transfer" transfer ;
|
||||
|
@ -28,7 +28,7 @@ let multiple_vars () : unit result =
|
||||
let%bind _ = Assert.assert_equal_int ~msg:__LOC__ 42 result in
|
||||
ok ()
|
||||
|
||||
let main = "Compiler (from Mini_C)", [
|
||||
let main = test_suite "Compiler (from Mini_C)" [
|
||||
test "identity" identity ;
|
||||
test "multiple_vars" multiple_vars ;
|
||||
]
|
||||
|
@ -106,9 +106,9 @@ let pop () : unit result =
|
||||
| Trace.Ok (output , _) -> (
|
||||
Format.printf "\nPop output on %d : %a\n" n Ast_typed.PP.annotated_expression output ;
|
||||
)
|
||||
| Errors errs -> (
|
||||
| Trace.Error err -> (
|
||||
Format.printf "\nPop output on %d : error\n" n) ;
|
||||
Format.printf "Errors : {\n%a}\n%!" errors_pp (List.rev (List.rev_map (fun f -> f ()) errs)) ;
|
||||
Format.printf "Errors : {\n%a}\n%!" error_pp (err ()) ;
|
||||
) ;
|
||||
ok ()
|
||||
in
|
||||
@ -118,7 +118,7 @@ let pop () : unit result =
|
||||
simple_fail "display"
|
||||
(* ok () *)
|
||||
|
||||
let main = "Heap (End to End)", [
|
||||
let main = test_suite "Heap (End to End)" [
|
||||
test "is_empty" is_empty ;
|
||||
test "get_top" get_top ;
|
||||
test "pop_switch" pop_switch ;
|
||||
|
@ -456,7 +456,7 @@ let guess_the_hash_mligo () : unit result =
|
||||
let make_expected = fun n -> e_pair (e_typed_list [] t_operation) (e_int (42 + n)) in
|
||||
expect_eq_n program "main" make_input make_expected
|
||||
|
||||
let main = "Integration (End to End)", [
|
||||
let main = test_suite "Integration (End to End)" [
|
||||
test "type alias" type_alias ;
|
||||
test "function" function_ ;
|
||||
test "assign" assign ;
|
||||
|
@ -1,9 +1,50 @@
|
||||
(* -*- compile-command: "cd .. ; dune runtest" -*- *)
|
||||
|
||||
open Test_helpers
|
||||
|
||||
let rec test_height : test -> int = fun t ->
|
||||
match t with
|
||||
| Test _ -> 1
|
||||
| Test_suite (_ , lst) -> (List.fold_left max 1 @@ List.map test_height lst) + 1
|
||||
|
||||
let extract_test : test -> test_case = fun t ->
|
||||
match t with
|
||||
| Test tc -> tc
|
||||
| _ -> assert false
|
||||
|
||||
let extract_param : test -> (string * (string * test_case list) list) =
|
||||
let extract_element = extract_test in
|
||||
let extract_group : test -> (string * test_case list) = fun t ->
|
||||
match t with
|
||||
| Test tc -> ("isolated" , [ tc ])
|
||||
| Test_suite (name , lst) -> (name , List.map extract_element lst) in
|
||||
fun t ->
|
||||
match t with
|
||||
| Test tc -> ("" , [ ("isolated" , [ tc ] ) ])
|
||||
| Test_suite (name , lst) -> (name , List.map extract_group lst)
|
||||
|
||||
let x : _ -> (unit Alcotest.test) = fun x -> x
|
||||
|
||||
(*
|
||||
Alcotest.run parameters:
|
||||
string * (string * f list) list
|
||||
*)
|
||||
|
||||
let rec run_test ?(prefix = "") : test -> unit = fun t ->
|
||||
match t with
|
||||
| Test case -> Alcotest.run "isolated test" [ ("" , [ case ]) ]
|
||||
| Test_suite (name , lst) -> (
|
||||
if (test_height t <= 3) then (
|
||||
let (name , tests) = extract_param t in
|
||||
Alcotest.run (prefix ^ name) tests
|
||||
) else (
|
||||
List.iter (run_test ~prefix:(prefix ^ name ^ "_")) lst
|
||||
)
|
||||
)
|
||||
|
||||
let () =
|
||||
(* Printexc.record_backtrace true ; *)
|
||||
Alcotest.run "LIGO" [
|
||||
Multifix_tests.main ;
|
||||
run_test @@ test_suite "LIGO" [
|
||||
Integration_tests.main ;
|
||||
Compiler_tests.main ;
|
||||
Transpiler_tests.main ;
|
||||
|
@ -1,15 +1,24 @@
|
||||
open! Trace
|
||||
|
||||
type test_case = unit Alcotest.test_case
|
||||
type test =
|
||||
| Test_suite of (string * test list)
|
||||
| Test of test_case
|
||||
|
||||
let test name f =
|
||||
Alcotest.test_case name `Quick @@ fun () ->
|
||||
let result =
|
||||
trace (fun () -> error (thunk "running test") (thunk name) ()) @@
|
||||
Test (
|
||||
Alcotest.test_case name `Quick @@ fun () ->
|
||||
let result =
|
||||
trace (fun () -> error (thunk "running test") (thunk name) ()) @@
|
||||
f () in
|
||||
match result with
|
||||
| Ok ((), annotations) -> ignore annotations; ()
|
||||
| Errors errs ->
|
||||
Format.printf "Errors : {\n%a}\n%!" errors_pp (List.rev (List.rev_map (fun f -> f ()) errs)) ;
|
||||
match result with
|
||||
| Ok ((), annotations) -> ignore annotations; ()
|
||||
| Error err ->
|
||||
Format.printf "Errors : {\n%a}\n%!" error_pp (err ()) ;
|
||||
raise Alcotest.Test_error
|
||||
)
|
||||
|
||||
let test_suite name lst = Test_suite (name , lst)
|
||||
|
||||
open Ast_simplified.Combinators
|
||||
|
||||
|
@ -1,12 +1,7 @@
|
||||
(* open Ligo_helpers.Trace
|
||||
* open Ligo.Mini_c
|
||||
* open Combinators
|
||||
* open Test_helpers *)
|
||||
* open Combinators *)
|
||||
open Test_helpers
|
||||
|
||||
(*
|
||||
How should one test the transpiler?
|
||||
I'm doing the dumb thing.
|
||||
*)
|
||||
|
||||
let main = "Transpiler (from Ast_typed)", [
|
||||
let main = test_suite "Transpiler (from Ast_typed)" [
|
||||
]
|
||||
|
@ -65,7 +65,7 @@ end
|
||||
(* TODO: deep types (e.g. record of record)
|
||||
TODO: negative tests (expected type error) *)
|
||||
|
||||
let main = "Typer (from simplified AST)", [
|
||||
let main = test_suite "Typer (from simplified AST)" [
|
||||
test "int" int ;
|
||||
test "unit" TestExpressions.unit ;
|
||||
test "int2" TestExpressions.int ;
|
||||
|
@ -15,6 +15,76 @@ let map_of_kv_list lst =
|
||||
let open AST.SMap in
|
||||
List.fold_left (fun prev (k, v) -> add k v prev) empty lst
|
||||
|
||||
module Errors = struct
|
||||
let corner_case ~loc message =
|
||||
let title () = "corner case" in
|
||||
let content () = "we don't have a good error message for this case. we are
|
||||
striving find ways to better report them and find the use-cases that generate
|
||||
them. please report this to the developers." in
|
||||
let data = [
|
||||
("location" , fun () -> loc) ;
|
||||
("message" , fun () -> message) ;
|
||||
] in
|
||||
error ~data title content
|
||||
|
||||
let unrecognized_type_constant name =
|
||||
let title () = "unrecognized type constant" in
|
||||
let content () = name in
|
||||
error title content
|
||||
|
||||
let unsupported_pattern_matching kind location =
|
||||
let title () = "unsupported pattern-matching" in
|
||||
let content () = Format.asprintf "%s patterns aren't supported yet" kind in
|
||||
let data = [
|
||||
("location" , fun () -> Format.asprintf "%a" Location.pp location) ;
|
||||
] in
|
||||
error ~data title content
|
||||
|
||||
let not_functional_main location =
|
||||
let title () = "not functional main" in
|
||||
let content () = "main should be a function" in
|
||||
let data = [
|
||||
("location" , fun () -> Format.asprintf "%a" Location.pp location) ;
|
||||
] in
|
||||
error ~data title content
|
||||
|
||||
let missing_entry_point name =
|
||||
let title () = "missing entry point" in
|
||||
let content () = "no entry point with the given name" in
|
||||
let data = [
|
||||
("name" , fun () -> name) ;
|
||||
] in
|
||||
error ~data title content
|
||||
|
||||
let wrong_mini_c_value expected_type actual =
|
||||
let title () = "illed typed intermediary value" in
|
||||
let content () = "type of intermediary value doesn't match what was expected" in
|
||||
let data = [
|
||||
("expected_type" , fun () -> expected_type) ;
|
||||
("actual" , fun () -> Format.asprintf "%a" Mini_c.PP.value actual ) ;
|
||||
] in
|
||||
error ~data title content
|
||||
|
||||
let bad_untranspile bad_type value =
|
||||
let title () = "untranspiling bad value" in
|
||||
let content () = Format.asprintf "can not untranspile %s" bad_type in
|
||||
let data = [
|
||||
("bad_type" , fun () -> bad_type) ;
|
||||
("value" , fun () -> Format.asprintf "%a" Mini_c.PP.value value) ;
|
||||
] in
|
||||
error ~data title content
|
||||
|
||||
let unknown_untranspile unknown_type value =
|
||||
let title () = "untranspiling unknown value" in
|
||||
let content () = Format.asprintf "can not untranspile %s" unknown_type in
|
||||
let data = [
|
||||
("unknown_type" , fun () -> unknown_type) ;
|
||||
("value" , fun () -> Format.asprintf "%a" Mini_c.PP.value value) ;
|
||||
] in
|
||||
error ~data title content
|
||||
end
|
||||
open Errors
|
||||
|
||||
let rec translate_type (t:AST.type_value) : type_value result =
|
||||
match t.type_value' with
|
||||
| T_constant ("bool", []) -> ok (T_base Base_bool)
|
||||
@ -37,12 +107,7 @@ let rec translate_type (t:AST.type_value) : type_value result =
|
||||
| T_constant ("option", [o]) ->
|
||||
let%bind o' = translate_type o in
|
||||
ok (T_option o')
|
||||
| T_constant (name , lst) ->
|
||||
let error =
|
||||
let title () = "unrecognized type constant" in
|
||||
let content () = Format.asprintf "%s (%d)" name (List.length lst) in
|
||||
error title content in
|
||||
fail error
|
||||
| T_constant (name , _lst) -> fail @@ unrecognized_type_constant name
|
||||
| T_sum m ->
|
||||
let node = Append_tree.of_list @@ list_of_map m in
|
||||
let aux a b : type_value result =
|
||||
@ -77,23 +142,13 @@ let tuple_access_to_lr : type_value -> type_value list -> int -> (type_value * [
|
||||
let node_tv = Append_tree.of_list @@ List.mapi (fun i a -> (i, a)) tys in
|
||||
let%bind path =
|
||||
let aux (i , _) = i = ind in
|
||||
trace_option (simple_error "no leaf with given index") @@
|
||||
trace_option (corner_case ~loc:__LOC__ "tuple access leaf") @@
|
||||
Append_tree.exists_path aux node_tv in
|
||||
let lr_path = List.map (fun b -> if b then `Right else `Left) path in
|
||||
let%bind (_ , lst) =
|
||||
let aux = fun (ty' , acc) cur ->
|
||||
let%bind (a , b) =
|
||||
let error =
|
||||
let title () = "expected a pair" in
|
||||
let content () = Format.asprintf "Big: %a.\tGot: %a\tFull path: %a\tSmall path: %a"
|
||||
Mini_c.PP.type_ ty
|
||||
Mini_c.PP.type_ ty'
|
||||
PP_helpers.(list_sep bool (const ".")) path
|
||||
PP_helpers.(list_sep lr (const ".")) (List.map snd acc)
|
||||
in
|
||||
error title content
|
||||
in
|
||||
trace_strong error @@
|
||||
trace_strong (corner_case ~loc:__LOC__ "tuple access pair") @@
|
||||
Mini_c.get_t_pair ty' in
|
||||
match cur with
|
||||
| `Left -> ok (a , acc @ [(a , `Left)])
|
||||
@ -107,12 +162,14 @@ let record_access_to_lr : type_value -> type_value AST.type_name_map -> string -
|
||||
let node_tv = Append_tree.of_list tys in
|
||||
let%bind path =
|
||||
let aux (i , _) = i = ind in
|
||||
trace_option (simple_error "no leaf with given index") @@
|
||||
trace_option (corner_case ~loc:__LOC__ "record access leaf") @@
|
||||
Append_tree.exists_path aux node_tv in
|
||||
let lr_path = List.map (fun b -> if b then `Right else `Left) path in
|
||||
let%bind (_ , lst) =
|
||||
let aux = fun (ty , acc) cur ->
|
||||
let%bind (a , b) = Mini_c.get_t_pair ty in
|
||||
let%bind (a , b) =
|
||||
trace_strong (corner_case ~loc:__LOC__ "recard access pair") @@
|
||||
Mini_c.get_t_pair ty in
|
||||
match cur with
|
||||
| `Left -> ok (a , acc @ [(a , `Left)])
|
||||
| `Right -> ok (b , acc @ [(b , `Right)] ) in
|
||||
@ -147,7 +204,6 @@ and transpile_small_environment : AST.small_environment -> Environment.t result
|
||||
ok @@ Environment.add (name , tv') prec
|
||||
in
|
||||
let%bind result =
|
||||
trace (simple_error "transpiling small environment") @@
|
||||
bind_fold_right_list aux Environment.empty x' in
|
||||
ok result
|
||||
|
||||
@ -163,8 +219,12 @@ and translate_annotated_expression (ae:AST.annotated_expression) : expression re
|
||||
let%bind tv = translate_type ae.type_annotation in
|
||||
let return ?(tv = tv) expr = ok @@ Combinators.Expression.make_tpl (expr, tv) in
|
||||
let f = translate_annotated_expression in
|
||||
let info =
|
||||
let title () = "translating expression" in
|
||||
let content () = Format.asprintf "%a" Location.pp ae.location in
|
||||
info title content in
|
||||
trace info @@
|
||||
match ae.expression with
|
||||
(* Optimise immediate application as a let-in *)
|
||||
| E_let_in {binder; rhs; result} ->
|
||||
let%bind rhs' = translate_annotated_expression rhs in
|
||||
let%bind result' = translate_annotated_expression result in
|
||||
@ -176,7 +236,7 @@ and translate_annotated_expression (ae:AST.annotated_expression) : expression re
|
||||
| E_literal l -> return @@ E_literal (translate_literal l)
|
||||
| E_variable name -> (
|
||||
let%bind ele =
|
||||
trace_option (simple_error "name not in environment") @@
|
||||
trace_option (corner_case ~loc:__LOC__ "name not in environment") @@
|
||||
AST.Environment.get_opt name ae.environment in
|
||||
let%bind tv = transpile_environment_element_type ele in
|
||||
return ~tv @@ E_variable name
|
||||
@ -185,14 +245,16 @@ and translate_annotated_expression (ae:AST.annotated_expression) : expression re
|
||||
let%bind a = translate_annotated_expression a in
|
||||
let%bind b = translate_annotated_expression b in
|
||||
return @@ E_application (a, b)
|
||||
| E_constructor (m, param) ->
|
||||
| E_constructor (m, param) -> (
|
||||
let%bind param' = translate_annotated_expression param in
|
||||
let (param'_expr , param'_tv) = Combinators.Expression.(get_content param' , get_type param') in
|
||||
let%bind node_tv = tree_of_sum ae.type_annotation in
|
||||
let%bind node_tv =
|
||||
trace_strong (corner_case ~loc:__LOC__ "getting lr tree") @@
|
||||
tree_of_sum ae.type_annotation in
|
||||
let leaf (k, tv) : (expression' option * type_value) result =
|
||||
if k = m then (
|
||||
let%bind _ =
|
||||
trace (simple_error "constructor parameter doesn't have expected type (shouldn't happen here)")
|
||||
trace_strong (corner_case ~loc:__LOC__ "wrong type for constructor parameter")
|
||||
@@ AST.assert_type_value_eq (tv, param.type_annotation) in
|
||||
ok (Some (param'_expr), param'_tv)
|
||||
) else (
|
||||
@ -204,16 +266,17 @@ and translate_annotated_expression (ae:AST.annotated_expression) : expression re
|
||||
let%bind b = b in
|
||||
match (a, b) with
|
||||
| (None, a), (None, b) -> ok (None, T_or (a, b))
|
||||
| (Some _, _), (Some _, _) -> simple_fail "several identical constructors in the same variant (shouldn't happen here)"
|
||||
| (Some _, _), (Some _, _) -> fail @@ corner_case ~loc:__LOC__ "multiple identical constructors in the same variant"
|
||||
| (Some v, a), (None, b) -> ok (Some (E_constant ("LEFT", [Combinators.Expression.make_tpl (v, a)])), T_or (a, b))
|
||||
| (None, a), (Some v, b) -> ok (Some (E_constant ("RIGHT", [Combinators.Expression.make_tpl (v, b)])), T_or (a, b))
|
||||
in
|
||||
let%bind (ae_opt, tv) = Append_tree.fold_ne leaf node node_tv in
|
||||
let%bind ae =
|
||||
trace_option (simple_error "constructor doesn't exist in claimed type (shouldn't happen here)")
|
||||
trace_option (corner_case ~loc:__LOC__ "inexistant constructor")
|
||||
ae_opt in
|
||||
return ~tv ae
|
||||
| E_tuple lst ->
|
||||
)
|
||||
| E_tuple lst -> (
|
||||
let node = Append_tree.of_list lst in
|
||||
let aux (a:expression result) (b:expression result) : expression result =
|
||||
let%bind a = a in
|
||||
@ -224,11 +287,16 @@ and translate_annotated_expression (ae:AST.annotated_expression) : expression re
|
||||
return ~tv @@ E_constant ("PAIR", [a; b])
|
||||
in
|
||||
Append_tree.fold_ne (translate_annotated_expression) aux node
|
||||
| E_tuple_accessor (tpl, ind) ->
|
||||
)
|
||||
| E_tuple_accessor (tpl, ind) -> (
|
||||
let%bind ty' = translate_type tpl.type_annotation in
|
||||
let%bind ty_lst = get_t_tuple tpl.type_annotation in
|
||||
let%bind ty_lst =
|
||||
trace_strong (corner_case ~loc:__LOC__ "not a tuple") @@
|
||||
get_t_tuple tpl.type_annotation in
|
||||
let%bind ty'_lst = bind_map_list translate_type ty_lst in
|
||||
let%bind path = tuple_access_to_lr ty' ty'_lst ind in
|
||||
let%bind path =
|
||||
trace_strong (corner_case ~loc:__LOC__ "tuple access") @@
|
||||
tuple_access_to_lr ty' ty'_lst ind in
|
||||
let aux = fun pred (ty, lr) ->
|
||||
let c = match lr with
|
||||
| `Left -> "CAR"
|
||||
@ -237,7 +305,8 @@ and translate_annotated_expression (ae:AST.annotated_expression) : expression re
|
||||
let%bind tpl' = translate_annotated_expression tpl in
|
||||
let expr = List.fold_left aux tpl' path in
|
||||
ok expr
|
||||
| E_record m ->
|
||||
)
|
||||
| E_record m -> (
|
||||
let node = Append_tree.of_list @@ list_of_map m in
|
||||
let aux a b : expression result =
|
||||
let%bind a = a in
|
||||
@ -247,12 +316,18 @@ and translate_annotated_expression (ae:AST.annotated_expression) : expression re
|
||||
let tv = T_pair (a_ty , b_ty) in
|
||||
return ~tv @@ E_constant ("PAIR", [a; b])
|
||||
in
|
||||
trace_strong (corner_case ~loc:__LOC__ "record build") @@
|
||||
Append_tree.fold_ne (translate_annotated_expression) aux node
|
||||
)
|
||||
| E_record_accessor (record, property) ->
|
||||
let%bind ty' = translate_type (get_type_annotation record) in
|
||||
let%bind ty_smap = get_t_record (get_type_annotation record) in
|
||||
let%bind ty_smap =
|
||||
trace_strong (corner_case ~loc:__LOC__ "not a record") @@
|
||||
get_t_record (get_type_annotation record) in
|
||||
let%bind ty'_smap = bind_map_smap translate_type ty_smap in
|
||||
let%bind path = record_access_to_lr ty' ty'_smap property in
|
||||
let%bind path =
|
||||
trace_strong (corner_case ~loc:__LOC__ "record access") @@
|
||||
record_access_to_lr ty' ty'_smap property in
|
||||
let aux = fun pred (ty, lr) ->
|
||||
let c = match lr with
|
||||
| `Left -> "CAR"
|
||||
@ -261,38 +336,49 @@ and translate_annotated_expression (ae:AST.annotated_expression) : expression re
|
||||
let%bind record' = translate_annotated_expression record in
|
||||
let expr = List.fold_left aux record' path in
|
||||
ok expr
|
||||
| E_constant (name, lst) ->
|
||||
let%bind lst' = bind_list @@ List.map (translate_annotated_expression) lst in (
|
||||
match name, lst with
|
||||
| "NONE", [] ->
|
||||
let%bind o = Mini_c.Combinators.get_t_option tv in
|
||||
return @@ E_make_none o
|
||||
| _ -> return @@ E_constant (name, lst')
|
||||
)
|
||||
| E_constant (name, lst) -> (
|
||||
let%bind lst' = bind_map_list (translate_annotated_expression) lst in
|
||||
match name, lst with
|
||||
| "NONE", [] ->
|
||||
let%bind o =
|
||||
trace_strong (corner_case ~loc:__LOC__ "not an option") @@
|
||||
Mini_c.Combinators.get_t_option tv in
|
||||
return @@ E_make_none o
|
||||
| _ -> return @@ E_constant (name, lst')
|
||||
)
|
||||
| E_lambda l ->
|
||||
let%bind env = transpile_environment ae.environment in
|
||||
let%bind env =
|
||||
trace_strong (corner_case ~loc:__LOC__ "environment") @@
|
||||
transpile_environment ae.environment in
|
||||
translate_lambda env l
|
||||
| E_list lst ->
|
||||
let%bind t = Mini_c.Combinators.get_t_list tv in
|
||||
| E_list lst -> (
|
||||
let%bind t =
|
||||
trace_strong (corner_case ~loc:__LOC__ "not a list") @@
|
||||
Mini_c.Combinators.get_t_list tv in
|
||||
let%bind lst' = bind_map_list (translate_annotated_expression) lst in
|
||||
let aux : expression -> expression -> expression result = fun prev cur ->
|
||||
return @@ E_constant ("CONS", [cur ; prev]) in
|
||||
let%bind (init : expression) = return @@ E_make_empty_list t in
|
||||
bind_fold_list aux init lst'
|
||||
| E_map m ->
|
||||
let%bind (src, dst) = Mini_c.Combinators.get_t_map tv in
|
||||
)
|
||||
| E_map m -> (
|
||||
let%bind (src, dst) =
|
||||
trace_strong (corner_case ~loc:__LOC__ "not a map") @@
|
||||
Mini_c.Combinators.get_t_map tv in
|
||||
let aux : expression result -> (AST.ae * AST.ae) -> expression result = fun prev (k, v) ->
|
||||
let%bind prev' = prev in
|
||||
let%bind (k', v') =
|
||||
let v' = e_a_some v ae.environment in
|
||||
bind_map_pair (translate_annotated_expression) (k, v') in
|
||||
bind_map_pair (translate_annotated_expression) (k , v') in
|
||||
return @@ E_constant ("UPDATE", [k' ; v' ; prev'])
|
||||
in
|
||||
let init = return @@ E_make_empty_map (src, dst) in
|
||||
List.fold_left aux init m
|
||||
| E_look_up dsi ->
|
||||
)
|
||||
| E_look_up dsi -> (
|
||||
let%bind (ds', i') = bind_map_pair f dsi in
|
||||
return @@ E_constant ("MAP_GET", [i' ; ds'])
|
||||
)
|
||||
| E_sequence (a , b) -> (
|
||||
let%bind a' = translate_annotated_expression a in
|
||||
let%bind b' = translate_annotated_expression b in
|
||||
@ -309,27 +395,25 @@ and translate_annotated_expression (ae:AST.annotated_expression) : expression re
|
||||
fun (prev, acc) cur ->
|
||||
let%bind ty' = translate_type prev in
|
||||
match cur with
|
||||
| Access_tuple ind ->
|
||||
let%bind ty_lst = AST.Combinators.get_t_tuple prev in
|
||||
| Access_tuple ind -> (
|
||||
let%bind ty_lst =
|
||||
trace_strong (corner_case ~loc:__LOC__ "not a tuple") @@
|
||||
AST.Combinators.get_t_tuple prev in
|
||||
let%bind ty'_lst = bind_map_list translate_type ty_lst in
|
||||
let%bind path = tuple_access_to_lr ty' ty'_lst ind in
|
||||
let path' = List.map snd path in
|
||||
ok (List.nth ty_lst ind, acc @ path')
|
||||
| Access_record prop ->
|
||||
let%bind ty_map =
|
||||
let error =
|
||||
let title () = "accessing property on not a record" in
|
||||
let content () = Format.asprintf "%s on %a in %a"
|
||||
prop Ast_typed.PP.type_value prev Ast_typed.PP.annotated_expression expr in
|
||||
error title content
|
||||
in
|
||||
trace error @@
|
||||
)
|
||||
| Access_record prop -> (
|
||||
let%bind ty_map =
|
||||
trace_strong (corner_case ~loc:__LOC__ "not a record") @@
|
||||
AST.Combinators.get_t_record prev in
|
||||
let%bind ty'_map = bind_map_smap translate_type ty_map in
|
||||
let%bind path = record_access_to_lr ty' ty'_map prop in
|
||||
let path' = List.map snd path in
|
||||
ok (Map.String.find prop ty_map, acc @ path')
|
||||
| Access_map _k -> simple_fail "no patch for map yet"
|
||||
ok (Map.String.find prop ty_map, acc @ path')
|
||||
)
|
||||
| Access_map _k -> fail (corner_case ~loc:__LOC__ "no patch for map yet")
|
||||
in
|
||||
let%bind (_, path) = bind_fold_right_list aux (ty, []) path in
|
||||
let%bind expr' = translate_annotated_expression expr in
|
||||
@ -349,9 +433,11 @@ and translate_annotated_expression (ae:AST.annotated_expression) : expression re
|
||||
ok (tv' , s') in
|
||||
return @@ E_if_none (expr' , n , ((name , tv') , s'))
|
||||
| Match_variant (lst , variant) -> (
|
||||
let%bind tree = tree_of_sum variant in
|
||||
let%bind tree =
|
||||
trace_strong (corner_case ~loc:__LOC__ "getting lr tree") @@
|
||||
tree_of_sum variant in
|
||||
let%bind tree' = match tree with
|
||||
| Empty -> simple_fail "match empty variant"
|
||||
| Empty -> fail (corner_case ~loc:__LOC__ "match empty variant")
|
||||
| Full x -> ok x in
|
||||
let%bind tree'' =
|
||||
let rec aux t =
|
||||
@ -371,7 +457,7 @@ and translate_annotated_expression (ae:AST.annotated_expression) : expression re
|
||||
match t with
|
||||
| ((`Leaf constructor_name) , tv) -> (
|
||||
let%bind ((_ , name) , body) =
|
||||
trace_option (simple_error "not supposed to happen here: missing match clause") @@
|
||||
trace_option (corner_case ~loc:__LOC__ "missing match clause") @@
|
||||
List.find_opt (fun ((constructor_name' , _) , _) -> constructor_name' = constructor_name) lst in
|
||||
let%bind body' = translate_annotated_expression body in
|
||||
return @@ E_let_in ((name , tv) , top , body')
|
||||
@ -391,10 +477,11 @@ and translate_annotated_expression (ae:AST.annotated_expression) : expression re
|
||||
in
|
||||
return @@ E_if_left (top , a' , b')
|
||||
in
|
||||
trace_strong (corner_case ~loc:__LOC__ "building constructor") @@
|
||||
aux expr' tree''
|
||||
)
|
||||
| AST.Match_list _ | AST.Match_tuple (_, _) ->
|
||||
simple_fail "only match bool, option and variants are translated yet"
|
||||
| AST.Match_list _ -> fail @@ unsupported_pattern_matching "list" ae.location
|
||||
| AST.Match_tuple _ -> fail @@ unsupported_pattern_matching "tuple" ae.location
|
||||
)
|
||||
|
||||
and translate_lambda_deep : Mini_c.Environment.t -> AST.lambda -> Mini_c.expression result = fun env l ->
|
||||
@ -433,7 +520,6 @@ and translate_lambda env l =
|
||||
| [] -> (
|
||||
let%bind result' = translate_annotated_expression result in
|
||||
let result' = ez_e_return result' in
|
||||
trace (simple_error "translate quote") @@
|
||||
let%bind input = translate_type input_type in
|
||||
let%bind output = translate_type output_type in
|
||||
let tv = Combinators.t_function input output in
|
||||
@ -441,7 +527,6 @@ and translate_lambda env l =
|
||||
ok @@ Combinators.Expression.make_tpl (E_literal content, tv)
|
||||
)
|
||||
| _ -> (
|
||||
trace (simple_error "translate lambda deep") @@
|
||||
translate_lambda_deep env l
|
||||
) in
|
||||
ok result
|
||||
@ -463,11 +548,11 @@ let translate_program (lst:AST.program) : program result =
|
||||
let%bind (statements, _) = List.fold_left aux (ok ([], Environment.empty)) (temp_unwrap_loc_list lst) in
|
||||
ok statements
|
||||
|
||||
let translate_main (l:AST.lambda) : anon_function result =
|
||||
let translate_main (l:AST.lambda) loc : anon_function result =
|
||||
let%bind expr = translate_lambda Environment.empty l in
|
||||
match Combinators.Expression.get_content expr with
|
||||
| E_literal (D_function f) -> ok f
|
||||
| _ -> simple_fail "main is not a function"
|
||||
| _ -> fail @@ not_functional_main loc
|
||||
|
||||
(* From an expression [expr], build the expression [fun () -> expr] *)
|
||||
let functionalize (e:AST.annotated_expression) : AST.lambda * AST.type_value =
|
||||
@ -484,7 +569,7 @@ let translate_entry (lst:AST.program) (name:string) : anon_function result =
|
||||
let rec aux acc (lst:AST.program) =
|
||||
let%bind acc = acc in
|
||||
match lst with
|
||||
| [] -> simple_fail "no entry point with given name"
|
||||
| [] -> fail @@ missing_entry_point name
|
||||
| hd :: tl -> (
|
||||
let (AST.Declaration_constant (an , (pre_env , _))) = temp_unwrap_loc hd in
|
||||
match an.name = name with
|
||||
@ -498,11 +583,11 @@ let translate_entry (lst:AST.program) (name:string) : anon_function result =
|
||||
match an.annotated_expression.expression with
|
||||
| E_lambda l ->
|
||||
let l' = { l with result = acc l.result } in
|
||||
translate_main l'
|
||||
translate_main l' an.annotated_expression.location
|
||||
| _ ->
|
||||
let (l , _) = functionalize an.annotated_expression in
|
||||
let l' = { l with result = acc l.result } in
|
||||
translate_main l'
|
||||
translate_main l' an.annotated_expression.location
|
||||
)
|
||||
)
|
||||
in
|
||||
@ -553,36 +638,62 @@ let rec untranspile (v : value) (t : AST.type_value) : AST.annotated_expression
|
||||
let open! AST in
|
||||
let return e = ok (make_a_e_empty e t) in
|
||||
match t.type_value' with
|
||||
| T_constant ("unit", []) ->
|
||||
let%bind () = get_unit v in
|
||||
| T_constant ("unit", []) -> (
|
||||
let%bind () =
|
||||
trace_strong (wrong_mini_c_value "unit" v) @@
|
||||
get_unit v in
|
||||
return (E_literal Literal_unit)
|
||||
| T_constant ("bool", []) ->
|
||||
let%bind b = get_bool v in
|
||||
)
|
||||
| T_constant ("bool", []) -> (
|
||||
let%bind b =
|
||||
trace_strong (wrong_mini_c_value "bool" v) @@
|
||||
get_bool v in
|
||||
return (E_literal (Literal_bool b))
|
||||
| T_constant ("int", []) ->
|
||||
let%bind n = get_int v in
|
||||
)
|
||||
| T_constant ("int", []) -> (
|
||||
let%bind n =
|
||||
trace_strong (wrong_mini_c_value "int" v) @@
|
||||
get_int v in
|
||||
return (E_literal (Literal_int n))
|
||||
| T_constant ("nat", []) ->
|
||||
let%bind n = get_nat v in
|
||||
)
|
||||
| T_constant ("nat", []) -> (
|
||||
let%bind n =
|
||||
trace_strong (wrong_mini_c_value "nat" v) @@
|
||||
get_nat v in
|
||||
return (E_literal (Literal_nat n))
|
||||
| T_constant ("tez", []) ->
|
||||
let%bind n = get_nat v in
|
||||
)
|
||||
| T_constant ("tez", []) -> (
|
||||
let%bind n =
|
||||
trace_strong (wrong_mini_c_value "tez" v) @@
|
||||
get_nat v in
|
||||
return (E_literal (Literal_tez n))
|
||||
| T_constant ("string", []) ->
|
||||
let%bind n = get_string v in
|
||||
)
|
||||
| T_constant ("string", []) -> (
|
||||
let%bind n =
|
||||
trace_strong (wrong_mini_c_value "string" v) @@
|
||||
get_string v in
|
||||
return (E_literal (Literal_string n))
|
||||
| T_constant ("address", []) ->
|
||||
let%bind n = get_string v in
|
||||
)
|
||||
| T_constant ("address", []) -> (
|
||||
let%bind n =
|
||||
trace_strong (wrong_mini_c_value "address" v) @@
|
||||
get_string v in
|
||||
return (E_literal (Literal_address n))
|
||||
)
|
||||
| T_constant ("option", [o]) -> (
|
||||
match%bind get_option v with
|
||||
let%bind opt =
|
||||
trace_strong (wrong_mini_c_value "option" v) @@
|
||||
get_option v in
|
||||
match opt with
|
||||
| None -> ok (e_a_empty_none o)
|
||||
| Some s ->
|
||||
let%bind s' = untranspile s o in
|
||||
ok (e_a_empty_some s')
|
||||
)
|
||||
| T_constant ("map", [k_ty;v_ty]) -> (
|
||||
let%bind lst = get_map v in
|
||||
let%bind lst =
|
||||
trace_strong (wrong_mini_c_value "map" v) @@
|
||||
get_map v in
|
||||
let%bind lst' =
|
||||
let aux = fun (k, v) ->
|
||||
let%bind k' = untranspile k k_ty in
|
||||
@ -592,48 +703,55 @@ let rec untranspile (v : value) (t : AST.type_value) : AST.annotated_expression
|
||||
return (E_map lst')
|
||||
)
|
||||
| T_constant ("list", [ty]) -> (
|
||||
let%bind lst = get_list v in
|
||||
let%bind lst =
|
||||
trace_strong (wrong_mini_c_value "list" v) @@
|
||||
get_list v in
|
||||
let%bind lst' =
|
||||
let aux = fun e -> untranspile e ty in
|
||||
bind_map_list aux lst in
|
||||
return (E_list lst')
|
||||
)
|
||||
| T_constant ("contract" , [_ty]) ->
|
||||
simple_fail "can't untranspile contract"
|
||||
| T_constant ("operation" , []) ->
|
||||
let%bind op = get_operation v in
|
||||
fail @@ bad_untranspile "contract" v
|
||||
| T_constant ("operation" , []) -> (
|
||||
let%bind op =
|
||||
trace_strong (wrong_mini_c_value "operation" v) @@
|
||||
get_operation v in
|
||||
return (E_literal (Literal_operation op))
|
||||
| T_constant (name , lst) ->
|
||||
let error =
|
||||
let title () = "unknown type_constant" in
|
||||
let content () = Format.asprintf "%s (%d)" name (List.length lst) in
|
||||
error title content in
|
||||
fail error
|
||||
)
|
||||
| T_constant (name , _lst) ->
|
||||
fail @@ unknown_untranspile name v
|
||||
| T_sum m ->
|
||||
let lst = kv_list_of_map m in
|
||||
let%bind node = match Append_tree.of_list lst with
|
||||
| Empty -> simple_fail "empty sum type"
|
||||
| Empty -> fail @@ corner_case ~loc:__LOC__ "empty sum type"
|
||||
| Full t -> ok t
|
||||
in
|
||||
let%bind (name, v, tv) = extract_constructor v node in
|
||||
let%bind (name, v, tv) =
|
||||
trace_strong (corner_case ~loc:__LOC__ "sum extract constructor") @@
|
||||
extract_constructor v node in
|
||||
let%bind sub = untranspile v tv in
|
||||
return (E_constructor (name, sub))
|
||||
| T_tuple lst ->
|
||||
let%bind node = match Append_tree.of_list lst with
|
||||
| Empty -> simple_fail "empty tuple"
|
||||
| Empty -> fail @@ corner_case ~loc:__LOC__ "empty tuple"
|
||||
| Full t -> ok t in
|
||||
let%bind tpl = extract_tuple v node in
|
||||
let%bind tpl =
|
||||
trace_strong (corner_case ~loc:__LOC__ "tuple extract") @@
|
||||
extract_tuple v node in
|
||||
let%bind tpl' = bind_list
|
||||
@@ List.map (fun (x, y) -> untranspile x y) tpl in
|
||||
return (E_tuple tpl')
|
||||
| T_record m ->
|
||||
let lst = kv_list_of_map m in
|
||||
let%bind node = match Append_tree.of_list lst with
|
||||
| Empty -> simple_fail "empty record"
|
||||
| Empty -> fail @@ corner_case ~loc:__LOC__ "empty record"
|
||||
| Full t -> ok t in
|
||||
let%bind lst = extract_record v node in
|
||||
let%bind lst =
|
||||
trace_strong (corner_case ~loc:__LOC__ "record extract") @@
|
||||
extract_record v node in
|
||||
let%bind lst = bind_list
|
||||
@@ List.map (fun (x, (y, z)) -> let%bind yz = untranspile y z in ok (x, yz)) lst in
|
||||
let m' = map_of_kv_list lst in
|
||||
return (E_record m')
|
||||
| T_function _ -> simple_fail "no untranspilation for functions yet"
|
||||
| T_function _ -> fail @@ bad_untranspile "function" v
|
||||
|
@ -13,39 +13,199 @@ type environment = Environment.t
|
||||
module Errors = struct
|
||||
let unbound_type_variable (e:environment) (n:string) () =
|
||||
let title = (thunk "unbound type variable") in
|
||||
let full () = Format.asprintf "%s in %a" n Environment.PP.full_environment e in
|
||||
error title full ()
|
||||
let message () = "" in
|
||||
let data = [
|
||||
("variable" , fun () -> Format.asprintf "%s" n) ;
|
||||
(* TODO: types don't have srclocs for now. *)
|
||||
(* ("location" , fun () -> Format.asprintf "%a" Location.pp (n.location)) ; *)
|
||||
("in" , fun () -> Format.asprintf "%a" Environment.PP.full_environment e)
|
||||
] in
|
||||
error ~data title message ()
|
||||
|
||||
let unbound_variable (e:environment) (n:string) () =
|
||||
let unbound_variable (e:environment) (n:string) (loc:Location.t) () =
|
||||
let title = (thunk "unbound variable") in
|
||||
let full () = Format.asprintf "%s in %a" n Environment.PP.full_environment e in
|
||||
error title full ()
|
||||
let message () = "" in
|
||||
let data = [
|
||||
("variable" , fun () -> Format.asprintf "%s" n) ;
|
||||
("environment" , fun () -> Format.asprintf "%a" Environment.PP.full_environment e) ;
|
||||
("location" , fun () -> Format.asprintf "%a" Location.pp loc)
|
||||
] in
|
||||
error ~data title message ()
|
||||
|
||||
let unrecognized_constant (n:string) () =
|
||||
let match_empty_variant : type a . a I.matching -> Location.t -> unit -> _ =
|
||||
fun matching loc () ->
|
||||
let title = (thunk "match with no cases") in
|
||||
let message () = "" in
|
||||
let data = [
|
||||
("variant" , fun () -> Format.asprintf "%a" I.PP.matching_type matching) ;
|
||||
("location" , fun () -> Format.asprintf "%a" Location.pp loc)
|
||||
] in
|
||||
error ~data title message ()
|
||||
|
||||
let match_missing_case : type a . a I.matching -> Location.t -> unit -> _ =
|
||||
fun matching loc () ->
|
||||
let title = (thunk "missing case in match") in
|
||||
let message () = "" in
|
||||
let data = [
|
||||
("variant" , fun () -> Format.asprintf "%a" I.PP.matching_type matching) ;
|
||||
("location" , fun () -> Format.asprintf "%a" Location.pp loc)
|
||||
] in
|
||||
error ~data title message ()
|
||||
|
||||
let match_redundant_case : type a . a I.matching -> Location.t -> unit -> _ =
|
||||
fun matching loc () ->
|
||||
let title = (thunk "missing case in match") in
|
||||
let message () = "" in
|
||||
let data = [
|
||||
("variant" , fun () -> Format.asprintf "%a" I.PP.matching_type matching) ;
|
||||
("location" , fun () -> Format.asprintf "%a" Location.pp loc)
|
||||
] in
|
||||
error ~data title message ()
|
||||
|
||||
let unbound_constructor (e:environment) (n:string) (loc:Location.t) () =
|
||||
let title = (thunk "unbound constructor") in
|
||||
let message () = "" in
|
||||
let data = [
|
||||
("constructor" , fun () -> Format.asprintf "%s" n) ;
|
||||
("environment" , fun () -> Format.asprintf "%a" Environment.PP.full_environment e) ;
|
||||
("location" , fun () -> Format.asprintf "%a" Location.pp loc)
|
||||
] in
|
||||
error ~data title message ()
|
||||
|
||||
let unrecognized_constant (n:string) (loc:Location.t) () =
|
||||
let title = (thunk "unrecognized constant") in
|
||||
let full () = n in
|
||||
error title full ()
|
||||
let message () = "" in
|
||||
let data = [
|
||||
("constant" , fun () -> Format.asprintf "%s" n) ;
|
||||
("location" , fun () -> Format.asprintf "%a" Location.pp loc)
|
||||
] in
|
||||
error ~data title message ()
|
||||
|
||||
let wrong_arity (n:string) (expected:int) (actual:int) () =
|
||||
let wrong_arity (n:string) (expected:int) (actual:int) (loc : Location.t) () =
|
||||
let title () = "wrong arity" in
|
||||
let full () =
|
||||
Format.asprintf "Wrong number of args passed to [%s]. Expected was %d, received was %d"
|
||||
n expected actual
|
||||
in
|
||||
error title full ()
|
||||
let message () = "" in
|
||||
let data = [
|
||||
("function" , fun () -> Format.asprintf "%s" n) ;
|
||||
("expected" , fun () -> Format.asprintf "%d" expected) ;
|
||||
("actual" , fun () -> Format.asprintf "%d" actual) ;
|
||||
("location" , fun () -> Format.asprintf "%a" Location.pp loc)
|
||||
] in
|
||||
error ~data title message ()
|
||||
|
||||
let match_tuple_wrong_arity (expected:'a list) (actual:'b list) (loc:Location.t) () =
|
||||
let title () = "matching tuple of different size" in
|
||||
let message () = "" in
|
||||
let data = [
|
||||
("expected" , fun () -> Format.asprintf "%d" (List.length expected)) ;
|
||||
("actual" , fun () -> Format.asprintf "%d" (List.length actual)) ;
|
||||
("location" , fun () -> Format.asprintf "%a" Location.pp loc)
|
||||
] in
|
||||
error ~data title message ()
|
||||
|
||||
(* TODO: this should be a trace_info? *)
|
||||
let program_error (p:I.program) () =
|
||||
let message () = "" in
|
||||
let title = (thunk "typing program") in
|
||||
let full () = Format.asprintf "%a" I.PP.program p in
|
||||
error title full ()
|
||||
let data = [
|
||||
("program" , fun () -> Format.asprintf "%a" I.PP.program p)
|
||||
] in
|
||||
error ~data title message ()
|
||||
|
||||
let constant_declaration_error (name:string) (ae:I.expr) () =
|
||||
let constant_declaration_error (name:string) (ae:I.expr) (expected: O.type_value option) () =
|
||||
let title = (thunk "typing constant declaration") in
|
||||
let full () =
|
||||
Format.asprintf "%s = %a" name
|
||||
I.PP.expression ae
|
||||
in
|
||||
error title full ()
|
||||
let message () = "" in
|
||||
let data = [
|
||||
("constant" , fun () -> Format.asprintf "%s" name) ;
|
||||
("expression" , fun () -> Format.asprintf "%a" I.PP.expression ae) ;
|
||||
("expected" , fun () ->
|
||||
match expected with
|
||||
None -> "(no annotation for the expected type)"
|
||||
| Some expected -> Format.asprintf "%a" O.PP.type_value expected) ;
|
||||
("location" , fun () -> Format.asprintf "%a" Location.pp ae.location)
|
||||
] in
|
||||
error ~data title message ()
|
||||
|
||||
let match_error : type a . ?msg:string -> expected: a I.matching -> actual: O.type_value -> Location.t -> unit -> _ =
|
||||
fun ?(msg = "") ~expected ~actual loc () ->
|
||||
let title = (thunk "typing match") in
|
||||
let message () = msg in
|
||||
let data = [
|
||||
("expected" , fun () -> Format.asprintf "%a" I.PP.matching_type expected);
|
||||
("actual" , fun () -> Format.asprintf "%a" O.PP.type_value actual) ;
|
||||
("location" , fun () -> Format.asprintf "%a" Location.pp loc)
|
||||
] in
|
||||
error ~data title message ()
|
||||
|
||||
let needs_annotation (e : I.expression) (case : string) () =
|
||||
let title = (thunk "this expression must be annotated with its type") in
|
||||
let message () = Format.asprintf "%s needs an annotation" case in
|
||||
let data = [
|
||||
("expression" , fun () -> Format.asprintf "%a" I.PP.expression e) ;
|
||||
("location" , fun () -> Format.asprintf "%a" Location.pp e.location)
|
||||
] in
|
||||
error ~data title message ()
|
||||
|
||||
let type_error_approximate ?(msg="") ~(expected: string) ~(actual: O.type_value) ~(expression : O.value) (loc:Location.t) () =
|
||||
let title = (thunk "type error") in
|
||||
let message () = msg in
|
||||
let data = [
|
||||
("expected" , fun () -> Format.asprintf "%s" expected);
|
||||
("actual" , fun () -> Format.asprintf "%a" O.PP.type_value actual);
|
||||
("expression" , fun () -> Format.asprintf "%a" O.PP.value expression) ;
|
||||
("location" , fun () -> Format.asprintf "%a" Location.pp loc)
|
||||
] in
|
||||
error ~data title message ()
|
||||
|
||||
let type_error ?(msg="") ~(expected: O.type_value) ~(actual: O.type_value) ~(expression : O.value) (loc:Location.t) () =
|
||||
let title = (thunk "type error") in
|
||||
let message () = msg in
|
||||
let data = [
|
||||
("expected" , fun () -> Format.asprintf "%a" O.PP.type_value expected);
|
||||
("actual" , fun () -> Format.asprintf "%a" O.PP.type_value actual);
|
||||
("expression" , fun () -> Format.asprintf "%a" O.PP.value expression) ;
|
||||
("location" , fun () -> Format.asprintf "%a" Location.pp loc)
|
||||
] in
|
||||
error ~data title message ()
|
||||
|
||||
let bad_tuple_index (index : int) (ae : I.expression) (t : O.type_value) (loc:Location.t) () =
|
||||
let title = (thunk "invalid tuple index") in
|
||||
let message () = "" in
|
||||
let data = [
|
||||
("index" , fun () -> Format.asprintf "%d" index) ;
|
||||
("tuple_value" , fun () -> Format.asprintf "%a" I.PP.expression ae) ;
|
||||
("tuple_type" , fun () -> Format.asprintf "%a" O.PP.type_value t) ;
|
||||
("location" , fun () -> Format.asprintf "%a" Location.pp loc)
|
||||
] in
|
||||
error ~data title message ()
|
||||
|
||||
let bad_record_access (field : string) (ae : I.expression) (t : O.type_value) (loc:Location.t) () =
|
||||
let title = (thunk "invalid record field") in
|
||||
let message () = "" in
|
||||
let data = [
|
||||
("field" , fun () -> Format.asprintf "%s" field) ;
|
||||
("record_value" , fun () -> Format.asprintf "%a" I.PP.expression ae) ;
|
||||
("tuple_type" , fun () -> Format.asprintf "%a" O.PP.type_value t) ;
|
||||
("location" , fun () -> Format.asprintf "%a" Location.pp loc)
|
||||
] in
|
||||
error ~data title message ()
|
||||
|
||||
let not_supported_yet (message : string) (ae : I.expression) () =
|
||||
let title = (thunk "not suported yet") in
|
||||
let message () = message in
|
||||
let data = [
|
||||
("expression" , fun () -> Format.asprintf "%a" I.PP.expression ae) ;
|
||||
("location" , fun () -> Format.asprintf "%a" Location.pp ae.location)
|
||||
] in
|
||||
error ~data title message ()
|
||||
|
||||
let not_supported_yet_untranspile (message : string) (ae : O.expression) () =
|
||||
let title = (thunk "not suported yet") in
|
||||
let message () = message in
|
||||
let data = [
|
||||
("expression" , fun () -> Format.asprintf "%a" O.PP.expression ae)
|
||||
] in
|
||||
error ~data title message ()
|
||||
|
||||
end
|
||||
open Errors
|
||||
|
||||
@ -71,24 +231,24 @@ and type_declaration env : I.declaration -> (environment * O.declaration option)
|
||||
| Declaration_constant (name , tv_opt , expression) -> (
|
||||
let%bind tv'_opt = bind_map_option (evaluate_type env) tv_opt in
|
||||
let%bind ae' =
|
||||
trace (constant_declaration_error name expression) @@
|
||||
trace (constant_declaration_error name expression tv'_opt) @@
|
||||
type_expression ?tv_opt:tv'_opt env expression in
|
||||
let env' = Environment.add_ez_ae name ae' env in
|
||||
ok (env', Some (O.Declaration_constant ((make_n_e name ae') , (env , env'))))
|
||||
)
|
||||
|
||||
and type_match : type i o . (environment -> i -> o result) -> environment -> O.type_value -> i I.matching -> o O.matching result =
|
||||
fun f e t i -> match i with
|
||||
and type_match : type i o . (environment -> i -> o result) -> environment -> O.type_value -> i I.matching -> Location.t -> o O.matching result =
|
||||
fun f e t i loc -> match i with
|
||||
| Match_bool {match_true ; match_false} ->
|
||||
let%bind _ =
|
||||
trace_strong (simple_error "Matching bool on not-a-bool")
|
||||
trace_strong (match_error ~expected:i ~actual:t loc)
|
||||
@@ get_t_bool t in
|
||||
let%bind match_true = f e match_true in
|
||||
let%bind match_false = f e match_false in
|
||||
ok (O.Match_bool {match_true ; match_false})
|
||||
| Match_option {match_none ; match_some} ->
|
||||
let%bind t_opt =
|
||||
trace_strong (simple_error "Matching option on not-an-option")
|
||||
trace_strong (match_error ~expected:i ~actual:t loc)
|
||||
@@ get_t_option t in
|
||||
let%bind match_none = f e match_none in
|
||||
let (n, b) = match_some in
|
||||
@ -98,7 +258,7 @@ and type_match : type i o . (environment -> i -> o result) -> environment -> O.t
|
||||
ok (O.Match_option {match_none ; match_some = (n', b')})
|
||||
| Match_list {match_nil ; match_cons} ->
|
||||
let%bind t_list =
|
||||
trace_strong (simple_error "Matching list on not-an-list")
|
||||
trace_strong (match_error ~expected:i ~actual:t loc)
|
||||
@@ get_t_list t in
|
||||
let%bind match_nil = f e match_nil in
|
||||
let (hd, tl, b) = match_cons in
|
||||
@ -108,10 +268,10 @@ and type_match : type i o . (environment -> i -> o result) -> environment -> O.t
|
||||
ok (O.Match_list {match_nil ; match_cons = (hd, tl, b')})
|
||||
| Match_tuple (lst, b) ->
|
||||
let%bind t_tuple =
|
||||
trace_strong (simple_error "Matching tuple on not-a-tuple")
|
||||
trace_strong (match_error ~expected:i ~actual:t loc)
|
||||
@@ get_t_tuple t in
|
||||
let%bind lst' =
|
||||
generic_try (simple_error "Matching tuple of different size")
|
||||
generic_try (match_tuple_wrong_arity t_tuple lst loc)
|
||||
@@ (fun () -> List.combine lst t_tuple) in
|
||||
let aux prev (name, tv) = Environment.add_ez_binder name tv prev in
|
||||
let e' = List.fold_left aux e lst' in
|
||||
@ -121,7 +281,7 @@ and type_match : type i o . (environment -> i -> o result) -> environment -> O.t
|
||||
let%bind variant_opt =
|
||||
let aux acc ((constructor_name , _) , _) =
|
||||
let%bind (_ , variant) =
|
||||
trace_option (simple_error "bad constructor") @@
|
||||
trace_option (unbound_constructor e constructor_name loc) @@
|
||||
Environment.get_constructor constructor_name e in
|
||||
let%bind acc = match acc with
|
||||
| None -> ok (Some variant)
|
||||
@ -130,30 +290,32 @@ and type_match : type i o . (environment -> i -> o result) -> environment -> O.t
|
||||
ok (Some variant)
|
||||
) in
|
||||
ok acc in
|
||||
trace (simple_error "in match variant") @@
|
||||
trace (simple_info "in match variant") @@
|
||||
bind_fold_list aux None lst in
|
||||
let%bind variant =
|
||||
trace_option (simple_error "empty variant") @@
|
||||
trace_option (match_empty_variant i loc) @@
|
||||
variant_opt in
|
||||
let%bind () =
|
||||
let%bind variant_cases' = Ast_typed.Combinators.get_t_sum variant in
|
||||
let%bind () =
|
||||
let%bind variant_cases' =
|
||||
trace (match_error ~expected:i ~actual:t loc)
|
||||
@@ Ast_typed.Combinators.get_t_sum variant in
|
||||
let variant_cases = List.map fst @@ Map.String.to_kv_list variant_cases' in
|
||||
let match_cases = List.map (Function.compose fst fst) lst in
|
||||
let test_case = fun c ->
|
||||
Assert.assert_true (List.mem c match_cases)
|
||||
in
|
||||
let%bind () =
|
||||
trace (simple_error "missing case match") @@
|
||||
trace_strong (match_missing_case i loc) @@
|
||||
bind_iter_list test_case variant_cases in
|
||||
let%bind () =
|
||||
trace_strong (simple_error "redundant case match") @@
|
||||
trace_strong (match_redundant_case i loc) @@
|
||||
Assert.assert_true List.(length variant_cases = length match_cases) in
|
||||
ok ()
|
||||
in
|
||||
let%bind lst' =
|
||||
let aux ((constructor_name , name) , b) =
|
||||
let%bind (constructor , _) =
|
||||
trace_option (simple_error "bad constructor??") @@
|
||||
trace_option (unbound_constructor e constructor_name loc) @@
|
||||
Environment.get_constructor constructor_name e in
|
||||
let e' = Environment.add_ez_binder name constructor e in
|
||||
let%bind b' = f e' b in
|
||||
@ -219,10 +381,10 @@ and type_expression : environment -> ?tv_opt:O.type_value -> I.expression -> O.a
|
||||
trace main_error @@
|
||||
match Location.unwrap ae with
|
||||
(* Basic *)
|
||||
| E_failwith _ -> simple_fail "can't type failwith in isolation"
|
||||
| E_failwith _ -> fail @@ needs_annotation ae "the failwith keyword"
|
||||
| E_variable name ->
|
||||
let%bind tv' =
|
||||
trace_option (unbound_variable e name)
|
||||
trace_option (unbound_variable e name ae.location)
|
||||
@@ Environment.get_opt name e in
|
||||
return (E_variable name) tv'.type_value
|
||||
| E_literal (Literal_bool b) ->
|
||||
@ -252,33 +414,33 @@ and type_expression : environment -> ?tv_opt:O.type_value -> I.expression -> O.a
|
||||
let%bind lst' = bind_list @@ List.map (type_expression e) lst in
|
||||
let tv_lst = List.map get_type_annotation lst' in
|
||||
return (E_tuple lst') (t_tuple tv_lst ())
|
||||
| E_accessor (ae, path) ->
|
||||
let%bind e' = type_expression e ae in
|
||||
| E_accessor (ae', path) ->
|
||||
let%bind e' = type_expression e ae' in
|
||||
let aux (prev:O.annotated_expression) (a:I.access) : O.annotated_expression result =
|
||||
match a with
|
||||
| Access_tuple index -> (
|
||||
let%bind tpl_tv = get_t_tuple prev.type_annotation in
|
||||
let%bind tv =
|
||||
generic_try (simple_error "bad tuple index")
|
||||
generic_try (bad_tuple_index index ae' prev.type_annotation ae.location)
|
||||
@@ (fun () -> List.nth tpl_tv index) in
|
||||
return (E_tuple_accessor (prev , index)) tv
|
||||
)
|
||||
| Access_record property -> (
|
||||
let%bind r_tv = get_t_record prev.type_annotation in
|
||||
let%bind tv =
|
||||
generic_try (simple_error "bad record index")
|
||||
generic_try (bad_record_access property ae' prev.type_annotation ae.location)
|
||||
@@ (fun () -> SMap.find property r_tv) in
|
||||
return (E_record_accessor (prev , property)) tv
|
||||
)
|
||||
| Access_map ae -> (
|
||||
let%bind ae' = type_expression e ae in
|
||||
| Access_map ae' -> (
|
||||
let%bind ae'' = type_expression e ae' in
|
||||
let%bind (k , v) = get_t_map prev.type_annotation in
|
||||
let%bind () =
|
||||
Ast_typed.assert_type_value_eq (k , get_type_annotation ae') in
|
||||
return (E_look_up (prev , ae')) v
|
||||
Ast_typed.assert_type_value_eq (k , get_type_annotation ae'') in
|
||||
return (E_look_up (prev , ae'')) v
|
||||
)
|
||||
in
|
||||
trace (simple_error "accessing") @@
|
||||
trace (simple_info "accessing") @@
|
||||
bind_fold_list aux e' path
|
||||
|
||||
(* Sum *)
|
||||
@ -322,7 +484,7 @@ and type_expression : environment -> ?tv_opt:O.type_value -> I.expression -> O.a
|
||||
let%bind ty =
|
||||
let%bind opt = bind_fold_list aux init
|
||||
@@ List.map get_type_annotation lst' in
|
||||
trace_option (simple_error "empty list expression without annotation") opt in
|
||||
trace_option (needs_annotation ae "empty list") opt in
|
||||
ok (t_list ty ())
|
||||
in
|
||||
return (E_list lst') tv
|
||||
@ -341,7 +503,7 @@ and type_expression : environment -> ?tv_opt:O.type_value -> I.expression -> O.a
|
||||
@@ List.map get_type_annotation
|
||||
@@ List.map fst lst' in
|
||||
let%bind annot = bind_map_option get_t_map_key tv_opt in
|
||||
trace (simple_error "untyped empty map expression") @@
|
||||
trace (simple_info "empty map expression without a type annotation") @@
|
||||
O.merge_annotation annot sub
|
||||
in
|
||||
let%bind value_type =
|
||||
@ -350,7 +512,7 @@ and type_expression : environment -> ?tv_opt:O.type_value -> I.expression -> O.a
|
||||
@@ List.map get_type_annotation
|
||||
@@ List.map snd lst' in
|
||||
let%bind annot = bind_map_option get_t_map_value tv_opt in
|
||||
trace (simple_error "untyped empty map expression") @@
|
||||
trace (simple_info "empty map expression without a type annotation") @@
|
||||
O.merge_annotation annot sub
|
||||
in
|
||||
ok (t_map key_type value_type ())
|
||||
@ -365,7 +527,7 @@ and type_expression : environment -> ?tv_opt:O.type_value -> I.expression -> O.a
|
||||
let%bind input_type =
|
||||
let%bind input_type =
|
||||
(* Hack to take care of let_in introduced by `simplify/ligodity.ml` in ECase's hack *)
|
||||
let default_action () = simple_fail "no input type provided" in
|
||||
let default_action e () = fail @@ (needs_annotation e "the returned value") in
|
||||
match input_type with
|
||||
| Some ty -> ok ty
|
||||
| None -> (
|
||||
@ -375,11 +537,11 @@ and type_expression : environment -> ?tv_opt:O.type_value -> I.expression -> O.a
|
||||
| I.E_variable name when name = (fst binder) -> (
|
||||
match snd li.binder with
|
||||
| Some ty -> ok ty
|
||||
| None -> default_action ()
|
||||
| None -> default_action li.rhs ()
|
||||
)
|
||||
| _ -> default_action ()
|
||||
| _ -> default_action li.rhs ()
|
||||
)
|
||||
| _ -> default_action ()
|
||||
| _ -> default_action result ()
|
||||
)
|
||||
in
|
||||
evaluate_type e input_type in
|
||||
@ -394,7 +556,7 @@ and type_expression : environment -> ?tv_opt:O.type_value -> I.expression -> O.a
|
||||
| E_constant (name, lst) ->
|
||||
let%bind lst' = bind_list @@ List.map (type_expression e) lst in
|
||||
let tv_lst = List.map get_type_annotation lst' in
|
||||
let%bind (name', tv) = type_constant name tv_lst tv_opt in
|
||||
let%bind (name', tv) = type_constant name tv_lst tv_opt ae.location in
|
||||
return (E_constant (name' , lst')) tv
|
||||
| E_application (f, arg) ->
|
||||
let%bind f = type_expression e f in
|
||||
@ -403,7 +565,12 @@ and type_expression : environment -> ?tv_opt:O.type_value -> I.expression -> O.a
|
||||
| T_function (param, result) ->
|
||||
let%bind _ = O.assert_type_value_eq (param, arg.type_annotation) in
|
||||
ok result
|
||||
| _ -> simple_fail "applying to not-a-function"
|
||||
| _ ->
|
||||
fail @@ type_error_approximate
|
||||
~expected:"should be a function type"
|
||||
~expression:f
|
||||
~actual:f.type_annotation
|
||||
f.location
|
||||
in
|
||||
return (E_application (f , arg)) tv
|
||||
| E_look_up dsi ->
|
||||
@ -420,11 +587,16 @@ and type_expression : environment -> ?tv_opt:O.type_value -> I.expression -> O.a
|
||||
let%bind fw = I.get_e_failwith match_true in
|
||||
let%bind fw' = type_expression e fw in
|
||||
let%bind mf' = type_expression e match_false in
|
||||
let t = get_type_annotation ex' in
|
||||
let%bind () =
|
||||
trace_strong (simple_error "Matching bool on not-a-bool")
|
||||
@@ assert_t_bool (get_type_annotation ex') in
|
||||
trace_strong (match_error ~expected:m ~actual:t ae.location)
|
||||
@@ assert_t_bool t in
|
||||
let%bind () =
|
||||
trace_strong (simple_error "Matching not-unit on an assert")
|
||||
trace_strong (match_error
|
||||
~msg:"matching not-unit on an assert"
|
||||
~expected:m
|
||||
~actual:t
|
||||
ae.location)
|
||||
@@ assert_t_unit (get_type_annotation mf') in
|
||||
let mt' = make_a_e
|
||||
(E_constant ("ASSERT" , [ex' ; fw']))
|
||||
@ -435,7 +607,7 @@ and type_expression : environment -> ?tv_opt:O.type_value -> I.expression -> O.a
|
||||
return (O.E_matching (ex' , m')) (t_unit ())
|
||||
)
|
||||
| _ -> (
|
||||
let%bind m' = type_match (type_expression ?tv_opt:None) e ex'.type_annotation m in
|
||||
let%bind m' = type_match (type_expression ?tv_opt:None) e ex'.type_annotation m ae.location in
|
||||
let tvs =
|
||||
let aux (cur:O.value O.matching) =
|
||||
match cur with
|
||||
@ -453,7 +625,7 @@ and type_expression : environment -> ?tv_opt:O.type_value -> I.expression -> O.a
|
||||
ok (Some cur) in
|
||||
let%bind tv_opt = bind_fold_list aux None tvs in
|
||||
let%bind tv =
|
||||
trace_option (simple_error "empty matching") @@
|
||||
trace_option (match_empty_variant m ae.location) @@
|
||||
tv_opt in
|
||||
return (O.E_matching (ex', m')) tv
|
||||
)
|
||||
@ -461,19 +633,37 @@ and type_expression : environment -> ?tv_opt:O.type_value -> I.expression -> O.a
|
||||
| E_sequence (a , b) ->
|
||||
let%bind a' = type_expression e a in
|
||||
let%bind b' = type_expression e b in
|
||||
let a'_type_annot = get_type_annotation a' in
|
||||
let%bind () =
|
||||
trace_strong (simple_error "first part of the sequence isn't of unit type") @@
|
||||
Ast_typed.assert_type_value_eq (t_unit () , get_type_annotation a') in
|
||||
trace_strong (type_error
|
||||
~msg:"first part of the sequence should be of unit type"
|
||||
~expected:(O.t_unit ())
|
||||
~actual:a'_type_annot
|
||||
~expression:a'
|
||||
a'.location) @@
|
||||
Ast_typed.assert_type_value_eq (t_unit () , a'_type_annot) in
|
||||
return (O.E_sequence (a' , b')) (get_type_annotation b')
|
||||
| E_loop (expr , body) ->
|
||||
let%bind expr' = type_expression e expr in
|
||||
let%bind body' = type_expression e body in
|
||||
let t_expr' = get_type_annotation expr' in
|
||||
let%bind () =
|
||||
trace_strong (simple_error "while condition isn't of type bool") @@
|
||||
Ast_typed.assert_type_value_eq (t_bool () , get_type_annotation expr') in
|
||||
trace_strong (type_error
|
||||
~msg:"while condition isn't of type bool"
|
||||
~expected:(O.t_bool ())
|
||||
~actual:t_expr'
|
||||
~expression:expr'
|
||||
expr'.location) @@
|
||||
Ast_typed.assert_type_value_eq (t_bool () , t_expr') in
|
||||
let t_body' = get_type_annotation body' in
|
||||
let%bind () =
|
||||
trace_strong (simple_error "while body isn't of unit type") @@
|
||||
Ast_typed.assert_type_value_eq (t_unit () , get_type_annotation body') in
|
||||
trace_strong (type_error
|
||||
~msg:"while body isn't of unit type"
|
||||
~expected:(O.t_unit ())
|
||||
~actual:t_body'
|
||||
~expression:body'
|
||||
body'.location) @@
|
||||
Ast_typed.assert_type_value_eq (t_unit () , t_body') in
|
||||
return (O.E_loop (expr' , body')) (t_unit ())
|
||||
| E_assign (name , path , expr) ->
|
||||
let%bind typed_name =
|
||||
@ -485,24 +675,31 @@ and type_expression : environment -> ?tv_opt:O.type_value -> I.expression -> O.a
|
||||
| Access_tuple index -> (
|
||||
let%bind tpl = get_t_tuple prec_tv in
|
||||
let%bind tv' =
|
||||
trace_option (simple_error "tuple too small") @@
|
||||
trace_option (bad_tuple_index index ae prec_tv ae.location) @@
|
||||
List.nth_opt tpl index in
|
||||
ok (tv' , prec_path @ [O.Access_tuple index])
|
||||
)
|
||||
| Access_record property -> (
|
||||
let%bind m = get_t_record prec_tv in
|
||||
let%bind tv' =
|
||||
trace_option (simple_error "tuple too small") @@
|
||||
trace_option (bad_record_access property ae prec_tv ae.location) @@
|
||||
Map.String.find_opt property m in
|
||||
ok (tv' , prec_path @ [O.Access_record property])
|
||||
)
|
||||
| Access_map _ -> simple_fail "no assign expressions with maps yet"
|
||||
| Access_map _ ->
|
||||
fail @@ not_supported_yet "assign expressions with maps are not supported yet" ae
|
||||
in
|
||||
bind_fold_list aux (typed_name.type_value , []) path in
|
||||
let%bind expr' = type_expression e expr in
|
||||
let t_expr' = get_type_annotation expr' in
|
||||
let%bind () =
|
||||
trace_strong (simple_error "assign type doesn't match left-hand-side") @@
|
||||
Ast_typed.assert_type_value_eq (assign_tv , get_type_annotation expr') in
|
||||
trace_strong (type_error
|
||||
~msg:"type of the expression to assign doesn't match left-hand-side"
|
||||
~expected:assign_tv
|
||||
~actual:t_expr'
|
||||
~expression:expr'
|
||||
expr'.location) @@
|
||||
Ast_typed.assert_type_value_eq (assign_tv , t_expr') in
|
||||
return (O.E_assign (typed_name , path' , expr')) (t_unit ())
|
||||
| E_let_in {binder ; rhs ; result} ->
|
||||
let%bind rhs_tv_opt = bind_map_option (evaluate_type e) (snd binder) in
|
||||
@ -517,11 +714,11 @@ and type_expression : environment -> ?tv_opt:O.type_value -> I.expression -> O.a
|
||||
ok {expr' with type_annotation}
|
||||
|
||||
|
||||
and type_constant (name:string) (lst:O.type_value list) (tv_opt:O.type_value option) : (string * O.type_value) result =
|
||||
and type_constant (name:string) (lst:O.type_value list) (tv_opt:O.type_value option) (loc : Location.t) : (string * O.type_value) result =
|
||||
(* Constant poorman's polymorphism *)
|
||||
let ct = Operators.Typer.constant_typers in
|
||||
let%bind typer =
|
||||
trace_option (unrecognized_constant name) @@
|
||||
trace_option (unrecognized_constant name loc) @@
|
||||
Map.String.find_opt name ct in
|
||||
typer lst tv_opt
|
||||
|
||||
@ -599,7 +796,7 @@ let rec untype_expression (e:O.annotated_expression) : (I.expression) result =
|
||||
return (e_failwith ae')
|
||||
| E_sequence _
|
||||
| E_loop _
|
||||
| E_assign _ -> simple_fail "not possible to untranspile statements yet"
|
||||
| E_assign _ -> fail @@ not_supported_yet_untranspile "not possible to untranspile statements yet" e.expression
|
||||
| E_let_in {binder;rhs;result} ->
|
||||
let%bind tv = untype_type_value rhs.type_annotation in
|
||||
let%bind rhs = untype_expression rhs in
|
||||
|
15
vendors/ligo-utils/proto-alpha-utils/trace.ml
vendored
15
vendors/ligo-utils/proto-alpha-utils/trace.ml
vendored
@ -12,7 +12,7 @@ let of_alpha_tz_error err = of_tz_error (AE.Ecoproto_error err)
|
||||
let trace_alpha_tzresult err : 'a AE.Error_monad.tzresult -> 'a result =
|
||||
function
|
||||
| Result.Ok x -> ok x
|
||||
| Error errs -> Errors (err :: List.map of_alpha_tz_error errs)
|
||||
| Error errs -> fail @@ thunk @@ patch_children (List.map of_alpha_tz_error errs) (err ())
|
||||
|
||||
let trace_alpha_tzresult_lwt error (x:_ AE.Error_monad.tzresult Lwt.t) : _ result =
|
||||
trace_alpha_tzresult error @@ Lwt_main.run x
|
||||
@ -20,21 +20,20 @@ let trace_alpha_tzresult_lwt error (x:_ AE.Error_monad.tzresult Lwt.t) : _ resul
|
||||
let trace_tzresult err =
|
||||
function
|
||||
| Result.Ok x -> ok x
|
||||
| Error errs -> Errors (err :: List.map of_tz_error errs)
|
||||
| Error errs -> fail @@ thunk @@ patch_children (List.map of_tz_error errs) (err ())
|
||||
|
||||
(* TODO: should be a combination of trace_tzresult and trace_r *)
|
||||
let trace_tzresult_r err_thunk_may_fail =
|
||||
function
|
||||
| Result.Ok x -> ok x
|
||||
| Error errs ->
|
||||
let tz_errs = List.map of_tz_error errs in
|
||||
| Error _errs ->
|
||||
(* let tz_errs = List.map of_tz_error errs in *)
|
||||
match err_thunk_may_fail () with
|
||||
| Simple_utils.Trace.Ok (err, annotations) -> ignore annotations; Errors (err :: tz_errs)
|
||||
| Errors errors_while_generating_error ->
|
||||
| Simple_utils.Trace.Ok (err, annotations) -> ignore annotations; Error (err)
|
||||
| Error errors_while_generating_error ->
|
||||
(* TODO: the complexity could be O(n*n) in the worst case,
|
||||
this should use some catenable lists. *)
|
||||
Errors (errors_while_generating_error
|
||||
@ tz_errs)
|
||||
Error (errors_while_generating_error)
|
||||
|
||||
let trace_tzresult_lwt err (x:_ TP.Error_monad.tzresult Lwt.t) : _ result =
|
||||
trace_tzresult err @@ Lwt_main.run x
|
||||
|
7
vendors/ligo-utils/simple-utils/location.ml
vendored
7
vendors/ligo-utils/simple-utils/location.ml
vendored
@ -12,6 +12,12 @@ type t =
|
||||
| File of Region.t (* file_location *)
|
||||
| Virtual of virtual_location
|
||||
|
||||
let pp = fun ppf t ->
|
||||
match t with
|
||||
| Virtual s -> Format.fprintf ppf "%s" s
|
||||
| File f -> Format.fprintf ppf "%s" (f#to_string `Point)
|
||||
|
||||
|
||||
let make (start_pos:Lexing.position) (end_pos:Lexing.position) : t =
|
||||
(* TODO: give correct unicode offsets (the random number is here so
|
||||
that searching for wrong souce locations appearing in messages
|
||||
@ -38,6 +44,7 @@ let pp_wrap f ppf { wrap_content ; _ } = Format.fprintf ppf "%a" f wrap_content
|
||||
let lift_region : 'a Region.reg -> 'a wrap = fun x ->
|
||||
wrap ~loc:(File x.region) x.value
|
||||
let lift : Region.region -> t = fun x -> File x
|
||||
let pp_lift = fun ppf r -> pp ppf @@ lift r
|
||||
|
||||
let r_extract : 'a Region.reg -> t = fun x -> File x.region
|
||||
let r_split : 'a Region.reg -> ('a * t) = fun x -> x.value , File x.region
|
||||
|
135
vendors/ligo-utils/simple-utils/trace.ml
vendored
135
vendors/ligo-utils/simple-utils/trace.ml
vendored
@ -3,8 +3,23 @@ module J = Yojson.Basic
|
||||
module JSON_string_utils = struct
|
||||
let member = J.Util.member
|
||||
let string = J.Util.to_string_option
|
||||
let to_list_option = fun x ->
|
||||
try ( Some (J.Util.to_list x))
|
||||
with _ -> None
|
||||
let to_assoc_option = fun x ->
|
||||
try ( Some (J.Util.to_assoc x))
|
||||
with _ -> None
|
||||
let list = to_list_option
|
||||
let assoc = to_assoc_option
|
||||
let int = J.Util.to_int_option
|
||||
|
||||
let patch j k v =
|
||||
match assoc j with
|
||||
| None -> j
|
||||
| Some assoc -> `Assoc (
|
||||
List.map (fun (k' , v') -> (k' , if k = k' then v else v')) assoc
|
||||
)
|
||||
|
||||
let swap f l r = f r l
|
||||
|
||||
let unit x = Some x
|
||||
@ -60,15 +75,15 @@ type annotation_thunk = annotation thunk
|
||||
point.
|
||||
*)
|
||||
type 'a result =
|
||||
Ok of 'a * annotation_thunk list
|
||||
| Errors of error_thunk list
|
||||
| Ok of 'a * annotation_thunk list
|
||||
| Error of error_thunk
|
||||
|
||||
|
||||
(**
|
||||
Constructors
|
||||
*)
|
||||
let ok x = Ok (x, [])
|
||||
let fail err = Errors [err]
|
||||
let fail err = Error err
|
||||
|
||||
(**
|
||||
Monadic operators
|
||||
@ -77,12 +92,12 @@ let bind f = function
|
||||
| Ok (x, annotations) ->
|
||||
(match f x with
|
||||
Ok (x', annotations') -> Ok (x', annotations' @ annotations)
|
||||
| Errors _ as e' -> ignore annotations; e')
|
||||
| Errors _ as e -> e
|
||||
| Error _ as e' -> ignore annotations; e')
|
||||
| Error _ as e -> e
|
||||
|
||||
let map f = function
|
||||
| Ok (x, annotations) -> Ok (f x, annotations)
|
||||
| Errors _ as e -> e
|
||||
| Error _ as e -> e
|
||||
|
||||
(**
|
||||
Usual bind-syntax is `>>=`, but this is taken from the Tezos code base. Where
|
||||
@ -125,6 +140,7 @@ let thunk x () = x
|
||||
let mk_error
|
||||
?(error_code : int thunk option) ?(message : string thunk option)
|
||||
?(data : (string * string thunk) list option)
|
||||
?(children = []) ?(infos = [])
|
||||
~(title : string thunk) () : error =
|
||||
let error_code' = X_option.map (fun x -> ("error_code" , `Int (x ()))) error_code in
|
||||
let title' = X_option.some ("title" , `String (title ())) in
|
||||
@ -132,14 +148,57 @@ let mk_error
|
||||
let aux (key , value) = (key , `String (value ())) in
|
||||
X_option.map (fun x -> ("data" , `Assoc (List.map aux x))) data in
|
||||
let message' = X_option.map (fun x -> ("message" , `String (x ()))) message in
|
||||
`Assoc (X_option.collapse_list [ error_code' ; title' ; message' ; data' ])
|
||||
let type' = Some ("type" , `String "error") in
|
||||
let children' = Some ("children" , `List children) in
|
||||
let infos' = Some ("infos" , `List infos) in
|
||||
`Assoc (X_option.collapse_list [ error_code' ; title' ; message' ; data' ; type' ; children' ; infos' ])
|
||||
|
||||
let error ?data ?error_code ?children ?infos title message () = mk_error ?data ?error_code ?children ?infos ~title:(title) ~message:(message) ()
|
||||
|
||||
let prepend_child = fun child err ->
|
||||
let open JSON_string_utils in
|
||||
let children_opt = err |> member "children" |> list in
|
||||
let children = match children_opt with
|
||||
| Some children -> (child ()) :: children
|
||||
| None -> [ child () ] in
|
||||
patch err "children" (`List children)
|
||||
|
||||
let patch_children = fun children err ->
|
||||
let open JSON_string_utils in
|
||||
patch err "children" (`List (List.map (fun f -> f ()) children))
|
||||
|
||||
(**
|
||||
Build a standard info, with a title, a message, an info code and some data.
|
||||
*)
|
||||
let mk_info
|
||||
?(info_code : int thunk option) ?(message : string thunk option)
|
||||
?(data : (string * string thunk) list option)
|
||||
~(title : string thunk) () : error =
|
||||
let error_code' = X_option.map (fun x -> ("error_code" , `Int (x ()))) info_code in
|
||||
let title' = X_option.some ("title" , `String (title ())) in
|
||||
let data' =
|
||||
let aux (key , value) = (key , `String (value ())) in
|
||||
X_option.map (fun x -> ("data" , `Assoc (List.map aux x))) data in
|
||||
let message' = X_option.map (fun x -> ("message" , `String (x ()))) message in
|
||||
let type' = Some ("type" , `String "info") in
|
||||
`Assoc (X_option.collapse_list [ error_code' ; title' ; message' ; data' ; type' ])
|
||||
|
||||
let info ?data ?info_code title message () = mk_info ?data ?info_code ~title:(title) ~message:(message) ()
|
||||
|
||||
let prepend_info = fun info err ->
|
||||
let open JSON_string_utils in
|
||||
let infos_opt = err |> member "infos" |> list in
|
||||
let infos = match infos_opt with
|
||||
| Some infos -> info :: infos
|
||||
| None -> [ info ] in
|
||||
patch err "infos" (`List infos)
|
||||
|
||||
let error ?data ?error_code title message () = mk_error ?data ?error_code ~title:(title) ~message:(message) ()
|
||||
|
||||
(**
|
||||
Helpers that ideally shouldn't be used in production.
|
||||
*)
|
||||
let simple_error str () = mk_error ~title:(thunk str) ()
|
||||
let simple_info str () = mk_info ~title:(thunk str) ()
|
||||
let simple_fail str = fail @@ simple_error str
|
||||
|
||||
(**
|
||||
@ -176,31 +235,62 @@ let dummy_fail = simple_fail "dummy"
|
||||
```
|
||||
And this will pass along the error triggered by "get key map".
|
||||
*)
|
||||
let trace err = function
|
||||
let trace info = function
|
||||
| Ok _ as o -> o
|
||||
| Errors errs -> Errors (err :: errs)
|
||||
| Error err -> Error (fun () -> prepend_info (info ()) (err ()))
|
||||
|
||||
(**
|
||||
Erase the current error stack, and replace it by the given error. It's useful
|
||||
when using `Asserts` and you want to discard its auto-generated message.
|
||||
when using `Assert` and you want to discard its auto-generated message.
|
||||
*)
|
||||
let trace_strong err = function
|
||||
| Ok _ as o -> o
|
||||
| Errors _ -> Errors [err]
|
||||
| Error _ -> Error err
|
||||
|
||||
(**
|
||||
Sometimes, when you have a list of potentially erroneous elements, you need
|
||||
to retrieve all the errors, instead of just the first one. In that case, do:
|
||||
```
|
||||
let type_list lst =
|
||||
let%bind lst' =
|
||||
trace_list (simple_error "Error while typing a list") @@
|
||||
List.map type_element lst in
|
||||
...
|
||||
```
|
||||
Where before you would have written:
|
||||
```
|
||||
let type_list lst =
|
||||
let%bind lst' = bind_map_list type_element lst in
|
||||
...
|
||||
```
|
||||
*)
|
||||
let trace_list err lst =
|
||||
let oks =
|
||||
let aux = function
|
||||
| Ok (x , _) -> Some x
|
||||
| _ -> None in
|
||||
X_list.filter_map aux lst in
|
||||
let errs =
|
||||
let aux = function
|
||||
| Error x -> Some x
|
||||
| _ -> None in
|
||||
X_list.filter_map aux lst in
|
||||
match errs with
|
||||
| [] -> ok oks
|
||||
| errs -> fail (fun () -> patch_children errs err)
|
||||
|
||||
(**
|
||||
Trace, but with an error which generation may itself fail.
|
||||
*)
|
||||
let trace_r err_thunk_may_fail = function
|
||||
| Ok _ as o -> o
|
||||
| Errors errs -> (
|
||||
| Error _ -> (
|
||||
match err_thunk_may_fail () with
|
||||
| Ok (err, annotations) -> ignore annotations; Errors (err :: errs)
|
||||
| Errors errors_while_generating_error ->
|
||||
| Ok (err, annotations) -> ignore annotations; Error (err)
|
||||
| Error errors_while_generating_error ->
|
||||
(* TODO: the complexity could be O(n*n) in the worst case,
|
||||
this should use some catenable lists. *)
|
||||
Errors (errors_while_generating_error
|
||||
@ errs)
|
||||
Error (errors_while_generating_error)
|
||||
)
|
||||
|
||||
(**
|
||||
@ -231,11 +321,11 @@ let trace_f_2_ez f name =
|
||||
*)
|
||||
let to_bool = function
|
||||
| Ok _ -> true
|
||||
| Errors _ -> false
|
||||
| Error _ -> false
|
||||
|
||||
let to_option = function
|
||||
| Ok (o, annotations) -> ignore annotations; Some o
|
||||
| Errors _ -> None
|
||||
| Error _ -> None
|
||||
|
||||
(**
|
||||
Convert an option to a result, with a given error if the parameter is None.
|
||||
@ -268,6 +358,7 @@ let rec bind_list = function
|
||||
bind_list tl >>? fun tl ->
|
||||
ok @@ hd :: tl
|
||||
)
|
||||
|
||||
let bind_ne_list = fun (hd , tl) ->
|
||||
hd >>? fun hd ->
|
||||
bind_list tl >>? fun tl ->
|
||||
@ -341,7 +432,7 @@ let bind_find_map_list error f lst =
|
||||
| [] -> fail error
|
||||
| hd :: tl -> (
|
||||
match f hd with
|
||||
| Errors _ -> aux tl
|
||||
| Error _ -> aux tl
|
||||
| o -> o
|
||||
)
|
||||
in
|
||||
@ -360,7 +451,7 @@ let bind_lr (type a b) ((a : a result), (b:b result)) : [`Left of a | `Right of
|
||||
match (a, b) with
|
||||
| (Ok _ as o), _ -> map (fun x -> `Left x) o
|
||||
| _, (Ok _ as o) -> map (fun x -> `Right x) o
|
||||
| _, Errors b -> Errors b
|
||||
| _, Error b -> Error b
|
||||
|
||||
let bind_lr_lazy (type a b) ((a : a result), (b:unit -> b result)) : [`Left of a | `Right of b] result =
|
||||
match a with
|
||||
@ -368,7 +459,7 @@ let bind_lr_lazy (type a b) ((a : a result), (b:unit -> b result)) : [`Left of a
|
||||
| _ -> (
|
||||
match b() with
|
||||
| Ok _ as o -> map (fun x -> `Right x) o
|
||||
| Errors b -> Errors b
|
||||
| Error b -> Error b
|
||||
)
|
||||
|
||||
let bind_and (a, b) =
|
||||
|
Loading…
Reference in New Issue
Block a user