From 662b611e6dc7c6d334a600f7caa331b3df3fa9b5 Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Fri, 29 Mar 2019 14:52:22 +0100 Subject: [PATCH] New representation of variant values. (#23) * New representation of variant values. * Specify type of elments (byte) in arities list. --- README.md | 4 +-- include/aeb_fate_data.hrl | 6 ++-- src/aeb_fate_asm.erl | 42 ++++++++++++++++----------- src/aeb_fate_data.erl | 32 +++++++++++++-------- src/aeb_fate_encoding.erl | 44 +++++++++++++++++++++-------- test/aeb_serialize_test.erl | 15 +++++----- test/asm_code/all_instructions.fate | 10 +++---- test/asm_code/immediates.fate | 4 +-- 8 files changed, 98 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 41ecff0..3ec4343 100644 --- a/README.md +++ b/README.md @@ -101,8 +101,8 @@ Immediate values can be of 11 types: `()` `(1, "foo")` -9. Variants: (| Size | Tag | ( Elements ) |) - `(| 42 | 12 | ( "foo", 12) |)` +9. Variants: (| [Arities] | Tag | ( Elements ) |) + `(| [1,3,5,2] | 3 | ( "foo", 12) |)` 10. Hashes: #{base64char}+ `#AQIDCioLFQ==` diff --git a/include/aeb_fate_data.hrl b/include/aeb_fate_data.hrl index d82c43c..c439abd 100644 --- a/include/aeb_fate_data.hrl +++ b/include/aeb_fate_data.hrl @@ -13,7 +13,7 @@ -define(FATE_ORACLE_T, {oracle, <<_:256>>}). -define(FATE_NAME_T, {name, <<_:256>>}). -define(FATE_CHANNEL_T, {channel, <<_:256>>}). --define(FATE_VARIANT_T, {variant, ?FATE_BYTE_T, ?FATE_BYTE_T, tuple()}). +-define(FATE_VARIANT_T, {variant, [byte()], ?FATE_BYTE_T, tuple()}). -define(FATE_VOID_T, void). -define(FATE_TUPLE_T, {tuple, tuple()}). -define(FATE_BITS_T, {bits, integer()}). @@ -34,7 +34,7 @@ -define(IS_FATE_VARIANT(X), (is_tuple(X) andalso (variant == element(1, X) - andalso is_integer(element(2, X)) + andalso is_list(element(2, X)) andalso is_integer(element(3, X)) andalso is_tuple(element(4, X)) ))). @@ -71,7 +71,7 @@ -define(FATE_VOID, void). -define(FATE_EMPTY_STRING, <<>>). -define(FATE_STRING(S), S). --define(FATE_VARIANT(Size, Tag,T), {variant, Size, Tag, T}). +-define(FATE_VARIANT(Arity, Tag,T), {variant, Arity, Tag, T}). -define(MAKE_FATE_INTEGER(X), X). -define(MAKE_FATE_LIST(X), X). diff --git a/src/aeb_fate_asm.erl b/src/aeb_fate_asm.erl index 708dea5..5d47d28 100644 --- a/src/aeb_fate_asm.erl +++ b/src/aeb_fate_asm.erl @@ -64,8 +64,8 @@ %%% 8. Tuples ( Elements ) %%% () %%% (1, "foo") -%%% 9. Variants: (| Size | Tag | ( Elements ) |) -%%% (| 42 | 12 | ( "foo", 12) |) +%%% 9. Variants: (| [Arities] | Tag | ( Elements ) |) +%%% (| [0,1,2] | 2 | ( "foo", 12) |) %%% 10. Hashes: #{base64char}+ %%% #AQIDCioLFQ== %%% 11. Signatures: $sg_{base58char}+ @@ -808,8 +808,8 @@ to_bytecode([{'(',_line}|Rest], Address, Env, Code, Opts) -> Tuple = aeb_fate_data:make_tuple(list_to_tuple(Elements)), to_bytecode(Rest2, Address, Env, [{immediate, Tuple}|Code], Opts); to_bytecode([{start_variant,_line}|_] = Tokens, Address, Env, Code, Opts) -> - {Size, Tag, Values, Rest} = parse_variant(Tokens), - Variant = aeb_fate_data:make_variant(Size, Tag, Values), + {Arities, Tag, Values, Rest} = parse_variant(Tokens), + Variant = aeb_fate_data:make_variant(Arities, Tag, Values), to_bytecode(Rest, Address, Env, [{immediate, Variant}|Code], Opts); to_bytecode([{bits,_line, Bits}|Rest], Address, Env, Code, Opts) -> to_bytecode(Rest, Address, Env, @@ -870,14 +870,25 @@ parse_tuple(Tokens) -> parse_variant([{start_variant,_line} - , {int,_line, Size} - , {'|',_} - , {int,_line, Tag} - , {'|',_} - , {'(',_} - | Rest]) when (Size > 0), (Tag < Size) -> - {Elements , [{end_variant, _} | Rest2]} = parse_tuple(Rest), - {Size, Tag, list_to_tuple(Elements), Rest2}. + , {'[', _line} + | Rest]) -> + {Arities, Rest2} = parse_list(Rest), + %% Make sure Arities is a list of bytes. + Arities = [A || A <- Arities, + is_integer(A), A < 256], + + [{'|',_} + , {int,_line, Tag} + , {'|',_} + , {'(',_} | Rest3] = Rest2, + {Elements , [{end_variant, _} | Rest4]} = parse_tuple(Rest3), + Size = length(Arities), + if 0 =< Tag, Tag < Size -> + Arity = lists:nth(Tag+1, Arities), + if length(Elements) =:= Arity -> + {Arities, Tag, list_to_tuple(Elements), Rest4} + end + end. parse_value([{int,_line, Int} | Rest]) -> {Int, Rest}; @@ -890,8 +901,8 @@ parse_value([{'(',_line} | Rest]) -> parse_value([{bits,_line, Bits} | Rest]) -> {aeb_fate_data:make_bits(Bits), Rest}; parse_value([{start_variant,_line}|_] = Tokens) -> - {Size, Tag, Values, Rest} = parse_variant(Tokens), - Variant = aeb_fate_data:make_variant(Size, Tag, Values), + {Arities, Tag, Values, Rest} = parse_variant(Tokens), + Variant = aeb_fate_data:make_variant(Arities, Tag, Values), {Variant, Rest}; parse_value([{string,_line, String} | Rest]) -> {aeb_fate_data:make_string(String), Rest}; @@ -956,8 +967,7 @@ to_type([{'{', _} , {id, _, "variant"} , {',', _} , {'[', _} - | Rest]) -> - %% TODO: Error handling + | Rest]) -> {ElementTypes, [{'}', _}| Rest2]} = to_list_of_types(Rest), {{variant, ElementTypes}, Rest2}. diff --git a/src/aeb_fate_data.erl b/src/aeb_fate_data.erl index 83aa247..dcc40f2 100644 --- a/src/aeb_fate_data.erl +++ b/src/aeb_fate_data.erl @@ -34,7 +34,7 @@ | name | channel | bits - | {variant, integer()}. + | {variant, list(), integer()}. -type fate_type() :: @@ -120,12 +120,18 @@ make_string(S) when is_list(S) -> ?FATE_STRING(list_to_binary(lists:flatten(S))); make_string(S) when is_binary(S) -> ?FATE_STRING(S). -make_variant(Size, Tag, Values) when is_integer(Size), is_integer(Tag) - , 0 =< Size - , 0 =< Tag - , Tag < Size - , is_tuple(Values) -> - ?FATE_VARIANT(Size, Tag, Values). +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. tuple_to_list(?FATE_TUPLE(T)) -> erlang:tuple_to_list(T). @@ -160,7 +166,7 @@ encode({channel, B}) when is_binary(B) -> make_channel(B); encode({channel, I}) when is_integer(I) -> B = <>, make_channel(B); encode({channel, S}) when is_list(S) -> make_channel(encode_address(channel, S)); -encode({variant, Size, Tag, Values}) -> make_variant(Size, Tag, Values); +encode({variant, Arities, Tag, Values}) -> make_variant(Arities, Tag, Values); encode(Term) when is_integer(Term) -> make_integer(Term); encode(Term) when is_boolean(Term) -> make_boolean(Term); encode(Term) when is_list(Term) -> make_list([encode(E) || E <- Term]); @@ -185,7 +191,7 @@ decode(?FATE_NAME(X)) -> {name, X}; decode(?FATE_CHANNEL(X)) -> {channel, X}; decode(?FATE_BITS(Bits)) -> {bits, Bits}; decode(?FATE_TUPLE(T)) -> erlang:list_to_tuple([decode(E) || E <- T]); -decode(?FATE_VARIANT(Size, Tag, Values)) -> {variant, Size, Tag, Values}; +decode(?FATE_VARIANT(Arities, Tag, Values)) -> {variant, Arities, Tag, Values}; decode(S) when ?IS_FATE_STRING(S) -> binary_to_list(S); decode(M) when ?IS_FATE_MAP(M) -> maps:from_list([{decode(K), decode(V)} || {K, V} <- maps:to_list(M)]). @@ -204,10 +210,12 @@ format(?FATE_BITS(B)) when B >= 0 -> ["<", format_bits(B, "") , ">"]; format(?FATE_BITS(B)) when B < 0 -> ["!< ", format_nbits(-B-1, "") , " >"]; -format(?FATE_VARIANT(Size, Tag, T)) -> +format(?FATE_VARIANT(Arities, Tag, T)) -> ["(| ", - lists:join("| ", [integer_to_list(Size), integer_to_list(Tag) | - [format(make_tuple(T))]]), + lists:join("| ", + [io_lib:format("~p", [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))), " }"]; diff --git a/src/aeb_fate_encoding.erl b/src/aeb_fate_encoding.erl index 7b68826..215624a 100644 --- a/src/aeb_fate_encoding.erl +++ b/src/aeb_fate_encoding.erl @@ -28,8 +28,6 @@ %% TODO: %% * Make the code production ready. %% (add tests, document exported functions). -%% * Handle Variant types better. -%% * Handle type representations. %% * Handle instructions. %% %% ------------------------------------------------------------------------ @@ -62,7 +60,7 @@ -define(TYPE_BITS , 2#01010111). %% 0101 0111 - Bits typedef -define(TYPE_MAP , 2#01100111). %% 0110 0111 | Type | Type -define(TYPE_STRING , 2#01110111). %% 0111 0111 - string typedef --define(TYPE_VARIANT , 2#10000111). %% 1000 0111 | Size | [Type] +-define(TYPE_VARIANT , 2#10000111). %% 1000 0111 | [Arities] | [Type] %% 1001 0111 %% 1010 0111 %% 1011 0111 @@ -82,7 +80,7 @@ -define(FALSE , 2#01111111). %% 0111 1111 %% %% 1000 1111 - FREE (Possibly for bytecode in the future.) -define(OBJECT , 2#10011111). %% 1001 1111 | ObjectType | RLP encoded Array --define(VARIANT , 2#10101111). %% 1010 1111 | encoded size | encoded tag | [encoded values] +-define(VARIANT , 2#10101111). %% 1010 1111 | [encoded arities] | encoded tag | [encoded values] -define(NIL , 2#10111111). %% 1011 1111 - Empty list -define(NEG_BITS , 2#11001111). %% 1100 1111 | RLP encoded integer (infinite 1:s bitfield) -define(EMPTY_MAP , 2#11011111). %% 1101 1111 @@ -174,11 +172,24 @@ serialize(Map) when ?IS_FATE_MAP(Map) -> <>; -serialize(?FATE_VARIANT(Size, Tag, Values)) when 0 < Size, Size < 256, - 0 =< Tag, Tag < Size -> - <>. +serialize(?FATE_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 -> + EncodedArities = aeser_rlp:encode(list_to_binary(Arities)), + <> + end + end. + %% ----------------------------------------------------- @@ -363,11 +374,20 @@ deserialize2(<>) -> {List, Rest2} = deserialize_elements(2*Size, Rest1), Map = insert_kv(List, #{}), {?MAKE_FATE_MAP(Map), Rest2}; -deserialize2(<>) -> +deserialize2(<>) -> + {AritiesBin, <>} = aeser_rlp:decode_one(Rest), + Arities = binary_to_list(AritiesBin), + Size = length(Arities), if Tag > Size -> exit({too_large_tag_in_variant, Tag, Size}); true -> - {?FATE_TUPLE(T), Rest2} = deserialize2(Rest), - {?FATE_VARIANT(Size, Tag, T), Rest2} + {?FATE_TUPLE(T), Rest3} = deserialize2(Rest2), + Arity = lists:nth(Tag+1, Arities), + NumElements = size(T), + if NumElements =/= Arity -> + exit({tag_does_not_match_type_in_variant, Tag, Arity}); + true -> + {?FATE_VARIANT(Arities, Tag, T), Rest3} + end end. insert_kv([], M) -> M; diff --git a/test/aeb_serialize_test.erl b/test/aeb_serialize_test.erl index ec8e88a..c96c006 100644 --- a/test/aeb_serialize_test.erl +++ b/test/aeb_serialize_test.erl @@ -77,12 +77,13 @@ sources() -> aeb_fate_data:make_bits(1), aeb_fate_data:make_bits(-1), aeb_fate_data:make_list(make_int_list(65)), - aeb_fate_data:make_variant(2, 0, {FortyTwo}), - aeb_fate_data:make_variant(2, 1, {}), - aeb_fate_data:make_list([aeb_fate_data:make_variant(3, 0, {})]), - aeb_fate_data:make_variant(255, 254, {}), - aeb_fate_data:make_variant(5, 3, {aeb_fate_data:make_boolean(true), - aeb_fate_data:make_list(make_int_list(3)), - aeb_fate_data:make_string(<<"foo">>)}) + aeb_fate_data:make_variant([1,2,3], 0, {FortyTwo}), + aeb_fate_data:make_variant([2,0], 1, {}), + aeb_fate_data:make_list([aeb_fate_data:make_variant([0,0,0], 0, {})]), + aeb_fate_data:make_variant([0|| _<-lists:seq(1,255)], 254, {}), + aeb_fate_data:make_variant([0,1,2,3,4,5], + 3, {aeb_fate_data:make_boolean(true), + aeb_fate_data:make_list(make_int_list(3)), + aeb_fate_data:make_string(<<"foo">>)}) ]. diff --git a/test/asm_code/all_instructions.fate b/test/asm_code/all_instructions.fate index e585471..d80797c 100644 --- a/test/asm_code/all_instructions.fate +++ b/test/asm_code/all_instructions.fate @@ -70,7 +70,7 @@ FUNCTION foo () : {tuple, []} AND var255 0x294a24f6 var189 - OR (| 2 | 0 | ( (), (42) ) |) arg168 var107 + OR (| [2,0] | 0 | ( (), (42) ) |) arg168 var107 NOT arg124 a @@ -136,7 +136,7 @@ FUNCTION foo () : {tuple, []} BITS_CLEAR arg98 a arg164 - BITS_TEST a a242 (| 5 | 2 | (1, "foo", ()) |) + BITS_TEST a a242 (| [0,0,3] | 2 | (1, "foo", ()) |) BITS_SUM a244 a71 @@ -176,7 +176,7 @@ FUNCTION foo () : {tuple, []} LOG1 arg94 arg86 arg208 - LOG2 a113 (| 5 | 2 | (1, "foo", ()) |) arg238 var108 + LOG2 a113 (| [0,1,3] | 2 | (1, "foo", ()) |) arg238 var108 LOG3 arg255 arg15 arg211 var139 arg44 @@ -186,7 +186,7 @@ FUNCTION foo () : {tuple, []} SPEND @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv var136 - ORACLE_REGISTER arg29 48 ((| 5 | 2 | (1, "foo", ()) |)) arg65 { <> => false} <> + ORACLE_REGISTER arg29 48 ((| [0,1,3] | 2 | (1, "foo", ()) |)) arg65 { <> => false} <> ORACLE_QUERY @@ -220,7 +220,7 @@ FUNCTION foo () : {tuple, []} BLAKE2B - DUMMY7ARG a a 7607708484837907159893701471377343595877 (| 2 | 0 | ( [], [ 45, { 1 => 3441201581501946066216994494994943246334} ] ) |) a0 var56 "foo" + DUMMY7ARG a a 7607708484837907159893701471377343595877 (| [2,1] | 0 | ( [], [ 45, { 1 => 3441201581501946066216994494994943246334} ] ) |) a0 var56 "foo" DUMMY8ARG 3673679924816289365509492271980889822579 a69 arg242 var237 a175 arg106 () var255 diff --git a/test/asm_code/immediates.fate b/test/asm_code/immediates.fate index d0b6445..267297c 100644 --- a/test/asm_code/immediates.fate +++ b/test/asm_code/immediates.fate @@ -70,8 +70,8 @@ FUNCTION address() : address ;; Option(integer) = NONE | SOME(integer) FUNCTION variant_none() : {variant, [{tuple, []}, {tuple, [integer]}]} - RETURNR (| 2 | 0 | () |) + RETURNR (| [0,1] | 0 | () |) ;; Option(integer) = NONE | SOME(integer) FUNCTION variant_some() : {variant, [{tuple, []}, {tuple, [integer]}]} - RETURNR (| 2 | 1 | (42) |) + RETURNR (| [0,1] | 1 | (42) |)