diff --git a/.circleci/config.yml b/.circleci/config.yml
index e34c31c..7709f3d 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -22,13 +22,13 @@ jobs:
command: rebar3 compile
- 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 318406e..1887839 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,4 +9,11 @@ rel/example_project
.concrete/DEV_MODE
.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
+src/aeb_fate_pp.erl
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..785acc4
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,36 @@
+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
+
+sources: $(GENERATED_SRC)
+
+local: $(GENERATED_SRC)
+ @$(REBAR) as local release
+
+console: local
+ @$(REBAR) as local shell
+
+clean:
+ @$(REBAR) clean
+ rm -f $(GENERATED_SRC)
+ rm -f ebin/*
+
+dialyzer: local
+ @$(REBAR) as local dialyzer
+
+distclean: clean
+ @rm -rf _build/
+
+eunit: local
+ @$(REBAR) as local eunit
+
+test: local
+ @$(REBAR) as local eunit
+
+ebin/%.beam: src/%.erl
+ erlc -o $(dir $@) $<
+
+$(GENERATED_SRC): $(GENERATOR_DEPS)
+ erl -pa ebin/ -noshell -s aeb_fate_generate_ops gen_and_halt src/ include/
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/ebin/.gitkeep b/ebin/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/include/aeb_fate_data.hrl b/include/aeb_fate_data.hrl
new file mode 100644
index 0000000..424c9b1
--- /dev/null
+++ b/include/aeb_fate_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/rebar.config b/rebar.config
index cfd8b45..e118ea9 100644
--- a/rebar.config
+++ b/rebar.config
@@ -2,10 +2,56 @@
{erl_opts, [debug_info]}.
-{deps, []}.
+{deps, [ {eblake2, "1.0.0"}
+ , {aeserialization, {git, "https://github.com/aeternity/aeserialization.git",
+ {ref, "b55c372"}}}
+ , {getopt, "1.0.1"}
+ ]}.
+
+{escript_incl_apps, [aebytecode, eblake2, aeserialization, getopt]}.
+{escript_main_app, aebytecode}.
+{escript_name, aefateasm}.
+{escript_emu_args, "%%!"}.
+
+{pre_hooks,
+ [{"(linux|darwin|solaris|win32)", compile, "make sources"},
+ {"(freebsd)", compile, "gmake sources"}]}.
+
+{provider_hooks, [{post, [{compile, escriptize}]}]}.
+
{dialyzer, [
{warnings, [unknown]},
{plt_apps, all_deps},
- {base_plt_apps, [erts, kernel, stdlib]}
+ {base_plt_apps, [erts, kernel, stdlib, crypto, getopt]}
]}.
+
+
+{relx, [{release, {aebytecode, "2.0.1"},
+ [aebytecode, eblake2, getopt]},
+
+ {dev_mode, true},
+ {include_erts, false},
+
+ {extended_start_script, true}]}.
+
+{profiles, [{binary, [
+ {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)",
+ 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
+ ]}
+ ]},
+ {eqc, [{erl_opts, [{parse_transform, eqc_cover}]},
+ {extra_src_dirs, ["quickcheck"]} %% May not be called eqc!
+ ]}
+ ]}.
diff --git a/rebar.lock b/rebar.lock
index 57afcca..ee15e69 100644
--- a/rebar.lock
+++ b/rebar.lock
@@ -1 +1,16 @@
-[].
+{"1.1.0",
+[{<<"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_fate_asm.erl b/src/aeb_fate_asm.erl
new file mode 100644
index 0000000..cc9200d
--- /dev/null
+++ b/src/aeb_fate_asm.erl
@@ -0,0 +1,1049 @@
+%%%-------------------------------------------------------------------
+%%% @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
+%%% ;; 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:
+%%% 1a. Integers as decimals: {Digits} or -{Digits}
+%%% 42
+%%% -2374683271468723648732648736498712634876147
+%%% 1b. Integers as Hexadecimals:: 0x{Hexdigits}
+%%% 0x0deadbeef0
+%%% 2. addresses, a base58 encoded string starting with #{base58char}
+%%% followed by up to 64 hex chars
+%%% #00000deadbeef
+%%% 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
+%%% Tag: Digits
+%%%
+%%% @end
+%%% Created : 21 Dec 2017
+%%%-------------------------------------------------------------------
+
+-module(aeb_fate_asm).
+
+-export([ assemble_file/3
+ , asm_to_bytecode/2
+ , bytecode_to_fate_code/2
+ , function_call/1
+ , pp/1
+ , read_file/1
+ , strip/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) ->
+ Asm = read_file(InFile),
+ {_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),
+ aeb_fate_encoding:serialize(
+ {tuple, {mk_hash(Name), {tuple, list_to_tuple(Args)}}}).
+
+
+to_args([{')', _}]) -> {[], []};
+to_args(Tokens) ->
+ case parse_value(Tokens) of
+ {Arg, [{',', _} | Rest]} ->
+ {More, Rest2} = to_args(Rest),
+ {[Arg|More], Rest2};
+ {Arg, [{')', _} | Rest]} ->
+ {[Arg], Rest}
+ end.
+
+pp(FateCode) ->
+ Listing = to_asm(FateCode),
+ io_lib:format("~ts~n",[Listing]).
+
+
+to_asm(#{ functions := 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) ->
+ [ "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_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) ->
+ [" ",
+ aeb_fate_pp:format_op(Op, Symbols),
+ "\n",
+ format_code(Rest, Symbols)].
+
+
+read_file(Filename) ->
+ {ok, File} = file:read_file(Filename),
+ binary_to_list(File).
+
+asm_to_bytecode(AssemblerCode, Options) ->
+ {ok, Tokens, _} = aeb_fate_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 => #{}
+ , annotations => #{}
+ }, [], Options),
+
+ ByteList = serialize(Env),
+ Signatures = serialize_sigs(Env),
+ SymbolTable = serialize_symbol_table(Env),
+ Annotatations = serialize_annotations(Env),
+ 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
+ {pp_hex_string, true} ->
+ io:format("Code: ~s~n",[to_hexstring(ByteList)]);
+ none ->
+ ok
+ end,
+
+ {Env, ByteCode}.
+
+strip(ByteCode) ->
+ {Code, _Rest} = aeser_rlp:decode_one(ByteCode),
+ Code.
+
+bytecode_to_fate_code(Bytes, _Options) ->
+ {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
+ , current_bb_code => []
+ , functions => #{}
+ , code => #{}
+ }),
+ Env2 = deserialize_signatures(Signatures, Env1),
+ Env3 = deserialize_symbols(SymbolTable, Env2),
+ Env4 = deserialize_annotations(Annotations, Env3),
+ Env4.
+
+
+deserialize(<>,
+ #{ function := none
+ , bb := 0
+ , current_bb_code := []
+ } = Env) ->
+ {Sig, Rest2} = deserialize_signature(Rest),
+ Env2 = Env#{function => {<>, Sig}},
+ deserialize(Rest2, Env2);
+deserialize(<>,
+ #{ function := {F, Sig}
+ , bb := BB
+ , current_bb_code := Code
+ , code := Program
+ , functions := Funs} = Env) ->
+ {NewSig, Rest2} = deserialize_signature(Rest),
+ case Code of
+ [] ->
+ Env2 = Env#{ bb => 0
+ , current_bb_code => []
+ , function => {<>, NewSig}
+ , code => #{}
+ , functions => Funs#{F => {Sig, Program}}},
+ deserialize(Rest2, Env2);
+ _ ->
+ Env2 = Env#{ bb => 0
+ , current_bb_code => []
+ , function => {<>, NewSig}
+ , code => #{}
+ , functions =>
+ Funs#{F => {Sig,
+ 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 aeb_fate_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, Sig}
+ , 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 => {Sig, FunctionCode}}}.
+
+deserialize_op(?ELEMENT, Rest, Code) ->
+ {Type, Rest2} = deserialize_type(Rest),
+ <> = Rest2,
+ {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, [{ aeb_fate_opcodes:mnemonic(?ELEMENT)
+ , Type
+ , {Modifier0, Arg0}
+ , {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
+ {L, Rest4} when is_list(L) ->
+ Modifier0 = bits_to_modifier(ArgType band 2#11),
+ immediate = bits_to_modifier((ArgType bsr 2) band 2#11),
+ {Rest4, [{aeb_fate_opcodes:mnemonic(?SWITCH_VN)
+ , {Modifier0, Arg0}
+ , {immediate, L}
+ }
+ | 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
+ 0 -> {Rest, [OpName | Code]};
+ 1 ->
+ <> = Rest,
+ {Arg, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
+ Modifier = bits_to_modifier(ArgType),
+ {Rest3, [{OpName, {Modifier, Arg}} | Code]};
+ 2 ->
+ <> = Rest,
+ {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} = 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),
+ {Rest5, [{ OpName
+ , {Modifier0, Arg0}
+ , {Modifier1, Arg1}
+ , {Modifier2, Arg2}}
+ | Code]};
+ 4 ->
+ <> = 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),
+ 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]};
+ 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_signatures(_Signatures, Env) -> Env.
+
+deserialize_symbols(Table, Env) ->
+ ?FATE_MAP_VALUE(SymbolTable) = aeb_fate_encoding:deserialize(Table),
+ Env#{symbols => SymbolTable}.
+
+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(#{ annotations := Annotations}) ->
+ aeb_fate_encoding:serialize(aeb_fate_data:make_map(Annotations)).
+
+
+
+
+
+serialize(#{functions := Functions} =_Env) ->
+ %% Sort the functions oon name to get a canonical serialisation.
+ Code = [[?FUNCTION, Name, serialize_signature(Sig), C] ||
+ {Name, {Sig, C}} <- lists:sort(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
+%% 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}
+ , {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([ ?SWITCH_VN
+ , {Arg0Type, Arg0}
+ , {immediate, L}
+ | Rest]) ->
+ ArgSpec =
+ modifier_bits(Arg0Type) bor
+ (modifier_bits(immediate) bsl 2),
+ [?SWITCH_VN
+ , ArgSpec
+ , serialize_data(Arg0Type, Arg0)
+ , serialize_data(immediate, L)] ++ 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) ->
+ aeb_fate_encoding:serialize(Data).
+
+serialize_signature({Args, RetType}) ->
+ [serialize_type({tuple, Args}) |
+ serialize_type(RetType)].
+
+
+
+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_type(<<7, Rest/binary>>) ->
+ {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]).
+
+
+
+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]).
+
+
+
+%% -------------------------------------------------------------------
+%% 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),
+ to_bytecode(Rest2, Fun, Env2, [], Opts);
+to_bytecode([{mnemonic,_line, 'ELEMENT'}|Rest], Address, Env, Code, Opts) ->
+ 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 = 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);
+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([{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);
+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([{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);
+
+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,
+ case proplists:lookup(pp_opcodes, Opts) of
+ {pp_opcodes, true} ->
+ Ops = [C || {_Name, {_Sig, C}} <- maps:to_list(Funs)],
+ io:format("opcodes ~p~n", [Ops]);
+ none ->
+ ok
+ 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_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]) ->
+ {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),
+ {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.
+
+
+%% Type handling
+
+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_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) ->
+ case to_type(Tokens) of
+ {Type, [{',', _} | Rest]} ->
+ {MoreTypes, Rest2} = to_list_of_types(Rest),
+ {[Type|MoreTypes], Rest2};
+ {Type, [{']', _} | Rest]} ->
+ {[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)];
+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)];
+serialize_type(string) -> [7];
+serialize_type({variant, ListOfVariants}) ->
+ Size = length(ListOfVariants),
+ if Size < 256 ->
+ [8, Size | [serialize_type(T) || T <- ListOfVariants]]
+ end.
+
+
+%% -------------------------------------------------------------------
+%% Helper functions
+%% -------------------------------------------------------------------
+
+%% State handling
+
+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)}}
+ }.
+
+mk_hash(Id) ->
+ %% Use first 4 bytes of blake hash
+ {ok, <> } = eblake2: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).
+
+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.
+
+%% Symbol table handling
+
+lookup(Name, Symbols) ->
+ maps:get(Name, Symbols, Name).
diff --git a/src/aeb_fate_asm_scan.template b/src/aeb_fate_asm_scan.template
new file mode 100644
index 0000000..1fa686a
--- /dev/null
+++ b/src/aeb_fate_asm_scan.template
@@ -0,0 +1,142 @@
+%%% -*- 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]
+BASE58 = [123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]
+INT = {DIGIT}+
+HEX = 0x{HEXDIGIT}+
+HASH = #{BASE58}+
+WS = [\000-\s]
+ID = {LOWER}[a-zA-Z0-9_]*
+STRING = "[^"]*"
+BITS = (\!)?\<[\s01]*\>
+
+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)}}.
+-{INT} :
+ {token, {int, TokenLine, parse_int(TokenChars)}}.
+{HASH} :
+ {token, {address, TokenLine, parse_hash(TokenChars)}}.
+{STRING} :
+ {token, {string, TokenLine, list_to_binary(TokenChars)}}.
+{BITS} :
+ {token, {bits, TokenLine, bits(TokenChars)}}.
+
+
+%% Symbols
+\-\> : {token, {to, TokenLine}}.
+\: : {token, {to, TokenLine}}.
+
+\=\> : {token, {arrow, TokenLine}}.
+\(\| : {token, {start_variant, TokenLine}}.
+\|\) : {token, {end_variant, TokenLine}}.
+
+, : {token, {',', 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) ->
+ base58_to_address(Chars).
+
+scan(S) ->
+ string(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).
+
+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
new file mode 100644
index 0000000..e2308ea
--- /dev/null
+++ b/src/aeb_fate_data.erl
@@ -0,0 +1,208 @@
+%% FATE data representation.
+%%
+-include("aeb_fate_data.hrl").
+
+-module(aeb_fate_data).
+
+-type fate_integer() :: ?FATE_INTEGER_T.
+-type fate_boolean() :: ?FATE_BOOLEAN_T.
+-type fate_nil() :: ?FATE_NIL_T.
+-type fate_list() :: ?FATE_LIST_T.
+-type fate_unit() :: ?FATE_UNIT_T.
+-type fate_map() :: ?FATE_MAP_T.
+-type fate_string() :: ?FATE_STRING_T.
+-type fate_address() :: ?FATE_ADDRESS_T.
+
+-type fate_variant() :: ?FATE_VARIANT_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()
+ | fate_nil()
+ | fate_list()
+ | fate_unit()
+ | fate_tuple()
+ | fate_string()
+ | fate_address()
+ | fate_variant()
+ | fate_map()
+ | fate_type_type().
+
+-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
+ , 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_TRUE) -> "true";
+format(?FATE_FALSE) -> "false";
+format(?FATE_NIL) -> "[]";
+format(L) when ?IS_FATE_LIST(L) -> format_list(?FATE_LIST_VALUE(L));
+format(?FATE_UNIT) -> "()";
+format(?FATE_TUPLE(T)) ->
+ ["( ", lists:join(", ", [ format(E) || E <- erlang:tuple_to_list(T)]), " )"];
+format(S) when ?IS_FATE_STRING(S) -> [S];
+format(?FATE_BITS(B)) when B >= 0 ->
+ ["<", format_bits(B, "") , ">"];
+format(?FATE_BITS(B)) when B < 0 ->
+ ["!< ", format_nbits(-B-1, "") , " >"];
+format(?FATE_VARIANT(Size, Tag, T)) ->
+ ["(| ",
+ lists:join("| ", [integer_to_list(Size), integer_to_list(Tag) |
+ [format(make_tuple(T))]]),
+ " |)"];
+format(M) when ?IS_FATE_MAP(M) ->
+ ["{ ", format_kvs(maps:to_list(?FATE_MAP_VALUE(M))), " }"];
+format(?FATE_ADDRESS(Address)) -> ["#", address_to_base58(Address)];
+format(V) -> exit({not_a_fate_type, V}).
+
+format_bits(0, Acc) -> Acc;
+format_bits(N, Acc) ->
+ Bit = $0 + (N band 1),
+ format_bits(N bsr 1, [Bit|Acc]).
+
+format_nbits(0, Acc) -> Acc;
+format_nbits(N, Acc) ->
+ Bit = $1 - (N band 1),
+ format_nbits(N bsr 1, [Bit|Acc]).
+
+format_list(List) ->
+ ["[ ", lists:join(", ", [format(E) || E <- List]), " ]"].
+
+format_kvs(List) ->
+ lists:join(", ", [ [format(K), " => ", format(V)] || {K, V} <- List]).
+
+
+%% -- 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.
+
+address_to_base58(<>) ->
+ integer_to_base58(A).
+
+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/aeb_fate_encoding.erl b/src/aeb_fate_encoding.erl
new file mode 100644
index 0000000..d0e5c12
--- /dev/null
+++ b/src/aeb_fate_encoding.erl
@@ -0,0 +1,260 @@
+%% 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(aeb_fate_encoding).
+
+-export([ deserialize/1
+ , deserialize_one/1
+ , serialize/1
+ ]).
+
+-include("aeb_fate_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(aeb_fate_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}|_] = 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
+ 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 ->
+ aeser_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()) -> aeb_fate_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} = aeser_rlp:decode_one(Rest),
+ {?MAKE_FATE_INTEGER(-binary:decode_unsigned(Bint) - ?SMALL_INT_SIZE),
+ Rest2};
+deserialize2(<>) ->
+ {Bint, Rest2} = aeser_rlp:decode_one(Rest),
+ {?MAKE_FATE_INTEGER(binary:decode_unsigned(Bint) + ?SMALL_INT_SIZE),
+ Rest2};
+deserialize2(<>) ->
+ {Bint, Rest2} = aeser_rlp:decode_one(Rest),
+ {?FATE_BITS(-binary:decode_unsigned(Bint)), Rest2};
+deserialize2(<>) ->
+ {Bint, Rest2} = aeser_rlp:decode_one(Rest),
+ {?FATE_BITS(binary:decode_unsigned(Bint)), Rest2};
+deserialize2(<>) ->
+ {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} = aeser_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} = 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};
+deserialize2(<>) ->
+ {List, Rest1} = deserialize_elements(S, Rest),
+ {?FATE_TUPLE(list_to_tuple(List)), Rest1};
+deserialize2(<>) ->
+ {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};
+deserialize2(<>) ->
+ {List, Rest1} = deserialize_elements(S, Rest),
+ {?MAKE_FATE_LIST(List), Rest1};
+deserialize2(<>) ->
+ {BSize, Rest1} = aeser_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/aeb_fate_generate_ops.erl b/src/aeb_fate_generate_ops.erl
new file mode 100644
index 0000000..6c29b40
--- /dev/null
+++ b/src/aeb_fate_generate_ops.erl
@@ -0,0 +1,687 @@
+-module(aeb_fate_generate_ops).
+
+-export([ gen_and_halt/1
+ , generate/0
+ , generate_documentation/1
+ , test_asm_generator/1]).
+
+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),
+ gen_asm_pp(aeb_fate_pp, 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, ""}
+
+
+ , { '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."
+ ].
+
+
+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)].
+
+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, {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"
+ "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, io_lib:format(\"~~w\",[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]);
+ [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.
+
+
+%% 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/src/aebytecode.app.src b/src/aebytecode.app.src
index 53b095c..7116b7e 100644
--- a/src/aebytecode.app.src
+++ b/src/aebytecode.app.src
@@ -1,10 +1,13 @@
{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.1"},
{registered, []},
{applications,
[kernel,
- stdlib
+ stdlib,
+ eblake2,
+ aeserialization,
+ getopt
]},
{env,[]},
{modules, []},
diff --git a/src/aefateasm.erl b/src/aefateasm.erl
new file mode 100644
index 0000000..12183ce
--- /dev/null
+++ b/src/aefateasm.erl
@@ -0,0 +1,57 @@
+-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 = 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]);
+ false -> ok
+ end,
+ io:format("Code: ~0p~n", [BC]);
+ OutFile ->
+ aeb_fate_asm:assemble_file(File, OutFile, Opts)
+ end.
+
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..33b3989
--- /dev/null
+++ b/test/aeb_fate_asm_test.erl
@@ -0,0 +1,69 @@
+%%%-------------------------------------------------------------------
+%%% @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"
+ , "mapofmap"
+ , "immediates"
+ , "all_instructions"
+ ].
+
+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),
+ 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/aeb_serialize_test.erl b/test/aeb_serialize_test.erl
new file mode 100644
index 0000000..778a2f9
--- /dev/null
+++ b/test/aeb_serialize_test.erl
@@ -0,0 +1,83 @@
+%%%-------------------------------------------------------------------
+%%% @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_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)),
+ 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/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/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/immediates.fate b/test/asm_code/immediates.fate
new file mode 100644
index 0000000..3efcc1a
--- /dev/null
+++ b/test/asm_code/immediates.fate
@@ -0,0 +1,77 @@
+;; 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))
+
+
+FUNCTION address() : address
+ RETURNR #deadbeef
+
+;; Option(integer) = NONE | SOME(integer)
+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) |)
diff --git a/test/asm_code/jumpif.fate b/test/asm_code/jumpif.fate
new file mode 100644
index 0000000..c7fefed
--- /dev/null
+++ b/test/asm_code/jumpif.fate
@@ -0,0 +1,11 @@
+;; CONTRACT jumpif
+FUNCTION skip(integer, integer) : integer
+;; 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/map.fate b/test/asm_code/map.fate
new file mode 100644
index 0000000..d841ea3
--- /dev/null
+++ b/test/asm_code/map.fate
@@ -0,0 +1,33 @@
+;; 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/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}}
+
+
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..aae9102
--- /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
+ INCA
+ INCA
+ RETURN
+
+FUNCTION call(integer) -> integer
+ INCA
+ CALL inc
+ INCA
+ RETURN
+
+
+FUNCTION tailcall(integer) -> integer
+ INCA
+ CALL_T inc
+
+FUNCTION remote_call(integer) : integer
+ PUSH arg0
+ CALL_R remote.add_five
+ INCA
+ 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..6240dca
--- /dev/null
+++ b/test/asm_code/tuple.fate
@@ -0,0 +1,35 @@
+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
+ RETURN
+
+FUNCTION make_5tuple(integer, integer, integer, integer, integer):
+ {tuple, [integer, integer, integer, integer, integer]}
+;; BB : 0
+ PUSH arg0
+ PUSH arg1
+ PUSH arg2
+ PUSH arg3
+ PUSH arg4
+ TUPLE 5
+ RETURN
+
+FUNCTION element1(integer, integer): integer
+;; BB : 0
+ PUSH arg0
+ PUSH arg1
+ TUPLE 2
+ ELEMENT integer a 1 a
+ RETURN
+
+FUNCTION element({tuple, [integer, integer]}, integer): integer
+;; BB : 0
+ ELEMENT integer a arg1 arg0
+ RETURN