ligo/src/compiler/compiler_program.ml

422 lines
14 KiB
OCaml
Raw Normal View History

2019-05-13 00:56:22 +04:00
open Trace
open Mini_c
open Michelson
module Stack = Meta_michelson.Stack
module Contract_types = Meta_michelson.Types
open Memory_proto_alpha.Script_ir_translator
open Operators.Compiler
let get_predicate : string -> type_value -> expression list -> predicate result = fun s ty lst ->
match Map.String.find_opt s Operators.Compiler.predicates with
| Some x -> ok x
| None -> (
match s with
2019-06-10 05:41:02 +04:00
| "NONE" -> (
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
)
2019-07-19 14:13:09 +04:00
| "NIL" -> (
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
)
2019-07-19 16:35:47 +04:00
| "SET_EMPTY" -> (
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
)
2019-06-10 05:41:02 +04:00
| "UNPACK" -> (
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
)
2019-05-13 00:56:22 +04:00
| "MAP_REMOVE" ->
let%bind v = match lst with
| [ _ ; expr ] ->
let%bind (_, v) = Mini_c.Combinators.(get_t_map (Expression.get_type expr)) in
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 ]
| "LEFT" ->
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
| "RIGHT" ->
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
| "CONTRACT" ->
let%bind r = match lst with
| [ _ ] -> get_t_contract ty
| _ -> simple_fail "mini_c . CONTRACT" in
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") ;
]
| x -> simple_fail ("predicate \"" ^ x ^ "\" doesn't exist")
)
2019-08-21 00:51:16 +04:00
let rec 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-05-13 00:56:22 +04:00
| D_tez n -> ok @@ int (Z.of_int n)
| D_string s -> ok @@ string s
| D_bytes s -> ok @@ bytes (Tezos_stdlib.MBytes.of_bytes s)
| 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-08-21 03:19:00 +04:00
| D_function func -> (
2019-08-21 00:51:16 +04:00
match ty with
2019-08-21 03:19:00 +04:00
| T_function (in_ty , _) -> translate_quote_body func in_ty
2019-08-21 00:51:16 +04:00
| T_deep_closure _ -> simple_fail "no support for closures yet"
| _ -> simple_fail "expected function type"
)
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-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_ 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
| E_application(f, arg) -> (
match Combinators.Expression.get_type f with
| T_function _ -> (
trace (simple_error "Compiling quote application") @@
2019-08-21 00:51:16 +04:00
let%bind f = translate_expression f env in
let%bind arg = translate_expression arg env in
2019-05-13 00:56:22 +04:00
return @@ seq [
i_comment "quote application" ;
i_comment "get f" ;
f ;
i_comment "get arg" ;
2019-08-21 03:19:00 +04:00
dip arg ;
i_swap ;
2019-05-13 00:56:22 +04:00
prim I_EXEC ;
]
)
2019-08-21 03:19:00 +04:00
(* TODO *)
(* | T_deep_closure (small_env, input_ty , _) -> () *)
2019-05-13 00:56:22 +04:00
| _ -> simple_fail "E_applicationing something not appliable"
)
| 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-05-13 00:56:22 +04:00
| E_constant(str, lst) ->
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-05-13 00:56:22 +04:00
let%bind predicate = get_predicate str ty lst in
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 ;
]
| _ -> simple_fail "bad arity"
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-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-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
)
| 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-07-20 15:46:42 +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
| "ITER" -> (
let%bind code = ok (seq [
expr' ;
2019-08-21 03:19:00 +04:00
i_iter (seq [body' ; dip i_drop]) ;
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
)
| "MAP" -> (
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
)
| s -> (
let error = error (thunk "bad iterator") (thunk s) in
fail error
)
)
2019-05-16 02:00:18 +04:00
| E_assignment (name , lrs , expr) -> (
2019-08-21 00:51:16 +04:00
let%bind expr' = translate_expression expr env in
let%bind get_code = Compiler_environment.get env name in
2019-05-16 02:00:18 +04:00
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]
in
let init = dip i_drop in
List.fold_right' aux init lrs
in
let%bind set_code = Compiler_environment.set env name in
let error =
let title () = "michelson type-checking patch" in
let content () =
let aux ppf = function
| `Left -> Format.fprintf ppf "left"
| `Right -> Format.fprintf ppf "right" in
Format.asprintf "Sub path: %a\n"
PP_helpers.(list_sep aux (const " , ")) lrs
in
error title content in
trace error @@
2019-08-21 00:51:16 +04:00
return @@ seq [
2019-05-20 20:17:26 +04:00
i_comment "assign: start # env" ;
2019-05-16 02:00:18 +04:00
expr' ;
2019-05-20 20:17:26 +04:00
i_comment "assign: compute rhs # rhs : env" ;
2019-08-21 03:19:00 +04:00
dip get_code ;
i_comment "assign: get name # rhs : name : env" ;
2019-05-20 20:17:26 +04:00
modify_code ;
i_comment "assign: modify code # name+rhs : env" ;
2019-05-16 02:00:18 +04:00
set_code ;
2019-05-20 20:17:26 +04:00
i_comment "assign: set new # new_env" ;
2019-08-21 03:19:00 +04:00
i_push_unit ;
2019-05-16 02:00:18 +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-08-21 00:51:16 +04:00
and translate_quote_body ({result ; binder} : anon_function) input : michelson result =
2019-05-20 20:17:26 +04:00
let env = Environment.(add (binder , input) empty) in
2019-08-21 00:51:16 +04:00
let%bind expr = translate_expression result env in
2019-05-13 00:56:22 +04:00
let code = seq [
i_comment "function result" ;
expr ;
2019-08-21 03:19:00 +04:00
dip i_drop ;
2019-05-13 00:56:22 +04:00
] in
ok code
type compiled_program = {
input : ex_ty ;
output : ex_ty ;
body : michelson ;
}
2019-08-21 00:51:16 +04:00
let get_main : program -> string -> (anon_function * _) result = fun p entry ->
2019-05-13 00:56:22 +04:00
let is_main (((name , expr), _):toplevel_statement) =
match Combinators.Expression.(get_content expr , get_type expr)with
2019-08-21 00:51:16 +04:00
| (E_literal (D_function content) , T_function ty)
2019-05-13 00:56:22 +04:00
when name = entry ->
2019-08-21 00:51:16 +04:00
Some (content , ty)
2019-05-13 00:56:22 +04:00
| _ -> None
in
let%bind main =
trace_option (simple_error "no functional entry") @@
List.find_map is_main p
in
ok main
let translate_program (p:program) (entry:string) : compiled_program result =
2019-08-21 00:51:16 +04:00
let%bind (main , (input , output)) = get_main p entry in
let%bind body = translate_quote_body main input in
2019-05-13 00:56:22 +04:00
let%bind input = Compiler_type.Ty.type_ input in
let%bind output = Compiler_type.Ty.type_ output in
ok ({input;output;body}:compiled_program)
2019-08-21 00:51:16 +04:00
let translate_entry (p:anon_function) ty : compiled_program result =
let (input , output) = ty in
2019-05-13 00:56:22 +04:00
let%bind body =
trace (simple_error "compile entry body") @@
2019-08-21 00:51:16 +04:00
translate_quote_body p input in
2019-05-13 00:56:22 +04:00
let%bind input = Compiler_type.Ty.type_ input in
let%bind output = Compiler_type.Ty.type_ output in
ok ({input;output;body}:compiled_program)
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
end
open Errors
2019-08-21 00:51:16 +04:00
let translate_contract : anon_function -> _ -> michelson result = fun f ty ->
let%bind compiled_program =
trace_strong (corner_case ~loc:__LOC__ "compiling") @@
2019-08-21 00:51:16 +04:00
translate_entry f ty in
let%bind (param_ty , storage_ty) = Combinators.get_t_pair (fst ty) in
2019-05-13 00:56:22 +04:00
let%bind param_michelson = Compiler_type.type_ param_ty in
let%bind storage_michelson = Compiler_type.type_ storage_ty in
let contract = Michelson.contract param_michelson storage_michelson compiled_program.body in
ok contract