Make Hakuzaru Great Again #22
+86
-2
@@ -25,11 +25,43 @@
|
||||
|
||||
%%% Types
|
||||
|
||||
-export_type([aaci/0]).
|
||||
-export_type([aaci/0, annotated_type/0, erlang_repr/0]).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-type aaci() :: {aaci, term(), term(), term()}.
|
||||
-type aaci() :: {aaci, string(), #{string() => function_spec()}, #{string() => typedef()}}.
|
||||
-type function_spec() :: {[{string(), annotated_type()}], annotated_type()}.
|
||||
-type typedef() :: {[string()], typedef_rhs()}.
|
||||
|
||||
-type annotated_type() :: {opaque_type(), already_normalized | opaque_type(), builtin_type(annotated_type())}.
|
||||
-type builtin_type(T) :: {bytes, [integer() | any]}
|
||||
| {record, [{string(), T}]}
|
||||
| {variant, [{string(), [T]}]}
|
||||
| {tuple, [T]}
|
||||
| {list, [T]}
|
||||
| {map, [T]}
|
||||
| integer
|
||||
| boolean
|
||||
| bits
|
||||
| char
|
||||
| string
|
||||
| address
|
||||
| signature
|
||||
| contract
|
||||
| channel
|
||||
| unknown_type.
|
||||
|
||||
-type opaque_type() :: string() | {string(), [opaque_type()]} | builtin_type(opaque_type()).
|
||||
|
||||
-type typedef_rhs() :: {var, string()} | string() | {string(), [opaque_type()]} | builtin_type(typedef_rhs()).
|
||||
|
||||
-type erlang_repr() :: integer()
|
||||
| string()
|
||||
| boolean()
|
||||
| binary()
|
||||
| tuple() % Tuples, variants, or raw addresses
|
||||
| [erlang_repr()]
|
||||
| #{erlang_repr() => erlang_repr()}.
|
||||
|
||||
%%% ACI/AACI
|
||||
|
||||
@@ -47,6 +79,9 @@ prepare_from_file(Path) ->
|
||||
Error -> Error
|
||||
end.
|
||||
|
||||
-spec prepare(ACI) -> AACI
|
||||
when ACI :: term(),
|
||||
AACI :: aaci().
|
||||
|
||||
prepare(ACI) ->
|
||||
% We want to take the types represented by the ACI, things like N1.T(N2.T),
|
||||
@@ -66,6 +101,12 @@ prepare(ACI) ->
|
||||
|
||||
{aaci, Name, Specs, TypeDefs}.
|
||||
|
||||
-spec convert_aci_types(ACI) -> {Name, OpaqueSpecs, TypeDefs}
|
||||
when ACI :: term(),
|
||||
Name :: string(),
|
||||
OpaqueSpecs :: [{string(), [{string(), opaque_type()}], opaque_type()}],
|
||||
TypeDefs :: #{string() => typedef()}.
|
||||
|
||||
convert_aci_types(ACI) ->
|
||||
% Find the main contract, so we can get the specifications of its
|
||||
% entrypoints.
|
||||
@@ -134,6 +175,12 @@ convert_typedefs_loop([Next | Rest], NamePrefix, Converted) ->
|
||||
Def = opaque_type(Params, DefACI),
|
||||
convert_typedefs_loop(Rest, NamePrefix, [Converted, {Name, Params, Def}]).
|
||||
|
||||
-spec collect_opaque_types(Tree, TypeDefs) -> TypeDefs
|
||||
when Tree :: typedef_tree(),
|
||||
TypeDefs :: #{string() => typedef()}.
|
||||
|
||||
-type typedef_tree() :: {string(), [string()], typedef_rhs()} | list(typedef_tree()).
|
||||
|
||||
collect_opaque_types([], Types) ->
|
||||
Types;
|
||||
collect_opaque_types([L | R], Types) ->
|
||||
@@ -144,6 +191,11 @@ collect_opaque_types({Name, Params, Def}, Types) ->
|
||||
|
||||
%%% ACI Type -> Opaque Type
|
||||
|
||||
-spec opaque_type(Params, ACIType) -> Opaque
|
||||
when Params :: [string()],
|
||||
ACIType :: binary() | map(),
|
||||
Opaque :: typedef_rhs().
|
||||
|
||||
% Convert an ACI type defintion/spec into the 'opaque type' representation that
|
||||
% our dereferencing algorithms can reason about.
|
||||
opaque_type(Params, NameBin) when is_binary(NameBin) ->
|
||||
@@ -171,6 +223,8 @@ opaque_type(Params, Pair) when is_map(Pair) ->
|
||||
[{Name, TypeArgs}] = maps:to_list(Pair),
|
||||
{opaque_type_name(Name), [opaque_type(Params, Arg) || Arg <- TypeArgs]}.
|
||||
|
||||
-spec opaque_type_name(binary()) -> atom() | string().
|
||||
|
||||
% Atoms for any builtins that aren't qualified by a namespace in Sophia.
|
||||
% Everything else stays as a string, user-defined or not.
|
||||
opaque_type_name(<<"int">>) -> integer;
|
||||
@@ -280,6 +334,12 @@ annotate_function_specs([{Name, ArgsOpaque, ResultOpaque} | Rest], Types, Specs)
|
||||
NewSpecs = maps:put(Name, {Args, Result}, Specs),
|
||||
annotate_function_specs(Rest, Types, NewSpecs).
|
||||
|
||||
-spec annotate_type(Opaque, Types) -> {ok, Annotated} | {error, Reason}
|
||||
when Opaque :: opaque_type(),
|
||||
Types :: #{string() => typedef()},
|
||||
Annotated :: annotated_type(),
|
||||
Reason :: none().
|
||||
|
||||
annotate_type(T, Types) ->
|
||||
case normalize_opaque_type(T, Types) of
|
||||
{ok, AlreadyNormalized, NOpaque, NExpanded} ->
|
||||
@@ -445,6 +505,14 @@ substitute_opaque_types(Bindings, Types) ->
|
||||
|
||||
%%% Erlang to FATE
|
||||
|
||||
-spec erlang_args_to_fate(VarTypes, Terms) -> {ok, FATE} | {error, Errors}
|
||||
when VarTypes :: [{string(), annotated_type()}],
|
||||
Terms :: [erlang_repr()],
|
||||
FATE :: gmb_fate_data:fate_type(),
|
||||
Errors :: [{Reason, [PathStep]}],
|
||||
Reason :: term(),
|
||||
PathStep :: term().
|
||||
|
||||
erlang_args_to_fate(VarTypes, Terms) ->
|
||||
DefLength = length(VarTypes),
|
||||
ArgLength = length(Terms),
|
||||
@@ -454,6 +522,14 @@ erlang_args_to_fate(VarTypes, Terms) ->
|
||||
DefLength < ArgLength -> {error, too_many_args}
|
||||
end.
|
||||
|
||||
-spec erlang_to_fate(Type, Erlang) -> {ok, FATE} | {error, Errors}
|
||||
when Type :: annotated_type(),
|
||||
FATE :: gmb_fate_data:fate_type(),
|
||||
Erlang :: erlang_repr(),
|
||||
Errors :: [{Reason, [PathStep]}],
|
||||
Reason :: term(),
|
||||
PathStep :: term().
|
||||
|
||||
erlang_to_fate({_, _, integer}, S) when is_integer(S) ->
|
||||
{ok, S};
|
||||
erlang_to_fate({O, N, integer}, S) when is_list(S) ->
|
||||
@@ -794,6 +870,14 @@ coerce_direction(Type, Term, to_fate) ->
|
||||
coerce_direction(Type, Term, from_fate) ->
|
||||
fate_to_erlang(Type, Term).
|
||||
|
||||
-spec fate_to_erlang(Type, FATE) -> {ok, Erlang} | {error, Errors}
|
||||
when Type :: annotated_type(),
|
||||
FATE :: gmb_fate_data:fate_type(),
|
||||
Erlang :: erlang_repr(),
|
||||
Errors :: [{Reason, [PathStep]}],
|
||||
Reason :: term(),
|
||||
PathStep :: term().
|
||||
|
||||
fate_to_erlang({_, _, integer}, S) when is_integer(S) ->
|
||||
{ok, S};
|
||||
fate_to_erlang({_, _, address}, {address, Bin}) ->
|
||||
|
||||
+29
-5
@@ -10,14 +10,20 @@
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
|
||||
-spec parse_literal(String) -> Result
|
||||
when String :: string(),
|
||||
Result :: {ok, gmb_fate_data:fate_type()}
|
||||
| {error, Reason :: term()}.
|
||||
-spec parse_literal(Sophia) -> {ok, FATE} | {error, Reason}
|
||||
when Sophia :: string(),
|
||||
FATE :: gmb_fate_data:fate_type(),
|
||||
Reason :: term().
|
||||
|
||||
parse_literal(String) ->
|
||||
parse_literal(unknown_type(), String).
|
||||
|
||||
-spec parse_literal(Type, Sophia) -> {ok, FATE} | {error, Reason}
|
||||
when Type :: hz_aaci:annotated_type(),
|
||||
Sophia :: string(),
|
||||
FATE :: gmb_fate_data:fate_type(),
|
||||
Reason :: term().
|
||||
|
||||
parse_literal(Type, String) ->
|
||||
case parse_expression(Type, {1, 1}, String) of
|
||||
{ok, {Result, NewPos, NewString}} ->
|
||||
@@ -917,16 +923,34 @@ wrap_error(Reason, _) -> Reason.
|
||||
|
||||
%%% Pretty Printing
|
||||
|
||||
-spec fate_to_list(FATE) -> Sophia
|
||||
when FATE :: gmb_fate_data:fate_type(),
|
||||
Sophia :: string().
|
||||
|
||||
fate_to_list(Term) ->
|
||||
fate_to_list(unknown_type(), Term).
|
||||
|
||||
-spec fate_to_list(Type, FATE) -> Sophia
|
||||
when Type :: hz_aaci:annotated_type(),
|
||||
FATE :: gmb_fate_data:fate_type(),
|
||||
Sophia :: string().
|
||||
|
||||
fate_to_list(Type, Term) ->
|
||||
IOList = fate_to_iolist(Type, Term),
|
||||
unicode:characters_to_list(IOList).
|
||||
|
||||
-spec fate_to_iolist(FATE) -> Sophia
|
||||
when FATE :: gmb_fate_data:fate_type(),
|
||||
Sophia :: iolist().
|
||||
|
||||
fate_to_iolist(Term) ->
|
||||
fate_to_iolist(unknown_type(), Term).
|
||||
|
||||
-spec fate_to_iolist(Type, FATE) -> Sophia
|
||||
when Type :: hz_aaci:annotated_type(),
|
||||
FATE :: gmb_fate_data:fate_type(),
|
||||
Sophia :: iolist().
|
||||
|
||||
% Special case for singleton records, since they are erased during compilation.
|
||||
fate_to_iolist({_, _, {record, [{FieldName, FieldType}]}}, Term) ->
|
||||
singleton_record_to_iolist(FieldName, FieldType, Term);
|
||||
@@ -1430,6 +1454,6 @@ singleton_test() ->
|
||||
|
||||
% Also if we wanted an integer, the singleton is NOT dropped, so is also an
|
||||
% error.
|
||||
{error, {expected_close_paren, 1, 3}} = parse_literal({integer, alread_normalized, integer}, "(1,)"),
|
||||
{error, {expected_close_paren, 1, 3}} = parse_literal({integer, already_normalized, integer}, "(1,)"),
|
||||
|
||||
ok.
|
||||
|
||||
Reference in New Issue
Block a user