345 lines
13 KiB
OCaml
345 lines
13 KiB
OCaml
|
open Trace
|
||
|
open Ligo_interpreter.Types
|
||
|
include Stage_common.Types
|
||
|
|
||
|
module Env = Ligo_interpreter.Environment
|
||
|
|
||
|
let apply_comparison : Ast_typed.constant -> value list -> value result =
|
||
|
fun c operands -> match (c,operands) with
|
||
|
| ( comp , [ V_Ct (C_int a' ) ; V_Ct (C_int b' ) ] )
|
||
|
| ( comp , [ V_Ct (C_nat a' ) ; V_Ct (C_nat b' ) ] )
|
||
|
| ( comp , [ V_Ct (C_mutez a' ) ; V_Ct (C_mutez b' ) ] )
|
||
|
| ( comp , [ V_Ct (C_timestamp a') ; V_Ct (C_timestamp b') ] ) ->
|
||
|
let f_op = match comp with
|
||
|
| C_EQ -> Int.equal
|
||
|
| C_NEQ -> fun a b -> not (Int.equal a b)
|
||
|
| C_LT -> (<)
|
||
|
| C_LE -> (<=)
|
||
|
| C_GT -> (>)
|
||
|
| C_GE -> (>=)
|
||
|
| _ -> failwith "apply compare must be called with a comparative constant" in
|
||
|
ok @@ V_Ct (C_bool (f_op a' b'))
|
||
|
|
||
|
| ( comp , [ V_Ct (C_string a' ) ; V_Ct (C_string b' ) ] )
|
||
|
| ( comp , [ V_Ct (C_address a' ) ; V_Ct (C_address b' ) ] )
|
||
|
| ( comp , [ V_Ct (C_key_hash a') ; V_Ct (C_key_hash b') ] ) ->
|
||
|
let f_op = match comp with
|
||
|
| C_EQ -> fun a b -> (String.compare a b = 0)
|
||
|
| C_NEQ -> fun a b -> (String.compare a b != 0)
|
||
|
(* the above might not be alligned with Michelson interpreter. Do we care ? *)
|
||
|
| C_LT -> fun a b -> (String.compare a b < 0)
|
||
|
| C_LE -> fun a b -> (String.compare a b <= 0)
|
||
|
| C_GT -> fun a b -> (String.compare a b > 0)
|
||
|
| C_GE -> fun a b -> (String.compare a b >= 0)
|
||
|
| _ -> failwith "apply compare must be called with a comparative constant" in
|
||
|
ok @@ V_Ct (C_bool (f_op a' b'))
|
||
|
|
||
|
| ( comp , [ V_Ct (C_bytes a' ) ; V_Ct (C_bytes b' ) ] ) ->
|
||
|
let f_op = match comp with
|
||
|
| C_EQ -> fun a b -> (Bytes.compare a b = 0)
|
||
|
| C_NEQ -> fun a b -> (Bytes.compare a b != 0)
|
||
|
(* the above might not be alligned with Michelson interpreter. Do we care ? *)
|
||
|
| C_LT -> fun a b -> (Bytes.compare a b < 0)
|
||
|
| C_LE -> fun a b -> (Bytes.compare a b <= 0)
|
||
|
| C_GT -> fun a b -> (Bytes.compare a b > 0)
|
||
|
| C_GE -> fun a b -> (Bytes.compare a b >= 0)
|
||
|
| _ -> failwith "apply compare must be called with a comparative constant" in
|
||
|
ok @@ V_Ct (C_bool (f_op a' b'))
|
||
|
| _ -> simple_fail "unsupported comparison"
|
||
|
|
||
|
(* applying those operators does not involve extending the environment *)
|
||
|
let rec apply_operator : Ast_typed.constant -> value list -> value result =
|
||
|
fun c operands ->
|
||
|
let return_ct v = ok @@ V_Ct v in
|
||
|
let return_none () = ok @@ V_Construct ("None" , V_Ct C_unit) in
|
||
|
let return_some v = ok @@ V_Construct ("Some" , v) in
|
||
|
( match (c,operands) with
|
||
|
(* nullary *)
|
||
|
| ( C_NONE , [] ) -> return_none ()
|
||
|
| ( C_UNIT , [] ) -> ok @@ V_Ct C_unit
|
||
|
| ( C_NIL , [] ) -> ok @@ V_List []
|
||
|
(* unary *)
|
||
|
| ( C_FAILWITH , [ V_Ct (C_string a') ] ) ->
|
||
|
(*TODO This raise is here until we properly implement effects*)
|
||
|
raise (Temprorary_hack a')
|
||
|
(*TODO This raise is here until we properly implement effects*)
|
||
|
| ( C_NOT , [ V_Ct (C_bool a' ) ] ) -> return_ct @@ C_bool (not a')
|
||
|
| ( C_INT , [ V_Ct (C_nat a') ] ) -> return_ct @@ C_int a'
|
||
|
| ( C_ABS , [ V_Ct (C_int a') ] ) -> return_ct @@ C_int (abs a')
|
||
|
| ( C_NEG , [ V_Ct (C_int a') ] ) -> return_ct @@ C_int (-a')
|
||
|
| ( C_SOME , [ v ] ) -> return_some v
|
||
|
| ( C_IS_NAT , [ V_Ct (C_int a') ] ) ->
|
||
|
if a' > 0 then return_some @@ V_Ct (C_nat a')
|
||
|
else return_none ()
|
||
|
(* binary *)
|
||
|
| ( (C_EQ | C_NEQ | C_LT | C_LE | C_GT | C_GE) , _ ) -> apply_comparison c operands
|
||
|
| ( C_SUB , [ V_Ct (C_int a' | C_nat a') ; V_Ct (C_int b' | C_nat b') ] ) -> return_ct @@ C_int (a' - b')
|
||
|
| ( C_CONS , [ v ; V_List vl ] ) -> ok @@ V_List (v::vl)
|
||
|
| ( C_ADD , [ V_Ct (C_int a' ) ; V_Ct (C_int b' ) ] ) -> return_ct @@ C_int (a' + b')
|
||
|
| ( C_ADD , [ V_Ct (C_nat a' ) ; V_Ct (C_nat b' ) ] ) -> return_ct @@ C_nat (a' + b')
|
||
|
| ( C_MUL , [ V_Ct (C_int a' ) ; V_Ct (C_int b' ) ] ) -> return_ct @@ C_int (a' * b')
|
||
|
| ( C_MUL , [ V_Ct (C_nat a' ) ; V_Ct (C_nat b' ) ] ) -> return_ct @@ C_nat (a' * b')
|
||
|
| ( C_MUL , [ V_Ct (C_nat a' ) ; V_Ct (C_mutez b') ] ) -> return_ct @@ C_mutez (a' * b')
|
||
|
| ( C_MUL , [ V_Ct (C_mutez a') ; V_Ct (C_mutez b') ] ) -> return_ct @@ C_mutez (a' * b')
|
||
|
| ( C_DIV , [ V_Ct (C_int a' ) ; V_Ct (C_int b' ) ] ) -> return_ct @@ C_int (a' / b')
|
||
|
| ( C_DIV , [ V_Ct (C_nat a' ) ; V_Ct (C_nat b' ) ] ) -> return_ct @@ C_nat (a' / b')
|
||
|
| ( C_DIV , [ V_Ct (C_mutez a') ; V_Ct (C_nat b' ) ] ) -> return_ct @@ C_mutez (a' / b')
|
||
|
| ( C_DIV , [ V_Ct (C_mutez a') ; V_Ct (C_mutez b') ] ) -> return_ct @@ C_nat (a' / b')
|
||
|
| ( C_CONCAT , [ V_Ct (C_string a') ; V_Ct (C_string b') ] ) -> return_ct @@ C_string (a' ^ b')
|
||
|
| ( C_CONCAT , [ V_Ct (C_bytes a' ) ; V_Ct (C_bytes b' ) ] ) -> return_ct @@ C_bytes (Bytes.cat a' b')
|
||
|
| ( C_OR , [ V_Ct (C_bool a' ) ; V_Ct (C_bool b' ) ] ) -> return_ct @@ C_bool (a' || b')
|
||
|
| ( C_AND , [ V_Ct (C_bool a' ) ; V_Ct (C_bool b' ) ] ) -> return_ct @@ C_bool (a' && b')
|
||
|
| ( C_XOR , [ V_Ct (C_bool a' ) ; V_Ct (C_bool b' ) ] ) -> return_ct @@ C_bool ( (a' || b') && (not (a' && b')) )
|
||
|
| ( C_LIST_MAP , [ V_Func_val (arg_name, body, env) ; V_List (elts) ] ) ->
|
||
|
let%bind elts' = bind_map_list
|
||
|
(fun elt ->
|
||
|
let env' = Env.extend env (arg_name,elt) in
|
||
|
eval body env')
|
||
|
elts in
|
||
|
ok @@ V_List elts'
|
||
|
| ( C_LIST_ITER , [ V_Func_val (arg_name, body, env) ; V_List (elts) ] ) ->
|
||
|
bind_fold_list
|
||
|
(fun _ elt ->
|
||
|
let env' = Env.extend env (arg_name,elt) in
|
||
|
eval body env'
|
||
|
)
|
||
|
(V_Ct C_unit) elts
|
||
|
(* tertiary *)
|
||
|
| ( C_LIST_FOLD , [ V_Func_val (arg_name, body, env) ; V_List (elts) ; init ] ) ->
|
||
|
bind_fold_list
|
||
|
(fun prev elt ->
|
||
|
let fold_args = V_Record (LMap.of_list [(Label "0",prev) ; (Label "1",elt)]) in
|
||
|
let env' = Env.extend env (arg_name, fold_args) in
|
||
|
eval body env'
|
||
|
)
|
||
|
init elts
|
||
|
| _ ->
|
||
|
let () = Format.printf "%a\n" Stage_common.PP.constant c in
|
||
|
let () = List.iter ( fun e -> Format.printf "%s\n" (Ligo_interpreter.PP.pp_value e)) operands in
|
||
|
simple_fail "Unsupported constant op"
|
||
|
)
|
||
|
|
||
|
(*
|
||
|
| C_NOW
|
||
|
| C_ASSERTION
|
||
|
| C_ASSERT_INFERRED
|
||
|
| C_UPDATE
|
||
|
| C_ITER
|
||
|
| C_FOLD_WHILE
|
||
|
| C_CONTINUE
|
||
|
| C_STOP
|
||
|
| C_FOLD
|
||
|
| C_SUB
|
||
|
| C_MOD
|
||
|
| C_SIZE
|
||
|
| C_SLICE
|
||
|
| C_BYTES_PACK
|
||
|
| C_BYTES_UNPACK
|
||
|
| C_PAIR
|
||
|
X| C_CAR
|
||
|
X| C_CDR
|
||
|
X| C_LEFT
|
||
|
X| C_RIGHT
|
||
|
| C_SET_EMPTY
|
||
|
| C_SET_LITERAL
|
||
|
| C_SET_ADD
|
||
|
| C_SET_REMOVE
|
||
|
| C_SET_ITER
|
||
|
| C_SET_FOLD
|
||
|
| C_SET_MEM
|
||
|
| C_MAP
|
||
|
| C_MAP_EMPTY
|
||
|
| C_MAP_LITERAL
|
||
|
| C_MAP_GET
|
||
|
| C_MAP_GET_FORCE
|
||
|
| C_MAP_ADD
|
||
|
| C_MAP_REMOVE
|
||
|
| C_MAP_UPDATE
|
||
|
| C_MAP_ITER
|
||
|
| C_MAP_MAP
|
||
|
| C_MAP_FOLD
|
||
|
| C_MAP_MEM
|
||
|
| C_MAP_FIND
|
||
|
| C_MAP_FIND_OPT
|
||
|
| C_BIG_MAP
|
||
|
| C_BIG_MAP_EMPTY
|
||
|
| C_BIG_MAP_LITERAL
|
||
|
x| C_LIST_CONS -> To remove ? seems unused
|
||
|
| C_SHA256
|
||
|
| C_SHA512
|
||
|
| C_BLAKE2b
|
||
|
| C_HASH
|
||
|
| C_HASH_KEY
|
||
|
| C_CHECK_SIGNATURE
|
||
|
| C_CHAIN_ID
|
||
|
| C_CALL
|
||
|
| C_CONTRACT
|
||
|
| C_CONTRACT_ENTRYPOINT
|
||
|
| C_AMOUNT
|
||
|
| C_BALANCE
|
||
|
| C_SOURCE
|
||
|
| C_SENDER
|
||
|
| C_ADDRESS
|
||
|
| C_SELF_ADDRESS
|
||
|
| C_IMPLICIT_ACCOUNT
|
||
|
| C_SET_DELEGATE
|
||
|
| C_STEPS_TO_QUOTA
|
||
|
*)
|
||
|
|
||
|
(*interpreter*)
|
||
|
and eval_literal : Ast_typed.literal -> value result = function
|
||
|
| Literal_unit -> ok @@ V_Ct (C_unit)
|
||
|
| Literal_int i -> ok @@ V_Ct (C_int i)
|
||
|
| Literal_nat n -> ok @@ V_Ct (C_nat n)
|
||
|
| Literal_string s -> ok @@ V_Ct (C_string s)
|
||
|
| Literal_bytes s -> ok @@ V_Ct (C_bytes s)
|
||
|
| Literal_bool b -> ok @@ V_Ct (C_bool b)
|
||
|
| Literal_mutez t -> ok @@ V_Ct (C_mutez t)
|
||
|
| _ -> simple_fail "Unsupported literal"
|
||
|
|
||
|
and eval : Ast_typed.expression -> env -> value result
|
||
|
= fun term env ->
|
||
|
match term with
|
||
|
| E_application ({expression = f; _}, args) -> (
|
||
|
let%bind f' = match f with
|
||
|
| E_variable f -> Env.lookup env f
|
||
|
| _ -> eval f env in
|
||
|
match f' with
|
||
|
| V_Func_val (arg_names, body, f_env) ->
|
||
|
let%bind args' = eval args.expression env in
|
||
|
let f_env' = Env.extend f_env (arg_names, args') in
|
||
|
eval body f_env'
|
||
|
| _ -> simple_fail "trying to apply on something that is not a function"
|
||
|
)
|
||
|
| E_lambda { binder; body;} ->
|
||
|
ok @@ V_Func_val (binder,body.expression,env)
|
||
|
| E_let_in { binder; rhs; result; _} ->
|
||
|
let%bind rhs' = eval rhs.expression env in
|
||
|
eval result.expression (Env.extend env (binder,rhs'))
|
||
|
| E_map kvlist | E_big_map kvlist ->
|
||
|
let%bind kvlist' = bind_map_list
|
||
|
(fun kv -> bind_map_pair (fun (el:Ast_typed.annotated_expression) -> eval el.expression env) kv)
|
||
|
kvlist in
|
||
|
ok @@ V_Map kvlist'
|
||
|
| E_list expl ->
|
||
|
let%bind expl' = bind_map_list
|
||
|
(fun (exp:Ast_typed.annotated_expression) -> eval exp.expression env)
|
||
|
expl in
|
||
|
ok @@ V_List expl'
|
||
|
| E_literal l ->
|
||
|
eval_literal l
|
||
|
| E_variable var ->
|
||
|
Env.lookup env var
|
||
|
| E_record recmap ->
|
||
|
let%bind lv' = bind_map_list
|
||
|
(fun (label,(v:Ast_typed.annotated_expression)) ->
|
||
|
let%bind v' = eval v.expression env in
|
||
|
ok (label,v'))
|
||
|
(LMap.to_kv_list recmap) in
|
||
|
ok @@ V_Record (LMap.of_list lv')
|
||
|
| E_record_accessor (record,label) -> (
|
||
|
let%bind record' = eval record.expression env in
|
||
|
match record' with
|
||
|
| V_Record recmap ->
|
||
|
let%bind a = trace_option (simple_error "unknown record field") @@
|
||
|
LMap.find_opt label recmap in
|
||
|
ok a
|
||
|
| _ -> simple_fail "trying to access a non-record"
|
||
|
)
|
||
|
| E_record_update (record, (l,field)) -> (
|
||
|
let%bind record' = eval record.expression env in
|
||
|
match record' with
|
||
|
| V_Record recmap ->
|
||
|
if LMap.mem l recmap then
|
||
|
let%bind field' = eval field.expression env in
|
||
|
ok @@ V_Record (LMap.add l field' recmap)
|
||
|
else
|
||
|
simple_fail "field l does not exist in record"
|
||
|
| _ -> simple_fail "this expression isn't a record"
|
||
|
)
|
||
|
| E_constant (op, operands) -> (
|
||
|
let%bind operands' = bind_map_list
|
||
|
(fun (ae:Ast_typed.annotated_expression) -> eval ae.expression env)
|
||
|
operands in
|
||
|
apply_operator op operands'
|
||
|
)
|
||
|
| E_constructor (Constructor c, v) ->
|
||
|
let%bind v' = eval v.expression env in
|
||
|
ok @@ V_Construct (c,v')
|
||
|
| E_matching (e , cases) -> (
|
||
|
let%bind e' = eval e.expression env in
|
||
|
match cases, e' with
|
||
|
| Match_list cases , V_List [] ->
|
||
|
eval cases.match_nil.expression env
|
||
|
| Match_list cases , V_List (head::tail) ->
|
||
|
let (head_var,tail_var,body,_) = cases.match_cons in
|
||
|
let env' = Env.extend (Env.extend env (head_var,head)) (tail_var, V_List tail) in
|
||
|
eval body.expression env'
|
||
|
| Match_variant (case_list , _) , V_Construct (matched_c , proj) ->
|
||
|
let ((_, var) , body) =
|
||
|
List.find
|
||
|
(fun case ->
|
||
|
let (Constructor c , _) = fst case in
|
||
|
String.equal matched_c c)
|
||
|
case_list in
|
||
|
let env' = Env.extend env (var, proj) in
|
||
|
eval body.expression env'
|
||
|
| Match_bool cases , V_Ct (C_bool true) ->
|
||
|
eval cases.match_true.expression env
|
||
|
| Match_bool cases , V_Ct (C_bool false) ->
|
||
|
eval cases.match_false.expression env
|
||
|
| Match_option cases, V_Construct ("Some" , proj) ->
|
||
|
let (var,body,_) = cases.match_some in
|
||
|
let env' = Env.extend env (var,proj) in
|
||
|
eval body.expression env'
|
||
|
| Match_option cases, V_Construct ("None" , V_Ct C_unit) ->
|
||
|
eval cases.match_none.expression env
|
||
|
| _ -> simple_fail "not yet supported case"
|
||
|
(* ((ctor,name),body) *)
|
||
|
)
|
||
|
(**********************************************
|
||
|
This is not necessary after Ast simplification
|
||
|
***********************************************)
|
||
|
| E_tuple el ->
|
||
|
let%bind lv = bind_mapi_list
|
||
|
(fun i (el:Ast_typed.annotated_expression) ->
|
||
|
let%bind el' = eval el.expression env in
|
||
|
ok (Label (string_of_int i), el'))
|
||
|
el in
|
||
|
ok @@ V_Record (LMap.of_list lv)
|
||
|
| E_tuple_accessor (tuple,i) -> (
|
||
|
let%bind record' = eval tuple.expression env in
|
||
|
match record' with
|
||
|
| V_Record recmap ->
|
||
|
let label = Label (string_of_int i) in
|
||
|
let%bind a = trace_option (simple_error "out of tuple range") @@
|
||
|
LMap.find_opt label recmap in
|
||
|
ok a
|
||
|
| _ -> simple_fail "trying to access a non-record"
|
||
|
)
|
||
|
(**********************************************
|
||
|
This is not necessary after Ast simplification
|
||
|
***********************************************)
|
||
|
| E_look_up _ | E_loop _ | E_set _ | E_sequence _ | E_assign _->
|
||
|
let serr = Format.asprintf "Unsupported construct :\n %a\n" Ast_typed.PP.expression term in
|
||
|
simple_fail serr
|
||
|
|
||
|
let dummy : Ast_typed.program -> string result =
|
||
|
fun prg ->
|
||
|
let%bind (res,_) = bind_fold_list
|
||
|
(fun (pp,top_env) el ->
|
||
|
let (Ast_typed.Declaration_constant (named_exp, _, _)) = Location.unwrap el in
|
||
|
let%bind v =
|
||
|
(*TODO This TRY-CATCH is here until we properly implement effects*)
|
||
|
try
|
||
|
eval named_exp.annotated_expression.expression top_env
|
||
|
with Temprorary_hack s -> ok @@ V_Failure s
|
||
|
(*TODO This TRY-CATCH is here until we properly implement effects*)
|
||
|
in
|
||
|
let pp' = pp^"\n val "^(Var.to_name named_exp.name)^" = "^(Ligo_interpreter.PP.pp_value v) in
|
||
|
let top_env' = Env.extend top_env (named_exp.name, v) in
|
||
|
ok @@ (pp',top_env')
|
||
|
)
|
||
|
("",Env.empty_env) prg in
|
||
|
ok @@ res
|