First iteration of assembler.
This commit is contained in:
parent
5878ef3e9b
commit
c4adf27a6e
@ -9,3 +9,11 @@
|
||||
{plt_apps, all_deps},
|
||||
{base_plt_apps, [erts, kernel, stdlib]}
|
||||
]}.
|
||||
|
||||
{relx, [{release, {aessembler, "0.0.1"},
|
||||
[aebytecode]},
|
||||
|
||||
{dev_mode, true},
|
||||
{include_erts, false},
|
||||
|
||||
{extended_start_script, true}]}.
|
@ -60,7 +60,7 @@ format(Asm) -> format(Asm, 0).
|
||||
format([{comment, Comment} | Rest], Address) ->
|
||||
";; " ++ Comment ++ "\n" ++ format(Rest, Address);
|
||||
format([Mnemonic | Rest], Address) ->
|
||||
_Op = aefa_opcodes:m_to_op(Mnemonic),
|
||||
_Op = aefa_opcode:m_to_op(Mnemonic),
|
||||
" " ++ atom_to_list(Mnemonic) ++ "\n"
|
||||
++ format(Rest, Address + 1);
|
||||
format([],_) -> [].
|
||||
@ -79,7 +79,9 @@ file(Filename, Options) ->
|
||||
ok
|
||||
end,
|
||||
|
||||
ByteList = to_bytecode(Tokens, 0, #{}, [], Options),
|
||||
Env = to_bytecode(Tokens, none, #{}, [], Options),
|
||||
|
||||
ByteList = serialize(Env),
|
||||
|
||||
case proplists:lookup(pp_hex_string, Options) of
|
||||
{pp_hex_string, true} ->
|
||||
@ -88,19 +90,27 @@ file(Filename, Options) ->
|
||||
ok
|
||||
end,
|
||||
|
||||
{Env, list_to_binary(ByteList)}.
|
||||
|
||||
list_to_binary(ByteList).
|
||||
serialize(Env) ->
|
||||
%% TODO: add serialization of immediates
|
||||
%% TODO: add serialization of function definitions
|
||||
Code = [C || {_Name, {_Sig, C}} <- maps:to_list(Env)],
|
||||
Code.
|
||||
|
||||
to_hexstring(ByteList) ->
|
||||
"0x" ++ lists:flatten(
|
||||
[io_lib:format("~2.16.0b", [X])
|
||||
|| X <- ByteList]).
|
||||
|
||||
|
||||
to_bytecode([{function,_line, 'FUNCTION'}|Rest], Address, Env, Code, Opts) ->
|
||||
Env2 = insert_fun(Address, Code, Env),
|
||||
{Fun, Rest2} = to_fun_def(Rest),
|
||||
to_bytecode(Rest2, Fun, Env2, [], Opts);
|
||||
to_bytecode([{mnemonic,_line, 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);
|
||||
OpCode = aefa_opcode:m_to_op(Op),
|
||||
%% TODO: arguments
|
||||
to_bytecode(Rest, Address, 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) ->
|
||||
@ -109,23 +119,63 @@ 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) ->
|
||||
to_bytecode([], Address, Env, Code, Opts) ->
|
||||
Env2 = insert_fun(Address, Code, Env),
|
||||
case proplists:lookup(pp_opcodes, Opts) of
|
||||
{pp_opcodes, true} ->
|
||||
io:format("opcodes ~p~n", [lists:reverse(Code)]);
|
||||
Ops = [C || {_Name, {_Sig, C}} <- maps:to_list(Env2)],
|
||||
io:format("opcodes ~p~n", [Ops]);
|
||||
none ->
|
||||
ok
|
||||
end,
|
||||
Env2.
|
||||
|
||||
|
||||
to_fun_def([{id, _, Name}, {'(', _} | Rest]) ->
|
||||
{ArgsType, [{'->', _} | Rest2]} = to_arg_types(Rest),
|
||||
{RetType, Rest3} = to_type(Rest2),
|
||||
{{Name, ArgsType, RetType}, Rest3}.
|
||||
|
||||
to_arg_types([{')', _} | Rest]) -> {[], Rest};
|
||||
to_arg_types(Tokens) ->
|
||||
case to_type(Tokens) of
|
||||
{Type, [{',', _} | Rest]} ->
|
||||
{MoreTypes, Rest2} = to_arg_types(Rest),
|
||||
{[Type|MoreTypes], Rest2};
|
||||
{Type, [{')', _} | Rest]} ->
|
||||
{[Type], Rest}
|
||||
end.
|
||||
|
||||
to_type([{id, _, "integer"} | Rest]) -> {integer, Rest};
|
||||
to_type([{id, _, "boolean"} | Rest]) -> {boolean, Rest};
|
||||
to_type([{id, _, "string"} | Rest]) -> {string, Rest};
|
||||
to_type([{id, _, "address"} | Rest]) -> {address, Rest};
|
||||
to_type([{id, _, "bits"} | Rest]) -> {bits, Rest};
|
||||
to_type([{'{', _}, {id, _, "list"}, {',', _} | Rest]) ->
|
||||
%% TODO: Error handling
|
||||
{ListType, [{'}', _}| Rest2]} = to_type(Rest),
|
||||
{{list, ListType}, Rest2};
|
||||
to_type([{'{', _}, {id, _, "tuple"}, {',', _}, {'[', _} | Rest]) ->
|
||||
%% TODO: Error handling
|
||||
{ElementTypes, [{'}', _}| Rest2]} = to_list_of_types(Rest),
|
||||
{{tuple, ElementTypes}, Rest2};
|
||||
to_type([{'{', _}, {id, _, "map"}, {',', _} | Rest]) ->
|
||||
%% TODO: Error handling
|
||||
{KeyType, [{',', _}| Rest2]} = to_type(Rest),
|
||||
{ValueType, [{'}', _}| Rest3]} = to_type(Rest2),
|
||||
{{map, KeyType, ValueType}, Rest3}.
|
||||
|
||||
to_list_of_types([{']', _} | Rest]) -> {[], Rest};
|
||||
to_list_of_types(Tokens) ->
|
||||
case to_type(Tokens) of
|
||||
{Type, [{',', _} | Rest]} ->
|
||||
{MoreTypes, Rest2} = to_list_of_types(Rest),
|
||||
{[Type|MoreTypes], Rest2};
|
||||
{Type, [{']', _} | Rest]} ->
|
||||
{[Type], Rest}
|
||||
end.
|
||||
|
||||
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) ->
|
||||
@ -138,3 +188,7 @@ resolve_refs([],_Env, Code) -> Code.
|
||||
expand_args([OP | Rest]) ->
|
||||
[OP | expand_args(Rest)];
|
||||
expand_args([]) -> [].
|
||||
|
||||
insert_fun(none, [], Env) -> Env;
|
||||
insert_fun({Name, Type, RetType}, Code, Env) ->
|
||||
Env#{Name => {{Type, RetType}, lists:reverse(Code)}}.
|
||||
|
@ -18,13 +18,15 @@ HASH = #{HEXDIGIT}+
|
||||
WS = [\000-\s]
|
||||
ID = {LOWER}[a-zA-Z0-9_]*
|
||||
|
||||
Rules.
|
||||
{ID} : {token, {id, TokenLine, TokenChars }}.
|
||||
|
||||
NOP : {token, {mnemonic, TokenLine, 'NOP'}}.
|
||||
Rules.
|
||||
%%{ID} : {token, {id, TokenLine, TokenChars }}.
|
||||
|
||||
RETURN : {token, {mnemonic, TokenLine, 'RETURN'}}.
|
||||
CALL : {token, {mnemonic, TokenLine, 'CALL'}}.
|
||||
FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}.
|
||||
NOP : {token, {mnemonic, TokenLine, 'NOP'}}.
|
||||
|
||||
CALL_R : {token, {mnemonic, TokenLine, 'CALL_R'}}.
|
||||
CALL_T : {token, {mnemonic, TokenLine, 'CALL_T'}}.
|
||||
CALL_TR : {token, {mnemonic, TokenLine, 'CALL_TR'}}.
|
||||
@ -127,14 +129,15 @@ COMMENT : {token, {mnemonic, TokenLine, 'COMMENT'}}.
|
||||
|
||||
|
||||
%% Symbols
|
||||
\-\> : {token, {'->', TokenLine}}.
|
||||
, : {token, {',', TokenLine}}.
|
||||
\. : {token, {'.', TokenLine}}.
|
||||
\( : {token, {'(', TokenLine}}.
|
||||
\) : {token, {')', TokenLine}}.
|
||||
\[ : {token, {'[', TokenLine}}.
|
||||
\] : {token, {']', TokenLine}}.
|
||||
{ : {token, {'{', TokenLine}}.
|
||||
} : {token, {'}', TokenLine}}.
|
||||
\{ : {token, {'{', TokenLine}}.
|
||||
\} : {token, {'}', TokenLine}}.
|
||||
|
||||
|
||||
%% Whitespace ignore
|
||||
@ -153,7 +156,7 @@ Erlang code.
|
||||
|
||||
-ignore_xref([format_error/1, string/2, token/2, token/3, tokens/2, tokens/3]).
|
||||
|
||||
-include_lib("aebytecode/include/aeb_opcodes.hrl").
|
||||
-include_lib("aebytecode/include/aefa_opcodes.hrl").
|
||||
|
||||
|
||||
parse_hex("0x" ++ Chars) -> list_to_integer(Chars, 16).
|
||||
|
@ -22,11 +22,13 @@
|
||||
opcode(X) when X >= 0, X =< 255 -> X;
|
||||
opcode({comment,X}) -> ?COMMENT(X).
|
||||
|
||||
mnemonic(?NOP) -> 'NOP' ;
|
||||
mnemonic({comment,_}) -> 'COMMENT' .
|
||||
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 .
|
||||
m_to_op('NOP') -> ?NOP ;
|
||||
m_to_op('COMMENT') -> ?COMMENT("") ;
|
||||
m_to_op('RETURN') -> ?RETURN ;
|
||||
m_to_op('PUSH') -> ?PUSH ;
|
||||
m_to_op('JUMP') -> ?JUMP ;
|
||||
m_to_op(Data) when 0=<Data, Data=<255 -> Data.
|
||||
|
||||
|
8
test/asm_code/identity.fate
Normal file
8
test/asm_code/identity.fate
Normal file
@ -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", []).
|
23
test/asm_code/test.fate
Normal file
23
test/asm_code/test.fate
Normal file
@ -0,0 +1,23 @@
|
||||
;; CONTRACT: Test
|
||||
FUNCTION id(integer) -> integer
|
||||
RETURN
|
||||
|
||||
FUNCTION jumps() -> integer
|
||||
PUSH 0
|
||||
JUMP 3
|
||||
NOP
|
||||
JUMP 2
|
||||
NOP
|
||||
RETURN
|
||||
NOP
|
||||
JUMP 1
|
||||
|
||||
FUNCTION inc(integer) -> integer
|
||||
INC arg0
|
||||
INC
|
||||
RETURN
|
||||
|
||||
;; Test the code from the shell
|
||||
;; _build/default/rel/aessembler/bin/aessembler console
|
||||
|
||||
;; aeb_aefa:file("../../../../test/asm_code/test.fate", []).
|
Loading…
x
Reference in New Issue
Block a user