diff --git a/rebar.config b/rebar.config index cfd8b45..db69d34 100644 --- a/rebar.config +++ b/rebar.config @@ -2,10 +2,18 @@ {erl_opts, [debug_info]}. -{deps, []}. +{deps, []}. {dialyzer, [ {warnings, [unknown]}, {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}]}. \ No newline at end of file diff --git a/src/aefa_asm.erl b/src/aefa_asm.erl index 6bf6d9e..a671e66 100644 --- a/src/aefa_asm.erl +++ b/src/aefa_asm.erl @@ -33,7 +33,7 @@ %%% <000> %%% <1010> %%% <> -%%% !<> +%%% !<> %%% 9. Tuples %%% () %%% (1, "foo") @@ -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)}}. diff --git a/src/aefa_asm_scan.xrl b/src/aefa_asm_scan.xrl index af3b7c6..3473418 100644 --- a/src/aefa_asm_scan.xrl +++ b/src/aefa_asm_scan.xrl @@ -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). diff --git a/src/aefa_opcode.erl b/src/aefa_opcode.erl index 4563f81..2c726ff 100644 --- a/src/aefa_opcode.erl +++ b/src/aefa_opcode.erl @@ -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 . +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. 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/test.fate b/test/asm_code/test.fate new file mode 100644 index 0000000..d4bf55b --- /dev/null +++ b/test/asm_code/test.fate @@ -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", []).