From a53937840572b11d840ecee94a80784e45cad538 Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Fri, 15 Feb 2019 11:24:25 +0100 Subject: [PATCH 01/29] Pt 162805963 fate opcodes (#6) * First iteration of assembler. * Stand alone assembler. --- .gitignore | 2 + include/aefa_data.hrl | 55 ++++ include/aefa_opcodes.hrl | 110 ++++++++ rebar.config | 30 ++- rebar.lock | 11 +- src/ae_rlp.erl | 91 +++++++ src/aeblake2.erl | 148 +++++++++++ src/aefa_asm.erl | 484 ++++++++++++++++++++++++++++++++++++ src/aefa_asm_scan.xrl | 191 ++++++++++++++ src/aefa_data.erl | 180 ++++++++++++++ src/aefa_encoding.erl | 262 +++++++++++++++++++ src/aefa_opcodes.erl | 156 ++++++++++++ src/aefateasm.erl | 58 +++++ test/asm_code/arith.fate | 26 ++ test/asm_code/bool.fate | 14 ++ test/asm_code/comp.fate | 26 ++ test/asm_code/identity.fate | 8 + test/asm_code/jumpif.fate | 9 + test/asm_code/map.fate | 34 +++ test/asm_code/memory.fate | 31 +++ test/asm_code/remote.fate | 4 + test/asm_code/test.fate | 45 ++++ test/asm_code/tuple.fate | 31 +++ 23 files changed, 2003 insertions(+), 3 deletions(-) create mode 100644 include/aefa_data.hrl create mode 100644 include/aefa_opcodes.hrl create mode 100644 src/ae_rlp.erl create mode 100644 src/aeblake2.erl create mode 100644 src/aefa_asm.erl create mode 100644 src/aefa_asm_scan.xrl create mode 100644 src/aefa_data.erl create mode 100644 src/aefa_encoding.erl create mode 100644 src/aefa_opcodes.erl create mode 100644 src/aefateasm.erl create mode 100644 test/asm_code/arith.fate create mode 100644 test/asm_code/bool.fate create mode 100644 test/asm_code/comp.fate create mode 100644 test/asm_code/identity.fate create mode 100644 test/asm_code/jumpif.fate create mode 100644 test/asm_code/map.fate create mode 100644 test/asm_code/memory.fate create mode 100644 test/asm_code/remote.fate create mode 100644 test/asm_code/test.fate create mode 100644 test/asm_code/tuple.fate diff --git a/.gitignore b/.gitignore index 318406e..cde56db 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,6 @@ rel/example_project .concrete/DEV_MODE .rebar aeb_asm_scan.erl +aefa_asm_scan.erl _build/ +aefateasm diff --git a/include/aefa_data.hrl b/include/aefa_data.hrl new file mode 100644 index 0000000..424c9b1 --- /dev/null +++ b/include/aefa_data.hrl @@ -0,0 +1,55 @@ +-define(FATE_INTEGER_T, integer()). +-define(FATE_BYTE_T, 0..255). +-define(FATE_BOOLEAN_T, true | false). +-define(FATE_NIL_T, []). +-define(FATE_LIST_T, list()). +-define(FATE_UNIT_T, {tuple, {}}). +-define(FATE_MAP_T, #{ fate_type() => fate_type() }). +-define(FATE_STRING_T, binary()). +-define(FATE_ADDRESS_T, {address, <<_:256>>}). +-define(FATE_VARIANT_T, {variant, ?FATE_BYTE_T, ?FATE_BYTE_T, tuple()}). +-define(FATE_VOID_T, void). +-define(FATE_TUPLE_T, {tuple, tuple()}). +-define(FATE_BITS_T, {bits, integer()}). + +-define(IS_FATE_INTEGER(X), is_integer(X)). +-define(IS_FATE_LIST(X), (is_list(X))). +-define(IS_FATE_STRING(X), (is_binary(X))). +-define(IS_FATE_MAP(X), (is_map(X))). +-define(IS_FATE_TUPLE(X), (is_tuple(X) andalso (tuple == element(1, X) andalso is_tuple(element(2, X))))). +-define(IS_FATE_ADDRESS(X), (is_tuple(X) andalso (address == element(1, X) andalso is_binary(element(2, X))))). +-define(IS_FATE_BITS(X), (is_tuple(X) andalso (bits == element(1, X) andalso is_integer(element(2, X))))). +-define(IS_FATE_VARIANT(X), (is_tuple(X) + andalso + (variant == element(1, X) + andalso is_integer(element(2, X)) + andalso is_integer(element(3, X)) + andalso is_tuple(element(4, X)) + ))). +-define(IS_FATE_BOOLEAN(X), is_boolean(X)). + +-define(FATE_UNIT, {tuple, {}}). +-define(FATE_TUPLE(T), {tuple, T}). +-define(FATE_ADDRESS(A), {address, A}). +-define(FATE_BITS(B), {bits, B}). + + +-define(FATE_INTEGER_VALUE(X), (X)). +-define(FATE_LIST_VALUE(X), (X)). +-define(FATE_STRING_VALUE(X), (X)). +-define(FATE_ADDRESS_VALUE(X), (element(2, X))). +-define(FATE_MAP_VALUE(X), (X)). +-define(FATE_MAP_SIZE(X), (map_size(X))). +-define(FATE_STRING_SIZE(X), (byte_size(X))). +-define(FATE_TRUE, true). +-define(FATE_FALSE, false). +-define(FATE_NIL, []). +-define(FATE_VOID, void). +-define(FATE_EMPTY_STRING, <<>>). +-define(FATE_STRING(S), S). +-define(FATE_VARIANT(Size, Tag,T), {variant, Size, Tag, T}). + +-define(MAKE_FATE_INTEGER(X), X). +-define(MAKE_FATE_LIST(X), X). +-define(MAKE_FATE_MAP(X), X). +-define(MAKE_FATE_STRING(X), X). diff --git a/include/aefa_opcodes.hrl b/include/aefa_opcodes.hrl new file mode 100644 index 0000000..e1f107c --- /dev/null +++ b/include/aefa_opcodes.hrl @@ -0,0 +1,110 @@ + +%% FATE opcodes +-define('NOP' , 16#00). +-define('RETURN' , 16#01). +-define('CALL' , 16#02). +-define('CALL_R' , 16#03). +-define('CALL_T' , 16#04). +-define('CALL_TR' , 16#05). +-define('JUMP' , 16#06). +-define('JUMPIF' , 16#07). +-define('SWITCH' , 16#08). +-define('PUSH' , 16#09). +-define('DUP' , 16#0a). +-define('POP' , 16#0b). +-define('STORE' , 16#10). +-define('ADD' , 16#11). +-define('MUL' , 16#12). +-define('SUB' , 16#13). +-define('DIV' , 16#14). +-define('MOD' , 16#15). +-define('POW' , 16#16). +-define('LT' , 16#17). +-define('GT' , 16#18). +-define('EQ' , 16#19). +-define('ELT' , 16#1a). +-define('EGT' , 16#1b). +-define('NEQ' , 16#1c). +-define('AND' , 16#1d). +-define('OR' , 16#1e). +-define('NOT' , 16#1f). +-define('TUPLE' , 16#20). +-define('ELEMENT' , 16#21). +-define('MAP_EMPTY' , 16#22). +-define('MAP_LOOKUP' , 16#23). +-define('MAP_UPDATE' , 16#24). +-define('MAP_DELETE' , 16#25). +-define('MAP_MEMBER' , 16#26). +-define('MAP_FROM_LIST' , 16#27). +-define('NIL' , 16#28). +-define('IS_NIL' , 16#29). +-define('CONS' , 16#2a). +-define('HD' , 16#2b). +-define('TL' , 16#2c). +-define('LENGTH' , 16#2d). +-define('STR_EQ' , 16#2e). +-define('STR_JOIN' , 16#2f). +-define('ADDR_TO_STR' , 16#30). +-define('STR_REVERSE' , 16#31). +-define('INT_TO_ADDR' , 16#32). +-define('VARIANT' , 16#33). +-define('VARIANT_TEST' , 16#34). +-define('VARIANT_ELEMENT', 16#35). +-define('BITS_NONE' , 16#36). +-define('BITS_ALL' , 16#37). +-define('BITS_SET' , 16#38). +-define('BITS_CLEAR' , 16#39). +-define('BITS_TEST' , 16#3a). +-define('BITS_SUM' , 16#3b). +-define('BITS_OR' , 16#3c). +-define('BITS_AND' , 16#3d). +-define('BITS_DIFF' , 16#3e). +-define('ADDRESS' , 16#3f). +-define('BALANCE' , 16#40). +-define('ORIGIN' , 16#41). +-define('CALLER' , 16#42). +-define('GASPRICE' , 16#43). +-define('BLOCKHASH' , 16#44). +-define('BENEFICIARY' , 16#45). +-define('TIMESTAMP' , 16#46). +-define('NUMBER' , 16#47). +-define('DIFFICULTY' , 16#48). +-define('GASLIMIT' , 16#49). +-define('GAS' , 16#4a). +-define('LOG0' , 16#4b). +-define('LOG1' , 16#4c). +-define('LOG2' , 16#4d). +-define('LOG3' , 16#4e). +-define('LOG4' , 16#4f). +-define('ABORT' , 16#50). +-define('EXIT' , 16#51). +-define('DEACTIVATE' , 16#52). +-define('INC' , 16#53). +-define('DEC' , 16#54). +-define('INT_TO_STR' , 16#55). +-define('SPEND' , 16#56). +-define('ORACLE_REGISTER', 16#57). +-define('ORACLE_QUERY' , 16#58). +-define('ORACLE_RESPOND' , 16#59). +-define('ORACLE_EXTEND' , 16#5a). +-define('ORACLE_GET_ANSWER', 16#5b). +-define('ORACLE_GET_QUESTION', 16#5c). +-define('ORACLE_QUERY_FEE', 16#5d). +-define('AENS_RESOLVE' , 16#5e). +-define('AENS_PRECLAIM' , 16#5f). +-define('AENS_CLAIM' , 16#60). +-define('AENS_UPDATE' , 16#61). +-define('AENS_TRANSFER' , 16#62). +-define('AENS_REVOKE' , 16#63). +-define('ECVERIFY' , 16#64). +-define('SHA3' , 16#65). +-define('SHA256' , 16#66). +-define('BLAKE2B' , 16#67). +-define('RETURNR' , 16#68). +-define('MAP_LOOKUPD' , 16#69). + +-define('FUNCTION' , 16#fe). +-define('EXTEND' , 16#ff). + +-define( COMMENT(X), {comment, X}). + diff --git a/rebar.config b/rebar.config index cfd8b45..aae208e 100644 --- a/rebar.config +++ b/rebar.config @@ -2,10 +2,36 @@ {erl_opts, [debug_info]}. -{deps, []}. +{deps, [ {getopt, "1.0.1"} + ]}. + + +{escript_incl_apps, [aebytecode, getopt]}. +{escript_main_app, aebytecode}. +{escript_name, aefateasm}. +{escript_emu_args, "%%! +sbtu +A0\n"}. +{provider_hooks, [{post, [{compile, escriptize}]}]}. + +{post_hooks, [{"(linux|darwin|solaris|freebsd|netbsd|openbsd)", + escriptize, + "cp \"$REBAR_BUILD_DIR/bin/aefateasm\" ./aefateasm"}, + {"win32", + escriptize, + "robocopy \"%REBAR_BUILD_DIR%/bin/\" ./ aefateasm* " + "/njs /njh /nfl /ndl & exit /b 0"} % silence things + ]}. {dialyzer, [ {warnings, [unknown]}, {plt_apps, all_deps}, - {base_plt_apps, [erts, kernel, stdlib]} + {base_plt_apps, [erts, kernel, stdlib, crypto]} ]}. + + +{relx, [{release, {aessembler, "0.0.1"}, + [aebytecode, getopt]}, + + {dev_mode, true}, + {include_erts, false}, + + {extended_start_script, true}]}. diff --git a/rebar.lock b/rebar.lock index 57afcca..3c625aa 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1 +1,10 @@ -[]. +{"1.1.0", +[{<<"enacl">>, + {git,"https://github.com/aeternity/enacl.git", + {ref,"26180f42c0b3a450905d2efd8bc7fd5fd9cece75"}}, + 0}, + {<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},0}]}. +[ +{pkg_hash,[ + {<<"getopt">>, <<"C73A9FA687B217F2FF79F68A3B637711BB1936E712B521D8CE466B29CBF7808A">>}]} +]. diff --git a/src/ae_rlp.erl b/src/ae_rlp.erl new file mode 100644 index 0000000..6e537b4 --- /dev/null +++ b/src/ae_rlp.erl @@ -0,0 +1,91 @@ +%%%------------------------------------------------------------------- +%%% @copyright (C) 2017, Aeternity Anstalt +%%% @doc +%%% Implementation of the Recursive Length Prefix. +%%% +%%% https://github.com/ethereum/wiki/wiki/RLP +%%% +%%% @end +%%%------------------------------------------------------------------- + +-module(ae_rlp). +-export([ decode/1 + , decode_one/1 + , encode/1 + ]). + +-export_type([ encodable/0 + , encoded/0 + ]). + +-type encodable() :: [encodable()] | binary(). +-type encoded() :: <<_:8, _:_*8>>. + +-define(UNTAGGED_SIZE_LIMIT , 55). +-define(UNTAGGED_LIMIT , 127). +-define(BYTE_ARRAY_OFFSET , 128). +-define(LIST_OFFSET , 192). + + +-spec encode(encodable()) -> encoded(). +encode(X) -> + encode(X, []). + +encode(<> = X,_Opts) when B =< ?UNTAGGED_LIMIT -> + %% An untagged value + X; +encode(X,_Opts) when is_binary(X) -> + %% Byte array + add_size(?BYTE_ARRAY_OFFSET, X); +encode(L, Opts) when is_list(L) -> + %% Lists items are encoded and concatenated + ByteArray = << << (encode(X, Opts))/binary >> || X <- L >>, + add_size(?LIST_OFFSET, ByteArray). + +add_size(Offset, X) when byte_size(X) =< ?UNTAGGED_SIZE_LIMIT -> + %% The size fits in one tagged byte + <<(Offset + byte_size(X)), X/binary>>; +add_size(Offset, X) when is_binary(X) -> + %% The size itself needs to be encoded as a byte array + %% Add the tagged size of the size byte array + SizeBin = binary:encode_unsigned(byte_size(X)), + TaggedSize = ?UNTAGGED_SIZE_LIMIT + Offset + byte_size(SizeBin), + true = (TaggedSize < 256 ), %% Assert + <>. + +-spec decode(encoded()) -> encodable(). +decode(Bin) when is_binary(Bin), byte_size(Bin) > 0 -> + case decode_one(Bin) of + {X, <<>>} -> X; + {X, Left} -> error({trailing, X, Bin, Left}) + end. + +decode_one(<>) when X =< ?UNTAGGED_LIMIT -> + %% Untagged value + {<>, B}; +decode_one(<> = B) when L < ?LIST_OFFSET -> + %% Byte array + {Size, Rest} = decode_size(B, ?BYTE_ARRAY_OFFSET), + <> = Rest, + {X, Tail}; +decode_one(<<_/binary>> = B) -> + %% List + {Size, Rest} = decode_size(B, ?LIST_OFFSET), + <> = Rest, + {decode_list(X), Tail}. + +decode_size(<>, Offset) when L =< Offset + ?UNTAGGED_SIZE_LIMIT-> + %% One byte tagged size. + {L - Offset, B}; +decode_size(<<_, 0, _/binary>>,_Offset) -> + error(leading_zeroes_in_size); +decode_size(<>, Offset) -> + %% Actual size is in a byte array. + BinSize = L - Offset - ?UNTAGGED_SIZE_LIMIT, + <> = B, + {Size, Rest}. + +decode_list(<<>>) -> []; +decode_list(B) -> + {Element, Rest} = decode_one(B), + [Element|decode_list(Rest)]. diff --git a/src/aeblake2.erl b/src/aeblake2.erl new file mode 100644 index 0000000..0519440 --- /dev/null +++ b/src/aeblake2.erl @@ -0,0 +1,148 @@ +%%%============================================================================= +%%% @copyright (C) 2019, Aeternity Anstalt +%%% @doc +%%% BLAKE2b implementation in Erlang - for details see: https://blake2.net +%%% @end +%%%============================================================================= + +-module(aeblake2). + +-export([ blake2b/2 + , blake2b/3 + ]). + +-define(MAX_64BIT, 16#ffffffffffffffff). + +-spec blake2b(HashLen :: integer(), Msg :: binary()) -> {ok, binary()}. +blake2b(HashLen, Msg) -> + blake2b(HashLen, Msg, <<>>). + +-spec blake2b(HashLen :: integer(), Msg :: binary(), Key :: binary()) -> {ok, binary()}. +blake2b(HashLen, Msg0, Key) -> + %% If message should be keyed, prepend message with padded key. + Msg = <<(pad(128, Key))/binary, Msg0/binary>>, + + %% Set up the initial state + Init = (16#01010000 + (byte_size(Key) bsl 8) + HashLen), + <> = blake_iv(), + H = <<(H0 bxor Init):64, H1_7/binary>>, + + %% Perform the compression - message will be chopped into 128-byte chunks. + State = blake2b_compress(H, Msg, 0), + + %% Just return the requested part of the hash + {ok, binary_part(to_little_endian(State), {0, HashLen})}. + +blake2b_compress(H, <>, BCompr) when Rest /= <<>> -> + H1 = blake2b_compress(H, <>, BCompr + 128, false), + blake2b_compress(H1, Rest, BCompr + 128); +blake2b_compress(H, SmallChunk, BCompr) -> + Size = byte_size(SmallChunk), + FillSize = (128 - Size) * 8, + blake2b_compress(H, <>, BCompr + Size, true). + +blake2b_compress(H, Chunk0, BCompr, Last) -> + Chunk = to_big_endian(Chunk0), + <> = <>, + V12_ = V12 bxor (BCompr band ?MAX_64BIT), + V13_ = V13 bxor ((BCompr bsr 64) band ?MAX_64BIT), + V14_ = case Last of + false -> V14; + true -> V14 bxor ?MAX_64BIT + end, + V = <>, + + <> = + lists:foldl(fun(Round, Vx) -> blake2b_mix(Round, Chunk, Vx) end, V, lists:seq(0, 11)), + + <> = H, + <<((HInt bxor VLow) bxor VHigh):(8*64)>>. + +blake2b_mix(Rnd, Chunk, V) -> + <> = V, + <> = Chunk, + Ms = {M0, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13, M14, M15}, + M = fun(Ix) -> element(Ix+1, Ms) end, + + [S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13, S14, S15] = sigma(Rnd rem 10), + + {Vx0, Vx4, Vx8, Vx12} = blake2b_mix(V0, V4, V8, V12, M(S0), M(S1)), + {Vx1, Vx5, Vx9, Vx13} = blake2b_mix(V1, V5, V9, V13, M(S2), M(S3)), + {Vx2, Vx6, Vx10, Vx14} = blake2b_mix(V2, V6, V10, V14, M(S4), M(S5)), + {Vx3, Vx7, Vx11, Vx15} = blake2b_mix(V3, V7, V11, V15, M(S6), M(S7)), + + {Vy0, Vy5, Vy10, Vy15} = blake2b_mix(Vx0, Vx5, Vx10, Vx15, M(S8), M(S9)), + {Vy1, Vy6, Vy11, Vy12} = blake2b_mix(Vx1, Vx6, Vx11, Vx12, M(S10), M(S11)), + {Vy2, Vy7, Vy8, Vy13} = blake2b_mix(Vx2, Vx7, Vx8, Vx13, M(S12), M(S13)), + {Vy3, Vy4, Vy9, Vy14} = blake2b_mix(Vx3, Vx4, Vx9, Vx14, M(S14), M(S15)), + + <>. + +blake2b_mix(Va, Vb, Vc, Vd, X, Y) -> + Va1 = (Va + Vb + X) band ?MAX_64BIT, + Vd1 = rotr64(32, Vd bxor Va1), + + Vc1 = (Vc + Vd1) band ?MAX_64BIT, + Vb1 = rotr64(24, Vb bxor Vc1), + + Va2 = (Va1 + Vb1 + Y) band ?MAX_64BIT, + Vd2 = rotr64(16, Va2 bxor Vd1), + + Vc2 = (Vc1 + Vd2) band ?MAX_64BIT, + Vb2 = rotr64(63, Vb1 bxor Vc2), + + {Va2, Vb2, Vc2, Vd2}. + +blake_iv() -> + IV0 = 16#6A09E667F3BCC908, + IV1 = 16#BB67AE8584CAA73B, + IV2 = 16#3C6EF372FE94F82B, + IV3 = 16#A54FF53A5F1D36F1, + IV4 = 16#510E527FADE682D1, + IV5 = 16#9B05688C2B3E6C1F, + IV6 = 16#1F83D9ABFB41BD6B, + IV7 = 16#5BE0CD19137E2179, + <>. + +sigma(N) -> + {_, Row} = lists:keyfind(N, 1, sigma()), Row. + +sigma() -> + [{0, [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]}, + {1, [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3]}, + {2, [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4]}, + {3, [ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8]}, + {4, [ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13]}, + {5, [ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9]}, + {6, [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11]}, + {7, [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10]}, + {8, [ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5]}, + {9, [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0]}]. + +rotr64(N, I64) -> + <> = rotr641(N, <>), + I64rot. + +rotr641(16, <>) -> <>; +rotr641(24, <>) -> <>; +rotr641(32, <>) -> <>; +rotr641(63, <>) -> <>. + +pad(N, Bin) -> + case (N - (byte_size(Bin) rem N)) rem N of + 0 -> Bin; + Pad -> <> + end. + +to_big_endian(Bin) -> to_big_endian(Bin, <<>>). +to_big_endian(<<>>, Acc) -> Acc; +to_big_endian(<>, Acc) -> + to_big_endian(Rest, <>). + +to_little_endian(Bin) -> to_little_endian(Bin, <<>>). +to_little_endian(<<>>, Acc) -> Acc; +to_little_endian(<>, Acc) -> + to_little_endian(Rest, <>). diff --git a/src/aefa_asm.erl b/src/aefa_asm.erl new file mode 100644 index 0000000..0b9d420 --- /dev/null +++ b/src/aefa_asm.erl @@ -0,0 +1,484 @@ +%%%------------------------------------------------------------------- +%%% @copyright (C) 2019, Aeternity Anstalt +%%% @doc Assembler for Fate machine code. +%%% +%%% Assembler code can be read from a file. +%%% The assembler has the following format +%%% Comments start with 2 semicolons and runs till end of line +%%% ;; This is a comment +%%% Opcode mnemonics start with an upper case letter. +%%% DUP +%%% Identifiers start with a lower case letter +%%% an_identifier +%%% Immediates can be of 9 types: +%%% 1. Integers +%%% 42 +%%% -2374683271468723648732648736498712634876147 +%%% 2. Hexadecimal integers starting with 0x +%%% 0x0deadbeef0 +%%% 3. addresses, a 256-bit hash strings starting with # +%%% followed by up to 64 hex chars +%%% #00000deadbeef +%%% 4. Boolean +%%% true +%%% false +%%% 5. Strings +%%% "Hello" +%%% 6. Empty map +%%% {} +%%% 7. Lists +%%% [] +%%% [1, 2] +%%% 8. Bit field +%%% <000> +%%% <1010> +%%% <> +%%% !<> +%%% 9. Tuples +%%% () +%%% (1, "foo") +%%% @end +%%% Created : 21 Dec 2017 +%%%------------------------------------------------------------------- + +-module(aefa_asm). + +-export([ assemble_file/3 + , asm_to_bytecode/2 + , bytecode_to_fate_code/2 + , pp/1 + , read_file/1 + , to_hexstring/1 + ]). + +-include_lib("aebytecode/include/aefa_opcodes.hrl"). +-define(HASH_BYTES, 32). + +assemble_file(InFile, OutFile, Options) -> + Asm = read_file(InFile), + {Env, BC} = aefa_asm:asm_to_bytecode(Asm, Options), + ok = file:write_file(OutFile, BC). + +pp(Asm) -> + Listing = format(Asm), + io:format("~s~n", [Listing]). + +format(Asm) -> format(Asm, 0). + +format([{comment, Comment} | Rest], Address) -> + ";; " ++ Comment ++ "\n" ++ format(Rest, Address); +format([Mnemonic | Rest], Address) -> + _Op = aefa_opcodes:m_to_op(Mnemonic), + " " ++ atom_to_list(Mnemonic) ++ "\n" + ++ format(Rest, Address + 1); +format([],_) -> []. + + +read_file(Filename) -> + {ok, File} = file:read_file(Filename), + binary_to_list(File). + +asm_to_bytecode(AssemblerCode, Options) -> + {ok, Tokens, _} = aefa_asm_scan:scan(AssemblerCode), + + case proplists:lookup(pp_tokens, Options) of + {pp_tokens, true} -> + io:format("Tokens ~p~n",[Tokens]); + none -> + ok + end, + + Env = to_bytecode(Tokens, none, #{ functions => #{} + , symbols => #{} + }, [], Options), + + ByteList = serialize(Env), + + case proplists:lookup(pp_hex_string, Options) of + {pp_hex_string, true} -> + io:format("Code: ~s~n",[to_hexstring(ByteList)]); + none -> + ok + end, + + {Env, list_to_binary(ByteList)}. + +bytecode_to_fate_code(ByteCode,_Options) -> + deserialize(ByteCode, #{ function => none + , bb => 0 + , current_bb_code => [] + , functions => #{} + , code => #{} + }). + +deserialize(<>, + #{ function := none + , bb := 0 + , current_bb_code := [] + } = Env) -> + {Sig, Rest2} = deserialize_signature(Rest), + Env2 = Env#{function => {<>, Sig}}, + deserialize(Rest2, Env2); +deserialize(<>, + #{ function := F + , bb := BB + , current_bb_code := Code + , code := Program + , functions := Funs} = Env) -> + {Sig, Rest2} = deserialize_signature(Rest), + case Code of + [] -> + Env2 = Env#{ bb => 0 + , current_bb_code => [] + , function => {<>, Sig} + , code => #{} + , functions => Funs#{F => Program}}, + deserialize(Rest2, Env2); + _ -> + Env2 = Env#{ bb => 0 + , current_bb_code => [] + , function => {<>, Sig} + , code => #{} + , functions => + Funs#{F => Program#{ BB => lists:reverse(Code)}}}, + deserialize(Rest2, Env2) + end; +deserialize(<>, + #{ bb := BB + , current_bb_code := Code + , code := Program} = Env) -> + {Rest2, OpCode} = deserialize_op(Op, Rest, Code), + case aefa_opcodes:end_bb(Op) of + true -> + deserialize(Rest2, Env#{ bb => BB+1 + , current_bb_code => [] + , code => Program#{BB => + lists:reverse(OpCode)}}); + false -> + deserialize(Rest2, Env#{ current_bb_code => OpCode}) + end; +deserialize(<<>>, #{ function := F + , bb := BB + , current_bb_code := Code + , code := Program + , functions := Funs} = Env) -> + FunctionCode = + case Code of + [] -> Program; + _ -> Program#{ BB => lists:reverse(Code)} + end, + Env#{ bb => 0 + , current_bb_code => [] + , function => none + , code => #{} + , functions => Funs#{F => FunctionCode}}. + +deserialize_op(?ELEMENT, Rest, Code) -> + {Type, Rest2} = deserialize_type(Rest), + <> = Rest2, + {Arg0, Rest4} = aefa_encoding:deserialize_one(Rest3), + {Arg1, Rest5} = aefa_encoding:deserialize_one(Rest4), + {Arg2, Rest6} = aefa_encoding:deserialize_one(Rest5), + Modifier0 = bits_to_modifier(ArgType band 2#11), + Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11), + Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11), + {Rest6, [{ aefa_opcodes:mnemonic(?ELEMENT) + , Type + , {Modifier0, Arg0} + , {Modifier1, Arg1} + , {Modifier2, Arg2}} + | Code]}; +deserialize_op(Op, Rest, Code) -> + OpName = aefa_opcodes:mnemonic(Op), + case aefa_opcodes:args(Op) of + 0 -> {Rest, [OpName | Code]}; + 1 -> + <> = Rest, + {Arg, Rest3} = aefa_encoding:deserialize_one(Rest2), + Modifier = bits_to_modifier(ArgType), + {Rest3, [{OpName, {Modifier, Arg}} | Code]}; + 2 -> + <> = Rest, + {Arg0, Rest3} = aefa_encoding:deserialize_one(Rest2), + {Arg1, Rest4} = aefa_encoding:deserialize_one(Rest3), + Modifier0 = bits_to_modifier(ArgType band 2#11), + Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11), + {Rest4, [{OpName, {Modifier0, Arg0}, + {Modifier1, Arg1}} | Code]}; + 3 -> + <> = Rest, + {Arg0, Rest3} = aefa_encoding:deserialize_one(Rest2), + {Arg1, Rest4} = aefa_encoding:deserialize_one(Rest3), + {Arg2, Rest5} = aefa_encoding:deserialize_one(Rest4), + Modifier0 = bits_to_modifier(ArgType band 2#11), + Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11), + Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11), + {Rest5, [{ OpName + , {Modifier0, Arg0} + , {Modifier1, Arg1} + , {Modifier2, Arg2}} + | Code]}; + 4 -> + <> = Rest, + {Arg0, Rest3} = aefa_encoding:deserialize_one(Rest2), + {Arg1, Rest4} = aefa_encoding:deserialize_one(Rest3), + {Arg2, Rest5} = aefa_encoding:deserialize_one(Rest4), + {Arg3, Rest6} = aefa_encoding:deserialize_one(Rest5), + Modifier0 = bits_to_modifier(ArgType band 2#11), + Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11), + Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11), + Modifier3 = bits_to_modifier((ArgType bsr 6) band 2#11), + {Rest6, [{ OpName + , {Modifier0, Arg0} + , {Modifier1, Arg1} + , {Modifier2, Arg2} + , {Modifier3, Arg3}} + | Code]} + end. + + +serialize(#{functions := Functions} =_Env) -> + Code = [[?FUNCTION, Name, serialize_signature(Sig), C] || + {Name, {Sig, C}} <- maps:to_list(Functions)], + serialize_code(lists:flatten(Code)). + + +%% Argument encoding +%% Agument Specification Byte +%% bitpos: 6 4 2 0 +%% xx xx xx xx +%% Arg3 Arg2 Arg1 Arg0 +%% Bit pattern +%% 00 : stack/unused (depending on instruction) +%% 01 : argN +%% 10 : varN +%% 11 : immediate + +serialize_code([ {Arg0Type, Arg0} + , {Arg1Type, Arg1} + , {Arg2Type, Arg2} + , {Arg3Type, Arg3}| Rest]) -> + ArgSpec = + modifier_bits(Arg0Type) bor + (modifier_bits(Arg1Type) bsl 2) bor + (modifier_bits(Arg2Type) bsl 4) bor + (modifier_bits(Arg3Type) bsl 6), + [ ArgSpec + , serialize_data(Arg0Type, Arg0) + , serialize_data(Arg1Type, Arg1) + , serialize_data(Arg2Type, Arg2) + , serialize_data(Arg3Type, Arg3) + | serialize_code(Rest)]; +serialize_code([ {Arg0Type, Arg0} + , {Arg1Type, Arg1} + , {Arg2Type, Arg2} + | Rest]) -> + ArgSpec = + modifier_bits(Arg0Type) bor + (modifier_bits(Arg1Type) bsl 2) bor + (modifier_bits(Arg2Type) bsl 4), + [ArgSpec + , serialize_data(Arg0Type, Arg0) + , serialize_data(Arg1Type, Arg1) + , serialize_data(Arg2Type, Arg2) + | serialize_code(Rest)]; +serialize_code([ {Arg0Type, Arg0} + , {Arg1Type, Arg1} + | Rest]) -> + ArgSpec = + modifier_bits(Arg0Type) bor + (modifier_bits(Arg1Type) bsl 2), + [ArgSpec + , serialize_data(Arg0Type, Arg0) + , serialize_data(Arg1Type, Arg1) + | serialize_code(Rest)]; +serialize_code([ {Arg0Type, Arg0} | Rest]) -> + ArgSpec = + modifier_bits(Arg0Type), + [ArgSpec + , serialize_data(Arg0Type, Arg0) + | serialize_code(Rest)]; +serialize_code([ ?ELEMENT + , ResType + | Rest]) -> + [?ELEMENT, + serialize_type(ResType) + | serialize_code(Rest)]; +serialize_code([B|Rest]) -> + [B | serialize_code(Rest)]; +serialize_code([]) -> []. + +%% 00 : stack/unused (depending on instruction) +%% 01 : argN +%% 10 : varN +%% 11 : immediate +modifier_bits(immediate) -> 2#11; +modifier_bits(var) -> 2#10; +modifier_bits(arg) -> 2#01; +modifier_bits(stack) -> 2#00. + +bits_to_modifier(2#11) -> immediate; +bits_to_modifier(2#10) -> var; +bits_to_modifier(2#01) -> arg; +bits_to_modifier(2#00) -> stack. + +serialize_data(_, Data) -> + aefa_encoding:serialize(Data). + +serialize_signature({Args, RetType}) -> + [serialize_type({tuple, Args}) | + serialize_type(RetType)]. + +serialize_type(integer) -> [0]; +serialize_type(boolean) -> [1]; +serialize_type({list, T}) -> [2 | serialize_type(T)]; +serialize_type({tuple, Ts}) -> + case length(Ts) of + 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)]. + + +deserialize_signature(Binary) -> + {{tuple, Args}, Rest} = deserialize_type(Binary), + {RetType, Rest2} = deserialize_type(Rest), + {{Args, RetType}, Rest2}. + +deserialize_type(<<0, Rest/binary>>) -> {integer, Rest}; +deserialize_type(<<1, Rest/binary>>) -> {boolean, Rest}; +deserialize_type(<<2, Rest/binary>>) -> + {T, Rest2} = deserialize_type(Rest), + {{list, T}, Rest2}; +deserialize_type(<<3, N, Rest/binary>>) -> + {Ts, Rest2} = deserialize_types(N, Rest, []), + {{tuple, Ts}, Rest2}; +deserialize_type(<<4, Rest/binary>>) -> {address, Rest}; +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}. + +deserialize_types(0, Binary, Acc) -> + {lists:reverse(Acc), Binary}; +deserialize_types(N, Binary, Acc) -> + {T, Rest} = deserialize_type(Binary), + deserialize_types(N-1, Rest, [T | Acc]). + + +to_hexstring(ByteList) -> + "0x" ++ lists:flatten( + [io_lib:format("~2.16.0b", [X]) + || X <- ByteList]). + +to_bytecode([{function,_line, 'FUNCTION'}|Rest], Address, Env, Code, Opts) -> + Env2 = insert_fun(Address, Code, Env), + {Fun, Rest2} = to_fun_def(Rest), + to_bytecode(Rest2, Fun, Env2, [], Opts); +to_bytecode([{mnemonic,_line, 'ELEMENT'}|Rest], Address, Env, Code, Opts) -> + OpCode = aefa_opcodes:m_to_op('ELEMENT'), + {RetType, Rest2} = to_type(Rest), + to_bytecode(Rest2, Address, Env, [RetType, OpCode|Code], Opts); +to_bytecode([{mnemonic,_line, Op}|Rest], Address, Env, Code, Opts) -> + OpCode = aefa_opcodes:m_to_op(Op), + to_bytecode(Rest, Address, Env, [OpCode|Code], Opts); +to_bytecode([{arg,_line, N}|Rest], Address, Env, Code, Opts) -> + to_bytecode(Rest, Address, Env, [{arg, N}|Code], Opts); +to_bytecode([{var,_line, N}|Rest], Address, Env, Code, Opts) -> + to_bytecode(Rest, Address, Env, [{var, N}|Code], Opts); +to_bytecode([{stack,_line, N}|Rest], Address, Env, Code, Opts) -> + to_bytecode(Rest, Address, Env, [{stack, N}|Code], Opts); +to_bytecode([{int,_line, Int}|Rest], Address, Env, Code, Opts) -> + to_bytecode(Rest, Address, Env, [{immediate, Int}|Code], Opts); +to_bytecode([{boolean,_line, Bool}|Rest], Address, Env, Code, Opts) -> + to_bytecode(Rest, Address, Env, [{immediate, Bool}|Code], Opts); +to_bytecode([{hash,_line, Hash}|Rest], Address, Env, Code, Opts) -> + to_bytecode(Rest, Address, Env, [{immediate, Hash}|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([], Address, Env, Code, Opts) -> + Env2 = insert_fun(Address, Code, Env), + case proplists:lookup(pp_opcodes, Opts) of + {pp_opcodes, true} -> + Ops = [C || {_Name, {_Sig, C}} <- maps:to_list(Env2)], + io:format("opcodes ~p~n", [Ops]); + none -> + ok + end, + Env2. + + +to_fun_def([{id, _, Name}, {'(', _} | Rest]) -> + {ArgsType, [{'to', _} | Rest2]} = to_arg_types(Rest), + {RetType, Rest3} = to_type(Rest2), + {{Name, ArgsType, RetType}, Rest3}. + +to_arg_types([{')', _} | Rest]) -> {[], Rest}; +to_arg_types(Tokens) -> + case to_type(Tokens) of + {Type, [{',', _} | Rest]} -> + {MoreTypes, Rest2} = to_arg_types(Rest), + {[Type|MoreTypes], Rest2}; + {Type, [{')', _} | Rest]} -> + {[Type], Rest} + end. + +to_type([{id, _, "integer"} | Rest]) -> {integer, Rest}; +to_type([{id, _, "boolean"} | Rest]) -> {boolean, Rest}; +to_type([{id, _, "string"} | Rest]) -> {string, Rest}; +to_type([{id, _, "address"} | Rest]) -> {address, Rest}; +to_type([{id, _, "bits"} | Rest]) -> {bits, Rest}; +to_type([{'{', _}, {id, _, "list"}, {',', _} | Rest]) -> + %% TODO: Error handling + {ListType, [{'}', _}| Rest2]} = to_type(Rest), + {{list, ListType}, Rest2}; +to_type([{'{', _}, {id, _, "tuple"}, {',', _}, {'[', _} | Rest]) -> + %% TODO: Error handling + {ElementTypes, [{'}', _}| Rest2]} = to_list_of_types(Rest), + {{tuple, ElementTypes}, Rest2}; +to_type([{'{', _}, {id, _, "map"}, {',', _} | Rest]) -> + %% TODO: Error handling + {KeyType, [{',', _}| Rest2]} = to_type(Rest), + {ValueType, [{'}', _}| Rest3]} = to_type(Rest2), + {{map, KeyType, ValueType}, Rest3}. + +to_list_of_types([{']', _} | Rest]) -> {[], Rest}; +to_list_of_types(Tokens) -> + case to_type(Tokens) of + {Type, [{',', _} | Rest]} -> + {MoreTypes, Rest2} = to_list_of_types(Rest), + {[Type|MoreTypes], Rest2}; + {Type, [{']', _} | Rest]} -> + {[Type], Rest} + end. + + + +insert_fun(none, [], Env) -> Env; +insert_fun({Name, Type, RetType}, Code, #{functions := Functions} = Env) -> + {Hash, Env2} = insert_symbol(Name, Env), + Env2#{ + functions => Functions#{Hash => {{Type, RetType}, lists:reverse(Code)}} + }. + +insert_symbol(Id, Env) -> + %% Use first 4 bytes of blake hash + {ok, <> } = aeblake2:blake2b(?HASH_BYTES, list_to_binary(Id)), + insert_symbol(Id, <>, Env). + +insert_symbol(Id, Hash, #{symbols := Symbols} = Env) -> + case maps:find(Hash, Symbols) of + {ok, Id} -> {Hash, Env}; + {ok, Id2} -> + %% Very unlikely... + exit({two_symbols_with_same_hash, Id, Id2}); + error -> + {Hash, Env#{symbols => Symbols#{ Id => Hash + , Hash => Id}}} + end. +lookup_symbol(Id, #{symbols := Symbols} = Env) -> + maps:find(Id, Symbols). diff --git a/src/aefa_asm_scan.xrl b/src/aefa_asm_scan.xrl new file mode 100644 index 0000000..b61d240 --- /dev/null +++ b/src/aefa_asm_scan.xrl @@ -0,0 +1,191 @@ +%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*- +%%%------------------------------------------------------------------- +%%% @copyright (C) 2019, aeternity Anstalt +%%% @doc +%%% Handling FATE code. +%%% @end +%%% Created : 9 Jan 2019 +%%%------------------------------------------------------------------- + +Definitions. +DIGIT = [0-9] +HEXDIGIT = [0-9a-fA-F] +LOWER = [a-z_] +UPPER = [A-Z] +INT = {DIGIT}+ +HEX = 0x{HEXDIGIT}+ +HASH = #{HEXDIGIT}+ +WS = [\000-\s] +ID = {LOWER}[a-zA-Z0-9_]* + + +Rules. +arg{INT} : {token, {arg, TokenLine, parse_arg(TokenChars)}}. +var{INT} : {token, {var, TokenLine, parse_var(TokenChars)}}. +a : {token, {stack, TokenLine, 0}}. +a{INT} : {token, {stack, TokenLine, parse_acc(TokenChars)}}. + +true : {token, {boolean, TokenLine, true}}. +false : {token, {boolean, TokenLine, false}}. + +RETURN : {token, {mnemonic, TokenLine, 'RETURN'}}. +RETURNR : {token, {mnemonic, TokenLine, 'RETURNR'}}. +CALL : {token, {mnemonic, TokenLine, 'CALL'}}. +FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}. +NOP : {token, {mnemonic, TokenLine, 'NOP'}}. + +CALL_R : {token, {mnemonic, TokenLine, 'CALL_R'}}. +CALL_T : {token, {mnemonic, TokenLine, 'CALL_T'}}. +CALL_TR : {token, {mnemonic, TokenLine, 'CALL_TR'}}. +JUMP : {token, {mnemonic, TokenLine, 'JUMP'}}. +JUMPIF : {token, {mnemonic, TokenLine, 'JUMPIF'}}. +SWITCH : {token, {mnemonic, TokenLine, 'SWITCH'}}. + +PUSH : {token, {mnemonic, TokenLine, 'PUSH'}}. +DUP : {token, {mnemonic, TokenLine, 'DUP'}}. +POP : {token, {mnemonic, TokenLine, 'POP'}}. + +STORE : {token, {mnemonic, TokenLine, 'STORE'}}. + +ADD : {token, {mnemonic, TokenLine, 'ADD'}}. +MUL : {token, {mnemonic, TokenLine, 'MUL'}}. +SUB : {token, {mnemonic, TokenLine, 'SUB'}}. +DIV : {token, {mnemonic, TokenLine, 'DIV'}}. +MOD : {token, {mnemonic, TokenLine, 'MOD'}}. +POW : {token, {mnemonic, TokenLine, 'POW'}}. + +INC : {token, {mnemonic, TokenLine, 'INC'}}. +DEC : {token, {mnemonic, TokenLine, 'DEC'}}. + +LT : {token, {mnemonic, TokenLine, 'LT'}}. +GT : {token, {mnemonic, TokenLine, 'GT'}}. +EQ : {token, {mnemonic, TokenLine, 'EQ'}}. +ELT : {token, {mnemonic, TokenLine, 'ELT'}}. +EGT : {token, {mnemonic, TokenLine, 'EGT'}}. +NEQ : {token, {mnemonic, TokenLine, 'NEQ'}}. + +AND : {token, {mnemonic, TokenLine, 'AND'}}. +OR : {token, {mnemonic, TokenLine, 'OR'}}. +NOT : {token, {mnemonic, TokenLine, 'NOT'}}. + +TUPLE : {token, {mnemonic, TokenLine, 'TUPLE'}}. +ELEMENT : {token, {mnemonic, TokenLine, 'ELEMENT'}}. + +MAP_EMPTY : {token, {mnemonic, TokenLine, 'MAP_EMPTY'}}. +MAP_LOOKUP : {token, {mnemonic, TokenLine, 'MAP_LOOKUP'}}. +MAP_LOOKUPD : {token, {mnemonic, TokenLine, 'MAP_LOOKUPD'}}. +MAP_UPDATE : {token, {mnemonic, TokenLine, 'MAP_UPDATE'}}. +MAP_MEMBER : {token, {mnemonic, TokenLine, 'MAP_MEMBER'}}. +MAP_DELETE : {token, {mnemonic, TokenLine, 'MAP_DELETE'}}. +MAP_FROM_LIST : {token, {mnemonic, TokenLine, 'MAP_FROM_LIST'}}. + +NIL : {token, {mnemonic, TokenLine, 'NIL'}}. +IS_NIL : {token, {mnemonic, TokenLine, 'IS_NIL'}}. +CONS : {token, {mnemonic, TokenLine, 'CONS'}}. +HD : {token, {mnemonic, TokenLine, 'HD'}}. +TL : {token, {mnemonic, TokenLine, 'TL'}}. +LENGTH : {token, {mnemonic, TokenLine, 'LENGTH'}}. + +STR_EQ : {token, {mnemonic, TokenLine, 'STR_EQ'}}. +STR_JOIN : {token, {mnemonic, TokenLine, 'STR_JOIN'}}. +INT_TO_STR : {token, {mnemonic, TokenLine, 'INT_TO_STR'}}. +ADDR_TO_STR : {token, {mnemonic, TokenLine, 'ADDR_TO_STR'}}. +STR_REVERSE : {token, {mnemonic, TokenLine, 'STR_REVERSE'}}. + +INT_TO_ADDR : {token, {mnemonic, TokenLine, 'INT_TO_ADDR'}}. + +VARIANT : {token, {mnemonic, TokenLine, 'VARIANT'}}. +VARIANT_TEST : {token, {mnemonic, TokenLine, 'VARIANT_TEST'}}. +VARIANT_ELEMENT : {token, {mnemonic, TokenLine, 'VARIANT_ELEMENT'}}. + +BITS_NONE : {token, {mnemonic, TokenLine, 'BITS_NONE'}}. +BITS_ALL : {token, {mnemonic, TokenLine, 'BITS_ALL'}}. +BITS_SET : {token, {mnemonic, TokenLine, 'BITS_SET'}}. +BITS_CLEAR : {token, {mnemonic, TokenLine, 'BITS_CLEAR'}}. +BITS_TEST : {token, {mnemonic, TokenLine, 'BITS_TEST'}}. +BITS_SUM : {token, {mnemonic, TokenLine, 'BITS_SUM'}}. +BITS_OR : {token, {mnemonic, TokenLine, 'BITS_OR'}}. +BITS_AND : {token, {mnemonic, TokenLine, 'BITS_AND'}}. +BITS_DIFF : {token, {mnemonic, TokenLine, 'BITS_DIFF'}}. + + +ADDRESS : {token, {mnemonic, TokenLine, 'ADDRESS'}}. +BALANCE : {token, {mnemonic, TokenLine, 'BALANCE'}}. +ORIGIN : {token, {mnemonic, TokenLine, 'ORIGIN'}}. +CALLER : {token, {mnemonic, TokenLine, 'CALLER'}}. +GASPRICE : {token, {mnemonic, TokenLine, 'GASPRICE'}}. +BLOCKHASH : {token, {mnemonic, TokenLine, 'BLOCKHASH'}}. +BENEFICIARY : {token, {mnemonic, TokenLine, 'BENEFICIARY'}}. +TIMESTAMP : {token, {mnemonic, TokenLine, 'TIMESTAMP'}}. +NUMBER : {token, {mnemonic, TokenLine, 'NUMBER'}}. +DIFFICULTY : {token, {mnemonic, TokenLine, 'DIFFICULTY'}}. +GASLIMIT : {token, {mnemonic, TokenLine, 'GASLIMIT'}}. +GAS : {token, {mnemonic, TokenLine, 'GAS'}}. +LOG0 : {token, {mnemonic, TokenLine, 'LOG0'}}. +LOG1 : {token, {mnemonic, TokenLine, 'LOG1'}}. +LOG2 : {token, {mnemonic, TokenLine, 'LOG2'}}. +LOG3 : {token, {mnemonic, TokenLine, 'LOG3'}}. +LOG4 : {token, {mnemonic, TokenLine, 'LOG4'}}. +ABORT : {token, {mnemonic, TokenLine, 'ABORT'}}. +EXIT : {token, {mnemonic, TokenLine, 'EXIT'}}. +DEACTIVATE : {token, {mnemonic, TokenLine, 'DEACTIVATE'}}. +COMMENT : {token, {mnemonic, TokenLine, 'COMMENT'}}. +{ID} : + {token, {id, TokenLine, TokenChars}}. +{HEX} : + {token, {int, TokenLine, parse_hex(TokenChars)}}. +{INT} : + {token, {int, TokenLine, parse_int(TokenChars)}}. +{HASH} : + {token, {hash, TokenLine, parse_hash(TokenChars)}}. + + +%% Symbols +\-\> : {token, {'to', TokenLine}}. +\: : {token, {'to', TokenLine}}. +, : {token, {',', TokenLine}}. +\( : {token, {'(', TokenLine}}. +\) : {token, {')', TokenLine}}. +\[ : {token, {'[', TokenLine}}. +\] : {token, {']', TokenLine}}. +\{ : {token, {'{', TokenLine}}. +\} : {token, {'}', TokenLine}}. + +\. : skip_token. + + +%% Whitespace ignore +{WS} : skip_token. + +%% Comments (TODO: nested comments) +;;.* : skip_token. + +. : {error, "Unexpected token: " ++ TokenChars}. + +Erlang code. + +-export([scan/1]). + +-dialyzer({nowarn_function, yyrev/2}). + +-ignore_xref([format_error/1, string/2, token/2, token/3, tokens/2, tokens/3]). + +-include_lib("aebytecode/include/aefa_opcodes.hrl"). + + +parse_hex("0x" ++ Chars) -> list_to_integer(Chars, 16). + +parse_int(Chars) -> list_to_integer(Chars). + +parse_arg("arg" ++ N) -> list_to_integer(N). +parse_var("var" ++ N) -> list_to_integer(N). +parse_acc("a" ++ N) -> list_to_integer(N). + + +parse_hash("#" ++ Chars) -> + N = list_to_integer(Chars, 16), + <>. + +scan(S) -> + string(S). + diff --git a/src/aefa_data.erl b/src/aefa_data.erl new file mode 100644 index 0000000..568dbd7 --- /dev/null +++ b/src/aefa_data.erl @@ -0,0 +1,180 @@ +%% First draft of FATE data representation. +%% Very likely to change. +%% +-include("aefa_data.hrl"). + +-module(aefa_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_variant() :: ?FATE_VARIANT_T. + +-type fate_void() :: ?FATE_VOID_T. + +-type fate_tuple() :: ?FATE_TUPLE_T. + +-type fate_type() :: + fate_boolean() + | fate_integer() + | fate_nil() + | fate_list() + | fate_unit() + | fate_tuple() + | fate_string() + | fate_address() + | fate_variant() + | fate_map() + | fate_list() + | fate_tuple() + | fate_void(). %% Not sure we need this. + +-export_type([fate_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_bits/1 + , make_unit/0 + , tuple_to_list/1 + , decode/1 + , encode/1 + ]). +-export([format/1]). + + +make_integer(I) when is_integer(I) -> ?MAKE_FATE_INTEGER(I). +make_boolean(true) -> ?FATE_TRUE; +make_boolean(false) -> ?FATE_FALSE. +make_list([]) -> ?FATE_NIL; +make_list(L) -> ?MAKE_FATE_LIST(L). +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_unit() -> ?FATE_UNIT. +make_tuple(T) -> ?FATE_TUPLE(T). +make_map(M) -> ?MAKE_FATE_MAP(M). +make_address(A) -> ?FATE_ADDRESS(A). +make_bits(I) when is_integer(I) -> ?FATE_BITS(I). + +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). + +tuple_to_list(?FATE_TUPLE(T)) -> erlang:tuple_to_list(T). + +%% Encode is a convinience function for testing, encoding an Erlang term +%% to a Fate term, but it can not distinguish between e.g. 32-byte strings +%% and addresses. Therfore an extra tuple layer on the erlang side for +%% addresses and bits. +encode({bits, Term}) when is_integer(Term) -> make_bits(Term); +%% TODO: check that each byte is in base58 +encode({address, B}) when is_binary(B) -> make_address(B); +encode({address, I}) when is_integer(I) -> B = <>, make_address(B); +encode({address, S}) when is_list(S) -> make_address(base58_to_address(S)); +encode({variant, Size, Tag, Values}) -> make_variant(Size, 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]); +encode(Term) when is_tuple(Term) -> + make_tuple(list_to_tuple([encode(E) || E <- erlang:tuple_to_list(Term)])); +encode(Term) when is_map(Term) -> + make_map(maps:from_list([{encode(K), encode(V)} || {K,V} <- maps:to_list(Term)])); +encode(Term) when is_binary(Term) -> make_string(Term). + + + +decode(I) when ?IS_FATE_INTEGER(I) -> I; +decode(?FATE_TRUE) -> true; +decode(?FATE_FALSE) -> false; +decode(L) when ?IS_FATE_LIST(L) -> [decode(E) || E <- L]; +decode(?FATE_ADDRESS(<>)) -> {address, Address}; +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(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)]). + +-spec format(fate_type()) -> iolist(). +format(I) when ?IS_FATE_INTEGER(I) -> integer_to_list(?MAKE_FATE_INTEGER(I)); +format(?FATE_VOID) -> "void"; +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) -> "unit"; +format(?FATE_TUPLE(T)) -> + "{ " ++ [format(E) ++ " " || E <- erlang:tuple_to_list(T)] ++ "}"; +format(S) when ?IS_FATE_STRING(S) -> [S]; +format(?FATE_VARIANT(Size, Tag, T)) -> + "( " ++ integer_to_list(Size) ++ ", " + ++ integer_to_list(Tag) ++ ", " + ++ [format(E) ++ " " || E <- erlang:tuple_to_list(T)] + ++ " )"; +format(M) when ?IS_FATE_MAP(M) -> + "#{ " + ++ format_kvs(maps:to_list(?FATE_MAP_VALUE(M))) + ++" }"; +format(?FATE_ADDRESS(Address)) -> base58:binary_to_base58(Address); +format(V) -> exit({not_a_fate_type, V}). + +format_list([]) -> " ]"; +format_list([E]) -> format(E) ++ " ]"; +format_list([H|T]) -> format(H) ++ ", " ++ format_list(T). + +format_kvs([]) -> ""; +format_kvs([{K,V}]) -> "( " ++ format(K) ++ " => " ++ format(V) ++ " )"; +format_kvs([{K,V} | Rest]) -> + "( " ++ format(K) ++ " => " ++ format(V) ++ " ), " ++ format_kvs(Rest). + + +%% -- Local base 58 library + +base58char(Char) -> + binary:at(<<"123456789ABCDEFGHJKLMNPQRSTUVWXYZ" + "abcdefghijkmnopqrstuvwxyz">>, Char). +char_to_base58(C) -> + binary:at(<<0,1,2,3,4,5,6,7,8,0,0,0,0,0,0,0,9,10,11,12,13,14,15,16,0,17, + 18,19,20,21,0,22,23,24,25,26,27,28,29,30,31,32,0,0,0,0,0,0, + 33,34,35,36,37,38,39,40,41,42,43,0,44,45,46,47,48,49,50,51, + 52,53,54,55,56,57>>, C-$1). + +base58_to_integer(C, []) -> C; +base58_to_integer(C, [X | Xs]) -> + base58_to_integer(C * 58 + char_to_base58(X), Xs). + +base58_to_integer([]) -> error; +base58_to_integer([Char]) -> char_to_base58(Char); +base58_to_integer([Char | Str]) -> + base58_to_integer(char_to_base58(Char), Str). + +base58_to_address(Base58) -> + I = base58_to_integer(Base58), + Bin = <>, + Bin. + +integer_to_base58(0) -> <<"1">>; +integer_to_base58(Integer) -> + Base58String = integer_to_base58(Integer, []), + list_to_binary(Base58String). + +integer_to_base58(0, Acc) -> Acc; +integer_to_base58(Integer, Acc) -> + Quot = Integer div 58, + Rem = Integer rem 58, + integer_to_base58(Quot, [base58char(Rem)|Acc]). diff --git a/src/aefa_encoding.erl b/src/aefa_encoding.erl new file mode 100644 index 0000000..dd83e64 --- /dev/null +++ b/src/aefa_encoding.erl @@ -0,0 +1,262 @@ +%% Fate data (and instruction) serialization. +%% +%% The FATE serialization has to fullfill the following properties: +%% * There has to be 1 and only 1 byte sequence +%% representing each unique value in FATE. +%% * A valid byte sequence has to be deserializable to a FATE value. +%% * A valid byte sequence must not contain any trailing bytes. +%% * A serialization is a sequence of 8-bit bytes. +%% +%% The serialization function should fullfill the following: +%% * A valid FATE value should be serialized to a byte sequence. +%% * Any other argument, not representing a valid FATE value should +%% throw an exception +%% +%% The deserialization function should fullfill the following: +%% * A valid byte sequence should be deserialized to a valid FATE value. +%% * Any other argument, not representing a valid byte sequence should +%% throw an exception +%% +%% History +%% * First draft of FATE serialization encoding/decoding. +%% Initial experiment with tags +%% * Second draft +%% * FATE data is now defined in aefa_data.erl +%% * Third draft +%% * Added Bit strings +%% +%% TODO: +%% * Make the code production ready. +%% (add tests, document exported functions). +%% * Handle Variant types better. +%% * Handle type representations. +%% * Handle instructions. +%% +%% ------------------------------------------------------------------------ +-module(aefa_encoding). + +-export([ deserialize/1 + , deserialize_one/1 + , serialize/1 + ]). + +-include("aefa_data.hrl"). + +%% Definition of tag scheme. +%% This has to follow the protocol specification. + +-define(SMALL_INT , 2#0). %% sxxxxxx 0 - 6 bit integer with sign bit +%% 1 Set below +-define(LONG_STRING , 2#00000001). %% 000000 01 - RLP encoded array, size >= 64 +-define(SHORT_STRING , 2#01). %% xxxxxx 01 - [bytes], 0 < xxxxxx:size < 64 +%% 11 Set below +-define(SHORT_LIST , 2#0011). %% xxxx 0011 - [encoded elements], 0 < length < 16 +%% xxxx 0111 - FREE (For typedefs in future) +-define(LONG_TUPLE , 2#00001011). %% 0000 1011 - RLP encoded (size - 16) + [encoded elements], +-define(SHORT_TUPLE , 2#1011). %% xxxx 1011 - [encoded elements], 0 < size < 16 +%% 1111 Set below +-define(LONG_LIST , 2#00011111). %% 0001 1111 - RLP encoded (length - 16) + [Elements] +-define(MAP , 2#00101111). %% 0010 1111 - RLP encoded size + [encoded key, encoded value] +-define(EMPTY_TUPLE , 2#00111111). %% 0011 1111 +-define(POS_BITS , 2#01001111). %% 0100 1111 - RLP encoded integer (to be interpreted as bitfield) +-define(EMPTY_STRING , 2#01011111). %% 0101 1111 +-define(POS_BIG_INT , 2#01101111). %% 0110 1111 - RLP encoded (integer - 64) +-define(FALSE , 2#01111111). %% 0111 1111 +%% %% 1000 1111 - FREE (Possibly for bytecode in the future.) +-define(ADDRESS , 2#10011111). %% 1001 1111 - [32 bytes] +-define(VARIANT , 2#10101111). %% 1010 1111 - encoded size + 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 +-define(NEG_BIG_INT , 2#11101111). %% 1110 1111 - RLP encoded (integer - 64) +-define(TRUE , 2#11111111). %% 1111 1111 + +-define(SHORT_TUPLE_SIZE, 16). +-define(SHORT_LIST_SIZE , 16). +-define(SMALL_INT_SIZE , 64). +-define(SHORT_STRING_SIZE, 64). + +-define(POS_SIGN, 0). +-define(NEG_SIGN, 1). + + +%% -------------------------------------------------- +%% Serialize +%% Serialized a Fate data value into a sequence of bytes +%% according to the Fate serialization specification. +%% TODO: The type Fate Data is not final yet. +-spec serialize(aefa_data:fate_type()) -> binary(). +serialize(?FATE_TRUE) -> <>; +serialize(?FATE_FALSE) -> <>; +serialize(?FATE_NIL) -> <>; %% ! Untyped +serialize(?FATE_UNIT) -> <>; %% ! Untyped +serialize(M) when ?IS_FATE_MAP(M), ?FATE_MAP_SIZE(M) =:= 0 -> <>; %% ! Untyped +serialize(?FATE_EMPTY_STRING) -> <>; +serialize(I) when ?IS_FATE_INTEGER(I) -> serialize_integer(I); +serialize(?FATE_BITS(Bits)) when is_integer(Bits) -> serialize_bits(Bits); +serialize(String) when ?IS_FATE_STRING(String), + ?FATE_STRING_SIZE(String) > 0, + ?FATE_STRING_SIZE(String) < ?SHORT_STRING_SIZE -> + Size = ?FATE_STRING_SIZE(String), + Bytes = ?FATE_STRING_VALUE(String), + <>; +serialize(String) when ?IS_FATE_STRING(String), + ?FATE_STRING_SIZE(String) > 0, + ?FATE_STRING_SIZE(String) >= ?SHORT_STRING_SIZE -> + Bytes = ?FATE_STRING_VALUE(String), + <>; +serialize(?FATE_ADDRESS(Address)) when is_binary(Address) -> + <>; +serialize(?FATE_TUPLE(T)) when size(T) > 0 -> + S = size(T), + L = tuple_to_list(T), + Rest = << <<(serialize(E))/binary>> || E <- L >>, + if S < ?SHORT_TUPLE_SIZE -> + <>; + true -> + Size = rlp_integer(S - ?SHORT_TUPLE_SIZE), + <> + end; +serialize(L) when ?IS_FATE_LIST(L) -> + [_E|_] = List = ?FATE_LIST_VALUE(L), + S = length(List), + Rest = << <<(serialize(El))/binary>> || El <- List >>, + if S < ?SHORT_LIST_SIZE -> + <>; + true -> + Val = rlp_integer(S - ?SHORT_LIST_SIZE), + <> + end; +serialize(Map) when ?IS_FATE_MAP(Map) -> + L = [{_K,_V}|_] = maps:to_list(?FATE_MAP_VALUE(Map)), + Size = length(L), + %% TODO: check all K same type, and all V same type + %% check K =/= map + Elements = << <<(serialize(K1))/binary, (serialize(V1))/binary>> || {K1,V1} <- L >>, + <>; +serialize(?FATE_VARIANT(Size, Tag, Values)) when 0 =< Size + , Size < 256 + , 0 =< Tag + , Tag < Size -> + <>. + + +%% ----------------------------------------------------- + +rlp_integer(S) when S >= 0 -> + ae_rlp:encode(binary:encode_unsigned(S)). + +serialize_integer(I) when ?IS_FATE_INTEGER(I) -> + V = ?FATE_INTEGER_VALUE(I), + Abs = abs(V), + Sign = case V < 0 of + true -> ?NEG_SIGN; + false -> ?POS_SIGN + end, + if Abs < ?SMALL_INT_SIZE -> <>; + Sign =:= ?NEG_SIGN -> <>; + Sign =:= ?POS_SIGN -> <> + end. + +serialize_bits(B) when is_integer(B) -> + Abs = abs(B), + Sign = case B < 0 of + true -> ?NEG_SIGN; + false -> ?POS_SIGN + end, + if + Sign =:= ?NEG_SIGN -> <>; + Sign =:= ?POS_SIGN -> <> + end. + +-spec deserialize(binary()) -> aefa_data:fate_type(). +deserialize(B) -> + {T, <<>>} = deserialize2(B), + T. + +deserialize_one(B) -> deserialize2(B). + +deserialize2(<>) -> + {?MAKE_FATE_INTEGER(I), Rest}; +deserialize2(<>) -> + {?MAKE_FATE_INTEGER(-I), Rest}; +deserialize2(<>) -> + {Bint, Rest2} = ae_rlp:decode_one(Rest), + {?MAKE_FATE_INTEGER(-binary:decode_unsigned(Bint) - ?SMALL_INT_SIZE), + Rest2}; +deserialize2(<>) -> + {Bint, Rest2} = ae_rlp:decode_one(Rest), + {?MAKE_FATE_INTEGER(binary:decode_unsigned(Bint) + ?SMALL_INT_SIZE), + Rest2}; +deserialize2(<>) -> + {Bint, Rest2} = ae_rlp:decode_one(Rest), + {?FATE_BITS(-binary:decode_unsigned(Bint)), Rest2}; +deserialize2(<>) -> + {Bint, Rest2} = ae_rlp:decode_one(Rest), + {?FATE_BITS(binary:decode_unsigned(Bint)), Rest2}; +deserialize2(<>) -> + {String, Rest2} = ae_rlp:decode_one(Rest), + {?MAKE_FATE_STRING(String), Rest2}; +deserialize2(<>) -> + String = binary:part(Rest, 0, S), + Rest2 = binary:part(Rest, byte_size(Rest), - (byte_size(Rest) - S)), + {?MAKE_FATE_STRING(String), Rest2}; +deserialize2(<>) -> + {A, Rest2} = ae_rlp:decode_one(Rest), + {?FATE_ADDRESS(A), Rest2}; +deserialize2(<>) -> + {?FATE_TRUE, Rest}; +deserialize2(<>) -> + {?FATE_FALSE, Rest}; +deserialize2(<>) -> + {?FATE_NIL, Rest}; +deserialize2(<>) -> + {?FATE_UNIT, Rest}; +deserialize2(<>) -> + {?MAKE_FATE_MAP(#{}), Rest}; +deserialize2(<>) -> + {?FATE_EMPTY_STRING, Rest}; +deserialize2(<>) -> + {BSize, Rest1} = ae_rlp:decode_one(Rest), + N = binary:decode_unsigned(BSize) + ?SHORT_TUPLE_SIZE, + {List, Rest2} = deserialize_elements(N, Rest1), + {?FATE_TUPLE(list_to_tuple(List)), Rest2}; +deserialize2(<>) -> + {List, Rest1} = deserialize_elements(S, Rest), + {?FATE_TUPLE(list_to_tuple(List)), Rest1}; +deserialize2(<>) -> + {BLength, Rest1} = ae_rlp:decode_one(Rest), + Length = binary:decode_unsigned(BLength) + ?SHORT_LIST_SIZE, + {List, Rest2} = deserialize_elements(Length, Rest1), + {?MAKE_FATE_LIST(List), Rest2}; +deserialize2(<>) -> + {List, Rest1} = deserialize_elements(S, Rest), + {?MAKE_FATE_LIST(List), Rest1}; +deserialize2(<>) -> + {BSize, Rest1} = ae_rlp:decode_one(Rest), + Size = binary:decode_unsigned(BSize), + {List, Rest2} = deserialize_elements(2*Size, Rest1), + Map = insert_kv(List, #{}), + {?MAKE_FATE_MAP(Map), Rest2}; +deserialize2(<>) -> + if Tag > Size -> exit({too_large_tag_in_variant, Tag, Size}); + true -> + {?FATE_TUPLE(T), Rest2} = deserialize2(Rest), + {?FATE_VARIANT(Size, Tag, T), Rest2} + end. + +insert_kv([], M) -> M; +insert_kv([K,V|R], M) -> insert_kv(R, maps:put(K, V, M)). + +deserialize_elements(0, Rest) -> + {[], Rest}; +deserialize_elements(N, Es) -> + {E, Rest} = deserialize2(Es), + {Tail, Rest2} = deserialize_elements(N-1, Rest), + {[E|Tail], Rest2}. diff --git a/src/aefa_opcodes.erl b/src/aefa_opcodes.erl new file mode 100644 index 0000000..20116e5 --- /dev/null +++ b/src/aefa_opcodes.erl @@ -0,0 +1,156 @@ +%%%------------------------------------------------------------------- +%%% @copyright (C) 2019, Aeternity Anstalt +%%% @doc +%%% Opcodes +%%% @end +%%%------------------------------------------------------------------- + +-module(aefa_opcodes). + +-export([ args/1 + , end_bb/1 + , mnemonic/1 + , m_to_op/1 + , opcode/1 + ]). + +-include_lib("aebytecode/include/aefa_opcodes.hrl"). + + +%%==================================================================== +%% API +%%==================================================================== + +opcode(X) when X >= 0, X =< 255 -> X; +opcode({comment,X}) -> ?COMMENT(X). + +mnemonic(?NOP) -> 'NOP' ; +mnemonic(?RETURN) -> 'RETURN' ; +mnemonic(?RETURNR) -> 'RETURNR' ; +mnemonic(?PUSH) -> 'PUSH' ; +mnemonic(?JUMP) -> 'JUMP' ; +mnemonic(?JUMPIF) -> 'JUMPIF' ; +mnemonic(?INC) -> 'INC' ; +mnemonic(?CALL) -> 'CALL' ; +mnemonic(?CALL_T) -> 'CALL_T' ; +mnemonic(?CALL_R) -> 'CALL_R' ; +mnemonic(?CALL_TR) -> 'CALL_TR' ; +mnemonic(?ADD) -> 'ADD' ; +mnemonic(?SUB) -> 'SUB' ; +mnemonic(?MUL) -> 'MUL' ; +mnemonic(?DIV) -> 'DIV' ; +mnemonic(?MOD) -> 'MOD' ; +mnemonic(?POW) -> 'POW' ; +mnemonic(?AND) -> 'AND' ; +mnemonic(?OR) -> 'OR' ; +mnemonic(?NOT) -> 'NOT' ; +mnemonic(?LT) -> 'LT' ; +mnemonic(?GT) -> 'GT' ; +mnemonic(?EGT) -> 'EGT' ; +mnemonic(?ELT) -> 'ELT' ; +mnemonic(?EQ) -> 'EQ' ; +mnemonic(?NEQ) -> 'NEQ' ; +mnemonic(?STORE) -> 'STORE' ; +mnemonic(?TUPLE) -> 'TUPLE' ; +mnemonic(?ELEMENT) -> 'ELEMENT' ; +mnemonic(?MAP_EMPTY) -> 'MAP_EMPTY' ; +mnemonic(?MAP_UPDATE) -> 'MAP_UPDATE' ; +mnemonic(?MAP_DELETE) -> 'MAP_DELETE' ; +mnemonic(?MAP_MEMBER) -> 'MAP_MEMBER' ; +mnemonic(?MAP_LOOKUP) -> 'MAP_LOOKUP' ; +mnemonic(?MAP_LOOKUPD) -> 'MAP_LOOKUPD'; +mnemonic(?MAP_FROM_LIST)->'MAP_FROM_LIST' ; + +mnemonic(OP) -> {OP, nothandled}. + +m_to_op('NOP') -> ?NOP ; +m_to_op('COMMENT') -> ?COMMENT("") ; +m_to_op('RETURN') -> ?RETURN ; +m_to_op('RETURNR') -> ?RETURNR ; +m_to_op('PUSH') -> ?PUSH ; +m_to_op('JUMP') -> ?JUMP ; +m_to_op('JUMPIF') -> ?JUMPIF ; +m_to_op('INC') -> ?INC ; +m_to_op('ADD') -> ?ADD ; +m_to_op('SUB') -> ?SUB ; +m_to_op('MUL') -> ?MUL ; +m_to_op('DIV') -> ?DIV ; +m_to_op('MOD') -> ?MOD ; +m_to_op('POW') -> ?POW ; +m_to_op('AND') -> ?AND ; +m_to_op('OR') -> ?OR ; +m_to_op('NOT') -> ?NOT ; +m_to_op('LT') -> ?LT ; +m_to_op('GT') -> ?GT ; +m_to_op('ELT') -> ?ELT ; +m_to_op('EGT') -> ?EGT ; +m_to_op('EQ') -> ?EQ ; +m_to_op('NEQ') -> ?NEQ ; +m_to_op('STORE') -> ?STORE ; +m_to_op('TUPLE') -> ?TUPLE ; +m_to_op('ELEMENT') -> ?ELEMENT ; +m_to_op('MAP_EMPTY') -> ?MAP_EMPTY ; +m_to_op('MAP_UPDATE') -> ?MAP_UPDATE ; +m_to_op('MAP_DELETE') -> ?MAP_DELETE ; +m_to_op('MAP_MEMBER') -> ?MAP_MEMBER ; +m_to_op('MAP_LOOKUP') -> ?MAP_LOOKUP ; +m_to_op('MAP_LOOKUPD') -> ?MAP_LOOKUPD ; +m_to_op('MAP_FROM_LIST')->?MAP_FROM_LIST ; +m_to_op('CALL') -> ?CALL ; +m_to_op('CALL_T') -> ?CALL_T ; +m_to_op('CALL_R') -> ?CALL_R ; +m_to_op('CALL_TR') -> ?CALL_TR . + +args(?NOP) -> 0; +args(?RETURN) -> 0; +args(?INC) -> 0; + +args(?RETURNR) -> 1; +args(?PUSH) -> 1; +args(?JUMP) -> 1; +args(?CALL) -> 1; +args(?CALL_T) -> 1; +args(?TUPLE) -> 1; +args(?MAP_EMPTY) -> 1; + +args(?JUMPIF) -> 2; +args(?CALL_R) -> 2; +args(?CALL_TR) -> 2; +args(?NOT) -> 2; +args(?STORE) -> 2; +args(?MAP_FROM_LIST) -> 2; + +args(?ADD) -> 3; +args(?SUB) -> 3; +args(?MUL) -> 3; +args(?DIV) -> 3; +args(?MOD) -> 3; +args(?POW) -> 3; +args(?AND) -> 3; +args(?OR) -> 3; +args(?LT) -> 3; +args(?GT) -> 3; +args(?EGT) -> 3; +args(?ELT) -> 3; +args(?EQ) -> 3; +args(?NEQ) -> 3; +args(?MAP_MEMBER) -> 3; +args(?MAP_LOOKUP) -> 3; +args(?MAP_DELETE) -> 3; + +args(?ELEMENT) -> 4; +args(?MAP_UPDATE) -> 4; +args(?MAP_LOOKUPD) -> 4; + +args(_) -> 0. %% TODO do not allow this + +end_bb(?RETURN) -> true; +end_bb(?RETURNR)-> true; +end_bb(?JUMP) -> true; +end_bb(?JUMPIF) -> true; +end_bb(?CALL) -> true; +end_bb(?CALL_T) -> true; +end_bb(?CALL_R) -> true; +end_bb(?CALL_TR)-> true; +end_bb(_) -> false. + diff --git a/src/aefateasm.erl b/src/aefateasm.erl new file mode 100644 index 0000000..2958808 --- /dev/null +++ b/src/aefateasm.erl @@ -0,0 +1,58 @@ +-module(aefateasm). + +-export([main/1]). + +-define(OPT_SPEC, + [ {src_file, undefined, undefined, string, "Fate assembler code file"} + , {verbose, $v, "verbose", undefined, "Verbose output"} + , {help, $h, "help", undefined, "Show this message"} + , {outfile, $o, "out", string, "Output file (experimental)"} ]). + +usage() -> + getopt:usage(?OPT_SPEC, "aefateasm"). + +main(Args) -> + case getopt:parse(?OPT_SPEC, Args) of + {ok, {Opts, []}} -> + case proplists:get_value(help, Opts, false) of + false -> + assemble(Opts); + true -> + usage() + end; + + {ok, {_, NonOpts}} -> + io:format("Can't understand ~p\n\n", [NonOpts]), + usage(); + + {error, {Reason, Data}} -> + io:format("Error: ~s ~p\n\n", [Reason, Data]), + usage() + end. + +assemble(Opts) -> + case proplists:get_value(src_file, Opts, undefined) of + undefined -> + io:format("Error: no input source file\n\n"), + usage(); + File -> + assemble(File, Opts) + end. + +assemble(File, Opts) -> + Verbose = proplists:get_value(verbose, Opts, false), + case proplists:get_value(outfile, Opts, undefined) of + undefined -> + Asm = aefa_asm:read_file(File), + {Env, BC} = aefa_asm:asm_to_bytecode(Asm, Opts), + case Verbose of + true -> + io:format("Env: ~0p~n", [Env]); + false -> ok + end, + io:format("Code: ~0p~n", [BC]); + OutFile -> + aefa_asm:assemble_file(File, OutFile, Opts) + end. + + diff --git a/test/asm_code/arith.fate b/test/asm_code/arith.fate new file mode 100644 index 0000000..ffd5311 --- /dev/null +++ b/test/asm_code/arith.fate @@ -0,0 +1,26 @@ +;; CONTRACT arith + +FUNCTION add (integer, integer) : integer + ADD a arg0 arg1 + RETURN + +FUNCTION sub (integer, integer) : integer + SUB a arg0 arg1 + RETURN + +FUNCTION mul (integer, integer) : integer + MUL a arg0 arg1 + RETURN + +FUNCTION div (integer, integer) : integer + DIV a arg0 arg1 + RETURN + +FUNCTION mod (integer, integer) : integer + MOD a arg0 arg1 + RETURN + +FUNCTION pow (integer, integer) : integer + POW a arg0 arg1 + RETURN + diff --git a/test/asm_code/bool.fate b/test/asm_code/bool.fate new file mode 100644 index 0000000..d12d881 --- /dev/null +++ b/test/asm_code/bool.fate @@ -0,0 +1,14 @@ +;; CONTRACT bool + +FUNCTION and(boolean, boolean) : boolean + AND a arg0 arg1 + RETURN + +FUNCTION or(boolean, boolean) : boolean + OR a arg0 arg1 + RETURN + +FUNCTION not(boolean) : boolean + NOT a arg0 + RETURN + diff --git a/test/asm_code/comp.fate b/test/asm_code/comp.fate new file mode 100644 index 0000000..b3f8345 --- /dev/null +++ b/test/asm_code/comp.fate @@ -0,0 +1,26 @@ +;; CONTRACT comp + +FUNCTION lt(integer, integer) : boolean + LT a arg0 arg1 + RETURN + +FUNCTION gt(integer, integer) : boolean + GT a arg0 arg1 + RETURN + +FUNCTION egt(integer, integer) : boolean + EGT a arg0 arg1 + RETURN + +FUNCTION elt(integer, integer) : boolean + ELT a arg0 arg1 + RETURN + +FUNCTION eq(integer, integer) : boolean + EQ a arg0 arg1 + RETURN + +FUNCTION neq(integer, integer) : boolean + NEQ a arg0 arg1 + RETURN + diff --git a/test/asm_code/identity.fate b/test/asm_code/identity.fate new file mode 100644 index 0000000..a2350dc --- /dev/null +++ b/test/asm_code/identity.fate @@ -0,0 +1,8 @@ +;; CONTRACT: Identity +FUNCTION id(integer) -> integer + RETURN + +;; Test the code from the shell +;; _build/default/rel/aessembler/bin/aessembler console + +;; aeb_aefa:file("../../../../test/asm_code/identity.fate", []). diff --git a/test/asm_code/jumpif.fate b/test/asm_code/jumpif.fate new file mode 100644 index 0000000..05855eb --- /dev/null +++ b/test/asm_code/jumpif.fate @@ -0,0 +1,9 @@ +;; CONTRACT jumpif +FUNCTION skip(integer, integer) : integer + PUSH arg1 + PUSH 0 + EQ a a arg0 + JUMPIF a 2 + INC + JUMP 2 + RETURN diff --git a/test/asm_code/map.fate b/test/asm_code/map.fate new file mode 100644 index 0000000..beb1589 --- /dev/null +++ b/test/asm_code/map.fate @@ -0,0 +1,34 @@ +;; CONTRACT map +FUNCTION make_empty_map():{map, integer, boolean} + MAP_EMPTY a + RETURN + +FUNCTION map_update({map, integer, boolean}, integer, boolean):{map, integer, boolean} + MAP_UPDATE a arg0 arg1 arg2 + RETURN + +FUNCTION map_lookup({map, integer, boolean}, integer):boolean + MAP_LOOKUP a arg0 arg1 + RETURN + +FUNCTION map_lookup_default({map, integer, boolean}, integer): boolean + MAP_LOOKUPD a arg0 arg1 false + RETURN + +FUNCTION map_member({map, integer, boolean}, integer):boolean + MAP_MEMBER a arg0 arg1 + RETURN + +FUNCTION map_delete({map, integer, boolean}, integer):{map, integer, boolean} + MAP_DELETE a arg0 arg1 + RETURN + + +FUNCTION map_member({map, integer, boolean}, integer) : boolean + MAP_MEMBER a arg0 arg1 + RETURN + +FUNCTION map_from_list({list, {tuple, [integer, boolean]}}) : {map, integer, boolean} + MAP_FROM_LIST a arg0 + RETURN + diff --git a/test/asm_code/memory.fate b/test/asm_code/memory.fate new file mode 100644 index 0000000..e162daa --- /dev/null +++ b/test/asm_code/memory.fate @@ -0,0 +1,31 @@ +;; CONTRACT memory +FUNCTION call(integer):integer + STORE var1 arg0 + PUSH 0 + CALL write + PUSH var1 + RETURN + +FUNCTION write(integer):integer + STORE var1 arg0 + RETURNR var1 + +FUNCTION dest_add(integer, integer): integer + STORE var1 arg0 + STORE var2 arg1 + ADD var3 var1 var2 + PUSH var3 + RETURN + +FUNCTION dest_add_imm(integer):integer + STORE var1 arg0 + ADD var3 var1 2 + PUSH var3 + RETURN + +FUNCTION dest_add_stack(integer, integer): integer + STORE var1 arg0 + PUSH arg1 + ADD var3 var1 a + PUSH var3 + RETURN diff --git a/test/asm_code/remote.fate b/test/asm_code/remote.fate new file mode 100644 index 0000000..5ad09ea --- /dev/null +++ b/test/asm_code/remote.fate @@ -0,0 +1,4 @@ +;; CONTRACT remote +FUNCTION add_five(integer):integer + ADD a 5 arg0 + RETURN diff --git a/test/asm_code/test.fate b/test/asm_code/test.fate new file mode 100644 index 0000000..294e0c8 --- /dev/null +++ b/test/asm_code/test.fate @@ -0,0 +1,45 @@ +;; CONTRACT: Test +FUNCTION id(integer) -> integer + RETURN + +FUNCTION jumps() -> integer + PUSH 0 + JUMP 3 + NOP + JUMP 2 + NOP + RETURN + NOP + JUMP 1 + +FUNCTION inc(integer) -> integer + INC + INC + RETURN + +FUNCTION call(integer) -> integer + INC + CALL inc + INC + RETURN + + +FUNCTION tailcall(integer) -> integer + INC + CALL_T inc + +FUNCTION remote_call(integer) : integer + PUSH arg0 + CALL_R remote.add_five + INC + RETURN + +FUNCTION remote_tailcall(integer) : integer + PUSH arg0 + CALL_TR remote add_five + +;; Test the code from the shell +;; _build/default/rel/aessembler/bin/aessembler console + +;; aeb_aefa:file("../../../../test/asm_code/test.fate", []). +;; f(Asm), f(Env), f(BC), Asm = aefa_asm:read_file("../../../../test/asm_code/test.fate"), {Env, BC} = aefa_asm:asm_to_bytecode(Asm, []), aefa_asm:bytecode_to_fate_code(BC, []). \ No newline at end of file diff --git a/test/asm_code/tuple.fate b/test/asm_code/tuple.fate new file mode 100644 index 0000000..573166b --- /dev/null +++ b/test/asm_code/tuple.fate @@ -0,0 +1,31 @@ +;;CONTRACT tuple +FUNCTION make_0tuple():{tuple, []} + TUPLE 0 + RETURN + +FUNCTION make_2tuple(integer, integer):{tuple, [integer, integer]} + PUSH arg0 + PUSH arg1 + TUPLE 2 + RETURN + +FUNCTION make_5tuple(integer, integer, integer, integer, integer): + {tuple, [integer, integer, integer, integer, integer]} + PUSH arg0 + PUSH arg1 + PUSH arg2 + PUSH arg3 + PUSH arg4 + TUPLE 5 + RETURN + +FUNCTION element1(integer, integer): integer + PUSH arg0 + PUSH arg1 + TUPLE 2 + ELEMENT integer a 1 a + RETURN + +FUNCTION element({tuple, [integer, integer]}, integer): integer + ELEMENT integer a arg1 arg0 + RETURN -- 2.30.2 From 37f97e3837bcb4c74dc5334c1e7df376dffabcea Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Fri, 15 Feb 2019 11:33:13 +0100 Subject: [PATCH 02/29] Bump version. --- src/aebytecode.app.src | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/aebytecode.app.src b/src/aebytecode.app.src index 53b095c..20855aa 100644 --- a/src/aebytecode.app.src +++ b/src/aebytecode.app.src @@ -1,6 +1,6 @@ {application, aebytecode, - [{description, "Bytecode definitions for AEthernity VM shared with compiler."}, - {vsn, "1.0.0"}, + [{description, "Bytecode definitions, serialization and deserialization for aeternity."}, + {vsn, "2.0.0"}, {registered, []}, {applications, [kernel, -- 2.30.2 From c5a9878bd9c56f623fe2caad30a1ead3dac1f391 Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Fri, 15 Feb 2019 12:34:46 +0100 Subject: [PATCH 03/29] Rename to library standard. --- include/{aefa_data.hrl => aeb_fate_data.hrl} | 0 ...{aefa_opcodes.hrl => aeb_fate_opcodes.hrl} | 0 src/{aeblake2.erl => aeb_blake2.erl} | 2 +- src/{aefa_asm.erl => aeb_fate_asm.erl} | 52 +++++++++---------- ...efa_asm_scan.xrl => aeb_fate_asm_scan.xrl} | 2 +- src/{aefa_data.erl => aeb_fate_data.erl} | 7 ++- ...efa_encoding.erl => aeb_fate_encoding.erl} | 32 ++++++------ ...{aefa_opcodes.erl => aeb_fate_opcodes.erl} | 4 +- src/{ae_rlp.erl => aeb_rlp.erl} | 2 +- src/aefateasm.erl | 6 +-- 10 files changed, 53 insertions(+), 54 deletions(-) rename include/{aefa_data.hrl => aeb_fate_data.hrl} (100%) rename include/{aefa_opcodes.hrl => aeb_fate_opcodes.hrl} (100%) rename src/{aeblake2.erl => aeb_blake2.erl} (99%) rename src/{aefa_asm.erl => aeb_fate_asm.erl} (91%) rename src/{aefa_asm_scan.xrl => aeb_fate_asm_scan.xrl} (99%) rename src/{aefa_data.erl => aeb_fate_data.erl} (98%) rename src/{aefa_encoding.erl => aeb_fate_encoding.erl} (93%) rename src/{aefa_opcodes.erl => aeb_fate_opcodes.erl} (98%) rename src/{ae_rlp.erl => aeb_rlp.erl} (99%) diff --git a/include/aefa_data.hrl b/include/aeb_fate_data.hrl similarity index 100% rename from include/aefa_data.hrl rename to include/aeb_fate_data.hrl diff --git a/include/aefa_opcodes.hrl b/include/aeb_fate_opcodes.hrl similarity index 100% rename from include/aefa_opcodes.hrl rename to include/aeb_fate_opcodes.hrl diff --git a/src/aeblake2.erl b/src/aeb_blake2.erl similarity index 99% rename from src/aeblake2.erl rename to src/aeb_blake2.erl index 0519440..37fda39 100644 --- a/src/aeblake2.erl +++ b/src/aeb_blake2.erl @@ -5,7 +5,7 @@ %%% @end %%%============================================================================= --module(aeblake2). +-module(aeb_blake2). -export([ blake2b/2 , blake2b/3 diff --git a/src/aefa_asm.erl b/src/aeb_fate_asm.erl similarity index 91% rename from src/aefa_asm.erl rename to src/aeb_fate_asm.erl index 0b9d420..c465c50 100644 --- a/src/aefa_asm.erl +++ b/src/aeb_fate_asm.erl @@ -41,7 +41,7 @@ %%% Created : 21 Dec 2017 %%%------------------------------------------------------------------- --module(aefa_asm). +-module(aeb_fate_asm). -export([ assemble_file/3 , asm_to_bytecode/2 @@ -51,12 +51,12 @@ , to_hexstring/1 ]). --include_lib("aebytecode/include/aefa_opcodes.hrl"). +-include_lib("aebytecode/include/aeb_fate_opcodes.hrl"). -define(HASH_BYTES, 32). assemble_file(InFile, OutFile, Options) -> Asm = read_file(InFile), - {Env, BC} = aefa_asm:asm_to_bytecode(Asm, Options), + {Env, BC} = asm_to_bytecode(Asm, Options), ok = file:write_file(OutFile, BC). pp(Asm) -> @@ -68,7 +68,7 @@ format(Asm) -> format(Asm, 0). format([{comment, Comment} | Rest], Address) -> ";; " ++ Comment ++ "\n" ++ format(Rest, Address); format([Mnemonic | Rest], Address) -> - _Op = aefa_opcodes:m_to_op(Mnemonic), + _Op = aeb_fate_opcodes:m_to_op(Mnemonic), " " ++ atom_to_list(Mnemonic) ++ "\n" ++ format(Rest, Address + 1); format([],_) -> []. @@ -79,7 +79,7 @@ read_file(Filename) -> binary_to_list(File). asm_to_bytecode(AssemblerCode, Options) -> - {ok, Tokens, _} = aefa_asm_scan:scan(AssemblerCode), + {ok, Tokens, _} = aeb_fate_asm_scan:scan(AssemblerCode), case proplists:lookup(pp_tokens, Options) of {pp_tokens, true} -> @@ -148,7 +148,7 @@ deserialize(<>, , current_bb_code := Code , code := Program} = Env) -> {Rest2, OpCode} = deserialize_op(Op, Rest, Code), - case aefa_opcodes:end_bb(Op) of + case aebe_fate_opcodes:end_bb(Op) of true -> deserialize(Rest2, Env#{ bb => BB+1 , current_bb_code => [] @@ -176,40 +176,40 @@ deserialize(<<>>, #{ function := F deserialize_op(?ELEMENT, Rest, Code) -> {Type, Rest2} = deserialize_type(Rest), <> = Rest2, - {Arg0, Rest4} = aefa_encoding:deserialize_one(Rest3), - {Arg1, Rest5} = aefa_encoding:deserialize_one(Rest4), - {Arg2, Rest6} = aefa_encoding:deserialize_one(Rest5), + {Arg0, Rest4} = aeb_fate_encoding:deserialize_one(Rest3), + {Arg1, Rest5} = aeb_fate_encoding:deserialize_one(Rest4), + {Arg2, Rest6} = aeb_fate_encoding:deserialize_one(Rest5), Modifier0 = bits_to_modifier(ArgType band 2#11), Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11), Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11), - {Rest6, [{ aefa_opcodes:mnemonic(?ELEMENT) + {Rest6, [{ aeb_fate_opcodes:mnemonic(?ELEMENT) , Type , {Modifier0, Arg0} , {Modifier1, Arg1} , {Modifier2, Arg2}} | Code]}; deserialize_op(Op, Rest, Code) -> - OpName = aefa_opcodes:mnemonic(Op), - case aefa_opcodes:args(Op) of + OpName = aeb_fate_opcodes:mnemonic(Op), + case aeb_fate_opcodes:args(Op) of 0 -> {Rest, [OpName | Code]}; 1 -> <> = Rest, - {Arg, Rest3} = aefa_encoding:deserialize_one(Rest2), + {Arg, Rest3} = aeb_fate_encoding:deserialize_one(Rest2), Modifier = bits_to_modifier(ArgType), {Rest3, [{OpName, {Modifier, Arg}} | Code]}; 2 -> <> = Rest, - {Arg0, Rest3} = aefa_encoding:deserialize_one(Rest2), - {Arg1, Rest4} = aefa_encoding:deserialize_one(Rest3), + {Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2), + {Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3), Modifier0 = bits_to_modifier(ArgType band 2#11), Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11), {Rest4, [{OpName, {Modifier0, Arg0}, {Modifier1, Arg1}} | Code]}; 3 -> <> = Rest, - {Arg0, Rest3} = aefa_encoding:deserialize_one(Rest2), - {Arg1, Rest4} = aefa_encoding:deserialize_one(Rest3), - {Arg2, Rest5} = aefa_encoding:deserialize_one(Rest4), + {Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2), + {Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3), + {Arg2, Rest5} = aeb_fate_encoding:deserialize_one(Rest4), Modifier0 = bits_to_modifier(ArgType band 2#11), Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11), Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11), @@ -220,10 +220,10 @@ deserialize_op(Op, Rest, Code) -> | Code]}; 4 -> <> = Rest, - {Arg0, Rest3} = aefa_encoding:deserialize_one(Rest2), - {Arg1, Rest4} = aefa_encoding:deserialize_one(Rest3), - {Arg2, Rest5} = aefa_encoding:deserialize_one(Rest4), - {Arg3, Rest6} = aefa_encoding:deserialize_one(Rest5), + {Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2), + {Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3), + {Arg2, Rest5} = aeb_fate_encoding:deserialize_one(Rest4), + {Arg3, Rest6} = aeb_fate_encoding:deserialize_one(Rest5), Modifier0 = bits_to_modifier(ArgType band 2#11), Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11), Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11), @@ -323,7 +323,7 @@ bits_to_modifier(2#01) -> arg; bits_to_modifier(2#00) -> stack. serialize_data(_, Data) -> - aefa_encoding:serialize(Data). + aeb_fate_encoding:serialize(Data). serialize_signature({Args, RetType}) -> [serialize_type({tuple, Args}) | @@ -379,11 +379,11 @@ to_bytecode([{function,_line, 'FUNCTION'}|Rest], Address, Env, Code, Opts) -> {Fun, Rest2} = to_fun_def(Rest), to_bytecode(Rest2, Fun, Env2, [], Opts); to_bytecode([{mnemonic,_line, 'ELEMENT'}|Rest], Address, Env, Code, Opts) -> - OpCode = aefa_opcodes:m_to_op('ELEMENT'), + OpCode = aeb_fate_opcodes:m_to_op('ELEMENT'), {RetType, Rest2} = to_type(Rest), to_bytecode(Rest2, Address, Env, [RetType, OpCode|Code], Opts); to_bytecode([{mnemonic,_line, Op}|Rest], Address, Env, Code, Opts) -> - OpCode = aefa_opcodes:m_to_op(Op), + OpCode = aeb_fate_opcodes:m_to_op(Op), to_bytecode(Rest, Address, Env, [OpCode|Code], Opts); to_bytecode([{arg,_line, N}|Rest], Address, Env, Code, Opts) -> to_bytecode(Rest, Address, Env, [{arg, N}|Code], Opts); @@ -467,7 +467,7 @@ insert_fun({Name, Type, RetType}, Code, #{functions := Functions} = Env) -> insert_symbol(Id, Env) -> %% Use first 4 bytes of blake hash - {ok, <> } = aeblake2:blake2b(?HASH_BYTES, list_to_binary(Id)), + {ok, <> } = aeb_blake2:blake2b(?HASH_BYTES, list_to_binary(Id)), insert_symbol(Id, <>, Env). insert_symbol(Id, Hash, #{symbols := Symbols} = Env) -> diff --git a/src/aefa_asm_scan.xrl b/src/aeb_fate_asm_scan.xrl similarity index 99% rename from src/aefa_asm_scan.xrl rename to src/aeb_fate_asm_scan.xrl index b61d240..59bf4d9 100644 --- a/src/aefa_asm_scan.xrl +++ b/src/aeb_fate_asm_scan.xrl @@ -170,7 +170,7 @@ Erlang code. -ignore_xref([format_error/1, string/2, token/2, token/3, tokens/2, tokens/3]). --include_lib("aebytecode/include/aefa_opcodes.hrl"). +-include_lib("aebytecode/include/aeb_fate_opcodes.hrl"). parse_hex("0x" ++ Chars) -> list_to_integer(Chars, 16). diff --git a/src/aefa_data.erl b/src/aeb_fate_data.erl similarity index 98% rename from src/aefa_data.erl rename to src/aeb_fate_data.erl index 568dbd7..9426b7c 100644 --- a/src/aefa_data.erl +++ b/src/aeb_fate_data.erl @@ -1,9 +1,8 @@ -%% First draft of FATE data representation. -%% Very likely to change. +%% FATE data representation. %% --include("aefa_data.hrl"). +-include("aeb_fate_data.hrl"). --module(aefa_data). +-module(aeb_fate_data). -type fate_integer() :: ?FATE_INTEGER_T. -type fate_boolean() :: ?FATE_BOOLEAN_T. diff --git a/src/aefa_encoding.erl b/src/aeb_fate_encoding.erl similarity index 93% rename from src/aefa_encoding.erl rename to src/aeb_fate_encoding.erl index dd83e64..4a5daa2 100644 --- a/src/aefa_encoding.erl +++ b/src/aeb_fate_encoding.erl @@ -33,14 +33,14 @@ %% * Handle instructions. %% %% ------------------------------------------------------------------------ --module(aefa_encoding). +-module(aeb_fate_encoding). -export([ deserialize/1 , deserialize_one/1 , serialize/1 ]). --include("aefa_data.hrl"). +-include("aeb_fate_data.hrl"). %% Definition of tag scheme. %% This has to follow the protocol specification. @@ -85,7 +85,7 @@ %% Serialized a Fate data value into a sequence of bytes %% according to the Fate serialization specification. %% TODO: The type Fate Data is not final yet. --spec serialize(aefa_data:fate_type()) -> binary(). +-spec serialize(aeb_fate_data:fate_type()) -> binary(). serialize(?FATE_TRUE) -> <>; serialize(?FATE_FALSE) -> <>; serialize(?FATE_NIL) -> <>; %% ! Untyped @@ -104,9 +104,9 @@ serialize(String) when ?IS_FATE_STRING(String), ?FATE_STRING_SIZE(String) > 0, ?FATE_STRING_SIZE(String) >= ?SHORT_STRING_SIZE -> Bytes = ?FATE_STRING_VALUE(String), - <>; + <>; serialize(?FATE_ADDRESS(Address)) when is_binary(Address) -> - <>; + <>; serialize(?FATE_TUPLE(T)) when size(T) > 0 -> S = size(T), L = tuple_to_list(T), @@ -148,7 +148,7 @@ serialize(?FATE_VARIANT(Size, Tag, Values)) when 0 =< Size %% ----------------------------------------------------- rlp_integer(S) when S >= 0 -> - ae_rlp:encode(binary:encode_unsigned(S)). + aeb_rlp:encode(binary:encode_unsigned(S)). serialize_integer(I) when ?IS_FATE_INTEGER(I) -> V = ?FATE_INTEGER_VALUE(I), @@ -175,7 +175,7 @@ serialize_bits(B) when is_integer(B) -> Sign =:= ?POS_SIGN -> <> end. --spec deserialize(binary()) -> aefa_data:fate_type(). +-spec deserialize(binary()) -> aeb_fate_data:fate_type(). deserialize(B) -> {T, <<>>} = deserialize2(B), T. @@ -187,28 +187,28 @@ deserialize2(<>) -> deserialize2(<>) -> {?MAKE_FATE_INTEGER(-I), Rest}; deserialize2(<>) -> - {Bint, Rest2} = ae_rlp:decode_one(Rest), + {Bint, Rest2} = aeb_rlp:decode_one(Rest), {?MAKE_FATE_INTEGER(-binary:decode_unsigned(Bint) - ?SMALL_INT_SIZE), Rest2}; deserialize2(<>) -> - {Bint, Rest2} = ae_rlp:decode_one(Rest), + {Bint, Rest2} = aeb_rlp:decode_one(Rest), {?MAKE_FATE_INTEGER(binary:decode_unsigned(Bint) + ?SMALL_INT_SIZE), Rest2}; deserialize2(<>) -> - {Bint, Rest2} = ae_rlp:decode_one(Rest), + {Bint, Rest2} = aeb_rlp:decode_one(Rest), {?FATE_BITS(-binary:decode_unsigned(Bint)), Rest2}; deserialize2(<>) -> - {Bint, Rest2} = ae_rlp:decode_one(Rest), + {Bint, Rest2} = aeb_rlp:decode_one(Rest), {?FATE_BITS(binary:decode_unsigned(Bint)), Rest2}; deserialize2(<>) -> - {String, Rest2} = ae_rlp:decode_one(Rest), + {String, Rest2} = aeb_rlp:decode_one(Rest), {?MAKE_FATE_STRING(String), Rest2}; deserialize2(<>) -> String = binary:part(Rest, 0, S), Rest2 = binary:part(Rest, byte_size(Rest), - (byte_size(Rest) - S)), {?MAKE_FATE_STRING(String), Rest2}; deserialize2(<>) -> - {A, Rest2} = ae_rlp:decode_one(Rest), + {A, Rest2} = aeb_rlp:decode_one(Rest), {?FATE_ADDRESS(A), Rest2}; deserialize2(<>) -> {?FATE_TRUE, Rest}; @@ -223,7 +223,7 @@ deserialize2(<>) -> deserialize2(<>) -> {?FATE_EMPTY_STRING, Rest}; deserialize2(<>) -> - {BSize, Rest1} = ae_rlp:decode_one(Rest), + {BSize, Rest1} = aeb_rlp:decode_one(Rest), N = binary:decode_unsigned(BSize) + ?SHORT_TUPLE_SIZE, {List, Rest2} = deserialize_elements(N, Rest1), {?FATE_TUPLE(list_to_tuple(List)), Rest2}; @@ -231,7 +231,7 @@ deserialize2(<>) -> {List, Rest1} = deserialize_elements(S, Rest), {?FATE_TUPLE(list_to_tuple(List)), Rest1}; deserialize2(<>) -> - {BLength, Rest1} = ae_rlp:decode_one(Rest), + {BLength, Rest1} = aeb_rlp:decode_one(Rest), Length = binary:decode_unsigned(BLength) + ?SHORT_LIST_SIZE, {List, Rest2} = deserialize_elements(Length, Rest1), {?MAKE_FATE_LIST(List), Rest2}; @@ -239,7 +239,7 @@ deserialize2(<>) -> {List, Rest1} = deserialize_elements(S, Rest), {?MAKE_FATE_LIST(List), Rest1}; deserialize2(<>) -> - {BSize, Rest1} = ae_rlp:decode_one(Rest), + {BSize, Rest1} = aeb_rlp:decode_one(Rest), Size = binary:decode_unsigned(BSize), {List, Rest2} = deserialize_elements(2*Size, Rest1), Map = insert_kv(List, #{}), diff --git a/src/aefa_opcodes.erl b/src/aeb_fate_opcodes.erl similarity index 98% rename from src/aefa_opcodes.erl rename to src/aeb_fate_opcodes.erl index 20116e5..21ebefe 100644 --- a/src/aefa_opcodes.erl +++ b/src/aeb_fate_opcodes.erl @@ -5,7 +5,7 @@ %%% @end %%%------------------------------------------------------------------- --module(aefa_opcodes). +-module(aeb_fate_opcodes). -export([ args/1 , end_bb/1 @@ -14,7 +14,7 @@ , opcode/1 ]). --include_lib("aebytecode/include/aefa_opcodes.hrl"). +-include_lib("aebytecode/include/aeb_fate_opcodes.hrl"). %%==================================================================== diff --git a/src/ae_rlp.erl b/src/aeb_rlp.erl similarity index 99% rename from src/ae_rlp.erl rename to src/aeb_rlp.erl index 6e537b4..46a27fa 100644 --- a/src/ae_rlp.erl +++ b/src/aeb_rlp.erl @@ -8,7 +8,7 @@ %%% @end %%%------------------------------------------------------------------- --module(ae_rlp). +-module(aeb_rlp). -export([ decode/1 , decode_one/1 , encode/1 diff --git a/src/aefateasm.erl b/src/aefateasm.erl index 2958808..db7910e 100644 --- a/src/aefateasm.erl +++ b/src/aefateasm.erl @@ -43,8 +43,8 @@ assemble(File, Opts) -> Verbose = proplists:get_value(verbose, Opts, false), case proplists:get_value(outfile, Opts, undefined) of undefined -> - Asm = aefa_asm:read_file(File), - {Env, BC} = aefa_asm:asm_to_bytecode(Asm, Opts), + Asm = aeb_fate_asm:read_file(File), + {Env, BC} = aeb_fate_asm:asm_to_bytecode(Asm, Opts), case Verbose of true -> io:format("Env: ~0p~n", [Env]); @@ -52,7 +52,7 @@ assemble(File, Opts) -> end, io:format("Code: ~0p~n", [BC]); OutFile -> - aefa_asm:assemble_file(File, OutFile, Opts) + aeb_fate_asm:assemble_file(File, OutFile, Opts) end. -- 2.30.2 From afdb78b93302b3b80aa979f7b9fff220573a731d Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Fri, 15 Feb 2019 13:47:40 +0100 Subject: [PATCH 04/29] Fix erros found by dialyzer and warnings. --- .gitignore | 2 +- rebar.config | 2 +- src/aeb_fate_asm.erl | 14 +++++++++----- src/aeb_fate_data.erl | 5 ++++- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index cde56db..4ad6576 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,6 @@ rel/example_project .concrete/DEV_MODE .rebar aeb_asm_scan.erl -aefa_asm_scan.erl +aeb_fate_asm_scan.erl _build/ aefateasm diff --git a/rebar.config b/rebar.config index aae208e..6118607 100644 --- a/rebar.config +++ b/rebar.config @@ -24,7 +24,7 @@ {dialyzer, [ {warnings, [unknown]}, {plt_apps, all_deps}, - {base_plt_apps, [erts, kernel, stdlib, crypto]} + {base_plt_apps, [erts, kernel, stdlib, crypto, getopt]} ]}. diff --git a/src/aeb_fate_asm.erl b/src/aeb_fate_asm.erl index c465c50..2932e2a 100644 --- a/src/aeb_fate_asm.erl +++ b/src/aeb_fate_asm.erl @@ -10,6 +10,10 @@ %%% DUP %%% Identifiers start with a lower case letter %%% an_identifier +%%% References to function arguments start with arg +%%% arg0 +%%% References to variables/registers start with var +%%% var0 %%% Immediates can be of 9 types: %%% 1. Integers %%% 42 @@ -56,7 +60,7 @@ assemble_file(InFile, OutFile, Options) -> Asm = read_file(InFile), - {Env, BC} = asm_to_bytecode(Asm, Options), + {_Env, BC} = asm_to_bytecode(Asm, Options), ok = file:write_file(OutFile, BC). pp(Asm) -> @@ -148,7 +152,7 @@ deserialize(<>, , current_bb_code := Code , code := Program} = Env) -> {Rest2, OpCode} = deserialize_op(Op, Rest, Code), - case aebe_fate_opcodes:end_bb(Op) of + case aeb_fate_opcodes:end_bb(Op) of true -> deserialize(Rest2, Env#{ bb => BB+1 , current_bb_code => [] @@ -402,9 +406,10 @@ to_bytecode([{id,_line, ID}|Rest], Address, Env, Code, Opts) -> to_bytecode(Rest, Address, Env2, [{immediate, Hash}|Code], Opts); to_bytecode([], Address, Env, Code, Opts) -> Env2 = insert_fun(Address, Code, Env), + #{functions := Funs} = Env2, case proplists:lookup(pp_opcodes, Opts) of {pp_opcodes, true} -> - Ops = [C || {_Name, {_Sig, C}} <- maps:to_list(Env2)], + Ops = [C || {_Name, {_Sig, C}} <- maps:to_list(Funs)], io:format("opcodes ~p~n", [Ops]); none -> ok @@ -480,5 +485,4 @@ insert_symbol(Id, Hash, #{symbols := Symbols} = Env) -> {Hash, Env#{symbols => Symbols#{ Id => Hash , Hash => Id}}} end. -lookup_symbol(Id, #{symbols := Symbols} = Env) -> - maps:find(Id, Symbols). + diff --git a/src/aeb_fate_data.erl b/src/aeb_fate_data.erl index 9426b7c..243b204 100644 --- a/src/aeb_fate_data.erl +++ b/src/aeb_fate_data.erl @@ -129,7 +129,7 @@ format(M) when ?IS_FATE_MAP(M) -> "#{ " ++ format_kvs(maps:to_list(?FATE_MAP_VALUE(M))) ++" }"; -format(?FATE_ADDRESS(Address)) -> base58:binary_to_base58(Address); +format(?FATE_ADDRESS(Address)) -> address_to_base58(Address); format(V) -> exit({not_a_fate_type, V}). format_list([]) -> " ]"; @@ -167,6 +167,9 @@ base58_to_address(Base58) -> Bin = <>, Bin. +address_to_base58(<>) -> + integer_to_base58(A). + integer_to_base58(0) -> <<"1">>; integer_to_base58(Integer) -> Base58String = integer_to_base58(Integer, []), -- 2.30.2 From b95827b2d01bd503918f4f218199ee9ddbdbeb72 Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Fri, 15 Feb 2019 14:34:32 +0100 Subject: [PATCH 05/29] Parse call. --- src/aeb_fate_asm.erl | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/aeb_fate_asm.erl b/src/aeb_fate_asm.erl index 2932e2a..893cbfd 100644 --- a/src/aeb_fate_asm.erl +++ b/src/aeb_fate_asm.erl @@ -50,6 +50,7 @@ -export([ assemble_file/3 , asm_to_bytecode/2 , bytecode_to_fate_code/2 + , function_call/1 , pp/1 , read_file/1 , to_hexstring/1 @@ -63,6 +64,33 @@ assemble_file(InFile, OutFile, Options) -> {_Env, BC} = asm_to_bytecode(Asm, Options), ok = file:write_file(OutFile, BC). +function_call(String) -> + {ok, Tokens} = aeb_fate_asm_scan:scan(String), + parse_function_call(Tokens). + +parse_function_call([{id,_,Name}, {'(',_}| Rest]) -> + Args = to_args(Rest), + {Name, Args}. + + +to_args([{')', _}]) -> []; +to_args(Tokens) -> + case to_data(Tokens) of + {Arg, [{',', _} | Rest]} -> + {More, Rest2} = to_args(Rest), + {[Arg|More], Rest2}; + {Arg, [{')', _} | Rest]} -> + {[Arg], Rest} + end. + +to_data([{int,_line, Int}|Rest]) -> + {Int, Rest}; +to_data([{boolean,_line, Bool}|Rest]) -> + {Bool, Rest}; +to_data([{hash,_line, Hash}|Rest]) -> + {Hash, Rest}. + + pp(Asm) -> Listing = format(Asm), io:format("~s~n", [Listing]). -- 2.30.2 From 18eb37a8c542412df358447b703ef14061c3cc1e Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Fri, 15 Feb 2019 15:38:34 +0100 Subject: [PATCH 06/29] Fix function_call. --- src/aeb_fate_asm.erl | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/aeb_fate_asm.erl b/src/aeb_fate_asm.erl index 893cbfd..3a9b24d 100644 --- a/src/aeb_fate_asm.erl +++ b/src/aeb_fate_asm.erl @@ -65,15 +65,16 @@ assemble_file(InFile, OutFile, Options) -> ok = file:write_file(OutFile, BC). function_call(String) -> - {ok, Tokens} = aeb_fate_asm_scan:scan(String), + {ok, Tokens, _} = aeb_fate_asm_scan:scan(String), parse_function_call(Tokens). parse_function_call([{id,_,Name}, {'(',_}| Rest]) -> - Args = to_args(Rest), - {Name, Args}. + {Args, []} = to_args(Rest), + aeb_fate_encoding:serialize( + {tuple, {mk_hash(Name), {tuple, list_to_tuple(Args)}}}). -to_args([{')', _}]) -> []; +to_args([{')', _}]) -> {[], []}; to_args(Tokens) -> case to_data(Tokens) of {Arg, [{',', _} | Rest]} -> @@ -498,10 +499,14 @@ insert_fun({Name, Type, RetType}, Code, #{functions := Functions} = Env) -> functions => Functions#{Hash => {{Type, RetType}, lists:reverse(Code)}} }. -insert_symbol(Id, Env) -> +mk_hash(Id) -> %% Use first 4 bytes of blake hash {ok, <> } = aeb_blake2:blake2b(?HASH_BYTES, list_to_binary(Id)), - insert_symbol(Id, <>, Env). + <>. + +insert_symbol(Id, Env) -> + Hash = mk_hash(Id), + insert_symbol(Id, Hash, Env). insert_symbol(Id, Hash, #{symbols := Symbols} = Env) -> case maps:find(Hash, Symbols) of -- 2.30.2 From 08e169c3b2f3959d263160b13f664628d31d76f1 Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Fri, 15 Feb 2019 16:14:20 +0100 Subject: [PATCH 07/29] New format for functions, signatures and code. --- src/aeb_fate_asm.erl | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/aeb_fate_asm.erl b/src/aeb_fate_asm.erl index 3a9b24d..da2b02d 100644 --- a/src/aeb_fate_asm.erl +++ b/src/aeb_fate_asm.erl @@ -153,27 +153,28 @@ deserialize(<>, Env2 = Env#{function => {<>, Sig}}, deserialize(Rest2, Env2); deserialize(<>, - #{ function := F + #{ function := {F, Sig} , bb := BB , current_bb_code := Code , code := Program , functions := Funs} = Env) -> - {Sig, Rest2} = deserialize_signature(Rest), + {NewSig, Rest2} = deserialize_signature(Rest), case Code of [] -> Env2 = Env#{ bb => 0 , current_bb_code => [] - , function => {<>, Sig} + , function => {<>, NewSig} , code => #{} - , functions => Funs#{F => Program}}, + , functions => Funs#{F => {Sig, Program}}}, deserialize(Rest2, Env2); _ -> Env2 = Env#{ bb => 0 , current_bb_code => [] - , function => {<>, Sig} + , function => {<>, NewSig} , code => #{} , functions => - Funs#{F => Program#{ BB => lists:reverse(Code)}}}, + Funs#{F => {Sig, + Program#{ BB => lists:reverse(Code)}}}}, deserialize(Rest2, Env2) end; deserialize(<>, @@ -190,7 +191,7 @@ deserialize(<>, false -> deserialize(Rest2, Env#{ current_bb_code => OpCode}) end; -deserialize(<<>>, #{ function := F +deserialize(<<>>, #{ function := {F, Sig} , bb := BB , current_bb_code := Code , code := Program @@ -204,7 +205,7 @@ deserialize(<<>>, #{ function := F , current_bb_code => [] , function => none , code => #{} - , functions => Funs#{F => FunctionCode}}. + , functions => Funs#{F => {Sig, FunctionCode}}}. deserialize_op(?ELEMENT, Rest, Code) -> {Type, Rest2} = deserialize_type(Rest), -- 2.30.2 From 36f910aff4141bc1b3beb86b1a29bc1e6db00a36 Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Fri, 15 Feb 2019 21:44:36 +0100 Subject: [PATCH 08/29] Add SWITCH opcodes. --- include/aeb_fate_opcodes.hrl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/aeb_fate_opcodes.hrl b/include/aeb_fate_opcodes.hrl index e1f107c..6a5033c 100644 --- a/include/aeb_fate_opcodes.hrl +++ b/include/aeb_fate_opcodes.hrl @@ -102,6 +102,11 @@ -define('BLAKE2B' , 16#67). -define('RETURNR' , 16#68). -define('MAP_LOOKUPD' , 16#69). +-define('SWITCH_V2' , 16#6a). +-define('SWITCH_V3' , 16#6b). +-define('SWITCH_V4' , 16#6c). +-define('SWITCH_V5' , 16#6d). +-define('BITS_ALL_N' , 16#6e). -define('FUNCTION' , 16#fe). -define('EXTEND' , 16#ff). -- 2.30.2 From 16644ded720a87a441ea163d0c208c30bd82abee Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Mon, 18 Feb 2019 18:09:00 +0100 Subject: [PATCH 09/29] Handle most ops. --- include/aeb_fate_opcodes.hrl | 4 + src/aeb_fate_opcodes.erl | 382 +++++++++++++++++++++++++++-------- test/asm_code/jumpif.fate | 2 +- test/asm_code/test.fate | 12 +- 4 files changed, 307 insertions(+), 93 deletions(-) diff --git a/include/aeb_fate_opcodes.hrl b/include/aeb_fate_opcodes.hrl index 6a5033c..b2c5da0 100644 --- a/include/aeb_fate_opcodes.hrl +++ b/include/aeb_fate_opcodes.hrl @@ -107,6 +107,10 @@ -define('SWITCH_V4' , 16#6c). -define('SWITCH_V5' , 16#6d). -define('BITS_ALL_N' , 16#6e). +-define('BITS_NONEA' , 16#6f). +-define('BITS_ALLA' , 16#70). +-define('INCA' , 16#71). +-define('DECA' , 16#72). -define('FUNCTION' , 16#fe). -define('EXTEND' , 16#ff). diff --git a/src/aeb_fate_opcodes.erl b/src/aeb_fate_opcodes.erl index 21ebefe..584616e 100644 --- a/src/aeb_fate_opcodes.erl +++ b/src/aeb_fate_opcodes.erl @@ -24,87 +24,243 @@ opcode(X) when X >= 0, X =< 255 -> X; opcode({comment,X}) -> ?COMMENT(X). -mnemonic(?NOP) -> 'NOP' ; -mnemonic(?RETURN) -> 'RETURN' ; -mnemonic(?RETURNR) -> 'RETURNR' ; -mnemonic(?PUSH) -> 'PUSH' ; -mnemonic(?JUMP) -> 'JUMP' ; -mnemonic(?JUMPIF) -> 'JUMPIF' ; -mnemonic(?INC) -> 'INC' ; -mnemonic(?CALL) -> 'CALL' ; -mnemonic(?CALL_T) -> 'CALL_T' ; -mnemonic(?CALL_R) -> 'CALL_R' ; -mnemonic(?CALL_TR) -> 'CALL_TR' ; -mnemonic(?ADD) -> 'ADD' ; -mnemonic(?SUB) -> 'SUB' ; -mnemonic(?MUL) -> 'MUL' ; -mnemonic(?DIV) -> 'DIV' ; -mnemonic(?MOD) -> 'MOD' ; -mnemonic(?POW) -> 'POW' ; -mnemonic(?AND) -> 'AND' ; -mnemonic(?OR) -> 'OR' ; -mnemonic(?NOT) -> 'NOT' ; -mnemonic(?LT) -> 'LT' ; -mnemonic(?GT) -> 'GT' ; -mnemonic(?EGT) -> 'EGT' ; -mnemonic(?ELT) -> 'ELT' ; -mnemonic(?EQ) -> 'EQ' ; -mnemonic(?NEQ) -> 'NEQ' ; -mnemonic(?STORE) -> 'STORE' ; -mnemonic(?TUPLE) -> 'TUPLE' ; -mnemonic(?ELEMENT) -> 'ELEMENT' ; -mnemonic(?MAP_EMPTY) -> 'MAP_EMPTY' ; -mnemonic(?MAP_UPDATE) -> 'MAP_UPDATE' ; -mnemonic(?MAP_DELETE) -> 'MAP_DELETE' ; -mnemonic(?MAP_MEMBER) -> 'MAP_MEMBER' ; -mnemonic(?MAP_LOOKUP) -> 'MAP_LOOKUP' ; -mnemonic(?MAP_LOOKUPD) -> 'MAP_LOOKUPD'; -mnemonic(?MAP_FROM_LIST)->'MAP_FROM_LIST' ; +mnemonic(?NOP) -> 'NOP' ; +mnemonic(?RETURN) -> 'RETURN' ; +mnemonic(?CALL) -> 'CALL' ; +mnemonic(?CALL_R) -> 'CALL_R' ; +mnemonic(?CALL_T) -> 'CALL_T' ; +mnemonic(?CALL_TR) -> 'CALL_TR' ; +mnemonic(?JUMP) -> 'JUMP' ; +mnemonic(?JUMPIF) -> 'JUMPIF' ; +mnemonic(?PUSH) -> 'PUSH' ; +mnemonic(?DUP) -> 'DUP' ; +mnemonic(?POP) -> 'POP' ; +mnemonic(?STORE) -> 'STORE' ; +mnemonic(?ADD) -> 'ADD' ; +mnemonic(?MUL) -> 'MUL' ; +mnemonic(?SUB) -> 'SUB' ; +mnemonic(?DIV) -> 'DIV' ; +mnemonic(?MOD) -> 'MOD' ; +mnemonic(?POW) -> 'POW' ; +mnemonic(?LT) -> 'LT' ; +mnemonic(?GT) -> 'GT' ; +mnemonic(?EQ) -> 'EQ' ; +mnemonic(?ELT) -> 'ELT' ; +mnemonic(?EGT) -> 'EGT' ; +mnemonic(?NEQ) -> 'NEQ' ; +mnemonic(?AND) -> 'AND' ; +mnemonic(?OR) -> 'OR' ; +mnemonic(?NOT) -> 'NOT' ; +mnemonic(?TUPLE) -> 'TUPLE' ; +mnemonic(?ELEMENT) -> 'ELEMENT' ; +mnemonic(?MAP_EMPTY) -> 'MAP_EMPTY' ; +mnemonic(?MAP_LOOKUP) -> 'MAP_LOOKUP' ; +mnemonic(?MAP_UPDATE) -> 'MAP_UPDATE' ; +mnemonic(?MAP_DELETE) -> 'MAP_DELETE' ; +mnemonic(?MAP_MEMBER) -> 'MAP_MEMBER' ; +mnemonic(?MAP_FROM_LIST) -> 'MAP_FROM_LIST' ; +mnemonic(?NIL) -> 'NIL' ; +mnemonic(?IS_NIL) -> 'IS_NIL' ; +mnemonic(?CONS) -> 'CONS' ; +mnemonic(?HD) -> 'HD' ; +mnemonic(?TL) -> 'TL' ; +mnemonic(?LENGTH) -> 'LENGTH' ; +mnemonic(?STR_EQ) -> 'STR_EQ' ; +mnemonic(?STR_JOIN) -> 'STR_JOIN' ; +mnemonic(?ADDR_TO_STR) -> 'ADDR_TO_STR' ; +mnemonic(?STR_REVERSE) -> 'STR_REVERSE' ; +mnemonic(?INT_TO_ADDR) -> 'INT_TO_ADDR' ; +mnemonic(?VARIANT) -> 'VARIANT' ; +mnemonic(?VARIANT_TEST) -> 'VARIANT_TEST' ; +mnemonic(?VARIANT_ELEMENT) -> 'VARIANT_ELEMENT' ; +mnemonic(?BITS_NONE) -> 'BITS_NONE' ; +mnemonic(?BITS_NONEA) -> 'BITS_NONEA' ; +mnemonic(?BITS_ALL) -> 'BITS_ALL' ; +mnemonic(?BITS_ALLA) -> 'BITS_ALLA' ; +mnemonic(?BITS_SET) -> 'BITS_SET' ; +mnemonic(?BITS_CLEAR) -> 'BITS_CLEAR' ; +mnemonic(?BITS_TEST) -> 'BITS_TEST' ; +mnemonic(?BITS_SUM) -> 'BITS_SUM' ; +mnemonic(?BITS_OR) -> 'BITS_OR' ; +mnemonic(?BITS_AND) -> 'BITS_AND' ; +mnemonic(?BITS_DIFF) -> 'BITS_DIFF' ; +mnemonic(?ADDRESS) -> 'ADDRESS' ; +mnemonic(?BALANCE) -> 'BALANCE' ; +mnemonic(?ORIGIN) -> 'ORIGIN' ; +mnemonic(?CALLER) -> 'CALLER' ; +mnemonic(?GASPRICE) -> 'GASPRICE' ; +mnemonic(?BLOCKHASH) -> 'BLOCKHASH' ; +mnemonic(?BENEFICIARY) -> 'BENEFICIARY' ; +mnemonic(?TIMESTAMP) -> 'TIMESTAMP' ; +mnemonic(?NUMBER) -> 'NUMBER' ; +mnemonic(?DIFFICULTY) -> 'DIFFICULTY' ; +mnemonic(?GASLIMIT) -> 'GASLIMIT' ; +mnemonic(?GAS) -> 'GAS' ; +mnemonic(?LOG0) -> 'LOG0' ; +mnemonic(?LOG1) -> 'LOG1' ; +mnemonic(?LOG2) -> 'LOG2' ; +mnemonic(?LOG3) -> 'LOG3' ; +mnemonic(?LOG4) -> 'LOG4' ; +mnemonic(?ABORT) -> 'ABORT' ; +mnemonic(?EXIT) -> 'EXIT' ; +mnemonic(?DEACTIVATE) -> 'DEACTIVATE' ; +mnemonic(?INC) -> 'INC' ; +mnemonic(?DEC) -> 'DEC' ; +mnemonic(?INCA) -> 'INCA' ; +mnemonic(?DECA) -> 'DECA' ; +mnemonic(?INT_TO_STR) -> 'INT_TO_STR' ; +mnemonic(?SPEND) -> 'SPEND' ; +mnemonic(?ORACLE_REGISTER) -> 'ORACLE_REGISTER' ; +mnemonic(?ORACLE_QUERY) -> 'ORACLE_QUERY' ; +mnemonic(?ORACLE_RESPOND) -> 'ORACLE_RESPOND' ; +mnemonic(?ORACLE_EXTEND) -> 'ORACLE_EXTEND' ; +mnemonic(?ORACLE_GET_ANSWER) -> 'ORACLE_GET_ANSWER' ; +mnemonic(?ORACLE_GET_QUESTION) -> 'ORACLE_GET_QUESTION' ; +mnemonic(?ORACLE_QUERY_FEE) -> 'ORACLE_QUERY_FEE' ; +mnemonic(?AENS_RESOLVE) -> 'AENS_RESOLVE' ; +mnemonic(?AENS_PRECLAIM) -> 'AENS_PRECLAIM' ; +mnemonic(?AENS_CLAIM) -> 'AENS_CLAIM' ; +mnemonic(?AENS_UPDATE) -> 'AENS_UPDATE' ; +mnemonic(?AENS_TRANSFER) -> 'AENS_TRANSFER' ; +mnemonic(?AENS_REVOKE) -> 'AENS_REVOKE' ; +mnemonic(?ECVERIFY) -> 'ECVERIFY' ; +mnemonic(?SHA3) -> 'SHA3' ; +mnemonic(?SHA256) -> 'SHA256' ; +mnemonic(?BLAKE2B) -> 'BLAKE2B' ; +mnemonic(?RETURNR) -> 'RETURNR' ; +mnemonic(?MAP_LOOKUPD) -> 'MAP_LOOKUPD' ; +mnemonic(?SWITCH_V2) -> 'SWITCH_V2' ; +mnemonic(?SWITCH_V3) -> 'SWITCH_V3' ; +mnemonic(?SWITCH_V4) -> 'SWITCH_V4' ; +mnemonic(?SWITCH_V5) -> 'SWITCH_V5' ; +mnemonic(?BITS_ALL_N) -> 'BITS_ALL_N' ; +mnemonic(?FUNCTION) -> 'FUNCTION' ; +mnemonic(?EXTEND) -> 'EXTEND'. -mnemonic(OP) -> {OP, nothandled}. -m_to_op('NOP') -> ?NOP ; -m_to_op('COMMENT') -> ?COMMENT("") ; -m_to_op('RETURN') -> ?RETURN ; -m_to_op('RETURNR') -> ?RETURNR ; -m_to_op('PUSH') -> ?PUSH ; -m_to_op('JUMP') -> ?JUMP ; -m_to_op('JUMPIF') -> ?JUMPIF ; -m_to_op('INC') -> ?INC ; -m_to_op('ADD') -> ?ADD ; -m_to_op('SUB') -> ?SUB ; -m_to_op('MUL') -> ?MUL ; -m_to_op('DIV') -> ?DIV ; -m_to_op('MOD') -> ?MOD ; -m_to_op('POW') -> ?POW ; -m_to_op('AND') -> ?AND ; -m_to_op('OR') -> ?OR ; -m_to_op('NOT') -> ?NOT ; -m_to_op('LT') -> ?LT ; -m_to_op('GT') -> ?GT ; -m_to_op('ELT') -> ?ELT ; -m_to_op('EGT') -> ?EGT ; -m_to_op('EQ') -> ?EQ ; -m_to_op('NEQ') -> ?NEQ ; -m_to_op('STORE') -> ?STORE ; -m_to_op('TUPLE') -> ?TUPLE ; -m_to_op('ELEMENT') -> ?ELEMENT ; -m_to_op('MAP_EMPTY') -> ?MAP_EMPTY ; -m_to_op('MAP_UPDATE') -> ?MAP_UPDATE ; -m_to_op('MAP_DELETE') -> ?MAP_DELETE ; -m_to_op('MAP_MEMBER') -> ?MAP_MEMBER ; -m_to_op('MAP_LOOKUP') -> ?MAP_LOOKUP ; -m_to_op('MAP_LOOKUPD') -> ?MAP_LOOKUPD ; -m_to_op('MAP_FROM_LIST')->?MAP_FROM_LIST ; -m_to_op('CALL') -> ?CALL ; -m_to_op('CALL_T') -> ?CALL_T ; -m_to_op('CALL_R') -> ?CALL_R ; -m_to_op('CALL_TR') -> ?CALL_TR . +m_to_op('NOP') -> ?NOP ; +m_to_op('RETURN') -> ?RETURN ; +m_to_op('CALL') -> ?CALL ; +m_to_op('CALL_R') -> ?CALL_R ; +m_to_op('CALL_T') -> ?CALL_T ; +m_to_op('CALL_TR') -> ?CALL_TR ; +m_to_op('JUMP') -> ?JUMP ; +m_to_op('JUMPIF') -> ?JUMPIF ; +m_to_op('PUSH') -> ?PUSH ; +m_to_op('DUP') -> ?DUP ; +m_to_op('POP') -> ?POP ; +m_to_op('STORE') -> ?STORE ; +m_to_op('ADD') -> ?ADD ; +m_to_op('MUL') -> ?MUL ; +m_to_op('SUB') -> ?SUB ; +m_to_op('DIV') -> ?DIV ; +m_to_op('MOD') -> ?MOD ; +m_to_op('POW') -> ?POW ; +m_to_op('LT') -> ?LT ; +m_to_op('GT') -> ?GT ; +m_to_op('EQ') -> ?EQ ; +m_to_op('ELT') -> ?ELT ; +m_to_op('EGT') -> ?EGT ; +m_to_op('NEQ') -> ?NEQ ; +m_to_op('AND') -> ?AND ; +m_to_op('OR') -> ?OR ; +m_to_op('NOT') -> ?NOT ; +m_to_op('TUPLE') -> ?TUPLE ; +m_to_op('ELEMENT') -> ?ELEMENT ; +m_to_op('MAP_EMPTY') -> ?MAP_EMPTY ; +m_to_op('MAP_LOOKUP') -> ?MAP_LOOKUP ; +m_to_op('MAP_UPDATE') -> ?MAP_UPDATE ; +m_to_op('MAP_DELETE') -> ?MAP_DELETE ; +m_to_op('MAP_MEMBER') -> ?MAP_MEMBER ; +m_to_op('MAP_FROM_LIST') -> ?MAP_FROM_LIST ; +m_to_op('NIL') -> ?NIL ; +m_to_op('IS_NIL') -> ?IS_NIL ; +m_to_op('CONS') -> ?CONS ; +m_to_op('HD') -> ?HD ; +m_to_op('TL') -> ?TL ; +m_to_op('LENGTH') -> ?LENGTH ; +m_to_op('STR_EQ') -> ?STR_EQ ; +m_to_op('STR_JOIN') -> ?STR_JOIN ; +m_to_op('ADDR_TO_STR') -> ?ADDR_TO_STR ; +m_to_op('STR_REVERSE') -> ?STR_REVERSE ; +m_to_op('INT_TO_ADDR') -> ?INT_TO_ADDR ; +m_to_op('VARIANT') -> ?VARIANT ; +m_to_op('VARIANT_TEST') -> ?VARIANT_TEST ; +m_to_op('VARIANT_ELEMENT') -> ?VARIANT_ELEMENT ; +m_to_op('BITS_NONEA') -> ?BITS_NONEA ; +m_to_op('BITS_ALL') -> ?BITS_ALL ; +m_to_op('BITS_ALLA') -> ?BITS_ALLA ; +m_to_op('BITS_SET') -> ?BITS_SET ; +m_to_op('BITS_CLEAR') -> ?BITS_CLEAR ; +m_to_op('BITS_TEST') -> ?BITS_TEST ; +m_to_op('BITS_SUM') -> ?BITS_SUM ; +m_to_op('BITS_OR') -> ?BITS_OR ; +m_to_op('BITS_AND') -> ?BITS_AND ; +m_to_op('BITS_DIFF') -> ?BITS_DIFF ; +m_to_op('ADDRESS') -> ?ADDRESS ; +m_to_op('BALANCE') -> ?BALANCE ; +m_to_op('ORIGIN') -> ?ORIGIN ; +m_to_op('CALLER') -> ?CALLER ; +m_to_op('GASPRICE') -> ?GASPRICE ; +m_to_op('BLOCKHASH') -> ?BLOCKHASH ; +m_to_op('BENEFICIARY') -> ?BENEFICIARY ; +m_to_op('TIMESTAMP') -> ?TIMESTAMP ; +m_to_op('NUMBER') -> ?NUMBER ; +m_to_op('DIFFICULTY') -> ?DIFFICULTY ; +m_to_op('GASLIMIT') -> ?GASLIMIT ; +m_to_op('GAS') -> ?GAS ; +m_to_op('LOG0') -> ?LOG0 ; +m_to_op('LOG1') -> ?LOG1 ; +m_to_op('LOG2') -> ?LOG2 ; +m_to_op('LOG3') -> ?LOG3 ; +m_to_op('LOG4') -> ?LOG4 ; +m_to_op('ABORT') -> ?ABORT ; +m_to_op('EXIT') -> ?EXIT ; +m_to_op('DEACTIVATE') -> ?DEACTIVATE ; +m_to_op('INC') -> ?INC ; +m_to_op('DEC') -> ?DEC ; +m_to_op('INCA') -> ?INCA ; +m_to_op('DECA') -> ?DECA ; +m_to_op('INT_TO_STR') -> ?INT_TO_STR ; +m_to_op('SPEND') -> ?SPEND ; +m_to_op('ORACLE_REGISTER') -> ?ORACLE_REGISTER ; +m_to_op('ORACLE_QUERY') -> ?ORACLE_QUERY ; +m_to_op('ORACLE_RESPOND') -> ?ORACLE_RESPOND ; +m_to_op('ORACLE_EXTEND') -> ?ORACLE_EXTEND ; +m_to_op('ORACLE_GET_ANSWER') -> ?ORACLE_GET_ANSWER ; +m_to_op('ORACLE_GET_QUESTION') -> ?ORACLE_GET_QUESTION ; +m_to_op('ORACLE_QUERY_FEE') -> ?ORACLE_QUERY_FEE ; +m_to_op('AENS_RESOLVE') -> ?AENS_RESOLVE ; +m_to_op('AENS_PRECLAIM') -> ?AENS_PRECLAIM ; +m_to_op('AENS_CLAIM') -> ?AENS_CLAIM ; +m_to_op('AENS_UPDATE') -> ?AENS_UPDATE ; +m_to_op('AENS_TRANSFER') -> ?AENS_TRANSFER ; +m_to_op('AENS_REVOKE') -> ?AENS_REVOKE ; +m_to_op('ECVERIFY') -> ?ECVERIFY ; +m_to_op('SHA3') -> ?SHA3 ; +m_to_op('SHA256') -> ?SHA256 ; +m_to_op('BLAKE2B') -> ?BLAKE2B ; +m_to_op('RETURNR') -> ?RETURNR ; +m_to_op('MAP_LOOKUPD') -> ?MAP_LOOKUPD ; +m_to_op('SWITCH_V2') -> ?SWITCH_V2 ; +m_to_op('SWITCH_V3') -> ?SWITCH_V3 ; +m_to_op('SWITCH_V4') -> ?SWITCH_V4 ; +m_to_op('SWITCH_V5') -> ?SWITCH_V5 ; +m_to_op('BITS_ALL_N') -> ?BITS_ALL_N ; +m_to_op('FUNCTION') -> ?FUNCTION ; +m_to_op('EXTEND') -> ?EXTEND. -args(?NOP) -> 0; -args(?RETURN) -> 0; -args(?INC) -> 0; + +args(?NOP) -> 0; +args(?RETURN) -> 0; +args(?INCA) -> 0; +args(?DECA) -> 0; +args(?BITS_NONEA) -> 0; +args(?BITS_ALLA) -> 0; + +args(?INC) -> 1; +args(?DEC) -> 1; args(?RETURNR) -> 1; args(?PUSH) -> 1; args(?JUMP) -> 1; @@ -112,14 +268,43 @@ args(?CALL) -> 1; args(?CALL_T) -> 1; args(?TUPLE) -> 1; args(?MAP_EMPTY) -> 1; +args(?DUP) -> 1; +args(?POP) -> 1; +args(?NIL) -> 1; +args(?BITS_NONE) -> 1; +args(?BITS_ALL) -> 1; +args(?ADDRESS) -> 1; +args(?BALANCE) -> 1; +args(?ORIGIN) -> 1; +args(?CALLER) -> 1; +args(?GASPRICE) -> 1; +args(?BLOCKHASH) -> 1; +args(?BENEFICIARY) -> 1; +args(?TIMESTAMP) -> 1; +args(?NUMBER) -> 1; +args(?DIFFICULTY)-> 1; +args(?GASLIMIT) -> 1; +args(?GAS) -> 1; +args(?ABORT) -> 1; +args(?EXIT) -> 1; args(?JUMPIF) -> 2; args(?CALL_R) -> 2; args(?CALL_TR) -> 2; +args(?HD) -> 2; +args(?TL) -> 2; args(?NOT) -> 2; args(?STORE) -> 2; +args(?LENGTH) -> 2; +args(?IS_NIL) -> 2; +args(?BITS_SUM) -> 2; +args(?BITS_ALL_N) -> 2; +args(?ADDR_TO_STR) -> 2; +args(?STR_REVERSE) -> 2; +args(?INT_TO_ADDR) -> 2; args(?MAP_FROM_LIST) -> 2; + args(?ADD) -> 3; args(?SUB) -> 3; args(?MUL) -> 3; @@ -134,23 +319,48 @@ args(?EGT) -> 3; args(?ELT) -> 3; args(?EQ) -> 3; args(?NEQ) -> 3; +args(?CONS) -> 3; +args(?STR_EQ) -> 3; +args(?STR_JOIN) -> 3; args(?MAP_MEMBER) -> 3; args(?MAP_LOOKUP) -> 3; args(?MAP_DELETE) -> 3; +args(?BITS_OR) -> 3; +args(?BITS_AND) -> 3; +args(?BITS_SET) -> 3; +args(?BITS_DIFF) -> 3; +args(?BITS_TEST) -> 3; +args(?BITS_CLEAR) -> 3; +args(?VARIANT_TEST) -> 3; +args(?VARIANT_ELEMENT) -> 3; +args(?INT_TO_STR) -> 3; +args(?SWITCH_V2) -> 3; +args(?SWITCH_V3) -> 4; args(?ELEMENT) -> 4; +args(?VARIANT) -> 4; args(?MAP_UPDATE) -> 4; args(?MAP_LOOKUPD) -> 4; +args(?SWITCH_V4) -> 5; + +args(?SWITCH_V5) -> 6; + args(_) -> 0. %% TODO do not allow this -end_bb(?RETURN) -> true; -end_bb(?RETURNR)-> true; -end_bb(?JUMP) -> true; -end_bb(?JUMPIF) -> true; -end_bb(?CALL) -> true; -end_bb(?CALL_T) -> true; -end_bb(?CALL_R) -> true; -end_bb(?CALL_TR)-> true; -end_bb(_) -> false. +end_bb(?RETURN) -> true; +end_bb(?RETURNR) -> true; +end_bb(?JUMP) -> true; +end_bb(?JUMPIF) -> true; +end_bb(?CALL) -> true; +end_bb(?CALL_T) -> true; +end_bb(?CALL_R) -> true; +end_bb(?CALL_TR) -> true; +end_bb(?SWITCH_V2) -> true; +end_bb(?SWITCH_V3) -> true; +end_bb(?SWITCH_V4) -> true; +end_bb(?SWITCH_V5) -> true; +end_bb(?ABORT) -> true; +end_bb(?EXIT) -> true; +end_bb(_) -> false. diff --git a/test/asm_code/jumpif.fate b/test/asm_code/jumpif.fate index 05855eb..d95aba9 100644 --- a/test/asm_code/jumpif.fate +++ b/test/asm_code/jumpif.fate @@ -4,6 +4,6 @@ FUNCTION skip(integer, integer) : integer PUSH 0 EQ a a arg0 JUMPIF a 2 - INC + INCA JUMP 2 RETURN diff --git a/test/asm_code/test.fate b/test/asm_code/test.fate index 294e0c8..aae9102 100644 --- a/test/asm_code/test.fate +++ b/test/asm_code/test.fate @@ -13,25 +13,25 @@ FUNCTION jumps() -> integer JUMP 1 FUNCTION inc(integer) -> integer - INC - INC + INCA + INCA RETURN FUNCTION call(integer) -> integer - INC + INCA CALL inc - INC + INCA RETURN FUNCTION tailcall(integer) -> integer - INC + INCA CALL_T inc FUNCTION remote_call(integer) : integer PUSH arg0 CALL_R remote.add_five - INC + INCA RETURN FUNCTION remote_tailcall(integer) : integer -- 2.30.2 From 7b671d218727f4645992c87418483246a2f9258e Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Mon, 18 Feb 2019 18:13:40 +0100 Subject: [PATCH 10/29] Lexer for inc and switch. --- src/aeb_fate_asm_scan.xrl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/aeb_fate_asm_scan.xrl b/src/aeb_fate_asm_scan.xrl index 59bf4d9..889ac2d 100644 --- a/src/aeb_fate_asm_scan.xrl +++ b/src/aeb_fate_asm_scan.xrl @@ -39,7 +39,10 @@ CALL_T : {token, {mnemonic, TokenLine, 'CALL_T'}}. CALL_TR : {token, {mnemonic, TokenLine, 'CALL_TR'}}. JUMP : {token, {mnemonic, TokenLine, 'JUMP'}}. JUMPIF : {token, {mnemonic, TokenLine, 'JUMPIF'}}. -SWITCH : {token, {mnemonic, TokenLine, 'SWITCH'}}. +SWITCH_V2 : {token, {mnemonic, TokenLine, 'SWITCH_V2'}}. +SWITCH_V3 : {token, {mnemonic, TokenLine, 'SWITCH_V3'}}. +SWITCH_V4 : {token, {mnemonic, TokenLine, 'SWITCH_V4'}}. +SWITCH_V5 : {token, {mnemonic, TokenLine, 'SWITCH_V5'}}. PUSH : {token, {mnemonic, TokenLine, 'PUSH'}}. DUP : {token, {mnemonic, TokenLine, 'DUP'}}. @@ -56,6 +59,8 @@ POW : {token, {mnemonic, TokenLine, 'POW'}}. INC : {token, {mnemonic, TokenLine, 'INC'}}. DEC : {token, {mnemonic, TokenLine, 'DEC'}}. +INCA : {token, {mnemonic, TokenLine, 'INCA'}}. +DECA : {token, {mnemonic, TokenLine, 'DECA'}}. LT : {token, {mnemonic, TokenLine, 'LT'}}. GT : {token, {mnemonic, TokenLine, 'GT'}}. @@ -99,7 +104,10 @@ VARIANT_TEST : {token, {mnemonic, TokenLine, 'VARIANT_TEST'}}. VARIANT_ELEMENT : {token, {mnemonic, TokenLine, 'VARIANT_ELEMENT'}}. BITS_NONE : {token, {mnemonic, TokenLine, 'BITS_NONE'}}. +BITS_NONEA : {token, {mnemonic, TokenLine, 'BITS_NONEA'}}. BITS_ALL : {token, {mnemonic, TokenLine, 'BITS_ALL'}}. +BITS_ALLA : {token, {mnemonic, TokenLine, 'BITS_ALLA'}}. +BITS_ALL_N : {token, {mnemonic, TokenLine, 'BITS_ALL_N'}}. BITS_SET : {token, {mnemonic, TokenLine, 'BITS_SET'}}. BITS_CLEAR : {token, {mnemonic, TokenLine, 'BITS_CLEAR'}}. BITS_TEST : {token, {mnemonic, TokenLine, 'BITS_TEST'}}. -- 2.30.2 From 3ddae5e6742a7737f37f1977d588c02825cd4542 Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Tue, 19 Feb 2019 11:50:23 +0100 Subject: [PATCH 11/29] Code generation api. --- src/aeb_fate_code.erl | 231 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 src/aeb_fate_code.erl diff --git a/src/aeb_fate_code.erl b/src/aeb_fate_code.erl new file mode 100644 index 0000000..75e28b0 --- /dev/null +++ b/src/aeb_fate_code.erl @@ -0,0 +1,231 @@ +-module(aeb_fate_code). + +% TODO: rewrite to explicit exports. +-compile(export_all). + +%-export([]). + +-define(i(__X__), {immediate, __X__ }). + +return() -> + 'RETURN'. + +return(Arg) -> + {'RETURNR', Arg}. + +call(Function) when is_binary(Function)-> + {'CALL', ?i(Function) }. + +call_t(Function) when is_binary(Function) -> + {'CALL_T', ?i(Function)}. + +call_r(Contract, Function) when is_binary(Function) -> + {'CALL_R', Contract, ?i(Function)}. + +call_tr(Contract, Function) when is_binary(Function) -> + {'CALL_TR', Contract, ?i(Function)}. + +jump(BB) when is_integer(BB) -> + {'JUMP', ?i(BB)}. + +jumpif(Arg, BB) when is_integer(BB) -> + {'JUMPIF', Arg, ?i(BB)}. + +switch(Arg, BB1, BB2) when is_integer(BB1), + is_integer(BB2) -> + {'SWITCH_V2', Arg, ?i(BB1), ?i(BB2)}. + +switch(Arg, BB1, BB2, BB3) when is_integer(BB1), + is_integer(BB2), + is_integer(BB3) -> + {'SWITCH_V3', Arg, ?i(BB1), ?i(BB2), ?i(BB3)}. + +switch(Arg, BB1, BB2, BB3, BB4) when is_integer(BB1), + is_integer(BB2), + is_integer(BB3), + is_integer(BB4) -> + {'SWITCH_V4', Arg, ?i(BB1), ?i(BB2), ?i(BB3), ?i(BB4)}. + +switch(Arg, BB1, BB2, BB3, BB4, BB5) when is_integer(BB1), + is_integer(BB2), + is_integer(BB3), + is_integer(BB4), + is_integer(BB5) -> + {'SWITCH_V5', Arg, ?i(BB1), ?i(BB2), ?i(BB3), ?i(BB4), ?i(BB5)}. + +push(Arg) -> + {'PUSH', Arg}. + +inc() -> + 'INCA'. + +inc(Arg) -> + {'INC', Arg}. + +dec() -> + 'DECA'. + +dec(Arg) -> + {'DEC', Arg}. + +add(Dest, Left, Right) -> + {'ADD', Dest, Left, Right}. + +sub(Dest, Left, Right) -> + {'SUB', Dest, Left, Right}. + +mul(Dest, Left, Right) -> + {'mul', Dest, Left, Right}. + +divide(Dest, Left, Right) -> + {'div', Dest, Left, Right}. + +modulo(Dest, Left, Right) -> + {'mod', Dest, Left, Right}. + +pow(Dest, Left, Right) -> + {'pow', Dest, Left, Right}. + +lt(Dest, Left, Right) -> + {'lt', Dest, Left, Right}. + +gt(Dest, Left, Right) -> + {'gt', Dest, Left, Right}. + +elt(Dest, Left, Right) -> + {'elt', Dest, Left, Right}. + +egt(Dest, Left, Right) -> + {'egt', Dest, Left, Right}. + +eq(Dest, Left, Right) -> + {'eq', Dest, Left, Right}. + +neq(Dest, Left, Right) -> + {'neq', Dest, Left, Right}. + +and_op(Dest, Left, Right) -> + {'and', Dest, Left, Right}. + +or_op(Dest, Left, Right) -> + {'or', Dest, Left, Right}. + +not_op(Dest, Arg) -> + {'not', Dest, Arg}. + +tuple(Size) when is_integer(Size) -> + {'TUPLE', ?i(Size)}. + +element_op(Type, Dest, N, T) -> + {'ELEMENT', Type, Dest, N, T}. + +map_empty(Dest) -> + {'MAP_EMPTY', Dest}. + +map_lookup(Dest, Map, Key) -> + {'MAP_LOOKUP', Dest, Map, Key}. + +map_lookup(Dest, Map, Key, Default) -> + {'MAP_LOOKUPD', Dest, Map, Key, Default}. + +map_update(Dest, Map, Key, Value) -> + {'MAP_UPDATE', Dest, Map, Key, Value}. + +map_member(Dest, Map, Key) -> + {'MAP_MEMBER', Dest, Map, Key}. + +map_from_list(Dest, List) -> + {'MAP_MEMBER', Dest, List}. + +nil(Dest) -> + {'NIL', Dest}. + +is_nil(Dest, List) -> + {'IS_NIL', Dest, List}. + +cons(Dest, Hd, Tl) -> + {'CONS', Dest, Hd, Tl}. + +hd(Dest, List) -> + {'HD', Dest, List}. + +tl(Dest, List) -> + {'TL', Dest, List}. + +length(Dest, List) -> + {'LENGTH', Dest, List}. + +str_eq(Dest, Str1, Str2) -> + {'STR_EQ', Dest, Str1, Str2}. + +str_join(Dest, Str1, Str2) -> + {'STR_JOIN', Dest, Str1, Str2}. + +int_to_str(Dest, Str) -> + {'INT_TO_STR', Dest, Str}. + +addr_to_str(Dest, Str) -> + {'ADDR_TO_STR', Dest, Str}. + +str_reverse(Dest, Str) -> + {'STR_REVERSE', Dest, Str}. + +int_to_addr(Dest, Str) -> + {'INT_TO_ADDR', Dest, Str}. + +variant_test(Dest, Variant, Tag) -> + {'VARIANT_TEST', Dest, Variant, Tag}. + +variant_element( Dest, Variant, Index) -> + {'VARIANT_ELEMENT', Dest, Variant, Index}. + +variant(Dest, SizeA, TagA, ElementsA) -> + {'VARIANT', Dest, SizeA, TagA, ElementsA}. + +bits_none() -> + 'BITS_NONEA'. + +bits_none(To) -> + {'BITS_NONE', To}. + +bits_all() -> + 'BITS_ALLA'. + +bits_all(To) -> + {'BITS_ALL', To}. + +bits_all_n(To, N) -> + {'BITS_ALL_N', To, N}. + +bits_set(To, Bits, Bit) -> + {'BITS_SET', To, Bits, Bit}. + +bits_clear(To, Bits, Bit) -> + {'BITS_CLEAR', To, Bits, Bit}. + +bits_test(To, Bits, Bit) -> + {'BITS_TEST', To, Bits, Bit}. + +bits_sum(To, Bits) -> + {'BITS_SUM', To, Bits}. + +bits_or(To, Bits, Bit) -> + {'BITS_OR', To, Bits, Bit}. + +bits_and(To, Bits, Bit) -> + {'BITS_AND', To, Bits, Bit}. + +bits_diff(To, Bits, Bit) -> + {'BITS_DIFF', To, Bits, Bit}. + +dup() -> + 'DUP'. + +pop() -> + 'POP'. + +store(Var, What) -> + {'STORE', Var, What}. + +nop() -> + 'NOP'. -- 2.30.2 From ce33ba88185010d7a1a844cbb39a570050a5eb6b Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Tue, 19 Feb 2019 12:15:01 +0100 Subject: [PATCH 12/29] Add new instructions. --- include/aeb_fate_opcodes.hrl | 1 + src/aeb_fate_asm.erl | 2 +- src/aeb_fate_asm_scan.xrl | 5 ++++- src/aeb_fate_code.erl | 5 ++++- src/aeb_fate_opcodes.erl | 3 +++ 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/include/aeb_fate_opcodes.hrl b/include/aeb_fate_opcodes.hrl index b2c5da0..fbe0b27 100644 --- a/include/aeb_fate_opcodes.hrl +++ b/include/aeb_fate_opcodes.hrl @@ -111,6 +111,7 @@ -define('BITS_ALLA' , 16#70). -define('INCA' , 16#71). -define('DECA' , 16#72). +-define('POPA' , 16#73). -define('FUNCTION' , 16#fe). -define('EXTEND' , 16#ff). diff --git a/src/aeb_fate_asm.erl b/src/aeb_fate_asm.erl index da2b02d..40d235a 100644 --- a/src/aeb_fate_asm.erl +++ b/src/aeb_fate_asm.erl @@ -83,7 +83,7 @@ to_args(Tokens) -> {Arg, [{')', _} | Rest]} -> {[Arg], Rest} end. - + to_data([{int,_line, Int}|Rest]) -> {Int, Rest}; to_data([{boolean,_line, Bool}|Rest]) -> diff --git a/src/aeb_fate_asm_scan.xrl b/src/aeb_fate_asm_scan.xrl index 889ac2d..fce9f27 100644 --- a/src/aeb_fate_asm_scan.xrl +++ b/src/aeb_fate_asm_scan.xrl @@ -31,7 +31,6 @@ false : {token, {boolean, TokenLine, false}}. RETURN : {token, {mnemonic, TokenLine, 'RETURN'}}. RETURNR : {token, {mnemonic, TokenLine, 'RETURNR'}}. CALL : {token, {mnemonic, TokenLine, 'CALL'}}. -FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}. NOP : {token, {mnemonic, TokenLine, 'NOP'}}. CALL_R : {token, {mnemonic, TokenLine, 'CALL_R'}}. @@ -46,6 +45,7 @@ SWITCH_V5 : {token, {mnemonic, TokenLine, 'SWITCH_V5'}}. PUSH : {token, {mnemonic, TokenLine, 'PUSH'}}. DUP : {token, {mnemonic, TokenLine, 'DUP'}}. +DUPA : {token, {mnemonic, TokenLine, 'DUPA'}}. POP : {token, {mnemonic, TokenLine, 'POP'}}. STORE : {token, {mnemonic, TokenLine, 'STORE'}}. @@ -138,6 +138,9 @@ ABORT : {token, {mnemonic, TokenLine, 'ABORT'}}. EXIT : {token, {mnemonic, TokenLine, 'EXIT'}}. DEACTIVATE : {token, {mnemonic, TokenLine, 'DEACTIVATE'}}. COMMENT : {token, {mnemonic, TokenLine, 'COMMENT'}}. + +FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}. + {ID} : {token, {id, TokenLine, TokenChars}}. {HEX} : diff --git a/src/aeb_fate_code.erl b/src/aeb_fate_code.erl index 75e28b0..e6ad225 100644 --- a/src/aeb_fate_code.erl +++ b/src/aeb_fate_code.erl @@ -219,7 +219,10 @@ bits_diff(To, Bits, Bit) -> {'BITS_DIFF', To, Bits, Bit}. dup() -> - 'DUP'. + 'DUPA'. + +dup(N) when is_integer(N) -> + {'DUP', ?i(N)}. pop() -> 'POP'. diff --git a/src/aeb_fate_opcodes.erl b/src/aeb_fate_opcodes.erl index 584616e..9240793 100644 --- a/src/aeb_fate_opcodes.erl +++ b/src/aeb_fate_opcodes.erl @@ -34,6 +34,7 @@ mnemonic(?JUMP) -> 'JUMP' ; mnemonic(?JUMPIF) -> 'JUMPIF' ; mnemonic(?PUSH) -> 'PUSH' ; mnemonic(?DUP) -> 'DUP' ; +mnemonic(?DUPA) -> 'DUPA' ; mnemonic(?POP) -> 'POP' ; mnemonic(?STORE) -> 'STORE' ; mnemonic(?ADD) -> 'ADD' ; @@ -148,6 +149,7 @@ m_to_op('JUMP') -> ?JUMP ; m_to_op('JUMPIF') -> ?JUMPIF ; m_to_op('PUSH') -> ?PUSH ; m_to_op('DUP') -> ?DUP ; +m_to_op('DUPA') -> ?DUPA ; m_to_op('POP') -> ?POP ; m_to_op('STORE') -> ?STORE ; m_to_op('ADD') -> ?ADD ; @@ -256,6 +258,7 @@ args(?NOP) -> 0; args(?RETURN) -> 0; args(?INCA) -> 0; args(?DECA) -> 0; +args(?DUPA) -> 0; args(?BITS_NONEA) -> 0; args(?BITS_ALLA) -> 0; -- 2.30.2 From dae2dbeed6c43ba17d7efc084cea3b8c944d5702 Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Tue, 19 Feb 2019 16:11:53 +0100 Subject: [PATCH 13/29] Add DUPA --- include/aeb_fate_opcodes.hrl | 1 + 1 file changed, 1 insertion(+) diff --git a/include/aeb_fate_opcodes.hrl b/include/aeb_fate_opcodes.hrl index fbe0b27..56de42c 100644 --- a/include/aeb_fate_opcodes.hrl +++ b/include/aeb_fate_opcodes.hrl @@ -112,6 +112,7 @@ -define('INCA' , 16#71). -define('DECA' , 16#72). -define('POPA' , 16#73). +-define('DUPA' , 16#74). -define('FUNCTION' , 16#fe). -define('EXTEND' , 16#ff). -- 2.30.2 From c0f2ac3163d92b2251ebe43d1b77735a6a7a5f97 Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Wed, 20 Feb 2019 09:49:11 +0100 Subject: [PATCH 14/29] Instructions are uppercase. --- src/aeb_fate_code.erl | 26 +++++++++++++------------- src/aefateasm.erl | 5 ++--- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/aeb_fate_code.erl b/src/aeb_fate_code.erl index e6ad225..4f8a489 100644 --- a/src/aeb_fate_code.erl +++ b/src/aeb_fate_code.erl @@ -75,43 +75,43 @@ sub(Dest, Left, Right) -> {'SUB', Dest, Left, Right}. mul(Dest, Left, Right) -> - {'mul', Dest, Left, Right}. + {'MUL', Dest, Left, Right}. divide(Dest, Left, Right) -> - {'div', Dest, Left, Right}. + {'DIV', Dest, Left, Right}. modulo(Dest, Left, Right) -> - {'mod', Dest, Left, Right}. + {'MOD', Dest, Left, Right}. pow(Dest, Left, Right) -> - {'pow', Dest, Left, Right}. + {'POW', Dest, Left, Right}. lt(Dest, Left, Right) -> - {'lt', Dest, Left, Right}. + {'LT', Dest, Left, Right}. gt(Dest, Left, Right) -> - {'gt', Dest, Left, Right}. + {'GT', Dest, Left, Right}. elt(Dest, Left, Right) -> - {'elt', Dest, Left, Right}. + {'ELT', Dest, Left, Right}. egt(Dest, Left, Right) -> - {'egt', Dest, Left, Right}. + {'EGT', Dest, Left, Right}. eq(Dest, Left, Right) -> - {'eq', Dest, Left, Right}. + {'EQ', Dest, Left, Right}. neq(Dest, Left, Right) -> - {'neq', Dest, Left, Right}. + {'NEQ', Dest, Left, Right}. and_op(Dest, Left, Right) -> - {'and', Dest, Left, Right}. + {'AND', Dest, Left, Right}. or_op(Dest, Left, Right) -> - {'or', Dest, Left, Right}. + {'OR', Dest, Left, Right}. not_op(Dest, Arg) -> - {'not', Dest, Arg}. + {'NOT', Dest, Arg}. tuple(Size) when is_integer(Size) -> {'TUPLE', ?i(Size)}. diff --git a/src/aefateasm.erl b/src/aefateasm.erl index db7910e..12183ce 100644 --- a/src/aefateasm.erl +++ b/src/aefateasm.erl @@ -45,7 +45,7 @@ assemble(File, Opts) -> undefined -> Asm = aeb_fate_asm:read_file(File), {Env, BC} = aeb_fate_asm:asm_to_bytecode(Asm, Opts), - case Verbose of + case Verbose of true -> io:format("Env: ~0p~n", [Env]); false -> ok @@ -54,5 +54,4 @@ assemble(File, Opts) -> OutFile -> aeb_fate_asm:assemble_file(File, OutFile, Opts) end. - - + -- 2.30.2 From 5e9d34849f399d2624d480980d335a66d678777c Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Wed, 20 Feb 2019 11:37:43 +0100 Subject: [PATCH 15/29] Explicit export. --- src/aeb_fate_code.erl | 77 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 4 deletions(-) diff --git a/src/aeb_fate_code.erl b/src/aeb_fate_code.erl index 4f8a489..26c881a 100644 --- a/src/aeb_fate_code.erl +++ b/src/aeb_fate_code.erl @@ -1,9 +1,78 @@ -module(aeb_fate_code). -% TODO: rewrite to explicit exports. --compile(export_all). - -%-export([]). +-export([ return/0 + , return/1 + , call/1 + , call_t/1 + , call_r/2 + , call_tr/2 + , jump/1 + , jumpif/2 + , switch/3 + , switch/4 + , switch/5 + , switch/6 + , push/1 + , inc/0 + , inc/1 + , dec/0 + , dec/1 + , add/3 + , sub/3 + , mul/3 + , divide/3 + , modulo/3 + , pow/3 + , lt/3 + , gt/3 + , elt/3 + , egt/3 + , eq/3 + , neq/3 + , and_op/3 + , or_op/3 + , not_op/2 + , tuple/1 + , element_op/4 + , map_empty/1 + , map_lookup/3 + , map_lookup/4 + , map_update/4 + , map_member/3 + , map_from_list/2 + , nil/1 + , is_nil/2 + , cons/3 + , hd/2 + , tl/2 + , length/2 + , str_eq/3 + , str_join/3 + , int_to_str/2 + , addr_to_str/2 + , str_reverse/2 + , int_to_addr/2 + , variant_test/3 + , variant_element/3 + , variant/4 + , bits_none/0 + , bits_none/1 + , bits_all/0 + , bits_all/1 + , bits_all_n/2 + , bits_set/3 + , bits_clear/3 + , bits_test/3 + , bits_sum/2 + , bits_or/3 + , bits_and/3 + , bits_diff/3 + , dup/0 + , dup/1 + , pop/0 + , store/2 + , nop/0 + ]). -define(i(__X__), {immediate, __X__ }). -- 2.30.2 From 3c056db0b54872a74f325fe1446cab2b53f478f4 Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Wed, 20 Feb 2019 14:06:11 +0100 Subject: [PATCH 16/29] Cleanup. --- rebar.config | 4 ++-- src/aebytecode.app.src | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/rebar.config b/rebar.config index 6118607..a98b54c 100644 --- a/rebar.config +++ b/rebar.config @@ -2,14 +2,14 @@ {erl_opts, [debug_info]}. -{deps, [ {getopt, "1.0.1"} +{deps, [ {getopt, "1.0.1"} ]}. {escript_incl_apps, [aebytecode, getopt]}. {escript_main_app, aebytecode}. {escript_name, aefateasm}. -{escript_emu_args, "%%! +sbtu +A0\n"}. +{escript_emu_args, "%%!"}. {provider_hooks, [{post, [{compile, escriptize}]}]}. {post_hooks, [{"(linux|darwin|solaris|freebsd|netbsd|openbsd)", diff --git a/src/aebytecode.app.src b/src/aebytecode.app.src index 20855aa..e8c0021 100644 --- a/src/aebytecode.app.src +++ b/src/aebytecode.app.src @@ -4,7 +4,8 @@ {registered, []}, {applications, [kernel, - stdlib + stdlib, + getopt ]}, {env,[]}, {modules, []}, -- 2.30.2 From 8ba5f9e2b1d57c293c162518b52e72a238e36544 Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Wed, 20 Feb 2019 19:04:43 +0100 Subject: [PATCH 17/29] Provide binary profile. --- rebar.config | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/rebar.config b/rebar.config index a98b54c..716b42a 100644 --- a/rebar.config +++ b/rebar.config @@ -6,21 +6,6 @@ ]}. -{escript_incl_apps, [aebytecode, getopt]}. -{escript_main_app, aebytecode}. -{escript_name, aefateasm}. -{escript_emu_args, "%%!"}. -{provider_hooks, [{post, [{compile, escriptize}]}]}. - -{post_hooks, [{"(linux|darwin|solaris|freebsd|netbsd|openbsd)", - escriptize, - "cp \"$REBAR_BUILD_DIR/bin/aefateasm\" ./aefateasm"}, - {"win32", - escriptize, - "robocopy \"%REBAR_BUILD_DIR%/bin/\" ./ aefateasm* " - "/njs /njh /nfl /ndl & exit /b 0"} % silence things - ]}. - {dialyzer, [ {warnings, [unknown]}, {plt_apps, all_deps}, @@ -35,3 +20,23 @@ {include_erts, false}, {extended_start_script, true}]}. + +{profiles, [{binary, [ + {deps, [ {getopt, "1.0.1"} + ]}, + {escript_incl_apps, [aebytecode, getopt]}, + {escript_main_app, aebytecode}, + {escript_name, aefateasm}, + {escript_emu_args, "%%!"}, + {provider_hooks, [{post, [{compile, escriptize}]}]}, + + {post_hooks, [{"(linux|darwin|solaris|freebsd|netbsd|openbsd)", + escriptize, + "cp \"$REBAR_BUILD_DIR/bin/aefateasm\" ./aefateasm"}, + {"win32", + escriptize, + "robocopy \"%REBAR_BUILD_DIR%/bin/\" ./ aefateasm* " + "/njs /njh /nfl /ndl & exit /b 0"} % silence things + ]} + ]}]}. + -- 2.30.2 From 7e26912bf9f396cb3d111eddc0161de0e664174e Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Wed, 20 Feb 2019 19:16:56 +0100 Subject: [PATCH 18/29] Always escriptize. --- rebar.config | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/rebar.config b/rebar.config index 716b42a..2b23e8e 100644 --- a/rebar.config +++ b/rebar.config @@ -5,6 +5,12 @@ {deps, [ {getopt, "1.0.1"} ]}. +{escript_incl_apps, [aebytecode, getopt]}. +{escript_main_app, aebytecode}. +{escript_name, aefateasm}. +{escript_emu_args, "%%!"}. +{provider_hooks, [{post, [{compile, escriptize}]}]}. + {dialyzer, [ {warnings, [unknown]}, @@ -24,11 +30,6 @@ {profiles, [{binary, [ {deps, [ {getopt, "1.0.1"} ]}, - {escript_incl_apps, [aebytecode, getopt]}, - {escript_main_app, aebytecode}, - {escript_name, aefateasm}, - {escript_emu_args, "%%!"}, - {provider_hooks, [{post, [{compile, escriptize}]}]}, {post_hooks, [{"(linux|darwin|solaris|freebsd|netbsd|openbsd)", escriptize, -- 2.30.2 From d04a827f05a175a0e1d0d32340e0b2ff9d1cd6af Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Sat, 23 Feb 2019 22:13:19 +0100 Subject: [PATCH 19/29] Add fate code pretty printer. Add symbol table to binary. Add tests of rundtrip serialization and deserialization. --- Makefile | 20 +++ include/aeb_fate_opcodes.hrl | 12 +- src/aeb_fate_asm.erl | 334 ++++++++++++++++++++++++++++++++--- src/aeb_fate_asm_scan.xrl | 3 +- src/aeb_fate_opcodes.erl | 14 +- test/aeb_data_test.erl | 12 ++ test/aeb_fate_asm_test.erl | 66 +++++++ test/aeb_serialize_test.erl | 82 +++++++++ test/asm_code/map.fate | 3 +- test/asm_code/tuple.fate | 3 +- 10 files changed, 506 insertions(+), 43 deletions(-) create mode 100644 Makefile create mode 100644 test/aeb_data_test.erl create mode 100644 test/aeb_fate_asm_test.erl create mode 100644 test/aeb_serialize_test.erl diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1d04708 --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ + + +REBAR ?= rebar3 + +all: local + +local: + @$(REBAR) as local release + +console: + @$(REBAR) as local shell + +clean: + @$(REBAR) clean + +distclean: clean + @rm -rf _build/ + +test: local + @$(REBAR) as local eunit diff --git a/include/aeb_fate_opcodes.hrl b/include/aeb_fate_opcodes.hrl index 56de42c..90f5b6a 100644 --- a/include/aeb_fate_opcodes.hrl +++ b/include/aeb_fate_opcodes.hrl @@ -104,15 +104,15 @@ -define('MAP_LOOKUPD' , 16#69). -define('SWITCH_V2' , 16#6a). -define('SWITCH_V3' , 16#6b). --define('SWITCH_V4' , 16#6c). --define('SWITCH_V5' , 16#6d). --define('BITS_ALL_N' , 16#6e). --define('BITS_NONEA' , 16#6f). --define('BITS_ALLA' , 16#70). +-define('SWITCH_VN' , 16#6c). +-define('BITS_ALL_N' , 16#6d). +-define('BITS_NONEA' , 16#6e). +-define('BITS_ALLA' , 16#6f). +-define('DUPA' , 16#70). -define('INCA' , 16#71). -define('DECA' , 16#72). -define('POPA' , 16#73). --define('DUPA' , 16#74). + -define('FUNCTION' , 16#fe). -define('EXTEND' , 16#ff). diff --git a/src/aeb_fate_asm.erl b/src/aeb_fate_asm.erl index 40d235a..9bbb38e 100644 --- a/src/aeb_fate_asm.erl +++ b/src/aeb_fate_asm.erl @@ -53,10 +53,12 @@ , function_call/1 , pp/1 , read_file/1 + , to_asm/1 , to_hexstring/1 ]). -include_lib("aebytecode/include/aeb_fate_opcodes.hrl"). +-include_lib("aebytecode/include/aeb_fate_data.hrl"). -define(HASH_BYTES, 32). assemble_file(InFile, OutFile, Options) -> @@ -91,20 +93,215 @@ to_data([{boolean,_line, Bool}|Rest]) -> to_data([{hash,_line, Hash}|Rest]) -> {Hash, Rest}. +pp(FateCode) -> + Listing = to_asm(FateCode), + io_lib:format("~ts~n",[Listing]). -pp(Asm) -> - Listing = format(Asm), - io:format("~s~n", [Listing]). -format(Asm) -> format(Asm, 0). +to_asm(#{ functions := Functions + , symbols := Symbols} = _FateCode) -> + lists:flatten( + io_lib:format("~s",[ + [format(lookup(Name, Symbols), + Sig, + lists:sort(maps:to_list(CodeMap)), + Symbols) || + {Name, {Sig, CodeMap}} <- maps:to_list(Functions)]])). -format([{comment, Comment} | Rest], Address) -> - ";; " ++ Comment ++ "\n" ++ format(Rest, Address); -format([Mnemonic | Rest], Address) -> - _Op = aeb_fate_opcodes:m_to_op(Mnemonic), - " " ++ atom_to_list(Mnemonic) ++ "\n" - ++ format(Rest, Address + 1); -format([],_) -> []. + +format(Name, Sig, BBs, Symbols) -> + [ "FUNCTION " + , Name + , format_sig(Sig) + , "\n" + , format_bbs(BBs, Symbols)]. + +format_sig({Args, RetType}) -> + [ "( " + , format_arg_types(Args) + , ") : " + , format_type(RetType)]. + +format_arg_types([]) -> ""; +format_arg_types([T]) -> format_type(T); +format_arg_types([T|Ts]) -> + [format_type(T) + , ", " + , format_arg_types(Ts)]. + +format_arg({immediate, I}) -> + aeb_fate_data:format(I); +format_arg({arg, N}) -> io_lib:format("arg~p", [N]); +format_arg({var, N}) -> io_lib:format("var~p", [N]); +format_arg({stack, 0}) -> "a"; +format_arg({stack, N}) -> io_lib:format("a~p", [N]). + + +format_type(T) -> + %% TODO: Limit to ok types. + io_lib:format("~p", [T]). + +format_bbs([], _) -> + []; +format_bbs([{BB, Code}|Rest], Symbols) -> + [ io_lib:format(" ;; BB : ~p~n", [BB]) + , format_code(Code, Symbols) + | format_bbs(Rest, Symbols)]. + +format_code([], _) -> + ""; +format_code([Op|Rest], Symbols) -> + [" ", + format_op(Op, Symbols), + "\n", + format_code(Rest, Symbols)]. + +format_op('RETURN', _) -> "RETURN"; +format_op({'RETURNR', Arg}, _) -> ["RETURNR ", format_arg(Arg)]; +format_op({'CALL', {immediate, Function}}, Symbols) -> + ["CALL ", lookup(Function, Symbols)]; +format_op({'CALL_T', {immediate, Function}}, Symbols) -> + ["CALL_T ", lookup(Function, Symbols)]; +format_op({'CALL_R', {immediate, Contract}, {immediate, Function}}, Symbols) -> + ["CALL_R ", lookup(Contract, Symbols), "." , lookup(Function, Symbols)]; +format_op({'CALL_R', Contract, {immediate, Function}}, Symbols) -> + ["CALL_R ", format_arg(Contract), "." , lookup(Function, Symbols)]; +format_op({'CALL_TR', {immediate, Contract}, {immediate, Function}}, Symbols) -> + ["CALL_TR ", lookup(Contract, Symbols), "." , lookup(Function, Symbols)]; +format_op({'CALL_TR', Contract, {immediate, Function}}, Symbols) -> + ["CALL_TR ", format_arg(Contract), "." , lookup(Function, Symbols)]; +format_op({'JUMP', {immediate, BB}}, _) -> + ["JUMP ", io_lib:format("~p", [BB])]; +format_op({'JUMPIF', Arg, {immediate, BB}}, _) -> + ["JUMPIF ", format_arg(Arg), " ", io_lib:format("~p", [BB])]; +format_op({'SWITCH_V2', Variant, {immediate, BB1}, {immediate, BB2}}, _) -> + ["SWITCH_V2 ", format_arg(Variant), " ", BB1, " ", BB2]; +format_op({'SWITCH_V3', Variant, {immediate, BB1}, {immediate, BB2}, {immediate, BB3}}, _) -> + ["SWITCH_V2 ", format_arg(Variant), " ", BB1, " ", BB2, " ", BB3]; +format_op({'SWITCH_VN', Variant, BBs}, _) -> + ["SWITCH_VN ", format_arg(Variant), [[" ", BB] || {immedate, BB} <- BBs]]; +format_op({'PUSH', Arg0}, _) -> + ["PUSH ", format_arg(Arg0)]; +format_op('INCA', _) -> "INCA"; +format_op({'INC', Name}, _) -> ["INC ", format_arg(Name)]; +format_op({'DEC', Name}, _) -> ["DEC ", format_arg(Name)]; +format_op('DECA', _) -> "DECA"; +format_op({'ADD', Dest, Left, Right}, _) -> + ["ADD ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'SUB', Dest, Left, Right}, _) -> + ["SUB ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'MUL', Dest, Left, Right}, _) -> + ["MUL ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'DIV', Dest, Left, Right}, _) -> + ["DIV ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'MOD', Dest, Left, Right}, _) -> + ["MOD ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'POW', Dest, Left, Right}, _) -> + ["POW ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'LT', Dest, Left, Right}, _) -> + ["LT ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'GT', Dest, Left, Right}, _) -> + ["GT ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'ELT', Dest, Left, Right}, _) -> + ["ELT ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'EGT', Dest, Left, Right}, _) -> + ["EGT ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'EQ', Dest, Left, Right}, _) -> + ["EQ ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'NEQ', Dest, Left, Right}, _) -> + ["NEQ ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'AND', Dest, Left, Right}, _) -> + ["AND ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'OR', Dest, Left, Right}, _) -> + ["OR ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; +format_op({'NOT', Dest, Name}, _) -> + ["NOT ", format_arg(Dest), " ", format_arg(Name)]; +format_op({'TUPLE', {immediate, Size}}, _) -> + ["TUPLE ", io_lib:format("~p", [Size])]; +format_op({'ELEMENT', Type, Dest, Which, Tuple}, _) -> + [ "ELEMENT " + , io_lib:format("~p ", [Type]) + , format_arg(Dest), " " + , format_arg(Which), " " + , format_arg(Tuple)]; +format_op({'MAP_EMPTY', Dest}, _) -> + ["MAP_EMPTY ", format_arg(Dest)]; +format_op({'MAP_LOOKUP', Dest, Map, Key}, _) -> + ["MAP_LOOKUP ", format_arg(Dest), " " + , format_arg(Map), " ", format_arg(Key)]; +format_op({'MAP_DELETE', Dest, Map, Key}, _) -> + ["MAP_DELETE ", format_arg(Dest), " " + , format_arg(Map), " ", format_arg(Key)]; +format_op({'MAP_LOOKUPD', Dest, Map, Key, Default}, _) -> + ["MAP_LOOKUPD ", format_arg(Dest), " " + , format_arg(Map), " ", format_arg(Key), " ", format_arg(Default)]; +format_op({'MAP_UPDATE', Dest, Map, Key, Value}, _) -> + ["MAP_UPDATE ", format_arg(Dest), " " + , format_arg(Map), " ", format_arg(Key), " ", format_arg(Value)]; +format_op({'MAP_MEMBER', Dest, Map, Key}, _) -> + ["MAP_MEMBER ", format_arg(Dest), " " + , format_arg(Map), " ", format_arg(Key)]; +format_op({'MAP_FROM_LIST', Dest, List}, _) -> + ["MAP_FROM_LIST ", format_arg(Dest), " ", format_arg(List)]; +format_op({'NIL', Dest}, _) -> + ["NIL ", format_arg(Dest)]; +format_op({'IS_NIL', Dest, List}, _) -> + ["IS_NIL ", format_arg(Dest), " ", format_arg(List)]; +format_op({'CONS', Dest, Hd, Tl}, _) -> + ["CONS ", format_arg(Dest), " ", format_arg(Hd), " ", format_arg(Tl)]; +format_op({'HD', Dest, List}, _) -> + ["HD ", format_arg(Dest), " ", format_arg(List)]; +format_op({'TL', Dest, List}, _) -> + ["TL ", format_arg(Dest), " ", format_arg(List)]; +format_op({'LENGTH', Dest, List}, _) -> + ["LENGTH ", format_arg(Dest), " ", format_arg(List)]; +format_op({'STR_EQ', Dest, Str1, Str2}, _) -> + ["STR_EQ ", format_arg(Dest), " ", format_arg(Str1), format_arg(Str2)]; +format_op({'STR_JOIN', Dest, Str1, Str2}, _) -> + ["STR_JOIN ", format_arg(Dest), " ", format_arg(Str1), format_arg(Str2)]; +format_op({'INT_TO_STR', Dest, Str}, _) -> + ["INT_TO_STR ", format_arg(Dest), " ", format_arg(Str)]; +format_op({'ADDR_TO_STR', Dest, Str}, _) -> + ["ADDR_TO_STR ", format_arg(Dest), " ", format_arg(Str)]; +format_op({'STR_REVERSE', Dest, Str}, _) -> + ["STR_REVERSE ", format_arg(Dest), " ", format_arg(Str)]; +format_op({'INT_TO_ADDR', Dest, Str}, _) -> + ["INT_TO_ADDR ", format_arg(Dest), " ", format_arg(Str)]; +format_op({'VARIANT_TEST', Dest, Variant, Tag}, _) -> + ["VARIANT_TEST ", format_arg(Dest), " ", format_arg(Variant), " ", format_arg(Tag)]; +format_op({'VARIANT_ELEMENT', Dest, Variant, Index}, _) -> + ["VARIANT_ELEMENT ", format_arg(Dest), " ", format_arg(Variant), " ", format_arg(Index)]; +format_op({'VARIANT', Dest, SizeA, TagA, ElementsA}, _) -> + ["VARIANT ", format_arg(Dest), " ", format_arg(SizeA), " " + , format_arg(TagA), " ", format_arg(ElementsA)]; +format_op('BITS_NONEA', _) -> "BITS_NONEA "; +format_op({'BITS_NONE', To}, _) -> ["BITS_NONE ", format_arg(To)]; +format_op('BITS_ALLA', _) -> "BITS_ALLA"; +format_op({'BITS_ALL', To}, _) -> ["BITS_ALL ", format_arg(To)]; +format_op({'BITS_ALL_N', To, N}, _) -> + ["BITS_ALL_N ", format_arg(To), " ", format_arg(N)]; +format_op({'BITS_SET', To, Bits, Bit}, _) -> + ["BITS_SET ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)]; +format_op({'BITS_CLEAR', To, Bits, Bit}, _) -> + ["BITS_CLEAR ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)]; +format_op({'BITS_TEST', To, Bits, Bit}, _) -> + ["BITS_TEST ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)]; +format_op({'BITS_SUM', To, Bits}, _) -> + ["BITS_SUM ", format_arg(To), " ", format_arg(Bits)]; +format_op({'BITS_OR', To, Bits, Bit}, _) -> + ["BITS_OR ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)]; +format_op({'BITS_AND', To, Bits, Bit}, _) -> + ["BITS_AND ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)]; +format_op({'BITS_DIFF', To, Bits, Bit}, _) -> + ["BITS_DIFF ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)]; +format_op('DUPA', _) -> "DUPA"; +format_op({'DUP', {immediate, N}}, _) -> + ["DUP ", io_lib:format("~p", [N])]; +format_op({'POP', Dest}, _) -> + ["POP ", format_arg(Dest)]; +format_op({'STORE', Var, What}, _) -> + ["STORE ", format_arg(Var), " ", format_arg(What)]; +format_op('NOP', _) -> "NOP". read_file(Filename) -> @@ -126,6 +323,14 @@ asm_to_bytecode(AssemblerCode, Options) -> }, [], Options), ByteList = serialize(Env), + Signatures = serialize_sigs(Env), + SymbolTable = serialize_symbol_table(Env), + Annotatations = serialize_annotations(Env), + ByteCode = << (aeb_rlp:encode(list_to_binary(ByteList)))/binary, + (aeb_rlp:encode(list_to_binary(Signatures)))/binary, + (aeb_rlp:encode(SymbolTable))/binary, + (aeb_rlp:encode(list_to_binary(Annotatations)))/binary + >>, case proplists:lookup(pp_hex_string, Options) of {pp_hex_string, true} -> @@ -134,15 +339,25 @@ asm_to_bytecode(AssemblerCode, Options) -> ok end, - {Env, list_to_binary(ByteList)}. + {Env, ByteCode}. + +bytecode_to_fate_code(Bytes, _Options) -> + {ByteCode, Rest1} = aeb_rlp:decode_one(Bytes), + {Signatures, Rest2} = aeb_rlp:decode_one(Rest1), + {SymbolTable, Rest3} = aeb_rlp:decode_one(Rest2), + {Annotations, <<>>} = aeb_rlp:decode_one(Rest3), + + Env1 = deserialize(ByteCode, #{ function => none + , bb => 0 + , current_bb_code => [] + , functions => #{} + , code => #{} + }), + Env2 = deserialize_signatures(Signatures, Env1), + Env3 = deserialize_symbols(SymbolTable, Env2), + Env4 = deserialize_annotations(Annotations, Env3), + Env4. -bytecode_to_fate_code(ByteCode,_Options) -> - deserialize(ByteCode, #{ function => none - , bb => 0 - , current_bb_code => [] - , functions => #{} - , code => #{} - }). deserialize(<>, #{ function := none @@ -222,6 +437,21 @@ deserialize_op(?ELEMENT, Rest, Code) -> , {Modifier1, Arg1} , {Modifier2, Arg2}} | Code]}; +deserialize_op(?SWITCH_VN, Rest, Code) -> + <> = Rest, + {Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2), + case aeb_fate_encoding:deserialize_one(Rest3) of + {N, Rest4} when is_integer(N), N >= 0 -> + Modifier0 = bits_to_modifier(ArgType band 2#11), + immediate = bits_to_modifier((ArgType bsr 2) band 2#11), + {BBs, Rest5} = deserialize_n(N, Rest4), + {Rest5, [{aeb_fate_opcodes:mnemonic(?SWITCH_VN) + , {Modifier0, Arg0} + , {immediate, N} + , list_to_tuple(BBs)} + | Code]}; + _ -> exit(bad_argument_to_switch_vn) + end; deserialize_op(Op, Rest, Code) -> OpName = aeb_fate_opcodes:mnemonic(Op), case aeb_fate_opcodes:args(Op) of @@ -270,10 +500,41 @@ deserialize_op(Op, Rest, Code) -> | Code]} end. +deserialize_n(N, Binary) -> + deserialize_n(N, Binary, []). + +deserialize_n(0, Binary, Acc) -> + {lists:reverse(Acc), Binary}; +deserialize_n(N, Binary, Acc) -> + {Value, Rest} = aeb_fate_encoding:deserialize_one(Binary), + deserialize_n(N-1, Rest, [Value|Acc]). + + + +deserialize_signatures(_Signatures, Env) -> Env. + +deserialize_symbols(Table, Env) -> + ?FATE_MAP_VALUE(SymbolTable) = aeb_fate_encoding:deserialize(Table), + Env#{symbols => SymbolTable}. + +deserialize_annotations(_Annotations, Env) -> Env. + +serialize_sigs(_Env) -> []. + +serialize_symbol_table(#{ symbols := Symbols }) -> + aeb_fate_encoding:serialize(aeb_fate_data:make_map(Symbols)). + +serialize_annotations(_Env) -> + []. + + + + serialize(#{functions := Functions} =_Env) -> + %% Sort the functions oon name to get a canonical serialisation. Code = [[?FUNCTION, Name, serialize_signature(Sig), C] || - {Name, {Sig, C}} <- maps:to_list(Functions)], + {Name, {Sig, C}} <- lists:sort(maps:to_list(Functions))], serialize_code(lists:flatten(Code)). @@ -335,13 +596,41 @@ serialize_code([ {Arg0Type, Arg0} | Rest]) -> serialize_code([ ?ELEMENT , ResType | Rest]) -> - [?ELEMENT, + [?ELEMENT, serialize_type(ResType) | serialize_code(Rest)]; +serialize_code([ ?SWITCH_VN + , {Arg0Type, Arg0} + , {immediate, N} + | Rest]) when is_integer(N), N >= 0 -> + ArgSpec = + modifier_bits(Arg0Type) bor + (modifier_bits(immediate) bsl 2), + {Serialization, Rest2} = serialize_n_ints(N, Rest), + [?SWITCH_VN + , ArgSpec + , serialize_data(Arg0Type, Arg0) + , serialize_data(immediate, N) + | Serialization] ++ serialize_code(Rest2); serialize_code([B|Rest]) -> [B | serialize_code(Rest)]; serialize_code([]) -> []. +serialize_n_ints(N, Rest) -> + serialize_n_ints(N, Rest, []). + +serialize_n_ints(0, Rest, Acc) -> + %% Acc is a list of binaries. + {lists:reverse(Acc), Rest}; +serialize_n_ints(N, [Int|Rest], Acc) when is_integer(Int), Int >= 0 -> + serialize_n_ints(N - 1, Rest, [aeb_fate_encoding:serialize(Int)|Acc]); +serialize_n_ints(_, [], _) -> + exit(not_enough_bbs_for_switch_vn); +serialize_n_ints(_, _, _) -> + exit(bad_bbs_value_for_switch_vn). + + + %% 00 : stack/unused (depending on instruction) %% 01 : argN %% 10 : varN @@ -520,3 +809,6 @@ insert_symbol(Id, Hash, #{symbols := Symbols} = Env) -> , Hash => Id}}} end. + +lookup(Name, Symbols) -> + maps:get(Name, Symbols, Name). diff --git a/src/aeb_fate_asm_scan.xrl b/src/aeb_fate_asm_scan.xrl index fce9f27..5833315 100644 --- a/src/aeb_fate_asm_scan.xrl +++ b/src/aeb_fate_asm_scan.xrl @@ -40,8 +40,7 @@ JUMP : {token, {mnemonic, TokenLine, 'JUMP'}}. JUMPIF : {token, {mnemonic, TokenLine, 'JUMPIF'}}. SWITCH_V2 : {token, {mnemonic, TokenLine, 'SWITCH_V2'}}. SWITCH_V3 : {token, {mnemonic, TokenLine, 'SWITCH_V3'}}. -SWITCH_V4 : {token, {mnemonic, TokenLine, 'SWITCH_V4'}}. -SWITCH_V5 : {token, {mnemonic, TokenLine, 'SWITCH_V5'}}. +SWITCH_VN : {token, {mnemonic, TokenLine, 'SWITCH_VN'}}. PUSH : {token, {mnemonic, TokenLine, 'PUSH'}}. DUP : {token, {mnemonic, TokenLine, 'DUP'}}. diff --git a/src/aeb_fate_opcodes.erl b/src/aeb_fate_opcodes.erl index 9240793..84d93f7 100644 --- a/src/aeb_fate_opcodes.erl +++ b/src/aeb_fate_opcodes.erl @@ -132,8 +132,7 @@ mnemonic(?RETURNR) -> 'RETURNR' ; mnemonic(?MAP_LOOKUPD) -> 'MAP_LOOKUPD' ; mnemonic(?SWITCH_V2) -> 'SWITCH_V2' ; mnemonic(?SWITCH_V3) -> 'SWITCH_V3' ; -mnemonic(?SWITCH_V4) -> 'SWITCH_V4' ; -mnemonic(?SWITCH_V5) -> 'SWITCH_V5' ; +mnemonic(?SWITCH_VN) -> 'SWITCH_VN' ; mnemonic(?BITS_ALL_N) -> 'BITS_ALL_N' ; mnemonic(?FUNCTION) -> 'FUNCTION' ; mnemonic(?EXTEND) -> 'EXTEND'. @@ -246,9 +245,7 @@ m_to_op('RETURNR') -> ?RETURNR ; m_to_op('MAP_LOOKUPD') -> ?MAP_LOOKUPD ; m_to_op('SWITCH_V2') -> ?SWITCH_V2 ; m_to_op('SWITCH_V3') -> ?SWITCH_V3 ; -m_to_op('SWITCH_V4') -> ?SWITCH_V4 ; -m_to_op('SWITCH_V5') -> ?SWITCH_V5 ; -m_to_op('BITS_ALL_N') -> ?BITS_ALL_N ; +m_to_op('SWITCH_VN') -> ?SWITCH_VN ; m_to_op('FUNCTION') -> ?FUNCTION ; m_to_op('EXTEND') -> ?EXTEND. @@ -345,9 +342,7 @@ args(?VARIANT) -> 4; args(?MAP_UPDATE) -> 4; args(?MAP_LOOKUPD) -> 4; -args(?SWITCH_V4) -> 5; - -args(?SWITCH_V5) -> 6; +args(?SWITCH_VN) -> 2; args(_) -> 0. %% TODO do not allow this @@ -361,8 +356,7 @@ end_bb(?CALL_R) -> true; end_bb(?CALL_TR) -> true; end_bb(?SWITCH_V2) -> true; end_bb(?SWITCH_V3) -> true; -end_bb(?SWITCH_V4) -> true; -end_bb(?SWITCH_V5) -> true; +end_bb(?SWITCH_VN) -> true; end_bb(?ABORT) -> true; end_bb(?EXIT) -> true; diff --git a/test/aeb_data_test.erl b/test/aeb_data_test.erl new file mode 100644 index 0000000..7c25932 --- /dev/null +++ b/test/aeb_data_test.erl @@ -0,0 +1,12 @@ +%%%------------------------------------------------------------------- +%%% @copyright (C) 2018, Aeternity Anstalt +%%% @doc Basic tests for Fate data +%%% @end +%%%------------------------------------------------------------------- + +-module(aeb_data_test). + +-include_lib("eunit/include/eunit.hrl"). + +format_integer_test() -> + "0" = aeb_fate_data:format(0). diff --git a/test/aeb_fate_asm_test.erl b/test/aeb_fate_asm_test.erl new file mode 100644 index 0000000..f6cf17e --- /dev/null +++ b/test/aeb_fate_asm_test.erl @@ -0,0 +1,66 @@ +%%%------------------------------------------------------------------- +%%% @copyright (C) 2018, Aeternity Anstalt +%%% @doc Basic tests for Fate serialization +%%% +%%% To run: +%%% TEST=aeb_fate_asm_test rebar3 eunit +%%% +%%% @end +%%%------------------------------------------------------------------- + +-module(aeb_fate_asm_test). + +-include_lib("eunit/include/eunit.hrl"). + +asm_path() -> + filename:join(code:lib_dir(aebytecode, test), "asm_code"). + + +file_path(File) -> + filename:join(asm_path(), File) ++ ".fate". + +read_file(File) -> + FilePath = file_path(File), + Asm = aeb_fate_asm:read_file(FilePath), + Asm. + +assemble(Asm) -> + {Env, BC} = aeb_fate_asm:asm_to_bytecode(Asm, []), + {Env, BC}. + +disassemble(BC) -> + aeb_fate_asm:bytecode_to_fate_code(BC, []). + + +asm_disasm_idenity_test() -> + check_roundtrip(identity). + +asm_disasm_files_test_() -> + [{lists:flatten(io_lib:format("~p", [X])), + fun() -> check_roundtrip(X) end} + || X <- sources()]. + +sources() -> + [ "arith" + , "bool" + , "comp" + , "jumpif" + , "map" + , "memory" + , "remote" + , "test" + , "tuple" + ]. + +check_roundtrip(File) -> + AssemblerCode = read_file(File), + {Env, ByteCode} = assemble(AssemblerCode), + FateCode = disassemble(ByteCode), + DissasmCode = aeb_fate_asm:to_asm(FateCode), + io:format("~s~n", [AssemblerCode]), + io:format("~s~n", [DissasmCode]), + {Env2, ByteCode2} = assemble(DissasmCode), + ?assertEqual(ByteCode, ByteCode2). + + + diff --git a/test/aeb_serialize_test.erl b/test/aeb_serialize_test.erl new file mode 100644 index 0000000..dcbe4e2 --- /dev/null +++ b/test/aeb_serialize_test.erl @@ -0,0 +1,82 @@ +%%%------------------------------------------------------------------- +%%% @copyright (C) 2018, Aeternity Anstalt +%%% @doc Basic tests for Fate serialization +%%% +%%% To run: +%%% TEST=aeb_serialize_test rebar3 eunit +%%% +%%% @end +%%%------------------------------------------------------------------- + +-module(aeb_serialize_test). + +-include_lib("eunit/include/eunit.hrl"). + +serialize_integer_test() -> + <<0>> = aeb_fate_encoding:serialize(aeb_fate_data:make_integer(0)), + <<2>> = aeb_fate_encoding:serialize(aeb_fate_data:make_integer(1)), + <<126>> = aeb_fate_encoding:serialize(aeb_fate_data:make_integer(63)), + <<111, 0>> = aeb_fate_encoding:serialize(aeb_fate_data:make_integer(64)), + <<111,130,255,255>> = aeb_fate_encoding:serialize(aeb_fate_data:make_integer(65535 + 64)), + <<111,184,129,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>> = + aeb_fate_encoding:serialize(aeb_fate_data:make_integer(1 bsl 1024 + 64)). + +serialize_deserialize_test_() -> + [{lists:flatten(io_lib:format("~p", [X])), + fun() -> + ?assertEqual(X, + aeb_fate_encoding:deserialize(aeb_fate_encoding:serialize(X))) + end} + || X <- sources()]. + +make_int_list(N) -> [aeb_fate_data:make_integer(I) || I <- lists:seq(1, N)]. + +sources() -> + FortyTwo = aeb_fate_data:make_integer(42), + Unit = aeb_fate_data:make_unit(), + True = aeb_fate_data:make_boolean(true), + False = aeb_fate_data:make_boolean(false), + Nil = aeb_fate_data:make_list([]), + EmptyString = aeb_fate_data:make_string(""), + EmptyMap = aeb_fate_data:make_map(#{}), + [aeb_fate_data:make_integer(0), + aeb_fate_data:make_integer(1), + True, False, Unit, Nil, EmptyString, EmptyMap, + aeb_fate_data:make_list([True]), + aeb_fate_data:make_address( + <<0,1,2,3,4,5,6,7,8,9, + 0,1,2,3,4,5,6,7,8,9, + 0,1,2,3,4,5,6,7,8,9, + 1,2>>), + aeb_fate_data:make_string(<<"Hello">>), + aeb_fate_data:make_string( + <<"0123456789012345678901234567890123456789" + "0123456789012345678901234567890123456789" + "0123456789012345678901234567890123456789" + "0123456789012345678901234567890123456789">>), %% Magic concat 80 char string. + aeb_fate_data:make_tuple({True, FortyTwo}), + aeb_fate_data:make_tuple(list_to_tuple(make_int_list(65))), + aeb_fate_data:make_map(#{ aeb_fate_data:make_integer(1) => True, aeb_fate_data:make_integer(2) => False}), + aeb_fate_data:make_map(#{ aeb_fate_data:make_string(<<"foo">>) => aeb_fate_data:make_tuple({FortyTwo, True})}), + aeb_fate_data:make_list(make_int_list(3)), + aeb_fate_data:make_integer(-65), + aeb_fate_data:make_integer(65), + aeb_fate_data:make_integer(-32432847932847928374983), + aeb_fate_data:make_bits(0), + 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">>)}) + + ]. diff --git a/test/asm_code/map.fate b/test/asm_code/map.fate index beb1589..d841ea3 100644 --- a/test/asm_code/map.fate +++ b/test/asm_code/map.fate @@ -22,8 +22,7 @@ FUNCTION map_member({map, integer, boolean}, integer):boolean FUNCTION map_delete({map, integer, boolean}, integer):{map, integer, boolean} MAP_DELETE a arg0 arg1 RETURN - - + FUNCTION map_member({map, integer, boolean}, integer) : boolean MAP_MEMBER a arg0 arg1 RETURN diff --git a/test/asm_code/tuple.fate b/test/asm_code/tuple.fate index 573166b..74a265d 100644 --- a/test/asm_code/tuple.fate +++ b/test/asm_code/tuple.fate @@ -1,4 +1,3 @@ -;;CONTRACT tuple FUNCTION make_0tuple():{tuple, []} TUPLE 0 RETURN @@ -18,7 +17,7 @@ FUNCTION make_5tuple(integer, integer, integer, integer, integer): PUSH arg4 TUPLE 5 RETURN - + FUNCTION element1(integer, integer): integer PUSH arg0 PUSH arg1 -- 2.30.2 From a35307f61bec285d742e312743b7a4d822dec190 Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Mon, 25 Feb 2019 07:57:08 +0100 Subject: [PATCH 20/29] Add annotations (comments) to bytecode. Add strip function to remove symboltable and annotations from bytecode. --- src/aeb_fate_asm.erl | 114 +++++++++++++++++++++++++++++-------- src/aeb_fate_asm_scan.xrl | 8 ++- test/aeb_fate_asm_test.erl | 7 +-- test/asm_code/jumpif.fate | 16 +++--- test/asm_code/tuple.fate | 5 ++ 5 files changed, 115 insertions(+), 35 deletions(-) diff --git a/src/aeb_fate_asm.erl b/src/aeb_fate_asm.erl index 9bbb38e..75228b8 100644 --- a/src/aeb_fate_asm.erl +++ b/src/aeb_fate_asm.erl @@ -53,6 +53,7 @@ , function_call/1 , pp/1 , read_file/1 + , strip/1 , to_asm/1 , to_hexstring/1 ]). @@ -99,14 +100,36 @@ pp(FateCode) -> to_asm(#{ functions := Functions - , symbols := Symbols} = _FateCode) -> - lists:flatten( - io_lib:format("~s",[ - [format(lookup(Name, Symbols), - Sig, - lists:sort(maps:to_list(CodeMap)), - Symbols) || - {Name, {Sig, CodeMap}} <- maps:to_list(Functions)]])). + , symbols := Symbols + , annotations := Annotations} = _FateCode) -> + insert_comments(get_comments(Annotations), 1, + lists:flatten( + io_lib:format("~s", + [format_functions(Functions, Symbols)]))). + +insert_comments([{L,C}|Comments], L, String) -> + ";; " ++ C ++ "\n" ++ insert_comments(Comments, L + 1, String); +insert_comments(Comments, L, [$\n|String]) -> + "\n" ++ insert_comments(Comments, L+1, String); +insert_comments(Comments, L, [C|Rest]) -> + [C|insert_comments(Comments, L, Rest)]; +insert_comments([],_,[]) -> []; +insert_comments([{L,C}|Rest], _, []) -> + ";; " ++ C ++ "\n" ++ insert_comments(Rest, L + 1, []). + + + + + + + +format_functions(Functions, Symbols) -> + [format(lookup(Name, Symbols), + Sig, + lists:sort(maps:to_list(CodeMap)), + Symbols) + || + {Name, {Sig, CodeMap}} <- maps:to_list(Functions)]. format(Name, Sig, BBs, Symbols) -> @@ -320,6 +343,7 @@ asm_to_bytecode(AssemblerCode, Options) -> Env = to_bytecode(Tokens, none, #{ functions => #{} , symbols => #{} + , annotations => #{} }, [], Options), ByteList = serialize(Env), @@ -329,7 +353,7 @@ asm_to_bytecode(AssemblerCode, Options) -> ByteCode = << (aeb_rlp:encode(list_to_binary(ByteList)))/binary, (aeb_rlp:encode(list_to_binary(Signatures)))/binary, (aeb_rlp:encode(SymbolTable))/binary, - (aeb_rlp:encode(list_to_binary(Annotatations)))/binary + (aeb_rlp:encode(Annotatations))/binary >>, case proplists:lookup(pp_hex_string, Options) of @@ -341,6 +365,10 @@ asm_to_bytecode(AssemblerCode, Options) -> {Env, ByteCode}. +strip(ByteCode) -> + {Code, _Rest} = aeb_rlp:decode_one(ByteCode), + Code. + bytecode_to_fate_code(Bytes, _Options) -> {ByteCode, Rest1} = aeb_rlp:decode_one(Bytes), {Signatures, Rest2} = aeb_rlp:decode_one(Rest1), @@ -517,15 +545,19 @@ deserialize_symbols(Table, Env) -> ?FATE_MAP_VALUE(SymbolTable) = aeb_fate_encoding:deserialize(Table), Env#{symbols => SymbolTable}. -deserialize_annotations(_Annotations, Env) -> Env. +deserialize_annotations(AnnotationsBin, Env) -> + ?FATE_MAP_VALUE(Annotations) = aeb_fate_encoding:deserialize(AnnotationsBin), + Env#{annotations => Annotations}. + + serialize_sigs(_Env) -> []. serialize_symbol_table(#{ symbols := Symbols }) -> aeb_fate_encoding:serialize(aeb_fate_data:make_map(Symbols)). -serialize_annotations(_Env) -> - []. +serialize_annotations(#{ annotations := Annotations}) -> + aeb_fate_encoding:serialize(aeb_fate_data:make_map(Annotations)). @@ -652,17 +684,6 @@ serialize_signature({Args, RetType}) -> [serialize_type({tuple, Args}) | serialize_type(RetType)]. -serialize_type(integer) -> [0]; -serialize_type(boolean) -> [1]; -serialize_type({list, T}) -> [2 | serialize_type(T)]; -serialize_type({tuple, Ts}) -> - case length(Ts) of - 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)]. deserialize_signature(Binary) -> @@ -697,6 +718,13 @@ to_hexstring(ByteList) -> [io_lib:format("~2.16.0b", [X]) || X <- ByteList]). + + +%% ------------------------------------------------------------------- +%% Parser +%% Asm tokens -> Fate code env +%% ------------------------------------------------------------------- + to_bytecode([{function,_line, 'FUNCTION'}|Rest], Address, Env, Code, Opts) -> Env2 = insert_fun(Address, Code, Env), {Fun, Rest2} = to_fun_def(Rest), @@ -723,6 +751,10 @@ 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([{comment, Line, Comment}|Rest], Address, Env, Code, Opts) -> + Env2 = insert_annotation(comment, Line, Comment, Env), + to_bytecode(Rest, Address, Env2, Code, Opts); + to_bytecode([], Address, Env, Code, Opts) -> Env2 = insert_fun(Address, Code, Env), #{functions := Funs} = Env2, @@ -751,6 +783,9 @@ to_arg_types(Tokens) -> {[Type], Rest} end. + +%% Type handling + to_type([{id, _, "integer"} | Rest]) -> {integer, Rest}; to_type([{id, _, "boolean"} | Rest]) -> {boolean, Rest}; to_type([{id, _, "string"} | Rest]) -> {string, Rest}; @@ -781,6 +816,24 @@ to_list_of_types(Tokens) -> end. +serialize_type(integer) -> [0]; +serialize_type(boolean) -> [1]; +serialize_type({list, T}) -> [2 | serialize_type(T)]; +serialize_type({tuple, Ts}) -> + case length(Ts) of + 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)]. + + +%% ------------------------------------------------------------------- +%% Helper functions +%% ------------------------------------------------------------------- + +%% State handling insert_fun(none, [], Env) -> Env; insert_fun({Name, Type, RetType}, Code, #{functions := Functions} = Env) -> @@ -794,6 +847,20 @@ mk_hash(Id) -> {ok, <> } = aeb_blake2:blake2b(?HASH_BYTES, list_to_binary(Id)), <>. +%% Handle annotations + +insert_annotation(comment, Line, Comment, #{annotations := A} = Env) -> + Key = aeb_fate_data:make_tuple({aeb_fate_data:make_string("comment"), Line}), + Value = aeb_fate_data:make_string(Comment), + Env#{annotations => A#{ Key => Value}}. + +get_comments(Annotations) -> + [ {Line, Comment} || + {?FATE_TUPLE({?FATE_STRING_VALUE("comment"), Line}), + ?FATE_STRING_VALUE(Comment)} <- maps:to_list(Annotations)]. + +%% Symbols handling + insert_symbol(Id, Env) -> Hash = mk_hash(Id), insert_symbol(Id, Hash, Env). @@ -809,6 +876,7 @@ insert_symbol(Id, Hash, #{symbols := Symbols} = Env) -> , Hash => Id}}} end. +%% Symbol table handling lookup(Name, Symbols) -> maps:get(Name, Symbols, Name). diff --git a/src/aeb_fate_asm_scan.xrl b/src/aeb_fate_asm_scan.xrl index 5833315..6d10f56 100644 --- a/src/aeb_fate_asm_scan.xrl +++ b/src/aeb_fate_asm_scan.xrl @@ -161,6 +161,9 @@ FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}. \{ : {token, {'{', TokenLine}}. \} : {token, {'}', TokenLine}}. +;;.* : + {token, {comment, TokenLine, drop_prefix($;, TokenChars)}}. + \. : skip_token. @@ -168,7 +171,7 @@ FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}. {WS} : skip_token. %% Comments (TODO: nested comments) -;;.* : skip_token. + . : {error, "Unexpected token: " ++ TokenChars}. @@ -199,3 +202,6 @@ parse_hash("#" ++ Chars) -> scan(S) -> string(S). +drop_prefix(C, [C|Rest]) -> + drop_prefix(C, Rest); +drop_prefix(_, Tail) -> Tail. diff --git a/test/aeb_fate_asm_test.erl b/test/aeb_fate_asm_test.erl index f6cf17e..e177c6c 100644 --- a/test/aeb_fate_asm_test.erl +++ b/test/aeb_fate_asm_test.erl @@ -60,7 +60,6 @@ check_roundtrip(File) -> io:format("~s~n", [AssemblerCode]), io:format("~s~n", [DissasmCode]), {Env2, ByteCode2} = assemble(DissasmCode), - ?assertEqual(ByteCode, ByteCode2). - - - + Code1 = aeb_fate_asm:strip(ByteCode), + Code2 = aeb_fate_asm:strip(ByteCode2), + ?assertEqual(Code1, Code2). diff --git a/test/asm_code/jumpif.fate b/test/asm_code/jumpif.fate index d95aba9..c7fefed 100644 --- a/test/asm_code/jumpif.fate +++ b/test/asm_code/jumpif.fate @@ -1,9 +1,11 @@ ;; CONTRACT jumpif FUNCTION skip(integer, integer) : integer - PUSH arg1 - PUSH 0 - EQ a a arg0 - JUMPIF a 2 - INCA - JUMP 2 - RETURN +;; BB : 0 + PUSH arg1 + PUSH 0 + EQ a a arg0 + JUMPIF a 2 +;; BB : 1 + INCA + JUMP 2 + RETURN diff --git a/test/asm_code/tuple.fate b/test/asm_code/tuple.fate index 74a265d..6240dca 100644 --- a/test/asm_code/tuple.fate +++ b/test/asm_code/tuple.fate @@ -1,8 +1,10 @@ FUNCTION make_0tuple():{tuple, []} +;; BB : 0 TUPLE 0 RETURN FUNCTION make_2tuple(integer, integer):{tuple, [integer, integer]} +;; BB : 0 PUSH arg0 PUSH arg1 TUPLE 2 @@ -10,6 +12,7 @@ FUNCTION make_2tuple(integer, integer):{tuple, [integer, integer]} FUNCTION make_5tuple(integer, integer, integer, integer, integer): {tuple, [integer, integer, integer, integer, integer]} +;; BB : 0 PUSH arg0 PUSH arg1 PUSH arg2 @@ -19,6 +22,7 @@ FUNCTION make_5tuple(integer, integer, integer, integer, integer): RETURN FUNCTION element1(integer, integer): integer +;; BB : 0 PUSH arg0 PUSH arg1 TUPLE 2 @@ -26,5 +30,6 @@ FUNCTION element1(integer, integer): integer RETURN FUNCTION element({tuple, [integer, integer]}, integer): integer +;; BB : 0 ELEMENT integer a arg1 arg0 RETURN -- 2.30.2 From 01ae99f7e8be7917d213e1ef04dc6d613a046b98 Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Tue, 26 Feb 2019 08:53:46 +0100 Subject: [PATCH 21/29] Removed unused enacl lib. Use eblake2 for hash. (#8) * Removed unused lib. * Replace local blake2 implementation with eblake2. * Add eblake2 dep to app file. * Add eblake2 to rebar config. * Use hex for eblake2. * Bump version. * Replace local rlp with aeserialization repo. Use ref till first release is available. * Remove unused vars. --- rebar.config | 16 ++-- rebar.lock | 12 ++- src/aeb_blake2.erl | 148 ------------------------------------- src/aeb_fate_asm.erl | 20 ++--- src/aeb_fate_encoding.erl | 24 +++--- src/aeb_rlp.erl | 91 ----------------------- src/aebytecode.app.src | 4 +- test/aeb_fate_asm_test.erl | 4 +- 8 files changed, 47 insertions(+), 272 deletions(-) delete mode 100644 src/aeb_blake2.erl delete mode 100644 src/aeb_rlp.erl diff --git a/rebar.config b/rebar.config index 2b23e8e..08234fd 100644 --- a/rebar.config +++ b/rebar.config @@ -2,10 +2,13 @@ {erl_opts, [debug_info]}. -{deps, [ {getopt, "1.0.1"} +{deps, [ {eblake2, "1.0.0"} + , {aeserialization, {git, "https://github.com/aeternity/aeserialization.git", + {ref, "b55c372"}}} + , {getopt, "1.0.1"} ]}. -{escript_incl_apps, [aebytecode, getopt]}. +{escript_incl_apps, [aebytecode, eblake2, aeserialization, getopt]}. {escript_main_app, aebytecode}. {escript_name, aefateasm}. {escript_emu_args, "%%!"}. @@ -19,8 +22,8 @@ ]}. -{relx, [{release, {aessembler, "0.0.1"}, - [aebytecode, getopt]}, +{relx, [{release, {aebytecode, "2.0.1"}, + [aebytecode, eblake2, getopt]}, {dev_mode, true}, {include_erts, false}, @@ -28,7 +31,10 @@ {extended_start_script, true}]}. {profiles, [{binary, [ - {deps, [ {getopt, "1.0.1"} + {deps, [ {eblake2, "1.0.0"} + , {aeserialization, {git, "https://github.com/aeternity/aeserialization.git", + {ref, "b55c372"}}} + , {getopt, "1.0.1"} ]}, {post_hooks, [{"(linux|darwin|solaris|freebsd|netbsd|openbsd)", diff --git a/rebar.lock b/rebar.lock index 3c625aa..ee15e69 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,10 +1,16 @@ {"1.1.0", -[{<<"enacl">>, - {git,"https://github.com/aeternity/enacl.git", - {ref,"26180f42c0b3a450905d2efd8bc7fd5fd9cece75"}}, +[{<<"aeserialization">>, + {git,"https://github.com/aeternity/aeserialization.git", + {ref,"b55c3726f4a21063721c68d6fa7fda39121edf11"}}, 0}, + {<<"base58">>, + {git,"https://github.com/aeternity/erl-base58.git", + {ref,"60a335668a60328a29f9731b67c4a0e9e3d50ab6"}}, + 1}, + {<<"eblake2">>,{pkg,<<"eblake2">>,<<"1.0.0">>},0}, {<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},0}]}. [ {pkg_hash,[ + {<<"eblake2">>, <<"EC8AD20E438AAB3F2E8D5D118C366A0754219195F8A0F536587440F8F9BCF2EF">>}, {<<"getopt">>, <<"C73A9FA687B217F2FF79F68A3B637711BB1936E712B521D8CE466B29CBF7808A">>}]} ]. diff --git a/src/aeb_blake2.erl b/src/aeb_blake2.erl deleted file mode 100644 index 37fda39..0000000 --- a/src/aeb_blake2.erl +++ /dev/null @@ -1,148 +0,0 @@ -%%%============================================================================= -%%% @copyright (C) 2019, Aeternity Anstalt -%%% @doc -%%% BLAKE2b implementation in Erlang - for details see: https://blake2.net -%%% @end -%%%============================================================================= - --module(aeb_blake2). - --export([ blake2b/2 - , blake2b/3 - ]). - --define(MAX_64BIT, 16#ffffffffffffffff). - --spec blake2b(HashLen :: integer(), Msg :: binary()) -> {ok, binary()}. -blake2b(HashLen, Msg) -> - blake2b(HashLen, Msg, <<>>). - --spec blake2b(HashLen :: integer(), Msg :: binary(), Key :: binary()) -> {ok, binary()}. -blake2b(HashLen, Msg0, Key) -> - %% If message should be keyed, prepend message with padded key. - Msg = <<(pad(128, Key))/binary, Msg0/binary>>, - - %% Set up the initial state - Init = (16#01010000 + (byte_size(Key) bsl 8) + HashLen), - <> = blake_iv(), - H = <<(H0 bxor Init):64, H1_7/binary>>, - - %% Perform the compression - message will be chopped into 128-byte chunks. - State = blake2b_compress(H, Msg, 0), - - %% Just return the requested part of the hash - {ok, binary_part(to_little_endian(State), {0, HashLen})}. - -blake2b_compress(H, <>, BCompr) when Rest /= <<>> -> - H1 = blake2b_compress(H, <>, BCompr + 128, false), - blake2b_compress(H1, Rest, BCompr + 128); -blake2b_compress(H, SmallChunk, BCompr) -> - Size = byte_size(SmallChunk), - FillSize = (128 - Size) * 8, - blake2b_compress(H, <>, BCompr + Size, true). - -blake2b_compress(H, Chunk0, BCompr, Last) -> - Chunk = to_big_endian(Chunk0), - <> = <>, - V12_ = V12 bxor (BCompr band ?MAX_64BIT), - V13_ = V13 bxor ((BCompr bsr 64) band ?MAX_64BIT), - V14_ = case Last of - false -> V14; - true -> V14 bxor ?MAX_64BIT - end, - V = <>, - - <> = - lists:foldl(fun(Round, Vx) -> blake2b_mix(Round, Chunk, Vx) end, V, lists:seq(0, 11)), - - <> = H, - <<((HInt bxor VLow) bxor VHigh):(8*64)>>. - -blake2b_mix(Rnd, Chunk, V) -> - <> = V, - <> = Chunk, - Ms = {M0, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13, M14, M15}, - M = fun(Ix) -> element(Ix+1, Ms) end, - - [S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13, S14, S15] = sigma(Rnd rem 10), - - {Vx0, Vx4, Vx8, Vx12} = blake2b_mix(V0, V4, V8, V12, M(S0), M(S1)), - {Vx1, Vx5, Vx9, Vx13} = blake2b_mix(V1, V5, V9, V13, M(S2), M(S3)), - {Vx2, Vx6, Vx10, Vx14} = blake2b_mix(V2, V6, V10, V14, M(S4), M(S5)), - {Vx3, Vx7, Vx11, Vx15} = blake2b_mix(V3, V7, V11, V15, M(S6), M(S7)), - - {Vy0, Vy5, Vy10, Vy15} = blake2b_mix(Vx0, Vx5, Vx10, Vx15, M(S8), M(S9)), - {Vy1, Vy6, Vy11, Vy12} = blake2b_mix(Vx1, Vx6, Vx11, Vx12, M(S10), M(S11)), - {Vy2, Vy7, Vy8, Vy13} = blake2b_mix(Vx2, Vx7, Vx8, Vx13, M(S12), M(S13)), - {Vy3, Vy4, Vy9, Vy14} = blake2b_mix(Vx3, Vx4, Vx9, Vx14, M(S14), M(S15)), - - <>. - -blake2b_mix(Va, Vb, Vc, Vd, X, Y) -> - Va1 = (Va + Vb + X) band ?MAX_64BIT, - Vd1 = rotr64(32, Vd bxor Va1), - - Vc1 = (Vc + Vd1) band ?MAX_64BIT, - Vb1 = rotr64(24, Vb bxor Vc1), - - Va2 = (Va1 + Vb1 + Y) band ?MAX_64BIT, - Vd2 = rotr64(16, Va2 bxor Vd1), - - Vc2 = (Vc1 + Vd2) band ?MAX_64BIT, - Vb2 = rotr64(63, Vb1 bxor Vc2), - - {Va2, Vb2, Vc2, Vd2}. - -blake_iv() -> - IV0 = 16#6A09E667F3BCC908, - IV1 = 16#BB67AE8584CAA73B, - IV2 = 16#3C6EF372FE94F82B, - IV3 = 16#A54FF53A5F1D36F1, - IV4 = 16#510E527FADE682D1, - IV5 = 16#9B05688C2B3E6C1F, - IV6 = 16#1F83D9ABFB41BD6B, - IV7 = 16#5BE0CD19137E2179, - <>. - -sigma(N) -> - {_, Row} = lists:keyfind(N, 1, sigma()), Row. - -sigma() -> - [{0, [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]}, - {1, [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3]}, - {2, [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4]}, - {3, [ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8]}, - {4, [ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13]}, - {5, [ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9]}, - {6, [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11]}, - {7, [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10]}, - {8, [ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5]}, - {9, [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0]}]. - -rotr64(N, I64) -> - <> = rotr641(N, <>), - I64rot. - -rotr641(16, <>) -> <>; -rotr641(24, <>) -> <>; -rotr641(32, <>) -> <>; -rotr641(63, <>) -> <>. - -pad(N, Bin) -> - case (N - (byte_size(Bin) rem N)) rem N of - 0 -> Bin; - Pad -> <> - end. - -to_big_endian(Bin) -> to_big_endian(Bin, <<>>). -to_big_endian(<<>>, Acc) -> Acc; -to_big_endian(<>, Acc) -> - to_big_endian(Rest, <>). - -to_little_endian(Bin) -> to_little_endian(Bin, <<>>). -to_little_endian(<<>>, Acc) -> Acc; -to_little_endian(<>, Acc) -> - to_little_endian(Rest, <>). diff --git a/src/aeb_fate_asm.erl b/src/aeb_fate_asm.erl index 75228b8..1ea0bcb 100644 --- a/src/aeb_fate_asm.erl +++ b/src/aeb_fate_asm.erl @@ -350,10 +350,10 @@ asm_to_bytecode(AssemblerCode, Options) -> Signatures = serialize_sigs(Env), SymbolTable = serialize_symbol_table(Env), Annotatations = serialize_annotations(Env), - ByteCode = << (aeb_rlp:encode(list_to_binary(ByteList)))/binary, - (aeb_rlp:encode(list_to_binary(Signatures)))/binary, - (aeb_rlp:encode(SymbolTable))/binary, - (aeb_rlp:encode(Annotatations))/binary + ByteCode = << (aeser_rlp:encode(list_to_binary(ByteList)))/binary, + (aeser_rlp:encode(list_to_binary(Signatures)))/binary, + (aeser_rlp:encode(SymbolTable))/binary, + (aeser_rlp:encode(Annotatations))/binary >>, case proplists:lookup(pp_hex_string, Options) of @@ -366,14 +366,14 @@ asm_to_bytecode(AssemblerCode, Options) -> {Env, ByteCode}. strip(ByteCode) -> - {Code, _Rest} = aeb_rlp:decode_one(ByteCode), + {Code, _Rest} = aeser_rlp:decode_one(ByteCode), Code. bytecode_to_fate_code(Bytes, _Options) -> - {ByteCode, Rest1} = aeb_rlp:decode_one(Bytes), - {Signatures, Rest2} = aeb_rlp:decode_one(Rest1), - {SymbolTable, Rest3} = aeb_rlp:decode_one(Rest2), - {Annotations, <<>>} = aeb_rlp:decode_one(Rest3), + {ByteCode, Rest1} = aeser_rlp:decode_one(Bytes), + {Signatures, Rest2} = aeser_rlp:decode_one(Rest1), + {SymbolTable, Rest3} = aeser_rlp:decode_one(Rest2), + {Annotations, <<>>} = aeser_rlp:decode_one(Rest3), Env1 = deserialize(ByteCode, #{ function => none , bb => 0 @@ -844,7 +844,7 @@ insert_fun({Name, Type, RetType}, Code, #{functions := Functions} = Env) -> mk_hash(Id) -> %% Use first 4 bytes of blake hash - {ok, <> } = aeb_blake2:blake2b(?HASH_BYTES, list_to_binary(Id)), + {ok, <> } = eblake2:blake2b(?HASH_BYTES, list_to_binary(Id)), <>. %% Handle annotations diff --git a/src/aeb_fate_encoding.erl b/src/aeb_fate_encoding.erl index 4a5daa2..1fcbd9e 100644 --- a/src/aeb_fate_encoding.erl +++ b/src/aeb_fate_encoding.erl @@ -104,9 +104,9 @@ serialize(String) when ?IS_FATE_STRING(String), ?FATE_STRING_SIZE(String) > 0, ?FATE_STRING_SIZE(String) >= ?SHORT_STRING_SIZE -> Bytes = ?FATE_STRING_VALUE(String), - <>; + <>; serialize(?FATE_ADDRESS(Address)) when is_binary(Address) -> - <>; + <>; serialize(?FATE_TUPLE(T)) when size(T) > 0 -> S = size(T), L = tuple_to_list(T), @@ -148,7 +148,7 @@ serialize(?FATE_VARIANT(Size, Tag, Values)) when 0 =< Size %% ----------------------------------------------------- rlp_integer(S) when S >= 0 -> - aeb_rlp:encode(binary:encode_unsigned(S)). + aeser_rlp:encode(binary:encode_unsigned(S)). serialize_integer(I) when ?IS_FATE_INTEGER(I) -> V = ?FATE_INTEGER_VALUE(I), @@ -187,28 +187,28 @@ deserialize2(<>) -> deserialize2(<>) -> {?MAKE_FATE_INTEGER(-I), Rest}; deserialize2(<>) -> - {Bint, Rest2} = aeb_rlp:decode_one(Rest), + {Bint, Rest2} = aeser_rlp:decode_one(Rest), {?MAKE_FATE_INTEGER(-binary:decode_unsigned(Bint) - ?SMALL_INT_SIZE), Rest2}; deserialize2(<>) -> - {Bint, Rest2} = aeb_rlp:decode_one(Rest), + {Bint, Rest2} = aeser_rlp:decode_one(Rest), {?MAKE_FATE_INTEGER(binary:decode_unsigned(Bint) + ?SMALL_INT_SIZE), Rest2}; deserialize2(<>) -> - {Bint, Rest2} = aeb_rlp:decode_one(Rest), + {Bint, Rest2} = aeser_rlp:decode_one(Rest), {?FATE_BITS(-binary:decode_unsigned(Bint)), Rest2}; deserialize2(<>) -> - {Bint, Rest2} = aeb_rlp:decode_one(Rest), + {Bint, Rest2} = aeser_rlp:decode_one(Rest), {?FATE_BITS(binary:decode_unsigned(Bint)), Rest2}; deserialize2(<>) -> - {String, Rest2} = aeb_rlp:decode_one(Rest), + {String, Rest2} = aeser_rlp:decode_one(Rest), {?MAKE_FATE_STRING(String), Rest2}; deserialize2(<>) -> String = binary:part(Rest, 0, S), Rest2 = binary:part(Rest, byte_size(Rest), - (byte_size(Rest) - S)), {?MAKE_FATE_STRING(String), Rest2}; deserialize2(<>) -> - {A, Rest2} = aeb_rlp:decode_one(Rest), + {A, Rest2} = aeser_rlp:decode_one(Rest), {?FATE_ADDRESS(A), Rest2}; deserialize2(<>) -> {?FATE_TRUE, Rest}; @@ -223,7 +223,7 @@ deserialize2(<>) -> deserialize2(<>) -> {?FATE_EMPTY_STRING, Rest}; deserialize2(<>) -> - {BSize, Rest1} = aeb_rlp:decode_one(Rest), + {BSize, Rest1} = aeser_rlp:decode_one(Rest), N = binary:decode_unsigned(BSize) + ?SHORT_TUPLE_SIZE, {List, Rest2} = deserialize_elements(N, Rest1), {?FATE_TUPLE(list_to_tuple(List)), Rest2}; @@ -231,7 +231,7 @@ deserialize2(<>) -> {List, Rest1} = deserialize_elements(S, Rest), {?FATE_TUPLE(list_to_tuple(List)), Rest1}; deserialize2(<>) -> - {BLength, Rest1} = aeb_rlp:decode_one(Rest), + {BLength, Rest1} = aeser_rlp:decode_one(Rest), Length = binary:decode_unsigned(BLength) + ?SHORT_LIST_SIZE, {List, Rest2} = deserialize_elements(Length, Rest1), {?MAKE_FATE_LIST(List), Rest2}; @@ -239,7 +239,7 @@ deserialize2(<>) -> {List, Rest1} = deserialize_elements(S, Rest), {?MAKE_FATE_LIST(List), Rest1}; deserialize2(<>) -> - {BSize, Rest1} = aeb_rlp:decode_one(Rest), + {BSize, Rest1} = aeser_rlp:decode_one(Rest), Size = binary:decode_unsigned(BSize), {List, Rest2} = deserialize_elements(2*Size, Rest1), Map = insert_kv(List, #{}), diff --git a/src/aeb_rlp.erl b/src/aeb_rlp.erl deleted file mode 100644 index 46a27fa..0000000 --- a/src/aeb_rlp.erl +++ /dev/null @@ -1,91 +0,0 @@ -%%%------------------------------------------------------------------- -%%% @copyright (C) 2017, Aeternity Anstalt -%%% @doc -%%% Implementation of the Recursive Length Prefix. -%%% -%%% https://github.com/ethereum/wiki/wiki/RLP -%%% -%%% @end -%%%------------------------------------------------------------------- - --module(aeb_rlp). --export([ decode/1 - , decode_one/1 - , encode/1 - ]). - --export_type([ encodable/0 - , encoded/0 - ]). - --type encodable() :: [encodable()] | binary(). --type encoded() :: <<_:8, _:_*8>>. - --define(UNTAGGED_SIZE_LIMIT , 55). --define(UNTAGGED_LIMIT , 127). --define(BYTE_ARRAY_OFFSET , 128). --define(LIST_OFFSET , 192). - - --spec encode(encodable()) -> encoded(). -encode(X) -> - encode(X, []). - -encode(<> = X,_Opts) when B =< ?UNTAGGED_LIMIT -> - %% An untagged value - X; -encode(X,_Opts) when is_binary(X) -> - %% Byte array - add_size(?BYTE_ARRAY_OFFSET, X); -encode(L, Opts) when is_list(L) -> - %% Lists items are encoded and concatenated - ByteArray = << << (encode(X, Opts))/binary >> || X <- L >>, - add_size(?LIST_OFFSET, ByteArray). - -add_size(Offset, X) when byte_size(X) =< ?UNTAGGED_SIZE_LIMIT -> - %% The size fits in one tagged byte - <<(Offset + byte_size(X)), X/binary>>; -add_size(Offset, X) when is_binary(X) -> - %% The size itself needs to be encoded as a byte array - %% Add the tagged size of the size byte array - SizeBin = binary:encode_unsigned(byte_size(X)), - TaggedSize = ?UNTAGGED_SIZE_LIMIT + Offset + byte_size(SizeBin), - true = (TaggedSize < 256 ), %% Assert - <>. - --spec decode(encoded()) -> encodable(). -decode(Bin) when is_binary(Bin), byte_size(Bin) > 0 -> - case decode_one(Bin) of - {X, <<>>} -> X; - {X, Left} -> error({trailing, X, Bin, Left}) - end. - -decode_one(<>) when X =< ?UNTAGGED_LIMIT -> - %% Untagged value - {<>, B}; -decode_one(<> = B) when L < ?LIST_OFFSET -> - %% Byte array - {Size, Rest} = decode_size(B, ?BYTE_ARRAY_OFFSET), - <> = Rest, - {X, Tail}; -decode_one(<<_/binary>> = B) -> - %% List - {Size, Rest} = decode_size(B, ?LIST_OFFSET), - <> = Rest, - {decode_list(X), Tail}. - -decode_size(<>, Offset) when L =< Offset + ?UNTAGGED_SIZE_LIMIT-> - %% One byte tagged size. - {L - Offset, B}; -decode_size(<<_, 0, _/binary>>,_Offset) -> - error(leading_zeroes_in_size); -decode_size(<>, Offset) -> - %% Actual size is in a byte array. - BinSize = L - Offset - ?UNTAGGED_SIZE_LIMIT, - <> = B, - {Size, Rest}. - -decode_list(<<>>) -> []; -decode_list(B) -> - {Element, Rest} = decode_one(B), - [Element|decode_list(Rest)]. diff --git a/src/aebytecode.app.src b/src/aebytecode.app.src index e8c0021..7116b7e 100644 --- a/src/aebytecode.app.src +++ b/src/aebytecode.app.src @@ -1,10 +1,12 @@ {application, aebytecode, [{description, "Bytecode definitions, serialization and deserialization for aeternity."}, - {vsn, "2.0.0"}, + {vsn, "2.0.1"}, {registered, []}, {applications, [kernel, stdlib, + eblake2, + aeserialization, getopt ]}, {env,[]}, diff --git a/test/aeb_fate_asm_test.erl b/test/aeb_fate_asm_test.erl index e177c6c..dd32af6 100644 --- a/test/aeb_fate_asm_test.erl +++ b/test/aeb_fate_asm_test.erl @@ -54,12 +54,12 @@ sources() -> check_roundtrip(File) -> AssemblerCode = read_file(File), - {Env, ByteCode} = assemble(AssemblerCode), + {_Env, ByteCode} = assemble(AssemblerCode), FateCode = disassemble(ByteCode), DissasmCode = aeb_fate_asm:to_asm(FateCode), io:format("~s~n", [AssemblerCode]), io:format("~s~n", [DissasmCode]), - {Env2, ByteCode2} = assemble(DissasmCode), + {_Env2, ByteCode2} = assemble(DissasmCode), Code1 = aeb_fate_asm:strip(ByteCode), Code2 = aeb_fate_asm:strip(ByteCode2), ?assertEqual(Code1, Code2). -- 2.30.2 From 8fc929b1ee4450eecd3a48060a092cc9f872a62e Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Thu, 28 Feb 2019 11:24:13 +0100 Subject: [PATCH 22/29] Pt 164259596 generate fate ops (#9) * Generate code for fate ops from spec. * Generate the code from the makefile. Remove generated files. * Test targets and cleanup. * Spell eunit the right way. * Use test target for ci. * Renumber opcodes. Add primops. * Generate tokens in scanner from definitions. * Rename NUMBER op to GENERATION and add MICROBLOCK instruction. --- .circleci/config.yml | 8 +- .gitignore | 4 + Makefile | 25 +- include/aeb_fate_opcodes.hrl | 121 ---------- src/aeb_fate_asm.erl | 11 +- src/aeb_fate_asm_scan.template | 99 ++++++++ src/aeb_fate_asm_scan.xrl | 207 ----------------- src/aeb_fate_code.erl | 303 ------------------------- src/aeb_fate_data.erl | 31 ++- src/aeb_fate_generate_ops.erl | 402 +++++++++++++++++++++++++++++++++ src/aeb_fate_opcodes.erl | 363 ----------------------------- 11 files changed, 564 insertions(+), 1010 deletions(-) delete mode 100644 include/aeb_fate_opcodes.hrl create mode 100644 src/aeb_fate_asm_scan.template delete mode 100644 src/aeb_fate_asm_scan.xrl delete mode 100644 src/aeb_fate_code.erl create mode 100644 src/aeb_fate_generate_ops.erl delete mode 100644 src/aeb_fate_opcodes.erl diff --git a/.circleci/config.yml b/.circleci/config.yml index e34c31c..0c9ac9f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -19,16 +19,16 @@ jobs: - dialyzer-cache-v1- - run: name: Build - command: rebar3 compile + command: make - run: name: Static Analysis - command: rebar3 dialyzer + command: make dialyzer - run: name: Eunit - command: rebar3 eunit + command: make eunit - run: name: Common Tests - command: rebar3 ct + command: make test - save_cache: key: dialyzer-cache-v1-{{ .Branch }}-{{ .Revision }} paths: diff --git a/.gitignore b/.gitignore index 4ad6576..cccd365 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,9 @@ rel/example_project .rebar aeb_asm_scan.erl aeb_fate_asm_scan.erl +aeb_fate_asm_scan.xrl _build/ aefateasm +include/aeb_fate_opcodes.hrl +src/aeb_fate_code.erl +src/aeb_fate_opcodes.erl diff --git a/Makefile b/Makefile index 1d04708..071e04c 100644 --- a/Makefile +++ b/Makefile @@ -4,17 +4,38 @@ REBAR ?= rebar3 all: local -local: +local: src/aeb_fate_opcodes.erl src/aeb_fate_code.erl include/aeb_fate_opcodes.hrl src/aeb_fate_asm_scan.xrl @$(REBAR) as local release -console: +console: local @$(REBAR) as local shell clean: @$(REBAR) clean + rm -f src/aeb_fate_opcodes.erl + rm -f src/aeb_fate_code.erl + rm -f include/aeb_fate_opcodes.hrl + +dialyzer: local + @$(REBAR) as local dialyzer + + distclean: clean @rm -rf _build/ +eunit: local + @$(REBAR) as local eunit + test: local @$(REBAR) as local eunit + + +ebin/aeb_fate_generate_ops.beam: src/aeb_fate_generate_ops.erl ebin + erlc -o $(dir $@) $< + +src/aeb_fate_opcodes.erl src/aeb_fate_code.erl include/aeb_fate_opcodes.hrl src/aeb_fate_asm_scan.xrl: ebin/aeb_fate_generate_ops.beam + erl -pa ebin/ -noshell -s aeb_fate_generate_ops gen_and_halt src/ include/ + +ebin: + mkdir ebin diff --git a/include/aeb_fate_opcodes.hrl b/include/aeb_fate_opcodes.hrl deleted file mode 100644 index 90f5b6a..0000000 --- a/include/aeb_fate_opcodes.hrl +++ /dev/null @@ -1,121 +0,0 @@ - -%% FATE opcodes --define('NOP' , 16#00). --define('RETURN' , 16#01). --define('CALL' , 16#02). --define('CALL_R' , 16#03). --define('CALL_T' , 16#04). --define('CALL_TR' , 16#05). --define('JUMP' , 16#06). --define('JUMPIF' , 16#07). --define('SWITCH' , 16#08). --define('PUSH' , 16#09). --define('DUP' , 16#0a). --define('POP' , 16#0b). --define('STORE' , 16#10). --define('ADD' , 16#11). --define('MUL' , 16#12). --define('SUB' , 16#13). --define('DIV' , 16#14). --define('MOD' , 16#15). --define('POW' , 16#16). --define('LT' , 16#17). --define('GT' , 16#18). --define('EQ' , 16#19). --define('ELT' , 16#1a). --define('EGT' , 16#1b). --define('NEQ' , 16#1c). --define('AND' , 16#1d). --define('OR' , 16#1e). --define('NOT' , 16#1f). --define('TUPLE' , 16#20). --define('ELEMENT' , 16#21). --define('MAP_EMPTY' , 16#22). --define('MAP_LOOKUP' , 16#23). --define('MAP_UPDATE' , 16#24). --define('MAP_DELETE' , 16#25). --define('MAP_MEMBER' , 16#26). --define('MAP_FROM_LIST' , 16#27). --define('NIL' , 16#28). --define('IS_NIL' , 16#29). --define('CONS' , 16#2a). --define('HD' , 16#2b). --define('TL' , 16#2c). --define('LENGTH' , 16#2d). --define('STR_EQ' , 16#2e). --define('STR_JOIN' , 16#2f). --define('ADDR_TO_STR' , 16#30). --define('STR_REVERSE' , 16#31). --define('INT_TO_ADDR' , 16#32). --define('VARIANT' , 16#33). --define('VARIANT_TEST' , 16#34). --define('VARIANT_ELEMENT', 16#35). --define('BITS_NONE' , 16#36). --define('BITS_ALL' , 16#37). --define('BITS_SET' , 16#38). --define('BITS_CLEAR' , 16#39). --define('BITS_TEST' , 16#3a). --define('BITS_SUM' , 16#3b). --define('BITS_OR' , 16#3c). --define('BITS_AND' , 16#3d). --define('BITS_DIFF' , 16#3e). --define('ADDRESS' , 16#3f). --define('BALANCE' , 16#40). --define('ORIGIN' , 16#41). --define('CALLER' , 16#42). --define('GASPRICE' , 16#43). --define('BLOCKHASH' , 16#44). --define('BENEFICIARY' , 16#45). --define('TIMESTAMP' , 16#46). --define('NUMBER' , 16#47). --define('DIFFICULTY' , 16#48). --define('GASLIMIT' , 16#49). --define('GAS' , 16#4a). --define('LOG0' , 16#4b). --define('LOG1' , 16#4c). --define('LOG2' , 16#4d). --define('LOG3' , 16#4e). --define('LOG4' , 16#4f). --define('ABORT' , 16#50). --define('EXIT' , 16#51). --define('DEACTIVATE' , 16#52). --define('INC' , 16#53). --define('DEC' , 16#54). --define('INT_TO_STR' , 16#55). --define('SPEND' , 16#56). --define('ORACLE_REGISTER', 16#57). --define('ORACLE_QUERY' , 16#58). --define('ORACLE_RESPOND' , 16#59). --define('ORACLE_EXTEND' , 16#5a). --define('ORACLE_GET_ANSWER', 16#5b). --define('ORACLE_GET_QUESTION', 16#5c). --define('ORACLE_QUERY_FEE', 16#5d). --define('AENS_RESOLVE' , 16#5e). --define('AENS_PRECLAIM' , 16#5f). --define('AENS_CLAIM' , 16#60). --define('AENS_UPDATE' , 16#61). --define('AENS_TRANSFER' , 16#62). --define('AENS_REVOKE' , 16#63). --define('ECVERIFY' , 16#64). --define('SHA3' , 16#65). --define('SHA256' , 16#66). --define('BLAKE2B' , 16#67). --define('RETURNR' , 16#68). --define('MAP_LOOKUPD' , 16#69). --define('SWITCH_V2' , 16#6a). --define('SWITCH_V3' , 16#6b). --define('SWITCH_VN' , 16#6c). --define('BITS_ALL_N' , 16#6d). --define('BITS_NONEA' , 16#6e). --define('BITS_ALLA' , 16#6f). --define('DUPA' , 16#70). --define('INCA' , 16#71). --define('DECA' , 16#72). --define('POPA' , 16#73). - - --define('FUNCTION' , 16#fe). --define('EXTEND' , 16#ff). - --define( COMMENT(X), {comment, X}). - diff --git a/src/aeb_fate_asm.erl b/src/aeb_fate_asm.erl index 1ea0bcb..5e320ef 100644 --- a/src/aeb_fate_asm.erl +++ b/src/aeb_fate_asm.erl @@ -10,10 +10,15 @@ %%% DUP %%% Identifiers start with a lower case letter %%% an_identifier -%%% References to function arguments start with arg +%%% References to function arguments start with arg followed by an integer %%% arg0 -%%% References to variables/registers start with var +%%% References to variables/registers start with var followed by an integer %%% var0 +%%% References to stack postions is either a (for stack 0) +%%% or start with stack followed by an integer +%%% stack1 +%%% a +%%% %%% Immediates can be of 9 types: %%% 1. Integers %%% 42 @@ -815,7 +820,7 @@ to_list_of_types(Tokens) -> {[Type], Rest} end. - +-spec serialize_type(aeb_fate_data:fate_type_type()) -> [byte()]. serialize_type(integer) -> [0]; serialize_type(boolean) -> [1]; serialize_type({list, T}) -> [2 | serialize_type(T)]; diff --git a/src/aeb_fate_asm_scan.template b/src/aeb_fate_asm_scan.template new file mode 100644 index 0000000..f894aa5 --- /dev/null +++ b/src/aeb_fate_asm_scan.template @@ -0,0 +1,99 @@ +%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*- +%%%------------------------------------------------------------------- +%%% @copyright (C) 2019, aeternity Anstalt +%%% @doc +%%% Handling FATE code. +%%% @end +###REPLACEWITHNOTE### +%%%------------------------------------------------------------------- + +Definitions. +DIGIT = [0-9] +HEXDIGIT = [0-9a-fA-F] +LOWER = [a-z_] +UPPER = [A-Z] +INT = {DIGIT}+ +HEX = 0x{HEXDIGIT}+ +HASH = #{HEXDIGIT}+ +WS = [\000-\s] +ID = {LOWER}[a-zA-Z0-9_]* + + +Rules. +arg{INT} : {token, {arg, TokenLine, parse_arg(TokenChars)}}. +var{INT} : {token, {var, TokenLine, parse_var(TokenChars)}}. +a : {token, {stack, TokenLine, 0}}. +a{INT} : {token, {stack, TokenLine, parse_acc(TokenChars)}}. + +true : {token, {boolean, TokenLine, true}}. +false : {token, {boolean, TokenLine, false}}. + +###REPLACEWITHOPTOKENS### + +FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}. + +{ID} : + {token, {id, TokenLine, TokenChars}}. +{HEX} : + {token, {int, TokenLine, parse_hex(TokenChars)}}. +{INT} : + {token, {int, TokenLine, parse_int(TokenChars)}}. +{HASH} : + {token, {hash, TokenLine, parse_hash(TokenChars)}}. + + +%% Symbols +\-\> : {token, {'to', TokenLine}}. +\: : {token, {'to', TokenLine}}. +, : {token, {',', TokenLine}}. +\( : {token, {'(', TokenLine}}. +\) : {token, {')', TokenLine}}. +\[ : {token, {'[', TokenLine}}. +\] : {token, {']', TokenLine}}. +\{ : {token, {'{', TokenLine}}. +\} : {token, {'}', TokenLine}}. + +;;.* : + {token, {comment, TokenLine, drop_prefix($;, TokenChars)}}. + +\. : skip_token. + + +%% Whitespace ignore +{WS} : skip_token. + +%% Comments (TODO: nested comments) + + +. : {error, "Unexpected token: " ++ TokenChars}. + +Erlang code. + +-export([scan/1]). + +-dialyzer({nowarn_function, yyrev/2}). + +-ignore_xref([format_error/1, string/2, token/2, token/3, tokens/2, tokens/3]). + +-include_lib("aebytecode/include/aeb_fate_opcodes.hrl"). + + +parse_hex("0x" ++ Chars) -> list_to_integer(Chars, 16). + +parse_int(Chars) -> list_to_integer(Chars). + +parse_arg("arg" ++ N) -> list_to_integer(N). +parse_var("var" ++ N) -> list_to_integer(N). +parse_acc("a" ++ N) -> list_to_integer(N). + + +parse_hash("#" ++ Chars) -> + N = list_to_integer(Chars, 16), + <>. + +scan(S) -> + string(S). + +drop_prefix(C, [C|Rest]) -> + drop_prefix(C, Rest); +drop_prefix(_, Tail) -> Tail. diff --git a/src/aeb_fate_asm_scan.xrl b/src/aeb_fate_asm_scan.xrl deleted file mode 100644 index 6d10f56..0000000 --- a/src/aeb_fate_asm_scan.xrl +++ /dev/null @@ -1,207 +0,0 @@ -%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*- -%%%------------------------------------------------------------------- -%%% @copyright (C) 2019, aeternity Anstalt -%%% @doc -%%% Handling FATE code. -%%% @end -%%% Created : 9 Jan 2019 -%%%------------------------------------------------------------------- - -Definitions. -DIGIT = [0-9] -HEXDIGIT = [0-9a-fA-F] -LOWER = [a-z_] -UPPER = [A-Z] -INT = {DIGIT}+ -HEX = 0x{HEXDIGIT}+ -HASH = #{HEXDIGIT}+ -WS = [\000-\s] -ID = {LOWER}[a-zA-Z0-9_]* - - -Rules. -arg{INT} : {token, {arg, TokenLine, parse_arg(TokenChars)}}. -var{INT} : {token, {var, TokenLine, parse_var(TokenChars)}}. -a : {token, {stack, TokenLine, 0}}. -a{INT} : {token, {stack, TokenLine, parse_acc(TokenChars)}}. - -true : {token, {boolean, TokenLine, true}}. -false : {token, {boolean, TokenLine, false}}. - -RETURN : {token, {mnemonic, TokenLine, 'RETURN'}}. -RETURNR : {token, {mnemonic, TokenLine, 'RETURNR'}}. -CALL : {token, {mnemonic, TokenLine, 'CALL'}}. -NOP : {token, {mnemonic, TokenLine, 'NOP'}}. - -CALL_R : {token, {mnemonic, TokenLine, 'CALL_R'}}. -CALL_T : {token, {mnemonic, TokenLine, 'CALL_T'}}. -CALL_TR : {token, {mnemonic, TokenLine, 'CALL_TR'}}. -JUMP : {token, {mnemonic, TokenLine, 'JUMP'}}. -JUMPIF : {token, {mnemonic, TokenLine, 'JUMPIF'}}. -SWITCH_V2 : {token, {mnemonic, TokenLine, 'SWITCH_V2'}}. -SWITCH_V3 : {token, {mnemonic, TokenLine, 'SWITCH_V3'}}. -SWITCH_VN : {token, {mnemonic, TokenLine, 'SWITCH_VN'}}. - -PUSH : {token, {mnemonic, TokenLine, 'PUSH'}}. -DUP : {token, {mnemonic, TokenLine, 'DUP'}}. -DUPA : {token, {mnemonic, TokenLine, 'DUPA'}}. -POP : {token, {mnemonic, TokenLine, 'POP'}}. - -STORE : {token, {mnemonic, TokenLine, 'STORE'}}. - -ADD : {token, {mnemonic, TokenLine, 'ADD'}}. -MUL : {token, {mnemonic, TokenLine, 'MUL'}}. -SUB : {token, {mnemonic, TokenLine, 'SUB'}}. -DIV : {token, {mnemonic, TokenLine, 'DIV'}}. -MOD : {token, {mnemonic, TokenLine, 'MOD'}}. -POW : {token, {mnemonic, TokenLine, 'POW'}}. - -INC : {token, {mnemonic, TokenLine, 'INC'}}. -DEC : {token, {mnemonic, TokenLine, 'DEC'}}. -INCA : {token, {mnemonic, TokenLine, 'INCA'}}. -DECA : {token, {mnemonic, TokenLine, 'DECA'}}. - -LT : {token, {mnemonic, TokenLine, 'LT'}}. -GT : {token, {mnemonic, TokenLine, 'GT'}}. -EQ : {token, {mnemonic, TokenLine, 'EQ'}}. -ELT : {token, {mnemonic, TokenLine, 'ELT'}}. -EGT : {token, {mnemonic, TokenLine, 'EGT'}}. -NEQ : {token, {mnemonic, TokenLine, 'NEQ'}}. - -AND : {token, {mnemonic, TokenLine, 'AND'}}. -OR : {token, {mnemonic, TokenLine, 'OR'}}. -NOT : {token, {mnemonic, TokenLine, 'NOT'}}. - -TUPLE : {token, {mnemonic, TokenLine, 'TUPLE'}}. -ELEMENT : {token, {mnemonic, TokenLine, 'ELEMENT'}}. - -MAP_EMPTY : {token, {mnemonic, TokenLine, 'MAP_EMPTY'}}. -MAP_LOOKUP : {token, {mnemonic, TokenLine, 'MAP_LOOKUP'}}. -MAP_LOOKUPD : {token, {mnemonic, TokenLine, 'MAP_LOOKUPD'}}. -MAP_UPDATE : {token, {mnemonic, TokenLine, 'MAP_UPDATE'}}. -MAP_MEMBER : {token, {mnemonic, TokenLine, 'MAP_MEMBER'}}. -MAP_DELETE : {token, {mnemonic, TokenLine, 'MAP_DELETE'}}. -MAP_FROM_LIST : {token, {mnemonic, TokenLine, 'MAP_FROM_LIST'}}. - -NIL : {token, {mnemonic, TokenLine, 'NIL'}}. -IS_NIL : {token, {mnemonic, TokenLine, 'IS_NIL'}}. -CONS : {token, {mnemonic, TokenLine, 'CONS'}}. -HD : {token, {mnemonic, TokenLine, 'HD'}}. -TL : {token, {mnemonic, TokenLine, 'TL'}}. -LENGTH : {token, {mnemonic, TokenLine, 'LENGTH'}}. - -STR_EQ : {token, {mnemonic, TokenLine, 'STR_EQ'}}. -STR_JOIN : {token, {mnemonic, TokenLine, 'STR_JOIN'}}. -INT_TO_STR : {token, {mnemonic, TokenLine, 'INT_TO_STR'}}. -ADDR_TO_STR : {token, {mnemonic, TokenLine, 'ADDR_TO_STR'}}. -STR_REVERSE : {token, {mnemonic, TokenLine, 'STR_REVERSE'}}. - -INT_TO_ADDR : {token, {mnemonic, TokenLine, 'INT_TO_ADDR'}}. - -VARIANT : {token, {mnemonic, TokenLine, 'VARIANT'}}. -VARIANT_TEST : {token, {mnemonic, TokenLine, 'VARIANT_TEST'}}. -VARIANT_ELEMENT : {token, {mnemonic, TokenLine, 'VARIANT_ELEMENT'}}. - -BITS_NONE : {token, {mnemonic, TokenLine, 'BITS_NONE'}}. -BITS_NONEA : {token, {mnemonic, TokenLine, 'BITS_NONEA'}}. -BITS_ALL : {token, {mnemonic, TokenLine, 'BITS_ALL'}}. -BITS_ALLA : {token, {mnemonic, TokenLine, 'BITS_ALLA'}}. -BITS_ALL_N : {token, {mnemonic, TokenLine, 'BITS_ALL_N'}}. -BITS_SET : {token, {mnemonic, TokenLine, 'BITS_SET'}}. -BITS_CLEAR : {token, {mnemonic, TokenLine, 'BITS_CLEAR'}}. -BITS_TEST : {token, {mnemonic, TokenLine, 'BITS_TEST'}}. -BITS_SUM : {token, {mnemonic, TokenLine, 'BITS_SUM'}}. -BITS_OR : {token, {mnemonic, TokenLine, 'BITS_OR'}}. -BITS_AND : {token, {mnemonic, TokenLine, 'BITS_AND'}}. -BITS_DIFF : {token, {mnemonic, TokenLine, 'BITS_DIFF'}}. - - -ADDRESS : {token, {mnemonic, TokenLine, 'ADDRESS'}}. -BALANCE : {token, {mnemonic, TokenLine, 'BALANCE'}}. -ORIGIN : {token, {mnemonic, TokenLine, 'ORIGIN'}}. -CALLER : {token, {mnemonic, TokenLine, 'CALLER'}}. -GASPRICE : {token, {mnemonic, TokenLine, 'GASPRICE'}}. -BLOCKHASH : {token, {mnemonic, TokenLine, 'BLOCKHASH'}}. -BENEFICIARY : {token, {mnemonic, TokenLine, 'BENEFICIARY'}}. -TIMESTAMP : {token, {mnemonic, TokenLine, 'TIMESTAMP'}}. -NUMBER : {token, {mnemonic, TokenLine, 'NUMBER'}}. -DIFFICULTY : {token, {mnemonic, TokenLine, 'DIFFICULTY'}}. -GASLIMIT : {token, {mnemonic, TokenLine, 'GASLIMIT'}}. -GAS : {token, {mnemonic, TokenLine, 'GAS'}}. -LOG0 : {token, {mnemonic, TokenLine, 'LOG0'}}. -LOG1 : {token, {mnemonic, TokenLine, 'LOG1'}}. -LOG2 : {token, {mnemonic, TokenLine, 'LOG2'}}. -LOG3 : {token, {mnemonic, TokenLine, 'LOG3'}}. -LOG4 : {token, {mnemonic, TokenLine, 'LOG4'}}. -ABORT : {token, {mnemonic, TokenLine, 'ABORT'}}. -EXIT : {token, {mnemonic, TokenLine, 'EXIT'}}. -DEACTIVATE : {token, {mnemonic, TokenLine, 'DEACTIVATE'}}. -COMMENT : {token, {mnemonic, TokenLine, 'COMMENT'}}. - -FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}. - -{ID} : - {token, {id, TokenLine, TokenChars}}. -{HEX} : - {token, {int, TokenLine, parse_hex(TokenChars)}}. -{INT} : - {token, {int, TokenLine, parse_int(TokenChars)}}. -{HASH} : - {token, {hash, TokenLine, parse_hash(TokenChars)}}. - - -%% Symbols -\-\> : {token, {'to', TokenLine}}. -\: : {token, {'to', TokenLine}}. -, : {token, {',', TokenLine}}. -\( : {token, {'(', TokenLine}}. -\) : {token, {')', TokenLine}}. -\[ : {token, {'[', TokenLine}}. -\] : {token, {']', TokenLine}}. -\{ : {token, {'{', TokenLine}}. -\} : {token, {'}', TokenLine}}. - -;;.* : - {token, {comment, TokenLine, drop_prefix($;, TokenChars)}}. - -\. : skip_token. - - -%% Whitespace ignore -{WS} : skip_token. - -%% Comments (TODO: nested comments) - - -. : {error, "Unexpected token: " ++ TokenChars}. - -Erlang code. - --export([scan/1]). - --dialyzer({nowarn_function, yyrev/2}). - --ignore_xref([format_error/1, string/2, token/2, token/3, tokens/2, tokens/3]). - --include_lib("aebytecode/include/aeb_fate_opcodes.hrl"). - - -parse_hex("0x" ++ Chars) -> list_to_integer(Chars, 16). - -parse_int(Chars) -> list_to_integer(Chars). - -parse_arg("arg" ++ N) -> list_to_integer(N). -parse_var("var" ++ N) -> list_to_integer(N). -parse_acc("a" ++ N) -> list_to_integer(N). - - -parse_hash("#" ++ Chars) -> - N = list_to_integer(Chars, 16), - <>. - -scan(S) -> - string(S). - -drop_prefix(C, [C|Rest]) -> - drop_prefix(C, Rest); -drop_prefix(_, Tail) -> Tail. diff --git a/src/aeb_fate_code.erl b/src/aeb_fate_code.erl deleted file mode 100644 index 26c881a..0000000 --- a/src/aeb_fate_code.erl +++ /dev/null @@ -1,303 +0,0 @@ --module(aeb_fate_code). - --export([ return/0 - , return/1 - , call/1 - , call_t/1 - , call_r/2 - , call_tr/2 - , jump/1 - , jumpif/2 - , switch/3 - , switch/4 - , switch/5 - , switch/6 - , push/1 - , inc/0 - , inc/1 - , dec/0 - , dec/1 - , add/3 - , sub/3 - , mul/3 - , divide/3 - , modulo/3 - , pow/3 - , lt/3 - , gt/3 - , elt/3 - , egt/3 - , eq/3 - , neq/3 - , and_op/3 - , or_op/3 - , not_op/2 - , tuple/1 - , element_op/4 - , map_empty/1 - , map_lookup/3 - , map_lookup/4 - , map_update/4 - , map_member/3 - , map_from_list/2 - , nil/1 - , is_nil/2 - , cons/3 - , hd/2 - , tl/2 - , length/2 - , str_eq/3 - , str_join/3 - , int_to_str/2 - , addr_to_str/2 - , str_reverse/2 - , int_to_addr/2 - , variant_test/3 - , variant_element/3 - , variant/4 - , bits_none/0 - , bits_none/1 - , bits_all/0 - , bits_all/1 - , bits_all_n/2 - , bits_set/3 - , bits_clear/3 - , bits_test/3 - , bits_sum/2 - , bits_or/3 - , bits_and/3 - , bits_diff/3 - , dup/0 - , dup/1 - , pop/0 - , store/2 - , nop/0 - ]). - --define(i(__X__), {immediate, __X__ }). - -return() -> - 'RETURN'. - -return(Arg) -> - {'RETURNR', Arg}. - -call(Function) when is_binary(Function)-> - {'CALL', ?i(Function) }. - -call_t(Function) when is_binary(Function) -> - {'CALL_T', ?i(Function)}. - -call_r(Contract, Function) when is_binary(Function) -> - {'CALL_R', Contract, ?i(Function)}. - -call_tr(Contract, Function) when is_binary(Function) -> - {'CALL_TR', Contract, ?i(Function)}. - -jump(BB) when is_integer(BB) -> - {'JUMP', ?i(BB)}. - -jumpif(Arg, BB) when is_integer(BB) -> - {'JUMPIF', Arg, ?i(BB)}. - -switch(Arg, BB1, BB2) when is_integer(BB1), - is_integer(BB2) -> - {'SWITCH_V2', Arg, ?i(BB1), ?i(BB2)}. - -switch(Arg, BB1, BB2, BB3) when is_integer(BB1), - is_integer(BB2), - is_integer(BB3) -> - {'SWITCH_V3', Arg, ?i(BB1), ?i(BB2), ?i(BB3)}. - -switch(Arg, BB1, BB2, BB3, BB4) when is_integer(BB1), - is_integer(BB2), - is_integer(BB3), - is_integer(BB4) -> - {'SWITCH_V4', Arg, ?i(BB1), ?i(BB2), ?i(BB3), ?i(BB4)}. - -switch(Arg, BB1, BB2, BB3, BB4, BB5) when is_integer(BB1), - is_integer(BB2), - is_integer(BB3), - is_integer(BB4), - is_integer(BB5) -> - {'SWITCH_V5', Arg, ?i(BB1), ?i(BB2), ?i(BB3), ?i(BB4), ?i(BB5)}. - -push(Arg) -> - {'PUSH', Arg}. - -inc() -> - 'INCA'. - -inc(Arg) -> - {'INC', Arg}. - -dec() -> - 'DECA'. - -dec(Arg) -> - {'DEC', Arg}. - -add(Dest, Left, Right) -> - {'ADD', Dest, Left, Right}. - -sub(Dest, Left, Right) -> - {'SUB', Dest, Left, Right}. - -mul(Dest, Left, Right) -> - {'MUL', Dest, Left, Right}. - -divide(Dest, Left, Right) -> - {'DIV', Dest, Left, Right}. - -modulo(Dest, Left, Right) -> - {'MOD', Dest, Left, Right}. - -pow(Dest, Left, Right) -> - {'POW', Dest, Left, Right}. - -lt(Dest, Left, Right) -> - {'LT', Dest, Left, Right}. - -gt(Dest, Left, Right) -> - {'GT', Dest, Left, Right}. - -elt(Dest, Left, Right) -> - {'ELT', Dest, Left, Right}. - -egt(Dest, Left, Right) -> - {'EGT', Dest, Left, Right}. - -eq(Dest, Left, Right) -> - {'EQ', Dest, Left, Right}. - -neq(Dest, Left, Right) -> - {'NEQ', Dest, Left, Right}. - -and_op(Dest, Left, Right) -> - {'AND', Dest, Left, Right}. - -or_op(Dest, Left, Right) -> - {'OR', Dest, Left, Right}. - -not_op(Dest, Arg) -> - {'NOT', Dest, Arg}. - -tuple(Size) when is_integer(Size) -> - {'TUPLE', ?i(Size)}. - -element_op(Type, Dest, N, T) -> - {'ELEMENT', Type, Dest, N, T}. - -map_empty(Dest) -> - {'MAP_EMPTY', Dest}. - -map_lookup(Dest, Map, Key) -> - {'MAP_LOOKUP', Dest, Map, Key}. - -map_lookup(Dest, Map, Key, Default) -> - {'MAP_LOOKUPD', Dest, Map, Key, Default}. - -map_update(Dest, Map, Key, Value) -> - {'MAP_UPDATE', Dest, Map, Key, Value}. - -map_member(Dest, Map, Key) -> - {'MAP_MEMBER', Dest, Map, Key}. - -map_from_list(Dest, List) -> - {'MAP_MEMBER', Dest, List}. - -nil(Dest) -> - {'NIL', Dest}. - -is_nil(Dest, List) -> - {'IS_NIL', Dest, List}. - -cons(Dest, Hd, Tl) -> - {'CONS', Dest, Hd, Tl}. - -hd(Dest, List) -> - {'HD', Dest, List}. - -tl(Dest, List) -> - {'TL', Dest, List}. - -length(Dest, List) -> - {'LENGTH', Dest, List}. - -str_eq(Dest, Str1, Str2) -> - {'STR_EQ', Dest, Str1, Str2}. - -str_join(Dest, Str1, Str2) -> - {'STR_JOIN', Dest, Str1, Str2}. - -int_to_str(Dest, Str) -> - {'INT_TO_STR', Dest, Str}. - -addr_to_str(Dest, Str) -> - {'ADDR_TO_STR', Dest, Str}. - -str_reverse(Dest, Str) -> - {'STR_REVERSE', Dest, Str}. - -int_to_addr(Dest, Str) -> - {'INT_TO_ADDR', Dest, Str}. - -variant_test(Dest, Variant, Tag) -> - {'VARIANT_TEST', Dest, Variant, Tag}. - -variant_element( Dest, Variant, Index) -> - {'VARIANT_ELEMENT', Dest, Variant, Index}. - -variant(Dest, SizeA, TagA, ElementsA) -> - {'VARIANT', Dest, SizeA, TagA, ElementsA}. - -bits_none() -> - 'BITS_NONEA'. - -bits_none(To) -> - {'BITS_NONE', To}. - -bits_all() -> - 'BITS_ALLA'. - -bits_all(To) -> - {'BITS_ALL', To}. - -bits_all_n(To, N) -> - {'BITS_ALL_N', To, N}. - -bits_set(To, Bits, Bit) -> - {'BITS_SET', To, Bits, Bit}. - -bits_clear(To, Bits, Bit) -> - {'BITS_CLEAR', To, Bits, Bit}. - -bits_test(To, Bits, Bit) -> - {'BITS_TEST', To, Bits, Bit}. - -bits_sum(To, Bits) -> - {'BITS_SUM', To, Bits}. - -bits_or(To, Bits, Bit) -> - {'BITS_OR', To, Bits, Bit}. - -bits_and(To, Bits, Bit) -> - {'BITS_AND', To, Bits, Bit}. - -bits_diff(To, Bits, Bit) -> - {'BITS_DIFF', To, Bits, Bit}. - -dup() -> - 'DUPA'. - -dup(N) when is_integer(N) -> - {'DUP', ?i(N)}. - -pop() -> - 'POP'. - -store(Var, What) -> - {'STORE', Var, What}. - -nop() -> - 'NOP'. diff --git a/src/aeb_fate_data.erl b/src/aeb_fate_data.erl index 243b204..f0a6898 100644 --- a/src/aeb_fate_data.erl +++ b/src/aeb_fate_data.erl @@ -15,10 +15,18 @@ -type fate_variant() :: ?FATE_VARIANT_T. --type fate_void() :: ?FATE_VOID_T. - -type fate_tuple() :: ?FATE_TUPLE_T. +-type fate_type_type() :: integer + | boolean + | {list, fate_type()} + | {map, fate_type(), fate_type()} + | {tuple, [fate_type()]} + | address + | bits + | {variant, integer()}. + + -type fate_type() :: fate_boolean() | fate_integer() @@ -30,11 +38,21 @@ | fate_address() | fate_variant() | fate_map() - | fate_list() - | fate_tuple() - | fate_void(). %% Not sure we need this. + | fate_type_type(). --export_type([fate_type/0]). +-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_variant/0 + , fate_map/0 + , fate_type_type/0 + ]). -export([ make_integer/1 , make_boolean/1 @@ -111,7 +129,6 @@ decode(M) when ?IS_FATE_MAP(M) -> -spec format(fate_type()) -> iolist(). format(I) when ?IS_FATE_INTEGER(I) -> integer_to_list(?MAKE_FATE_INTEGER(I)); -format(?FATE_VOID) -> "void"; format(?FATE_TRUE) -> "true"; format(?FATE_FALSE) -> "false"; format(?FATE_NIL) -> "[]"; diff --git a/src/aeb_fate_generate_ops.erl b/src/aeb_fate_generate_ops.erl new file mode 100644 index 0000000..3eb7def --- /dev/null +++ b/src/aeb_fate_generate_ops.erl @@ -0,0 +1,402 @@ +-module(aeb_fate_generate_ops). + +-export([ gen_and_halt/1 + , generate/0]). + +gen_and_halt([SrcDirArg, IncludeDirArg]) -> + generate(atom_to_list(SrcDirArg), + atom_to_list(IncludeDirArg)), + halt(). + +generate() -> + generate("src/", "include/"). + +generate(Src, Include) -> + Ops = gen(ops_defs()), + %% io:format("ops: ~p\n", [Ops]), + HrlFile = Include ++ "aeb_fate_opcodes.hrl", + generate_header_file(HrlFile, Ops), + generate_opcodes_ops(aeb_fate_opcodes, HrlFile, Src, Ops), + generate_code_ops(aeb_fate_code, Src, Ops), + generate_scanner("aeb_fate_asm_scan.template", "aeb_fate_asm_scan.xrl", Src, Ops). + +%% TODO: Some real gas numbers... +ops_defs() -> + %% Opname, Opcode, args, end_bb, gas, format, Constructor, Documentation + [ { 'RETURN', 16#00, 0, true, 2, atomic, return, "Return from function call pop stack to arg0. The type of the retun value has to match the return type of the function."} + , { 'RETURNR', 16#01, 1, true, 2, [a], returnr, "Return from function call copy Arg0 to arg0. The type of the retun value has to match the return type of the function."} + , { 'CALL', 16#02, 1, true, 4, [is], call, "Call given function with args on stack. The types of the arguments has to match the argument typs of the function."} + , { 'CALL_R', 16#03, 2, true, 8, [a,is], call_r, "Remote call to given contract and function. The types of the arguments has to match the argument typs of the function."} + , { 'CALL_T', 16#04, 1, true, 4, [is], call_t, "Tail call to given function. The types of the arguments has to match the argument typs of the function. And the return type of the called function has to match the type of the current function."} + , { 'CALL_TR', 16#05, 2, true, 8, [a,is], call_tr, "Remote tail call to given contract and function. The types of the arguments has to match the argument typs of the function. And the return type of the called function has to match the type of the current function."} + , { 'JUMP', 16#06, 1, true, 3, [ii], jump, "Jump to a basic block. The basic block has to exist in the current function."} + , { 'JUMPIF', 16#07, 2, true, 4, [a,ii], jumpif, "Conditional jump to a basic block. If Arg0 then jump to Arg1."} + , { 'SWITCH_V2', 16#08, 3, true, 4, [a,ii,ii], switch, "Conditional jump to a basic block on variant tag."} + , { 'SWITCH_V3', 16#09, 4, true, 4, [a,ii,ii,ii], switch, "Conditional jump to a basic block on variant tag."} + , { 'SWITCH_VN', 16#0a, 2, true, 4, [a,li], switch, "Conditional jump to a basic block on variant tag."} + , { 'PUSH', 16#0b, 1, false, 2, [a], push, "Push argument to stack."} + , { 'DUPA', 16#0c, 0, false, 3, atomic, dup, "push copy of accumulator on stack."} + , { 'DUP', 16#0d, 1, false, 3, [a], dup, "push Arg0 stack pos on top of stack."} + , { 'POP', 16#0e, 1, false, 3, [a], pop, "Arg0 := top of stack."} + , { 'STORE', 16#0f, 2, false, 3, [a,a], store, "Arg0 := Arg1."} + , { 'INCA', 16#10, 0, false, 2, atomic, inc, "Increment accumulator."} + , { 'INC', 16#11, 1, false, 2, [a], inc, "Increment argument."} + , { 'DECA', 16#12, 0, false, 2, atomic, dec, "Decrement accumulator."} + , { 'DEC', 16#13, 1, false, 2, [a], dec, "Decrement argument."} + , { 'ADD', 16#14, 3, false, 3, [a,a,a], add, "Arg0 := Arg1 + Arg2."} + , { 'SUB', 16#15, 3, false, 3, [a,a,a], sub, "Arg0 := Arg1 - Arg2."} + , { 'MUL', 16#16, 3, false, 3, [a,a,a], mul, "Arg0 := Arg1 * Arg2."} + , { 'DIV', 16#17, 3, false, 3, [a,a,a], divide, "Arg0 := Arg1 / Arg2."} + , { 'MOD', 16#18, 3, false, 3, [a,a,a], modulo, "Arg0 := Arg1 mod Arg2."} + , { 'POW', 16#19, 3, false, 3, [a,a,a], pow, "Arg0 := Arg1 ^ Arg2."} + , { 'LT', 16#20, 3, false, 3, [a,a,a], lt, "Arg0 := Arg1 < Arg2."} + , { 'GT', 16#21, 3, false, 3, [a,a,a], gt, "Arg0 := Arg1 > Arg2."} + , { 'EQ', 16#22, 3, false, 3, [a,a,a], eq, "Arg0 := Arg1 = Arg2."} + , { 'ELT', 16#23, 3, false, 3, [a,a,a], elt, "Arg0 := Arg1 =< Arg2."} + , { 'EGT', 16#24, 3, false, 3, [a,a,a], egt, "Arg0 := Arg1 >= Arg2."} + , { 'NEQ', 16#25, 3, false, 3, [a,a,a], neq, "Arg0 := Arg1 /= Arg2."} + , { 'AND', 16#26, 3, false, 3, [a,a,a], and_op, "Arg0 := Arg1 and Arg2."} + , { 'OR', 16#27, 3, false, 3, [a,a,a], or_op, "Arg0 := Arg1 or Arg2."} + , { 'NOT', 16#28, 2, false, 3, [a,a], not_op, "Arg0 := not Arg1."} + , { 'TUPLE', 16#29, 1, false, 3, [ii], tuple, "Create a tuple of size = Arg0. Elements on stack."} + , { 'ELEMENT', 16#2a, 4, false, 3, [t,a,a,a], element_op, "Arg1 := element(Arg2, Arg3). The element should be of type Arg1"} + , { 'MAP_EMPTY', 16#2b, 1, false, 3, [a], map_empty, "Arg0 := #{}."} + , { 'MAP_LOOKUP', 16#2c, 3, false, 3, [a,a,a], map_lookup, "Arg0 := lookup key Arg2 in map Arg1."} + , { 'MAP_LOOKUPD', 16#2d, 4, false, 3, [a,a,a,a], map_lookup, "Arg0 := lookup key Arg2 in map Arg1 if key exists in map otherwise Arg0 := Arg3."} + , { 'MAP_UPDATE', 16#2e, 4, false, 3, [a,a,a,a], map_update, "Arg0 := update key Arg2 in map Arg1 with value Arg3."} + , { 'MAP_DELETE', 16#2f, 3, false, 3, [a,a,a], map_delete, "Arg0 := delete key Arg2 from map Arg1."} + , { 'MAP_MEMBER', 16#30, 3, false, 3, [a,a,a], map_member, "Arg0 := true if key Arg2 is in map Arg1."} + , { 'MAP_FROM_LIST',16#31, 2, false, 3, [a,a], map_from_list, "Arg0 := make a map from (key, value) list in Arg1."} + , { 'NIL', 16#32, 1, false, 3, [a], nil, "Arg0 := []."} + , { 'IS_NIL', 16#33, 2, false, 3, [a,a], is_nil, "Arg0 := true if Arg1 == []."} + , { 'CONS', 16#34, 3, false, 3, [a,a,a], cons, "Arg0 := [Arg1|Arg2]."} + , { 'HD', 16#35, 2, false, 3, [a,a], hd, "Arg0 := head of list Arg1."} + , { 'TL', 16#36, 2, false, 3, [a,a], tl, "Arg0 := tail of list Arg1."} + , { 'LENGTH', 16#37, 2, false, 3, [a,a], length, "Arg0 := length of list Arg1."} + , { 'STR_EQ', 16#38, 3, false, 3, [a,a,a], str_eq, "Arg0 := true iff the strings Arg1 and Arg2 are the same."} + , { 'STR_JOIN', 16#39, 3, false, 3, [a,a,a], str_join, "Arg0 := string Arg1 followed by string Arg2."} + , { 'INT_TO_STR', 16#40, 2, false, 3, [a,a], int_to_str, "Arg0 := turn integer Arg1 into a string."} + , { 'ADDR_TO_STR', 16#41, 2, false, 3, [a,a], addr_to_str, "Arg0 := turn address Arg1 into a string."} + , { 'STR_REVERSE', 16#42, 2, false, 3, [a,a], str_reverse, "Arg0 := the reverse of string Arg1."} + , { 'INT_TO_ADDR', 16#43, 2, false, 3, [a,a], int_to_addr, "Arg0 := turn integer Arg1 into an address."} + , { 'VARIANT', 16#44, 4, false, 3, [a,a,a,a], variant, "Arg0 := create a variant of size Arg1 with the tag Arg2 (Arg2 < Arg1) and take Arg3 elements from the stack."} + , { 'VARIANT_TEST', 16#45, 3, false, 3, [a,a,a], variant_test, "Arg0 := true if variant Arg1 has the tag Arg2."} + , { 'VARIANT_ELEMENT',16#46, 3, false, 3, [a,a,a], variant_element, "Arg0 := element number Arg2 from variant Arg1."} + , { 'BITS_NONEA', 16#47, 0, false, 3, atomic, bits_none, "accumulator := empty bitmap."} + , { 'BITS_NONE', 16#48, 1, false, 3, [a], bits_none, "Arg0 := empty bitmap."} + , { 'BITS_ALLA', 16#49, 0, false, 3, atomic, bits_all, "accumulator := full bitmap."} + , { 'BITS_ALL', 16#50, 1, false, 3, [a], bits_all, "Arg0 := full bitmap."} + , { 'BITS_ALL_N', 16#51, 2, false, 3, [a,a], bits_all_n, "Arg0 := bitmap with Arg1 bits set."} + , { 'BITS_SET', 16#52, 3, false, 3, [a,a,a], bits_set, "Arg0 := set bit Arg2 of bitmap Arg1."} + , { 'BITS_CLEAR', 16#53, 3, false, 3, [a,a,a], bits_clear, "Arg0 := clear bit Arg2 of bitmap Arg1."} + , { 'BITS_TEST', 16#54, 3, false, 3, [a,a,a], bits_test, "Arg0 := true if bit Arg2 of bitmap Arg1 is set."} + , { 'BITS_SUM', 16#55, 2, false, 3, [a,a], bits_sum, "Arg0 := sum of set bits in bitmap Arg1. Exception if infinit bitmap."} + , { 'BITS_OR', 16#56, 3, false, 3, [a,a,a], bits_or, "Arg0 := Arg1 v Arg2."} + , { 'BITS_AND', 16#57, 3, false, 3, [a,a,a], bits_and, "Arg0 := Arg1 ^ Arg2."} + , { 'BITS_DIFF', 16#58, 3, false, 3, [a,a,a], bits_diff, "Arg0 := Arg1 - Arg2."} + , { 'ADDRESS', 16#59, 1, false, 3, [a], address, "Arg0 := The current contract address."} + , { 'BALANCE', 16#5a, 1, false, 3, [a], balance, "Arg0 := The current contract address."} + , { 'ORIGIN', 16#5b, 1, false, 3, [a], origin, "Arg0 := Address of contract called by the call transaction."} + , { 'CALLER', 16#5c, 1, false, 3, [a], caller, "Arg0 := The address that signed the call transaction."} + , { 'GASPRICE', 16#5d, 1, false, 3, [a], gasprice, "Arg0 := The current gas price."} + , { 'BLOCKHASH', 16#5e, 1, false, 3, [a], blockhash, "Arg0 := The current blockhash."} %% TODO: Do we support has at height? + , { 'BENEFICIARY', 16#5f, 1, false, 3, [a], beneficiary, "Arg0 := The address of the current beneficiary."} + , { 'TIMESTAMP', 16#60, 1, false, 3, [a], timestamp, "Arg0 := The current timestamp. Unrelaiable, don't use for anything."} + , { 'GENERATION', 16#61, 1, false, 3, [a], generation, "Arg0 := The block height of the cureent generation."} + , { 'MICROBLOCK', 16#62, 1, false, 3, [a], microblock, "Arg0 := The current micro block number."} + , { 'DIFFICULTY', 16#63, 1, false, 3, [a], difficulty, "Arg0 := The current difficulty."} + , { 'GASLIMIT', 16#64, 1, false, 3, [a], gaslimit, "Arg0 := The current gaslimit."} + , { 'GAS', 16#65, 1, false, 3, [a], gas, "Arg0 := The amount of gas left."} + + , { 'LOG0', 16#66, 2, false, 3, [a,a], log, "Create a log message in the call object."} + , { 'LOG1', 16#67, 3, false, 3, [a,a,a], log, "Create a log message with one topic in the call object."} + , { 'LOG2', 16#68, 4, false, 3, [a,a,a,a], log, "Create a log message with two topics in the call object."} + , { 'LOG3', 16#69, 5, false, 3, [a,a,a,a,a], log, "Create a log message with three topics in the call object."} + , { 'LOG4', 16#6a, 6, false, 3, [a,a,a,a,a,a], log, "Create a log message with four topics in the call object."} + , { 'DEACTIVATE', 16#6b, 0, false, 3, atomic, deactivate, "Mark the current contract for deactication."} + %% Transaction ops + , { 'SPEND', 16#6c, 2, false,3, [a,a], spend, "Transfer Arg0 tokens to account Arg1. (If the contract account has at least that many tokens."} + , { 'ORACLE_REGISTER', 16#6d, 6, false,3, [a,a,a,a,a,a], oracle_register, "Mark the current contract for deactication."} + %% TODO: + , { 'ORACLE_QUERY', 16#6e, 0, false,3, atomic, oracle_query, ""} + , { 'ORACLE_RESPOND', 16#6f, 0, false,3, atomic, oracle_respond, ""} + , { 'ORACLE_EXTEND', 16#70, 0, false,3, atomic, oracle_extend, ""} + , { 'ORACLE_GET_ANSWER', 16#71, 0, false,3, atomic, oracle_get_answer, ""} + , { 'ORACLE_GET_QUESTION', 16#72, 0, false,3, atomic,oracle_get_question, ""} + , { 'ORACLE_QUERY_FEE', 16#73, 0, false,3, atomic, oracle_query_fee, ""} + , { 'AENS_RESOLVE', 16#74, 0, false,3, atomic, aens_resolve, ""} + , { 'AENS_PRECLAIM', 16#75, 0, false,3, atomic, aens_preclaim, ""} + , { 'AENS_CLAIM', 16#76, 0, false,3, atomic, aens_claim, ""} + , { 'AENS_UPDATE', 16#77, 0, false,3, atomic, aend_update, ""} + , { 'AENS_TRANSFER', 16#78, 0, false,3, atomic, aens_transfer, ""} + , { 'AENS_REVOKE', 16#79, 0, false,3, atomic, aens_revoke, ""} + , { 'ECVERIFY', 16#7a, 0, false,3, atomic, ecverify, ""} + , { 'SHA3', 16#7b, 0, false,3, atomic, sha3, ""} + , { 'SHA256', 16#7c, 0, false,3, atomic, sha256, ""} + , { 'BLAKE2B', 16#7d, 0, false,3, atomic, blake2b, ""} + + , {'ABORT', 16#fb, 1, false, 3, [a], abort, "Abort execution (dont use all gas) with error message in Arg0."} + , {'EXIT', 16#fc, 1, false, 3, [a], exit, "Abort execution (use upp all gas) with error message in Arg0."} + , { 'NOP', 16#fd, 0, false, 1, atomic, nop, "The no op. does nothing."} + %% FUNCTION 16#fe "Function declaration and entrypoint." + %% EXTEND 16#ff "Reserved for future extensions beyond one byte opcodes." + ]. + + +generate_header_file(Filename, Ops) -> + {ok, File} = file:open(Filename, [write]), + Defines = lists:flatten([gen_defines(Op) || Op <- Ops]), + io:format(File, "~s", [prelude("Provides opcode defines.\n")]), + io:format(File, "%% FATE opcodes\n~s", [Defines]), + io:format(File, "~s", + ["-define('FUNCTION' , 16#fe).\n" + "-define('EXTEND' , 16#ff).\n\n"]), + file:close(File). + +generate_opcodes_ops(Modulename, HrlFile, SrcDir, Ops) -> + Filename = SrcDir ++ atom_to_list(Modulename) ++ ".erl", + + {ok, File} = file:open(Filename, [write]), + Mnemonic = lists:flatten([gen_mnemonic(Op) || Op <- Ops]), + ToOp = lists:flatten([gen_m_to_op(Op) || Op <- Ops]), + Args = lists:flatten([gen_args(Op) || Op <- Ops]), + EndBB = lists:flatten([gen_bb(Op) || Op <- Ops]), + + io:format(File, "~s", [prelude("Provides opcode primitives.\n")]), + io:format(File, "~s", [ops_exports(Modulename, HrlFile, + ["args/1\n" + " , end_bb/1\n" + " , mnemonic/1\n" + " , m_to_op/1\n" + ])]), + + io:format(File, "%% FATE mnemonics\n~s", [Mnemonic]), + io:format(File, "mnemonic(Op) -> exit({bad_opcode, Op}).\n\n", []), + + io:format(File, "%% FATE opcodes\n~s", [ToOp]), + io:format(File, "m_to_op(M) -> exit({bad_mnemonic, M}).\n\n", []), + + io:format(File, "%% FATE numbers of args to op.\n~s", [Args]), + io:format(File, "args(Op) -> exit({bad_opcode, Op}).\n\n", []), + + io:format(File, "%% Does FATE Op end a Basic Block?\n~s", [EndBB]), + io:format(File, "end_bb(_) -> false.\n\n", []), + + file:close(File). + +generate_code_ops(Modulename, SrcDir, Ops) -> + Filename = SrcDir ++ atom_to_list(Modulename) ++ ".erl", + + {ok, File} = file:open(Filename, [write]), + Types = lists:flatten([gen_type(Op) || Op <- Ops]), + TypeExports = lists:flatten([gen_type_exports(Op) || Op <- Ops]), + [#{type_name := FirstType} | RestOfOps] = Ops, + FateTypes = lists:flatten([gen_fate_code_type(Op) || Op <- RestOfOps]), + ConstructorExports = lists:flatten([gen_constructor_exports(Op) || Op <- Ops]), + Constructors = lists:flatten([gen_constructors(Op) || Op <- Ops]), + + io:format(File, "~s", [prelude(" Provide constructor functuions for " + "Fate instructions.\n%%% Provide types" + " and documentation for Fate " + "instructions.\n")]), + io:format(File, "-module(~w).\n\n", [Modulename]), + io:format(File, "-include_lib(\"aebytecode/include/aeb_fate_data.hrl\").\n\n" + "-define(i(__X__), {immediate, __X__ }).\n\n" + "-type fate_arg_immediate(T) :: {immediate, T}.\n" + "-type fate_arg_var() :: {var, integer()}.\n" + "-type fate_arg_arg() :: {arg, integer()}.\n" + "-type fate_arg_stack() :: {stack, integer()}.\n" + "-type fate_arg() :: fate_arg_immediate()\n" + " | fate_arg_var()\n" + " | fate_arg_arg()\n" + " | fate_arg_stack().\n\n" + "-type fate_arg_immediate() :: {immediate, aeb_fate_data:fate_type()}.\n" + , []), + io:format(File, "~s", [Types]), + io:format(File, "-type fate_code() :: ~s\n~s .\n\n", + [FirstType, FateTypes]), + io:format(File, "-export_type([ fate_code/0\n~s ]).\n\n", [TypeExports]), + io:format(File, "-export([ foo/0\n~s ]).\n\n", [ConstructorExports]), + io:format(File, "~s\n", [Constructors]), + + io:format(File, "foo() -> \"A temp hack.\".\n", []), + + file:close(File). + +gen_type(#{type_name := TypeName, type := Type}) -> + lists:flatten(io_lib:format("-type ~-26s :: ~s.\n", + [TypeName, Type])). + +gen_fate_code_type(#{type_name := TypeName}) -> + lists:flatten(io_lib:format(" | ~s\n", [TypeName])). + +gen_type_exports(#{type_name := TypeName}) -> + lists:flatten(io_lib:format(" , ~s/0\n", [TypeName--"()"])). + +gen_constructor_exports(#{constructor_type := Function}) -> + lists:flatten(io_lib:format(" , ~s\n", [Function])). + +gen_constructors(#{constructor := Function, format := atomic, + type_name := Type, opname := Name}) -> + lists:flatten(io_lib:format("-spec ~s() -> ~s.\n" + "~s() ->\n" + " ~w.\n\n", + [Function, Type, Function, Name])); +gen_constructors(#{constructor := Function, format := ArgSpec, + type_name := Type, opname := Name}) -> + ArgTypeSpecs = gen_arg_type_specs(ArgSpec), + Args = gen_arg_names(0, ArgSpec), + UseArgs = gen_arg_uses(0, ArgSpec), + lists:flatten(io_lib:format("-spec ~s(~s) -> ~s.\n" + "~s(~s) ->\n" + " {~w, ~s}.\n\n", + [Function, ArgTypeSpecs, Type, + Function, Args, Name, UseArgs])). + +gen_arg_type_specs([]) -> []; +gen_arg_type_specs([a]) -> "fate_arg()"; +gen_arg_type_specs([is]) -> "aeb_fate_data:fate_string()"; +gen_arg_type_specs([ii]) -> "aeb_fate_data:fate_integer()"; +gen_arg_type_specs([li]) -> "[aeb_fate_data:fate_integer()]"; +gen_arg_type_specs([t]) -> "aeb_fate_data:fate_type_type()"; +gen_arg_type_specs([a | Args]) -> "fate_arg(), " ++ gen_arg_type_specs(Args); +gen_arg_type_specs([is | Args]) -> "aeb_fate_data:fate_string(), " ++ gen_arg_type_specs(Args); +gen_arg_type_specs([ii | Args]) -> "aeb_fate_data:fate_integer(), " ++ gen_arg_type_specs(Args); +gen_arg_type_specs([li | Args]) -> "[aeb_fate_data:fate_integer()], " ++ gen_arg_type_specs(Args); +gen_arg_type_specs([t | Args]) -> "aeb_fate_data:fate_type_type(), " ++ gen_arg_type_specs(Args). + + +gen_arg_names(_, []) -> + []; +gen_arg_names(N, [_]) -> io_lib:format("Arg~w", [N]); +gen_arg_names(N, [_|Args]) -> + io_lib:format("Arg~w, ", [N]) ++ gen_arg_names(N+1, Args). + +gen_arg_uses(_, []) -> + []; +gen_arg_uses(N, [a]) -> io_lib:format("Arg~w", [N]); +gen_arg_uses(N, [is]) -> io_lib:format("{immediate, Arg~w}", [N]); +gen_arg_uses(N, [ii]) -> io_lib:format("{immediate, Arg~w}", [N]); +gen_arg_uses(N, [li]) -> io_lib:format("[{immediate, I} || I <- Arg~w]", [N]); +gen_arg_uses(N, [t]) -> io_lib:format("Arg~w", [N]); +gen_arg_uses(N, [a | Args]) -> + io_lib:format("Arg~w, ", [N]) ++ gen_arg_uses(N+1, Args); +gen_arg_uses(N, [is | Args]) -> + io_lib:format("{immediate, Arg~w}, ", [N]) ++ gen_arg_uses(N+1, Args); +gen_arg_uses(N, [ii | Args]) -> + io_lib:format("{immediate, Arg~w}, ", [N]) ++ gen_arg_uses(N+1, Args); +gen_arg_uses(N, [li | Args]) -> + io_lib:format("[{immediate, I} || I <- Arg~w], ", [N]) ++ gen_arg_uses(N+1, Args); +gen_arg_uses(N, [t | Args]) -> + io_lib:format("Arg~w, ", [N]) ++ gen_arg_uses(N+1, Args). + + +ops_exports(Module, HrlFile, Exports) -> + lists:flatten(io_lib:format( + "-module(~w).\n\n" + "-export([ ~s ]).\n\n" + "-include_lib(\"aebytecode/" ++ HrlFile ++"\").\n\n" + "%%====================================================================\n" + "%% API\n" + "%%====================================================================\n", + [Module, Exports])). + +gen_mnemonic(#{opname := Name, macro := Macro}) -> + lists:flatten(io_lib:format("mnemonic(~21s) -> ~21w ;\n", + [Macro, Name])). + + +gen_m_to_op(#{opname := Name, macro := Macro}) -> + lists:flatten(io_lib:format("m_to_op(~21w) -> ~21s ;\n", + [Name, Macro])). + +gen_args(#{macro := Macro, args := Args}) -> + lists:flatten(io_lib:format("args(~21s) -> ~2w ;\n", + [Macro, Args])). + +gen_bb(#{macro := Macro, end_bb := EndBB}) -> + lists:flatten(io_lib:format("end_bb(~21s) -> ~w ;\n", + [Macro, EndBB])). + + +prelude(Doc) -> + "%%%-------------------------------------------------------------------\n" + "%%% @copyright (C) 2019, Aeternity Anstalt\n" + "%%%\n" + "%%% === === N O T E : This file is generated do not edit. === ===\n" + "%%%\n" + "%%% Source is in aeb_fate_generate_ops.erl\n" + "%%% @doc\n" + "%%% "++Doc++ + "%%% @end\n" + "%%%-------------------------------------------------------------------\n\n". + + +gen_defines(#{opname := Name, opcode := OpCode}) -> + lists:flatten(io_lib:format("-define(~-26w, 16#~2.16.0b).\n", [Name, OpCode])). + +gen([]) -> + []; +gen([{OpName, OpCode, Args, EndBB, Gas, FateFormat, Constructor, Doc} | Rest]) -> + Name = atom_to_list(OpName), + LowerName = string:to_lower(Name), + TypeName = "fate_" ++ LowerName ++ "()", + Macro = "?" ++ Name, + Type = case FateFormat of + atomic -> io_lib:format("~w", [OpName]); + ArgTypes -> + io_lib:format("{~w, ~s}", [OpName, expand_types(ArgTypes)]) + end, + ConstructorType = atom_to_list(Constructor) ++ "/" ++ io_lib:format("~w", [Args]), + + [#{ opname => OpName + , opcode => OpCode + , args => Args + , end_bb => EndBB + , format => FateFormat + , macro => Macro + , type_name => TypeName + , doc => Doc + , gas => Gas + , type => Type + , constructor => Constructor + , constructor_type => ConstructorType + }| gen(Rest)]. + + +expand_types([]) -> ""; +expand_types([T]) -> expand_type(T); +expand_types([T|Ts]) ->expand_type(T) ++ ", " ++ expand_types(Ts). + +expand_type(a) -> "fate_arg()"; +expand_type(is) -> "fate_arg_immediate(aeb_fate_data:fate_string())"; +expand_type(ii) -> "fate_arg_immediate(aeb_fate_data:fate_integer())"; +expand_type(li) -> "[fate_arg_immediate(aeb_fate_data:fate_integer())]"; +expand_type(t) -> "aeb_fate_data:fate_type_type()". + +generate_scanner(TemplateFile, Outfile, Path, Ops) -> + {ok, Template} = file:read_file(filename:join(Path,TemplateFile)), + Tokens = lists:flatten([gen_token(Op) || Op <- Ops]), + NewFile = insert_tokens_in_template(Template, Tokens), + file:write_file(filename:join(Path, Outfile), NewFile). + +gen_token(#{opname := OpName}) -> + Name = atom_to_list(OpName), + io_lib:format("~-28s: {token, {mnemonic, TokenLine, ~w}}.\n", + [Name, OpName]). + +insert_tokens_in_template(<<"###REPLACEWITHOPTOKENS###", Rest/binary >>, Tokens) -> + [Tokens, Rest]; +insert_tokens_in_template(<<"###REPLACEWITHNOTE###", Rest/binary >>, Tokens) -> + [ + "%%%\n" + "%%% === === N O T E : This file is generated do not edit. === ===\n" + "%%%\n" + "%%% Source is in aeb_fate_generate_ops.erl\n" + "%%% and aeb_fate_asm_scan.template" + | insert_tokens_in_template(Rest, Tokens)]; +insert_tokens_in_template(<>, Tokens) -> + [B|insert_tokens_in_template(Rest, Tokens)]. + + + diff --git a/src/aeb_fate_opcodes.erl b/src/aeb_fate_opcodes.erl deleted file mode 100644 index 84d93f7..0000000 --- a/src/aeb_fate_opcodes.erl +++ /dev/null @@ -1,363 +0,0 @@ -%%%------------------------------------------------------------------- -%%% @copyright (C) 2019, Aeternity Anstalt -%%% @doc -%%% Opcodes -%%% @end -%%%------------------------------------------------------------------- - --module(aeb_fate_opcodes). - --export([ args/1 - , end_bb/1 - , mnemonic/1 - , m_to_op/1 - , opcode/1 - ]). - --include_lib("aebytecode/include/aeb_fate_opcodes.hrl"). - - -%%==================================================================== -%% API -%%==================================================================== - -opcode(X) when X >= 0, X =< 255 -> X; -opcode({comment,X}) -> ?COMMENT(X). - -mnemonic(?NOP) -> 'NOP' ; -mnemonic(?RETURN) -> 'RETURN' ; -mnemonic(?CALL) -> 'CALL' ; -mnemonic(?CALL_R) -> 'CALL_R' ; -mnemonic(?CALL_T) -> 'CALL_T' ; -mnemonic(?CALL_TR) -> 'CALL_TR' ; -mnemonic(?JUMP) -> 'JUMP' ; -mnemonic(?JUMPIF) -> 'JUMPIF' ; -mnemonic(?PUSH) -> 'PUSH' ; -mnemonic(?DUP) -> 'DUP' ; -mnemonic(?DUPA) -> 'DUPA' ; -mnemonic(?POP) -> 'POP' ; -mnemonic(?STORE) -> 'STORE' ; -mnemonic(?ADD) -> 'ADD' ; -mnemonic(?MUL) -> 'MUL' ; -mnemonic(?SUB) -> 'SUB' ; -mnemonic(?DIV) -> 'DIV' ; -mnemonic(?MOD) -> 'MOD' ; -mnemonic(?POW) -> 'POW' ; -mnemonic(?LT) -> 'LT' ; -mnemonic(?GT) -> 'GT' ; -mnemonic(?EQ) -> 'EQ' ; -mnemonic(?ELT) -> 'ELT' ; -mnemonic(?EGT) -> 'EGT' ; -mnemonic(?NEQ) -> 'NEQ' ; -mnemonic(?AND) -> 'AND' ; -mnemonic(?OR) -> 'OR' ; -mnemonic(?NOT) -> 'NOT' ; -mnemonic(?TUPLE) -> 'TUPLE' ; -mnemonic(?ELEMENT) -> 'ELEMENT' ; -mnemonic(?MAP_EMPTY) -> 'MAP_EMPTY' ; -mnemonic(?MAP_LOOKUP) -> 'MAP_LOOKUP' ; -mnemonic(?MAP_UPDATE) -> 'MAP_UPDATE' ; -mnemonic(?MAP_DELETE) -> 'MAP_DELETE' ; -mnemonic(?MAP_MEMBER) -> 'MAP_MEMBER' ; -mnemonic(?MAP_FROM_LIST) -> 'MAP_FROM_LIST' ; -mnemonic(?NIL) -> 'NIL' ; -mnemonic(?IS_NIL) -> 'IS_NIL' ; -mnemonic(?CONS) -> 'CONS' ; -mnemonic(?HD) -> 'HD' ; -mnemonic(?TL) -> 'TL' ; -mnemonic(?LENGTH) -> 'LENGTH' ; -mnemonic(?STR_EQ) -> 'STR_EQ' ; -mnemonic(?STR_JOIN) -> 'STR_JOIN' ; -mnemonic(?ADDR_TO_STR) -> 'ADDR_TO_STR' ; -mnemonic(?STR_REVERSE) -> 'STR_REVERSE' ; -mnemonic(?INT_TO_ADDR) -> 'INT_TO_ADDR' ; -mnemonic(?VARIANT) -> 'VARIANT' ; -mnemonic(?VARIANT_TEST) -> 'VARIANT_TEST' ; -mnemonic(?VARIANT_ELEMENT) -> 'VARIANT_ELEMENT' ; -mnemonic(?BITS_NONE) -> 'BITS_NONE' ; -mnemonic(?BITS_NONEA) -> 'BITS_NONEA' ; -mnemonic(?BITS_ALL) -> 'BITS_ALL' ; -mnemonic(?BITS_ALLA) -> 'BITS_ALLA' ; -mnemonic(?BITS_SET) -> 'BITS_SET' ; -mnemonic(?BITS_CLEAR) -> 'BITS_CLEAR' ; -mnemonic(?BITS_TEST) -> 'BITS_TEST' ; -mnemonic(?BITS_SUM) -> 'BITS_SUM' ; -mnemonic(?BITS_OR) -> 'BITS_OR' ; -mnemonic(?BITS_AND) -> 'BITS_AND' ; -mnemonic(?BITS_DIFF) -> 'BITS_DIFF' ; -mnemonic(?ADDRESS) -> 'ADDRESS' ; -mnemonic(?BALANCE) -> 'BALANCE' ; -mnemonic(?ORIGIN) -> 'ORIGIN' ; -mnemonic(?CALLER) -> 'CALLER' ; -mnemonic(?GASPRICE) -> 'GASPRICE' ; -mnemonic(?BLOCKHASH) -> 'BLOCKHASH' ; -mnemonic(?BENEFICIARY) -> 'BENEFICIARY' ; -mnemonic(?TIMESTAMP) -> 'TIMESTAMP' ; -mnemonic(?NUMBER) -> 'NUMBER' ; -mnemonic(?DIFFICULTY) -> 'DIFFICULTY' ; -mnemonic(?GASLIMIT) -> 'GASLIMIT' ; -mnemonic(?GAS) -> 'GAS' ; -mnemonic(?LOG0) -> 'LOG0' ; -mnemonic(?LOG1) -> 'LOG1' ; -mnemonic(?LOG2) -> 'LOG2' ; -mnemonic(?LOG3) -> 'LOG3' ; -mnemonic(?LOG4) -> 'LOG4' ; -mnemonic(?ABORT) -> 'ABORT' ; -mnemonic(?EXIT) -> 'EXIT' ; -mnemonic(?DEACTIVATE) -> 'DEACTIVATE' ; -mnemonic(?INC) -> 'INC' ; -mnemonic(?DEC) -> 'DEC' ; -mnemonic(?INCA) -> 'INCA' ; -mnemonic(?DECA) -> 'DECA' ; -mnemonic(?INT_TO_STR) -> 'INT_TO_STR' ; -mnemonic(?SPEND) -> 'SPEND' ; -mnemonic(?ORACLE_REGISTER) -> 'ORACLE_REGISTER' ; -mnemonic(?ORACLE_QUERY) -> 'ORACLE_QUERY' ; -mnemonic(?ORACLE_RESPOND) -> 'ORACLE_RESPOND' ; -mnemonic(?ORACLE_EXTEND) -> 'ORACLE_EXTEND' ; -mnemonic(?ORACLE_GET_ANSWER) -> 'ORACLE_GET_ANSWER' ; -mnemonic(?ORACLE_GET_QUESTION) -> 'ORACLE_GET_QUESTION' ; -mnemonic(?ORACLE_QUERY_FEE) -> 'ORACLE_QUERY_FEE' ; -mnemonic(?AENS_RESOLVE) -> 'AENS_RESOLVE' ; -mnemonic(?AENS_PRECLAIM) -> 'AENS_PRECLAIM' ; -mnemonic(?AENS_CLAIM) -> 'AENS_CLAIM' ; -mnemonic(?AENS_UPDATE) -> 'AENS_UPDATE' ; -mnemonic(?AENS_TRANSFER) -> 'AENS_TRANSFER' ; -mnemonic(?AENS_REVOKE) -> 'AENS_REVOKE' ; -mnemonic(?ECVERIFY) -> 'ECVERIFY' ; -mnemonic(?SHA3) -> 'SHA3' ; -mnemonic(?SHA256) -> 'SHA256' ; -mnemonic(?BLAKE2B) -> 'BLAKE2B' ; -mnemonic(?RETURNR) -> 'RETURNR' ; -mnemonic(?MAP_LOOKUPD) -> 'MAP_LOOKUPD' ; -mnemonic(?SWITCH_V2) -> 'SWITCH_V2' ; -mnemonic(?SWITCH_V3) -> 'SWITCH_V3' ; -mnemonic(?SWITCH_VN) -> 'SWITCH_VN' ; -mnemonic(?BITS_ALL_N) -> 'BITS_ALL_N' ; -mnemonic(?FUNCTION) -> 'FUNCTION' ; -mnemonic(?EXTEND) -> 'EXTEND'. - - -m_to_op('NOP') -> ?NOP ; -m_to_op('RETURN') -> ?RETURN ; -m_to_op('CALL') -> ?CALL ; -m_to_op('CALL_R') -> ?CALL_R ; -m_to_op('CALL_T') -> ?CALL_T ; -m_to_op('CALL_TR') -> ?CALL_TR ; -m_to_op('JUMP') -> ?JUMP ; -m_to_op('JUMPIF') -> ?JUMPIF ; -m_to_op('PUSH') -> ?PUSH ; -m_to_op('DUP') -> ?DUP ; -m_to_op('DUPA') -> ?DUPA ; -m_to_op('POP') -> ?POP ; -m_to_op('STORE') -> ?STORE ; -m_to_op('ADD') -> ?ADD ; -m_to_op('MUL') -> ?MUL ; -m_to_op('SUB') -> ?SUB ; -m_to_op('DIV') -> ?DIV ; -m_to_op('MOD') -> ?MOD ; -m_to_op('POW') -> ?POW ; -m_to_op('LT') -> ?LT ; -m_to_op('GT') -> ?GT ; -m_to_op('EQ') -> ?EQ ; -m_to_op('ELT') -> ?ELT ; -m_to_op('EGT') -> ?EGT ; -m_to_op('NEQ') -> ?NEQ ; -m_to_op('AND') -> ?AND ; -m_to_op('OR') -> ?OR ; -m_to_op('NOT') -> ?NOT ; -m_to_op('TUPLE') -> ?TUPLE ; -m_to_op('ELEMENT') -> ?ELEMENT ; -m_to_op('MAP_EMPTY') -> ?MAP_EMPTY ; -m_to_op('MAP_LOOKUP') -> ?MAP_LOOKUP ; -m_to_op('MAP_UPDATE') -> ?MAP_UPDATE ; -m_to_op('MAP_DELETE') -> ?MAP_DELETE ; -m_to_op('MAP_MEMBER') -> ?MAP_MEMBER ; -m_to_op('MAP_FROM_LIST') -> ?MAP_FROM_LIST ; -m_to_op('NIL') -> ?NIL ; -m_to_op('IS_NIL') -> ?IS_NIL ; -m_to_op('CONS') -> ?CONS ; -m_to_op('HD') -> ?HD ; -m_to_op('TL') -> ?TL ; -m_to_op('LENGTH') -> ?LENGTH ; -m_to_op('STR_EQ') -> ?STR_EQ ; -m_to_op('STR_JOIN') -> ?STR_JOIN ; -m_to_op('ADDR_TO_STR') -> ?ADDR_TO_STR ; -m_to_op('STR_REVERSE') -> ?STR_REVERSE ; -m_to_op('INT_TO_ADDR') -> ?INT_TO_ADDR ; -m_to_op('VARIANT') -> ?VARIANT ; -m_to_op('VARIANT_TEST') -> ?VARIANT_TEST ; -m_to_op('VARIANT_ELEMENT') -> ?VARIANT_ELEMENT ; -m_to_op('BITS_NONEA') -> ?BITS_NONEA ; -m_to_op('BITS_ALL') -> ?BITS_ALL ; -m_to_op('BITS_ALLA') -> ?BITS_ALLA ; -m_to_op('BITS_SET') -> ?BITS_SET ; -m_to_op('BITS_CLEAR') -> ?BITS_CLEAR ; -m_to_op('BITS_TEST') -> ?BITS_TEST ; -m_to_op('BITS_SUM') -> ?BITS_SUM ; -m_to_op('BITS_OR') -> ?BITS_OR ; -m_to_op('BITS_AND') -> ?BITS_AND ; -m_to_op('BITS_DIFF') -> ?BITS_DIFF ; -m_to_op('ADDRESS') -> ?ADDRESS ; -m_to_op('BALANCE') -> ?BALANCE ; -m_to_op('ORIGIN') -> ?ORIGIN ; -m_to_op('CALLER') -> ?CALLER ; -m_to_op('GASPRICE') -> ?GASPRICE ; -m_to_op('BLOCKHASH') -> ?BLOCKHASH ; -m_to_op('BENEFICIARY') -> ?BENEFICIARY ; -m_to_op('TIMESTAMP') -> ?TIMESTAMP ; -m_to_op('NUMBER') -> ?NUMBER ; -m_to_op('DIFFICULTY') -> ?DIFFICULTY ; -m_to_op('GASLIMIT') -> ?GASLIMIT ; -m_to_op('GAS') -> ?GAS ; -m_to_op('LOG0') -> ?LOG0 ; -m_to_op('LOG1') -> ?LOG1 ; -m_to_op('LOG2') -> ?LOG2 ; -m_to_op('LOG3') -> ?LOG3 ; -m_to_op('LOG4') -> ?LOG4 ; -m_to_op('ABORT') -> ?ABORT ; -m_to_op('EXIT') -> ?EXIT ; -m_to_op('DEACTIVATE') -> ?DEACTIVATE ; -m_to_op('INC') -> ?INC ; -m_to_op('DEC') -> ?DEC ; -m_to_op('INCA') -> ?INCA ; -m_to_op('DECA') -> ?DECA ; -m_to_op('INT_TO_STR') -> ?INT_TO_STR ; -m_to_op('SPEND') -> ?SPEND ; -m_to_op('ORACLE_REGISTER') -> ?ORACLE_REGISTER ; -m_to_op('ORACLE_QUERY') -> ?ORACLE_QUERY ; -m_to_op('ORACLE_RESPOND') -> ?ORACLE_RESPOND ; -m_to_op('ORACLE_EXTEND') -> ?ORACLE_EXTEND ; -m_to_op('ORACLE_GET_ANSWER') -> ?ORACLE_GET_ANSWER ; -m_to_op('ORACLE_GET_QUESTION') -> ?ORACLE_GET_QUESTION ; -m_to_op('ORACLE_QUERY_FEE') -> ?ORACLE_QUERY_FEE ; -m_to_op('AENS_RESOLVE') -> ?AENS_RESOLVE ; -m_to_op('AENS_PRECLAIM') -> ?AENS_PRECLAIM ; -m_to_op('AENS_CLAIM') -> ?AENS_CLAIM ; -m_to_op('AENS_UPDATE') -> ?AENS_UPDATE ; -m_to_op('AENS_TRANSFER') -> ?AENS_TRANSFER ; -m_to_op('AENS_REVOKE') -> ?AENS_REVOKE ; -m_to_op('ECVERIFY') -> ?ECVERIFY ; -m_to_op('SHA3') -> ?SHA3 ; -m_to_op('SHA256') -> ?SHA256 ; -m_to_op('BLAKE2B') -> ?BLAKE2B ; -m_to_op('RETURNR') -> ?RETURNR ; -m_to_op('MAP_LOOKUPD') -> ?MAP_LOOKUPD ; -m_to_op('SWITCH_V2') -> ?SWITCH_V2 ; -m_to_op('SWITCH_V3') -> ?SWITCH_V3 ; -m_to_op('SWITCH_VN') -> ?SWITCH_VN ; -m_to_op('FUNCTION') -> ?FUNCTION ; -m_to_op('EXTEND') -> ?EXTEND. - - - -args(?NOP) -> 0; -args(?RETURN) -> 0; -args(?INCA) -> 0; -args(?DECA) -> 0; -args(?DUPA) -> 0; -args(?BITS_NONEA) -> 0; -args(?BITS_ALLA) -> 0; - -args(?INC) -> 1; -args(?DEC) -> 1; -args(?RETURNR) -> 1; -args(?PUSH) -> 1; -args(?JUMP) -> 1; -args(?CALL) -> 1; -args(?CALL_T) -> 1; -args(?TUPLE) -> 1; -args(?MAP_EMPTY) -> 1; -args(?DUP) -> 1; -args(?POP) -> 1; -args(?NIL) -> 1; -args(?BITS_NONE) -> 1; -args(?BITS_ALL) -> 1; -args(?ADDRESS) -> 1; -args(?BALANCE) -> 1; -args(?ORIGIN) -> 1; -args(?CALLER) -> 1; -args(?GASPRICE) -> 1; -args(?BLOCKHASH) -> 1; -args(?BENEFICIARY) -> 1; -args(?TIMESTAMP) -> 1; -args(?NUMBER) -> 1; -args(?DIFFICULTY)-> 1; -args(?GASLIMIT) -> 1; -args(?GAS) -> 1; -args(?ABORT) -> 1; -args(?EXIT) -> 1; - -args(?JUMPIF) -> 2; -args(?CALL_R) -> 2; -args(?CALL_TR) -> 2; -args(?HD) -> 2; -args(?TL) -> 2; -args(?NOT) -> 2; -args(?STORE) -> 2; -args(?LENGTH) -> 2; -args(?IS_NIL) -> 2; -args(?BITS_SUM) -> 2; -args(?BITS_ALL_N) -> 2; -args(?ADDR_TO_STR) -> 2; -args(?STR_REVERSE) -> 2; -args(?INT_TO_ADDR) -> 2; -args(?MAP_FROM_LIST) -> 2; - - -args(?ADD) -> 3; -args(?SUB) -> 3; -args(?MUL) -> 3; -args(?DIV) -> 3; -args(?MOD) -> 3; -args(?POW) -> 3; -args(?AND) -> 3; -args(?OR) -> 3; -args(?LT) -> 3; -args(?GT) -> 3; -args(?EGT) -> 3; -args(?ELT) -> 3; -args(?EQ) -> 3; -args(?NEQ) -> 3; -args(?CONS) -> 3; -args(?STR_EQ) -> 3; -args(?STR_JOIN) -> 3; -args(?MAP_MEMBER) -> 3; -args(?MAP_LOOKUP) -> 3; -args(?MAP_DELETE) -> 3; -args(?BITS_OR) -> 3; -args(?BITS_AND) -> 3; -args(?BITS_SET) -> 3; -args(?BITS_DIFF) -> 3; -args(?BITS_TEST) -> 3; -args(?BITS_CLEAR) -> 3; -args(?VARIANT_TEST) -> 3; -args(?VARIANT_ELEMENT) -> 3; -args(?INT_TO_STR) -> 3; -args(?SWITCH_V2) -> 3; - -args(?SWITCH_V3) -> 4; -args(?ELEMENT) -> 4; -args(?VARIANT) -> 4; -args(?MAP_UPDATE) -> 4; -args(?MAP_LOOKUPD) -> 4; - -args(?SWITCH_VN) -> 2; - -args(_) -> 0. %% TODO do not allow this - -end_bb(?RETURN) -> true; -end_bb(?RETURNR) -> true; -end_bb(?JUMP) -> true; -end_bb(?JUMPIF) -> true; -end_bb(?CALL) -> true; -end_bb(?CALL_T) -> true; -end_bb(?CALL_R) -> true; -end_bb(?CALL_TR) -> true; -end_bb(?SWITCH_V2) -> true; -end_bb(?SWITCH_V3) -> true; -end_bb(?SWITCH_VN) -> true; -end_bb(?ABORT) -> true; -end_bb(?EXIT) -> true; - -end_bb(_) -> false. -- 2.30.2 From fccc570beeb5cf37a234659fa1810838102070b4 Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Thu, 28 Feb 2019 19:18:25 +0100 Subject: [PATCH 23/29] Pt 164259596 generate format op (#10) * Generate code for fate ops from spec. * Generate the code from the makefile. Remove generated files. * Test targets and cleanup. * Spell eunit the right way. * Use test target for ci. * Renumber opcodes. Add primops. * Generate tokens in scanner from definitions. * Rename NUMBER op to GENERATION and add MICROBLOCK instruction. * Since Tag < Size, Size cannot be zero * unit is printed `()` * Formatting differently * Add eqc profile * Generate code for fate ops from spec. * Generate the code from the makefile. Remove generated files. * Test targets and cleanup. * Generate op pretty printer. * Removed unused function. * Polish Makefile file references (#11) * Parse all types of values except variants. --- .gitignore | 1 + Makefile | 21 +-- ebin/.gitkeep | 0 rebar.config | 7 +- src/aeb_fate_asm.erl | 249 ++++++++++++--------------------- src/aeb_fate_asm_scan.template | 26 +++- src/aeb_fate_data.erl | 31 ++-- src/aeb_fate_encoding.erl | 8 +- src/aeb_fate_generate_ops.erl | 101 ++++++++++++- test/aeb_fate_asm_test.erl | 3 + test/asm_code/immediates.fate | 68 +++++++++ test/asm_code/mapofmap.fate | 7 + 12 files changed, 317 insertions(+), 205 deletions(-) create mode 100644 ebin/.gitkeep create mode 100644 test/asm_code/immediates.fate create mode 100644 test/asm_code/mapofmap.fate diff --git a/.gitignore b/.gitignore index cccd365..1887839 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ aefateasm include/aeb_fate_opcodes.hrl src/aeb_fate_code.erl src/aeb_fate_opcodes.erl +src/aeb_fate_pp.erl diff --git a/Makefile b/Makefile index 071e04c..916426e 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,10 @@ - - +GENERATED_SRC = src/aeb_fate_opcodes.erl src/aeb_fate_code.erl include/aeb_fate_opcodes.hrl src/aeb_fate_asm_scan.xrl src/aeb_fate_pp.erl +GENERATOR_DEPS = ebin/aeb_fate_generate_ops.beam src/aeb_fate_asm_scan.template REBAR ?= rebar3 all: local -local: src/aeb_fate_opcodes.erl src/aeb_fate_code.erl include/aeb_fate_opcodes.hrl src/aeb_fate_asm_scan.xrl +local: $(GENERATED_SRC) @$(REBAR) as local release console: local @@ -12,15 +12,12 @@ console: local clean: @$(REBAR) clean - rm -f src/aeb_fate_opcodes.erl - rm -f src/aeb_fate_code.erl - rm -f include/aeb_fate_opcodes.hrl + rm -f $(GENERATED_SRC) + rm -f ebin/* dialyzer: local @$(REBAR) as local dialyzer - - distclean: clean @rm -rf _build/ @@ -30,12 +27,8 @@ eunit: local test: local @$(REBAR) as local eunit - -ebin/aeb_fate_generate_ops.beam: src/aeb_fate_generate_ops.erl ebin +ebin/%.beam: src/%.erl erlc -o $(dir $@) $< -src/aeb_fate_opcodes.erl src/aeb_fate_code.erl include/aeb_fate_opcodes.hrl src/aeb_fate_asm_scan.xrl: ebin/aeb_fate_generate_ops.beam +$(GENERATED_SRC): $(GENERATOR_DEPS) erl -pa ebin/ -noshell -s aeb_fate_generate_ops gen_and_halt src/ include/ - -ebin: - mkdir ebin diff --git a/ebin/.gitkeep b/ebin/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/rebar.config b/rebar.config index 08234fd..2b014b7 100644 --- a/rebar.config +++ b/rebar.config @@ -45,5 +45,8 @@ "robocopy \"%REBAR_BUILD_DIR%/bin/\" ./ aefateasm* " "/njs /njh /nfl /ndl & exit /b 0"} % silence things ]} - ]}]}. - + ]}, + {eqc, [{erl_opts, [{parse_transform, eqc_cover}]}, + {extra_src_dirs, ["quickcheck"]} %% May not be called eqc! + ]} + ]}. diff --git a/src/aeb_fate_asm.erl b/src/aeb_fate_asm.erl index 5e320ef..5606894 100644 --- a/src/aeb_fate_asm.erl +++ b/src/aeb_fate_asm.erl @@ -2,6 +2,21 @@ %%% @copyright (C) 2019, Aeternity Anstalt %%% @doc Assembler for Fate machine code. %%% +%%% Fate code exists in 3 formats: +%%% +%%% 1. Fate byte code. This format is under consensus. +%%% 2. Fate assembler. This is a text represenation of fate code. +%%% This is not under consensus and other +%%% implemenation and toolchains could have +%%% their own format. +%%% 3. Internal. This is an Erlang representation of fate code +%%% Used by this particular engin implementation. +%%% +%%% This library handles all tree representations. +%%% The byte code format is described in a separate document. +%%% The internal format is described in a separate document. +%%% The text representation is described here: +%%% %%% Assembler code can be read from a file. %%% The assembler has the following format %%% Comments start with 2 semicolons and runs till end of line @@ -33,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 @@ -157,14 +173,6 @@ format_arg_types([T|Ts]) -> , ", " , format_arg_types(Ts)]. -format_arg({immediate, I}) -> - aeb_fate_data:format(I); -format_arg({arg, N}) -> io_lib:format("arg~p", [N]); -format_arg({var, N}) -> io_lib:format("var~p", [N]); -format_arg({stack, 0}) -> "a"; -format_arg({stack, N}) -> io_lib:format("a~p", [N]). - - format_type(T) -> %% TODO: Limit to ok types. io_lib:format("~p", [T]). @@ -180,157 +188,10 @@ format_code([], _) -> ""; format_code([Op|Rest], Symbols) -> [" ", - format_op(Op, Symbols), + aeb_fate_pp:format_op(Op, Symbols), "\n", format_code(Rest, Symbols)]. -format_op('RETURN', _) -> "RETURN"; -format_op({'RETURNR', Arg}, _) -> ["RETURNR ", format_arg(Arg)]; -format_op({'CALL', {immediate, Function}}, Symbols) -> - ["CALL ", lookup(Function, Symbols)]; -format_op({'CALL_T', {immediate, Function}}, Symbols) -> - ["CALL_T ", lookup(Function, Symbols)]; -format_op({'CALL_R', {immediate, Contract}, {immediate, Function}}, Symbols) -> - ["CALL_R ", lookup(Contract, Symbols), "." , lookup(Function, Symbols)]; -format_op({'CALL_R', Contract, {immediate, Function}}, Symbols) -> - ["CALL_R ", format_arg(Contract), "." , lookup(Function, Symbols)]; -format_op({'CALL_TR', {immediate, Contract}, {immediate, Function}}, Symbols) -> - ["CALL_TR ", lookup(Contract, Symbols), "." , lookup(Function, Symbols)]; -format_op({'CALL_TR', Contract, {immediate, Function}}, Symbols) -> - ["CALL_TR ", format_arg(Contract), "." , lookup(Function, Symbols)]; -format_op({'JUMP', {immediate, BB}}, _) -> - ["JUMP ", io_lib:format("~p", [BB])]; -format_op({'JUMPIF', Arg, {immediate, BB}}, _) -> - ["JUMPIF ", format_arg(Arg), " ", io_lib:format("~p", [BB])]; -format_op({'SWITCH_V2', Variant, {immediate, BB1}, {immediate, BB2}}, _) -> - ["SWITCH_V2 ", format_arg(Variant), " ", BB1, " ", BB2]; -format_op({'SWITCH_V3', Variant, {immediate, BB1}, {immediate, BB2}, {immediate, BB3}}, _) -> - ["SWITCH_V2 ", format_arg(Variant), " ", BB1, " ", BB2, " ", BB3]; -format_op({'SWITCH_VN', Variant, BBs}, _) -> - ["SWITCH_VN ", format_arg(Variant), [[" ", BB] || {immedate, BB} <- BBs]]; -format_op({'PUSH', Arg0}, _) -> - ["PUSH ", format_arg(Arg0)]; -format_op('INCA', _) -> "INCA"; -format_op({'INC', Name}, _) -> ["INC ", format_arg(Name)]; -format_op({'DEC', Name}, _) -> ["DEC ", format_arg(Name)]; -format_op('DECA', _) -> "DECA"; -format_op({'ADD', Dest, Left, Right}, _) -> - ["ADD ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'SUB', Dest, Left, Right}, _) -> - ["SUB ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'MUL', Dest, Left, Right}, _) -> - ["MUL ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'DIV', Dest, Left, Right}, _) -> - ["DIV ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'MOD', Dest, Left, Right}, _) -> - ["MOD ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'POW', Dest, Left, Right}, _) -> - ["POW ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'LT', Dest, Left, Right}, _) -> - ["LT ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'GT', Dest, Left, Right}, _) -> - ["GT ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'ELT', Dest, Left, Right}, _) -> - ["ELT ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'EGT', Dest, Left, Right}, _) -> - ["EGT ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'EQ', Dest, Left, Right}, _) -> - ["EQ ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'NEQ', Dest, Left, Right}, _) -> - ["NEQ ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'AND', Dest, Left, Right}, _) -> - ["AND ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'OR', Dest, Left, Right}, _) -> - ["OR ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'NOT', Dest, Name}, _) -> - ["NOT ", format_arg(Dest), " ", format_arg(Name)]; -format_op({'TUPLE', {immediate, Size}}, _) -> - ["TUPLE ", io_lib:format("~p", [Size])]; -format_op({'ELEMENT', Type, Dest, Which, Tuple}, _) -> - [ "ELEMENT " - , io_lib:format("~p ", [Type]) - , format_arg(Dest), " " - , format_arg(Which), " " - , format_arg(Tuple)]; -format_op({'MAP_EMPTY', Dest}, _) -> - ["MAP_EMPTY ", format_arg(Dest)]; -format_op({'MAP_LOOKUP', Dest, Map, Key}, _) -> - ["MAP_LOOKUP ", format_arg(Dest), " " - , format_arg(Map), " ", format_arg(Key)]; -format_op({'MAP_DELETE', Dest, Map, Key}, _) -> - ["MAP_DELETE ", format_arg(Dest), " " - , format_arg(Map), " ", format_arg(Key)]; -format_op({'MAP_LOOKUPD', Dest, Map, Key, Default}, _) -> - ["MAP_LOOKUPD ", format_arg(Dest), " " - , format_arg(Map), " ", format_arg(Key), " ", format_arg(Default)]; -format_op({'MAP_UPDATE', Dest, Map, Key, Value}, _) -> - ["MAP_UPDATE ", format_arg(Dest), " " - , format_arg(Map), " ", format_arg(Key), " ", format_arg(Value)]; -format_op({'MAP_MEMBER', Dest, Map, Key}, _) -> - ["MAP_MEMBER ", format_arg(Dest), " " - , format_arg(Map), " ", format_arg(Key)]; -format_op({'MAP_FROM_LIST', Dest, List}, _) -> - ["MAP_FROM_LIST ", format_arg(Dest), " ", format_arg(List)]; -format_op({'NIL', Dest}, _) -> - ["NIL ", format_arg(Dest)]; -format_op({'IS_NIL', Dest, List}, _) -> - ["IS_NIL ", format_arg(Dest), " ", format_arg(List)]; -format_op({'CONS', Dest, Hd, Tl}, _) -> - ["CONS ", format_arg(Dest), " ", format_arg(Hd), " ", format_arg(Tl)]; -format_op({'HD', Dest, List}, _) -> - ["HD ", format_arg(Dest), " ", format_arg(List)]; -format_op({'TL', Dest, List}, _) -> - ["TL ", format_arg(Dest), " ", format_arg(List)]; -format_op({'LENGTH', Dest, List}, _) -> - ["LENGTH ", format_arg(Dest), " ", format_arg(List)]; -format_op({'STR_EQ', Dest, Str1, Str2}, _) -> - ["STR_EQ ", format_arg(Dest), " ", format_arg(Str1), format_arg(Str2)]; -format_op({'STR_JOIN', Dest, Str1, Str2}, _) -> - ["STR_JOIN ", format_arg(Dest), " ", format_arg(Str1), format_arg(Str2)]; -format_op({'INT_TO_STR', Dest, Str}, _) -> - ["INT_TO_STR ", format_arg(Dest), " ", format_arg(Str)]; -format_op({'ADDR_TO_STR', Dest, Str}, _) -> - ["ADDR_TO_STR ", format_arg(Dest), " ", format_arg(Str)]; -format_op({'STR_REVERSE', Dest, Str}, _) -> - ["STR_REVERSE ", format_arg(Dest), " ", format_arg(Str)]; -format_op({'INT_TO_ADDR', Dest, Str}, _) -> - ["INT_TO_ADDR ", format_arg(Dest), " ", format_arg(Str)]; -format_op({'VARIANT_TEST', Dest, Variant, Tag}, _) -> - ["VARIANT_TEST ", format_arg(Dest), " ", format_arg(Variant), " ", format_arg(Tag)]; -format_op({'VARIANT_ELEMENT', Dest, Variant, Index}, _) -> - ["VARIANT_ELEMENT ", format_arg(Dest), " ", format_arg(Variant), " ", format_arg(Index)]; -format_op({'VARIANT', Dest, SizeA, TagA, ElementsA}, _) -> - ["VARIANT ", format_arg(Dest), " ", format_arg(SizeA), " " - , format_arg(TagA), " ", format_arg(ElementsA)]; -format_op('BITS_NONEA', _) -> "BITS_NONEA "; -format_op({'BITS_NONE', To}, _) -> ["BITS_NONE ", format_arg(To)]; -format_op('BITS_ALLA', _) -> "BITS_ALLA"; -format_op({'BITS_ALL', To}, _) -> ["BITS_ALL ", format_arg(To)]; -format_op({'BITS_ALL_N', To, N}, _) -> - ["BITS_ALL_N ", format_arg(To), " ", format_arg(N)]; -format_op({'BITS_SET', To, Bits, Bit}, _) -> - ["BITS_SET ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)]; -format_op({'BITS_CLEAR', To, Bits, Bit}, _) -> - ["BITS_CLEAR ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)]; -format_op({'BITS_TEST', To, Bits, Bit}, _) -> - ["BITS_TEST ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)]; -format_op({'BITS_SUM', To, Bits}, _) -> - ["BITS_SUM ", format_arg(To), " ", format_arg(Bits)]; -format_op({'BITS_OR', To, Bits, Bit}, _) -> - ["BITS_OR ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)]; -format_op({'BITS_AND', To, Bits, Bit}, _) -> - ["BITS_AND ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)]; -format_op({'BITS_DIFF', To, Bits, Bit}, _) -> - ["BITS_DIFF ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)]; -format_op('DUPA', _) -> "DUPA"; -format_op({'DUP', {immediate, N}}, _) -> - ["DUP ", io_lib:format("~p", [N])]; -format_op({'POP', Dest}, _) -> - ["POP ", format_arg(Dest)]; -format_op({'STORE', Var, What}, _) -> - ["STORE ", format_arg(Var), " ", format_arg(What)]; -format_op('NOP', _) -> "NOP". - read_file(Filename) -> {ok, File} = file:read_file(Filename), @@ -709,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}; @@ -756,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); @@ -772,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), @@ -829,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 f0a6898..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()}. @@ -133,30 +133,25 @@ 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) -> "unit"; +format(?FATE_UNIT) -> "()"; format(?FATE_TUPLE(T)) -> - "{ " ++ [format(E) ++ " " || E <- erlang:tuple_to_list(T)] ++ "}"; + ["( ", lists:join(", ", [ format(E) || E <- erlang:tuple_to_list(T)]), " )"]; format(S) when ?IS_FATE_STRING(S) -> [S]; format(?FATE_VARIANT(Size, Tag, T)) -> - "( " ++ integer_to_list(Size) ++ ", " - ++ integer_to_list(Tag) ++ ", " - ++ [format(E) ++ " " || E <- erlang:tuple_to_list(T)] - ++ " )"; + ["(| ", + lists:join("| ", [integer_to_list(Size), integer_to_list(Tag) | + [format(E) || E <- erlang:tuple_to_list(T)]]), + " |)"]; format(M) when ?IS_FATE_MAP(M) -> - "#{ " - ++ format_kvs(maps:to_list(?FATE_MAP_VALUE(M))) - ++" }"; -format(?FATE_ADDRESS(Address)) -> address_to_base58(Address); + ["{ ", format_kvs(maps:to_list(?FATE_MAP_VALUE(M))), " }"]; +format(?FATE_ADDRESS(Address)) -> ["#", address_to_base58(Address)]; format(V) -> exit({not_a_fate_type, V}). -format_list([]) -> " ]"; -format_list([E]) -> format(E) ++ " ]"; -format_list([H|T]) -> format(H) ++ ", " ++ format_list(T). +format_list(List) -> + ["[ ", lists:join(", ", [format(E) || E <- List]), " ]"]. -format_kvs([]) -> ""; -format_kvs([{K,V}]) -> "( " ++ format(K) ++ " => " ++ format(V) ++ " )"; -format_kvs([{K,V} | Rest]) -> - "( " ++ format(K) ++ " => " ++ format(V) ++ " ), " ++ format_kvs(Rest). +format_kvs(List) -> + lists:join(", ", [ [format(K), " => ", format(V)] || {K, V} <- List]). %% -- Local base 58 library diff --git a/src/aeb_fate_encoding.erl b/src/aeb_fate_encoding.erl index 1fcbd9e..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 @@ -136,10 +136,8 @@ 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(Size, Tag, Values)) when 0 < Size, Size < 256, + 0 =< Tag, Tag < Size -> <>. diff --git a/src/aeb_fate_generate_ops.erl b/src/aeb_fate_generate_ops.erl index 3eb7def..5cfe13a 100644 --- a/src/aeb_fate_generate_ops.erl +++ b/src/aeb_fate_generate_ops.erl @@ -18,7 +18,8 @@ generate(Src, Include) -> generate_header_file(HrlFile, Ops), generate_opcodes_ops(aeb_fate_opcodes, HrlFile, Src, Ops), generate_code_ops(aeb_fate_code, Src, Ops), - generate_scanner("aeb_fate_asm_scan.template", "aeb_fate_asm_scan.xrl", Src, Ops). + generate_scanner("aeb_fate_asm_scan.template", "aeb_fate_asm_scan.xrl", Src, Ops), + gen_asm_pp(aeb_fate_pp, Src, Ops). %% TODO: Some real gas numbers... ops_defs() -> @@ -398,5 +399,103 @@ insert_tokens_in_template(<<"###REPLACEWITHNOTE###", Rest/binary >>, Tokens) -> insert_tokens_in_template(<>, Tokens) -> [B|insert_tokens_in_template(Rest, Tokens)]. +gen_asm_pp(Module, Path, Ops) -> + Filename = filename:join(Path, atom_to_list(Module)) ++ ".erl", + {ok, File} = file:open(Filename, [write]), + Formats = lists:flatten([gen_format(Op)++"\n" || Op <- Ops]), + io:format(File, "~s", [prelude(" Provide pretty printing functuions for " + "Fate instructions.\n")]), + io:format(File, "-module(~w).\n\n", [Module]), + io:format(File, + "-export([format_op/2]).\n\n" + "format_arg(t, T) ->\n" + " io_lib:format(\"~~p \", [T]);\n" + "format_arg(li, List) ->\n" + " [[\" \", E] || {immedate, E} <- List];\n" + "format_arg(_, {immediate, I}) ->\n" + " aeb_fate_data:format(I);\n" + "format_arg(a, {arg, N}) -> io_lib:format(\"arg~~p\", [N]);\n" + "format_arg(a, {var, N}) -> io_lib:format(\"var~~p\", [N]);\n" + "format_arg(a, {stack, 0}) -> \"a\";\n" + "format_arg(a, {stack, N}) -> io_lib:format(\"a~~p\", [N]).\n\n" + "lookup(Name, Symbols) ->\n" + " maps:get(Name, Symbols, Name).\n\n" + "~s" + , [Formats]), + io:format(File, "format_op(Op, _Symbols) -> io_lib:format(\";; Bad Op: ~~w\\n\", [Op]).\n", []), + file:close(File). + +gen_format(#{opname := Name}) when ('CALL' =:= Name) or (Name =:= 'CALL_T') -> + io_lib:format("format_op({~w, {immediate, Function}}, Symbols) ->\n" + "[\"~s \", lookup(Function, Symbols)];", + [Name, atom_to_list(Name)]); +gen_format(#{opname := Name}) when (Name =:= 'CALL_R') or (Name =:= 'CALL_TR') -> + io_lib:format("format_op({~w, {immediate, Contract}, {immediate, Function}}, Symbols) ->\n" + "[\"~s \", lookup(Contract, Symbols), \".\", lookup(Function, Symbols)];\n" + "format_op({~w, Contract, {immediate, Function}}, Symbols) ->\n" + "[\"~s \", format_arg(a, Contract), \".\", lookup(Function, Symbols)];", + [Name, atom_to_list(Name), Name, atom_to_list(Name)]); +gen_format(#{opname := Name, format := atomic}) -> + io_lib:format("format_op(~w, _) -> [\"~s\"];", [Name, atom_to_list(Name)]); +gen_format(#{opname := Name, format := Args}) -> + NameAsString = atom_to_list(Name), + case Args of + [T0] -> + io_lib:format( + "format_op({~w, Arg0}, _) ->\n" + " [\"~s \", format_arg(~w, Arg0)];", + [Name, NameAsString, T0]); + [T0, T1] -> + io_lib:format( + "format_op({~w, Arg0, Arg1}, _) ->\n" + " [\"~s \", format_arg(~w, Arg0), " + "\" \", format_arg(~w, Arg1)];", + [Name, NameAsString, T0, T1]); + [T0, T1, T2] -> + io_lib:format( + "format_op({~w, Arg0, Arg1, Arg2}, _) ->\n" + " [\"~s \", format_arg(~w, Arg0), " + "\" \", format_arg(~w, Arg1)," + "\" \", format_arg(~w, Arg2)];", + [Name, NameAsString, T0, T1, T2]); + [T0, T1, T2, T3] -> + io_lib:format( + "format_op({~w, Arg0, Arg1, Arg2, Arg3}, _) ->\n" + " [\"~s \", format_arg(~w, Arg0), " + "\" \", format_arg(~w, Arg1)," + "\" \", format_arg(~w, Arg2)," + "\" \", format_arg(~w, Arg3)];", + [Name, NameAsString, T0, T1, T2, T3]); + [T0, T1, T2, T3, T4] -> + io_lib:format( + "format_op({~w, Arg0, Arg1, Arg2, Arg3, Arg4}, _) ->\n" + " [\"~s \", format_arg(~w, Arg0), " + "\" \", format_arg(~w, Arg1)," + "\" \", format_arg(~w, Arg2)," + "\" \", format_arg(~w, Arg3)," + "\" \", format_arg(~w, Arg4)];", + [Name, NameAsString, T0, T1, T2, T3, T4]); + [T0, T1, T2, T3, T4, T5] -> + io_lib:format( + "format_op({~w, Arg0, Arg1, Arg2, Arg3, Arg4, Arg5}, _) ->\n" + " [\"~s \", format_arg(~w, Arg0), " + "\" \", format_arg(~w, Arg1)," + "\" \", format_arg(~w, Arg2)," + "\" \", format_arg(~w, Arg3)," + "\" \", format_arg(~w, Arg4)," + "\" \", format_arg(~w, Arg5)];", + [Name, NameAsString, T0, T1, T2, T3, T4, T5]); + [T0, T1, T2, T3, T4, T5, T6] -> + io_lib:format( + "format_op({~w, Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6}, _) ->\n" + " [\"~s \", format_arg(~w, Arg0), " + "\" \", format_arg(~w, Arg1)," + "\" \", format_arg(~w, Arg2)," + "\" \", format_arg(~w, Arg3)," + "\" \", format_arg(~w, Arg4)," + "\" \", format_arg(~w, Arg5)," + "\" \", format_arg(~w, Arg6)];", + [Name, NameAsString, T0, T1, T2, T3, T4, T5, T6]) + end. 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}} + + -- 2.30.2 From 20c8fbabc91e4f5c2e98a35c4f1f650e3c239f9e Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Fri, 1 Mar 2019 10:36:17 +0100 Subject: [PATCH 24/29] Fix bits formatting and parsing. (#12) --- src/aeb_fate_asm.erl | 3 ++- src/aeb_fate_data.erl | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/aeb_fate_asm.erl b/src/aeb_fate_asm.erl index 5606894..17f8f60 100644 --- a/src/aeb_fate_asm.erl +++ b/src/aeb_fate_asm.erl @@ -632,7 +632,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([{bits,_line, Bits}|Rest], Address, Env, Code, Opts) -> - to_bytecode(Rest, Address, Env, [{immediate, Bits}|Code], Opts); + to_bytecode(Rest, Address, Env, + [{immediate, aeb_fate_data:make_bits(Bits)}|Code], Opts); to_bytecode([{comment, Line, Comment}|Rest], Address, Env, Code, Opts) -> Env2 = insert_annotation(comment, Line, Comment, Env), diff --git a/src/aeb_fate_data.erl b/src/aeb_fate_data.erl index 3037d64..5ddab1e 100644 --- a/src/aeb_fate_data.erl +++ b/src/aeb_fate_data.erl @@ -137,6 +137,10 @@ 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(Size, Tag, T)) -> ["(| ", lists:join("| ", [integer_to_list(Size), integer_to_list(Tag) | @@ -147,6 +151,21 @@ format(M) when ?IS_FATE_MAP(M) -> format(?FATE_ADDRESS(Address)) -> ["#", address_to_base58(Address)]; format(V) -> exit({not_a_fate_type, V}). +format_bits(0, Acc) -> Acc; +format_bits(N, Acc) -> + case N band 1 of + 1 -> format_bits(N bsr 1, [$1|Acc]); + 0 -> format_bits(N bsr 1, [$0|Acc]) + end. + +format_nbits(0, Acc) -> Acc; +format_nbits(N, Acc) -> + case N band 1 of + 1 -> format_nbits(N bsr 1, [$0|Acc]); + 0 -> format_nbits(N bsr 1, [$1|Acc]) + end. + + format_list(List) -> ["[ ", lists:join(", ", [format(E) || E <- List]), " ]"]. -- 2.30.2 From 6f67da1292f81e6842bb74997a0d25694073b61c Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Fri, 1 Mar 2019 13:05:24 +0100 Subject: [PATCH 25/29] Pt 164325512 variant constants (#13) * Handle varaint constants and types. * Format Readme. * Format Readme step 2. * Format Readme step 3. * Format Readme step 4. * Format Readme step 5. * Update src/aeb_fate_asm.erl Co-Authored-By: happi * Update README.md Co-Authored-By: happi * Get rid of size from varaint type representation. --- README.md | 113 +++++++++++++++++++++++++++++++-- src/aeb_fate_asm.erl | 112 ++++++++++++++++++++++++-------- src/aeb_fate_asm_scan.template | 33 ++++++++-- src/aeb_fate_data.erl | 15 ++--- test/asm_code/immediates.fate | 5 ++ 5 files changed, 231 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 96be24b..1472bf1 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,110 @@ -aebytecode -===== +# aebytecode +An library and stand alone assembler for aeternity bytecode. -An OTP library +This version supports Aevm bytecode and Fate bytecode. -Build ------ +## Build + + $ make + +## Fate Code + +Fate code exists in 3 formats: + +1. Fate byte code. This format is under consensus. +2. Fate assembler. This is a text represenation of fate code. + This is not under consensus and other + implemenation and toolchains could have + their own format. +3. Internal. This is an Erlang representation of fate code + Used by this particular engin implementation. + +This library handles all tree representations. +The byte code format is described in a separate document. +The internal format is described in a separate document. +The text representation is described below. + +### Fate Assembler Code + +Assembler code can be read from a file. +The assembler has the following format: + +Comments start with 2 semicolons and runs till end of line +`;; This is a comment` + +Opcode mnemonics start with an upper case letter. +`DUP` + +Identifiers start with a lower case letter +`an_identifier` + +References to function arguments start with arg followed by an integer +`arg0` + +References to variables/registers start with var followed by an integer +`var0` + +References to stack postions is either a (for stack 0) +or start with stack followed by an integer +`stack1` +`a` + +Immediate values can be of 9 types: + +1. Integers as decimals: {Digits} or -{Digits} + `42` + `-2374683271468723648732648736498712634876147` + And integers as Hexadecimals:: 0x{Hexdigits} + `0x0deadbeef0` + +2. addresses, a base58 encoded string starting with # followed by a number of base58chars + `#nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv` + +3. Boolean true or false + `true` + `false` + +4. Strings "{Characters}" + `"Hello"` + +5. Map { Key => Value } + `{}` + `{ 1 => { "foo" => true, "bar" => false}` + +6. Lists [ Elements ] + `[]` + `[1, 2]` + +7. Bit field < Bits > or !< Bits > + `<000>` + `<1010 1010>` + `<>` + `!<>` + +8. Tuples ( Elements ) + `()` + `(1, "foo")` + +9. Variants: (| Size | Tag | ( Elements ) |) + `(| 42 | 12 | ( "foo", 12) |)` + +Where + +Digits: [0123456789] + +Hexdigits: [0123456789abcdef] + +base58char: [123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz] + +Characters: any printable ascii character 0..255 (except " no quoting yet) + +Key: any value except for a map + +Bits: 01 or space + +Elements: Nothing or Value , Elements + +Size: Digits (0 < Size < 256) + +Tag: Digits (0 =< Tag < Size) - $ rebar3 compile diff --git a/src/aeb_fate_asm.erl b/src/aeb_fate_asm.erl index 17f8f60..024b5ba 100644 --- a/src/aeb_fate_asm.erl +++ b/src/aeb_fate_asm.erl @@ -34,34 +34,47 @@ %%% stack1 %%% a %%% -%%% Immediates can be of 9 types: -%%% 1. Integers +%%% Immediate values can be of 9 types: +%%% 1a. Integers as decimals: {Digits} or -{Digits} %%% 42 %%% -2374683271468723648732648736498712634876147 -%%% 2. Hexadecimal integers starting with 0x +%%% 1b. Integers as Hexadecimals:: 0x{Hexdigits} %%% 0x0deadbeef0 -%%% 3. addresses, a 256-bit hash strings starting with # +%%% 2. addresses, a base58 encoded string starting with #{base58char} %%% followed by up to 64 hex chars %%% #00000deadbeef -%%% 4. Boolean +%%% 3. Boolean true or false %%% true %%% false -%%% 5. Strings +%%% 4. Strings "{Characters}" %%% "Hello" -%%% 6. Map +%%% 5. Map { Key => Value } %%% {} %%% { 1 => { "foo" => true, "bar" => false} -%%% 7. Lists +%%% 6. Lists [ Elements ] %%% [] %%% [1, 2] -%%% 8. Bit field +%%% 7. Bit field < Bits > or !< Bits > %%% <000> %%% <1010 1010> %%% <> %%% !<> -%%% 9. Tuples +%%% 8. Tuples ( Elements ) %%% () %%% (1, "foo") +%%% 9. Variants: (| Size | Tag | ( Elements ) |) +%%% (| 42 | 12 | ( "foo", 12) |) +%%% +%%% Where Digits: [0123456789] +%%% Hexdigits: [0123456789abcdef] +%%% base58char: [123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz] +%%% Characters any printable ascii character 0..255 (except " no quoting yet) +%%% Key: any value except for a map +%%% Bits: 01 or space +%%% Elements: Nothing or Value , Elements +%%% Size: Digits +%%% Tag: Digits +%%% %%% @end %%% Created : 21 Dec 2017 %%%------------------------------------------------------------------- @@ -100,7 +113,7 @@ parse_function_call([{id,_,Name}, {'(',_}| Rest]) -> to_args([{')', _}]) -> {[], []}; to_args(Tokens) -> - case to_data(Tokens) of + case parse_value(Tokens) of {Arg, [{',', _} | Rest]} -> {More, Rest2} = to_args(Rest), {[Arg|More], Rest2}; @@ -108,13 +121,6 @@ to_args(Tokens) -> {[Arg], Rest} end. -to_data([{int,_line, Int}|Rest]) -> - {Int, Rest}; -to_data([{boolean,_line, Bool}|Rest]) -> - {Bool, Rest}; -to_data([{hash,_line, Hash}|Rest]) -> - {Hash, Rest}. - pp(FateCode) -> Listing = to_asm(FateCode), io_lib:format("~ts~n",[Listing]). @@ -572,7 +578,16 @@ deserialize_type(<<6, Rest/binary>>) -> {V, Rest3} = deserialize_type(Rest2), {{map, K, V}, Rest3}; deserialize_type(<<7, Rest/binary>>) -> - {string, Rest}. + {string, Rest}; +deserialize_type(<<8, Size, Rest/binary>>) -> + {Variants, Rest2} = deserialize_variants(Size, Rest, []), + {{variant, Variants}, Rest2}. + +deserialize_variants(0, Rest, Variants) -> + {lists:reverse(Variants), Rest}; +deserialize_variants(N, Rest, Variants) -> + {T, Rest2} = deserialize_type(Rest), + deserialize_variants(N-1, Rest2, [T|Variants]). @@ -616,8 +631,14 @@ to_bytecode([{int,_line, Int}|Rest], Address, Env, Code, Opts) -> to_bytecode(Rest, Address, Env, [{immediate, Int}|Code], Opts); to_bytecode([{boolean,_line, Bool}|Rest], Address, Env, Code, Opts) -> to_bytecode(Rest, Address, Env, [{immediate, Bool}|Code], Opts); -to_bytecode([{hash,_line, Hash}|Rest], Address, Env, Code, Opts) -> - to_bytecode(Rest, Address, Env, [{immediate, Hash}|Code], Opts); +to_bytecode([{string,_line, String}|Rest], Address, Env, Code, Opts) -> + to_bytecode(Rest, Address, Env, + [{immediate, aeb_fate_data:make_string(String)}|Code], + Opts); +to_bytecode([{address,_line, Value}|Rest], Address, Env, Code, Opts) -> + to_bytecode(Rest, Address, Env, + [{immediate, aeb_fate_data:make_address(Value)}|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); @@ -631,6 +652,10 @@ 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([{start_variant,_line}|_] = Tokens, Address, Env, Code, Opts) -> + {Size, Tag, Values, Rest} = parse_variant(Tokens), + Variant = aeb_fate_data:make_variant(Size, 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, [{immediate, aeb_fate_data:make_bits(Bits)}|Code], Opts); @@ -689,13 +714,35 @@ parse_tuple(Tokens) -> end. +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}. + + 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). - +parse_value([{'(',_line} | Rest]) -> + {T, Rest2} = parse_tuple(Rest), + {aeb_fate_data:make_tuple(list_to_tuple(T)), Rest2}; +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), + {Variant, Rest}; +parse_value([{string,_line, String} | Rest]) -> + {aeb_fate_data:make_string(String), Rest}; +parse_value([{address,_line, Address} | Rest]) -> + {aeb_fate_data:make_address(Address), Rest}. to_fun_def([{id, _, Name}, {'(', _} | Rest]) -> {ArgsType, [{'to', _} | Rest2]} = to_arg_types(Rest), @@ -732,7 +779,16 @@ to_type([{'{', _}, {id, _, "map"}, {',', _} | Rest]) -> %% TODO: Error handling {KeyType, [{',', _}| Rest2]} = to_type(Rest), {ValueType, [{'}', _}| Rest3]} = to_type(Rest2), - {{map, KeyType, ValueType}, Rest3}. + {{map, KeyType, ValueType}, Rest3}; +to_type([{'{', _} + , {id, _, "variant"} + , {',', _} + , {'[', _} + | Rest]) -> + %% TODO: Error handling + {ElementTypes, [{'}', _}| Rest2]} = to_list_of_types(Rest), + {{variant, ElementTypes}, Rest2}. + to_list_of_types([{']', _} | Rest]) -> {[], Rest}; to_list_of_types(Tokens) -> @@ -756,8 +812,12 @@ serialize_type({tuple, Ts}) -> serialize_type(address) -> [4]; serialize_type(bits) -> [5]; serialize_type({map, K, V}) -> [6 | serialize_type(K) ++ serialize_type(V)]; -serialize_type(string) -> [7]. - +serialize_type(string) -> [7]; +serialize_type({variant, ListOfVariants}) -> + Size = length(ListOfVariants), + if Size < 256 -> + [8, Size | [serialize_type(T) || T <- ListOfVariants]] + end. %% ------------------------------------------------------------------- diff --git a/src/aeb_fate_asm_scan.template b/src/aeb_fate_asm_scan.template index fa4d49d..9c6fd00 100644 --- a/src/aeb_fate_asm_scan.template +++ b/src/aeb_fate_asm_scan.template @@ -12,9 +12,10 @@ DIGIT = [0-9] HEXDIGIT = [0-9a-fA-F] LOWER = [a-z_] UPPER = [A-Z] +BASE58 = [123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz] INT = {DIGIT}+ HEX = 0x{HEXDIGIT}+ -HASH = #{HEXDIGIT}+ +HASH = #{BASE58}+ WS = [\000-\s] ID = {LOWER}[a-zA-Z0-9_]* STRING = "[^"]*" @@ -42,9 +43,9 @@ FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}. -{INT} : {token, {int, TokenLine, parse_int(TokenChars)}}. {HASH} : - {token, {hash, TokenLine, parse_hash(TokenChars)}}. + {token, {address, TokenLine, parse_hash(TokenChars)}}. {STRING} : - {token, {hash, TokenLine, list_to_binary(TokenChars)}}. + {token, {string, TokenLine, list_to_binary(TokenChars)}}. {BITS} : {token, {bits, TokenLine, bits(TokenChars)}}. @@ -54,6 +55,8 @@ FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}. \: : {token, {to, TokenLine}}. \=\> : {token, {arrow, TokenLine}}. +\(\| : {token, {start_varaint, TokenLine}}. +\|\) : {token, {end_varaint, TokenLine}}. , : {token, {',', TokenLine}}. \( : {token, {'(', TokenLine}}. @@ -62,6 +65,7 @@ FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}. \] : {token, {']', TokenLine}}. \{ : {token, {'{', TokenLine}}. \} : {token, {'}', TokenLine}}. +\| : {token, {'|', TokenLine}}. ;;.* : {token, {comment, TokenLine, drop_prefix($;, TokenChars)}}. @@ -98,8 +102,7 @@ parse_acc("a" ++ N) -> list_to_integer(N). parse_hash("#" ++ Chars) -> - N = list_to_integer(Chars, 16), - <>. + base58_to_address(Chars). scan(S) -> string(S). @@ -117,3 +120,23 @@ 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). + +char_to_base58(C) -> + binary:at(<<0,1,2,3,4,5,6,7,8,0,0,0,0,0,0,0,9,10,11,12,13,14,15,16,0,17, + 18,19,20,21,0,22,23,24,25,26,27,28,29,30,31,32,0,0,0,0,0,0, + 33,34,35,36,37,38,39,40,41,42,43,0,44,45,46,47,48,49,50,51, + 52,53,54,55,56,57>>, C-$1). + +base58_to_integer(C, []) -> C; +base58_to_integer(C, [X | Xs]) -> + base58_to_integer(C * 58 + char_to_base58(X), Xs). + +base58_to_integer([]) -> error; +base58_to_integer([Char]) -> char_to_base58(Char); +base58_to_integer([Char | Str]) -> + base58_to_integer(char_to_base58(Char), Str). + +base58_to_address(Base58) -> + I = base58_to_integer(Base58), + Bin = <>, + Bin. \ No newline at end of file diff --git a/src/aeb_fate_data.erl b/src/aeb_fate_data.erl index 5ddab1e..e2308ea 100644 --- a/src/aeb_fate_data.erl +++ b/src/aeb_fate_data.erl @@ -144,7 +144,7 @@ format(?FATE_BITS(B)) when B < 0 -> format(?FATE_VARIANT(Size, Tag, T)) -> ["(| ", lists:join("| ", [integer_to_list(Size), integer_to_list(Tag) | - [format(E) || E <- erlang:tuple_to_list(T)]]), + [format(make_tuple(T))]]), " |)"]; format(M) when ?IS_FATE_MAP(M) -> ["{ ", format_kvs(maps:to_list(?FATE_MAP_VALUE(M))), " }"]; @@ -153,18 +153,13 @@ format(V) -> exit({not_a_fate_type, V}). format_bits(0, Acc) -> Acc; format_bits(N, Acc) -> - case N band 1 of - 1 -> format_bits(N bsr 1, [$1|Acc]); - 0 -> format_bits(N bsr 1, [$0|Acc]) - end. + Bit = $0 + (N band 1), + format_bits(N bsr 1, [Bit|Acc]). format_nbits(0, Acc) -> Acc; format_nbits(N, Acc) -> - case N band 1 of - 1 -> format_nbits(N bsr 1, [$0|Acc]); - 0 -> format_nbits(N bsr 1, [$1|Acc]) - end. - + Bit = $1 - (N band 1), + format_nbits(N bsr 1, [Bit|Acc]). format_list(List) -> ["[ ", lists:join(", ", [format(E) || E <- List]), " ]"]. diff --git a/test/asm_code/immediates.fate b/test/asm_code/immediates.fate index d3a0077..8f32280 100644 --- a/test/asm_code/immediates.fate +++ b/test/asm_code/immediates.fate @@ -65,4 +65,9 @@ FUNCTION tuple() : {tuple, [integer, boolean, string, {tuple, [integer, integer] RETURNR (42, true, "FooBar", (1, 2)) +FUNCTION address() : address + RETURNR #deadbeef +;; Option(integer) = NONE | SOME(integer) +FUNCTION varaint() : {variant, [{tuple, []}, {tuple, [integer]}]} + RETURNR #deadbeef -- 2.30.2 From 43652e08437888259b52be5881669b38a3c2ca77 Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Mon, 4 Mar 2019 10:34:17 +0100 Subject: [PATCH 26/29] =?UTF-8?q?Handle=205=20to=208=20args.=20Generate=20?= =?UTF-8?q?a=20test=20file=20with=20all=20instructions=20for=20as=E2=80=A6?= =?UTF-8?q?=20(#14)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Handle 5 to 8 args. Generate a test file with all instructions for asm/disasm. * Add ops to test 7 and 8 arguments. --- src/aeb_fate_asm.erl | 242 ++++++++++++++++++++++++---- src/aeb_fate_asm_scan.template | 4 +- src/aeb_fate_generate_ops.erl | 152 ++++++++++++++++- test/aeb_fate_asm_test.erl | 1 + test/asm_code/all_instructions.fate | 233 ++++++++++++++++++++++++++ test/asm_code/immediates.fate | 8 +- 6 files changed, 595 insertions(+), 45 deletions(-) create mode 100644 test/asm_code/all_instructions.fate diff --git a/src/aeb_fate_asm.erl b/src/aeb_fate_asm.erl index 024b5ba..cc9200d 100644 --- a/src/aeb_fate_asm.erl +++ b/src/aeb_fate_asm.erl @@ -341,14 +341,13 @@ deserialize_op(?SWITCH_VN, Rest, Code) -> <> = Rest, {Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2), case aeb_fate_encoding:deserialize_one(Rest3) of - {N, Rest4} when is_integer(N), N >= 0 -> + {L, Rest4} when is_list(L) -> Modifier0 = bits_to_modifier(ArgType band 2#11), immediate = bits_to_modifier((ArgType bsr 2) band 2#11), - {BBs, Rest5} = deserialize_n(N, Rest4), - {Rest5, [{aeb_fate_opcodes:mnemonic(?SWITCH_VN) + {Rest4, [{aeb_fate_opcodes:mnemonic(?SWITCH_VN) , {Modifier0, Arg0} - , {immediate, N} - , list_to_tuple(BBs)} + , {immediate, L} + } | Code]}; _ -> exit(bad_argument_to_switch_vn) end; @@ -397,17 +396,107 @@ deserialize_op(Op, Rest, Code) -> , {Modifier1, Arg1} , {Modifier2, Arg2} , {Modifier3, Arg3}} - | Code]} + | Code]}; + 5 -> + <> = Rest, + {Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2), + {Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3), + {Arg2, Rest5} = aeb_fate_encoding:deserialize_one(Rest4), + {Arg3, Rest6} = aeb_fate_encoding:deserialize_one(Rest5), + {Arg4, Rest7} = aeb_fate_encoding:deserialize_one(Rest6), + Modifier0 = bits_to_modifier(ArgType band 2#11), + Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11), + Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11), + Modifier3 = bits_to_modifier((ArgType bsr 6) band 2#11), + Modifier4 = bits_to_modifier(ArgType2 band 2#11), + {Rest7, [{ OpName + , {Modifier0, Arg0} + , {Modifier1, Arg1} + , {Modifier2, Arg2} + , {Modifier3, Arg3} + , {Modifier4, Arg4} + } + | Code]}; + 6 -> + <> = Rest, + {Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2), + {Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3), + {Arg2, Rest5} = aeb_fate_encoding:deserialize_one(Rest4), + {Arg3, Rest6} = aeb_fate_encoding:deserialize_one(Rest5), + {Arg4, Rest7} = aeb_fate_encoding:deserialize_one(Rest6), + {Arg5, Rest8} = aeb_fate_encoding:deserialize_one(Rest7), + Modifier0 = bits_to_modifier(ArgType band 2#11), + Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11), + Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11), + Modifier3 = bits_to_modifier((ArgType bsr 6) band 2#11), + Modifier4 = bits_to_modifier(ArgType2 band 2#11), + Modifier5 = bits_to_modifier((ArgType2 bsr 2) band 2#11), + {Rest8, [{ OpName + , {Modifier0, Arg0} + , {Modifier1, Arg1} + , {Modifier2, Arg2} + , {Modifier3, Arg3} + , {Modifier4, Arg4} + , {Modifier5, Arg5} + } + | Code]}; + 7 -> + <> = Rest, + {Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2), + {Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3), + {Arg2, Rest5} = aeb_fate_encoding:deserialize_one(Rest4), + {Arg3, Rest6} = aeb_fate_encoding:deserialize_one(Rest5), + {Arg4, Rest7} = aeb_fate_encoding:deserialize_one(Rest6), + {Arg5, Rest8} = aeb_fate_encoding:deserialize_one(Rest7), + {Arg6, Rest9} = aeb_fate_encoding:deserialize_one(Rest8), + Modifier0 = bits_to_modifier(ArgType band 2#11), + Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11), + Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11), + Modifier3 = bits_to_modifier((ArgType bsr 6) band 2#11), + Modifier4 = bits_to_modifier(ArgType2 band 2#11), + Modifier5 = bits_to_modifier((ArgType2 bsr 2) band 2#11), + Modifier6 = bits_to_modifier((ArgType2 bsr 4) band 2#11), + {Rest9, [{ OpName + , {Modifier0, Arg0} + , {Modifier1, Arg1} + , {Modifier2, Arg2} + , {Modifier3, Arg3} + , {Modifier4, Arg4} + , {Modifier5, Arg5} + , {Modifier6, Arg6} + } + | Code]}; + 8 -> + <> = Rest, + {Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2), + {Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3), + {Arg2, Rest5} = aeb_fate_encoding:deserialize_one(Rest4), + {Arg3, Rest6} = aeb_fate_encoding:deserialize_one(Rest5), + {Arg4, Rest7} = aeb_fate_encoding:deserialize_one(Rest6), + {Arg5, Rest8} = aeb_fate_encoding:deserialize_one(Rest7), + {Arg6, Rest9} = aeb_fate_encoding:deserialize_one(Rest8), + {Arg7, Rest10} = aeb_fate_encoding:deserialize_one(Rest9), + Modifier0 = bits_to_modifier(ArgType band 2#11), + Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11), + Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11), + Modifier3 = bits_to_modifier((ArgType bsr 6) band 2#11), + Modifier4 = bits_to_modifier(ArgType2 band 2#11), + Modifier5 = bits_to_modifier((ArgType2 bsr 2) band 2#11), + Modifier6 = bits_to_modifier((ArgType2 bsr 4) band 2#11), + Modifier7 = bits_to_modifier((ArgType2 bsr 6) band 2#11), + {Rest10, [{ OpName + , {Modifier0, Arg0} + , {Modifier1, Arg1} + , {Modifier2, Arg2} + , {Modifier3, Arg3} + , {Modifier4, Arg4} + , {Modifier5, Arg5} + , {Modifier6, Arg6} + , {Modifier7, Arg7} + } + | Code]} end. -deserialize_n(N, Binary) -> - deserialize_n(N, Binary, []). - -deserialize_n(0, Binary, Acc) -> - {lists:reverse(Acc), Binary}; -deserialize_n(N, Binary, Acc) -> - {Value, Rest} = aeb_fate_encoding:deserialize_one(Binary), - deserialize_n(N-1, Rest, [Value|Acc]). @@ -447,11 +536,114 @@ serialize(#{functions := Functions} =_Env) -> %% bitpos: 6 4 2 0 %% xx xx xx xx %% Arg3 Arg2 Arg1 Arg0 +%% For 5-8 args another Argument Spec Byte is used %% Bit pattern %% 00 : stack/unused (depending on instruction) %% 01 : argN %% 10 : varN %% 11 : immediate +serialize_code([ {Arg0Type, Arg0} + , {Arg1Type, Arg1} + , {Arg2Type, Arg2} + , {Arg3Type, Arg3} + , {Arg4Type, Arg4} + , {Arg5Type, Arg5} + , {Arg6Type, Arg6} + , {Arg7Type, Arg7} + | Rest]) -> + ArgSpec1 = + modifier_bits(Arg0Type) bor + (modifier_bits(Arg1Type) bsl 2) bor + (modifier_bits(Arg2Type) bsl 4) bor + (modifier_bits(Arg3Type) bsl 6), + ArgSpec2 = + modifier_bits(Arg4Type) bor + (modifier_bits(Arg5Type) bsl 2) bor + (modifier_bits(Arg6Type) bsl 4) bor + (modifier_bits(Arg7Type) bsl 6), + [ ArgSpec1 + , ArgSpec2 + , serialize_data(Arg0Type, Arg0) + , serialize_data(Arg1Type, Arg1) + , serialize_data(Arg2Type, Arg2) + , serialize_data(Arg3Type, Arg3) + , serialize_data(Arg4Type, Arg4) + , serialize_data(Arg5Type, Arg5) + , serialize_data(Arg6Type, Arg6) + , serialize_data(Arg7Type, Arg7) + | serialize_code(Rest)]; +serialize_code([ {Arg0Type, Arg0} + , {Arg1Type, Arg1} + , {Arg2Type, Arg2} + , {Arg3Type, Arg3} + , {Arg4Type, Arg4} + , {Arg5Type, Arg5} + , {Arg6Type, Arg6} + | Rest]) -> + ArgSpec1 = + modifier_bits(Arg0Type) bor + (modifier_bits(Arg1Type) bsl 2) bor + (modifier_bits(Arg2Type) bsl 4) bor + (modifier_bits(Arg3Type) bsl 6), + ArgSpec2 = + modifier_bits(Arg4Type) bor + (modifier_bits(Arg5Type) bsl 2) bor + (modifier_bits(Arg6Type) bsl 4), + [ ArgSpec1 + , ArgSpec2 + , serialize_data(Arg0Type, Arg0) + , serialize_data(Arg1Type, Arg1) + , serialize_data(Arg2Type, Arg2) + , serialize_data(Arg3Type, Arg3) + , serialize_data(Arg4Type, Arg4) + , serialize_data(Arg5Type, Arg5) + , serialize_data(Arg6Type, Arg6) + | serialize_code(Rest)]; +serialize_code([ {Arg0Type, Arg0} + , {Arg1Type, Arg1} + , {Arg2Type, Arg2} + , {Arg3Type, Arg3} + , {Arg4Type, Arg4} + , {Arg5Type, Arg5} + | Rest]) -> + ArgSpec1 = + modifier_bits(Arg0Type) bor + (modifier_bits(Arg1Type) bsl 2) bor + (modifier_bits(Arg2Type) bsl 4) bor + (modifier_bits(Arg3Type) bsl 6), + ArgSpec2 = + modifier_bits(Arg4Type) bor + (modifier_bits(Arg5Type) bsl 2), + [ ArgSpec1 + , ArgSpec2 + , serialize_data(Arg0Type, Arg0) + , serialize_data(Arg1Type, Arg1) + , serialize_data(Arg2Type, Arg2) + , serialize_data(Arg3Type, Arg3) + , serialize_data(Arg4Type, Arg4) + , serialize_data(Arg5Type, Arg5) + | serialize_code(Rest)]; +serialize_code([ {Arg0Type, Arg0} + , {Arg1Type, Arg1} + , {Arg2Type, Arg2} + , {Arg3Type, Arg3} + , {Arg4Type, Arg4} + | Rest]) -> + ArgSpec1 = + modifier_bits(Arg0Type) bor + (modifier_bits(Arg1Type) bsl 2) bor + (modifier_bits(Arg2Type) bsl 4) bor + (modifier_bits(Arg3Type) bsl 6), + ArgSpec2 = + modifier_bits(Arg4Type), + [ ArgSpec1 + , ArgSpec2 + , serialize_data(Arg0Type, Arg0) + , serialize_data(Arg1Type, Arg1) + , serialize_data(Arg2Type, Arg2) + , serialize_data(Arg3Type, Arg3) + , serialize_data(Arg4Type, Arg4) + | serialize_code(Rest)]; serialize_code([ {Arg0Type, Arg0} , {Arg1Type, Arg1} @@ -505,35 +697,19 @@ serialize_code([ ?ELEMENT | serialize_code(Rest)]; serialize_code([ ?SWITCH_VN , {Arg0Type, Arg0} - , {immediate, N} - | Rest]) when is_integer(N), N >= 0 -> + , {immediate, L} + | Rest]) -> ArgSpec = modifier_bits(Arg0Type) bor (modifier_bits(immediate) bsl 2), - {Serialization, Rest2} = serialize_n_ints(N, Rest), [?SWITCH_VN , ArgSpec , serialize_data(Arg0Type, Arg0) - , serialize_data(immediate, N) - | Serialization] ++ serialize_code(Rest2); + , serialize_data(immediate, L)] ++ serialize_code(Rest); serialize_code([B|Rest]) -> [B | serialize_code(Rest)]; serialize_code([]) -> []. -serialize_n_ints(N, Rest) -> - serialize_n_ints(N, Rest, []). - -serialize_n_ints(0, Rest, Acc) -> - %% Acc is a list of binaries. - {lists:reverse(Acc), Rest}; -serialize_n_ints(N, [Int|Rest], Acc) when is_integer(Int), Int >= 0 -> - serialize_n_ints(N - 1, Rest, [aeb_fate_encoding:serialize(Int)|Acc]); -serialize_n_ints(_, [], _) -> - exit(not_enough_bbs_for_switch_vn); -serialize_n_ints(_, _, _) -> - exit(bad_bbs_value_for_switch_vn). - - %% 00 : stack/unused (depending on instruction) %% 01 : argN diff --git a/src/aeb_fate_asm_scan.template b/src/aeb_fate_asm_scan.template index 9c6fd00..1fa686a 100644 --- a/src/aeb_fate_asm_scan.template +++ b/src/aeb_fate_asm_scan.template @@ -55,8 +55,8 @@ FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}. \: : {token, {to, TokenLine}}. \=\> : {token, {arrow, TokenLine}}. -\(\| : {token, {start_varaint, TokenLine}}. -\|\) : {token, {end_varaint, TokenLine}}. +\(\| : {token, {start_variant, TokenLine}}. +\|\) : {token, {end_variant, TokenLine}}. , : {token, {',', TokenLine}}. \( : {token, {'(', TokenLine}}. diff --git a/src/aeb_fate_generate_ops.erl b/src/aeb_fate_generate_ops.erl index 5cfe13a..3b77417 100644 --- a/src/aeb_fate_generate_ops.erl +++ b/src/aeb_fate_generate_ops.erl @@ -1,7 +1,8 @@ -module(aeb_fate_generate_ops). -export([ gen_and_halt/1 - , generate/0]). + , generate/0 + , test_asm_generator/1]). gen_and_halt([SrcDirArg, IncludeDirArg]) -> generate(atom_to_list(SrcDirArg), @@ -34,7 +35,7 @@ ops_defs() -> , { 'JUMPIF', 16#07, 2, true, 4, [a,ii], jumpif, "Conditional jump to a basic block. If Arg0 then jump to Arg1."} , { 'SWITCH_V2', 16#08, 3, true, 4, [a,ii,ii], switch, "Conditional jump to a basic block on variant tag."} , { 'SWITCH_V3', 16#09, 4, true, 4, [a,ii,ii,ii], switch, "Conditional jump to a basic block on variant tag."} - , { 'SWITCH_VN', 16#0a, 2, true, 4, [a,li], switch, "Conditional jump to a basic block on variant tag."} + , { 'SWITCH_VN', 16#0a, 2, true, 4, [a, li], switch, "Conditional jump to a basic block on variant tag."} , { 'PUSH', 16#0b, 1, false, 2, [a], push, "Push argument to stack."} , { 'DUPA', 16#0c, 0, false, 3, atomic, dup, "push copy of accumulator on stack."} , { 'DUP', 16#0d, 1, false, 3, [a], dup, "push Arg0 stack pos on top of stack."} @@ -136,8 +137,11 @@ ops_defs() -> , { 'SHA256', 16#7c, 0, false,3, atomic, sha256, ""} , { 'BLAKE2B', 16#7d, 0, false,3, atomic, blake2b, ""} - , {'ABORT', 16#fb, 1, false, 3, [a], abort, "Abort execution (dont use all gas) with error message in Arg0."} - , {'EXIT', 16#fc, 1, false, 3, [a], exit, "Abort execution (use upp all gas) with error message in Arg0."} + + , { 'DUMMY7ARG', 16#f9, 7, false,3, [a,a,a,a,a,a,a], dummyarg, "Temporary dummy instruction to test 7 args."} + , { 'DUMMY8ARG', 16#fa, 8, false,3, [a,a,a,a,a,a,a,a],dummyarg, "Temporary dummy instruction to test 8 args."} + , { 'ABORT', 16#fb, 1, false, 3, [a], abort, "Abort execution (dont use all gas) with error message in Arg0."} + , { 'EXIT', 16#fc, 1, false, 3, [a], exit, "Abort execution (use upp all gas) with error message in Arg0."} , { 'NOP', 16#fd, 0, false, 1, atomic, nop, "The no op. does nothing."} %% FUNCTION 16#fe "Function declaration and entrypoint." %% EXTEND 16#ff "Reserved for future extensions beyond one byte opcodes." @@ -411,8 +415,8 @@ gen_asm_pp(Module, Path, Ops) -> "-export([format_op/2]).\n\n" "format_arg(t, T) ->\n" " io_lib:format(\"~~p \", [T]);\n" - "format_arg(li, List) ->\n" - " [[\" \", E] || {immedate, E} <- List];\n" + "format_arg(li, {immediate, LI}) ->\n" + " aeb_fate_data:format(LI);\n" "format_arg(_, {immediate, I}) ->\n" " aeb_fate_data:format(I);\n" "format_arg(a, {arg, N}) -> io_lib:format(\"arg~~p\", [N]);\n" @@ -420,7 +424,7 @@ gen_asm_pp(Module, Path, Ops) -> "format_arg(a, {stack, 0}) -> \"a\";\n" "format_arg(a, {stack, N}) -> io_lib:format(\"a~~p\", [N]).\n\n" "lookup(Name, Symbols) ->\n" - " maps:get(Name, Symbols, Name).\n\n" + " maps:get(Name, Symbols, io_lib:format(\"~~w\",[Name])).\n\n" "~s" , [Formats]), @@ -497,5 +501,137 @@ gen_format(#{opname := Name, format := Args}) -> "\" \", format_arg(~w, Arg4)," "\" \", format_arg(~w, Arg5)," "\" \", format_arg(~w, Arg6)];", - [Name, NameAsString, T0, T1, T2, T3, T4, T5, T6]) + [Name, NameAsString, T0, T1, T2, T3, T4, T5, T6]); + [T0, T1, T2, T3, T4, T5, T6, T7] -> + io_lib:format( + "format_op({~w, Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7}, _) ->\n" + " [\"~s \", format_arg(~w, Arg0), " + "\" \", format_arg(~w, Arg1)," + "\" \", format_arg(~w, Arg2)," + "\" \", format_arg(~w, Arg3)," + "\" \", format_arg(~w, Arg4)," + "\" \", format_arg(~w, Arg5)," + "\" \", format_arg(~w, Arg6)," + "\" \", format_arg(~w, Arg7)];", + [Name, NameAsString, T0, T1, T2, T3, T4, T5, T6, T7]) + end. + +test_asm_generator(Filename) -> + {ok, File} = file:open(Filename, [write]), + Instructions = lists:flatten([gen_instruction(Op)++"\n" || Op <- gen(ops_defs())]), + io:format(File, + ";; CONTRACT all_instructions\n\n" + ";; Dont expect this contract to typecheck or run.\n" + ";; Just used to check assembler rountrip of all instruction.\n\n" + "FUNCTION foo () : {tuple, []}\n" + "~s" + , [Instructions]), + io:format(File, " RETURNR ()\n", []), + file:close(File). + + +gen_instruction(#{opname := Name, format := atomic}) -> + io_lib:format(" ~s\n", [Name]); +gen_instruction(#{opname := Name, format := ArgTypes}) -> + Args = lists:flatten(lists:join(" ", [gen_arg(A) || A <- ArgTypes])), + I = io_lib:format(" ~s ~s\n", [Name, Args]), + I. + +%% This should be done with a Quick Check generator... +gen_arg(a) -> any_arg(); +gen_arg(is) -> "foo"; +gen_arg(ii) -> gen_int(); +gen_arg(li) -> "[1, 2, 3]"; +gen_arg(t) -> "integer". + +any_arg() -> + element(rand:uniform(5), {"a", stack_arg(), var_arg(), arg_arg(), imm_arg()}). +stack_arg() -> "a" ++ integer_to_list(rand:uniform(255)-1). +arg_arg() -> "arg" ++ integer_to_list(rand:uniform(256)-1). +var_arg() -> "var" ++ integer_to_list(rand:uniform(256)-1). +imm_arg() -> + case rand:uniform(15) of + 1 -> gen_int(); + 2 -> gen_int(); + 3 -> gen_int(); + 4 -> gen_int(); + 5 -> gen_int(); + 6 -> gen_int(); + 7 -> gen_int(); + 8 -> gen_address(); + 9 -> gen_boolean(); + 10 -> gen_string(); + 11 -> gen_map(); + 12 -> gen_list(); + 13 -> gen_bits(); + 14 -> gen_tuple(); + 15 -> gen_variant() + end. + +gen_key() -> + case rand:uniform(15) of + 1 -> gen_int(); + 2 -> gen_int(); + 3 -> gen_int(); + 4 -> gen_int(); + 5 -> gen_int(); + 6 -> gen_int(); + 7 -> gen_int(); + 8 -> gen_address(); + 9 -> gen_boolean(); + 10 -> gen_string(); + 11 -> gen_string(); + 12 -> gen_list(); + 13 -> gen_bits(); + 14 -> gen_tuple(); + 15 -> gen_variant() + end. + +gen_boolean() -> + element(rand:uniform(2), {"true", "false"}). + +gen_int() -> + element(rand:uniform(4), + { integer_to_list(rand:uniform(round(math:pow(10,40)))) + , integer_to_list(rand:uniform(10)) + , integer_to_list(rand:uniform(100)) + , io_lib:format("0x~.16b",[rand:uniform(round(math:pow(10,10)))])}). + +gen_address() -> "#nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv". +gen_string() -> "\"foo\"". +gen_map() -> "{ " ++ gen_key() ++ " => " ++ imm_arg() ++ "}". +gen_list() -> + case rand:uniform(4) of + 1 -> "[]"; + 2 -> "[" ++ lists:join(", ", gen_list_elements()) ++ " ]"; + 3 -> "[ " ++ imm_arg() ++ " ]"; + 4 -> "[ " ++ imm_arg() ++ ", " ++ imm_arg() ++ " ]" + end. + +%% Not type correct. +gen_list_elements() -> + case rand:uniform(3) of + 1 -> [imm_arg() | gen_list_elements()]; + 2 -> []; + 3 -> [imm_arg()] + end. + +gen_bits() -> + element(rand:uniform(3), + {"<>" + ,"!<>" + , "101010"}). + +gen_tuple() -> + case rand:uniform(3) of + 1 -> "()"; + 2 -> "(42)"; + 3 -> "(" ++ imm_arg() ++ ")" + end. + +gen_variant() -> + case rand:uniform(3) of + 1 -> "(| 5 | 2 | (1, \"foo\", ()) |)"; + 2 -> "(| 2 | 1 | ( " ++ imm_arg() ++ " ) |)"; + 3 -> "(| 2 | 0 | ( " ++ imm_arg() ++ ", " ++ imm_arg() ++ " ) |)" end. diff --git a/test/aeb_fate_asm_test.erl b/test/aeb_fate_asm_test.erl index fc20dbd..33b3989 100644 --- a/test/aeb_fate_asm_test.erl +++ b/test/aeb_fate_asm_test.erl @@ -52,6 +52,7 @@ sources() -> , "tuple" , "mapofmap" , "immediates" + , "all_instructions" ]. check_roundtrip(File) -> diff --git a/test/asm_code/all_instructions.fate b/test/asm_code/all_instructions.fate new file mode 100644 index 0000000..0755a6e --- /dev/null +++ b/test/asm_code/all_instructions.fate @@ -0,0 +1,233 @@ +;; CONTRACT all_instructions + +;; Dont expect this contract to typecheck or run. +;; Just used to check assembler rountrip of all instruction. + +FUNCTION foo () : {tuple, []} + RETURN + + RETURNR a13 + + CALL foo + + CALL_R arg125 foo + + CALL_T foo + + CALL_TR arg245 foo + + JUMP 5514251025295783441695716053282666408426 + + JUMPIF arg196 0x12c651665 + + SWITCH_V2 a27 63 33 + + SWITCH_V3 var4 0x1d61723dd 79 7 + + SWITCH_VN #nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv [1, 2, 3] + + PUSH var80 + + DUPA + + DUP a + + POP a107 + + STORE arg183 var225 + + INCA + + INC a25 + + DECA + + DEC a + + ADD a217 a a + + SUB arg35 arg165 var74 + + MUL 44 35 "foo" + + DIV 263838340369912686645632650718169038811 a24 a + + MOD var113 arg80 arg207 + + POW a176 a a123 + + LT a 78 var81 + + GT arg19 4729414120208894485838100532547810615352 var175 + + EQ 85 a arg164 + + ELT a161 arg226 a168 + + EGT a131 1 var250 + + NEQ a85 a a83 + + AND var255 0x294a24f6 var189 + + OR (| 2 | 0 | ( (), (42) ) |) arg168 var107 + + NOT arg124 a + + TUPLE 5019186157739257888756115213149493826410 + + ELEMENT integer arg148 var25 a219 + + MAP_EMPTY a135 + + MAP_LOOKUP a82 a a143 + + MAP_LOOKUPD var112 arg35 a163 var112 + + MAP_UPDATE false a0 a56 a + + MAP_DELETE arg180 a var1 + + MAP_MEMBER a { true => 4} 94 + + MAP_FROM_LIST () a159 + + NIL arg91 + + IS_NIL a121 var6 + + CONS arg185 "foo" a114 + + HD a150 var124 + + TL arg223 a + + LENGTH var216 a143 + + STR_EQ { 203961992615221001243597889146034217896 => 0x1f53a1843} 281217554184165828643225535776787296845 a177 + + STR_JOIN a a 7144184027126178769820155907121270843348 + + INT_TO_STR var238 a + + ADDR_TO_STR a arg216 + + STR_REVERSE a174 #nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv + + INT_TO_ADDR arg127 var207 + + VARIANT a a 0x1f7b72200 a + + VARIANT_TEST a26 arg217 a + + VARIANT_ELEMENT a86 arg103 arg108 + + BITS_NONEA + + BITS_NONE a + + BITS_ALLA + + BITS_ALL a164 + + BITS_ALL_N a221 arg135 + + BITS_SET arg150 a48 { 0x1a715e2a6 => 3} + + BITS_CLEAR arg98 a arg164 + + BITS_TEST a a242 (| 5 | 2 | (1, "foo", ()) |) + + BITS_SUM a244 a71 + + BITS_OR var20 var186 a + + BITS_AND a187 4 arg203 + + BITS_DIFF var200 arg247 var20 + + ADDRESS a237 + + BALANCE a231 + + ORIGIN arg216 + + CALLER a27 + + GASPRICE arg119 + + BLOCKHASH arg110 + + BENEFICIARY var163 + + TIMESTAMP a + + GENERATION 242795038229506961431398379342231049652 + + MICROBLOCK arg43 + + DIFFICULTY var24 + + GASLIMIT arg220 + + GAS var35 + + LOG0 a a85 + + LOG1 arg94 arg86 arg208 + + LOG2 a113 (| 5 | 2 | (1, "foo", ()) |) arg238 var108 + + LOG3 arg255 arg15 arg211 var139 arg44 + + LOG4 #nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv a247 a 9 a38 a + + DEACTIVATE + + SPEND #nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv var136 + + ORACLE_REGISTER arg29 48 ((| 5 | 2 | (1, "foo", ()) |)) arg65 { <> => false} <> + + ORACLE_QUERY + + ORACLE_RESPOND + + ORACLE_EXTEND + + ORACLE_GET_ANSWER + + ORACLE_GET_QUESTION + + ORACLE_QUERY_FEE + + AENS_RESOLVE + + AENS_PRECLAIM + + AENS_CLAIM + + AENS_UPDATE + + AENS_TRANSFER + + AENS_REVOKE + + ECVERIFY + + SHA3 + + SHA256 + + BLAKE2B + + DUMMY7ARG a a 7607708484837907159893701471377343595877 (| 2 | 0 | ( [], [ 45, { 1 => 3441201581501946066216994494994943246334} ] ) |) a0 var56 "foo" + + DUMMY8ARG 3673679924816289365509492271980889822579 a69 arg242 var237 a175 arg106 () var255 + + ABORT a + + EXIT var120 + + NOP + + RETURNR () diff --git a/test/asm_code/immediates.fate b/test/asm_code/immediates.fate index 8f32280..3efcc1a 100644 --- a/test/asm_code/immediates.fate +++ b/test/asm_code/immediates.fate @@ -69,5 +69,9 @@ FUNCTION address() : address RETURNR #deadbeef ;; Option(integer) = NONE | SOME(integer) -FUNCTION varaint() : {variant, [{tuple, []}, {tuple, [integer]}]} - RETURNR #deadbeef +FUNCTION variant_none() : {variant, [{tuple, []}, {tuple, [integer]}]} + RETURNR (| 2 | 0 | () |) + +;; Option(integer) = NONE | SOME(integer) +FUNCTION variant_some() : {variant, [{tuple, []}, {tuple, [integer]}]} + RETURNR (| 2 | 1 | (42) |) -- 2.30.2 From 23695330ef1767c78193f330c3c3effdb76d4045 Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Mon, 4 Mar 2019 13:28:15 +0100 Subject: [PATCH 27/29] Make rebar use make (#15) * Handle 5 to 8 args. Generate a test file with all instructions for asm/disasm. * Add ops to test 7 and 8 arguments. * Make sure rebar builds sources before trying to build. * Make CI use rebar to build to make sure it works on top level without make. --- .circleci/config.yml | 2 +- Makefile | 2 ++ rebar.config | 5 +++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0c9ac9f..7709f3d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -19,7 +19,7 @@ jobs: - dialyzer-cache-v1- - run: name: Build - command: make + command: rebar3 compile - run: name: Static Analysis command: make dialyzer diff --git a/Makefile b/Makefile index 916426e..785acc4 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,8 @@ REBAR ?= rebar3 all: local +sources: $(GENERATED_SRC) + local: $(GENERATED_SRC) @$(REBAR) as local release diff --git a/rebar.config b/rebar.config index 2b014b7..4684063 100644 --- a/rebar.config +++ b/rebar.config @@ -12,6 +12,11 @@ {escript_main_app, aebytecode}. {escript_name, aefateasm}. {escript_emu_args, "%%!"}. + +{pre_hooks, + [{"(linux|darwin|solaris)", compile, "make sources"}, + {"(freebsd)", compile, "gmake sources"}]}. + {provider_hooks, [{post, [{compile, escriptize}]}]}. -- 2.30.2 From 6c60f1e37f420de353bcff2cf682aadce098844a Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Wed, 6 Mar 2019 11:41:16 +0100 Subject: [PATCH 28/29] Pre hook to build sources on Windoes also. (#16) --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index 4684063..e118ea9 100644 --- a/rebar.config +++ b/rebar.config @@ -14,7 +14,7 @@ {escript_emu_args, "%%!"}. {pre_hooks, - [{"(linux|darwin|solaris)", compile, "make sources"}, + [{"(linux|darwin|solaris|win32)", compile, "make sources"}, {"(freebsd)", compile, "gmake sources"}]}. {provider_hooks, [{post, [{compile, escriptize}]}]}. -- 2.30.2 From 9763a1a6f5dd6a463e88f0e6c2124c47bfab8a49 Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Fri, 8 Mar 2019 07:59:09 +0100 Subject: [PATCH 29/29] Pt 164460166 generate documentation (#17) * Generate docs. * Test lists of length 16. --- src/aeb_fate_generate_ops.erl | 50 +++++++++++++++++++++++++++++++++++ test/aeb_serialize_test.erl | 1 + 2 files changed, 51 insertions(+) diff --git a/src/aeb_fate_generate_ops.erl b/src/aeb_fate_generate_ops.erl index 3b77417..6c29b40 100644 --- a/src/aeb_fate_generate_ops.erl +++ b/src/aeb_fate_generate_ops.erl @@ -2,6 +2,7 @@ -export([ gen_and_halt/1 , generate/0 + , generate_documentation/1 , test_asm_generator/1]). gen_and_halt([SrcDirArg, IncludeDirArg]) -> @@ -635,3 +636,52 @@ gen_variant() -> 2 -> "(| 2 | 1 | ( " ++ imm_arg() ++ " ) |)"; 3 -> "(| 2 | 0 | ( " ++ imm_arg() ++ ", " ++ imm_arg() ++ " ) |)" end. + + +%% TODO: add gas cost. +generate_documentation(Filename) -> + {ok, File} = file:open(Filename, [write]), + Instructions = lists:flatten([gen_doc(Op)++"\n" || Op <- gen(ops_defs())]), + io:format(File, + "### Operations\n\n" + "| OpCode | Name | Args | Description |\n" + "| --- | --- | --- | --- |\n" + "~s" + , [Instructions]), + io:format(File, "\n", []), + file:close(File). + +gen_doc(#{ opname := Name + , opcode := OpCode + , args := Args + , end_bb := EndBB + , format := FateFormat + , macro := Macro + , type_name := TypeName + , doc := Doc + , gas := Gas + , type := Type + , constructor := Constructor + , constructor_type := ConstructorType + }) -> + Arguments = + case FateFormat of + atomic -> ""; + _ -> lists:join(" ", + [format_arg_doc(A) || + A <- + lists:zip(FateFormat, + lists:seq(0,length(FateFormat)-1))]) + end, + io_lib:format("| 0x~.16b | ~w | ~s | ~s |\n", + [ OpCode + , Name + , Arguments + , Doc]). + +format_arg_doc({a, N}) -> io_lib:format("Arg~w", [N]); +format_arg_doc({is,N}) -> "Identifier"; +format_arg_doc({ii,N}) -> "Integer"; +format_arg_doc({li,N}) -> "[Integers]"; +format_arg_doc({t,N}) -> "Type". + diff --git a/test/aeb_serialize_test.erl b/test/aeb_serialize_test.erl index dcbe4e2..778a2f9 100644 --- a/test/aeb_serialize_test.erl +++ b/test/aeb_serialize_test.erl @@ -61,6 +61,7 @@ sources() -> "0123456789012345678901234567890123456789">>), %% Magic concat 80 char string. aeb_fate_data:make_tuple({True, FortyTwo}), aeb_fate_data:make_tuple(list_to_tuple(make_int_list(65))), + aeb_fate_data:make_tuple(list_to_tuple(make_int_list(16))), aeb_fate_data:make_map(#{ aeb_fate_data:make_integer(1) => True, aeb_fate_data:make_integer(2) => False}), aeb_fate_data:make_map(#{ aeb_fate_data:make_string(<<"foo">>) => aeb_fate_data:make_tuple({FortyTwo, True})}), aeb_fate_data:make_list(make_int_list(3)), -- 2.30.2