ligo/src/passes/8-compiler/compiler_program.ml

518 lines
18 KiB
OCaml
Raw Normal View History

2019-05-13 00:56:22 +04:00
open Trace
open Mini_c
open Michelson
2019-09-05 17:21:01 +04:00
open Memory_proto_alpha.Protocol.Script_ir_translator
2019-05-13 00:56:22 +04:00
open Operators.Compiler
2019-11-09 11:27:30 +04:00
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 contract_entrypoint_must_be_literal ~loc =
let title () = "contract entrypoint must be literal" in
let content () = "For get_entrypoint, entrypoint must be given as a literal string" in
let data =
[ ("location", fun () -> loc) ;
] in
error ~data title content
end
open Errors
(* This does not makes sense to me *)
2020-02-28 21:11:02 +04:00
let rec get_operator : constant' -> type_value -> expression list -> predicate result = fun s ty lst ->
match Operators.Compiler.get_operators s with
| Ok (x,_) -> ok x
| Error _ -> (
2019-05-13 00:56:22 +04:00
match s with
| C_SELF -> (
let%bind entrypoint_as_string = match lst with
| [{ content = E_literal (D_string s); type_value = _ }] -> (
match String.split_on_char '%' s with
| ["" ; s] -> ok @@ String.concat "" ["%" ; (String.uncapitalize_ascii s)]
| _ -> fail @@ corner_case ~loc:__LOC__ "mini_c . SELF"
)
| _ ->
fail @@ corner_case ~loc:__LOC__ "mini_c . SELF" in
ok @@ simple_unary @@ seq [
i_drop ;
prim ~annot:[entrypoint_as_string] I_SELF
]
)
| C_NONE -> (
2019-06-10 05:41:02 +04:00
let%bind ty' = Mini_c.get_t_option ty in
let%bind m_ty = Compiler_type.type_ ty' in
2019-07-20 15:46:42 +04:00
ok @@ simple_constant @@ prim ~children:[m_ty] I_NONE
2019-06-10 05:41:02 +04:00
)
| C_NIL -> (
2019-07-19 14:13:09 +04:00
let%bind ty' = Mini_c.get_t_list ty in
let%bind m_ty = Compiler_type.type_ ty' in
ok @@ simple_unary @@ prim ~children:[m_ty] I_NIL
)
2020-03-07 02:44:28 +04:00
| C_LOOP_CONTINUE -> (
let%bind (_,ty) = get_t_or ty in
let%bind m_ty = Compiler_type.type_ ty in
let m_ty' = t_pair t_unit m_ty in
ok @@ simple_unary @@ prim ~children:[m_ty'] I_LEFT
)
| C_LOOP_STOP -> (
let%bind (ty, _) = get_t_or ty in
let%bind m_ty = Compiler_type.type_ ty in
ok @@ simple_unary @@ seq [ i_push_unit; i_pair; prim ~children:[m_ty] I_RIGHT]
)
| C_SET_EMPTY -> (
2019-07-19 16:35:47 +04:00
let%bind ty' = Mini_c.get_t_set ty in
let%bind m_ty = Compiler_type.type_ ty' in
ok @@ simple_constant @@ prim ~children:[m_ty] I_EMPTY_SET
)
| C_BYTES_UNPACK -> (
2019-06-10 05:41:02 +04:00
let%bind ty' = Mini_c.get_t_option ty in
let%bind m_ty = Compiler_type.type_ ty' in
ok @@ simple_unary @@ prim ~children:[m_ty] I_UNPACK
)
| C_MAP_REMOVE ->
2019-05-13 00:56:22 +04:00
let%bind v = match lst with
| [ _ ; expr ] ->
2019-09-11 18:02:06 +04:00
let%bind (_, v) = Mini_c.Combinators.(bind_map_or (get_t_map , get_t_big_map) (Expression.get_type expr)) in
2019-05-13 00:56:22 +04:00
ok v
| _ -> simple_fail "mini_c . MAP_REMOVE" in
let%bind v_ty = Compiler_type.type_ v in
ok @@ simple_binary @@ seq [dip (i_none v_ty) ; prim I_UPDATE ]
| C_LEFT ->
2019-05-13 00:56:22 +04:00
let%bind r = match lst with
| [ _ ] -> get_t_right ty
| _ -> simple_fail "mini_c . LEFT" in
let%bind r_ty = Compiler_type.type_ r in
ok @@ simple_unary @@ prim ~children:[r_ty] I_LEFT
| C_RIGHT ->
2019-05-13 00:56:22 +04:00
let%bind l = match lst with
| [ _ ] -> get_t_left ty
| _ -> simple_fail "mini_c . RIGHT" in
let%bind l_ty = Compiler_type.type_ l in
ok @@ simple_unary @@ prim ~children:[l_ty] I_RIGHT
| C_CONTRACT ->
2019-11-09 11:27:30 +04:00
let%bind r = get_t_contract ty in
2019-05-13 00:56:22 +04:00
let%bind r_ty = Compiler_type.type_ r in
ok @@ simple_unary @@ seq [
prim ~children:[r_ty] I_CONTRACT ;
i_assert_some_msg (i_push_string "bad address for get_contract") ;
]
| C_CONTRACT_OPT ->
let%bind tc = get_t_option ty in
let%bind r = get_t_contract tc in
let%bind r_ty = Compiler_type.type_ r in
ok @@ simple_unary @@ prim ~children:[r_ty] I_CONTRACT ;
| C_CONTRACT_ENTRYPOINT ->
2019-11-09 11:27:30 +04:00
let%bind r = get_t_contract ty in
let%bind r_ty = Compiler_type.type_ r in
let%bind entry = match lst with
| [ { content = E_literal (D_string entry); type_value = _ } ; _addr ] -> ok entry
| [ _entry ; _addr ] ->
fail @@ contract_entrypoint_must_be_literal ~loc:__LOC__
| _ ->
fail @@ corner_case ~loc:__LOC__ "mini_c . CONTRACT_ENTRYPOINT" in
ok @@ simple_binary @@ seq [
i_drop ; (* drop the entrypoint... *)
prim ~annot:[entry] ~children:[r_ty] I_CONTRACT ;
i_assert_some_msg (i_push_string @@ Format.sprintf "bad address for get_entrypoint (%s)" entry) ;
]
| C_CONTRACT_ENTRYPOINT_OPT ->
let%bind tc = get_t_option ty in
let%bind r = get_t_contract tc in
let%bind r_ty = Compiler_type.type_ r in
let%bind entry = match lst with
| [ { content = E_literal (D_string entry); type_value = _ } ; _addr ] -> ok entry
| [ _entry ; _addr ] ->
fail @@ contract_entrypoint_must_be_literal ~loc:__LOC__
| _ ->
fail @@ corner_case ~loc:__LOC__ "mini_c . CONTRACT_ENTRYPOINT" in
ok @@ simple_binary @@ seq [
i_drop ; (* drop the entrypoint... *)
prim ~annot:[entry] ~children:[r_ty] I_CONTRACT ;
]
2020-02-28 21:11:02 +04:00
| C_CREATE_CONTRACT ->
let%bind ch = match lst with
| { content= E_closure {body;binder} ; type_value = T_function (T_pair ((_,p),(_,s)) as tin,_)} :: _ ->
let%bind closure = translate_function_body {body;binder} [] tin in
let%bind (p',s') = bind_map_pair Compiler_type.type_ (p,s) in
ok @@ contract p' s' closure
| _ -> fail @@ corner_case ~loc:__LOC__ "mini_c . CREATE_CONTRACT"
in
ok @@ simple_tetrary @@ seq [
i_drop ;
prim ~children:[ch] I_CREATE_CONTRACT ;
i_pair ;
]
2019-12-04 21:30:52 +04:00
| x -> simple_fail (Format.asprintf "predicate \"%a\" doesn't exist" PP.constant x)
2019-05-13 00:56:22 +04:00
)
2020-02-28 21:11:02 +04:00
and translate_value (v:value) ty : michelson result = match v with
2019-05-13 00:56:22 +04:00
| D_bool b -> ok @@ prim (if b then D_True else D_False)
| D_int n -> ok @@ int (Z.of_int n)
| D_nat n -> ok @@ int (Z.of_int n)
| D_timestamp n -> ok @@ int (Z.of_int n)
2019-09-24 16:29:18 +04:00
| D_mutez n -> ok @@ int (Z.of_int n)
2019-05-13 00:56:22 +04:00
| D_string s -> ok @@ string s
2019-11-03 02:54:33 +04:00
| D_bytes s -> ok @@ bytes s
2019-05-13 00:56:22 +04:00
| D_unit -> ok @@ prim D_Unit
| D_pair (a, b) -> (
2019-08-21 00:51:16 +04:00
let%bind (a_ty , b_ty) = get_t_pair ty in
let%bind a = translate_value a a_ty in
let%bind b = translate_value b b_ty in
2019-05-13 00:56:22 +04:00
ok @@ prim ~children:[a;b] D_Pair
)
2019-08-21 00:51:16 +04:00
| D_left a -> (
let%bind (a_ty , _) = get_t_or ty in
let%bind a' = translate_value a a_ty in
ok @@ prim ~children:[a'] D_Left
)
| D_right b -> (
let%bind (_ , b_ty) = get_t_or ty in
let%bind b' = translate_value b b_ty in
ok @@ prim ~children:[b'] D_Right
)
2019-05-13 00:56:22 +04:00
| D_none -> ok @@ prim D_None
| D_some s ->
2019-08-21 00:51:16 +04:00
let%bind s' = translate_value s ty in
2019-05-13 00:56:22 +04:00
ok @@ prim ~children:[s'] D_Some
2019-08-21 00:51:16 +04:00
| D_map lst -> (
let%bind (k_ty , v_ty) = get_t_map ty in
let%bind lst' =
let aux (k , v) = bind_pair (translate_value k k_ty , translate_value v v_ty) in
bind_map_list aux lst in
2019-09-11 18:02:06 +04:00
let sorted = List.sort (fun (x , _) (y , _) -> compare x y) lst' in
let aux (a, b) = prim ~children:[a;b] D_Elt in
ok @@ seq @@ List.map aux sorted
)
| D_big_map lst -> (
let%bind (k_ty , v_ty) = get_t_big_map ty in
let%bind lst' =
let aux (k , v) = bind_pair (translate_value k k_ty , translate_value v v_ty) in
bind_map_list aux lst in
2019-07-19 16:35:47 +04:00
let sorted = List.sort (fun (x , _) (y , _) -> compare x y) lst' in
2019-05-13 00:56:22 +04:00
let aux (a, b) = prim ~children:[a;b] D_Elt in
2019-07-19 16:35:47 +04:00
ok @@ seq @@ List.map aux sorted
2019-08-21 00:51:16 +04:00
)
| D_list lst -> (
let%bind e_ty = get_t_list ty in
let%bind lst' = bind_map_list (fun x -> translate_value x e_ty) lst in
2019-05-13 00:56:22 +04:00
ok @@ seq lst'
2019-08-21 00:51:16 +04:00
)
| D_set lst -> (
let%bind e_ty = get_t_set ty in
let%bind lst' = bind_map_list (fun x -> translate_value x e_ty) lst in
2019-07-19 16:35:47 +04:00
let sorted = List.sort compare lst' in
ok @@ seq sorted
2019-08-21 00:51:16 +04:00
)
2019-05-13 00:56:22 +04:00
| D_operation _ ->
simple_fail "can't compile an operation"
2019-08-21 00:51:16 +04:00
and translate_expression (expr:expression) (env:environment) : michelson result =
2019-05-13 00:56:22 +04:00
let (expr' , ty) = Combinators.Expression.(get_content expr , get_type expr) in
let error_message () =
Format.asprintf "\n- expr: %a\n- type: %a\n" PP.expression expr PP.type_variable ty
in
2019-08-21 00:51:16 +04:00
let return code = ok code in
2019-05-13 00:56:22 +04:00
trace (error (thunk "compiling expression") error_message) @@
match expr' with
2019-08-21 00:51:16 +04:00
| E_skip -> return @@ i_push_unit
2019-05-13 00:56:22 +04:00
| E_literal v ->
2019-08-21 00:51:16 +04:00
let%bind v = translate_value v ty in
2019-05-13 00:56:22 +04:00
let%bind t = Compiler_type.type_ ty in
return @@ i_push t v
2019-08-21 12:28:27 +04:00
| E_closure anon -> (
match ty with
| T_function (input_ty , output_ty) ->
translate_function anon env input_ty output_ty
| _ -> simple_fail "expected function type"
2019-08-21 12:28:27 +04:00
)
| E_application (f , arg) -> (
2019-10-17 18:34:02 +04:00
trace (simple_error "Compiling quote application") @@
let%bind f = translate_expression f env in
let%bind arg = translate_expression arg env in
return @@ seq [
arg ;
dip f ;
prim I_EXEC ;
]
2019-05-13 00:56:22 +04:00
)
| E_variable x ->
2019-05-16 01:05:09 +04:00
let%bind code = Compiler_environment.get env x in
return code
| E_sequence (a , b) -> (
2019-08-21 00:51:16 +04:00
let%bind a' = translate_expression a env in
let%bind b' = translate_expression b env in
return @@ seq [
2019-05-16 01:05:09 +04:00
a' ;
2019-08-21 03:19:00 +04:00
i_drop ;
2019-05-16 01:05:09 +04:00
b' ;
]
)
2019-12-04 21:30:52 +04:00
| E_constant{cons_name=str;arguments= lst} ->
2019-05-13 00:56:22 +04:00
let module L = Logger.Stateful() in
2019-08-21 03:19:00 +04:00
let%bind pre_code =
let aux code expr =
let%bind expr_code = translate_expression expr env in
2019-05-13 00:56:22 +04:00
L.log @@ Format.asprintf "\n%a -> %a in %a\n"
PP.expression expr
2019-08-21 03:19:00 +04:00
Michelson.pp expr_code
2019-05-13 00:56:22 +04:00
PP.environment env ;
2019-08-21 03:19:00 +04:00
ok (seq [ expr_code ; dip code ]) in
bind_fold_right_list aux (seq []) lst in
2019-09-11 15:56:39 +04:00
let%bind predicate = get_operator str ty lst in
2019-05-13 00:56:22 +04:00
let%bind code = match (predicate, List.length lst) with
| Constant c, 0 -> ok @@ seq [
pre_code ;
c ;
]
| Unary f, 1 -> ok @@ seq [
pre_code ;
f ;
]
| Binary f, 2 -> ok @@ seq [
pre_code ;
f ;
]
| Ternary f, 3 -> ok @@ seq [
pre_code ;
f ;
]
2020-02-28 21:11:02 +04:00
| Tetrary f, 4 -> ok @@ seq [
pre_code ;
f ;
]
2019-12-04 21:30:52 +04:00
| _ -> simple_fail (Format.asprintf "bad arity for %a" PP.constant str)
2019-05-13 00:56:22 +04:00
in
let error =
let title () = "error compiling constant" in
let content () = L.get () in
error title content in
trace error @@
return code
2019-05-15 22:28:25 +04:00
| E_make_empty_map sd ->
2019-05-13 00:56:22 +04:00
let%bind (src, dst) = bind_map_pair Compiler_type.type_ sd in
return @@ i_empty_map src dst
2019-11-05 03:01:39 +04:00
| E_make_empty_big_map sd ->
let%bind (src, dst) = bind_map_pair Compiler_type.type_ sd in
return @@ i_empty_big_map src dst
2019-05-15 22:28:25 +04:00
| E_make_empty_list t ->
2019-05-13 00:56:22 +04:00
let%bind t' = Compiler_type.type_ t in
return @@ i_nil t'
| E_make_empty_set t ->
let%bind t' = Compiler_type.type_ t in
return @@ i_empty_set t'
2019-05-13 00:56:22 +04:00
| E_make_none o ->
let%bind o' = Compiler_type.type_ o in
return @@ i_none o'
2019-05-15 22:28:25 +04:00
| E_if_bool (c, a, b) -> (
2019-08-21 00:51:16 +04:00
let%bind c' = translate_expression c env in
let%bind a' = translate_expression a env in
let%bind b' = translate_expression b env in
2019-05-13 00:56:22 +04:00
let%bind code = ok (seq [
c' ;
i_if a' b' ;
]) in
2019-08-21 00:51:16 +04:00
return code
2019-05-13 00:56:22 +04:00
)
2019-05-15 22:16:28 +04:00
| E_if_none (c, n, (ntv , s)) -> (
2019-08-21 00:51:16 +04:00
let%bind c' = translate_expression c env in
let%bind n' = translate_expression n env in
let s_env = Environment.add ntv env in
let%bind s' = translate_expression s s_env in
2019-05-13 00:56:22 +04:00
let%bind code = ok (seq [
c' ;
i_if_none n' (seq [
s' ;
2019-08-21 03:19:00 +04:00
dip i_drop ;
2019-05-13 00:56:22 +04:00
])
;
]) in
return code
)
2019-09-21 11:12:00 +04:00
| E_if_cons (cond , nil , ((hd , tl) , cons)) -> (
let%bind cond' = translate_expression cond env in
let%bind nil' = translate_expression nil env in
let s_env =
Environment.add hd
@@ Environment.add tl env
in
let%bind s' = translate_expression cons s_env in
let%bind code = ok (seq [
cond' ;
i_if_cons (seq [
s' ;
dip (seq [ i_drop ; i_drop ]) ;
]) nil'
;
]) in
return code
)
2019-05-15 22:16:28 +04:00
| E_if_left (c, (l_ntv , l), (r_ntv , r)) -> (
2019-08-21 00:51:16 +04:00
let%bind c' = translate_expression c env in
2019-05-15 22:16:28 +04:00
let l_env = Environment.add l_ntv env in
2019-08-21 00:51:16 +04:00
let%bind l' = translate_expression l l_env in
2019-05-15 22:16:28 +04:00
let r_env = Environment.add r_ntv env in
2019-08-21 00:51:16 +04:00
let%bind r' = translate_expression r r_env in
2019-05-13 00:56:22 +04:00
let%bind code = ok (seq [
c' ;
i_if_left (seq [
l' ;
i_comment "restrict left" ;
2019-08-21 03:19:00 +04:00
dip i_drop ;
2019-05-13 00:56:22 +04:00
]) (seq [
r' ;
i_comment "restrict right" ;
2019-08-21 03:19:00 +04:00
dip i_drop ;
2019-05-13 00:56:22 +04:00
])
;
]) in
return code
)
2020-01-16 23:36:04 +04:00
| E_let_in (v , _, expr , body) -> (
2019-08-21 00:51:16 +04:00
let%bind expr' = translate_expression expr env in
2019-08-21 03:19:00 +04:00
let%bind body' = translate_expression body (Environment.add v env) in
2019-05-13 00:56:22 +04:00
let%bind code = ok (seq [
expr' ;
body' ;
i_comment "restrict let" ;
2019-08-21 03:19:00 +04:00
dip i_drop ;
2019-05-13 00:56:22 +04:00
]) in
return code
)
2019-12-04 21:30:52 +04:00
| E_iterator (name,(v , body) , expr) -> (
2019-08-21 00:51:16 +04:00
let%bind expr' = translate_expression expr env in
2019-08-21 03:19:00 +04:00
let%bind body' = translate_expression body (Environment.add v env) in
2019-07-20 15:46:42 +04:00
match name with
| C_ITER -> (
2019-07-20 15:46:42 +04:00
let%bind code = ok (seq [
expr' ;
2019-08-21 13:41:57 +04:00
i_iter (seq [body' ; i_drop ; i_drop]) ;
2019-08-21 03:19:00 +04:00
i_push_unit ;
2019-07-20 15:46:42 +04:00
]) in
2019-08-21 00:51:16 +04:00
return code
2019-07-20 15:46:42 +04:00
)
| C_MAP -> (
2019-07-20 15:46:42 +04:00
let%bind code = ok (seq [
expr' ;
2019-08-21 03:19:00 +04:00
i_map (seq [body' ; dip i_drop]) ;
2019-07-20 15:46:42 +04:00
]) in
2019-08-21 00:51:16 +04:00
return code
2019-07-20 15:46:42 +04:00
)
2020-03-07 02:44:28 +04:00
| C_LOOP_LEFT -> (
let%bind (_, ty) = get_t_or (snd v) in
let%bind m_ty = Compiler_type.type_ ty in
let m_ty' = t_pair t_unit m_ty in
let%bind code = ok (seq [
expr' ;
prim ~children:[m_ty'] I_LEFT;
i_loop_left body';
prim I_CDR
]) in
return code
)
2019-07-20 15:46:42 +04:00
| s -> (
2019-12-04 21:30:52 +04:00
let iter = Format.asprintf "iter %a" PP.constant s in
let error = error (thunk "bad iterator") (thunk iter) in
2019-07-20 15:46:42 +04:00
fail error
)
)
2019-09-24 01:33:25 +04:00
| E_fold ((v , body) , collection , initial) -> (
let%bind collection' = translate_expression collection env in
let%bind initial' = translate_expression initial env in
let%bind body' = translate_expression body (Environment.add v env) in
let code = seq [
collection' ;
dip initial' ;
i_iter (seq [
i_swap ;
i_pair ; body' ; dip i_drop ;
]) ;
] in
ok code
)
2019-12-04 21:30:52 +04:00
| E_record_update (record, path, expr) -> (
2020-01-10 20:28:45 +04:00
let%bind record' = translate_expression record env in
let record_var = Var.fresh () in
let env' = Environment.add (record_var, record.type_value) env in
let%bind expr' = translate_expression expr env' in
let modify_code =
let aux acc step = match step with
| `Left -> seq [dip i_unpair ; acc ; i_pair]
| `Right -> seq [dip i_unpiar ; acc ; i_piar]
2020-01-09 21:23:37 +04:00
in
let init = dip i_drop in
List.fold_right' aux init path
2020-01-09 21:23:37 +04:00
in
return @@ seq [
i_comment "r_update: start # env";
record';
i_comment "r_update: move the record on top # env";
expr';
i_comment "r_updates : compute rhs # rhs:env";
modify_code;
i_comment "r_update: modify code # record+rhs : env";
]
2020-01-09 21:23:37 +04:00
)
| E_while (expr , block) -> (
2019-08-21 00:51:16 +04:00
let%bind expr' = translate_expression expr env in
2019-08-21 03:19:00 +04:00
let%bind block' = translate_expression block env in
2019-08-21 00:51:16 +04:00
return @@ seq [
2019-05-16 12:12:53 +04:00
expr' ;
prim ~children:[seq [
block' ;
2019-08-21 03:19:00 +04:00
i_drop ;
2019-05-16 12:12:53 +04:00
expr']] I_LOOP ;
2019-08-21 03:19:00 +04:00
i_push_unit ;
2019-05-16 12:12:53 +04:00
]
)
2019-05-13 00:56:22 +04:00
2019-09-19 03:34:37 +04:00
and translate_function_body ({body ; binder} : anon_function) lst input : michelson result =
2019-08-21 12:28:27 +04:00
let pre_env = Environment.of_list lst in
let env = Environment.(add (binder , input) pre_env) in
2019-09-19 03:34:37 +04:00
let%bind expr_code = translate_expression body env in
2019-08-21 12:28:27 +04:00
let%bind unpack_closure_code = Compiler_environment.unpack_closure pre_env in
2019-05-13 00:56:22 +04:00
let code = seq [
2019-08-21 12:28:27 +04:00
i_comment "unpack closure env" ;
unpack_closure_code ;
2019-05-13 00:56:22 +04:00
i_comment "function result" ;
2019-08-21 12:28:27 +04:00
expr_code ;
i_comment "remove env" ;
2019-08-21 03:19:00 +04:00
dip i_drop ;
2019-08-21 12:28:27 +04:00
seq (List.map (Function.constant (dip i_drop)) lst) ;
2019-05-13 00:56:22 +04:00
] in
ok code
and translate_function anon env input_ty output_ty : michelson result =
let fvs = Mini_c.Free_variables.lambda [] anon in
let small_env = Mini_c.Environment.select fvs env in
let%bind lambda_ty = Compiler_type.lambda_closure (small_env , input_ty , output_ty) in
let%bind lambda_body_code = translate_function_body anon small_env input_ty in
match fvs with
| [] -> ok @@ seq [ i_push lambda_ty lambda_body_code ]
| _ :: _ ->
let selector = List.map fst small_env in
let%bind closure_pack_code = Compiler_environment.pack_closure env selector in
ok @@ seq [
closure_pack_code ;
i_push lambda_ty lambda_body_code ;
i_swap ;
i_apply ;
]
type compiled_expression = {
expr_ty : ex_ty ;
expr : michelson ;
}