Framework and tests for code generation (icode/fcode) errors
This commit is contained in:
+1
-11
@@ -74,19 +74,9 @@ do_contract_interface(Type, ContractString, Options) ->
|
||||
string -> do_render_aci_json(JArray)
|
||||
end
|
||||
catch
|
||||
throw:{type_errors, Errors} -> {error, Errors};
|
||||
%% The compiler errors.
|
||||
error:{parse_errors, Errors} ->
|
||||
{error, join_errors("Parse errors", Errors, fun(E) -> E end)};
|
||||
error:{code_errors, Errors} ->
|
||||
{error, join_errors("Code errors", Errors,
|
||||
fun (E) -> io_lib:format("~p", [E]) end)}
|
||||
throw:{error, Errors} -> {error, Errors}
|
||||
end.
|
||||
|
||||
join_errors(Prefix, Errors, Pfun) ->
|
||||
Ess = [ Pfun(E) || E <- Errors ],
|
||||
list_to_binary(string:join([Prefix|Ess], "\n")).
|
||||
|
||||
encode_contract(Contract = {contract, _, {con, _, Name}, _}) ->
|
||||
C0 = #{name => encode_name(Name)},
|
||||
|
||||
|
||||
@@ -2069,7 +2069,7 @@ destroy_and_report_type_errors(Env) ->
|
||||
%% io:format("Type errors now: ~p\n", [Errors0]),
|
||||
ets_delete(type_errors),
|
||||
Errors = [ mk_error(unqualify(Env, Err)) || Err <- Errors0 ],
|
||||
Errors == [] orelse throw({type_errors, Errors}).
|
||||
aeso_errors:throw(Errors). %% No-op if Errors == []
|
||||
|
||||
%% Strip current namespace from error message for nicer printing.
|
||||
unqualify(#env{ namespace = NS }, {qid, Ann, Xs}) ->
|
||||
|
||||
+39
-17
@@ -232,7 +232,7 @@ is_no_code(Env) ->
|
||||
%% -- Compilation ------------------------------------------------------------
|
||||
|
||||
-spec to_fcode(env(), aeso_syntax:ast()) -> fcode().
|
||||
to_fcode(Env, [{contract, Attrs, {con, _, Main}, Decls}]) ->
|
||||
to_fcode(Env, [{contract, Attrs, MainCon = {con, _, Main}, Decls}]) ->
|
||||
#{ builtins := Builtins } = Env,
|
||||
MainEnv = Env#{ context => {main_contract, Main},
|
||||
builtins => Builtins#{[Main, "state"] => {get_state, none},
|
||||
@@ -247,8 +247,10 @@ to_fcode(Env, [{contract, Attrs, {con, _, Main}, Decls}]) ->
|
||||
state_type => StateType,
|
||||
event_type => EventType,
|
||||
payable => Payable,
|
||||
functions => add_init_function(Env1, StateType,
|
||||
functions => add_init_function(Env1, MainCon, StateType,
|
||||
add_event_function(Env1, EventType, Funs)) };
|
||||
to_fcode(_Env, [NotContract]) ->
|
||||
fcode_error({last_declaration_must_be_contract, NotContract});
|
||||
to_fcode(Env, [{contract, _, {con, _, Con}, Decls} | Code]) ->
|
||||
Env1 = decls_to_fcode(Env#{ context => {abstract_contract, Con} }, Decls),
|
||||
to_fcode(Env1, Code);
|
||||
@@ -294,9 +296,10 @@ decl_to_fcode(Env = #{ functions := Funs }, {letfun, Ann, {id, _, Name}, Args, R
|
||||
Env#{ functions := NewFuns }.
|
||||
|
||||
-spec typedef_to_fcode(env(), aeso_syntax:id(), [aeso_syntax:tvar()], aeso_syntax:typedef()) -> env().
|
||||
typedef_to_fcode(Env, {id, _, Name}, Xs, Def) ->
|
||||
typedef_to_fcode(Env, Id = {id, _, Name}, Xs, Def) ->
|
||||
check_state_and_event_types(Env, Id, Xs),
|
||||
Q = qname(Env, Name),
|
||||
FDef = fun(Args) ->
|
||||
FDef = fun(Args) when length(Args) == length(Xs) ->
|
||||
Sub = maps:from_list(lists:zip([X || {tvar, _, X} <- Xs], Args)),
|
||||
case Def of
|
||||
{record_t, Fields} -> {todo, Xs, Args, record_t, Fields};
|
||||
@@ -307,7 +310,9 @@ typedef_to_fcode(Env, {id, _, Name}, Xs, Def) ->
|
||||
end || Con <- Cons ],
|
||||
{variant, FCons};
|
||||
{alias_t, Type} -> {todo, Xs, Args, alias_t, Type}
|
||||
end end,
|
||||
end;
|
||||
(Args) -> internal_error({type_arity_mismatch, Name, length(Args), length(Xs)})
|
||||
end,
|
||||
Constructors =
|
||||
case Def of
|
||||
{variant_t, Cons} ->
|
||||
@@ -328,6 +333,14 @@ typedef_to_fcode(Env, {id, _, Name}, Xs, Def) ->
|
||||
end,
|
||||
bind_type(Env2, Q, FDef).
|
||||
|
||||
check_state_and_event_types(#{ context := {main_contract, _} }, Id, [_ | _]) ->
|
||||
case Id of
|
||||
{id, _, "state"} -> fcode_error({parameterized_state, Id});
|
||||
{id, _, "event"} -> fcode_error({parameterized_event, Id});
|
||||
_ -> ok
|
||||
end;
|
||||
check_state_and_event_types(_, _, _) -> ok.
|
||||
|
||||
-spec type_to_fcode(env(), aeso_syntax:type()) -> ftype().
|
||||
type_to_fcode(Env, Type) ->
|
||||
type_to_fcode(Env, #{}, Type).
|
||||
@@ -533,7 +546,7 @@ expr_to_fcode(Env, Type, {app, _, Fun = {typed, _, _, {fun_t, _, NamedArgsT, _,
|
||||
{builtin_u, B, _} when B =:= aens_resolve ->
|
||||
%% Get the type we are assuming the name resolves to
|
||||
AensType = type_to_fcode(Env, Type),
|
||||
validate_aens_resolve_type(aeso_syntax:get_ann(Fun), AensType),
|
||||
validate_aens_resolve_type(aeso_syntax:get_ann(Fun), Type, AensType),
|
||||
TypeArgs = [{lit, {typerep, AensType}}],
|
||||
builtin_to_fcode(B, FArgs ++ TypeArgs);
|
||||
{builtin_u, B, _Ar} -> builtin_to_fcode(B, FArgs);
|
||||
@@ -616,10 +629,15 @@ validate_oracle_type(Ann, QType, RType) ->
|
||||
ensure_first_order(RType, {higher_order_response_type, Ann, RType}),
|
||||
ok.
|
||||
|
||||
validate_aens_resolve_type(Ann, {variant, [[], [Type]]}) ->
|
||||
ensure_monomorphic(Type, {polymorphic_aens_resolve, Ann, Type}),
|
||||
ensure_first_order(Type, {higher_order_aens_resolve, Ann, Type}),
|
||||
ok.
|
||||
validate_aens_resolve_type(Ann, {app_t, _, _, [Type]}, {variant, [[], [FType]]}) ->
|
||||
case FType of
|
||||
string -> ok;
|
||||
address -> ok;
|
||||
contract -> ok;
|
||||
{oracle, _, _} -> ok;
|
||||
oracle_query -> ok;
|
||||
_ -> fcode_error({invalid_aens_resolve_type, Ann, Type})
|
||||
end.
|
||||
|
||||
ensure_first_order_entrypoint(Ann, Args, Ret) ->
|
||||
[ ensure_first_order(T, {higher_order_entrypoint_argument, Ann, X, T})
|
||||
@@ -904,18 +922,18 @@ builtin_to_fcode(Builtin, Args) ->
|
||||
|
||||
%% -- Init function --
|
||||
|
||||
add_init_function(Env, StateType, Funs0) ->
|
||||
add_init_function(Env, Main, StateType, Funs0) ->
|
||||
case is_no_code(Env) of
|
||||
true -> Funs0;
|
||||
false ->
|
||||
Funs = add_default_init_function(Env, StateType, Funs0),
|
||||
Funs = add_default_init_function(Env, Main, StateType, Funs0),
|
||||
InitName = {entrypoint, <<"init">>},
|
||||
InitFun = #{ body := InitBody} = maps:get(InitName, Funs),
|
||||
Funs#{ InitName => InitFun#{ return => {tuple, []},
|
||||
body => {builtin, set_state, [InitBody]} } }
|
||||
end.
|
||||
|
||||
add_default_init_function(_Env, StateType, Funs) ->
|
||||
add_default_init_function(_Env, Main, StateType, Funs) ->
|
||||
InitName = {entrypoint, <<"init">>},
|
||||
case maps:get(InitName, Funs, none) of
|
||||
%% Only add default init function if state is unit.
|
||||
@@ -924,7 +942,7 @@ add_default_init_function(_Env, StateType, Funs) ->
|
||||
args => [],
|
||||
return => {tuple, []},
|
||||
body => {tuple, []}} };
|
||||
none -> fcode_error(missing_init_function);
|
||||
none -> fcode_error({missing_init_function, Main});
|
||||
_ -> Funs
|
||||
end.
|
||||
|
||||
@@ -1115,7 +1133,7 @@ lookup_type(Env, {qid, _, Name}, Args) ->
|
||||
lookup_type(Env, Name, Args);
|
||||
lookup_type(Env, Name, Args) ->
|
||||
case lookup_type(Env, Name, Args, not_found) of
|
||||
not_found -> error({unknown_type, Name});
|
||||
not_found -> internal_error({unknown_type, Name});
|
||||
Type -> Type
|
||||
end.
|
||||
|
||||
@@ -1440,8 +1458,12 @@ get_attributes(Ann) ->
|
||||
indexed(Xs) ->
|
||||
lists:zip(lists:seq(1, length(Xs)), Xs).
|
||||
|
||||
fcode_error(Err) ->
|
||||
error(Err).
|
||||
fcode_error(Error) ->
|
||||
aeso_errors:throw(aeso_code_errors:format(Error)).
|
||||
|
||||
internal_error(Error) ->
|
||||
Msg = lists:flatten(io_lib:format("~p\n", [Error])),
|
||||
aeso_errors:throw(aeso_errors:new(internal_error, aeso_errors:pos(0, 0), Msg)).
|
||||
|
||||
%% -- Pretty printing --------------------------------------------------------
|
||||
|
||||
|
||||
+25
-14
@@ -21,8 +21,8 @@ convert_typed(TypedTree, Options) ->
|
||||
case lists:last(TypedTree) of
|
||||
{contract, Attrs, {con, _, Con}, _} ->
|
||||
{proplists:get_value(payable, Attrs, false), Con};
|
||||
_ ->
|
||||
gen_error(last_declaration_must_be_contract)
|
||||
Decl ->
|
||||
gen_error({last_declaration_must_be_contract, Decl})
|
||||
end,
|
||||
NewIcode = aeso_icode:set_payable(Payable,
|
||||
aeso_icode:set_name(Name, aeso_icode:new(Options))),
|
||||
@@ -42,17 +42,17 @@ code([], Icode, Options) ->
|
||||
%% Generate error on correct format.
|
||||
|
||||
gen_error(Error) ->
|
||||
error({code_errors, [Error]}).
|
||||
aeso_errors:throw(aeso_code_errors:format(Error)).
|
||||
|
||||
%% Create default init function (only if state is unit).
|
||||
add_default_init_function(Icode = #{functions := Funs, state_type := State}, Options) ->
|
||||
add_default_init_function(Icode = #{namespace := NS, functions := Funs, state_type := State}, Options) ->
|
||||
NoCode = proplists:get_value(no_code, Options, false),
|
||||
{_, _, QInit} = aeso_icode:qualify({id, [], "init"}, Icode),
|
||||
case lists:keymember(QInit, 1, Funs) of
|
||||
true -> Icode;
|
||||
false when NoCode -> Icode;
|
||||
false when State /= {tuple, []} ->
|
||||
gen_error(missing_init_function);
|
||||
gen_error({missing_init_function, NS});
|
||||
false ->
|
||||
Type = {tuple, [typerep, {tuple, []}]},
|
||||
Value = #tuple{ cpts = [type_value({tuple, []}), {tuple, []}] },
|
||||
@@ -83,9 +83,9 @@ contract_to_icode([{type_def, _Attrib, Id = {id, _, Name}, Args, Def} | Rest],
|
||||
constructors := maps:merge(Constructors, NewConstructors) },
|
||||
Icode2 = case Name of
|
||||
"state" when Args == [] -> Icode1#{ state_type => ast_typerep(Def, Icode) };
|
||||
"state" -> gen_error(state_type_cannot_be_parameterized);
|
||||
"state" -> gen_error({parameterized_state, Id});
|
||||
"event" when Args == [] -> Icode1#{ event_type => Def };
|
||||
"event" -> gen_error(event_type_cannot_be_parameterized);
|
||||
"event" -> gen_error({parameterized_event, Id});
|
||||
_ -> Icode1
|
||||
end,
|
||||
contract_to_icode(Rest, Icode2);
|
||||
@@ -398,10 +398,8 @@ ast_binop(Op, Ann, {typed, _, A, Type}, B, Icode)
|
||||
when Op == '=='; Op == '!=';
|
||||
Op == '<'; Op == '>';
|
||||
Op == '<='; Op == '=<'; Op == '>=' ->
|
||||
Monomorphic = is_monomorphic(Type),
|
||||
[ gen_error({cant_compare_type_aevm, Ann, Op, Type}) || not is_simple_type(Type) ],
|
||||
case ast_typerep(Type, Icode) of
|
||||
_ when not Monomorphic ->
|
||||
gen_error({cant_compare_polymorphic_type, Ann, Op, Type});
|
||||
word -> #binop{op = Op, left = ast_body(A, Icode), right = ast_body(B, Icode)};
|
||||
OtherType ->
|
||||
Neg = case Op of
|
||||
@@ -767,14 +765,22 @@ map_upd(Key, Default, ValFun, Map = {typed, Ann, _, MapType}, Icode) ->
|
||||
builtin_call(FunName, Args).
|
||||
|
||||
check_entrypoint_type(Ann, Name, Args, Ret) ->
|
||||
Check = fun(T, Err) ->
|
||||
case is_simple_type(T) of
|
||||
CheckFirstOrder = fun(T, Err) ->
|
||||
case is_first_order_type(T) of
|
||||
false -> gen_error(Err);
|
||||
true -> ok
|
||||
end end,
|
||||
[ Check(T, {entrypoint_argument_must_have_simple_type, Ann1, Name, X, T})
|
||||
CheckMonomorphic = fun(T, Err) ->
|
||||
case is_monomorphic(T) of
|
||||
false -> gen_error(Err);
|
||||
true -> ok
|
||||
end end,
|
||||
[ CheckFirstOrder(T, {entrypoint_argument_must_have_first_order_type, Ann1, Name, X, T})
|
||||
|| {arg, Ann1, X, T} <- Args ],
|
||||
Check(Ret, {entrypoint_must_have_simple_return_type, Ann, Name, Ret}).
|
||||
CheckFirstOrder(Ret, {entrypoint_must_have_first_order_return_type, Ann, Name, Ret}),
|
||||
[ CheckMonomorphic(T, {entrypoint_argument_must_have_monomorphic_type, Ann1, Name, X, T})
|
||||
|| {arg, Ann1, X, T} <- Args ],
|
||||
CheckMonomorphic(Ret, {entrypoint_must_have_monomorphic_return_type, Ann, Name, Ret}).
|
||||
|
||||
is_simple_type({tvar, _, _}) -> false;
|
||||
is_simple_type({fun_t, _, _, _, _}) -> false;
|
||||
@@ -782,6 +788,11 @@ is_simple_type(Ts) when is_list(Ts) -> lists:all(fun is_simple_type/1, Ts);
|
||||
is_simple_type(T) when is_tuple(T) -> is_simple_type(tuple_to_list(T));
|
||||
is_simple_type(_) -> true.
|
||||
|
||||
is_first_order_type({fun_t, _, _, _, _}) -> false;
|
||||
is_first_order_type(Ts) when is_list(Ts) -> lists:all(fun is_first_order_type/1, Ts);
|
||||
is_first_order_type(T) when is_tuple(T) -> is_first_order_type(tuple_to_list(T));
|
||||
is_first_order_type(_) -> true.
|
||||
|
||||
is_monomorphic({tvar, _, _}) -> false;
|
||||
is_monomorphic([H|T]) ->
|
||||
is_monomorphic(H) andalso is_monomorphic(T);
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @author Ulf Norell
|
||||
%%% @copyright (C) 2019, Aeternity Anstalt
|
||||
%%% @doc
|
||||
%%% Formatting of code generation errors.
|
||||
%%% @end
|
||||
%%%
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeso_code_errors).
|
||||
|
||||
-export([format/1]).
|
||||
|
||||
format({last_declaration_must_be_contract, Decl = {namespace, _, {con, _, C}, _}}) ->
|
||||
Msg = io_lib:format("Expected a contract as the last declaration instead of the namespace '~s'\n",
|
||||
[C]),
|
||||
mk_err(pos(Decl), Msg);
|
||||
format({missing_init_function, Con}) ->
|
||||
Msg = io_lib:format("Missing init function for the contract '~s'.\n", [pp_expr(Con)]),
|
||||
Cxt = "The 'init' function can only be omitted if the state type is 'unit'.\n",
|
||||
mk_err(pos(Con), Msg, Cxt);
|
||||
format({parameterized_state, Decl}) ->
|
||||
Msg = "The state type cannot be parameterized.\n",
|
||||
mk_err(pos(Decl), Msg);
|
||||
format({parameterized_event, Decl}) ->
|
||||
Msg = "The event type cannot be parameterized.\n",
|
||||
mk_err(pos(Decl), Msg);
|
||||
format({entrypoint_argument_must_have_monomorphic_type, Ann, {id, _, Name}, X, T}) ->
|
||||
Msg = io_lib:format("The argument\n~s\nof entrypoint '~s' does not have a monomorphic type.\n",
|
||||
[pp_typed(X, T), Name]),
|
||||
Cxt = "Use the FATE backend if you want polymorphic entrypoints.",
|
||||
mk_err(pos(Ann), Msg, Cxt);
|
||||
format({cant_compare_type_aevm, Ann, Op, Type}) ->
|
||||
StringAndTuple = [ "- type string\n"
|
||||
"- tuple or record of word type\n" || lists:member(Op, ['==', '!=']) ],
|
||||
Msg = io_lib:format("Cannot compare values of type\n"
|
||||
"~s\n"
|
||||
"The AEVM only supports '~s' on values of\n"
|
||||
"- word type (int, bool, bits, address, oracle(_, _), etc)\n"
|
||||
"~s"
|
||||
"Use FATE if you need to compare arbitrary types.\n",
|
||||
[pp_type(2, Type), Op, StringAndTuple]),
|
||||
mk_err(pos(Ann), Msg);
|
||||
format({invalid_aens_resolve_type, Ann, T}) ->
|
||||
Msg = io_lib:format("Invalid return type of AENS.resolve:\n"
|
||||
"~s\n"
|
||||
"It must be a string or a pubkey type (address, oracle, etc).\n",
|
||||
[pp_type(2, T)]),
|
||||
mk_err(pos(Ann), Msg);
|
||||
format(Err) ->
|
||||
mk_err(aeso_errors:pos(0, 0), io_lib:format("Unknown error: ~p\n", [Err])).
|
||||
|
||||
pos(Ann) ->
|
||||
File = aeso_syntax:get_ann(file, Ann, no_file),
|
||||
Line = aeso_syntax:get_ann(line, Ann, 0),
|
||||
Col = aeso_syntax:get_ann(col, Ann, 0),
|
||||
aeso_errors:pos(File, Line, Col).
|
||||
|
||||
pp_typed(E, T) ->
|
||||
prettypr:format(prettypr:nest(2,
|
||||
lists:foldr(fun prettypr:beside/2, prettypr:empty(),
|
||||
[aeso_pretty:expr(E), prettypr:text(" : "),
|
||||
aeso_pretty:type(T)]))).
|
||||
|
||||
pp_expr(E) ->
|
||||
prettypr:format(aeso_pretty:expr(E)).
|
||||
|
||||
pp_type(N, T) ->
|
||||
prettypr:format(prettypr:nest(N, aeso_pretty:type(T))).
|
||||
|
||||
mk_err(Pos, Msg) ->
|
||||
aeso_errors:new(code_error, Pos, lists:flatten(Msg)).
|
||||
|
||||
mk_err(Pos, Msg, Cxt) ->
|
||||
aeso_errors:new(code_error, Pos, lists:flatten(Msg), lists:flatten(Cxt)).
|
||||
|
||||
+7
-32
@@ -98,15 +98,7 @@ from_string(Backend, ContractString, Options) ->
|
||||
try
|
||||
from_string1(Backend, ContractString, Options)
|
||||
catch
|
||||
%% The compiler errors.
|
||||
throw:{parse_errors, Errors} ->
|
||||
{error, Errors};
|
||||
throw:{type_errors, Errors} ->
|
||||
{error, Errors};
|
||||
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.
|
||||
throw:{error, Errors} -> {error, Errors}
|
||||
end.
|
||||
|
||||
from_string1(aevm, ContractString, Options) ->
|
||||
@@ -230,16 +222,10 @@ check_call1(ContractString0, FunName, Args, Options) ->
|
||||
{ok, FunName, CallArgs}
|
||||
end
|
||||
catch
|
||||
throw:{parse_errors, Errors} ->
|
||||
{error, Errors};
|
||||
throw:{type_errors, Errors} ->
|
||||
{error, Errors};
|
||||
throw:{error, Errors} -> {error, Errors};
|
||||
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)}
|
||||
fun (E) -> E end)}
|
||||
end.
|
||||
|
||||
arguments_of_body(CallName, _FunName, Fcode) ->
|
||||
@@ -345,16 +331,10 @@ to_sophia_value(ContractString, FunName, ok, Data, Options0) ->
|
||||
end
|
||||
end
|
||||
catch
|
||||
throw:{parse_errors, Errors} ->
|
||||
{error, Errors};
|
||||
throw:{type_errors, Errors} ->
|
||||
{error, Errors};
|
||||
throw:{error, Errors} -> {error, Errors};
|
||||
error:{badmatch, {error, missing_function}} ->
|
||||
{error, join_errors("Type errors", ["no function: '" ++ FunName ++ "'"],
|
||||
fun (E) -> E end)};
|
||||
throw:Error -> %Don't ask
|
||||
{error, join_errors("Code errors", [Error],
|
||||
fun (E) -> io_lib:format("~p", [E]) end)}
|
||||
fun (E) -> E end)}
|
||||
end.
|
||||
|
||||
|
||||
@@ -444,16 +424,11 @@ decode_calldata(ContractString, FunName, Calldata, Options0) ->
|
||||
end
|
||||
end
|
||||
catch
|
||||
throw:{parse_errors, Errors} ->
|
||||
{error, Errors};
|
||||
throw:{type_errors, Errors} ->
|
||||
throw:{error, Errors} ->
|
||||
{error, Errors};
|
||||
error:{badmatch, {error, missing_function}} ->
|
||||
{error, join_errors("Type errors", ["no function: '" ++ FunName ++ "'"],
|
||||
fun (E) -> E end)};
|
||||
throw:Error -> %Don't ask
|
||||
{error, join_errors("Code errors", [Error],
|
||||
fun (E) -> io_lib:format("~p", [E]) end)}
|
||||
fun (E) -> E end)}
|
||||
end.
|
||||
|
||||
get_arg_icode(Funs) ->
|
||||
|
||||
+8
-1
@@ -34,6 +34,7 @@
|
||||
, pos/2
|
||||
, pos/3
|
||||
, pp/1
|
||||
, throw/1
|
||||
, type/1
|
||||
]).
|
||||
|
||||
@@ -49,6 +50,12 @@ pos(Line, Col) ->
|
||||
pos(File, Line, Col) ->
|
||||
#pos{ file = File, line = Line, col = Col }.
|
||||
|
||||
throw([]) -> ok;
|
||||
throw(Errs) when is_list(Errs) ->
|
||||
erlang:throw({error, Errs});
|
||||
throw(#err{} = Err) ->
|
||||
erlang:throw({error, [Err]}).
|
||||
|
||||
msg(#err{ message = Msg, context = none }) -> Msg;
|
||||
msg(#err{ message = Msg, context = Ctxt }) -> Msg ++ Ctxt.
|
||||
|
||||
@@ -68,4 +75,4 @@ pp(#err{ pos = Pos } = Err) ->
|
||||
pp_pos(#pos{file = no_file, line = L, col = C}) ->
|
||||
io_lib:format("At line ~p, col ~p:", [L, C]);
|
||||
pp_pos(#pos{file = F, line = L, col = C}) ->
|
||||
io_lib:format("In '~s' at line ~p, col~p:", [F, L, C]).
|
||||
io_lib:format("In '~s' at line ~p, col ~p:", [F, L, C]).
|
||||
|
||||
+2
-2
@@ -33,10 +33,10 @@ string(String, Included, Opts) ->
|
||||
{ok, AST} ->
|
||||
case expand_includes(AST, Included, Opts) of
|
||||
{ok, AST1} -> AST1;
|
||||
{error, Err} -> throw({parse_errors, [mk_error(Err)]})
|
||||
{error, Err} -> aeso_errors:throw(mk_error(Err))
|
||||
end;
|
||||
{error, Err} ->
|
||||
throw({parse_errors, [mk_error(Err)]})
|
||||
aeso_errors:throw(mk_error(Err))
|
||||
end.
|
||||
|
||||
type(String) ->
|
||||
|
||||
Reference in New Issue
Block a user