Contract factories and bytecode introspection #796

Merged
zxq9 merged 34 commits from factories into master 2021-05-18 19:21:57 +09:00
50 changed files with 247 additions and 157 deletions
Showing only changes of commit 0bea3030bc - Show all commits

View File

@ -21,6 +21,8 @@
, json_encode_expr/1 , json_encode_expr/1
, json_encode_type/1]). , json_encode_type/1]).
-include("aeso_utils.hrl").
-type aci_type() :: json | string. -type aci_type() :: json | string.
-type json() :: jsx:json_term(). -type json() :: jsx:json_term().
-type json_text() :: binary(). -type json_text() :: binary().
@ -68,9 +70,7 @@ do_contract_interface(Type, Contract, Options) when is_binary(Contract) ->
do_contract_interface(Type, ContractString, Options) -> do_contract_interface(Type, ContractString, Options) ->
try try
Ast = aeso_compiler:parse(ContractString, Options), Ast = aeso_compiler:parse(ContractString, Options),
%% io:format("~p\n", [Ast]),
{TypedAst, _} = aeso_ast_infer_types:infer(Ast, [dont_unfold | Options]), {TypedAst, _} = aeso_ast_infer_types:infer(Ast, [dont_unfold | Options]),
%% io:format("~p\n", [TypedAst]),
from_typed_ast(Type, TypedAst) from_typed_ast(Type, TypedAst)
catch catch
throw:{error, Errors} -> {error, Errors} throw:{error, Errors} -> {error, Errors}
@ -83,7 +83,7 @@ from_typed_ast(Type, TypedAst) ->
string -> do_render_aci_json(JArray) string -> do_render_aci_json(JArray)
end. end.
encode_contract(Contract = {contract, _, {con, _, Name}, _}) -> encode_contract(Contract = {Head, _, {con, _, Name}, _}) when ?IS_CONTRACT_HEAD(Head) ->
C0 = #{name => encode_name(Name)}, C0 = #{name => encode_name(Name)},
Tdefs0 = [ encode_typedef(T) || T <- sort_decls(contract_types(Contract)) ], Tdefs0 = [ encode_typedef(T) || T <- sort_decls(contract_types(Contract)) ],
@ -107,7 +107,7 @@ encode_contract(Contract = {contract, _, {con, _, Name}, _}) ->
|| F <- sort_decls(contract_funcs(Contract)), || F <- sort_decls(contract_funcs(Contract)),
is_entrypoint(F) ], is_entrypoint(F) ],
#{contract => C3#{functions => Fdefs, payable => is_payable(Contract)}}; #{contract => C3#{kind => Head, functions => Fdefs, payable => is_payable(Contract)}};
encode_contract(Namespace = {namespace, _, {con, _, Name}, _}) -> encode_contract(Namespace = {namespace, _, {con, _, Name}, _}) ->
Tdefs = [ encode_typedef(T) || T <- sort_decls(contract_types(Namespace)) ], Tdefs = [ encode_typedef(T) || T <- sort_decls(contract_types(Namespace)) ],
#{namespace => #{name => encode_name(Name), #{namespace => #{name => encode_name(Name),
@ -232,13 +232,19 @@ do_render_aci_json(Json) ->
{ok, list_to_binary(string:join(DecodedContracts, "\n"))}. {ok, list_to_binary(string:join(DecodedContracts, "\n"))}.
decode_contract(#{contract := #{name := Name, decode_contract(#{contract := #{name := Name,
kind := Kind,
payable := Payable, payable := Payable,
type_defs := Ts0, type_defs := Ts0,
functions := Fs} = C}) -> functions := Fs} = C}) ->
MkTDef = fun(N, T) -> #{name => N, vars => [], typedef => T} end, MkTDef = fun(N, T) -> #{name => N, vars => [], typedef => T} end,
Ts = [ MkTDef(<<"state">>, maps:get(state, C)) || maps:is_key(state, C) ] ++ Ts = [ MkTDef(<<"state">>, maps:get(state, C)) || maps:is_key(state, C) ] ++
[ MkTDef(<<"event">>, maps:get(event, C)) || maps:is_key(event, C) ] ++ Ts0, [ MkTDef(<<"event">>, maps:get(event, C)) || maps:is_key(event, C) ] ++ Ts0,
[payable(Payable), "contract ", io_lib:format("~s", [Name])," =\n", [payable(Payable), case Kind of
contract_main -> "main contract ";
contract_child -> "contract ";
contract_interface -> "contract interface "
end,
io_lib:format("~s", [Name])," =\n",
decode_tdefs(Ts), decode_funcs(Fs)]; decode_tdefs(Ts), decode_funcs(Fs)];
decode_contract(#{namespace := #{name := Name, type_defs := Ts}}) when Ts /= [] -> decode_contract(#{namespace := #{name := Name, type_defs := Ts}}) when Ts /= [] ->
["namespace ", io_lib:format("~s", [Name])," =\n", ["namespace ", io_lib:format("~s", [Name])," =\n",
@ -332,10 +338,10 @@ payable(false) -> "".
%% #contract{Ann, Con, [Declarations]}. %% #contract{Ann, Con, [Declarations]}.
contract_funcs({C, _, _, Decls}) when C == contract; C == namespace -> contract_funcs({C, _, _, Decls}) when ?IS_CONTRACT_HEAD(C); C == namespace ->
[ D || D <- Decls, is_fun(D)]. [ D || D <- Decls, is_fun(D)].
contract_types({C, _, _, Decls}) when C == contract; C == namespace -> contract_types({C, _, _, Decls}) when ?IS_CONTRACT_HEAD(C); C == namespace ->
[ D || D <- Decls, is_type(D) ]. [ D || D <- Decls, is_type(D) ].
is_fun({letfun, _, _, _, _, _}) -> true; is_fun({letfun, _, _, _, _, _}) -> true;

View File

@ -18,6 +18,8 @@
, pp_type/2 , pp_type/2
]). ]).
-include("aeso_utils.hrl").
-type utype() :: {fun_t, aeso_syntax:ann(), named_args_t(), [utype()], utype()} -type utype() :: {fun_t, aeso_syntax:ann(), named_args_t(), [utype()], utype()}
| {app_t, aeso_syntax:ann(), utype(), [utype()]} | {app_t, aeso_syntax:ann(), utype(), [utype()]}
| {tuple_t, aeso_syntax:ann(), [utype()]} | {tuple_t, aeso_syntax:ann(), [utype()]}
@ -123,7 +125,7 @@
, in_pattern = false :: boolean() , in_pattern = false :: boolean()
, stateful = false :: boolean() , stateful = false :: boolean()
, current_function = none :: none | aeso_syntax:id() , current_function = none :: none | aeso_syntax:id()
, what = top :: top | namespace | contract | main_contract , what = top :: top | namespace | contract | contract_interface
}). }).
-type env() :: #env{}. -type env() :: #env{}.
@ -191,9 +193,9 @@ bind_fun(X, Type, Env) ->
force_bind_fun(X, Type, Env = #env{ what = What }) -> force_bind_fun(X, Type, Env = #env{ what = What }) ->
Ann = aeso_syntax:get_ann(Type), Ann = aeso_syntax:get_ann(Type),
NoCode = get_option(no_code, false), NoCode = get_option(no_code, false),
Entry = if X == "init", What == main_contract, not NoCode -> Entry = if X == "init", What == contract, not NoCode ->
{reserved_init, Ann, Type}; {reserved_init, Ann, Type};
What == contract -> {contract_fun, Ann, Type}; What == contract_interface -> {contract_fun, Ann, Type};
true -> {Ann, Type} true -> {Ann, Type}
end, end,
on_current_scope(Env, fun(Scope = #scope{ funs = Funs }) -> on_current_scope(Env, fun(Scope = #scope{ funs = Funs }) ->
@ -261,11 +263,19 @@ contract_call_type({fun_t, Ann, [], Args, Ret}) ->
Args, {if_t, Ann, Id("protected"), {app_t, Ann, {id, Ann, "option"}, [Ret]}, Ret}}. Args, {if_t, Ann, Id("protected"), {app_t, Ann, {id, Ann, "option"}, [Ret]}, Ret}}.
-spec bind_contract(aeso_syntax:decl(), env()) -> env(). -spec bind_contract(aeso_syntax:decl(), env()) -> env().
bind_contract({contract, Ann, Id, Contents}, Env) -> bind_contract({Contract, Ann, Id, Contents}, Env)
when ?IS_CONTRACT_HEAD(Contract) ->
Key = name(Id), Key = name(Id),
Sys = [{origin, system}], Sys = [{origin, system}],
Fields = [ {field_t, AnnF, Entrypoint, contract_call_type(Type)} Fields =
[ {field_t, AnnF, Entrypoint, contract_call_type(Type)}
|| {fun_decl, AnnF, Entrypoint, Type} <- Contents ] ++ || {fun_decl, AnnF, Entrypoint, Type} <- Contents ] ++
[ {field_t, AnnF, Entrypoint,
contract_call_type(
{fun_t, AnnF, [], [ArgT || ArgT <- if is_list(Args) -> Args; true -> [Args] end], RetT})
}
|| {letfun, AnnF, Entrypoint, _Named, Args, {typed, _, _, RetT}} <- Contents
] ++
%% Predefined fields %% Predefined fields
[ {field_t, Sys, {id, Sys, "address"}, {id, Sys, "address"}} ], [ {field_t, Sys, {id, Sys, "address"}, {id, Sys, "address"}} ],
FieldInfo = [ {Entrypoint, #field_info{ ann = FieldAnn, FieldInfo = [ {Entrypoint, #field_info{ ann = FieldAnn,
@ -463,6 +473,7 @@ global_env() ->
{"block_height", Int}, {"block_height", Int},
{"difficulty", Int}, {"difficulty", Int},
{"gas_limit", Int}, {"gas_limit", Int},
{"bytecode_hash", Fun1(Address, Option(Hash))},
%% Tx constructors %% Tx constructors
{"GAMetaTx", Fun([Address, Int], GAMetaTx)}, {"GAMetaTx", Fun([Address, Int], GAMetaTx)},
{"PayingForTx", Fun([Address, Int], PayForTx)}, {"PayingForTx", Fun([Address, Int], PayForTx)},
@ -701,7 +712,10 @@ infer(Contracts, Options) ->
create_options(Options), create_options(Options),
ets_new(type_vars, [set]), ets_new(type_vars, [set]),
check_modifiers(Env, Contracts), check_modifiers(Env, Contracts),
{Env1, Decls} = infer1(Env, Contracts, [], Options), create_type_errors(),
Contracts1 = identify_main_contract(Contracts),
destroy_and_report_type_errors(Env),
{Env1, Decls} = infer1(Env, Contracts1, [], Options),
{Env2, DeclsFolded, DeclsUnfolded} = {Env2, DeclsFolded, DeclsUnfolded} =
case proplists:get_value(dont_unfold, Options, false) of case proplists:get_value(dont_unfold, Options, false) of
true -> {Env1, Decls, Decls}; true -> {Env1, Decls, Decls};
@ -719,12 +733,16 @@ infer(Contracts, Options) ->
-spec infer1(env(), [aeso_syntax:decl()], [aeso_syntax:decl()], list(option())) -> -spec infer1(env(), [aeso_syntax:decl()], [aeso_syntax:decl()], list(option())) ->
{env(), [aeso_syntax:decl()]}. {env(), [aeso_syntax:decl()]}.
infer1(Env, [], Acc, _Options) -> {Env, lists:reverse(Acc)}; infer1(Env, [], Acc, _Options) -> {Env, lists:reverse(Acc)};
infer1(Env, [{contract, Ann, ConName, Code} | Rest], Acc, Options) -> infer1(Env, [{Contract, Ann, ConName, Code} | Rest], Acc, Options)
when ?IS_CONTRACT_HEAD(Contract) ->
%% do type inference on each contract independently. %% do type inference on each contract independently.
check_scope_name_clash(Env, contract, ConName), check_scope_name_clash(Env, contract, ConName),
What = if Rest == [] -> main_contract; true -> contract end, What = case aeso_syntax:get_ann(interface, Ann, false) of
true -> contract_interface;
false -> contract
end,
{Env1, Code1} = infer_contract_top(push_scope(contract, ConName, Env), What, Code, Options), {Env1, Code1} = infer_contract_top(push_scope(contract, ConName, Env), What, Code, Options),
Contract1 = {contract, Ann, ConName, Code1}, Contract1 = {Contract, Ann, ConName, Code1},
Env2 = pop_scope(Env1), Env2 = pop_scope(Env1),
Env3 = bind_contract(Contract1, Env2), Env3 = bind_contract(Contract1, Env2),
infer1(Env3, Rest, [Contract1 | Acc], Options); infer1(Env3, Rest, [Contract1 | Acc], Options);
@ -737,6 +755,26 @@ infer1(Env, [{pragma, _, _} | Rest], Acc, Options) ->
%% Pragmas are checked in check_modifiers %% Pragmas are checked in check_modifiers
infer1(Env, Rest, Acc, Options). infer1(Env, Rest, Acc, Options).
%% Checks if the main contract is somehow defined.
%% Performs some basic sorting to make the dependencies more happy.
identify_main_contract(Contracts) ->
Childs = [C || C = {contract_child, _, _, _} <- Contracts],
Mains = [C || C = {contract_main, _, _, _} <- Contracts],
Interfaces = [C || C = {contract_interface, _, _, _} <- Contracts],
Namespaces = [N || N = {namespace, _, _, _} <- Contracts],
case Mains of
[] -> case Childs of
[] -> type_error({main_contract_undefined});
[{contract_child, Ann, Con, Body}] ->
Interfaces ++ Namespaces ++
[C || C = {_, _, Con1, _} <- Childs, Con1 /= Con] ++
[{contract_main, Ann, Con, Body}];
_ -> type_error({ambiguous_main_contract})
end;
[_] -> Interfaces ++ Namespaces ++ Childs ++ Mains;
_ -> type_error({multiple_main_contracts})
end.
check_scope_name_clash(Env, Kind, Name) -> check_scope_name_clash(Env, Kind, Name) ->
case get_scope(Env, qname(Name)) of case get_scope(Env, qname(Name)) of
false -> ok; false -> ok;
@ -746,7 +784,7 @@ check_scope_name_clash(Env, Kind, Name) ->
destroy_and_report_type_errors(Env) destroy_and_report_type_errors(Env)
end. end.
-spec infer_contract_top(env(), main_contract | contract | namespace, [aeso_syntax:decl()], list(option())) -> -spec infer_contract_top(env(), contract_interface | contract | namespace, [aeso_syntax:decl()], list(option())) ->
{env(), [aeso_syntax:decl()]}. {env(), [aeso_syntax:decl()]}.
infer_contract_top(Env, Kind, Defs0, Options) -> infer_contract_top(Env, Kind, Defs0, Options) ->
create_type_errors(), create_type_errors(),
@ -756,7 +794,7 @@ infer_contract_top(Env, Kind, Defs0, Options) ->
%% infer_contract takes a proplist mapping global names to types, and %% infer_contract takes a proplist mapping global names to types, and
%% a list of definitions. %% a list of definitions.
-spec infer_contract(env(), main_contract | contract | namespace, [aeso_syntax:decl()], list(option())) -> {env(), [aeso_syntax:decl()]}. -spec infer_contract(env(), contract_interface | contract | namespace, [aeso_syntax:decl()], list(option())) -> {env(), [aeso_syntax:decl()]}.
infer_contract(Env0, What, Defs0, Options) -> infer_contract(Env0, What, Defs0, Options) ->
create_type_errors(), create_type_errors(),
Defs01 = process_blocks(Defs0), Defs01 = process_blocks(Defs0),
@ -772,19 +810,19 @@ infer_contract(Env0, What, Defs0, Options) ->
({fun_decl, _, _, _}) -> prototype; ({fun_decl, _, _, _}) -> prototype;
(_) -> unexpected (_) -> unexpected
end, end,
Get = fun(K) -> [ Def || Def <- Defs, Kind(Def) == K ] end, Get = fun(K, In) -> [ Def || Def <- In, Kind(Def) == K ] end,
{Env1, TypeDefs} = check_typedefs(Env, Get(type)), {Env1, TypeDefs} = check_typedefs(Env, Get(type, Defs)),
create_type_errors(), create_type_errors(),
check_unexpected(Get(unexpected)), check_unexpected(Get(unexpected, Defs)),
Env2 = Env2 =
case What of case What of
namespace -> Env1; namespace -> Env1;
contract -> Env1; contract_interface -> Env1;
main_contract -> bind_state(Env1) %% bind state and put contract -> bind_state(Env1) %% bind state and put
end, end,
{ProtoSigs, Decls} = lists:unzip([ check_fundecl(Env1, Decl) || Decl <- Get(prototype) ]), {ProtoSigs, Decls} = lists:unzip([ check_fundecl(Env1, Decl) || Decl <- Get(prototype, Defs) ]),
Env3 = bind_funs(ProtoSigs, Env2), Env3 = bind_funs(ProtoSigs, Env2),
Functions = Get(function), Functions = Get(function, Defs),
%% Check for duplicates in Functions (we turn it into a map below) %% Check for duplicates in Functions (we turn it into a map below)
FunBind = fun({letfun, Ann, {id, _, Fun}, _, _, _}) -> {Fun, {tuple_t, Ann, []}}; FunBind = fun({letfun, Ann, {id, _, Fun}, _, _, _}) -> {Fun, {tuple_t, Ann, []}};
({fun_clauses, Ann, {id, _, Fun}, _, _}) -> {Fun, {tuple_t, Ann, []}} end, ({fun_clauses, Ann, {id, _, Fun}, _, _}) -> {Fun, {tuple_t, Ann, []}} end,
@ -794,11 +832,11 @@ infer_contract(Env0, What, Defs0, Options) ->
check_reserved_entrypoints(FunMap), check_reserved_entrypoints(FunMap),
DepGraph = maps:map(fun(_, Def) -> aeso_syntax_utils:used_ids(Def) end, FunMap), DepGraph = maps:map(fun(_, Def) -> aeso_syntax_utils:used_ids(Def) end, FunMap),
SCCs = aeso_utils:scc(DepGraph), SCCs = aeso_utils:scc(DepGraph),
%% io:format("Dependency sorted functions:\n ~p\n", [SCCs]),
{Env4, Defs1} = check_sccs(Env3, FunMap, SCCs, []), {Env4, Defs1} = check_sccs(Env3, FunMap, SCCs, []),
%% Check that `init` doesn't read or write the state %% Check that `init` doesn't read or write the state
check_state_dependencies(Env4, Defs1), check_state_dependencies(Env4, Defs1),
destroy_and_report_type_errors(Env4), destroy_and_report_type_errors(Env4),
%% Add inferred types of definitions
{Env4, TypeDefs ++ Decls ++ Defs1}. {Env4, TypeDefs ++ Decls ++ Defs1}.
%% Restructure blocks into multi-clause fundefs (`fun_clauses`). %% Restructure blocks into multi-clause fundefs (`fun_clauses`).
@ -831,8 +869,8 @@ expose_internals(Defs, What) ->
Ann = element(2, Def), Ann = element(2, Def),
NewAnn = case What of NewAnn = case What of
namespace -> [A ||A <- Ann, A /= {private, true}, A /= private]; namespace -> [A ||A <- Ann, A /= {private, true}, A /= private];
main_contract -> [{entrypoint, true}|Ann]; % minor duplication contract -> [{entrypoint, true}|Ann]; % minor duplication
contract -> Ann contract_interface -> Ann
end, end,
Def1 = setelement(2, Def, NewAnn), Def1 = setelement(2, Def, NewAnn),
case Def1 of % fix inner clauses case Def1 of % fix inner clauses
@ -907,15 +945,16 @@ check_modifiers(Env, Contracts) ->
check_modifiers_(Env, Contracts), check_modifiers_(Env, Contracts),
destroy_and_report_type_errors(Env). destroy_and_report_type_errors(Env).
check_modifiers_(Env, [{contract, _, Con, Decls} | Rest]) -> check_modifiers_(Env, [{Contract, _, Con, Decls} | Rest])
IsMain = Rest == [], when ?IS_CONTRACT_HEAD(Contract) ->
IsInterface = Contract =:= contract_interface,
check_modifiers1(contract, Decls), check_modifiers1(contract, Decls),
case {lists:keymember(letfun, 1, Decls), case {lists:keymember(letfun, 1, Decls),
[ D || D <- Decls, aeso_syntax:get_ann(entrypoint, D, false) ]} of [ D || D <- Decls, aeso_syntax:get_ann(entrypoint, D, false) ]} of
{true, []} -> type_error({contract_has_no_entrypoints, Con}); {true, []} -> type_error({contract_has_no_entrypoints, Con});
_ when not IsMain -> _ when IsInterface ->
case [ {Ann, Id} || {letfun, Ann, Id, _, _, _} <- Decls ] of case [ {AnnF, Id} || {letfun, AnnF, Id, _, _, _} <- Decls ] of
[{Ann, Id} | _] -> type_error({definition_in_non_main_contract, Ann, Id}); [{AnnF, Id} | _] -> type_error({definition_in_contract_interface, AnnF, Id});
[] -> ok [] -> ok
end; end;
_ -> ok _ -> ok
@ -2653,7 +2692,7 @@ mk_error({namespace, _Pos, {con, Pos, Name}, _Def}) ->
Msg = io_lib:format("Nested namespaces are not allowed\nNamespace '~s' at ~s not defined at top level.\n", Msg = io_lib:format("Nested namespaces are not allowed\nNamespace '~s' at ~s not defined at top level.\n",
[Name, pp_loc(Pos)]), [Name, pp_loc(Pos)]),
mk_t_err(pos(Pos), Msg); mk_t_err(pos(Pos), Msg);
mk_error({contract, _Pos, {con, Pos, Name}, _Def}) -> mk_error({Contract, _Pos, {con, Pos, Name}, _Def}) when ?IS_CONTRACT_HEAD(Contract) ->
Msg = io_lib:format("Nested contracts are not allowed\nContract '~s' at ~s not defined at top level.\n", Msg = io_lib:format("Nested contracts are not allowed\nContract '~s' at ~s not defined at top level.\n",
[Name, pp_loc(Pos)]), [Name, pp_loc(Pos)]),
mk_t_err(pos(Pos), Msg); mk_t_err(pos(Pos), Msg);
@ -2728,8 +2767,8 @@ mk_error({contract_has_no_entrypoints, Con}) ->
"contract functions must be declared with the 'entrypoint' keyword instead of\n" "contract functions must be declared with the 'entrypoint' keyword instead of\n"
"'function'.\n", [pp_expr("", Con), pp_loc(Con)]), "'function'.\n", [pp_expr("", Con), pp_loc(Con)]),
mk_t_err(pos(Con), Msg); mk_t_err(pos(Con), Msg);
mk_error({definition_in_non_main_contract, Ann, {id, _, Id}}) -> mk_error({definition_in_contract_interface, Ann, {id, _, Id}}) ->
Msg = "Only the main contract can contain defined functions or entrypoints.\n", Msg = "Contract interfaces cannot contain defined functions or entrypoints.\n",
Cxt = io_lib:format("Fix: replace the definition of '~s' by a type signature.\n", [Id]), Cxt = io_lib:format("Fix: replace the definition of '~s' by a type signature.\n", [Id]),
mk_t_err(pos(Ann), Msg, Cxt); mk_t_err(pos(Ann), Msg, Cxt);
mk_error({unbound_type, Type}) -> mk_error({unbound_type, Type}) ->
@ -2798,6 +2837,15 @@ mk_error({named_argument_must_be_literal_bool, Name, Arg}) ->
mk_error({conflicting_updates_for_field, Upd, Key}) -> mk_error({conflicting_updates_for_field, Upd, Key}) ->
Msg = io_lib:format("Conflicting updates for field '~s'\n", [Key]), Msg = io_lib:format("Conflicting updates for field '~s'\n", [Key]),
mk_t_err(pos(Upd), Msg); mk_t_err(pos(Upd), Msg);
mk_error({ambiguous_main_contract}) ->
Msg = "Could not deduce the main contract. You can point it manually with `main` keyword.",
mk_t_err(pos(0, 0), Msg);
mk_error({main_contract_undefined}) ->
Msg = "No contract defined.",
mk_t_err(pos(0, 0), Msg);
mk_error({multiple_main_contracts}) ->
Msg = "Up to one main contract can be defined.",
mk_t_err(pos(0, 0), Msg);
mk_error(Err) -> mk_error(Err) ->
Msg = io_lib:format("Unknown error: ~p\n", [Err]), Msg = io_lib:format("Unknown error: ~p\n", [Err]),
mk_t_err(pos(0, 0), Msg). mk_t_err(pos(0, 0), Msg).

View File

@ -12,6 +12,8 @@
-export([ast_to_fcode/2, format_fexpr/1]). -export([ast_to_fcode/2, format_fexpr/1]).
-export_type([fcode/0, fexpr/0, fun_def/0]). -export_type([fcode/0, fexpr/0, fun_def/0]).
-include("aeso_utils.hrl").
%% -- Type definitions ------------------------------------------------------- %% -- Type definitions -------------------------------------------------------
-type option() :: term(). -type option() :: term().
@ -136,6 +138,7 @@
-type type_env() :: #{ sophia_name() => type_def() }. -type type_env() :: #{ sophia_name() => type_def() }.
-type fun_env() :: #{ sophia_name() => {fun_name(), non_neg_integer()} }. -type fun_env() :: #{ sophia_name() => {fun_name(), non_neg_integer()} }.
-type con_env() :: #{ sophia_name() => con_tag() }. -type con_env() :: #{ sophia_name() => con_tag() }.
-type child_con_env() :: #{sophia_name() => fcode()}.
-type builtins() :: #{ sophia_name() => {builtin(), non_neg_integer() | none} }. -type builtins() :: #{ sophia_name() => {builtin(), non_neg_integer() | none} }.
-type context() :: {main_contract, string()} -type context() :: {main_contract, string()}
@ -147,6 +150,7 @@
-type env() :: #{ type_env := type_env(), -type env() :: #{ type_env := type_env(),
fun_env := fun_env(), fun_env := fun_env(),
con_env := con_env(), con_env := con_env(),
child_con_env := child_con_env(),
event_type => aeso_syntax:typedef(), event_type => aeso_syntax:typedef(),
builtins := builtins(), builtins := builtins(),
options := [option()], options := [option()],
@ -182,6 +186,7 @@ init_env(Options) ->
#{ type_env => init_type_env(), #{ type_env => init_type_env(),
fun_env => #{}, fun_env => #{},
builtins => builtins(), builtins => builtins(),
child_con_env => #{},
con_env => #{["None"] => #con_tag{ tag = 0, arities = [0, 1] }, con_env => #{["None"] => #con_tag{ tag = 0, arities = [0, 1] },
["Some"] => #con_tag{ tag = 1, arities = [0, 1] }, ["Some"] => #con_tag{ tag = 1, arities = [0, 1] },
["RelativeTTL"] => #con_tag{ tag = 0, arities = [1, 1] }, ["RelativeTTL"] => #con_tag{ tag = 0, arities = [1, 1] },
@ -308,30 +313,41 @@ get_option(Opt, Env, Default) ->
%% -- Compilation ------------------------------------------------------------ %% -- Compilation ------------------------------------------------------------
-spec to_fcode(env(), aeso_syntax:ast()) -> fcode(). -spec to_fcode(env(), aeso_syntax:ast()) -> fcode().
to_fcode(Env, [{contract, Attrs, MainCon = {con, _, Main}, Decls}]) -> to_fcode(Env, [{Contract, Attrs, Con = {con, _, Name}, Decls}|Rest])
when ?IS_CONTRACT_HEAD(Contract) ->
case Contract =:= contract_interface of
false ->
#{ builtins := Builtins } = Env, #{ builtins := Builtins } = Env,
MainEnv = Env#{ context => {main_contract, Main}, ConEnv = Env#{ context => {main_contract, Name},
builtins => Builtins#{[Main, "state"] => {get_state, none}, builtins => Builtins#{[Name, "state"] => {get_state, none},
[Main, "put"] => {set_state, 1}, [Name, "put"] => {set_state, 1},
[Main, "Chain", "event"] => {chain_event, 1}} }, [Name, "Chain", "event"] => {chain_event, 1}} },
#{ functions := Funs } = Env1 = #{ functions := Funs } = Env1 =
decls_to_fcode(MainEnv, Decls), decls_to_fcode(ConEnv, Decls),
StateType = lookup_type(Env1, [Main, "state"], [], {tuple, []}), StateType = lookup_type(Env1, [Name, "state"], [], {tuple, []}),
EventType = lookup_type(Env1, [Main, "event"], [], none), EventType = lookup_type(Env1, [Name, "event"], [], none),
StateLayout = state_layout(Env1), StateLayout = state_layout(Env1),
Payable = proplists:get_value(payable, Attrs, false), Payable = proplists:get_value(payable, Attrs, false),
#{ contract_name => Main, ConFcode = #{ contract_name => Name,
state_type => StateType, state_type => StateType,
state_layout => StateLayout, state_layout => StateLayout,
event_type => EventType, event_type => EventType,
payable => Payable, payable => Payable,
functions => add_init_function(Env1, MainCon, StateType, functions => add_init_function(Env1, Con, StateType,
add_event_function(Env1, EventType, Funs)) }; add_event_function(Env1, EventType, Funs)) },
to_fcode(_Env, [NotContract]) -> case Contract of
fcode_error({last_declaration_must_be_contract, NotContract}); contract_main -> Rest = [], ConFcode;
to_fcode(Env, [{contract, _, {con, _, Con}, Decls} | Code]) -> contract_child ->
Env2 = add_child_con(Env1, Name, ConFcode),
to_fcode(Env2, Rest)
end;
true ->
Env1 = decls_to_fcode(Env#{ context => {abstract_contract, Con} }, Decls), Env1 = decls_to_fcode(Env#{ context => {abstract_contract, Con} }, Decls),
to_fcode(Env1, Code); to_fcode(Env1, Rest)
end;
to_fcode(_Env, [NotMain = {NotMainHead, _ ,_ , _}]) when NotMainHead =/= main_contract ->
fcode_error({last_declaration_must_be_main_contract, NotMain});
to_fcode(Env, [{namespace, _, {con, _, Con}, Decls} | Code]) -> to_fcode(Env, [{namespace, _, {con, _, Con}, Decls} | Code]) ->
Env1 = decls_to_fcode(Env#{ context => {namespace, Con} }, Decls), Env1 = decls_to_fcode(Env#{ context => {namespace, Con} }, Decls),
to_fcode(Env1, Code). to_fcode(Env1, Code).
@ -341,9 +357,7 @@ decls_to_fcode(Env, Decls) ->
%% First compute mapping from Sophia names to fun_names and add it to the %% First compute mapping from Sophia names to fun_names and add it to the
%% environment. %% environment.
Env1 = add_fun_env(Env, Decls), Env1 = add_fun_env(Env, Decls),
lists:foldl(fun(D, E) -> lists:foldl(fun(D, E) -> decl_to_fcode(E, D)
R = decl_to_fcode(E, D),
R
end, Env1, Decls). end, Env1, Decls).
-spec decl_to_fcode(env(), aeso_syntax:decl()) -> env(). -spec decl_to_fcode(env(), aeso_syntax:decl()) -> env().
@ -1614,6 +1628,10 @@ bind_constructors(Env = #{ con_env := ConEnv }, NewCons) ->
%% -- Names -- %% -- Names --
-spec add_child_con(env(), sophia_name(), fcode()) -> env().
add_child_con(Env = #{child_con_env := CEnv}, Name, Fcode) ->
Env#{ child_con_env := CEnv#{Name => Fcode} }.
-spec add_fun_env(env(), [aeso_syntax:decl()]) -> env(). -spec add_fun_env(env(), [aeso_syntax:decl()]) -> env().
add_fun_env(Env = #{ context := {abstract_contract, _} }, _) -> Env; %% no functions from abstract contracts add_fun_env(Env = #{ context := {abstract_contract, _} }, _) -> Env; %% no functions from abstract contracts
add_fun_env(Env = #{ fun_env := FunEnv }, Decls) -> add_fun_env(Env = #{ fun_env := FunEnv }, Decls) ->

View File

@ -14,12 +14,13 @@
-include_lib("aebytecode/include/aeb_opcodes.hrl"). -include_lib("aebytecode/include/aeb_opcodes.hrl").
-include("aeso_icode.hrl"). -include("aeso_icode.hrl").
-include("aeso_utils.hrl").
-spec convert_typed(aeso_syntax:ast(), list()) -> aeso_icode:icode(). -spec convert_typed(aeso_syntax:ast(), list()) -> aeso_icode:icode().
convert_typed(TypedTree, Options) -> convert_typed(TypedTree, Options) ->
{Payable, Name} = {Payable, Name} =
case lists:last(TypedTree) of case lists:last(TypedTree) of
{contract, Attrs, {con, _, Con}, _} -> {Contr, Attrs, {con, _, Con}, _} when ?IS_CONTRACT_HEAD(Contr) ->
{proplists:get_value(payable, Attrs, false), Con}; {proplists:get_value(payable, Attrs, false), Con};
Decl -> Decl ->
gen_error({last_declaration_must_be_contract, Decl}) gen_error({last_declaration_must_be_contract, Decl})
@ -29,7 +30,8 @@ convert_typed(TypedTree, Options) ->
Icode = code(TypedTree, NewIcode, Options), Icode = code(TypedTree, NewIcode, Options),
deadcode_elimination(Icode). deadcode_elimination(Icode).
code([{contract, _Attribs, Con, Code}|Rest], Icode, Options) -> code([{Contract, _Attribs, Con, Code}|Rest], Icode, Options)
when ?IS_CONTRACT_HEAD(Contract) ->
NewIcode = contract_to_icode(Code, aeso_icode:set_namespace(Con, Icode)), NewIcode = contract_to_icode(Code, aeso_icode:set_namespace(Con, Icode)),
code(Rest, NewIcode, Options); code(Rest, NewIcode, Options);
code([{namespace, _Ann, Name, Code}|Rest], Icode, Options) -> code([{namespace, _Ann, Name, Code}|Rest], Icode, Options) ->

View File

@ -10,9 +10,9 @@
-export([format/1, pos/1]). -export([format/1, pos/1]).
format({last_declaration_must_be_contract, Decl = {namespace, _, {con, _, C}, _}}) -> format({last_declaration_must_be_contract, Decl = {Kind, _, {con, _, C}, _}}) ->
Msg = io_lib:format("Expected a contract as the last declaration instead of the namespace '~s'\n", Msg = io_lib:format("Expected a contract as the last declaration instead of the ~p '~s'\n",
[C]), [Kind, C]),
mk_err(pos(Decl), Msg); mk_err(pos(Decl), Msg);
format({missing_init_function, Con}) -> format({missing_init_function, Con}) ->
Msg = io_lib:format("Missing init function for the contract '~s'.\n", [pp_expr(Con)]), Msg = io_lib:format("Missing init function for the contract '~s'.\n", [pp_expr(Con)]),

View File

@ -28,6 +28,7 @@
-include_lib("aebytecode/include/aeb_opcodes.hrl"). -include_lib("aebytecode/include/aeb_opcodes.hrl").
-include("aeso_icode.hrl"). -include("aeso_icode.hrl").
-include("aeso_utils.hrl").
-type option() :: pp_sophia_code -type option() :: pp_sophia_code
@ -468,7 +469,7 @@ error_missing_call_function() ->
Msg = "Internal error: missing '__call'-function", Msg = "Internal error: missing '__call'-function",
aeso_errors:throw(aeso_errors:new(internal_error, Msg)). aeso_errors:throw(aeso_errors:new(internal_error, Msg)).
get_call_type([{contract, _, _, Defs}]) -> get_call_type([{Contract, _, _, Defs}]) when ?IS_CONTRACT_HEAD(Contract) ->
case [ {lists:last(QFunName), FunType} case [ {lists:last(QFunName), FunType}
|| {letfun, _, {id, _, ?CALL_NAME}, [], _Ret, || {letfun, _, {id, _, ?CALL_NAME}, [], _Ret,
{typed, _, {typed, _,
@ -482,7 +483,7 @@ get_call_type([_ | Contracts]) ->
get_call_type(Contracts). get_call_type(Contracts).
-dialyzer({nowarn_function, get_decode_type/2}). -dialyzer({nowarn_function, get_decode_type/2}).
get_decode_type(FunName, [{contract, Ann, _, Defs}]) -> get_decode_type(FunName, [{Contract, Ann, _, Defs}]) when ?IS_CONTRACT_HEAD(Contract) ->
GetType = fun({letfun, _, {id, _, Name}, Args, Ret, _}) when Name == FunName -> [{Args, Ret}]; GetType = fun({letfun, _, {id, _, Name}, Args, Ret, _}) when Name == FunName -> [{Args, Ret}];
({fun_decl, _, {id, _, Name}, {fun_t, _, _, Args, Ret}}) when Name == FunName -> [{Args, Ret}]; ({fun_decl, _, {id, _, Name}, {fun_t, _, _, Args, Ret}}) when Name == FunName -> [{Args, Ret}];
(_) -> [] end, (_) -> [] end,

View File

@ -93,8 +93,20 @@ decl() ->
?LAZY_P( ?LAZY_P(
choice( choice(
%% Contract declaration %% Contract declaration
[ ?RULE(keyword(contract), con(), tok('='), maybe_block(decl()), {contract, _1, _2, _4}) [ ?RULE(token(main), keyword(contract),
, ?RULE(token(payable), keyword(contract), con(), tok('='), maybe_block(decl()), add_modifiers([_1], {contract, _2, _3, _5})) con(), tok('='), maybe_block(decl()), {contract_main, _2, _3, _5})
, ?RULE(keyword(contract),
con(), tok('='), maybe_block(decl()), {contract_child, _1, _2, _4})
, ?RULE(keyword(contract), token(interface),
con(), tok('='), maybe_block(decl()), {contract_interface, _1, _3, _5})
, ?RULE(token(payable), token(main), keyword(contract),
con(), tok('='), maybe_block(decl()), add_modifiers([_1], {contract_main, _3, _4, _6}))
, ?RULE(token(payable), keyword(contract),
con(), tok('='), maybe_block(decl()), add_modifiers([_1], {contract_child, _2, _3, _5}))
, ?RULE(token(payable), keyword(contract), token(interface),
con(), tok('='), maybe_block(decl()), add_modifiers([_1], {contract_interface, _2, _4, _6}))
, ?RULE(keyword(namespace), con(), tok('='), maybe_block(decl()), {namespace, _1, _2, _4}) , ?RULE(keyword(namespace), con(), tok('='), maybe_block(decl()), {namespace, _1, _2, _4})
, ?RULE(keyword(include), str(), {include, get_ann(_1), _2}) , ?RULE(keyword(include), str(), {include, get_ann(_1), _2})
, pragma() , pragma()

View File

@ -13,6 +13,8 @@
-export_type([options/0]). -export_type([options/0]).
-include("aeso_utils.hrl").
-type doc() :: prettypr:document(). -type doc() :: prettypr:document().
-type options() :: [{indent, non_neg_integer()} | show_generated]. -type options() :: [{indent, non_neg_integer()} | show_generated].
@ -131,6 +133,10 @@ typed(A, Type) ->
false -> follow(hsep(A, text(":")), type(Type)) false -> follow(hsep(A, text(":")), type(Type))
end. end.
contract_head(contract_main) -> text("main contract");
contract_head(contract_child) -> text("contract");
contract_head(contract_interface) -> text("contract interface").
%% -- Exports ---------------------------------------------------------------- %% -- Exports ----------------------------------------------------------------
-spec decls([aeso_syntax:decl()], options()) -> doc(). -spec decls([aeso_syntax:decl()], options()) -> doc().
@ -145,11 +151,11 @@ decl(D, Options) ->
with_options(Options, fun() -> decl(D) end). with_options(Options, fun() -> decl(D) end).
-spec decl(aeso_syntax:decl()) -> doc(). -spec decl(aeso_syntax:decl()) -> doc().
decl({contract, Attrs, C, Ds}) -> decl({Con, Attrs, C, Ds}) when ?IS_CONTRACT_HEAD(Con) ->
Mod = fun({Mod, true}) when Mod == payable -> Mod = fun({Mod, true}) when Mod == payable ->
text(atom_to_list(Mod)); text(atom_to_list(Mod));
(_) -> empty() end, (_) -> empty() end,
block(follow( hsep(lists:map(Mod, Attrs) ++ [text("contract")]) block(follow( hsep(lists:map(Mod, Attrs) ++ [contract_head(Con)])
, hsep(name(C), text("="))), decls(Ds)); , hsep(name(C), text("="))), decls(Ds));
decl({namespace, _, C, Ds}) -> decl({namespace, _, C, Ds}) ->
block(follow(text("namespace"), hsep(name(C), text("="))), decls(Ds)); block(follow(text("namespace"), hsep(name(C), text("="))), decls(Ds));

View File

@ -44,7 +44,9 @@ lexer() ->
, {"[^/*]+|[/*]", skip()} ], , {"[^/*]+|[/*]", skip()} ],
Keywords = ["contract", "include", "let", "switch", "type", "record", "datatype", "if", "elif", "else", "function", Keywords = ["contract", "include", "let", "switch", "type", "record", "datatype", "if", "elif", "else", "function",
"stateful", "payable", "true", "false", "mod", "public", "entrypoint", "private", "indexed", "namespace"], "stateful", "payable", "true", "false", "mod", "public", "entrypoint", "private", "indexed", "namespace",
"interface", "main"
],
KW = string:join(Keywords, "|"), KW = string:join(Keywords, "|"),
Rules = Rules =

View File

@ -25,7 +25,8 @@
-type ann_origin() :: system | user. -type ann_origin() :: system | user.
-type ann_format() :: '?:' | hex | infix | prefix | elif. -type ann_format() :: '?:' | hex | infix | prefix | elif.
-type ann() :: [{line, ann_line()} | {col, ann_col()} | {format, ann_format()} | {origin, ann_origin()} | stateful | private]. -type ann() :: [ {line, ann_line()} | {col, ann_col()} | {format, ann_format()} | {origin, ann_origin()}
| stateful | private] | payable | main | interface.
-type name() :: string(). -type name() :: string().
-type id() :: {id, ann(), name()}. -type id() :: {id, ann(), name()}.
@ -34,7 +35,9 @@
-type qcon() :: {qcon, ann(), [name()]}. -type qcon() :: {qcon, ann(), [name()]}.
-type tvar() :: {tvar, ann(), name()}. -type tvar() :: {tvar, ann(), name()}.
-type decl() :: {contract, ann(), con(), [decl()]} -type decl() :: {contract_main, ann(), con(), [decl()]}
| {contract_child, ann(), con(), [decl()]}
| {contract_interface, ann(), con(), [decl()]}
| {namespace, ann(), con(), [decl()]} | {namespace, ann(), con(), [decl()]}
| {pragma, ann(), pragma()} | {pragma, ann(), pragma()}
| {type_decl, ann(), id(), [tvar()]} % Only for error msgs | {type_decl, ann(), id(), [tvar()]} % Only for error msgs

View File

@ -190,7 +190,7 @@ parameterized_contract(ExtraCode, FunName, Types) ->
lists:flatten( lists:flatten(
["contract Remote =\n" ["contract Remote =\n"
" entrypoint bla : () => unit\n\n" " entrypoint bla : () => unit\n\n"
"contract Dummy =\n", "main contract Dummy =\n",
ExtraCode, "\n", ExtraCode, "\n",
" type an_alias('a) = string * 'a\n" " type an_alias('a) = string * 'a\n"
" record r = {x : an_alias(int), y : variant}\n" " record r = {x : an_alias(int), y : variant}\n"

View File

@ -59,8 +59,8 @@ calldata_aci_test_() ->
end} || {ContractName, Fun, Args} <- compilable_contracts()]. end} || {ContractName, Fun, Args} <- compilable_contracts()].
parse_args(Fun, Args) -> parse_args(Fun, Args) ->
[{contract, _, _, [{letfun, _, _, _, _, {app, _, _, AST}}]}] = [{contract_main, _, _, [{letfun, _, _, _, _, {app, _, _, AST}}]}] =
aeso_parser:string("contract Temp = function foo() = " ++ Fun ++ "(" ++ string:join(Args, ", ") ++ ")"), aeso_parser:string("main contract Temp = function foo() = " ++ Fun ++ "(" ++ string:join(Args, ", ") ++ ")"),
strip_ann(AST). strip_ann(AST).
strip_ann(T) when is_tuple(T) -> strip_ann(T) when is_tuple(T) ->

View File

@ -179,17 +179,12 @@ compilable_contracts() ->
"lhs_matching", "lhs_matching",
"more_strings", "more_strings",
"protected_call", "protected_call",
"hermetization_turnoff" "hermetization_turnoff",
"multiple_contracts"
]. ].
not_compilable_on(fate) -> []; not_compilable_on(fate) -> [];
not_compilable_on(aevm) -> not_compilable_on(aevm) -> compilable_contracts().
[ "stdlib_include", "manual_stdlib_include", "pairing_crypto"
, "aens_update", "basic_auth_tx", "more_strings"
, "unapplied_builtins", "bytes_to_x", "state_handling", "protected_call"
, "hermetization_turnoff"
].
debug_mode_contracts() -> debug_mode_contracts() ->
["hermetization_turnoff"]. ["hermetization_turnoff"].
@ -635,9 +630,9 @@ failing_contracts() ->
<<?Pos(2, 1) <<?Pos(2, 1)
"Cannot compile with this version of the compiler,\n" "Cannot compile with this version of the compiler,\n"
"because it does not satisfy the constraint ", Version/binary, " == 9.9.9">>]) "because it does not satisfy the constraint ", Version/binary, " == 9.9.9">>])
, ?TYPE_ERROR(multiple_contracts, , ?TYPE_ERROR(interface_with_defs,
[<<?Pos(2, 3) [<<?Pos(2, 3)
"Only the main contract can contain defined functions or entrypoints.\n" "Contract interfaces cannot contain defined functions or entrypoints.\n"
"Fix: replace the definition of 'foo' by a type signature.">>]) "Fix: replace the definition of 'foo' by a type signature.">>])
, ?TYPE_ERROR(contract_as_namespace, , ?TYPE_ERROR(contract_as_namespace,
[<<?Pos(5, 28) [<<?Pos(5, 28)
@ -746,9 +741,7 @@ failing_contracts() ->
{fate, ?Msg(File, Line, Col, ErrFATE)}]}). {fate, ?Msg(File, Line, Col, ErrFATE)}]}).
failing_code_gen_contracts() -> failing_code_gen_contracts() ->
[ ?SAME(last_declaration_must_be_contract, 1, 1, [ ?SAME(missing_definition, 2, 14,
"Expected a contract as the last declaration instead of the namespace 'LastDeclarationIsNotAContract'")
, ?SAME(missing_definition, 2, 14,
"Missing definition of function 'foo'.") "Missing definition of function 'foo'.")
, ?AEVM(polymorphic_entrypoint, 2, 17, , ?AEVM(polymorphic_entrypoint, 2, 17,
"The argument\n" "The argument\n"

View File

@ -12,10 +12,10 @@ simple_contracts_test_() ->
fun(_) -> ok end, fun(_) -> ok end,
[{"Parse a contract with an identity function.", [{"Parse a contract with an identity function.",
fun() -> fun() ->
Text = "contract Identity =\n" Text = "main contract Identity =\n"
" function id(x) = x\n", " function id(x) = x\n",
?assertMatch( ?assertMatch(
[{contract, _, {con, _, "Identity"}, [{contract_main, _, {con, _, "Identity"},
[{letfun, _, {id, _, "id"}, [{id, _, "x"}], {id, _, "_"}, [{letfun, _, {id, _, "id"}, [{id, _, "x"}], {id, _, "_"},
{id, _, "x"}}]}], parse_string(Text)), {id, _, "x"}}]}], parse_string(Text)),
ok ok

View File

@ -1,5 +1,5 @@
contract Identity = contract Identity =
function main (x:int) = x function main_fun (x:int) = x
function __call() = 12 function __call() = 12

View File

@ -1,5 +1,5 @@
contract Remote = contract interface Remote =
entrypoint main : (int) => unit entrypoint main_fun : (int) => unit
contract AddrChain = contract AddrChain =
type o_type = oracle(string, map(string, int)) type o_type = oracle(string, map(string, int))

View File

@ -1,5 +1,5 @@
contract Remote = contract interface Remote =
entrypoint foo : () => unit entrypoint foo : () => unit
contract AddressLiterals = contract AddressLiterals =

View File

@ -1,5 +1,5 @@
contract Remote = contract interface Remote =
entrypoint foo : () => unit entrypoint foo : () => unit
contract AddressLiterals = contract AddressLiterals =

View File

@ -1,4 +1,4 @@
contract Remote = contract interface Remote =
entrypoint id : int => int entrypoint id : int => int
contract ProtectedCall = contract ProtectedCall =

View File

@ -1,3 +1,3 @@
function square(x) = x ^ 2 function square(x) = x ^ 2
contract Main = contract Main =
entrypoint main() = square(10) entrypoint main_fun() = square(10)

View File

@ -5,5 +5,5 @@ contract BadAENSresolve =
function fail() : t(int) = function fail() : t(int) =
AENS.resolve("foo.aet", "whatever") AENS.resolve("foo.aet", "whatever")
entrypoint main() = () entrypoint main_fun() = ()

View File

@ -3,4 +3,4 @@ contract MapAsMapKey =
function foo(m) : t(int => int) = {[m] = 0} function foo(m) : t(int => int) = {[m] = 0}
entrypoint main() = () entrypoint main_fun() = ()

View File

@ -2,4 +2,4 @@ contract HigherOrderQueryType =
stateful function foo(o) : oracle_query(_, string ) = stateful function foo(o) : oracle_query(_, string ) =
Oracle.query(o, (x) => x + 1, 100, RelativeTTL(100), RelativeTTL(100)) Oracle.query(o, (x) => x + 1, 100, RelativeTTL(100), RelativeTTL(100))
entrypoint main() = () entrypoint main_fun() = ()

View File

@ -2,4 +2,4 @@ contract HigherOrderResponseType =
stateful function foo(o, q : oracle_query(string, _)) = stateful function foo(o, q : oracle_query(string, _)) =
Oracle.respond(o, q, (x) => x + 1) Oracle.respond(o, q, (x) => x + 1)
entrypoint main() = () entrypoint main_fun() = ()

View File

@ -1,2 +0,0 @@
namespace LastDeclarationIsNotAContract =
function add(x, y) = x + y

View File

@ -1,3 +1,3 @@
contract MissingDefinition = contract MissingDefinition =
entrypoint foo : int => int entrypoint foo : int => int
entrypoint main() = foo(0) entrypoint main_fun() = foo(0)

View File

@ -3,5 +3,5 @@ contract PolymorphicAENSresolve =
function fail() : option('a) = function fail() : option('a) =
AENS.resolve("foo.aet", "whatever") AENS.resolve("foo.aet", "whatever")
entrypoint main() = () entrypoint main_fun() = ()

View File

@ -3,4 +3,4 @@ contract MapAsMapKey =
function foo(m) : t('a) = {[m] = 0} function foo(m) : t('a) = {[m] = 0}
entrypoint main() = () entrypoint main_fun() = ()

View File

@ -2,4 +2,4 @@ contract PolymorphicQueryType =
stateful function is_oracle(o) = stateful function is_oracle(o) =
Oracle.check(o) Oracle.check(o)
entrypoint main() = () entrypoint main_fun() = ()

View File

@ -2,4 +2,4 @@ contract PolymorphicResponseType =
function is_oracle(o : oracle(string, 'r)) = function is_oracle(o : oracle(string, 'r)) =
Oracle.check(o) Oracle.check(o)
entrypoint main(o : oracle(string, int)) = is_oracle(o) entrypoint main_fun(o : oracle(string, int)) = is_oracle(o)

View File

@ -1,4 +1,4 @@
contract Remote = contract interface Remote =
entrypoint foo : int => int entrypoint foo : int => int
contract UnappliedContractCall = contract UnappliedContractCall =

View File

@ -1,5 +1,5 @@
contract UnappliedNamedArgBuiltin = contract UnappliedNamedArgBuiltin =
// Allowed in FATE, but not AEVM // Allowed in FATE, but not AEVM
stateful entrypoint main(s) = stateful entrypoint main_fun(s) =
let reg = Oracle.register let reg = Oracle.register
reg(signature = s, Contract.address, 100, RelativeTTL(100)) : oracle(int, int) reg(signature = s, Contract.address, 100, RelativeTTL(100)) : oracle(int, int)

View File

@ -1,5 +1,5 @@
contract Remote = contract interface Remote =
entrypoint up_to : (int) => list(int) entrypoint up_to : (int) => list(int)
entrypoint sum : (list(int)) => int entrypoint sum : (list(int)) => int
entrypoint some_string : () => string entrypoint some_string : () => string

View File

@ -1,4 +1,4 @@
contract Foo = contract interface Foo =
entrypoint foo : () => int entrypoint foo : () => int
contract Fail = contract Fail =

View File

@ -1,6 +1,6 @@
// Testing primitives for accessing the block chain environment // Testing primitives for accessing the block chain environment
contract Interface = contract interface Interface =
entrypoint contract_address : () => address entrypoint contract_address : () => address
entrypoint call_origin : () => address entrypoint call_origin : () => address
entrypoint call_caller : () => address entrypoint call_caller : () => address

View File

@ -1,4 +1,4 @@
contract Remote = contract interface Remote =
entrypoint dummy : () => unit entrypoint dummy : () => unit
contract Events = contract Events =

View File

@ -1,6 +1,6 @@
// An implementation of the factorial function where each recursive // An implementation of the factorial function where each recursive
// call is to another contract. Not the cheapest way to compute factorial. // call is to another contract. Not the cheapest way to compute factorial.
contract FactorialServer = contract interface FactorialServer =
entrypoint fac : (int) => int entrypoint fac : (int) => int
contract Factorial = contract Factorial =

View File

@ -1,3 +1,2 @@
main contract Identity =
contract Identity = entrypoint main_fun (x:int) = x
entrypoint main (x:int) = x

View File

@ -16,7 +16,7 @@ contract LHSMatching =
let null(_ :: _) = false let null(_ :: _) = false
!null(xs) !null(xs)
entrypoint main() = entrypoint main_fun() =
from_some(Some([0])) from_some(Some([0]))
++ append([length([true]), 2, 3], [4, 5, 6]) ++ append([length([true]), 2, 3], [4, 5, 6])
++ [7 | if (local_match([false]))] ++ [7 | if (local_match([false]))]

View File

@ -1,3 +1,3 @@
contract MissingEventType = contract MissingEventType =
entrypoint main() = entrypoint main_fun() =
Chain.event("MAIN") Chain.event("MAIN")

View File

@ -1,5 +1,7 @@
contract ContractOne = contract Child =
entrypoint foo() = "foo" entrypoint
add2 : int => int
add2(x) = x + 2
contract ContractTwo = main contract Main =
entrypoint bar() = "bar" entrypoint add4(x, c : Child) = c.add2(x) + 2

View File

@ -1,4 +1,4 @@
contract C1 = contract interface C1 =
entrypoint f : int entrypoint f : int
contract C = contract C =

View File

@ -1,4 +1,4 @@
contract Remote = contract interface Remote =
entrypoint id : int => int entrypoint id : int => int
contract ProtectedCall = contract ProtectedCall =

View File

@ -1,18 +1,18 @@
contract Remote1 = contract interface Remote1 =
entrypoint main : (int) => int entrypoint main_fun : (int) => int
contract Remote2 = contract interface Remote2 =
entrypoint call : (Remote1, int) => int entrypoint call : (Remote1, int) => int
contract Remote3 = contract interface Remote3 =
entrypoint get : () => int entrypoint get : () => int
entrypoint tick : () => unit entrypoint tick : () => unit
contract RemoteCall = contract RemoteCall =
stateful entrypoint call(r : Remote1, x : int) : int = stateful entrypoint call(r : Remote1, x : int) : int =
r.main(gas = 10000, value = 10, x) r.main_fun(gas = 10000, value = 10, x)
entrypoint staged_call(r1 : Remote1, r2 : Remote2, x : int) = entrypoint staged_call(r1 : Remote1, r2 : Remote2, x : int) =
r2.call(r1, x) r2.call(r1, x)

View File

@ -1,5 +1,5 @@
contract SpendContract = contract interface SpendContract =
entrypoint withdraw : (int) => int entrypoint withdraw : (int) => int
contract SpendTest = contract SpendTest =

View File

@ -1,5 +1,5 @@
include "String.aes" include "String.aes"
contract Remote = contract interface Remote =
record rstate = { i : int, s : string, m : map(int, int) } record rstate = { i : int, s : string, m : map(int, int) }
entrypoint look_at : (rstate) => unit entrypoint look_at : (rstate) => unit

View File

@ -1,5 +1,5 @@
contract Remote = contract interface Remote =
stateful entrypoint remote_spend : (address, int) => unit stateful entrypoint remote_spend : (address, int) => unit
entrypoint remote_pure : int => int entrypoint remote_pure : int => int

View File

@ -95,7 +95,7 @@ contract Identity =
function s(n) = (f,x)=>f(n(f,x)) function s(n) = (f,x)=>f(n(f,x))
function add(m,n) = (f,x)=>m(f,n(f,x)) function add(m,n) = (f,x)=>m(f,n(f,x))
entrypoint main() = entrypoint main_fun() =
let three=s(s(s(z))) let three=s(s(s(z)))
add(three,three) add(three,three)
(((i)=>i+1),0) (((i)=>i+1),0)

View File

@ -1,5 +1,5 @@
contract Remote = contract interface Remote =
type themap = map(int, string) type themap = map(int, string)
entrypoint foo : () => themap entrypoint foo : () => themap

View File

@ -9,7 +9,7 @@
// Oracle.extend // Oracle.extend
include "String.aes" include "String.aes"
contract UnappliedBuiltins = contract UnappliedBuiltins =
entrypoint main() = () entrypoint main_fun() = ()
type o = oracle(int, int) type o = oracle(int, int)
type t = list(int * string) type t = list(int * string)
type m = map(int, int) type m = map(int, int)