diff --git a/src/aeb_fate_asm.erl b/src/aeb_fate_asm.erl index 9aea5ad..5606894 100644 --- a/src/aeb_fate_asm.erl +++ b/src/aeb_fate_asm.erl @@ -48,14 +48,15 @@ %%% false %%% 5. Strings %%% "Hello" -%%% 6. Empty map +%%% 6. Map %%% {} +%%% { 1 => { "foo" => true, "bar" => false} %%% 7. Lists %%% [] %%% [1, 2] %%% 8. Bit field %%% <000> -%%% <1010> +%%% <1010 1010> %%% <> %%% !<> %%% 9. Tuples @@ -569,7 +570,11 @@ deserialize_type(<<5, Rest/binary>>) -> {bits, Rest}; deserialize_type(<<6, Rest/binary>>) -> {K, Rest2} = deserialize_type(Rest), {V, Rest3} = deserialize_type(Rest2), - {{map, K, V}, Rest3}. + {{map, K, V}, Rest3}; +deserialize_type(<<7, Rest/binary>>) -> + {string, Rest}. + + deserialize_types(0, Binary, Acc) -> {lists:reverse(Acc), Binary}; @@ -616,6 +621,19 @@ to_bytecode([{hash,_line, Hash}|Rest], Address, Env, Code, Opts) -> to_bytecode([{id,_line, ID}|Rest], Address, Env, Code, Opts) -> {Hash, Env2} = insert_symbol(ID, Env), to_bytecode(Rest, Address, Env2, [{immediate, Hash}|Code], Opts); +to_bytecode([{'{',_line}|Rest], Address, Env, Code, Opts) -> + {Map, Rest2} = parse_map(Rest), + to_bytecode(Rest2, Address, Env, [{immediate, Map}|Code], Opts); +to_bytecode([{'[',_line}|Rest], Address, Env, Code, Opts) -> + {List, Rest2} = parse_list(Rest), + to_bytecode(Rest2, Address, Env, [{immediate, List}|Code], Opts); +to_bytecode([{'(',_line}|Rest], Address, Env, Code, Opts) -> + {Elements, Rest2} = parse_tuple(Rest), + Tuple = aeb_fate_data:make_tuple(list_to_tuple(Elements)), + to_bytecode(Rest2, Address, Env, [{immediate, Tuple}|Code], Opts); +to_bytecode([{bits,_line, Bits}|Rest], Address, Env, Code, Opts) -> + to_bytecode(Rest, Address, Env, [{immediate, Bits}|Code], Opts); + to_bytecode([{comment, Line, Comment}|Rest], Address, Env, Code, Opts) -> Env2 = insert_annotation(comment, Line, Comment, Env), to_bytecode(Rest, Address, Env2, Code, Opts); @@ -632,6 +650,51 @@ to_bytecode([], Address, Env, Code, Opts) -> end, Env2. +parse_map([{'}',_line}|Rest]) -> + {#{}, Rest}; +parse_map(Tokens) -> + {Key, [{arrow, _} | Rest]} = parse_value(Tokens), + {Value, Rest2} = parse_value(Rest), + case Rest2 of + [{',',_} | Rest3] -> + {Map, Rest4} = parse_map(Rest3), + {Map#{Key => Value}, Rest4}; + [{'}',_} | Rest3] -> + {#{Key => Value}, Rest3} + end. + +parse_list([{']',_line}|Rest]) -> + {[], Rest}; +parse_list(Tokens) -> + {Head , Rest} = parse_value(Tokens), + case Rest of + [{',',_} | Rest2] -> + {Tail, Rest3} = parse_list(Rest2), + {[Head | Tail], Rest3}; + [{']',_} | Rest3] -> + {[Head], Rest3} + end. + +parse_tuple([{')',_line}|Rest]) -> + {[], Rest}; +parse_tuple(Tokens) -> + {Head , Rest} = parse_value(Tokens), + case Rest of + [{',',_} | Rest2] -> + {Tail, Rest3} = parse_tuple(Rest2), + {[Head | Tail], Rest3}; + [{')',_} | Rest3] -> + {[Head], Rest3} + end. + + +parse_value([{int,_line, Int} | Rest]) -> {Int, Rest}; +parse_value([{boolean,_line, Bool} | Rest]) -> {Bool, Rest}; +parse_value([{hash,_line, Hash} | Rest]) -> {Hash, Rest}; +parse_value([{'{',_line} | Rest]) -> parse_map(Rest); +parse_value([{'[',_line} | Rest]) -> parse_list(Rest); +parse_value([{'(',_line} | Rest]) -> parse_tuple(Rest). + to_fun_def([{id, _, Name}, {'(', _} | Rest]) -> {ArgsType, [{'to', _} | Rest2]} = to_arg_types(Rest), @@ -689,9 +752,11 @@ serialize_type({tuple, Ts}) -> N when N =< 255 -> [3, N | [serialize_type(T) || T <- Ts]] end; -serialize_type(address) -> 4; -serialize_type(bits) -> 5; -serialize_type({map, K, V}) -> [6 | serialize_type(K) ++ serialize_type(V)]. +serialize_type(address) -> [4]; +serialize_type(bits) -> [5]; +serialize_type({map, K, V}) -> [6 | serialize_type(K) ++ serialize_type(V)]; +serialize_type(string) -> [7]. + %% ------------------------------------------------------------------- diff --git a/src/aeb_fate_asm_scan.template b/src/aeb_fate_asm_scan.template index f894aa5..fa4d49d 100644 --- a/src/aeb_fate_asm_scan.template +++ b/src/aeb_fate_asm_scan.template @@ -17,7 +17,8 @@ HEX = 0x{HEXDIGIT}+ HASH = #{HEXDIGIT}+ WS = [\000-\s] ID = {LOWER}[a-zA-Z0-9_]* - +STRING = "[^"]*" +BITS = (\!)?\<[\s01]*\> Rules. arg{INT} : {token, {arg, TokenLine, parse_arg(TokenChars)}}. @@ -38,13 +39,22 @@ FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}. {token, {int, TokenLine, parse_hex(TokenChars)}}. {INT} : {token, {int, TokenLine, parse_int(TokenChars)}}. +-{INT} : + {token, {int, TokenLine, parse_int(TokenChars)}}. {HASH} : {token, {hash, TokenLine, parse_hash(TokenChars)}}. +{STRING} : + {token, {hash, TokenLine, list_to_binary(TokenChars)}}. +{BITS} : + {token, {bits, TokenLine, bits(TokenChars)}}. %% Symbols -\-\> : {token, {'to', TokenLine}}. -\: : {token, {'to', TokenLine}}. +\-\> : {token, {to, TokenLine}}. +\: : {token, {to, TokenLine}}. + +\=\> : {token, {arrow, TokenLine}}. + , : {token, {',', TokenLine}}. \( : {token, {'(', TokenLine}}. \) : {token, {')', TokenLine}}. @@ -97,3 +107,13 @@ scan(S) -> drop_prefix(C, [C|Rest]) -> drop_prefix(C, Rest); drop_prefix(_, Tail) -> Tail. + +bits([$!, $< | Rest]) -> + bits(Rest, -1); +bits([$< | Rest]) -> + bits(Rest, 0). + +bits([$> |_Rest], Acc) -> Acc; +bits([$0 | Rest], Acc) -> bits(Rest, Acc bsl 1); +bits([$1 | Rest], Acc) -> bits(Rest, (Acc bsl 1) bor 1); +bits([$ | Rest], Acc) -> bits(Rest, Acc). diff --git a/src/aeb_fate_data.erl b/src/aeb_fate_data.erl index e5404dd..3037d64 100644 --- a/src/aeb_fate_data.erl +++ b/src/aeb_fate_data.erl @@ -23,7 +23,7 @@ | {map, fate_type(), fate_type()} | {tuple, [fate_type()]} | address - | bits + | bits | {variant, integer()}. @@ -147,7 +147,7 @@ format(M) when ?IS_FATE_MAP(M) -> format(?FATE_ADDRESS(Address)) -> ["#", address_to_base58(Address)]; format(V) -> exit({not_a_fate_type, V}). -format_list(List) -> +format_list(List) -> ["[ ", lists:join(", ", [format(E) || E <- List]), " ]"]. format_kvs(List) -> diff --git a/src/aeb_fate_encoding.erl b/src/aeb_fate_encoding.erl index 9bdadbb..d0e5c12 100644 --- a/src/aeb_fate_encoding.erl +++ b/src/aeb_fate_encoding.erl @@ -128,7 +128,7 @@ serialize(L) when ?IS_FATE_LIST(L) -> <> end; serialize(Map) when ?IS_FATE_MAP(Map) -> - L = [{_K,_V}|_] = maps:to_list(?FATE_MAP_VALUE(Map)), + L = [{_K,_V}|_] = lists:sort(maps:to_list(?FATE_MAP_VALUE(Map))), Size = length(L), %% TODO: check all K same type, and all V same type %% check K =/= map diff --git a/test/aeb_fate_asm_test.erl b/test/aeb_fate_asm_test.erl index dd32af6..fc20dbd 100644 --- a/test/aeb_fate_asm_test.erl +++ b/test/aeb_fate_asm_test.erl @@ -50,6 +50,8 @@ sources() -> , "remote" , "test" , "tuple" + , "mapofmap" + , "immediates" ]. check_roundtrip(File) -> @@ -62,4 +64,5 @@ check_roundtrip(File) -> {_Env2, ByteCode2} = assemble(DissasmCode), Code1 = aeb_fate_asm:strip(ByteCode), Code2 = aeb_fate_asm:strip(ByteCode2), + io:format("~s~n", [aeb_fate_asm:to_asm(disassemble(ByteCode2))]), ?assertEqual(Code1, Code2). diff --git a/test/asm_code/immediates.fate b/test/asm_code/immediates.fate new file mode 100644 index 0000000..d3a0077 --- /dev/null +++ b/test/asm_code/immediates.fate @@ -0,0 +1,68 @@ +;; CONTRACT immediates + +FUNCTION integer() : integer + RETURNR 42 + +FUNCTION neg_integer() : integer + RETURNR -2374683271468723648732648736498712634876147 + +FUNCTION hex_integer() : integer + RETURNR 0x0deadbeef0 + +FUNCTION bool() : boolean + RETURNR true + +FUNCTION bool_f() : boolean + RETURNR false + +FUNCTION string() : string + RETURNR "Hello" + +FUNCTION map() : {map, integer, boolean} + RETURNR {} + +FUNCTION map2() : {map, integer, boolean} + RETURNR {1 => true} + +FUNCTION map3() : {map, integer, boolean} + RETURNR {1 => true, + 2 => false} + +FUNCTION map4() : {map, integer, {map, string, boolean}} + RETURNR {1 => { "foo" => true, "bar" => false}, + 2 => {}, + 3 => { "foo" => false}} + +FUNCTION nil() : {list, integer} + RETURNR [] + +FUNCTION list1() : {list, integer} + RETURNR [1] + +FUNCTION list2() : {list, integer} + RETURNR [1, 2] + + +FUNCTION no_bits() : bits + RETURNR <> + +FUNCTION all_bits() : bits + RETURNR !<> + +FUNCTION some_bits() : bits + RETURNR <101010> + +FUNCTION many_bits() : bits + RETURNR !<010101> + +FUNCTION group_bits() : bits + RETURNR <1010 1010 0011 1001> + +FUNCTION unit() : {tuple, []} + RETURNR () + +FUNCTION tuple() : {tuple, [integer, boolean, string, {tuple, [integer, integer]}]} + RETURNR (42, true, "FooBar", (1, 2)) + + + diff --git a/test/asm_code/mapofmap.fate b/test/asm_code/mapofmap.fate new file mode 100644 index 0000000..bbda554 --- /dev/null +++ b/test/asm_code/mapofmap.fate @@ -0,0 +1,7 @@ +;; CONTRACT mapofmap +FUNCTION map() : {map, integer, {map, string, boolean}} + RETURNR {1 => { "foo" => true, "bar" => false}, + 2 => {}, + 3 => { "foo" => false}} + +