Initial handling of Fate opcodes.
This commit is contained in:
parent
720510a24d
commit
5878ef3e9b
107
include/aefa_opcodes.hrl
Normal file
107
include/aefa_opcodes.hrl
Normal file
@ -0,0 +1,107 @@
|
||||
|
||||
%% FATE opcodes
|
||||
-define('NOP' , 16#00).
|
||||
-define('RETURN' , 16#01).
|
||||
-define('CALL' , 16#02).
|
||||
-define('CALL_R' , 16#03).
|
||||
-define('CALL_T' , 16#04).
|
||||
-define('CALL_TR' , 16#05).
|
||||
-define('JUMP' , 16#06).
|
||||
-define('JUMPI' , 16#07).
|
||||
-define('SWITCH' , 16#08).
|
||||
-define('PUSH' , 16#09).
|
||||
-define('DUP' , 16#0a).
|
||||
-define('POP' , 16#0b).
|
||||
-define('STORE' , 16#10).
|
||||
-define('ADD' , 16#11).
|
||||
-define('MUL' , 16#12).
|
||||
-define('SUB' , 16#13).
|
||||
-define('DIV' , 16#14).
|
||||
-define('MOD' , 16#15).
|
||||
-define('POW' , 16#16).
|
||||
-define('LT' , 16#17).
|
||||
-define('GT' , 16#18).
|
||||
-define('EQ' , 16#19).
|
||||
-define('ELT' , 16#1a).
|
||||
-define('EGT' , 16#1b).
|
||||
-define('NEQ' , 16#1c).
|
||||
-define('AND' , 16#1d).
|
||||
-define('OR' , 16#1e).
|
||||
-define('NOT' , 16#1f).
|
||||
-define('TUPLE' , 16#20).
|
||||
-define('ELEMENT' , 16#21).
|
||||
-define('MAP_EMPTY' , 16#22).
|
||||
-define('MAP_LOOKUP' , 16#23).
|
||||
-define('MAP_UPDATE' , 16#24).
|
||||
-define('MAP_DELETE' , 16#25).
|
||||
-define('MAP_MEMBER' , 16#26).
|
||||
-define('MAP_FROM_LIST' , 16#27).
|
||||
-define('NIL' , 16#28).
|
||||
-define('IS_NIL' , 16#29).
|
||||
-define('CONS' , 16#2a).
|
||||
-define('HD' , 16#2b).
|
||||
-define('TL' , 16#2c).
|
||||
-define('LENGTH' , 16#2d).
|
||||
-define('STR_EQ' , 16#2e).
|
||||
-define('STR_JOIN' , 16#2f).
|
||||
-define('ADDR_TO_STR' , 16#30).
|
||||
-define('STR_REVERSE' , 16#31).
|
||||
-define('INT_TO_ADDR' , 16#32).
|
||||
-define('VARIANT' , 16#33).
|
||||
-define('VARIANT_TEST' , 16#34).
|
||||
-define('VARIANT_ELEMENT', 16#35).
|
||||
-define('BITS_NONE' , 16#36).
|
||||
-define('BITS_ALL' , 16#37).
|
||||
-define('BITS_SET' , 16#38).
|
||||
-define('BITS_CLEAR' , 16#39).
|
||||
-define('BITS_TEST' , 16#3a).
|
||||
-define('BITS_SUM' , 16#3b).
|
||||
-define('BITS_OR' , 16#3c).
|
||||
-define('BITS_AND' , 16#3d).
|
||||
-define('BITS_DIFF' , 16#3e).
|
||||
-define('ADDRESS' , 16#3f).
|
||||
-define('BALANCE' , 16#40).
|
||||
-define('ORIGIN' , 16#41).
|
||||
-define('CALLER' , 16#42).
|
||||
-define('GASPRICE' , 16#43).
|
||||
-define('BLOCKHASH' , 16#44).
|
||||
-define('BENEFICIARY' , 16#45).
|
||||
-define('TIMESTAMP' , 16#46).
|
||||
-define('NUMBER' , 16#47).
|
||||
-define('DIFFICULTY' , 16#48).
|
||||
-define('GASLIMIT' , 16#49).
|
||||
-define('GAS' , 16#4a).
|
||||
-define('LOG0' , 16#4b).
|
||||
-define('LOG1' , 16#4c).
|
||||
-define('LOG2' , 16#4d).
|
||||
-define('LOG3' , 16#4e).
|
||||
-define('LOG4' , 16#4f).
|
||||
-define('ABORT' , 16#50).
|
||||
-define('EXIT' , 16#51).
|
||||
-define('DEACTIVATE' , 16#52).
|
||||
-define('INT_TO_STR' , 16#53).
|
||||
|
||||
|
||||
-define('SPEND' , 16#56).
|
||||
-define('ORACLE_REGISTER', 16#57).
|
||||
-define('ORACLE_QUERY' , 16#58).
|
||||
-define('ORACLE_RESPOND' , 16#59).
|
||||
-define('ORACLE_EXTEND' , 16#5a).
|
||||
-define('ORACLE_GET_ANSWER', 16#5b).
|
||||
-define('ORACLE_GET_QUESTION', 16#5c).
|
||||
-define('ORACLE_QUERY_FEE', 16#5d).
|
||||
-define('AENS_RESOLVE' , 16#5e).
|
||||
-define('AENS_PRECLAIM' , 16#5f).
|
||||
-define('AENS_CLAIM' , 16#60).
|
||||
-define('AENS_UPDATE' , 16#61).
|
||||
-define('AENS_TRANSFER' , 16#62).
|
||||
-define('AENS_REVOKE' , 16#63).
|
||||
-define('ECVERIFY' , 16#64).
|
||||
-define('SHA3' , 16#65).
|
||||
-define('SHA256' , 16#66).
|
||||
-define('BLAKE2B' , 16#67).
|
||||
|
||||
-define('EXTEND' , 16#ff).
|
||||
|
||||
-define( COMMENT(X), {comment, X}).
|
||||
|
140
src/aefa_asm.erl
Normal file
140
src/aefa_asm.erl
Normal file
@ -0,0 +1,140 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @copyright (C) 2019, Aeternity Anstalt
|
||||
%%% @doc Assembler for Fate machine code.
|
||||
%%%
|
||||
%%% Assembler code can be read from a file.
|
||||
%%% The assembler has the following format
|
||||
%%% Comments start with 2 semicolons and runs till end of line
|
||||
%%% ;; This is a comment
|
||||
%%% Opcode mnemonics start with an upper case letter.
|
||||
%%% DUP
|
||||
%%% Identifiers start with a lower case letter
|
||||
%%% an_identifier
|
||||
%%% Immediates can be of 9 types:
|
||||
%%% 1. Integers
|
||||
%%% 42
|
||||
%%% -2374683271468723648732648736498712634876147
|
||||
%%% 2. Hexadecimal integers starting with 0x
|
||||
%%% 0x0deadbeef0
|
||||
%%% 3. addresses, a 256-bit hash strings starting with #
|
||||
%%% followed by up to 64 hex chars
|
||||
%%% #00000deadbeef
|
||||
%%% 4. Boolean
|
||||
%%% true
|
||||
%%% false
|
||||
%%% 5. Strings
|
||||
%%% "Hello"
|
||||
%%% 6. Empty map
|
||||
%%% {}
|
||||
%%% 7. Lists
|
||||
%%% []
|
||||
%%% [1, 2]
|
||||
%%% 8. Bit field
|
||||
%%% <000>
|
||||
%%% <1010>
|
||||
%%% <>
|
||||
%%% !<>
|
||||
%%% 9. Tuples
|
||||
%%% ()
|
||||
%%% (1, "foo")
|
||||
%%% @end
|
||||
%%% Created : 21 Dec 2017
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(aefa_asm).
|
||||
|
||||
-export([ file/2
|
||||
, pp/1
|
||||
, to_hexstring/1
|
||||
]).
|
||||
|
||||
-include_lib("aebytecode/include/aefa_opcodes.hrl").
|
||||
|
||||
|
||||
pp(Asm) ->
|
||||
Listing = format(Asm),
|
||||
io:format("~s~n", [Listing]).
|
||||
|
||||
format(Asm) -> format(Asm, 0).
|
||||
|
||||
format([{comment, Comment} | Rest], Address) ->
|
||||
";; " ++ Comment ++ "\n" ++ format(Rest, Address);
|
||||
format([Mnemonic | Rest], Address) ->
|
||||
_Op = aefa_opcodes:m_to_op(Mnemonic),
|
||||
" " ++ atom_to_list(Mnemonic) ++ "\n"
|
||||
++ format(Rest, Address + 1);
|
||||
format([],_) -> [].
|
||||
|
||||
|
||||
|
||||
|
||||
file(Filename, Options) ->
|
||||
{ok, File} = file:read_file(Filename),
|
||||
{ok, Tokens, _} = aefa_asm_scan:scan(binary_to_list(File)),
|
||||
|
||||
case proplists:lookup(pp_tokens, Options) of
|
||||
{pp_tokens, true} ->
|
||||
io:format("Tokens ~p~n",[Tokens]);
|
||||
none ->
|
||||
ok
|
||||
end,
|
||||
|
||||
ByteList = to_bytecode(Tokens, 0, #{}, [], Options),
|
||||
|
||||
case proplists:lookup(pp_hex_string, Options) of
|
||||
{pp_hex_string, true} ->
|
||||
io:format("Code: ~s~n",[to_hexstring(ByteList)]);
|
||||
none ->
|
||||
ok
|
||||
end,
|
||||
|
||||
|
||||
list_to_binary(ByteList).
|
||||
|
||||
to_hexstring(ByteList) ->
|
||||
"0x" ++ lists:flatten(
|
||||
[io_lib:format("~2.16.0b", [X])
|
||||
|| X <- ByteList]).
|
||||
|
||||
|
||||
to_bytecode([{mnemonic,_line, Op}|Rest], Address, Env, Code, Opts) ->
|
||||
OpCode = aefa_opcodes:m_to_op(Op),
|
||||
OpSize = aefa_opcodes:op_size(OpCode),
|
||||
to_bytecode(Rest, Address + OpSize, Env, [OpCode|Code], Opts);
|
||||
to_bytecode([{int,_line, Int}|Rest], Address, Env, Code, Opts) ->
|
||||
to_bytecode(Rest, Address, Env, [Int|Code], Opts);
|
||||
to_bytecode([{hash,_line, Hash}|Rest], Address, Env, Code, Opts) ->
|
||||
to_bytecode(Rest, Address, Env, [Hash|Code], Opts);
|
||||
to_bytecode([{id,_line, ID}|Rest], Address, Env, Code, Opts) ->
|
||||
to_bytecode(Rest, Address, Env, [{ref, ID}|Code], Opts);
|
||||
to_bytecode([{label,_line, Label}|Rest], Address, Env, Code, Opts) ->
|
||||
to_bytecode(Rest, Address, Env#{Label => Address}, Code, Opts);
|
||||
to_bytecode([], _Address, Env, Code, Opts) ->
|
||||
case proplists:lookup(pp_opcodes, Opts) of
|
||||
{pp_opcodes, true} ->
|
||||
io:format("opcodes ~p~n", [lists:reverse(Code)]);
|
||||
none ->
|
||||
ok
|
||||
end,
|
||||
|
||||
PatchedCode = resolve_refs(Code, Env, []),
|
||||
case proplists:lookup(pp_patched_code, Opts) of
|
||||
{pp_patched_code, true} ->
|
||||
io:format("Patched Code: ~p~n", [PatchedCode]);
|
||||
none ->
|
||||
ok
|
||||
end,
|
||||
|
||||
expand_args(PatchedCode).
|
||||
|
||||
%% Also reverses the code (back to unreversed state).
|
||||
resolve_refs([{ref, ID} | Rest], Env, Code) ->
|
||||
Address = maps:get(ID, Env),
|
||||
resolve_refs(Rest, Env, [Address | Code]);
|
||||
resolve_refs([Op | Rest], Env, Code) ->
|
||||
resolve_refs(Rest, Env, [Op | Code]);
|
||||
resolve_refs([],_Env, Code) -> Code.
|
||||
|
||||
expand_args([OP | Rest]) ->
|
||||
[OP | expand_args(Rest)];
|
||||
expand_args([]) -> [].
|
169
src/aefa_asm_scan.xrl
Normal file
169
src/aefa_asm_scan.xrl
Normal file
@ -0,0 +1,169 @@
|
||||
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @copyright (C) 2019, aeternity Anstalt
|
||||
%%% @doc
|
||||
%%% Handling FATE code.
|
||||
%%% @end
|
||||
%%% Created : 9 Jan 2019
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
Definitions.
|
||||
DIGIT = [0-9]
|
||||
HEXDIGIT = [0-9a-fA-F]
|
||||
LOWER = [a-z_]
|
||||
UPPER = [A-Z]
|
||||
INT = {DIGIT}+
|
||||
HEX = 0x{HEXDIGIT}+
|
||||
HASH = #{HEXDIGIT}+
|
||||
WS = [\000-\s]
|
||||
ID = {LOWER}[a-zA-Z0-9_]*
|
||||
|
||||
Rules.
|
||||
{ID} : {token, {id, TokenLine, TokenChars }}.
|
||||
|
||||
NOP : {token, {mnemonic, TokenLine, 'NOP'}}.
|
||||
|
||||
RETURN : {token, {mnemonic, TokenLine, 'RETURN'}}.
|
||||
CALL : {token, {mnemonic, TokenLine, 'CALL'}}.
|
||||
CALL_R : {token, {mnemonic, TokenLine, 'CALL_R'}}.
|
||||
CALL_T : {token, {mnemonic, TokenLine, 'CALL_T'}}.
|
||||
CALL_TR : {token, {mnemonic, TokenLine, 'CALL_TR'}}.
|
||||
JUMP : {token, {mnemonic, TokenLine, 'JUMP'}}.
|
||||
JUMPI : {token, {mnemonic, TokenLine, 'JUMPI'}}.
|
||||
SWITCH : {token, {mnemonic, TokenLine, 'SWITCH'}}.
|
||||
|
||||
PUSH : {token, {mnemonic, TokenLine, 'PUSH'}}.
|
||||
DUP : {token, {mnemonic, TokenLine, 'DUP'}}.
|
||||
POP : {token, {mnemonic, TokenLine, 'POP'}}.
|
||||
|
||||
STORE : {token, {mnemonic, TokenLine, 'STORE'}}.
|
||||
|
||||
ADD : {token, {mnemonic, TokenLine, 'ADD'}}.
|
||||
MUL : {token, {mnemonic, TokenLine, 'MUL'}}.
|
||||
SUB : {token, {mnemonic, TokenLine, 'SUB'}}.
|
||||
DIV : {token, {mnemonic, TokenLine, 'DIV'}}.
|
||||
MOD : {token, {mnemonic, TokenLine, 'MOD'}}.
|
||||
POW : {token, {mnemonic, TokenLine, 'POW'}}.
|
||||
|
||||
LT : {token, {mnemonic, TokenLine, 'LT'}}.
|
||||
GT : {token, {mnemonic, TokenLine, 'GT'}}.
|
||||
EQ : {token, {mnemonic, TokenLine, 'EQ'}}.
|
||||
ELT : {token, {mnemonic, TokenLine, 'ELT'}}.
|
||||
EGT : {token, {mnemonic, TokenLine, 'EGT'}}.
|
||||
NEQ : {token, {mnemonic, TokenLine, 'NEQ'}}.
|
||||
|
||||
AND : {token, {mnemonic, TokenLine, 'AND'}}.
|
||||
OR : {token, {mnemonic, TokenLine, 'OR'}}.
|
||||
NOT : {token, {mnemonic, TokenLine, 'NOT'}}.
|
||||
|
||||
TUPLE : {token, {mnemonic, TokenLine, 'TUPLE'}}.
|
||||
ELEMENT : {token, {mnemonic, TokenLine, 'ELEMENT'}}.
|
||||
|
||||
MAP_EMPTY : {token, {mnemonic, TokenLine, 'MAP_EMPTY'}}.
|
||||
MAP_LOOKUP : {token, {mnemonic, TokenLine, 'MAP_LOOKUP'}}.
|
||||
MAP_UPDATE : {token, {mnemonic, TokenLine, 'MAP_UPDATE'}}.
|
||||
MAP_MEMBER : {token, {mnemonic, TokenLine, 'MAP_MEMBER'}}.
|
||||
MAP_FROM_LIST : {token, {mnemonic, TokenLine, 'MAP_FROM_LIST'}}.
|
||||
|
||||
NIL : {token, {mnemonic, TokenLine, 'NIL'}}.
|
||||
IS_NIL : {token, {mnemonic, TokenLine, 'IS_NIL'}}.
|
||||
CONS : {token, {mnemonic, TokenLine, 'CONS'}}.
|
||||
HD : {token, {mnemonic, TokenLine, 'HD'}}.
|
||||
TL : {token, {mnemonic, TokenLine, 'TL'}}.
|
||||
LENGTH : {token, {mnemonic, TokenLine, 'LENGTH'}}.
|
||||
|
||||
STR_EQ : {token, {mnemonic, TokenLine, 'STR_EQ'}}.
|
||||
STR_JOIN : {token, {mnemonic, TokenLine, 'STR_JOIN'}}.
|
||||
INT_TO_STR : {token, {mnemonic, TokenLine, 'INT_TO_STR'}}.
|
||||
ADDR_TO_STR : {token, {mnemonic, TokenLine, 'ADDR_TO_STR'}}.
|
||||
STR_REVERSE : {token, {mnemonic, TokenLine, 'STR_REVERSE'}}.
|
||||
|
||||
INT_TO_ADDR : {token, {mnemonic, TokenLine, 'INT_TO_ADDR'}}.
|
||||
|
||||
VARIANT : {token, {mnemonic, TokenLine, 'VARIANT'}}.
|
||||
VARIANT_TEST : {token, {mnemonic, TokenLine, 'VARIANT_TEST'}}.
|
||||
VARIANT_ELEMENT : {token, {mnemonic, TokenLine, 'VARIANT_ELEMENT'}}.
|
||||
|
||||
BITS_NONE : {token, {mnemonic, TokenLine, 'BITS_NONE'}}.
|
||||
BITS_ALL : {token, {mnemonic, TokenLine, 'BITS_ALL'}}.
|
||||
BITS_SET : {token, {mnemonic, TokenLine, 'BITS_SET'}}.
|
||||
BITS_CLEAR : {token, {mnemonic, TokenLine, 'BITS_CLEAR'}}.
|
||||
BITS_TEST : {token, {mnemonic, TokenLine, 'BITS_TEST'}}.
|
||||
BITS_SUM : {token, {mnemonic, TokenLine, 'BITS_SUM'}}.
|
||||
BITS_OR : {token, {mnemonic, TokenLine, 'BITS_OR'}}.
|
||||
BITS_AND : {token, {mnemonic, TokenLine, 'BITS_AND'}}.
|
||||
BITS_DIFF : {token, {mnemonic, TokenLine, 'BITS_DIFF'}}.
|
||||
|
||||
|
||||
ADDRESS : {token, {mnemonic, TokenLine, 'ADDRESS'}}.
|
||||
BALANCE : {token, {mnemonic, TokenLine, 'BALANCE'}}.
|
||||
ORIGIN : {token, {mnemonic, TokenLine, 'ORIGIN'}}.
|
||||
CALLER : {token, {mnemonic, TokenLine, 'CALLER'}}.
|
||||
GASPRICE : {token, {mnemonic, TokenLine, 'GASPRICE'}}.
|
||||
BLOCKHASH : {token, {mnemonic, TokenLine, 'BLOCKHASH'}}.
|
||||
BENEFICIARY : {token, {mnemonic, TokenLine, 'BENEFICIARY'}}.
|
||||
TIMESTAMP : {token, {mnemonic, TokenLine, 'TIMESTAMP'}}.
|
||||
NUMBER : {token, {mnemonic, TokenLine, 'NUMBER'}}.
|
||||
DIFFICULTY : {token, {mnemonic, TokenLine, 'DIFFICULTY'}}.
|
||||
GASLIMIT : {token, {mnemonic, TokenLine, 'GASLIMIT'}}.
|
||||
GAS : {token, {mnemonic, TokenLine, 'GAS'}}.
|
||||
LOG0 : {token, {mnemonic, TokenLine, 'LOG0'}}.
|
||||
LOG1 : {token, {mnemonic, TokenLine, 'LOG1'}}.
|
||||
LOG2 : {token, {mnemonic, TokenLine, 'LOG2'}}.
|
||||
LOG3 : {token, {mnemonic, TokenLine, 'LOG3'}}.
|
||||
LOG4 : {token, {mnemonic, TokenLine, 'LOG4'}}.
|
||||
ABORT : {token, {mnemonic, TokenLine, 'ABORT'}}.
|
||||
EXIT : {token, {mnemonic, TokenLine, 'EXIT'}}.
|
||||
DEACTIVATE : {token, {mnemonic, TokenLine, 'DEACTIVATE'}}.
|
||||
COMMENT : {token, {mnemonic, TokenLine, 'COMMENT'}}.
|
||||
{ID} :
|
||||
{token, {id, TokenLine, TokenChars}}.
|
||||
{HEX} :
|
||||
{token, {int, TokenLine, parse_hex(TokenChars)}}.
|
||||
{INT} :
|
||||
{token, {int, TokenLine, parse_int(TokenChars)}}.
|
||||
{HASH} :
|
||||
{token, {hash, TokenLine, parse_hash(TokenChars)}}.
|
||||
|
||||
|
||||
%% Symbols
|
||||
, : {token, {',', TokenLine}}.
|
||||
\. : {token, {'.', TokenLine}}.
|
||||
\( : {token, {'(', TokenLine}}.
|
||||
\) : {token, {')', TokenLine}}.
|
||||
\[ : {token, {'[', TokenLine}}.
|
||||
\] : {token, {']', TokenLine}}.
|
||||
{ : {token, {'{', TokenLine}}.
|
||||
} : {token, {'}', TokenLine}}.
|
||||
|
||||
|
||||
%% Whitespace ignore
|
||||
{WS} : skip_token.
|
||||
|
||||
%% Comments (TODO: nested comments)
|
||||
;;.* : skip_token.
|
||||
|
||||
. : {error, "Unexpected token: " ++ TokenChars}.
|
||||
|
||||
Erlang code.
|
||||
|
||||
-export([scan/1]).
|
||||
|
||||
-dialyzer({nowarn_function, yyrev/2}).
|
||||
|
||||
-ignore_xref([format_error/1, string/2, token/2, token/3, tokens/2, tokens/3]).
|
||||
|
||||
-include_lib("aebytecode/include/aeb_opcodes.hrl").
|
||||
|
||||
|
||||
parse_hex("0x" ++ Chars) -> list_to_integer(Chars, 16).
|
||||
|
||||
parse_int(Chars) -> list_to_integer(Chars).
|
||||
|
||||
parse_hash("#" ++ Chars) ->
|
||||
N = list_to_integer(Chars, 16),
|
||||
<<N:256>>.
|
||||
|
||||
scan(S) ->
|
||||
string(S).
|
||||
|
32
src/aefa_opcode.erl
Normal file
32
src/aefa_opcode.erl
Normal file
@ -0,0 +1,32 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @copyright (C) 2019, Aeternity Anstalt
|
||||
%%% @doc
|
||||
%%% Opcodes
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(aefa_opcode).
|
||||
|
||||
-export([ mnemonic/1
|
||||
, m_to_op/1
|
||||
, opcode/1
|
||||
]).
|
||||
|
||||
-include_lib("aebytecode/include/aefa_opcodes.hrl").
|
||||
|
||||
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
|
||||
opcode(X) when X >= 0, X =< 255 -> X;
|
||||
opcode({comment,X}) -> ?COMMENT(X).
|
||||
|
||||
mnemonic(?NOP) -> 'NOP' ;
|
||||
mnemonic({comment,_}) -> 'COMMENT' .
|
||||
|
||||
m_to_op('NOP') -> ?NOP ;
|
||||
m_to_op('COMMENT') -> ?COMMENT("") ;
|
||||
m_to_op(Data) when 0=<Data, Data=<255
|
||||
-> Data .
|
||||
|
Loading…
x
Reference in New Issue
Block a user