gmbytecode/src/aeb_fate_data.erl
2019-06-28 11:22:33 +02:00

362 lines
11 KiB
Erlang

%% FATE data representation.
%%
-include("aeb_fate_data.hrl").
-module(aeb_fate_data).
-type fate_integer() :: ?FATE_INTEGER_T.
-type fate_boolean() :: ?FATE_BOOLEAN_T.
-type fate_nil() :: ?FATE_NIL_T.
-type fate_list() :: ?FATE_LIST_T.
-type fate_unit() :: ?FATE_UNIT_T.
-type fate_map() :: ?FATE_MAP_T.
-type fate_string() :: ?FATE_STRING_T.
-type fate_address() :: ?FATE_ADDRESS_T.
-type fate_hash() :: ?FATE_BYTES_T(32).
-type fate_signature() :: ?FATE_BYTES_T(64).
-type fate_contract() :: ?FATE_CONTRACT_T.
-type fate_oracle() :: ?FATE_ORACLE_T.
-type fate_channel() :: ?FATE_CHANNEL_T.
-type fate_variant() :: ?FATE_VARIANT_T.
-type fate_tuple() :: ?FATE_TUPLE_T.
-type fate_bits() :: ?FATE_BITS_T.
-type fate_type_type() :: integer
| boolean
| {list, fate_type_type()}
| {map, fate_type_type(), fate_type_type()}
| {tuple, [fate_type_type()]}
| address
| hash
| signature
| contract
| oracle
| channel
| bits
| string
| {variant, [fate_type_type()]}.
-type fate_type() ::
fate_boolean()
| fate_integer()
| fate_nil()
| fate_list()
| fate_unit()
| fate_tuple()
| fate_string()
| fate_address()
| fate_hash()
| fate_signature()
| fate_contract()
| fate_oracle()
| fate_channel()
| fate_variant()
| fate_map()
| fate_bits().
-export_type([fate_type/0
, fate_boolean/0
, fate_integer/0
, fate_nil/0
, fate_list/0
, fate_unit/0
, fate_tuple/0
, fate_string/0
, fate_address/0
, fate_hash/0
, fate_signature/0
, fate_contract/0
, fate_oracle/0
, fate_channel/0
, fate_variant/0
, fate_map/0
, fate_bits/0
, fate_type_type/0
]).
-export([ make_integer/1
, make_boolean/1
, make_list/1
, make_variant/3
, make_tuple/1
, make_string/1
, make_map/1
, make_address/1
, make_bytes/1
, make_hash/1
, make_signature/1
, make_contract/1
, make_oracle/1
, make_oracle_query/1
, make_channel/1
, make_bits/1
, make_unit/0
, make_typerep/1
]).
-export([
elt/2
, lt/2
, format/1
, ordinal/1]).
make_boolean(true) -> ?FATE_TRUE;
make_boolean(false) -> ?FATE_FALSE.
make_list([]) -> ?FATE_NIL;
make_list(L) -> ?MAKE_FATE_LIST(L).
make_unit() -> ?FATE_UNIT.
make_tuple(T) -> ?FATE_TUPLE(T).
make_map(M) -> ?MAKE_FATE_MAP(M).
make_address(X) -> ?FATE_ADDRESS(X).
make_bytes(X) -> ?FATE_BYTES(X).
make_hash(X) -> make_bytes(X).
make_signature(X) -> make_bytes(X).
make_contract(X) -> ?FATE_CONTRACT(X).
make_oracle(X) -> ?FATE_ORACLE(X).
make_oracle_query(X) -> ?FATE_ORACLE_Q(X).
make_channel(X) -> ?FATE_CHANNEL(X).
make_integer(I) when is_integer(I) -> ?MAKE_FATE_INTEGER(I).
make_bits(I) when is_integer(I) -> ?FATE_BITS(I).
make_string(S) when is_list(S) ->
?FATE_STRING(iolist_to_binary(S));
make_string(S) when is_binary(S) -> ?FATE_STRING(S).
make_typerep(T) -> ?FATE_TYPEREP(T).
%% Tag points to the selected variant (zero based)
%% The arity of this variant is read from the list of provided arities
%% and should match the size of the given tuple.
make_variant(Arities, Tag, Values) ->
Arities = [A || A <- Arities, is_integer(A), A < 256],
Size = length(Arities),
if is_integer(Tag)
, 0 =< Tag
, Tag < Size
, is_tuple(Values) ->
Arity = lists:nth(Tag+1, Arities),
if size(Values) =:= Arity ->
?FATE_VARIANT(Arities, Tag, Values)
end
end.
-spec format(fate_type()) -> iolist().
format(I) when ?IS_FATE_INTEGER(I) -> integer_to_list(?MAKE_FATE_INTEGER(I));
format(?FATE_TRUE) -> "true";
format(?FATE_FALSE) -> "false";
format(?FATE_NIL) -> "[]";
format(L) when ?IS_FATE_LIST(L) -> format_list(?FATE_LIST_VALUE(L));
format(?FATE_UNIT) -> "()";
format(?FATE_TUPLE(T)) ->
["( ", lists:join(", ", [ format(E) || E <- erlang:tuple_to_list(T)]), " )"];
format(S) when ?IS_FATE_STRING(S) -> ["\"", S, "\""];
format(?FATE_BITS(B)) when B >= 0 ->
["<", format_bits(B, "") , ">"];
format(?FATE_BITS(B)) when B < 0 ->
["!< ", format_nbits(-B-1, "") , " >"];
format(?FATE_VARIANT(Arities, Tag, T)) ->
["(| ",
lists:join("| ",
[format_arities(Arities),
integer_to_list(Tag) |
[format(make_tuple(T))]]),
" |)"];
format(M) when ?IS_FATE_MAP(M) ->
["{ ", format_kvs(maps:to_list(?FATE_MAP_VALUE(M))), " }"];
format(?FATE_BYTES(X)) -> ["#", base64:encode(X)];
format(?FATE_ADDRESS(X)) ->
["@", aeser_api_encoder:encode(account_pubkey, X)];
format(?FATE_CONTRACT(X)) ->
["@", aeser_api_encoder:encode(contract_pubkey, X)];
format(?FATE_ORACLE(X)) ->
["@", aeser_api_encoder:encode(oracle_pubkey, X)];
format(?FATE_ORACLE_Q(X)) ->
["@", aeser_api_encoder:encode(oracle_query_id, X)];
format(?FATE_CHANNEL(X)) ->
["@", aeser_api_encoder:encode(channel, X)];
format(?FATE_TYPEREP(X)) ->
["'", io_lib:format("~p", [X])];
format(V) -> exit({not_a_fate_type, V}).
format_bits(0, Acc) -> Acc;
format_bits(N, Acc) ->
Bit = $0 + (N band 1),
format_bits(N bsr 1, [Bit|Acc]).
format_nbits(0, Acc) -> Acc;
format_nbits(N, Acc) ->
Bit = $1 - (N band 1),
format_nbits(N bsr 1, [Bit|Acc]).
format_arities(Arities) ->
["[ ", lists:join(", ", [integer_to_list(E) || E <- Arities]), " ]"].
format_list(List) ->
["[ ", lists:join(", ", [format(E) || E <- List]), " ]"].
format_kvs(List) ->
lists:join(", ", [ [format(K), " => ", format(V)] || {K, V} <- List]).
%% Total order of FATE terms.
%% Integers < Booleans < Address < Channel < Contract < Oracle
%% < Hash < Signature < Bits < String < Tuple < Map < List < Variant
-define(ORD_INTEGER , 0).
-define(ORD_BOOLEAN , 1).
-define(ORD_ADDRESS , 2).
-define(ORD_CHANNEL , 3).
-define(ORD_CONTRACT , 4).
-define(ORD_ORACLE , 5).
-define(ORD_BYTES , 6).
-define(ORD_BITS , 7).
-define(ORD_STRING , 8).
-define(ORD_TUPLE , 9).
-define(ORD_MAP , 10).
-define(ORD_LIST , 11).
-define(ORD_VARIANT , 12).
-define(ORD_ORACLE_Q , 13).
-spec ordinal(fate_type()) -> integer().
ordinal(T) when ?IS_FATE_INTEGER(T) -> ?ORD_INTEGER;
ordinal(T) when ?IS_FATE_BOOLEAN(T) -> ?ORD_BOOLEAN;
ordinal(T) when ?IS_FATE_ADDRESS(T) -> ?ORD_ADDRESS;
ordinal(T) when ?IS_FATE_CHANNEL(T) -> ?ORD_CHANNEL;
ordinal(T) when ?IS_FATE_CONTRACT(T) -> ?ORD_CONTRACT;
ordinal(T) when ?IS_FATE_ORACLE(T) -> ?ORD_ORACLE;
ordinal(T) when ?IS_FATE_BYTES(T) -> ?ORD_BYTES;
ordinal(T) when ?IS_FATE_BITS(T) -> ?ORD_BITS;
ordinal(T) when ?IS_FATE_STRING(T) -> ?ORD_STRING;
ordinal(T) when ?IS_FATE_TUPLE(T) -> ?ORD_TUPLE;
ordinal(T) when ?IS_FATE_MAP(T) -> ?ORD_MAP;
ordinal(T) when ?IS_FATE_LIST(T) -> ?ORD_LIST;
ordinal(T) when ?IS_FATE_VARIANT(T) -> ?ORD_VARIANT;
ordinal(T) when ?IS_FATE_ORACLE_Q(T) -> ?ORD_ORACLE_Q.
-spec lt(fate_type(), fate_type()) -> boolean().
lt(A, B) ->
O1 = ordinal(A),
O2 = ordinal(B),
if O1 == O2 -> lt(O1, A, B);
true -> O1 < O2
end.
%% Integers are ordered as usual.
lt(?ORD_INTEGER, A, B) when ?IS_FATE_INTEGER(A), ?IS_FATE_INTEGER(B) ->
?FATE_INTEGER_VALUE(A) < ?FATE_INTEGER_VALUE(B);
%% false is smaller than true (true also for erlang booleans).
lt(?ORD_BOOLEAN, A, B) when ?IS_FATE_BOOLEAN(A), ?IS_FATE_BOOLEAN(B) ->
?FATE_BOOLEAN_VALUE(A) < ?FATE_BOOLEAN_VALUE(B);
lt(?ORD_BITS, A, B) when ?IS_FATE_BITS(A), ?IS_FATE_BITS(B) ->
BitsA = ?FATE_BITS_VALUE(A),
BitsB = ?FATE_BITS_VALUE(B),
if BitsA < 0 ->
if BitsB < 0 -> BitsA < BitsB;
true -> false
end;
BitsB < 0 ->
true;
true -> BitsA < BitsB
end;
lt(?ORD_STRING,?FATE_STRING(A), ?FATE_STRING(B)) ->
SizeA = size(A),
SizeB = size(B),
case SizeA - SizeB of
0 -> A < B;
N -> N < 0
end;
lt(?ORD_TUPLE,?FATE_TUPLE(A), ?FATE_TUPLE(B)) ->
SizeA = size(A),
SizeB = size(B),
case SizeA - SizeB of
0 -> tuple_elements_lt(0, A, B, SizeA);
N -> N < 0
end;
lt(?ORD_MAP, ?FATE_MAP_VALUE(A), ?FATE_MAP_VALUE(B)) ->
SizeA = maps:size(A),
SizeB = maps:size(B),
case SizeA - SizeB of
0 -> maps_lt(A, B);
N -> N < 0
end;
lt(?ORD_LIST, ?FATE_LIST_VALUE(_), ?FATE_LIST_VALUE([])) -> false;
lt(?ORD_LIST, ?FATE_LIST_VALUE([]), ?FATE_LIST_VALUE(_)) -> true;
lt(?ORD_LIST, ?FATE_LIST_VALUE([A|RA]), ?FATE_LIST_VALUE([B|RB])) ->
O1 = ordinal(A),
O2 = ordinal(B),
if O1 == O2 ->
if A == B -> lt(RA, RB);
true -> A < B
end;
true -> O1 < O2
end;
lt(?ORD_VARIANT, ?FATE_VARIANT(AritiesA, TagA, TA),
?FATE_VARIANT(AritiesB, TagB, TB)) ->
if length(AritiesA) < length(AritiesB) -> true;
length(AritiesA) > length(AritiesB) -> false;
true ->
if AritiesA < AritiesB -> true;
AritiesA > AritiesB -> false;
true ->
if TagA < TagB -> true;
TagA > TagB -> false;
true -> lt(make_tuple(TA), make_tuple(TB))
end
end
end;
lt(_, A, B) -> A < B.
tuple_elements_lt(N,_A,_B, N) ->
false;
tuple_elements_lt(N, A, B, Size) ->
E = N + 1,
EA = element(E, A),
EB = element(E, B),
if EA =:= EB -> tuple_elements_lt(E, A, B, Size);
true -> lt(EA, EB)
end.
maps_lt(A, B) ->
IA = maps_iterator(A),
IB = maps_iterator(B),
maps_i_lt(IA, IB).
maps_i_lt(IA, IB) ->
case {maps_next(IA), maps_next(IB)} of
{none, none} -> false;
{_, none} -> false;
{none, _} -> true;
{{KA1, VA1, IA2}, {KB1, VB1, IB2}} ->
case lt(KA1, KB1) of
true -> true;
false ->
case lt(KB1, KA1) of
true -> false;
false ->
case lt(VA1, VB1) of
true -> true;
false ->
case lt(VB1, VA1) of
true -> false;
false ->
maps_i_lt(IA2, IB2)
end
end
end
end
end.
maps_iterator(M) -> lists:sort(fun ({K1,_}, {K2,_}) -> lt(K1, K2) end, maps:to_list(M)).
maps_next([]) -> none;
maps_next([{K,V}|Rest]) -> {K, V, Rest}.
-spec elt(fate_type(), fate_type()) -> boolean().
elt(A, A) -> true;
elt(A, B) ->
R = lt(A, B),
R.