open Helpers module AST = Ast_typed module Append_tree = Tree.Append open Mini_c open Trace 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 wrong_mini_c_value expected_type actual = let title () = "untranspiler: 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 end open Errors let rec untranspile (v : value) (t : AST.type_expression) : AST.expression result = let open! AST in let return e = ok (make_a_e_empty e t) in match t.type_content with | T_variable (name) when Var.equal name Stage_common.Constant.t_bool -> ( let%bind b = trace_strong (wrong_mini_c_value "bool" v) @@ get_bool v in return (e_bool b Environment.empty) ) | t when (compare t (t_bool ()).type_content) = 0-> ( let%bind b = trace_strong (wrong_mini_c_value "bool" v) @@ get_bool v in return (e_bool b Environment.empty) ) | T_constant type_constant -> ( match type_constant with | TC_unit -> ( let%bind () = trace_strong (wrong_mini_c_value "unit" v) @@ get_unit v in return (E_literal Literal_unit) ) | TC_int -> ( let%bind n = trace_strong (wrong_mini_c_value "int" v) @@ get_int v in return (E_literal (Literal_int n)) ) | TC_nat -> ( let%bind n = trace_strong (wrong_mini_c_value "nat" v) @@ get_nat v in return (E_literal (Literal_nat n)) ) | TC_timestamp -> ( let%bind n = trace_strong (wrong_mini_c_value "timestamp" v) @@ get_timestamp v in return (E_literal (Literal_timestamp n)) ) | TC_mutez -> ( let%bind n = trace_strong (wrong_mini_c_value "tez" v) @@ get_mutez v in return (E_literal (Literal_mutez n)) ) | TC_string -> ( let%bind n = trace_strong (wrong_mini_c_value "string" v) @@ get_string v in return (E_literal (Literal_string n)) ) | TC_bytes -> ( let%bind n = trace_strong (wrong_mini_c_value "bytes" v) @@ get_bytes v in return (E_literal (Literal_bytes n)) ) | TC_address -> ( let%bind n = trace_strong (wrong_mini_c_value "address" v) @@ get_string v in return (E_literal (Literal_address n)) ) | TC_operation -> ( let%bind op = trace_strong (wrong_mini_c_value "operation" v) @@ get_operation v in return (E_literal (Literal_operation op)) ) | TC_key -> ( let%bind n = trace_strong (wrong_mini_c_value "key" v) @@ get_string v in return (E_literal (Literal_key n)) ) | TC_key_hash -> ( let%bind n = trace_strong (wrong_mini_c_value "key_hash" v) @@ get_string v in return (E_literal (Literal_key_hash n)) ) | TC_chain_id -> ( let%bind n = trace_strong (wrong_mini_c_value "chain_id" v) @@ get_string v in return (E_literal (Literal_chain_id n)) ) | TC_void -> ( let%bind () = trace_strong (wrong_mini_c_value "void" v) @@ get_unit v in return (E_literal (Literal_void)) ) | TC_signature -> ( let%bind n = trace_strong (wrong_mini_c_value "signature" v) @@ get_string v in return (E_literal (Literal_signature n)) ) ) | T_operator type_operator -> ( match type_operator with | TC_option o -> ( 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') ) | TC_map {k=k_ty;v=v_ty}-> ( let%bind map = trace_strong (wrong_mini_c_value "map" v) @@ get_map v in let%bind map' = let aux = fun (k, v) -> let%bind k = untranspile k k_ty in let%bind v = untranspile v v_ty in ok ({k; v} : AST.map_kv) in bind_map_list aux map in let map' = List.sort_uniq compare map' in let aux = fun prev ({ k ; v } : AST.map_kv) -> return @@ E_constant {cons_name=C_MAP_ADD;arguments=[k ; v ; prev]} in let%bind init = return @@ E_constant {cons_name=C_MAP_EMPTY;arguments=[]} in bind_fold_right_list aux init map' ) | TC_big_map {k=k_ty; v=v_ty} -> ( let%bind big_map = trace_strong (wrong_mini_c_value "big_map" v) @@ get_big_map v in let%bind big_map' = let aux = fun (k, v) -> let%bind k = untranspile k k_ty in let%bind v = untranspile v v_ty in ok ({k; v} : AST.map_kv) in bind_map_list aux big_map in let big_map' = List.sort_uniq compare big_map' in let aux = fun prev ({ k ; v } : AST.map_kv) -> return @@ E_constant {cons_name=C_MAP_ADD;arguments=[k ; v ; prev]} in let%bind init = return @@ E_constant {cons_name=C_BIG_MAP_EMPTY;arguments=[]} in bind_fold_right_list aux init big_map' ) | TC_map_or_big_map _ -> fail @@ corner_case ~loc:"untranspiler" "TC_map_or_big_map t should not be present in mini-c" | TC_list ty -> ( 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 let aux = fun prev cur -> return @@ E_constant {cons_name=C_CONS;arguments=[cur ; prev]} in let%bind init = return @@ E_constant {cons_name=C_LIST_EMPTY;arguments=[]} in bind_fold_right_list aux init lst' ) | TC_set ty -> ( let%bind lst = trace_strong (wrong_mini_c_value "set" v) @@ get_set v in let%bind lst' = let aux = fun e -> untranspile e ty in bind_map_list aux lst in let lst' = List.sort_uniq compare lst' in let aux = fun prev cur -> return @@ E_constant {cons_name=C_SET_ADD;arguments=[cur ; prev]} in let%bind init = return @@ E_constant {cons_name=C_SET_EMPTY;arguments=[]} in bind_fold_list aux init lst' ) | TC_contract _ -> fail @@ bad_untranspile "contract" v ) | T_sum m -> let lst = List.map (fun (k,{ctor_type;_}) -> (k,ctor_type)) @@ kv_list_of_cmap m in let%bind node = match Append_tree.of_list lst with | Empty -> fail @@ corner_case ~loc:__LOC__ "empty sum type" | Full t -> ok t 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 {constructor=Constructor name;element=sub}) | T_record m -> let lst = List.map (fun (k,{field_type;_}) -> (k,field_type)) @@ Ast_typed.Helpers.kv_list_of_record_or_tuple m in let%bind node = match Append_tree.of_list lst with | Empty -> fail @@ corner_case ~loc:__LOC__ "empty record" | Full t -> ok t 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' = AST.LMap.of_list lst in return (E_record m') | T_arrow _ -> let%bind n = trace_strong (wrong_mini_c_value "lambda as string" v) @@ get_string v in return (E_literal (Literal_string n)) | T_variable _ -> fail @@ corner_case ~loc:__LOC__ "trying to untranspile at variable type"