From c60999edf0c3da4660fc38bae42e0aa376d10f8d Mon Sep 17 00:00:00 2001 From: Hans Svensson Date: Mon, 27 May 2019 16:04:06 +0200 Subject: [PATCH] Refactor aeso_aci with dont_unfold, etc. --- src/aeso_aci.erl | 378 +++++++++++++++------------------------- src/aeso_compiler.erl | 1 + test/aeso_aci_tests.erl | 61 +++++-- 3 files changed, 188 insertions(+), 252 deletions(-) diff --git a/src/aeso_aci.erl b/src/aeso_aci.erl index 1db0a01..dafdce2 100644 --- a/src/aeso_aci.erl +++ b/src/aeso_aci.erl @@ -9,46 +9,12 @@ -module(aeso_aci). --export([encode/1,encode/2,decode/1]). --export([encode_type/1,encode_stmt/1,encode_expr/1]). +-export([ encode/1 + , encode/2 + , decode/1]). -%% Define records for the various typed syntactic forms. These make -%% the code easier but don't seem to exist elsewhere. - -%% Top-level --record(contract, {ann,con,decls}). -%% -record(namespace, {ann,con,decls}). --record(letfun, {ann,id,args,type,body}). --record(type_def, {ann,id,vars,typedef}). - -%% Types --record(app_t, {ann,id,fields}). --record(tuple_t, {ann,args}). --record(bytes_t, {ann,len}). --record(record_t, {fields}). --record(field_t, {ann,id,type}). --record(alias_t, {type}). --record(variant_t, {cons}). --record(constr_t, {ann,con,args}). --record(fun_t, {ann,named,args,type}). - -%% Tokens --record(arg, {ann,id,type}). --record(id, {ann,name}). --record(con, {ann,name}). --record(qid, {ann,names}). --record(qcon, {ann,names}). --record(tvar, {ann,name}). - -%% Expressions --record(bool, {ann,bool}). --record(int, {ann,value}). --record(string, {ann,bin}). --record(bytes, {ann,bin}). --record(tuple, {ann,args}). --record(list, {ann,args}). --record(app, {ann,func,args}). --record(typed, {ann,expr,type}). +-export([ encode_type/1 + , encode_expr/1]). %% encode(ContractString) -> {ok,JSON} | {error,String}. %% encode(ContractString, Options) -> {ok,JSON} | {error,String}. @@ -61,24 +27,12 @@ encode(ContractString, Options) when is_binary(ContractString) -> encode(binary_to_list(ContractString), Options); encode(ContractString, Options) -> try - Ast = parse(ContractString, Options), + Ast = aeso_compiler:parse(ContractString, Options), %% io:format("~p\n", [Ast]), - %% aeso_ast:pp(Ast), - TypedAst = aeso_ast_infer_types:infer(Ast, Options), + TypedAst = aeso_ast_infer_types:infer(Ast, [dont_unfold]), %% io:format("~p\n", [TypedAst]), - %% aeso_ast:pp_typed(TypedAst), - %% We find and look at the last contract. - Contract = lists:last(TypedAst), - Cname = contract_name(Contract), - Tdefs = [ encode_typedef(T) || - T <- sort_decls(contract_types(Contract)) ], - Fdefs = [ encode_func(F) || F <- sort_decls(contract_funcs(Contract)), - not is_private_func(F) ], - Jmap = [{<<"contract">>, [{<<"name">>, encode_name(Cname)}, - {<<"type_defs">>, Tdefs}, - {<<"functions">>, Fdefs}]}], - %% io:format("~p\n", [Jmap]), - {ok,jsx:encode(Jmap)} + JList = [ encode_contract(C) || C <- TypedAst ], + {ok,jsx:encode(JList)} catch %% The compiler errors. error:{parse_errors, Errors} -> @@ -95,136 +49,115 @@ join_errors(Prefix, Errors, Pfun) -> Ess = [ Pfun(E) || E <- Errors ], list_to_binary(string:join([Prefix|Ess], "\n")). -%% encode_func(Function) -> JSON +encode_contract(Contract) -> + Cname = contract_name(Contract), + Tdefs = [ encode_typedef(T) || + T <- sort_decls(contract_types(Contract)) ], + Fdefs = [ encode_function(F) + || F <- sort_decls(contract_funcs(Contract)), + not is_private(F) ], + #{<<"contract">> => #{<<"name">> => encode_name(Cname), + <<"type_defs">> => Tdefs, + <<"functions">> => Fdefs}}. + %% Encode a function definition. Currently we are only interested in %% the interface and type. +encode_function(FDef = {letfun, _, {id, _, Name}, Args, Type, _}) -> + #{<<"name">> => encode_name(Name), + <<"arguments">> => encode_args(Args), + <<"returns">> => encode_type(Type), + <<"stateful">> => is_stateful(FDef)}; +encode_function(FDecl = {fun_decl, _, {id, _, Name}, {fun_t, _, _, Args, Type}}) -> + #{<<"name">> => encode_name(Name), + <<"arguments">> => encode_anon_args(Args), + <<"returns">> => encode_type(Type), + <<"stateful">> => is_stateful(FDecl)}. -encode_func(Fdef) -> - Name = function_name(Fdef), - Args = function_args(Fdef), - Type = function_type(Fdef), - [{<<"name">>, encode_name(Name)}, - {<<"arguments">>, encode_args(Args)}, - {<<"returns">>, encode_type(Type)}, - {<<"stateful">>, is_stateful_func(Fdef)}]. +encode_anon_args(Types) -> + Anons = [ list_to_binary("_" ++ integer_to_list(X)) || X <- lists:seq(1, length(Types))], + [ #{<<"name">> => V, <<"type">> => encode_type(T)} + || {V, T} <- lists:zip(Anons, Types) ]. -%% encode_args(Args) -> [JSON]. -%% encode_arg(Args) -> JSON. +encode_args(Args) -> [ encode_arg(A) || A <- Args ]. -encode_args(Args) -> - [ encode_arg(A) || A <- Args ]. - -encode_arg(#arg{id=Id,type=T}) -> - [{<<"name">>,encode_type(Id)}, - {<<"type">>,[encode_type(T)]}]. - -%% encode_types(Types) -> [JSON]. -%% encode_type(Type) -> JSON. - -encode_types(Types) -> - [ encode_type(T) || T <- Types ]. - -encode_type(#tvar{name=N}) -> encode_name(N); -encode_type(#id{name=N}) -> encode_name(N); -encode_type(#con{name=N}) -> encode_name(N); -encode_type(#qid{names=Ns}) -> - encode_name(lists:join(".", Ns)); -encode_type(#qcon{names=Ns}) -> - encode_name(lists:join(".", Ns)); %? -encode_type(#tuple_t{args=As}) -> - Eas = encode_types(As), - [{<<"tuple">>,Eas}]; -encode_type(#bytes_t{len=Len}) -> - {<<"bytes">>, Len}; -encode_type(#record_t{fields=Fs}) -> - Efs = encode_fields(Fs), - [{<<"record">>,Efs}]; -encode_type(#app_t{id=Id,fields=Fs}) -> - Name = encode_type(Id), - Efs = encode_types(Fs), - [{Name,Efs}]; -encode_type(#variant_t{cons=Cs}) -> - Ecs = encode_types(Cs), - [{<<"variant">>,Ecs}]; -encode_type(#constr_t{con=C,args=As}) -> - Ec = encode_type(C), - Eas = encode_types(As), - [{Ec,Eas}]; -encode_type(#fun_t{args=As,type=T}) -> - Eas = encode_types(As), - Et = encode_type(T), - [{<<"function">>,[{<<"arguments">>,Eas},{<<"returns">>,Et}]}]. - -encode_name(Name) -> - list_to_binary(Name). - -%% encode_fields(Fields) -> [JSON]. -%% encode_field(Field) -> JSON. -%% Encode a record field. - -encode_fields(Fs) -> - [ encode_field(F) || F <- Fs ]. - -encode_field(#field_t{id=Id,type=T}) -> - [{<<"name">>,encode_type(Id)}, - {<<"type">>,[encode_type(T)]}]. - -%% encode_typedef(TypeDef) -> JSON. +encode_arg({arg, _, Id, T}) -> + #{<<"name">> => encode_type(Id), + <<"type">> => encode_type(T)}. encode_typedef(Type) -> Name = typedef_name(Type), Vars = typedef_vars(Type), - Def = typedef_def(Type), - [{<<"name">>, encode_name(Name)}, - {<<"vars">>, encode_tvars(Vars)}, - {<<"typedef">>, encode_alias(Def)}]. + Def = typedef_def(Type), + #{<<"name">> => encode_name(Name), + <<"vars">> => encode_tvars(Vars), + <<"typedef">> => encode_type(Def)}. encode_tvars(Vars) -> - [ encode_tvar(V) || V <- Vars ]. + [ #{<<"name">> => encode_type(V)} || V <- Vars ]. -encode_tvar(#tvar{name=N}) -> - [{<<"name">>, encode_name(N)}]. +%% Encode type +encode_type({tvar, _, N}) -> encode_name(N); +encode_type({id, _, N}) -> encode_name(N); +encode_type({con, _, N}) -> encode_name(N); +encode_type({qid, _, Ns}) -> encode_name(lists:join(".", Ns)); +encode_type({qcon, _, Ns}) -> encode_name(lists:join(".", Ns)); +encode_type({tuple_t, _, As}) -> #{<<"tuple">> => encode_types(As)}; +encode_type({bytes_t, _, Len}) -> #{<<"bytes">> => Len}; +encode_type({record_t, Fs}) -> #{<<"record">> => encode_type_fields(Fs)}; +encode_type({app_t, _, Id, Fs}) -> #{encode_type(Id) => encode_types(Fs)}; +encode_type({variant_t, Cs}) -> #{<<"variant">> => encode_types(Cs)}; +encode_type({constr_t, _, C, As}) -> #{encode_type(C) => encode_types(As)}; +encode_type({alias_t, Type}) -> encode_type(Type); +encode_type({fun_t, _, _, As, T}) -> #{<<"function">> => + #{<<"arguments">> => encode_types(As), + <<"returns">> => encode_type(T)}}. -encode_alias(#alias_t{type=T}) -> - encode_type(T); -encode_alias(A) -> encode_type(A). +encode_types(Ts) -> [ encode_type(T) || T <- Ts ]. -%% encode_stmt(Stmt) -> JSON. +encode_type_fields(Fs) -> [ encode_type_field(F) || F <- Fs ]. -encode_stmt(E) -> - encode_expr(E). +encode_type_field({field_t, _, Id, T}) -> + #{<<"name">> => encode_type(Id), + <<"type">> => encode_type(T)}. -%% encode_exprs(Exprs) -> [JSON]. -%% encode_expr(Expr) -> JSON. +encode_name(Name) when is_list(Name) -> + list_to_binary(Name); +encode_name(Name) when is_binary(Name) -> + Name. -encode_exprs(Es) -> - [ encode_expr(E) || E <- Es ]. +%% Encode Expr +encode_exprs(Es) -> [ encode_expr(E) || E <- Es ]. -encode_expr(#id{name=N}) -> encode_name(N); -encode_expr(#con{name=N}) -> encode_name(N); -encode_expr(#qid{names=Ns}) -> - encode_name(lists:join(".", Ns)); -encode_expr(#qcon{names=Ns}) -> - encode_name(lists:join(".", Ns)); %? -encode_expr(#typed{expr=E}) -> - encode_expr(E); -encode_expr(#bool{bool=B}) -> B; -encode_expr(#int{value=V}) -> V; -encode_expr(#string{bin=B}) -> B; -encode_expr(#bytes{bin=B}) -> B; -encode_expr(#tuple{args=As}) -> - Eas = encode_exprs(As), - [{<<"tuple">>,Eas}]; -encode_expr(#list{args=As}) -> - Eas = encode_exprs(As), - [{<<"list">>,Eas}]; -encode_expr(#app{func=F,args=As}) -> +encode_expr({id, _, N}) -> encode_name(N); +encode_expr({con, _, N}) -> encode_name(N); +encode_expr({qid, _, Ns}) -> encode_name(lists:join(".", Ns)); +encode_expr({qcon, _, Ns}) -> encode_name(lists:join(".", Ns)); +encode_expr({typed, _, E}) -> encode_expr(E); +encode_expr({bool, _, B}) -> B; +encode_expr({int, _, V}) -> V; +encode_expr({string, _, S}) -> S; +encode_expr({tuple, _, As}) -> encode_exprs(As); +encode_expr({list, _, As}) -> encode_exprs(As); +encode_expr({bytes, _, B}) -> + Digits = byte_size(B), + <> = B, + list_to_binary(lists:flatten(io_lib:format("#~*.16.0b", [Digits*2, N]))); +encode_expr({Lit, _, L}) when Lit == oracle_pubkey; Lit == oracle_query_id; + Lit == contract_pubkey; Lit == account_pubkey -> + aeser_api_encoder:encode(Lit, L); +encode_expr({app, _, F, As}) -> Ef = encode_expr(F), Eas = encode_exprs(As), - [{<<"apply">>,[{<<"function">>,Ef}, - {<<"arguments">>,Eas}]}]; + #{Ef => Eas}; +encode_expr({record, _, Flds}) -> encode_fields(Flds); +encode_expr({map, _, KVs}) -> [ [encode_expr(K), encode_expr(V)] || {K, V} <- KVs ]; encode_expr({Op,_Ann}) -> - list_to_binary(atom_to_list(Op)). + error({encode_expr_todo, Op}). + +encode_fields(Flds) -> [ encode_field(F) || F <- Flds ]. + +encode_field({field, _, [{proj, _, {id, _, Fld}}], Val}) -> + #{encode_name(Fld) => encode_expr(Val)}. %% decode(JSON) -> ContractString. %% Decode a JSON string and generate a suitable contract string which @@ -232,10 +165,15 @@ encode_expr({Op,_Ann}) -> %% here as this is easier to work with and order is not important. decode(Json) -> - Map = jsx:decode(Json, [return_maps]), + Cs = + case jsx:decode(Json, [return_maps]) of + Map when is_map(Map) -> [Map]; + List -> List + end, + %% io:format("~p\n", [Map]), - #{<<"contract">> := C} = Map, - list_to_binary(decode_contract(C)). + DecCs = [ decode_contract(C) || #{<<"contract">> := C} <- Cs ], + list_to_binary(string:join(DecCs, "\n")). decode_contract(#{<<"name">> := Name, <<"type_defs">> := Ts, @@ -246,7 +184,7 @@ decode_contract(#{<<"name">> := Name, decode_funcs(Fs) -> [ decode_func(F) || F <- Fs ]. -decode_func(#{<<"name">> := <<"init">>}) -> []; +%% decode_func(#{<<"name">> := <<"init">>}) -> []; decode_func(#{<<"name">> := Name,<<"arguments">> := As,<<"returns">> := T}) -> [" function"," ",io_lib:format("~s", [Name])," : ", decode_args(As)," => ",decode_type(T),$\n]. @@ -255,7 +193,7 @@ decode_args(As) -> Das = [ decode_arg(A) || A <- As ], [$(,lists:join(", ", Das),$)]. -decode_arg(#{<<"type">> := [T]}) -> decode_type(T). +decode_arg(#{<<"type">> := T}) -> decode_type(T). decode_types(Ets) -> [ decode_type(Et) || Et <- Ets ]. @@ -272,9 +210,13 @@ decode_type(#{<<"list">> := [Et]}) -> decode_type(#{<<"map">> := Ets}) -> Ts = decode_types(Ets), ["map",$(,lists:join(",", Ts),$)]; +decode_type(#{<<"bytes">> := Len}) -> + ["bytes(", integer_to_list(Len), ")"]; decode_type(#{<<"variant">> := Ets}) -> Ts = decode_types(Ets), lists:join(" | ", Ts); +decode_type(#{<<"function">> := #{<<"arguments">> := Args, <<"returns">> := R}}) -> + [decode_type(#{<<"tuple">> => Args}), " => ", decode_type(R)]; decode_type(Econs) when is_map(Econs) -> %General constructor [{Ec,Ets}] = maps:to_list(Econs), C = decode_name(Ec), @@ -289,7 +231,7 @@ decode_name(En) -> decode_fields(Efs) -> [ decode_field(Ef) || Ef <- Efs ]. -decode_field(#{<<"name">> := En,<<"type">> := [Et]}) -> +decode_field(#{<<"name">> := En,<<"type">> := Et}) -> Name = decode_name(En), Type = decode_type(Et), [Name," : ",Type]. @@ -298,13 +240,16 @@ decode_field(#{<<"name">> := En,<<"type">> := [Et]}) -> %% Here we are only interested in the type definitions and ignore the %% aliases. We find them as they always have variants. -decode_tdefs(Ts) -> [ decode_tdef(T) || - #{<<"typedef">> := #{<<"variant">> := _}} = T <- Ts - ]. +decode_tdefs(Ts) -> [ decode_tdef(T) || T <- Ts ]. -decode_tdef(#{<<"name">> := Name,<<"vars">> := Vs,<<"typedef">> := T}) -> - [" datatype"," ",decode_name(Name),decode_tvars(Vs), - " = ",decode_type(T),$\n]. +decode_tdef(#{<<"name">> := Name, <<"vars">> := Vs, <<"typedef">> := T}) -> + TypeDef = decode_type(T), + DefType = decode_deftype(T), + [" ", DefType, " ", decode_name(Name), decode_tvars(Vs), " = ", TypeDef, $\n]. + +decode_deftype(#{<<"record">> := _Efs}) -> "record"; +decode_deftype(#{<<"variant">> := _}) -> "datatype"; +decode_deftype(_T) -> "type". decode_tvars([]) -> []; %No tvars, no parentheses decode_tvars(Vs) -> @@ -315,22 +260,21 @@ decode_tvar(#{<<"name">> := N}) -> io_lib:format("~s", [N]). %% #contract{Ann, Con, [Declarations]}. -contract_name(#contract{con=#con{name=N}}) -> N. +contract_name({contract, _, {con, _, Name}, _}) -> Name; +contract_name({namespace, _, {con, _, Name}, _}) -> Name. -contract_funcs(#contract{decls=Decls}) -> - [ D || D <- Decls, is_record(D, letfun) ]. +contract_funcs({C, _, _, Decls}) when C == contract; C == namespace -> + [ D || D <- Decls, is_fun(D)]. -contract_types(#contract{decls=Decls}) -> - [ D || D <- Decls, is_record(D, type_def) ]. +contract_types({C, _, _, Decls}) when C == contract; C == namespace -> + [ D || D <- Decls, is_type(D) ]. -%% To keep dialyzer happy and quiet. -%% namespace_name(#namespace{con=#con{name=N}}) -> N. -%% -%% namespace_funcs(#namespace{decls=Decls}) -> -%% [ D || D <- Decls, is_record(D, letfun) ]. -%% -%% namespace_types(#namespace{decls=Decls}) -> -%% [ D || D <- Decls, is_record(D, type_def) ]. +is_fun({letfun, _, _, _, _, _}) -> true; +is_fun({fun_decl, _, _, _}) -> true; +is_fun(_) -> false. + +is_type({type_def, _, _, _, _}) -> true; +is_type(_) -> false. sort_decls(Ds) -> Sort = fun (D1, D2) -> @@ -339,58 +283,12 @@ sort_decls(Ds) -> end, lists:sort(Sort, Ds). -%% #letfun{Ann, Id, [Arg], Type, Typedef}. -function_name(#letfun{id=#id{name=N}}) -> N. +is_private(Node) -> aeso_syntax:get_ann(private, Node, false). +is_stateful(Node) -> aeso_syntax:get_ann(stateful, Node, false). -function_args(#letfun{args=Args}) -> Args. +typedef_name({type_def, _, {id, _, Name}, _, _}) -> Name. -function_type(#letfun{type=Type}) -> Type. +typedef_vars({type_def, _, _, Vars, _}) -> Vars. -is_private_func(#letfun{ann=A}) -> aeso_syntax:get_ann(private, A, false). - -is_stateful_func(#letfun{ann=A}) -> aeso_syntax:get_ann(stateful, A, false). - -%% #type_def{Ann, Id, [Var], Typedef}. - -typedef_name(#type_def{id=#id{name=N}}) -> N. - -typedef_vars(#type_def{vars=Vars}) -> Vars. - -typedef_def(#type_def{typedef=Def}) -> Def. - -%% parse(ContractString, Options) -> {ok,AST}. -%% Signal errors, the sophia compiler way. Sigh! - -parse(Text, Options) -> - %% Try and return something sensible here! - case aeso_parser:string(Text, Options) of - %% Yay, it worked! - {ok, Contract} -> Contract; - %% Scan errors. - {error, {Pos, scan_error}} -> - parse_error(Pos, "scan error"); - {error, {Pos, scan_error_no_state}} -> - parse_error(Pos, "scan error"); - %% Parse errors. - {error, {Pos, parse_error, Error}} -> - parse_error(Pos, Error); - {error, {Pos, ambiguous_parse, As}} -> - ErrorString = io_lib:format("Ambiguous ~p", [As]), - parse_error(Pos, ErrorString); - %% Include error - {error, {Pos, include_error, File}} -> - parse_error(Pos, io_lib:format("could not find include file '~s'", [File])) - end. - -parse_error(Pos, ErrorString) -> - %% io:format("Error ~p ~p\n", [Pos,ErrorString]), - Error = io_lib:format("~s: ~s", [pos_error(Pos), ErrorString]), - error({parse_errors, [Error]}). - -pos_error({Line, Pos}) -> - io_lib:format("line ~p, column ~p", [Line, Pos]); -pos_error({no_file, Line, Pos}) -> - pos_error({Line, Pos}); -pos_error({File, Line, Pos}) -> - io_lib:format("file ~s, line ~p, column ~p", [File, Line, Pos]). +typedef_def({type_def, _, _, _, Def}) -> Def. diff --git a/src/aeso_compiler.erl b/src/aeso_compiler.erl index 2fb5763..bc796c2 100644 --- a/src/aeso_compiler.erl +++ b/src/aeso_compiler.erl @@ -18,6 +18,7 @@ , to_sophia_value/4 , to_sophia_value/5 , decode_calldata/3 + , parse/2 ]). -include_lib("aebytecode/include/aeb_opcodes.hrl"). diff --git a/test/aeso_aci_tests.erl b/test/aeso_aci_tests.erl index 8b5e1d7..e5c0453 100644 --- a/test/aeso_aci_tests.erl +++ b/test/aeso_aci_tests.erl @@ -2,16 +2,15 @@ -include_lib("eunit/include/eunit.hrl"). - -do_test() -> - test_contract(1), - test_contract(2), - test_contract(3). +simple_aci_test_() -> + [{"Test contract " ++ integer_to_list(N), + fun() -> test_contract(N) end} + || N <- [1, 2, 3]]. test_contract(N) -> {Contract,MapACI,DecACI} = test_cases(N), {ok,JSON} = aeso_aci:encode(Contract), - ?assertEqual(MapACI, jsx:decode(JSON, [return_maps])), + ?assertEqual([MapACI], jsx:decode(JSON, [return_maps])), ?assertEqual(DecACI, aeso_aci:decode(JSON)). test_cases(1) -> @@ -22,9 +21,9 @@ test_cases(1) -> <<"type_defs">> => [], <<"functions">> => [#{<<"name">> => <<"a">>, - <<"arguments">> => + <<"arguments">> => [#{<<"name">> => <<"i">>, - <<"type">> => [<<"int">>]}], + <<"type">> => <<"int">>}], <<"returns">> => <<"int">>, <<"stateful">> => false}]}}, DecACI = <<"contract C =\n" @@ -44,12 +43,11 @@ test_cases(2) -> <<"functions">> => [#{<<"arguments">> => [#{<<"name">> => <<"i">>, - <<"type">> => [<<"int">>]}], + <<"type">> => <<"C.allan">>}], <<"name">> => <<"a">>, <<"returns">> => <<"int">>, <<"stateful">> => false}]}}, - DecACI = <<"contract C =\n" - " function a : (int) => int\n">>, + DecACI = <<"contract C =\n type allan = int\n function a : (C.allan) => int\n">>, {Contract,MapACI,DecACI}; test_cases(3) -> Contract = <<"contract C =\n" @@ -60,7 +58,7 @@ test_cases(3) -> [#{<<"arguments">> => [#{<<"name">> => <<"i">>, <<"type">> => - [#{<<"C.bert">> => [<<"string">>]}]}], + #{<<"C.bert">> => [<<"string">>]}}], <<"name">> => <<"a">>,<<"returns">> => <<"int">>, <<"stateful">> => false}], <<"name">> => <<"C">>, @@ -74,3 +72,42 @@ test_cases(3) -> " datatype bert('a) = Bin('a)\n" " function a : (C.bert(string)) => int\n">>, {Contract,MapACI,DecACI}. + +%% Rounttrip +aci_test_() -> + [{"Testing ACI generation for " ++ ContractName, + fun() -> aci_test_contract(ContractName) end} + || ContractName <- all_contracts()]. + +all_contracts() -> aeso_compiler_tests:compilable_contracts(). + +aci_test_contract(Name) -> + String = aeso_test_utils:read_contract(Name), + {ok, JSON} = aeso_aci:encode(String, [{include, {file_system, [aeso_test_utils:contract_path()]}}]), + + %% io:format("JSON:\n~s\n", [JSON]), + ContractStub = aeso_aci:decode(JSON), + + %% io:format("STUB:\n~s\n", [ContractStub]), + check_stub(ContractStub, [{src_file, Name}]), + + ok. + +check_stub(Stub, Options) -> + case aeso_parser:string(binary_to_list(Stub), Options) of + {ok, Ast} -> + try + %% io:format("AST: ~120p\n", [Ast]), + aeso_ast_infer_types:infer(Ast, []) + catch _:{type_errors, TE} -> + io:format("Type error:\n~s\n", [TE]), + error(TE); + _:R -> + io:format("Error: ~p\n", [R]), + error(R) + end; + {error, E} -> + io:format("Error: ~p\n", [E]), + error({parse_error, E}) + end. +