Compare commits

..

5 Commits

Author SHA1 Message Date
Hans Svensson a1d33e93ab Merge pull request #26 from aeternity/improved_roma_standalone
Improved roma standalone
2019-02-12 14:52:12 +01:00
Robert Virding d14c56d4b5 Fix testing to use new error message format 2019-02-12 11:31:30 +01:00
Hans Svensson f41b0a0ba7 Add create calldata to standalone compiler 2019-02-12 11:26:12 +01:00
Hans Svensson cef2383726 Cleanup whitespace, bad typespec, and remaining enacl reference 2019-02-12 11:26:10 +01:00
Robert Virding 3137bc4d4a Improve the interface to the compiler
It is now more consistent though we can still discuss how we want the
interface to look.
2019-02-12 11:25:34 +01:00
23 changed files with 188 additions and 467 deletions
-3
View File
@@ -1,3 +0,0 @@
{build, "rebar3 as eqc compile"}.
{test_root, "."}.
{test_path, "_build/eqc/lib/aesophia/quickcheck"}. %% here are the properties
-5
View File
@@ -16,8 +16,3 @@ _build
.idea .idea
*.iml *.iml
rebar3.crashdump rebar3.crashdump
current_counterexample.eqc
.qcci
*.erl~
*.aes~
+2 -8
View File
@@ -5,12 +5,6 @@ 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
[the æternity node](https://github.com/aeternity/epoch). However, it can [æternity Epoch](https://github.com/aeternity/epoch). However, it can
also be included in other systems to compile contracts coded in sophia which also be included in other system 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
@@ -1,86 +0,0 @@
# 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
@@ -1,109 +0,0 @@
%%% 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
@@ -1,59 +0,0 @@
%%% 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.
+1 -8
View File
@@ -1,17 +1,10 @@
{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,"720510a"}}} {ref,"99bf097"}}}
, {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,"720510a24de32c9bad6486f34ca7babde124bf1e"}}, {ref,"99bf097759dedbe7553f87a796bc7e1c7322e64b"}},
0}, 0},
{<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},0}]}. {<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},0}]}.
[ [
+4 -5
View File
@@ -17,12 +17,11 @@ line({symbol, Line, _}) -> Line.
symbol_name({symbol, _, Name}) -> Name. symbol_name({symbol, _, Name}) -> Name.
pp(Ast) -> pp(Ast) ->
%% io:format("Tree:\n~p\n",[Ast]), %% TODO: Actually do *Pretty* printing.
String = prettypr:format(aeso_pretty:decls(Ast, [])), io:format("~p~n", [Ast]).
io:format("Ast:\n~s\n", [String]).
pp_typed(TypedAst) -> pp_typed(TypedAst) ->
%% io:format("Typed tree:\n~p\n",[TypedAst]),
String = prettypr:format(aeso_pretty:decls(TypedAst, [show_generated])), String = prettypr:format(aeso_pretty:decls(TypedAst, [show_generated])),
io:format("Type ast:\n~s\n",[String]). %%io:format("Typed tree:\n~p\n",[TypedAst]),
io:format("Type info:\n~s\n",[String]).
+5 -21
View File
@@ -79,7 +79,6 @@ 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, []},
@@ -149,27 +148,10 @@ 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, Hash)}, {["String", "sha3"], Fun1(String, Int)},
{["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)}
@@ -702,7 +684,8 @@ 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 == '^'; IntOp == 'mod'; IntOp == 'bsl'; IntOp == 'bsr';
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})
@@ -724,7 +707,8 @@ 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}) when IntOp =:= '-' -> infer_prefix({IntOp,As})
when IntOp =:= '-'; IntOp =:= 'bnot' ->
Int = {id, As, "int"}, Int = {id, As, "int"},
{fun_t, As, [], [Int], Int}. {fun_t, As, [], [Int], Int}.
+6 -54
View File
@@ -316,23 +316,6 @@ 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) ->
@@ -348,33 +331,6 @@ 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)]);
@@ -553,6 +509,12 @@ 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)}.
@@ -631,16 +593,6 @@ 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) ->
+28 -45
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_copy, string_shift_copy]; builtin_deps1(string_concat) -> [string_concat_inner1, string_concat_inner2];
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,9 +81,8 @@ 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)).
%% Bit shift operations takes their arguments backwards!? -define(BSL(X, B), ?MUL(X, ?EXP(2, ?MUL(B, 8)))).
-define(BSL(X, B), op('bsl', ?MUL(B, 8), X)). -define(BSR(X, B), ?DIV(X, ?EXP(2, ?MUL(B, 8)))).
-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)}.
@@ -144,11 +143,9 @@ 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_copy -> bfun(BF, builtin_string_copy()); string_concat_inner2 -> bfun(BF, builtin_string_concat_inner2());
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));
@@ -324,8 +321,6 @@ 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)]},
@@ -334,7 +329,6 @@ 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}.
@@ -342,33 +336,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}],
?LET(w1, ?call(string_copy, [?V(n1), ?V(p1)]), ?DEREF(w1, p1,
?LET(nx, ?MOD(n1, 32), {ifte, ?GT(n1, 32),
{ifte, ?EQ(nx, 0), {seq, [?V(w1), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
?LET(w2, ?call(string_copy, [?V(n2), ?V(p2)]), ?call(string_concat_inner1, [?SUB(n1, 32), ?NXT(p1), ?V(n2), ?V(p2)])]},
{seq, [?V(w2), {inline_asm, [?A(?MSIZE), ?A(?MSTORE), ?A(?MSIZE)]}]}), {ifte, ?EQ(n1, 0),
?call(string_shift_copy, [?V(nx), ?V(w1), ?V(n2), ?V(p2)]) ?call(string_concat_inner2, [?I(32), ?I(0), ?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_shift_copy() -> builtin_string_concat_inner2() ->
{[{"off", word}, {"dst", word}, {"n", word}, {"p", pointer}], %% Current "work in progess" word 'x', has 'o' bytes that are "free" - fill them from
?DEREF(w, p, %% words of the second string.
{seq, [?ADD(dst, ?BSR(w, off)), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]}, {[{"o", word}, {"x", word}, {"n2", word}, {"p2", pointer}],
{ifte, ?GT(n, ?SUB(32, off)), {ifte, ?LT(n2, 1),
?call(string_shift_copy, [?V(off), ?BSL(w, ?SUB(32, off)), ?SUB(n, 32), ?NXT(p)]), {seq, [?V(x), {inline_asm, [?A(?MSIZE), ?A(?MSTORE), ?A(?MSIZE)]}]}, %% Use MSIZE as dummy return value
{inline_asm, [?A(?MSIZE)]}}] ?DEREF(w2, p2,
}), {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() ->
@@ -399,17 +393,6 @@ 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}.
+2 -3
View File
@@ -46,9 +46,7 @@ file(Filename) ->
file(File, Options) -> file(File, Options) ->
case read_contract(File) of case read_contract(File) of
{ok, Bin} -> from_string(Bin, Options); {ok, Bin} -> from_string(Bin, Options);
{error, Error} -> {error, Error} -> {error, {File, Error}}
ErrorString = [File,": ",file:format_error(Error)],
{error, join_errors("File errors", [ErrorString], fun(E) -> E end)}
end. end.
-spec from_string(binary() | string(), options()) -> {ok, map()} | {error, binary()}. -spec from_string(binary() | string(), options()) -> {ok, map()} | {error, binary()}.
@@ -287,3 +285,4 @@ parse_error({Line, Pos}, ErrorString) ->
read_contract(Name) -> read_contract(Name) ->
file:read_file(Name). file:read_file(Name).
-1
View File
@@ -59,7 +59,6 @@ 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,8 +562,6 @@ 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(['+', '-'])). expr600() -> infixl(expr650(), binop(['+', '-', 'bor', 'bxor', 'bsr', 'bsl'])).
expr650() -> ?RULE(many(token('-')), expr700(), prefixes(_1, _2)). expr650() -> ?RULE(many(token('-')), expr700(), prefixes(_1, _2)).
expr700() -> infixl(expr750(), binop(['*', '/', mod])). expr700() -> infixl(expr750(), binop(['*', '/', mod, 'band'])).
expr750() -> infixl(expr800(), binop(['^'])). expr750() -> infixl(expr800(), binop(['^'])).
expr800() -> ?RULE(many(token('!')), expr900(), prefixes(_1, _2)). expr800() -> ?RULE(many(choice(token('!'), token('bnot'))), expr900(), prefixes(_1, _2)).
expr900() -> ?RULE(exprAtom(), many(elim()), elim(_1, _2)). expr900() -> ?RULE(exprAtom(), many(elim()), elim(_1, _2)).
exprAtom() -> exprAtom() ->
+7 -1
View File
@@ -359,14 +359,20 @@ 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]}.
+2 -1
View File
@@ -37,7 +37,8 @@ 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 | '^' -type bin_op() :: '+' | '-' | '*' | '/' | mod | '^' | 'band' | 'bor' | 'bsl' | 'bsr' | 'bxor'
| '++' | '::' | '<' | '>' | '=<' | '>=' | '==' | '!=' | '++' | '::' | '<' | '>' | '=<' | '>=' | '==' | '!='
| '||' | '&&' | '..'. | '||' | '&&' | '..'.
-type un_op() :: '-' | '!'. -type un_op() :: '-' | '!' | 'bnot'.
-type expr() -type expr()
:: {lam, ann(), [arg()], expr()} :: {lam, ann(), [arg()], expr()}
+87 -23
View File
@@ -6,7 +6,13 @@
[ {src_file, undefined, undefined, string, "Sophia source code file"} [ {src_file, undefined, undefined, string, "Sophia source code file"}
, {verbose, $v, "verbose", undefined, "Verbose output"} , {verbose, $v, "verbose", undefined, "Verbose output"}
, {help, $h, "help", undefined, "Show this message"} , {help, $h, "help", undefined, "Show this message"}
, {outfile, $o, "out", string, "Output file (experimental)"} ]). , {create_calldata, $c, "create_calldata", string,
"Create calldata with respect to (compiled) contract in this file"}
, {create_calldata_fun, undefined, "calldata_fun", string,
"Deprecated calldata creation - using function + arguments - function"}
, {create_calldata_args, undefined, "calldata_args", string,
"Deprecated calldata creation - using function + arguments - arguments"}
, {outfile, $o, "out", string, "Output the result to file (experimental)"} ]).
usage() -> usage() ->
getopt:usage(?OPT_SPEC, "aesophia"). getopt:usage(?OPT_SPEC, "aesophia").
@@ -14,11 +20,14 @@ usage() ->
main(Args) -> main(Args) ->
case getopt:parse(?OPT_SPEC, Args) of case getopt:parse(?OPT_SPEC, Args) of
{ok, {Opts, []}} -> {ok, {Opts, []}} ->
case proplists:get_value(help, Opts, false) of IsHelp = proplists:get_value(help, Opts, false),
false -> CreateCallData = proplists:get_value(create_calldata, Opts, undefined),
compile(Opts); if IsHelp ->
usage();
CreateCallData /= undefined ->
create_calldata(CreateCallData, Opts);
true -> true ->
usage() compile(Opts)
end; end;
{ok, {_, NonOpts}} -> {ok, {_, NonOpts}} ->
@@ -44,28 +53,83 @@ compile(File, Opts) ->
Verbose = proplists:get_value(verbose, Opts, false), Verbose = proplists:get_value(verbose, Opts, false),
OutFile = proplists:get_value(outfile, Opts, undefined), OutFile = proplists:get_value(outfile, Opts, undefined),
try Res =
Res = aeso_compiler:file(File, [pp_ast || Verbose]), try aeso_compiler:file(File, [pp_ast || Verbose]) of
write_outfile(OutFile, Res), {ok, Map} ->
io:format("\nCompiled successfully!\n") io:format("\nCompiled successfully!\n"),
{ok, Map};
{error, Reason} ->
io:format("\nError: ~p\n\n", [Reason]),
{error, Reason}
catch catch
%% The compiler errors.
error:{type_errors, Errors} ->
io:format("\n~s\n", [string:join(["** Type errors\n" | Errors], "\n")]);
error:{parse_errors, Errors} ->
io:format("\n~s\n", [string:join(["** Parse errors\n" | Errors], "\n")]);
error:{code_errors, Errors} ->
ErrorStrings = [ io_lib:format("~p", [E]) || E <- Errors ],
io:format("\n~s\n", [string:join(["** Code errors\n" | ErrorStrings], "\n")]);
%% General programming errors in the compiler.
error:Error -> error:Error ->
Where = hd(erlang:get_stacktrace()), Where = hd(erlang:get_stacktrace()),
ErrorString = io_lib:format("Error: ~p in\n ~p", [Error,Where]), ErrorString = io_lib:format("Error: ~p in\n ~p", [Error, Where]),
io:format("\n~s\n", [ErrorString]) io:format("~s\n", [ErrorString]),
{error, list_to_binary(lists:flatten(ErrorString))}
end,
write_outfile(OutFile, Res).
create_calldata(ContractFile, Opts) ->
case file:read_file(ContractFile) of
{ok, Bin} ->
try
Contract = binary_to_term(Bin),
create_calldata_(Contract, Opts)
catch _:_ ->
io:format("Error: Bad contract file ~s\n\n", [ContractFile]), usage()
end;
{error, _} ->
io:format("Error: Could not find file ~s\n\n", [ContractFile]), usage()
end. end.
create_calldata_(Contract, Opts) ->
case proplists:get_value(src_file, Opts, undefined) of
undefined -> %% Check if old deprecated style is used
case {proplists:get_value(create_calldata_fun, Opts, undefined),
proplists:get_value(create_calldata_args, Opts, undefined)} of
{undefined, _} ->
io:format("Error: not enough create call data input\n\n"), usage();
{_, undefined} ->
io:format("Error: not enough create call data input\n\n"), usage();
{Fun, Args} ->
create_calldata(Contract, Fun, Args, Opts)
end;
CallFile ->
case file:read_file(CallFile) of
{ok, Bin} ->
create_calldata(Contract, "", binary_to_list(Bin), Opts);
{error, _} ->
io:format("Error: Could not find file ~s\n\n", [CallFile]), usage()
end
end.
create_calldata(Contract, CallFun, CallArgs, Opts) ->
OutFile = proplists:get_value(outfile, Opts, undefined),
Res = try
case aeso_compiler:create_calldata(Contract, CallFun, CallArgs) of
{ok, CallData, _CallDataType, _OutputType} ->
io:format("Call data created successfully!\n"),
{ok, CallData};
Err = {error, Reason} ->
io:format("Error: Create calldata failed: ~p\n\n", [Reason]),
Err
end
catch
error:Error ->
Where = hd(erlang:get_stacktrace()),
ErrorString = io_lib:format("Error: ~p in\n ~p", [Error, Where]),
io:format("~s\n", [ErrorString]),
{error, list_to_binary(lists:flatten(ErrorString))}
end,
write_outfile(OutFile, Res).
write_outfile(undefined, _) -> ok; write_outfile(undefined, _) -> ok;
write_outfile(Out, ResMap) -> write_outfile(Out, Res) ->
%% Lazy approach %% Lazy approach
file:write_file(Out, term_to_binary(ResMap)), file:write_file(Out, term_to_binary(Res)),
io:format("Output written to: ~s\n", [Out]). io:format("Output written to: ~s\n\n", [Out]).
+2
View File
@@ -36,6 +36,8 @@ 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)
+15 -13
View File
@@ -5,7 +5,7 @@
contract MultiSig = contract MultiSig =
record pending_state = { yetNeeded : int, ownersDone : bits, index : int } record pending_state = { yetNeeded : uint, ownersDone : uint, index : uint }
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 (int) // of { .newReq : int } | ReqChanged (uint) // of { .newReq : uint }
let maxOwners : int = 250 let maxOwners : uint = 250
record state = { nRequired : int record state = { nRequired : uint
, nOwners : int , nOwners : uint
, owners : map(int, address) , owners : map(uint, address)
, ownerIndex : map(address, int) , ownerIndex : map(address, uint)
, pending : map(hash, pending_state) , pending : map(hash, pending_state)
, pendingIndex : list(address) } , pendingIndex : list(address) }
function init (owners : list(address), nRequired : int) : state = function init (owners : list(address), nRequired : uint) : state =
let n = length(owners) + 1 let n = length(owners) + 1
{ nRequired = nRequired, { nRequired = nRequired,
nOwners = n, nOwners = n,
@@ -39,9 +39,10 @@ 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 _ = require(Bits.test(pending.ownersDone, ownerIx)) let ownerIxBit = 1 bsl (ownerIx - 1)
let _ = require(pending.ownersDone band ownerIxBit > 0)
let pending' = pending { yetNeeded = pending.yetNeeded + 1 let pending' = pending { yetNeeded = pending.yetNeeded + 1
, ownersDone = Bits.clear(pending.ownersDone, ownerIx - 1) } , ownersDone = pending.ownersDone - ownerIxBit }
put(state{ pendingIndex.operator = pending' }) put(state{ pendingIndex.operator = pending' })
event(Revoke(caller, operation)) event(Revoke(caller, operation))
@@ -90,7 +91,7 @@ contract MultiSig =
, pendingIx = [] }, , pendingIx = [] },
event = [OwnerRemoved(oldOwner)] } event = [OwnerRemoved(oldOwner)] }
function changeRequirement(newReq : int) = function changeRequirement(newReq : uint) =
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' }
@@ -101,7 +102,7 @@ contract MultiSig =
event = [ReqChanged(newReq)] } event = [ReqChanged(newReq)] }
function getOwner(ownerIx0 : int) = function getOwner(ownerIx0 : uint) =
lookup(state.owners, ownerIx0 + 1) lookup(state.owners, ownerIx0 + 1)
function isOwner(owner : address) = function isOwner(owner : address) =
@@ -115,7 +116,8 @@ 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)
Bits.test(pending.ownersDone, ownerIx - 1) let ownerIxBit = 1 bsl (ownerIx - 1)
(pending.ownersDone band ownerIxBit) != 0
/* Leave the rest for now... */ /* Leave the rest for now... */
+7
View File
@@ -1,4 +1,5 @@
// - + * / 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
@@ -12,6 +13,12 @@ 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)