Improve the interface to the compiler

It is now more consistent though we can still discuss how we want the
interface to look.
This commit is contained in:
Robert Virding 2019-01-11 17:36:31 +01:00 committed by Hans Svensson
parent 267fef3a5b
commit 3137bc4d4a

View File

@ -21,8 +21,8 @@
-include("aeso_icode.hrl"). -include("aeso_icode.hrl").
-type option() :: pp_sophia_code | pp_ast | pp_icode | pp_assembler | -type option() :: pp_sophia_code | pp_ast | pp_types | pp_typed_ast |
pp_bytecode. pp_icode| pp_assembler | pp_bytecode.
-type options() :: [option()]. -type options() :: [option()].
-export_type([ option/0 -export_type([ option/0
@ -43,29 +43,51 @@ file(Filename) ->
file(Filename, []). file(Filename, []).
-spec file(string(), options()) -> map(). -spec file(string(), options()) -> map().
file(Filename, Options) -> file(File, Options) ->
C = read_contract(Filename), case read_contract(File) of
from_string(C, Options). {ok, Bin} -> from_string(Bin, Options);
{error, Error} -> {error, {File, Error}}
end.
-spec from_string(string(), options()) -> map(). -spec from_string(binary() | string(), options()) -> map().
from_string(ContractBin, Options) when is_binary(ContractBin) ->
from_string(binary_to_list(ContractBin), Options);
from_string(ContractString, Options) -> from_string(ContractString, Options) ->
Ast = parse(ContractString, Options), try
ok = pp_sophia_code(Ast, Options), Ast = parse(ContractString, Options),
ok = pp_ast(Ast, Options), ok = pp_sophia_code(Ast, Options),
TypedAst = aeso_ast_infer_types:infer(Ast, Options), ok = pp_ast(Ast, Options),
%% pp_types is handled inside aeso_ast_infer_types. TypedAst = aeso_ast_infer_types:infer(Ast, Options),
ok = pp_typed_ast(TypedAst, Options), %% pp_types is handled inside aeso_ast_infer_types.
ICode = to_icode(TypedAst, Options), ok = pp_typed_ast(TypedAst, Options),
TypeInfo = extract_type_info(ICode), ICode = to_icode(TypedAst, Options),
ok = pp_icode(ICode, Options), TypeInfo = extract_type_info(ICode),
Assembler = assemble(ICode, Options), ok = pp_icode(ICode, Options),
ok = pp_assembler(Assembler, Options), Assembler = assemble(ICode, Options),
ByteCodeList = to_bytecode(Assembler, Options), ok = pp_assembler(Assembler, Options),
ByteCode = << << B:8 >> || B <- ByteCodeList >>, ByteCodeList = to_bytecode(Assembler, Options),
ok = pp_bytecode(ByteCode, Options), ByteCode = << << B:8 >> || B <- ByteCodeList >>,
#{byte_code => ByteCode, type_info => TypeInfo, ok = pp_bytecode(ByteCode, Options),
contract_source => ContractString, {ok,#{byte_code => ByteCode,
compiler_version => version()}. compiler_version => version(),
contract_source => ContractString,
type_info => TypeInfo
}}
catch
%% The compiler errors.
error:{parse_errors,Errors} ->
{error,join_errors("Parse errors", Errors, fun(E) -> E end)};
error:{type_errors,Errors} ->
{error,join_errors("Type errors", Errors, fun(E) -> E end)};
error:{code_errors,Errors} ->
{error,join_errors("Code errors", Errors,
fun (E) -> io_lib:format("~p", [E]) end)}
%% General programming errors in the compiler just signal error.
end.
join_errors(Prefix, Errors, Pfun) ->
Ess = [ Pfun(E) || E <- Errors ],
list_to_binary(string:join([Prefix|Ess], "\n")).
-define(CALL_NAME, "__call"). -define(CALL_NAME, "__call").
@ -76,26 +98,36 @@ from_string(ContractString, Options) ->
-spec check_call(string(), options()) -> {ok, string(), {[Type], Type | any}, [term()]} | {error, term()} -spec check_call(string(), options()) -> {ok, string(), {[Type], Type | any}, [term()]} | {error, term()}
when Type :: term(). when Type :: term().
check_call(ContractString, Options) -> check_call(ContractString, Options) ->
Ast = parse(ContractString, Options), try
ok = pp_sophia_code(Ast, Options), Ast = parse(ContractString, Options),
ok = pp_ast(Ast, Options), ok = pp_sophia_code(Ast, Options),
TypedAst = aeso_ast_infer_types:infer(Ast, [permissive_address_literals]), ok = pp_ast(Ast, Options),
{ok, {FunName, {fun_t, _, _, ArgTypes, RetType}}} = get_call_type(TypedAst), TypedAst = aeso_ast_infer_types:infer(Ast, [permissive_address_literals]),
ok = pp_typed_ast(TypedAst, Options), {ok, {FunName, {fun_t, _, _, ArgTypes, RetType}}} = get_call_type(TypedAst),
Icode = to_icode(TypedAst, Options), ok = pp_typed_ast(TypedAst, Options),
ArgVMTypes = [ aeso_ast_to_icode:ast_typerep(T, Icode) || T <- ArgTypes ], Icode = to_icode(TypedAst, Options),
RetVMType = case RetType of ArgVMTypes = [ aeso_ast_to_icode:ast_typerep(T, Icode) || T <- ArgTypes ],
{id, _, "_"} -> any; RetVMType = case RetType of
_ -> aeso_ast_to_icode:ast_typerep(RetType, Icode) {id, _, "_"} -> any;
end, _ -> aeso_ast_to_icode:ast_typerep(RetType, Icode)
ok = pp_icode(Icode, Options), end,
#{ functions := Funs } = Icode, ok = pp_icode(Icode, Options),
ArgIcode = get_arg_icode(Funs), #{ functions := Funs } = Icode,
try [ icode_to_term(T, Arg) || {T, Arg} <- lists:zip(ArgVMTypes, ArgIcode) ] of ArgIcode = get_arg_icode(Funs),
ArgTerms -> ArgTerms = [ icode_to_term(T, Arg) ||
{ok, FunName, {ArgVMTypes, RetVMType}, ArgTerms} {T, Arg} <- lists:zip(ArgVMTypes, ArgIcode) ],
catch throw:Err -> {ok, FunName, {ArgVMTypes, RetVMType}, ArgTerms}
{error, Err} catch
error:{parse_errors, Errors} ->
{error,join_errors("Parse errors", Errors, fun (E) -> E end)};
error:{type_errors, Errors} ->
{error,join_errors("Type errors", Errors, fun (E) -> E end)};
error:{badmatch,{error,missing_call_function}} ->
{error,join_errors("Type errors", ["missing __call function"],
fun (E) -> E end)};
throw:Error -> %Don't ask
{error,join_errors("Code errors", [Error],
fun (E) -> io_lib:format("~p", [E]) end)}
end. end.
-spec create_calldata(map(), string(), string()) -> -spec create_calldata(map(), string(), string()) ->
@ -113,7 +145,7 @@ create_calldata(Contract, Function, Argument) when is_map(Contract) ->
%% Function should be "foo : type", and %% Function should be "foo : type", and
%% Argument should be "Arg1, Arg2, .., ArgN" (no parens) %% Argument should be "Arg1, Arg2, .., ArgN" (no parens)
case string:lexemes(Function, ": ") of case string:lexemes(Function, ": ") of
%% If function is a single word fallback to old calldata generation %% If function is a single word fallback to old calldata generation
[FunName] -> aeso_abi:old_create_calldata(Contract, FunName, Argument); [FunName] -> aeso_abi:old_create_calldata(Contract, FunName, Argument);
[FunName | _] -> [FunName | _] ->
Args = lists:map(fun($\n) -> 32; (X) -> X end, Argument), %% newline to space Args = lists:map(fun($\n) -> 32; (X) -> X end, Argument), %% newline to space
@ -252,6 +284,5 @@ parse_error({Line,Pos}, ErrorString) ->
error({parse_errors,[Error]}). error({parse_errors,[Error]}).
read_contract(Name) -> read_contract(Name) ->
{ok, Bin} = file:read_file(Name), file:read_file(Name).
binary_to_list(Bin).