Compare commits

..

36 Commits

Author SHA1 Message Date
Thomas Arts be9935cd7e Merge branch 'master' into quickcheck-ci 2019-02-11 13:11:46 +01:00
Hans Svensson 026ff52528 Merge pull request #24 from aeternity/merge_roma
Merge ROMA branch to master
2019-02-08 14:13:10 +01:00
Hans Svensson 3ba89d9f55 Merge ROMA into MINERVA 2019-02-08 13:38:37 +01:00
Robert Virding 0b4c2f14fe Move module documentation to separate files (#23) 2019-02-08 10:31:35 +01:00
Robert Virding b65c4edd19 Merge pull request #22 from aeternity/fix-compiler-interface
Remove specific filename extension handling
2019-02-06 15:17:53 +01:00
Robert Virding 515f444e7c Remove specific filename extension handling
And take the chance to make file handling errors ahve the same format
as other errors.
2019-02-06 14:02:46 +01:00
Dincho Todorov d3fa04483e Initial CircleCI integration (#20) 2019-01-29 15:35:41 +02:00
Ulf Norell 2edafe0adc Merge pull request #19 from aeternity/dialyzer-warnings
Fix incorrect type specs
2019-01-29 09:19:13 +01:00
Ulf Norell 3a7c8f905a Fix incorrect type specs
h/t OTP-21 dialyzer
2019-01-28 14:54:34 +01:00
Hans Svensson 9026e1fe6b Merge pull request #18 from aeternity/minor_fixing_up
Cleanup whitespace, bad typespec, and remaining enacl reference
2019-01-28 11:24:29 +01:00
Hans Svensson f1f2b09294 Cleanup whitespace, bad typespec, and remaining enacl reference 2019-01-28 10:50:32 +01:00
Ulf Norell 53299b9b17 Merge pull request #17 from aeternity/PT-163478903-builtin-bits-type
PT-163478903 builtin bits type
2019-01-28 10:05:09 +01:00
Robert Virding 64fd91197b Merge pull request #9 from aeternity/interface-to-sophia
PT-163063316 Interface to sophia
2019-01-25 16:20:38 +01:00
Robert Virding a73abf8e8e Fix testing to use new error message format 2019-01-25 16:16:20 +01:00
Robert Virding db1c0fa05a Improve pretty printing the code AST 2019-01-25 16:16:20 +01:00
Robert Virding 70ad303e16 Document the aeso_compiler module
The module doc format is loosely based on the standard erlang doc
formats.
2019-01-25 16:16:20 +01:00
Robert Virding fe1a2758c3 Improve the interface to the compiler
It is now more consistent though we can still discuss how we want the
interface to look.
2019-01-25 16:16:20 +01:00
Ulf Norell 79de25b3a5 Fix minor bugs in compilation of bit fields 2019-01-25 16:09:31 +01:00
Ulf Norell 3e1290efaf Add Bits.all and rename Bits.zero to Bits.none 2019-01-25 16:09:31 +01:00
Ulf Norell 9c77622c7c Add set operations on bit fields (union, isect, diff) 2019-01-25 16:09:31 +01:00
Ulf Norell a367d5040a Add builtin bit field type 2019-01-25 16:09:31 +01:00
Ulf Norell d8bf0bda45 Remove integer bit operations 2019-01-25 16:09:31 +01:00
Hans Svensson 922107e438 Merge pull request #16 from aeternity/git_mess_up_fix
Fix git mess up
2019-01-25 10:53:06 +01:00
Hans Svensson f133483a90 Fix git mess up 2019-01-25 10:49:36 +01:00
Hans Svensson be42ee08ab Merge pull request #15 from aeternity/fix_string_concat
Refactor String.concat to not shift 256 bits
2019-01-25 10:36:09 +01:00
Hans Svensson 86285a8ae0 Refactor String.concat to not shift 256 bits
Adding SafeMath to VM_AEVM_SOPHIA_2 will break String.concat otherwise.
2019-01-25 09:57:23 +01:00
Thomas Arts aa2e6aa218 machinery for running QuickCheck
No script needed if we make sure extra_src_dirs has different name than "eqc"

Obsolete QuickCheck property
2019-01-24 09:11:26 +01:00
Hans Svensson c5c73097fc Merge pull request #13 from aeternity/PT-163388694-add_native_bit_shift
Use ?SHL and ?SHR for 'bsl' and 'bsr'
2019-01-23 13:15:14 +01:00
Hans Svensson 23ccce4c22 Use ?SHL and ?SHR for 'bsl' and 'bsr' 2019-01-22 21:53:30 +01:00
Ulf Norell 387fdf5c34 Merge pull request #12 from aeternity/PT-163362624-generic-hash
PT-163362624 generic hash functions
2019-01-22 15:04:47 +01:00
Ulf Norell f0dcda27bc Update aebytecode commit 2019-01-22 14:54:38 +01:00
Ulf Norell 5fd24fec86 Add more hash primops 2019-01-22 09:09:37 +01:00
Hans Svensson 4bedbfee61 Merge pull request #11 from aeternity/PT-163083710-implement_ecverify
Add Crypto.ecverify
2019-01-22 08:51:10 +01:00
Hans Svensson e4c46ee16f Bump aebytecode dependency 2019-01-21 20:14:20 +01:00
Hans Svensson d8fff8f20f Add Crypto.ecverify 2019-01-21 14:20:15 +01:00
Luca Favatella b074aa3323 Rename description of node in readme (#10) 2019-01-18 10:58:26 +00:00
23 changed files with 617 additions and 229 deletions
+3
View File
@@ -0,0 +1,3 @@
{build, "rebar3 as eqc compile"}.
{test_root, "."}.
{test_path, "_build/eqc/lib/aesophia/quickcheck"}. %% here are the properties
+5
View File
@@ -16,3 +16,8 @@ _build
.idea .idea
*.iml *.iml
rebar3.crashdump rebar3.crashdump
current_counterexample.eqc
.qcci
*.erl~
*.aes~
+8 -2
View File
@@ -5,6 +5,12 @@ This is the __sophia__ compiler for the æternity system which compiles contract
For more information about æternity smart contracts and the sophia language see [Smart Contracts](https://github.com/aeternity/protocol/blob/master/contracts/contracts.md) and the [Sophia Language](https://github.com/aeternity/protocol/blob/master/contracts/sophia.md). For more information about æternity smart contracts and the sophia language see [Smart Contracts](https://github.com/aeternity/protocol/blob/master/contracts/contracts.md) and the [Sophia Language](https://github.com/aeternity/protocol/blob/master/contracts/sophia.md).
It is an OTP application written in Erlang and is by default included in It is an OTP application written in Erlang and is by default included in
[æternity Epoch](https://github.com/aeternity/epoch). However, it can [the æternity node](https://github.com/aeternity/epoch). However, it can
also be included in other system to compile contracts coded in sophia which also be included in other systems to compile contracts coded in sophia which
can then be loaded into the æternity system. can then be loaded into the æternity system.
## Interface Modules
The basic modules for interfacing the compiler:
* [aeso_compiler: the Sophia compiler](./docs/aeso_compiler.md)
+86
View File
@@ -0,0 +1,86 @@
# aeso_compiler
### Module
### aeso_compiler
The Sophia compiler
### Description
This module provides the interface to the standard Sophia compiler. It
returns the compiled module in a map which can then be loaded.
### Types
``` erlang
contract_string() = string() | binary()
contract_map() = #{bytecode => binary(),
compiler_version => string(),
contract_souce => string(),
type_info => type_info()}
type_info()
errorstring() = binary()
```
### Exports
#### file(File)
#### file(File, Options) -> CompRet
#### from_string(ContractString, Options) -> CompRet
Types
``` erlang
ContractString = contract_string()
Options = [Option]
CompRet = {ok,ContractMap} | {error,ErrorString}
ContractMap = contract_map()
ErrorString = errorstring()
```
Compile a contract defined in a file or in a string.
The **pp_** options all print to standard output the following:
`pp_sophia_code` - print the input Sophia code.
`pp_ast` - print the AST of the code
`pp_types` - print information about the types
`pp_typed_ast` - print the AST with type information at each node
`pp_icode` - print the internal code structure
`pp_assembler` - print the generated assembler code
`pp_bytecode` - print the bytecode instructions
#### check_call(ContractString, Options) -> CheckRet
Types
```
ContractString = string() | binary()
CheckRet = {ok,string(),{Types,Type | any()},Terms} | {error,Term}
Types = [Type]
Type = term()
```
Check a call in contract through the `__call` function.
#### sophia_type_to_typerep(String) -> TypeRep
Types
``` erlang
{ok,TypeRep} | {error, badtype}
```
Get the type representation of a type declaration.
#### version() -> Version
Types
``` erlang
Version = integer()
```
Get the current version of the Sophia compiler.
+109
View File
@@ -0,0 +1,109 @@
%%% File : aeso_heap_eqc.erl
%%% Author : Ulf Norell
%%% Description :
%%% Created : 28 May 2018 by Ulf Norell
-module(aeso_heap_eqc).
-compile([export_all, nowarn_export_all]).
-include_lib("eqc/include/eqc.hrl").
-define(SANDBOX(Code), sandbox(fun() -> Code end)).
sandbox(Code) ->
Parent = self(),
Tag = make_ref(),
{Pid, Ref} = spawn_monitor(fun() -> Parent ! {Tag, Code()} end),
receive
{Tag, Res} -> erlang:demonitor(Ref, [flush]), {ok, Res};
{'DOWN', Ref, process, Pid, Reason} -> {error, Reason}
after 100 ->
exit(Pid, kill),
{error, loop}
end.
prop_from_binary() ->
?FORALL({T, Bin}, {type(), blob()},
begin
Tag = fun(X) when is_atom(X) -> X; (X) when is_tuple(X) -> element(1, X) end,
case ?SANDBOX(aeso_heap:from_binary(T, Bin)) of
{ok, Res} -> collect({Tag(T), element(1, Res)}, true);
Err -> equals(Err, {ok, '_'})
end end).
type() -> ?LET(Depth, choose(0, 2), type(Depth, true)).
type(Depth, TypeRep) ->
oneof(
[ elements([word, string] ++ [typerep || TypeRep]) ] ++
[ ?LETSHRINK([T], [type(Depth - 1, TypeRep)], {list, T}) || Depth > 0 ] ++
[ ?LETSHRINK([T], [type(Depth - 1, TypeRep)], {option, T}) || Depth > 0 ] ++
[ ?LETSHRINK(Ts, list(type(Depth - 1, TypeRep)), {tuple, Ts}) || Depth > 0 ] ++
[ ?LETSHRINK([K, V], vector(2, type(Depth - 1, TypeRep)), {map, K, V}) || Depth > 0 ] ++
[]
).
blob() ->
?LET(Blobs, list(oneof([ ?LET(Ws, words(), return(from_words(Ws)))
, binary() ])),
return(list_to_binary(Blobs))).
words() -> list(word()).
word() ->
frequency(
[ {4, ?LET(N, nat(), 32 * N)}
, {1, choose(0, 320)}
, {2, -1}
, {2, elements(["foo", "zzzzz"])} ]).
from_words(Ws) ->
<< <<(from_word(W))/binary>> || W <- Ws >>.
from_word(W) when is_integer(W) ->
<<W:256>>;
from_word(S) when is_list(S) ->
Len = length(S),
Bin = <<(list_to_binary(S))/binary, 0:(32 - Len)/unit:8>>,
<<Len:256, Bin/binary>>.
typerep() -> ?LET(Depth, choose(0, 2),
?LET(T, type(Depth, true), return(typerep(T)))).
typerep(word) -> word;
typerep(string) -> string;
typerep(typerep) -> typerep;
typerep({tuple, Ts}) -> {tuple, typerep(Ts)};
typerep({list, T}) -> {list, typerep(T)};
typerep({variant, Cs}) -> {variant, typerep(Cs)};
typerep({option, T}) -> {variant, [[], [typerep(T)]]};
typerep({map, K, V}) -> {list, typerep({tuple, [K, V]})};
typerep([]) -> [];
typerep([T | Ts]) -> [typerep(T) | typerep(Ts)].
value(word) ->
<<N:256>> = <<(-1):256>>,
choose(0, N);
value(string) ->
?LET(N, choose(0, 128), binary(N));
value(typerep) ->
typerep();
value({list, T}) ->
list(value(T));
value({option, T}) ->
weighted_default({1, none}, {3, {some, value(T)}});
value({tuple, Ts}) ->
?LET(Vs, [ value(T) || T <- Ts ], list_to_tuple(Vs));
value({map, K, V}) ->
map(value(K), value(V));
value({variant, Cs}) ->
?LET(I, choose(0, length(Cs) - 1),
{variant, I, [ value(T) || T <- lists:nth(I + 1, Cs) ]}).
typed_val() ->
?LET(T, type(), ?LET(V, value(T), return({T, V}))).
prop_roundtrip() ->
?FORALL(T, type(),
?FORALL(V, value(T),
?FORALL(B, choose(0, 4),
equals(aeso_heap:from_binary(T, aeso_heap:to_binary(V, B * 32), B * 32),
{ok, V})))).
+59
View File
@@ -0,0 +1,59 @@
%%% File : aeso_utils_eqc.erl
%%% Author : Ulf Norell
%%% Description :
%%% Created : 2 Jul 2018 by Ulf Norell
-module(aeso_utils_eqc).
-compile([export_all, nowarn_export_all]).
-include_lib("eqc/include/eqc.hrl").
%% QuickCheck property
graph() ->
?LET(M, map(choose(0, 10), list(choose(0, 10))),
return(complete(M))).
complete(G) ->
Is = lists:usort(lists:concat(maps:values(G))),
maps:merge(maps:from_list([ {I, []} || I <- Is ]), G).
prop_scc() ->
?FORALL(G, graph(),
begin
SCCs = aeso_utils:scc(G),
BadSCC = fun({acyclic, I}) -> reachable_from(G, I, I);
({cyclic, Is}) -> [] /= [ {I, J} || I <- Is, J <- Is, not reachable_from(G, I, J) ]
end,
ToList = fun({acyclic, I}) -> [I];
({cyclic, Is}) -> Is end,
?WHENFAIL(eqc:format("SCCs = ~p\n", [SCCs]),
conjunction(
[ {elems, equals(lists:sort(lists:flatmap(ToList, SCCs)), lists:sort(maps:keys(G)))}
, {sorted, equals([], [ {I, J} || {I, Js} <- maps:to_list(G),
J <- Js,
find_component(I, SCCs) < find_component(J, SCCs) ])}
, {precise, equals([], [ SCC || SCC <- SCCs, BadSCC(SCC) ])}
]))
end).
reachable_from(Graph, I, J) ->
reachable_from1(Graph, maps:get(I, Graph, []), J).
reachable_from1(_, [], _) -> false;
reachable_from1(_, [I | _], I) -> true;
reachable_from1(Graph, [I | Is], J) ->
case maps:get(I, Graph, undefined) of
undefined -> reachable_from1(Graph, Is, J);
Js -> reachable_from1(maps:remove(I, Graph), Js ++ Is, J)
end.
find_component(X, SCCs) ->
ISCCs = lists:zip(SCCs, lists:seq(1, length(SCCs))),
HasX = fun({acyclic, Y}) -> X == Y;
({cyclic, Ys}) -> lists:member(X, Ys) end,
case [ I || {SCC, I} <- ISCCs, HasX(SCC) ] of
[I | _] -> I;
[] -> false
end.
+8 -1
View File
@@ -1,10 +1,17 @@
{erl_opts, [debug_info]}. {erl_opts, [debug_info]}.
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git",
{ref,"99bf097"}}} {ref,"720510a"}}}
, {getopt, "1.0.1"} , {getopt, "1.0.1"}
]}. ]}.
{profiles, [ {eqc, [{erl_opts, [{parse_transform, eqc_cover}]},
{deps, [{eqc_ci, "1.0.0"}]},
{extra_src_dirs, ["quickcheck"]} %% May not be called eqc!
]}
]}.
{escript_incl_apps, [aesophia, aebytecode, getopt]}. {escript_incl_apps, [aesophia, aebytecode, getopt]}.
{escript_main_app, aesophia}. {escript_main_app, aesophia}.
{escript_name, aesophia}. {escript_name, aesophia}.
+1 -1
View File
@@ -1,7 +1,7 @@
{"1.1.0", {"1.1.0",
[{<<"aebytecode">>, [{<<"aebytecode">>,
{git,"https://github.com/aeternity/aebytecode.git", {git,"https://github.com/aeternity/aebytecode.git",
{ref,"99bf097759dedbe7553f87a796bc7e1c7322e64b"}}, {ref,"720510a24de32c9bad6486f34ca7babde124bf1e"}},
0}, 0},
{<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},0}]}. {<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},0}]}.
[ [
+5 -4
View File
@@ -17,11 +17,12 @@ line({symbol, Line, _}) -> Line.
symbol_name({symbol, _, Name}) -> Name. symbol_name({symbol, _, Name}) -> Name.
pp(Ast) -> pp(Ast) ->
%% TODO: Actually do *Pretty* printing. %% io:format("Tree:\n~p\n",[Ast]),
io:format("~p~n", [Ast]). String = prettypr:format(aeso_pretty:decls(Ast, [])),
io:format("Ast:\n~s\n", [String]).
pp_typed(TypedAst) -> pp_typed(TypedAst) ->
String = prettypr:format(aeso_pretty:decls(TypedAst, [show_generated])),
%% io:format("Typed tree:\n~p\n",[TypedAst]), %% io:format("Typed tree:\n~p\n",[TypedAst]),
io:format("Type info:\n~s\n",[String]). String = prettypr:format(aeso_pretty:decls(TypedAst, [show_generated])),
io:format("Type ast:\n~s\n",[String]).
+21 -5
View File
@@ -79,6 +79,7 @@ global_env() ->
Event = {id, Ann, "event"}, Event = {id, Ann, "event"},
State = {id, Ann, "state"}, State = {id, Ann, "state"},
Hash = {id, Ann, "hash"}, Hash = {id, Ann, "hash"},
Bits = {id, Ann, "bits"},
Oracle = fun(Q, R) -> {app_t, Ann, {id, Ann, "oracle"}, [Q, R]} end, Oracle = fun(Q, R) -> {app_t, Ann, {id, Ann, "oracle"}, [Q, R]} end,
Query = fun(Q, R) -> {app_t, Ann, {id, Ann, "oracle_query"}, [Q, R]} end, Query = fun(Q, R) -> {app_t, Ann, {id, Ann, "oracle_query"}, [Q, R]} end,
Unit = {tuple_t, Ann, []}, Unit = {tuple_t, Ann, []},
@@ -148,10 +149,27 @@ global_env() ->
{["Map", "delete"], Fun([K, Map(K, V)], Map(K, V))}, {["Map", "delete"], Fun([K, Map(K, V)], Map(K, V))},
{["Map", "member"], Fun([K, Map(K, V)], Bool)}, {["Map", "member"], Fun([K, Map(K, V)], Bool)},
{["Map", "size"], Fun1(Map(K, V), Int)}, {["Map", "size"], Fun1(Map(K, V), Int)},
%% Crypto/Curve operations
{["Crypto", "ecverify"], Fun([Hash, Address, SignId], Bool)},
{["Crypto", "sha3"], Fun1(A, Hash)},
{["Crypto", "sha256"], Fun1(A, Hash)},
{["Crypto", "blake2b"], Fun1(A, Hash)},
%% Strings %% Strings
{["String", "length"], Fun1(String, Int)}, {["String", "length"], Fun1(String, Int)},
{["String", "concat"], Fun([String, String], String)}, {["String", "concat"], Fun([String, String], String)},
{["String", "sha3"], Fun1(String, Int)}, {["String", "sha3"], Fun1(String, Hash)},
{["String", "sha256"], Fun1(String, Hash)},
{["String", "blake2b"], Fun1(String, Hash)},
%% Bits
{["Bits", "set"], Fun([Bits, Int], Bits)},
{["Bits", "clear"], Fun([Bits, Int], Bits)},
{["Bits", "test"], Fun([Bits, Int], Bool)},
{["Bits", "sum"], Fun1(Bits, Int)},
{["Bits", "intersection"], Fun([Bits, Bits], Bits)},
{["Bits", "union"], Fun([Bits, Bits], Bits)},
{["Bits", "difference"], Fun([Bits, Bits], Bits)},
{["Bits", "none"], Bits},
{["Bits", "all"], Bits},
%% Conversion %% Conversion
{["Int", "to_str"], Fun1(Int, String)}, {["Int", "to_str"], Fun1(Int, String)},
{["Address", "to_str"], Fun1(Address, String)} {["Address", "to_str"], Fun1(Address, String)}
@@ -684,8 +702,7 @@ infer_infix({BoolOp, As})
{fun_t, As, [], [Bool,Bool], Bool}; {fun_t, As, [], [Bool,Bool], Bool};
infer_infix({IntOp, As}) infer_infix({IntOp, As})
when IntOp == '+'; IntOp == '-'; IntOp == '*'; IntOp == '/'; when IntOp == '+'; IntOp == '-'; IntOp == '*'; IntOp == '/';
IntOp == '^'; IntOp == 'mod'; IntOp == 'bsl'; IntOp == 'bsr'; IntOp == '^'; IntOp == 'mod' ->
IntOp == 'band'; IntOp == 'bor'; IntOp == 'bxor' ->
Int = {id, As, "int"}, Int = {id, As, "int"},
{fun_t, As, [], [Int, Int], Int}; {fun_t, As, [], [Int, Int], Int};
infer_infix({RelOp, As}) infer_infix({RelOp, As})
@@ -707,8 +724,7 @@ infer_infix({'++', As}) ->
infer_prefix({'!',As}) -> infer_prefix({'!',As}) ->
Bool = {id, As, "bool"}, Bool = {id, As, "bool"},
{fun_t, As, [], [Bool], Bool}; {fun_t, As, [], [Bool], Bool};
infer_prefix({IntOp,As}) infer_prefix({IntOp,As}) when IntOp =:= '-' ->
when IntOp =:= '-'; IntOp =:= 'bnot' ->
Int = {id, As, "int"}, Int = {id, As, "int"},
{fun_t, As, [], [Int], Int}. {fun_t, As, [], [Int], Int}.
+54 -6
View File
@@ -316,6 +316,23 @@ ast_body({map, _, Map, [Upd]}, Icode) ->
ast_body({map, Ann, Map, [Upd | Upds]}, Icode) -> ast_body({map, Ann, Map, [Upd | Upds]}, Icode) ->
ast_body({map, Ann, {map, Ann, Map, [Upd]}, Upds}, Icode); ast_body({map, Ann, {map, Ann, Map, [Upd]}, Upds}, Icode);
%% Crypto
ast_body(?qid_app(["Crypto", "ecverify"], [Msg, PK, Sig], _, _), Icode) ->
prim_call(?PRIM_CALL_CRYPTO_ECVERIFY, #integer{value = 0},
[ast_body(Msg, Icode), ast_body(PK, Icode), ast_body(Sig, Icode)],
[word, word, sign_t()], word);
ast_body(?qid_app(["Crypto", "sha3"], [Term], [Type], _), Icode) ->
generic_hash_primop(?PRIM_CALL_CRYPTO_SHA3, Term, Type, Icode);
ast_body(?qid_app(["Crypto", "sha256"], [Term], [Type], _), Icode) ->
generic_hash_primop(?PRIM_CALL_CRYPTO_SHA256, Term, Type, Icode);
ast_body(?qid_app(["Crypto", "blake2b"], [Term], [Type], _), Icode) ->
generic_hash_primop(?PRIM_CALL_CRYPTO_BLAKE2B, Term, Type, Icode);
ast_body(?qid_app(["String", "sha256"], [String], _, _), Icode) ->
string_hash_primop(?PRIM_CALL_CRYPTO_SHA256_STRING, String, Icode);
ast_body(?qid_app(["String", "blake2b"], [String], _, _), Icode) ->
string_hash_primop(?PRIM_CALL_CRYPTO_BLAKE2B_STRING, String, Icode);
%% Strings %% Strings
%% -- String length %% -- String length
ast_body(?qid_app(["String", "length"], [String], _, _), Icode) -> ast_body(?qid_app(["String", "length"], [String], _, _), Icode) ->
@@ -331,6 +348,33 @@ ast_body(?qid_app(["String", "concat"], [String1, String2], _, _), Icode) ->
ast_body(?qid_app(["String", "sha3"], [String], _, _), Icode) -> ast_body(?qid_app(["String", "sha3"], [String], _, _), Icode) ->
#unop{ op = 'sha3', rand = ast_body(String, Icode) }; #unop{ op = 'sha3', rand = ast_body(String, Icode) };
%% -- Bits
ast_body(?qid_app(["Bits", Fun], Args, _, _), Icode)
when Fun == "test"; Fun == "set"; Fun == "clear";
Fun == "union"; Fun == "intersection"; Fun == "difference" ->
C = fun(N) when is_integer(N) -> #integer{ value = N };
(X) -> X end,
Bin = fun(O) -> fun(A, B) -> #binop{ op = O, left = C(A), right = C(B) } end end,
And = Bin('band'),
Or = Bin('bor'),
Bsl = fun(A, B) -> (Bin('bsl'))(B, A) end, %% flipped arguments
Bsr = fun(A, B) -> (Bin('bsr'))(B, A) end,
Neg = fun(A) -> #unop{ op = 'bnot', rand = C(A) } end,
case [Fun | [ ast_body(Arg, Icode) || Arg <- Args ]] of
["test", Bits, Ix] -> And(Bsr(Bits, Ix), 1);
["set", Bits, Ix] -> Or(Bits, Bsl(1, Ix));
["clear", Bits, Ix] -> And(Bits, Neg(Bsl(1, Ix)));
["union", A, B] -> Or(A, B);
["intersection", A, B] -> And(A, B);
["difference", A, B] -> And(A, Neg(And(A, B)))
end;
ast_body({qid, _, ["Bits", "none"]}, _Icode) ->
#integer{ value = 0 };
ast_body({qid, _, ["Bits", "all"]}, _Icode) ->
#integer{ value = 1 bsl 256 - 1 };
ast_body(?qid_app(["Bits", "sum"], [Bits], _, _), Icode) ->
builtin_call(popcount, [ast_body(Bits, Icode), #integer{ value = 0 }]);
%% -- Conversion %% -- Conversion
ast_body(?qid_app(["Int", "to_str"], [Int], _, _), Icode) -> ast_body(?qid_app(["Int", "to_str"], [Int], _, _), Icode) ->
builtin_call(int_to_str, [ast_body(Int, Icode)]); builtin_call(int_to_str, [ast_body(Int, Icode)]);
@@ -509,12 +553,6 @@ ast_binop(Op, Ann, {typed, _, A, Type}, B, Icode)
ast_binop('++', _, A, B, Icode) -> ast_binop('++', _, A, B, Icode) ->
#funcall{ function = #var_ref{ name = {builtin, list_concat} }, #funcall{ function = #var_ref{ name = {builtin, list_concat} },
args = [ast_body(A, Icode), ast_body(B, Icode)] }; args = [ast_body(A, Icode), ast_body(B, Icode)] };
ast_binop('bsl', _, A, B, Icode) ->
#binop{op = '*', left = ast_body(A, Icode),
right = #binop{op = '^', left = {integer, 2}, right = ast_body(B, Icode)}};
ast_binop('bsr', _, A, B, Icode) ->
#binop{op = 'div', left = ast_body(A, Icode),
right = #binop{op = '^', left = {integer, 2}, right = ast_body(B, Icode)}};
ast_binop(Op, _, A, B, Icode) -> ast_binop(Op, _, A, B, Icode) ->
#binop{op = Op, left = ast_body(A, Icode), right = ast_body(B, Icode)}. #binop{op = Op, left = ast_body(A, Icode), right = ast_body(B, Icode)}.
@@ -593,6 +631,16 @@ prim_call(Prim, Amount, Args, ArgTypes, OutType) ->
type_hash= #integer{value = TypeHash} type_hash= #integer{value = TypeHash}
}. }.
generic_hash_primop(PrimOp, Term, Type, Icode) ->
ArgType = ast_type(Type, Icode),
TypeValue = type_value(ArgType),
prim_call(PrimOp, #integer{value = 0},
[TypeValue, ast_body(Term, Icode)],
[typerep, ArgType], word).
string_hash_primop(PrimOp, String, Icode) ->
prim_call(PrimOp, #integer{value = 0}, [ast_body(String, Icode)], [string], word).
make_type_def(Args, Def, Icode = #{ type_vars := TypeEnv }) -> make_type_def(Args, Def, Icode = #{ type_vars := TypeEnv }) ->
TVars = [ X || {tvar, _, X} <- Args ], TVars = [ X || {tvar, _, X} <- Args ],
fun(Types) -> fun(Types) ->
+45 -28
View File
@@ -38,7 +38,7 @@ builtin_deps1({map_upd, Type}) -> [{map_get, Type}, map_put];
builtin_deps1({map_upd_default, Type}) -> [{map_lookup_default, Type}, map_put]; builtin_deps1({map_upd_default, Type}) -> [{map_lookup_default, Type}, map_put];
builtin_deps1(map_from_list) -> [map_put]; builtin_deps1(map_from_list) -> [map_put];
builtin_deps1(str_equal) -> [str_equal_p]; builtin_deps1(str_equal) -> [str_equal_p];
builtin_deps1(string_concat) -> [string_concat_inner1, string_concat_inner2]; builtin_deps1(string_concat) -> [string_concat_inner1, string_copy, string_shift_copy];
builtin_deps1(int_to_str) -> [{baseX_int, 10}]; builtin_deps1(int_to_str) -> [{baseX_int, 10}];
builtin_deps1(addr_to_str) -> [{baseX_int, 58}]; builtin_deps1(addr_to_str) -> [{baseX_int, 58}];
builtin_deps1({baseX_int, X}) -> [{baseX_int_pad, X}]; builtin_deps1({baseX_int, X}) -> [{baseX_int_pad, X}];
@@ -81,8 +81,9 @@ option_some(X) -> {tuple, [{integer, 1}, X]}.
-define(EXP(A, B), op('^', A, B)). -define(EXP(A, B), op('^', A, B)).
-define(AND(A, B), op('&&', A, B)). -define(AND(A, B), op('&&', A, B)).
-define(BSL(X, B), ?MUL(X, ?EXP(2, ?MUL(B, 8)))). %% Bit shift operations takes their arguments backwards!?
-define(BSR(X, B), ?DIV(X, ?EXP(2, ?MUL(B, 8)))). -define(BSL(X, B), op('bsl', ?MUL(B, 8), X)).
-define(BSR(X, B), op('bsr', ?MUL(B, 8), X)).
op(Op, A, B) -> {binop, Op, operand(A), operand(B)}. op(Op, A, B) -> {binop, Op, operand(A), operand(B)}.
@@ -143,9 +144,11 @@ builtin_function(BF) ->
string_length -> bfun(BF, builtin_string_length()); string_length -> bfun(BF, builtin_string_length());
string_concat -> bfun(BF, builtin_string_concat()); string_concat -> bfun(BF, builtin_string_concat());
string_concat_inner1 -> bfun(BF, builtin_string_concat_inner1()); string_concat_inner1 -> bfun(BF, builtin_string_concat_inner1());
string_concat_inner2 -> bfun(BF, builtin_string_concat_inner2()); string_copy -> bfun(BF, builtin_string_copy());
string_shift_copy -> bfun(BF, builtin_string_shift_copy());
str_equal_p -> bfun(BF, builtin_str_equal_p()); str_equal_p -> bfun(BF, builtin_str_equal_p());
str_equal -> bfun(BF, builtin_str_equal()); str_equal -> bfun(BF, builtin_str_equal());
popcount -> bfun(BF, builtin_popcount());
int_to_str -> bfun(BF, builtin_int_to_str()); int_to_str -> bfun(BF, builtin_int_to_str());
addr_to_str -> bfun(BF, builtin_addr_to_str()); addr_to_str -> bfun(BF, builtin_addr_to_str());
{baseX_int, X} -> bfun(BF, builtin_baseX_int(X)); {baseX_int, X} -> bfun(BF, builtin_baseX_int(X));
@@ -321,6 +324,8 @@ builtin_string_concat() ->
{[{"s1", string}, {"s2", string}], {[{"s1", string}, {"s2", string}],
?DEREF(n1, s1, ?DEREF(n1, s1,
?DEREF(n2, s2, ?DEREF(n2, s2,
{ifte, ?EQ(n1, 0),
?V(s2), %% First string is empty return second string
{ifte, ?EQ(n2, 0), {ifte, ?EQ(n2, 0),
?V(s1), %% Second string is empty return first string ?V(s1), %% Second string is empty return first string
?LET(ret, {inline_asm, [?A(?MSIZE)]}, ?LET(ret, {inline_asm, [?A(?MSIZE)]},
@@ -329,6 +334,7 @@ builtin_string_concat() ->
{inline_asm, [?A(?POP)]}, %% Discard fun ret val {inline_asm, [?A(?POP)]}, %% Discard fun ret val
?V(ret) %% Put the actual return value ?V(ret) %% Put the actual return value
]})} ]})}
}
)), )),
word}. word}.
@@ -336,33 +342,33 @@ builtin_string_concat_inner1() ->
%% Copy all whole words from the first string, and set up for word fusion %% Copy all whole words from the first string, and set up for word fusion
%% Special case when the length of the first string is divisible by 32. %% Special case when the length of the first string is divisible by 32.
{[{"n1", word}, {"p1", pointer}, {"n2", word}, {"p2", pointer}], {[{"n1", word}, {"p1", pointer}, {"n2", word}, {"p2", pointer}],
?DEREF(w1, p1, ?LET(w1, ?call(string_copy, [?V(n1), ?V(p1)]),
{ifte, ?GT(n1, 32), ?LET(nx, ?MOD(n1, 32),
{seq, [?V(w1), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]}, {ifte, ?EQ(nx, 0),
?call(string_concat_inner1, [?SUB(n1, 32), ?NXT(p1), ?V(n2), ?V(p2)])]}, ?LET(w2, ?call(string_copy, [?V(n2), ?V(p2)]),
{ifte, ?EQ(n1, 0), {seq, [?V(w2), {inline_asm, [?A(?MSIZE), ?A(?MSTORE), ?A(?MSIZE)]}]}),
?call(string_concat_inner2, [?I(32), ?I(0), ?V(n2), ?V(p2)]), ?call(string_shift_copy, [?V(nx), ?V(w1), ?V(n2), ?V(p2)])
?call(string_concat_inner2, [?SUB(32, n1), ?V(w1), ?V(n2), ?V(p2)])} })),
word}.
builtin_string_copy() ->
{[{"n", word}, {"p", pointer}],
?DEREF(w, p,
{ifte, ?GT(n, 31),
{seq, [?V(w), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
?call(string_copy, [?SUB(n, 32), ?NXT(p)])]},
?V(w)
}), }),
word}. word}.
builtin_string_concat_inner2() -> builtin_string_shift_copy() ->
%% Current "work in progess" word 'x', has 'o' bytes that are "free" - fill them from {[{"off", word}, {"dst", word}, {"n", word}, {"p", pointer}],
%% words of the second string. ?DEREF(w, p,
{[{"o", word}, {"x", word}, {"n2", word}, {"p2", pointer}], {seq, [?ADD(dst, ?BSR(w, off)), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
{ifte, ?LT(n2, 1), {ifte, ?GT(n, ?SUB(32, off)),
{seq, [?V(x), {inline_asm, [?A(?MSIZE), ?A(?MSTORE), ?A(?MSIZE)]}]}, %% Use MSIZE as dummy return value ?call(string_shift_copy, [?V(off), ?BSL(w, ?SUB(32, off)), ?SUB(n, 32), ?NXT(p)]),
?DEREF(w2, p2, {inline_asm, [?A(?MSIZE)]}}]
{ifte, ?GT(n2, o), }),
{seq, [?ADD(x, ?BSR(w2, ?SUB(32, o))),
{inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
?call(string_concat_inner2,
[?V(o), ?BSL(w2, o), ?SUB(n2, 32), ?NXT(p2)])
]},
{seq, [?ADD(x, ?BSR(w2, ?SUB(32, o))),
{inline_asm, [?A(?MSIZE), ?A(?MSTORE), ?A(?MSIZE)]}]} %% Use MSIZE as dummy return value
})
},
word}. word}.
builtin_str_equal_p() -> builtin_str_equal_p() ->
@@ -393,6 +399,17 @@ builtin_str_equal() ->
)), )),
word}. word}.
%% Count the number of 1s in a bit field.
builtin_popcount() ->
%% function popcount(bits, acc) =
%% if (bits == 0) acc
%% else popcount(bits bsr 1, acc + bits band 1)
{[{"bits", word}, {"acc", word}],
{ifte, ?EQ(bits, 0),
?V(acc),
?call(popcount, [op('bsr', 1, bits), ?ADD(acc, op('band', bits, 1))])
}, word}.
builtin_int_to_str() -> builtin_int_to_str() ->
{[{"i", word}], ?call({baseX_int, 10}, [?V(i)]), word}. {[{"i", word}], ?call({baseX_int, 10}, [?V(i)]), word}.
+49 -17
View File
@@ -21,8 +21,8 @@
-include("aeso_icode.hrl"). -include("aeso_icode.hrl").
-type option() :: pp_sophia_code | pp_ast | pp_icode | pp_assembler | -type option() :: pp_sophia_code | pp_ast | pp_types | pp_typed_ast |
pp_bytecode. pp_icode| pp_assembler | pp_bytecode.
-type options() :: [option()]. -type options() :: [option()].
-export_type([ option/0 -export_type([ option/0
@@ -38,17 +38,24 @@
version() -> version() ->
?COMPILER_VERSION. ?COMPILER_VERSION.
-spec file(string()) -> map(). -spec file(string()) -> {ok, map()} | {error, binary()}.
file(Filename) -> file(Filename) ->
file(Filename, []). file(Filename, []).
-spec file(string(), options()) -> map(). -spec file(string(), options()) -> {ok, map()} | {error, binary()}.
file(Filename, Options) -> file(File, Options) ->
C = read_contract(Filename), case read_contract(File) of
from_string(C, Options). {ok, Bin} -> from_string(Bin, Options);
{error, Error} ->
ErrorString = [File,": ",file:format_error(Error)],
{error, join_errors("File errors", [ErrorString], fun(E) -> E end)}
end.
-spec from_string(string(), options()) -> map(). -spec from_string(binary() | string(), options()) -> {ok, map()} | {error, binary()}.
from_string(ContractBin, Options) when is_binary(ContractBin) ->
from_string(binary_to_list(ContractBin), Options);
from_string(ContractString, Options) -> from_string(ContractString, Options) ->
try
Ast = parse(ContractString, Options), Ast = parse(ContractString, Options),
ok = pp_sophia_code(Ast, Options), ok = pp_sophia_code(Ast, Options),
ok = pp_ast(Ast, Options), ok = pp_ast(Ast, Options),
@@ -63,9 +70,26 @@ from_string(ContractString, Options) ->
ByteCodeList = to_bytecode(Assembler, Options), ByteCodeList = to_bytecode(Assembler, Options),
ByteCode = << << B:8 >> || B <- ByteCodeList >>, ByteCode = << << B:8 >> || B <- ByteCodeList >>,
ok = pp_bytecode(ByteCode, Options), ok = pp_bytecode(ByteCode, Options),
#{byte_code => ByteCode, type_info => TypeInfo, {ok, #{byte_code => ByteCode,
compiler_version => version(),
contract_source => ContractString, contract_source => ContractString,
compiler_version => version()}. type_info => TypeInfo
}}
catch
%% The compiler errors.
error:{parse_errors, Errors} ->
{error, join_errors("Parse errors", Errors, fun(E) -> E end)};
error:{type_errors, Errors} ->
{error, join_errors("Type errors", Errors, fun(E) -> E end)};
error:{code_errors, Errors} ->
{error, join_errors("Code errors", Errors,
fun (E) -> io_lib:format("~p", [E]) end)}
%% General programming errors in the compiler just signal error.
end.
join_errors(Prefix, Errors, Pfun) ->
Ess = [ Pfun(E) || E <- Errors ],
list_to_binary(string:join([Prefix|Ess], "\n")).
-define(CALL_NAME, "__call"). -define(CALL_NAME, "__call").
@@ -76,6 +100,7 @@ from_string(ContractString, Options) ->
-spec check_call(string(), options()) -> {ok, string(), {[Type], Type | any}, [term()]} | {error, term()} -spec check_call(string(), options()) -> {ok, string(), {[Type], Type | any}, [term()]} | {error, term()}
when Type :: term(). when Type :: term().
check_call(ContractString, Options) -> check_call(ContractString, Options) ->
try
Ast = parse(ContractString, Options), Ast = parse(ContractString, Options),
ok = pp_sophia_code(Ast, Options), ok = pp_sophia_code(Ast, Options),
ok = pp_ast(Ast, Options), ok = pp_ast(Ast, Options),
@@ -91,11 +116,20 @@ check_call(ContractString, Options) ->
ok = pp_icode(Icode, Options), ok = pp_icode(Icode, Options),
#{ functions := Funs } = Icode, #{ functions := Funs } = Icode,
ArgIcode = get_arg_icode(Funs), ArgIcode = get_arg_icode(Funs),
try [ icode_to_term(T, Arg) || {T, Arg} <- lists:zip(ArgVMTypes, ArgIcode) ] of ArgTerms = [ icode_to_term(T, Arg) ||
ArgTerms -> {T, Arg} <- lists:zip(ArgVMTypes, ArgIcode) ],
{ok, FunName, {ArgVMTypes, RetVMType}, ArgTerms} {ok, FunName, {ArgVMTypes, RetVMType}, ArgTerms}
catch throw:Err -> catch
{error, Err} error:{parse_errors, Errors} ->
{error, join_errors("Parse errors", Errors, fun (E) -> E end)};
error:{type_errors, Errors} ->
{error, join_errors("Type errors", Errors, fun (E) -> E end)};
error:{badmatch, {error, missing_call_function}} ->
{error, join_errors("Type errors", ["missing __call function"],
fun (E) -> E end)};
throw:Error -> %Don't ask
{error, join_errors("Code errors", [Error],
fun (E) -> io_lib:format("~p", [E]) end)}
end. end.
-spec create_calldata(map(), string(), string()) -> -spec create_calldata(map(), string(), string()) ->
@@ -252,6 +286,4 @@ parse_error({Line,Pos}, ErrorString) ->
error({parse_errors, [Error]}). error({parse_errors, [Error]}).
read_contract(Name) -> read_contract(Name) ->
{ok, Bin} = file:read_file(Name), file:read_file(Name).
binary_to_list(Bin).
+1
View File
@@ -59,6 +59,7 @@ builtin_types() ->
Word = fun([]) -> word end, Word = fun([]) -> word end,
#{ "bool" => Word #{ "bool" => Word
, "int" => Word , "int" => Word
, "bits" => Word
, "string" => fun([]) -> string end , "string" => fun([]) -> string end
, "address" => Word , "address" => Word
, "hash" => Word , "hash" => Word
+2
View File
@@ -562,6 +562,8 @@ assemble_infix('^') -> i(?EXP);
assemble_infix('bor') -> i(?OR); assemble_infix('bor') -> i(?OR);
assemble_infix('band') -> i(?AND); assemble_infix('band') -> i(?AND);
assemble_infix('bxor') -> i(?XOR); assemble_infix('bxor') -> i(?XOR);
assemble_infix('bsl') -> i(?SHL);
assemble_infix('bsr') -> i(?SHR);
assemble_infix('<') -> i(?SLT); %% comparisons are SIGNED assemble_infix('<') -> i(?SLT); %% comparisons are SIGNED
assemble_infix('>') -> i(?SGT); assemble_infix('>') -> i(?SGT);
assemble_infix('==') -> i(?EQ); assemble_infix('==') -> i(?EQ);
+3 -3
View File
@@ -176,11 +176,11 @@ expr200() -> infixr(expr300(), binop('||')).
expr300() -> infixr(expr400(), binop('&&')). expr300() -> infixr(expr400(), binop('&&')).
expr400() -> infix(expr500(), binop(['<', '>', '=<', '>=', '==', '!='])). expr400() -> infix(expr500(), binop(['<', '>', '=<', '>=', '==', '!='])).
expr500() -> infixr(expr600(), binop(['::', '++'])). expr500() -> infixr(expr600(), binop(['::', '++'])).
expr600() -> infixl(expr650(), binop(['+', '-', 'bor', 'bxor', 'bsr', 'bsl'])). expr600() -> infixl(expr650(), binop(['+', '-'])).
expr650() -> ?RULE(many(token('-')), expr700(), prefixes(_1, _2)). expr650() -> ?RULE(many(token('-')), expr700(), prefixes(_1, _2)).
expr700() -> infixl(expr750(), binop(['*', '/', mod, 'band'])). expr700() -> infixl(expr750(), binop(['*', '/', mod])).
expr750() -> infixl(expr800(), binop(['^'])). expr750() -> infixl(expr800(), binop(['^'])).
expr800() -> ?RULE(many(choice(token('!'), token('bnot'))), expr900(), prefixes(_1, _2)). expr800() -> ?RULE(many(token('!')), expr900(), prefixes(_1, _2)).
expr900() -> ?RULE(exprAtom(), many(elim()), elim(_1, _2)). expr900() -> ?RULE(exprAtom(), many(elim()), elim(_1, _2)).
exprAtom() -> exprAtom() ->
+1 -7
View File
@@ -359,20 +359,14 @@ bin_prec('++') -> {500, 600, 500};
bin_prec('::') -> {500, 600, 500}; bin_prec('::') -> {500, 600, 500};
bin_prec('+') -> {600, 600, 650}; bin_prec('+') -> {600, 600, 650};
bin_prec('-') -> {600, 600, 650}; bin_prec('-') -> {600, 600, 650};
bin_prec('bor') -> {600, 600, 650};
bin_prec('bxor') -> {600, 600, 650};
bin_prec('bsl') -> {600, 600, 650};
bin_prec('bsr') -> {600, 600, 650};
bin_prec('*') -> {700, 700, 750}; bin_prec('*') -> {700, 700, 750};
bin_prec('/') -> {700, 700, 750}; bin_prec('/') -> {700, 700, 750};
bin_prec(mod) -> {700, 700, 750}; bin_prec(mod) -> {700, 700, 750};
bin_prec('band') -> {700, 700, 750};
bin_prec('^') -> {750, 750, 800}. bin_prec('^') -> {750, 750, 800}.
-spec un_prec(aeso_syntax:un_op()) -> {integer(), integer()}. -spec un_prec(aeso_syntax:un_op()) -> {integer(), integer()}.
un_prec('-') -> {650, 650}; un_prec('-') -> {650, 650};
un_prec('!') -> {800, 800}; un_prec('!') -> {800, 800}.
un_prec('bnot') -> {800, 800}.
equals(Ann, A, B) -> equals(Ann, A, B) ->
{app, [{format, infix} | Ann], {'=', Ann}, [A, B]}. {app, [{format, infix} | Ann], {'=', Ann}, [A, B]}.
+1 -2
View File
@@ -37,8 +37,7 @@ lexer() ->
, {"[^/*]+|[/*]", skip()} ], , {"[^/*]+|[/*]", skip()} ],
Keywords = ["contract", "import", "let", "rec", "switch", "type", "record", "datatype", "if", "elif", "else", "function", Keywords = ["contract", "import", "let", "rec", "switch", "type", "record", "datatype", "if", "elif", "else", "function",
"stateful", "true", "false", "and", "mod", "public", "private", "indexed", "internal", "stateful", "true", "false", "and", "mod", "public", "private", "indexed", "internal"],
"band", "bor", "bxor", "bsl", "bsr", "bnot"],
KW = string:join(Keywords, "|"), KW = string:join(Keywords, "|"),
Rules = Rules =
+2 -2
View File
@@ -75,10 +75,10 @@
-type op() :: bin_op() | un_op(). -type op() :: bin_op() | un_op().
-type bin_op() :: '+' | '-' | '*' | '/' | mod | '^' | 'band' | 'bor' | 'bsl' | 'bsr' | 'bxor' -type bin_op() :: '+' | '-' | '*' | '/' | mod | '^'
| '++' | '::' | '<' | '>' | '=<' | '>=' | '==' | '!=' | '++' | '::' | '<' | '>' | '=<' | '>=' | '==' | '!='
| '||' | '&&' | '..'. | '||' | '&&' | '..'.
-type un_op() :: '-' | '!' | 'bnot'. -type un_op() :: '-' | '!'.
-type expr() -type expr()
:: {lam, ann(), [arg()], expr()} :: {lam, ann(), [arg()], expr()}
+64 -50
View File
@@ -28,13 +28,15 @@ simple_compile_test_() ->
end} || ContractName <- compilable_contracts() ] ++ end} || ContractName <- compilable_contracts() ] ++
[ {"Testing error messages of " ++ ContractName, [ {"Testing error messages of " ++ ContractName,
fun() -> fun() ->
{type_errors, Errors} = compile(ContractName), <<"Type errors\n",ErrorString/binary>> = compile(ContractName),
check_errors(lists:sort(ExpectedErrors), lists:sort(Errors)) check_errors(lists:sort(ExpectedErrors), ErrorString)
end} || end} ||
{ContractName, ExpectedErrors} <- failing_contracts() ] {ContractName, ExpectedErrors} <- failing_contracts() ]
}. }.
check_errors(Expect, Actual) -> check_errors(Expect, ErrorString) ->
%% This removes the final single \n as well.
Actual = binary:split(<<ErrorString/binary,$\n>>, <<"\n\n">>, [global,trim]),
case {Expect -- Actual, Actual -- Expect} of case {Expect -- Actual, Actual -- Expect} of
{[], Extra} -> ?assertMatch({unexpected, []}, {unexpected, Extra}); {[], Extra} -> ?assertMatch({unexpected, []}, {unexpected, Extra});
{Missing, []} -> ?assertMatch({missing, []}, {missing, Missing}); {Missing, []} -> ?assertMatch({missing, []}, {missing, Missing});
@@ -42,10 +44,10 @@ check_errors(Expect, Actual) ->
end. end.
compile(Name) -> compile(Name) ->
try String = aeso_test_utils:read_contract(Name),
aeso_compiler:from_string(aeso_test_utils:read_contract(Name), []) case aeso_compiler:from_string(String, []) of
catch _:{type_errors, _} = E -> {ok,Map} -> Map;
E {error,ErrorString} -> ErrorString
end. end.
%% compilable_contracts() -> [ContractName]. %% compilable_contracts() -> [ContractName].
@@ -75,82 +77,94 @@ compilable_contracts() ->
failing_contracts() -> failing_contracts() ->
[ {"name_clash", [ {"name_clash",
["Duplicate definitions of abort at\n - (builtin location)\n - line 14, column 3\n", [<<"Duplicate definitions of abort at\n"
"Duplicate definitions of double_def at\n - line 10, column 3\n - line 11, column 3\n", " - (builtin location)\n"
"Duplicate definitions of double_proto at\n - line 4, column 3\n - line 5, column 3\n", " - line 14, column 3">>,
"Duplicate definitions of proto_and_def at\n - line 7, column 3\n - line 8, column 3\n", <<"Duplicate definitions of double_def at\n"
"Duplicate definitions of put at\n - (builtin location)\n - line 15, column 3\n", " - line 10, column 3\n"
"Duplicate definitions of state at\n - (builtin location)\n - line 16, column 3\n"]} " - line 11, column 3">>,
<<"Duplicate definitions of double_proto at\n"
" - line 4, column 3\n"
" - line 5, column 3">>,
<<"Duplicate definitions of proto_and_def at\n"
" - line 7, column 3\n"
" - line 8, column 3">>,
<<"Duplicate definitions of put at\n"
" - (builtin location)\n"
" - line 15, column 3">>,
<<"Duplicate definitions of state at\n"
" - (builtin location)\n"
" - line 16, column 3">>]}
, {"type_errors", , {"type_errors",
["Unbound variable zz at line 17, column 21\n", [<<"Unbound variable zz at line 17, column 21">>,
"Cannot unify int\n" <<"Cannot unify int\n"
" and list(int)\n" " and list(int)\n"
"when checking the application at line 26, column 9 of\n" "when checking the application at line 26, column 9 of\n"
" (::) : (int, list(int)) => list(int)\n" " (::) : (int, list(int)) => list(int)\n"
"to arguments\n" "to arguments\n"
" x : int\n" " x : int\n"
" x : int\n", " x : int">>,
"Cannot unify string\n" <<"Cannot unify string\n"
" and int\n" " and int\n"
"when checking the assignment of the field\n" "when checking the assignment of the field\n"
" x : map(string, string) (at line 9, column 46)\n" " x : map(string, string) (at line 9, column 46)\n"
"to the old value __x and the new value\n" "to the old value __x and the new value\n"
" __x {[\"foo\"] @ x = x + 1} : map(string, int)\n", " __x {[\"foo\"] @ x = x + 1} : map(string, int)">>,
"Cannot unify int\n" <<"Cannot unify int\n"
" and string\n" " and string\n"
"when checking the type of the expression at line 34, column 45\n" "when checking the type of the expression at line 34, column 45\n"
" 1 : int\n" " 1 : int\n"
"against the expected type\n" "against the expected type\n"
" string\n", " string">>,
"Cannot unify string\n" <<"Cannot unify string\n"
" and int\n" " and int\n"
"when checking the type of the expression at line 34, column 50\n" "when checking the type of the expression at line 34, column 50\n"
" \"bla\" : string\n" " \"bla\" : string\n"
"against the expected type\n" "against the expected type\n"
" int\n", " int">>,
"Cannot unify string\n" <<"Cannot unify string\n"
" and int\n" " and int\n"
"when checking the type of the expression at line 32, column 18\n" "when checking the type of the expression at line 32, column 18\n"
" \"x\" : string\n" " \"x\" : string\n"
"against the expected type\n" "against the expected type\n"
" int\n", " int">>,
"Cannot unify string\n" <<"Cannot unify string\n"
" and int\n" " and int\n"
"when checking the type of the expression at line 11, column 56\n" "when checking the type of the expression at line 11, column 56\n"
" \"foo\" : string\n" " \"foo\" : string\n"
"against the expected type\n" "against the expected type\n"
" int\n", " int">>,
"Cannot unify int\n" <<"Cannot unify int\n"
" and string\n" " and string\n"
"when comparing the types of the if-branches\n" "when comparing the types of the if-branches\n"
" - w : int (at line 38, column 13)\n" " - w : int (at line 38, column 13)\n"
" - z : string (at line 39, column 10)\n", " - z : string (at line 39, column 10)">>,
"Not a record type: string\n" <<"Not a record type: string\n"
"arising from the projection of the field y (at line 22, column 38)\n", "arising from the projection of the field y (at line 22, column 38)">>,
"Not a record type: string\n" <<"Not a record type: string\n"
"arising from an assignment of the field y (at line 21, column 42)\n", "arising from an assignment of the field y (at line 21, column 42)">>,
"Not a record type: string\n" <<"Not a record type: string\n"
"arising from an assignment of the field y (at line 20, column 38)\n", "arising from an assignment of the field y (at line 20, column 38)">>,
"Not a record type: string\n" <<"Not a record type: string\n"
"arising from an assignment of the field y (at line 19, column 35)\n", "arising from an assignment of the field y (at line 19, column 35)">>,
"Ambiguous record type with field y (at line 13, column 25) could be one of\n" <<"Ambiguous record type with field y (at line 13, column 25) could be one of\n"
" - r (at line 4, column 10)\n" " - r (at line 4, column 10)\n"
" - r' (at line 5, column 10)\n", " - r' (at line 5, column 10)">>,
"Record type r2 does not have field y (at line 15, column 22)\n", <<"Record type r2 does not have field y (at line 15, column 22)">>,
"The field z is missing when constructing an element of type r2 (at line 15, column 24)\n", <<"The field z is missing when constructing an element of type r2 (at line 15, column 24)">>,
"Repeated name x in pattern\n" <<"Repeated name x in pattern\n"
" x :: x (at line 26, column 7)\n", " x :: x (at line 26, column 7)">>,
"No record type with fields y, z (at line 14, column 22)\n"]} <<"No record type with fields y, z (at line 14, column 22)">>]}
, {"init_type_error", , {"init_type_error",
["Cannot unify string\n" [<<"Cannot unify string\n"
" and map(int, int)\n" " and map(int, int)\n"
"when checking that 'init' returns a value of type 'state' at line 7, column 3\n"]} "when checking that 'init' returns a value of type 'state' at line 7, column 3">>]}
, {"missing_state_type", , {"missing_state_type",
["Cannot unify string\n" [<<"Cannot unify string\n"
" and ()\n" " and ()\n"
"when checking that 'init' returns a value of type 'state' at line 5, column 3\n"]} "when checking that 'init' returns a value of type 'state' at line 5, column 3">>]}
, {"missing_fields_in_record_expression", , {"missing_fields_in_record_expression",
["The field x is missing when constructing an element of type r('a) (at line 7, column 40)\n", [<<"The field x is missing when constructing an element of type r('a) (at line 7, column 40)">>,
"The field y is missing when constructing an element of type r(int) (at line 8, column 40)\n", <<"The field y is missing when constructing an element of type r(int) (at line 8, column 40)">>,
"The fields y, z are missing when constructing an element of type r('1) (at line 6, column 40)\n"]} <<"The fields y, z are missing when constructing an element of type r('1) (at line 6, column 40)">>]}
]. ].
-2
View File
@@ -36,8 +36,6 @@ contract AllSyntax =
(x, [y, z]) => bar({x = z, y = -y + - -z * (-1)}) (x, [y, z]) => bar({x = z, y = -y + - -z * (-1)})
(x, y :: _) => () (x, y :: _) => ()
function bitOperations(x, y) = bnot (0xff00 band x bsl 4 bxor 0xa5a5a5 bsr 4 bor y)
function mutual() = function mutual() =
let rec recFun(x : int) = mutFun(x) let rec recFun(x : int) = mutFun(x)
and mutFun(x) = if(x =< 0) 1 else x * recFun(x - 1) and mutFun(x) = if(x =< 0) 1 else x * recFun(x - 1)
+13 -15
View File
@@ -5,7 +5,7 @@
contract MultiSig = contract MultiSig =
record pending_state = { yetNeeded : uint, ownersDone : uint, index : uint } record pending_state = { yetNeeded : int, ownersDone : bits, index : int }
datatype event = datatype event =
Confirmation (address, hash) // of { .owner : Address, .operation : Hash } Confirmation (address, hash) // of { .owner : Address, .operation : Hash }
@@ -13,18 +13,18 @@ contract MultiSig =
| OwnerChanged (address, address) // of { .oldOwner : Address, .newOwner : Address } | OwnerChanged (address, address) // of { .oldOwner : Address, .newOwner : Address }
| OwnerAdded (address) // of { .newOwner : Address } | OwnerAdded (address) // of { .newOwner : Address }
| OwnerRemoved (address) // of { .removedOwner : Address } | OwnerRemoved (address) // of { .removedOwner : Address }
| ReqChanged (uint) // of { .newReq : uint } | ReqChanged (int) // of { .newReq : int }
let maxOwners : uint = 250 let maxOwners : int = 250
record state = { nRequired : uint record state = { nRequired : int
, nOwners : uint , nOwners : int
, owners : map(uint, address) , owners : map(int, address)
, ownerIndex : map(address, uint) , ownerIndex : map(address, int)
, pending : map(hash, pending_state) , pending : map(hash, pending_state)
, pendingIndex : list(address) } , pendingIndex : list(address) }
function init (owners : list(address), nRequired : uint) : state = function init (owners : list(address), nRequired : int) : state =
let n = length(owners) + 1 let n = length(owners) + 1
{ nRequired = nRequired, { nRequired = nRequired,
nOwners = n, nOwners = n,
@@ -39,10 +39,9 @@ contract MultiSig =
function revoke(operation : hash) = function revoke(operation : hash) =
let ownerIx = lookup(state.ownerIndex, caller()) let ownerIx = lookup(state.ownerIndex, caller())
let pending = lookup(state.pendingIndex, operation) let pending = lookup(state.pendingIndex, operation)
let ownerIxBit = 1 bsl (ownerIx - 1) let _ = require(Bits.test(pending.ownersDone, ownerIx))
let _ = require(pending.ownersDone band ownerIxBit > 0)
let pending' = pending { yetNeeded = pending.yetNeeded + 1 let pending' = pending { yetNeeded = pending.yetNeeded + 1
, ownersDone = pending.ownersDone - ownerIxBit } , ownersDone = Bits.clear(pending.ownersDone, ownerIx - 1) }
put(state{ pendingIndex.operator = pending' }) put(state{ pendingIndex.operator = pending' })
event(Revoke(caller, operation)) event(Revoke(caller, operation))
@@ -91,7 +90,7 @@ contract MultiSig =
, pendingIx = [] }, , pendingIx = [] },
event = [OwnerRemoved(oldOwner)] } event = [OwnerRemoved(oldOwner)] }
function changeRequirement(newReq : uint) = function changeRequirement(newReq : int) =
let _ = require(newReq =< state.nOwners) let _ = require(newReq =< state.nOwners)
switch(check_pending(callhash())) switch(check_pending(callhash()))
CheckFail(state') => { state = state' } CheckFail(state') => { state = state' }
@@ -102,7 +101,7 @@ contract MultiSig =
event = [ReqChanged(newReq)] } event = [ReqChanged(newReq)] }
function getOwner(ownerIx0 : uint) = function getOwner(ownerIx0 : int) =
lookup(state.owners, ownerIx0 + 1) lookup(state.owners, ownerIx0 + 1)
function isOwner(owner : address) = function isOwner(owner : address) =
@@ -116,8 +115,7 @@ contract MultiSig =
Some(pending) => Some(pending) =>
let _ = require(isOwner(owner)) let _ = require(isOwner(owner))
let ownerIx = lookup(state.ownerIndex, owner) let ownerIx = lookup(state.ownerIndex, owner)
let ownerIxBit = 1 bsl (ownerIx - 1) Bits.test(pending.ownersDone, ownerIx - 1)
(pending.ownersDone band ownerIxBit) != 0
/* Leave the rest for now... */ /* Leave the rest for now... */
-7
View File
@@ -1,5 +1,4 @@
// - + * / mod arithmetic operators // - + * / mod arithmetic operators
// bnot band bor bxor bsl bsr bitwise operators
// ! && || logical operators // ! && || logical operators
// == != < > =< >= comparison operators // == != < > =< >= comparison operators
// :: ++ list operators // :: ++ list operators
@@ -13,12 +12,6 @@ contract Operators =
"/" => a / b "/" => a / b
"mod" => a mod b "mod" => a mod b
"^" => a ^ b "^" => a ^ b
"bnot" => bnot a
"band" => a band b
"bor" => a bor b
"bxor" => a bxor b
"bsl" => a bsl b
"bsr" => a bsr b
function bool_op(a : bool, b : bool, op : string) = function bool_op(a : bool, b : bool, op : string) =
switch(op) switch(op)