Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| be9935cd7e | |||
| 026ff52528 | |||
| 3ba89d9f55 | |||
| 0b4c2f14fe | |||
| b65c4edd19 | |||
| 515f444e7c | |||
| d3fa04483e | |||
| 2edafe0adc | |||
| 3a7c8f905a | |||
| 9026e1fe6b | |||
| f1f2b09294 | |||
| 53299b9b17 | |||
| 64fd91197b | |||
| a73abf8e8e | |||
| db1c0fa05a | |||
| 70ad303e16 | |||
| fe1a2758c3 | |||
| 79de25b3a5 | |||
| 3e1290efaf | |||
| 9c77622c7c | |||
| a367d5040a | |||
| d8bf0bda45 | |||
| 922107e438 | |||
| f133483a90 | |||
| be42ee08ab | |||
| 86285a8ae0 | |||
| aa2e6aa218 | |||
| c5c73097fc | |||
| 23ccce4c22 | |||
| 387fdf5c34 | |||
| f0dcda27bc | |||
| 5fd24fec86 | |||
| 4bedbfee61 | |||
| e4c46ee16f | |||
| d8fff8f20f | |||
| b074aa3323 |
@@ -0,0 +1,3 @@
|
||||
{build, "rebar3 as eqc compile"}.
|
||||
{test_root, "."}.
|
||||
{test_path, "_build/eqc/lib/aesophia/quickcheck"}. %% here are the properties
|
||||
@@ -16,3 +16,8 @@ _build
|
||||
.idea
|
||||
*.iml
|
||||
rebar3.crashdump
|
||||
current_counterexample.eqc
|
||||
.qcci
|
||||
*.erl~
|
||||
*.aes~
|
||||
|
||||
|
||||
@@ -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).
|
||||
|
||||
It is an OTP application written in Erlang and is by default included in
|
||||
[æternity Epoch](https://github.com/aeternity/epoch). However, it can
|
||||
also be included in other system to compile contracts coded in sophia which
|
||||
[the æternity node](https://github.com/aeternity/epoch). However, it can
|
||||
also be included in other systems to compile contracts coded in sophia which
|
||||
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)
|
||||
|
||||
@@ -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.
|
||||
@@ -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})))).
|
||||
|
||||
@@ -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
@@ -1,10 +1,17 @@
|
||||
{erl_opts, [debug_info]}.
|
||||
|
||||
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git",
|
||||
{ref,"99bf097"}}}
|
||||
{ref,"720510a"}}}
|
||||
, {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_main_app, aesophia}.
|
||||
{escript_name, aesophia}.
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{"1.1.0",
|
||||
[{<<"aebytecode">>,
|
||||
{git,"https://github.com/aeternity/aebytecode.git",
|
||||
{ref,"99bf097759dedbe7553f87a796bc7e1c7322e64b"}},
|
||||
{ref,"720510a24de32c9bad6486f34ca7babde124bf1e"}},
|
||||
0},
|
||||
{<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},0}]}.
|
||||
[
|
||||
|
||||
+5
-4
@@ -17,11 +17,12 @@ line({symbol, Line, _}) -> Line.
|
||||
symbol_name({symbol, _, Name}) -> Name.
|
||||
|
||||
pp(Ast) ->
|
||||
%% TODO: Actually do *Pretty* printing.
|
||||
io:format("~p~n", [Ast]).
|
||||
%% io:format("Tree:\n~p\n",[Ast]),
|
||||
String = prettypr:format(aeso_pretty:decls(Ast, [])),
|
||||
io:format("Ast:\n~s\n", [String]).
|
||||
|
||||
pp_typed(TypedAst) ->
|
||||
%% io:format("Typed tree:\n~p\n",[TypedAst]),
|
||||
String = prettypr:format(aeso_pretty:decls(TypedAst, [show_generated])),
|
||||
%%io:format("Typed tree:\n~p\n",[TypedAst]),
|
||||
io:format("Type info:\n~s\n",[String]).
|
||||
io:format("Type ast:\n~s\n",[String]).
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@ global_env() ->
|
||||
Event = {id, Ann, "event"},
|
||||
State = {id, Ann, "state"},
|
||||
Hash = {id, Ann, "hash"},
|
||||
Bits = {id, Ann, "bits"},
|
||||
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,
|
||||
Unit = {tuple_t, Ann, []},
|
||||
@@ -148,12 +149,29 @@ global_env() ->
|
||||
{["Map", "delete"], Fun([K, Map(K, V)], Map(K, V))},
|
||||
{["Map", "member"], Fun([K, Map(K, V)], Bool)},
|
||||
{["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
|
||||
{["String", "length"], Fun1(String, Int)},
|
||||
{["String", "concat"], Fun([String, String], String)},
|
||||
{["String", "sha3"], Fun1(String, Int)},
|
||||
{["String", "length"], Fun1(String, Int)},
|
||||
{["String", "concat"], Fun([String, String], String)},
|
||||
{["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
|
||||
{["Int", "to_str"], Fun1(Int, String)},
|
||||
{["Int", "to_str"], Fun1(Int, String)},
|
||||
{["Address", "to_str"], Fun1(Address, String)}
|
||||
].
|
||||
|
||||
@@ -684,8 +702,7 @@ infer_infix({BoolOp, As})
|
||||
{fun_t, As, [], [Bool,Bool], Bool};
|
||||
infer_infix({IntOp, As})
|
||||
when IntOp == '+'; IntOp == '-'; IntOp == '*'; IntOp == '/';
|
||||
IntOp == '^'; IntOp == 'mod'; IntOp == 'bsl'; IntOp == 'bsr';
|
||||
IntOp == 'band'; IntOp == 'bor'; IntOp == 'bxor' ->
|
||||
IntOp == '^'; IntOp == 'mod' ->
|
||||
Int = {id, As, "int"},
|
||||
{fun_t, As, [], [Int, Int], Int};
|
||||
infer_infix({RelOp, As})
|
||||
@@ -707,8 +724,7 @@ infer_infix({'++', As}) ->
|
||||
infer_prefix({'!',As}) ->
|
||||
Bool = {id, As, "bool"},
|
||||
{fun_t, As, [], [Bool], Bool};
|
||||
infer_prefix({IntOp,As})
|
||||
when IntOp =:= '-'; IntOp =:= 'bnot' ->
|
||||
infer_prefix({IntOp,As}) when IntOp =:= '-' ->
|
||||
Int = {id, As, "int"},
|
||||
{fun_t, As, [], [Int], Int}.
|
||||
|
||||
|
||||
@@ -316,6 +316,23 @@ ast_body({map, _, Map, [Upd]}, Icode) ->
|
||||
ast_body({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
|
||||
%% -- String length
|
||||
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) ->
|
||||
#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
|
||||
ast_body(?qid_app(["Int", "to_str"], [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) ->
|
||||
#funcall{ function = #var_ref{ name = {builtin, list_concat} },
|
||||
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) ->
|
||||
#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}
|
||||
}.
|
||||
|
||||
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 }) ->
|
||||
TVars = [ X || {tvar, _, X} <- Args ],
|
||||
fun(Types) ->
|
||||
|
||||
+53
-36
@@ -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_from_list) -> [map_put];
|
||||
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(addr_to_str) -> [{baseX_int, 58}];
|
||||
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(AND(A, B), op('&&', A, B)).
|
||||
|
||||
-define(BSL(X, B), ?MUL(X, ?EXP(2, ?MUL(B, 8)))).
|
||||
-define(BSR(X, B), ?DIV(X, ?EXP(2, ?MUL(B, 8)))).
|
||||
%% Bit shift operations takes their arguments backwards!?
|
||||
-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)}.
|
||||
|
||||
@@ -143,9 +144,11 @@ builtin_function(BF) ->
|
||||
string_length -> bfun(BF, builtin_string_length());
|
||||
string_concat -> bfun(BF, builtin_string_concat());
|
||||
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 -> bfun(BF, builtin_str_equal());
|
||||
popcount -> bfun(BF, builtin_popcount());
|
||||
int_to_str -> bfun(BF, builtin_int_to_str());
|
||||
addr_to_str -> bfun(BF, builtin_addr_to_str());
|
||||
{baseX_int, X} -> bfun(BF, builtin_baseX_int(X));
|
||||
@@ -321,14 +324,17 @@ builtin_string_concat() ->
|
||||
{[{"s1", string}, {"s2", string}],
|
||||
?DEREF(n1, s1,
|
||||
?DEREF(n2, s2,
|
||||
{ifte, ?EQ(n2, 0),
|
||||
?V(s1), %% Second string is empty return first string
|
||||
?LET(ret, {inline_asm, [?A(?MSIZE)]},
|
||||
{seq, [?ADD(n1, n2), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]}, %% Store total len
|
||||
?call(string_concat_inner1, [?V(n1), ?NXT(s1), ?V(n2), ?NXT(s2)]),
|
||||
{inline_asm, [?A(?POP)]}, %% Discard fun ret val
|
||||
?V(ret) %% Put the actual return value
|
||||
]})}
|
||||
{ifte, ?EQ(n1, 0),
|
||||
?V(s2), %% First string is empty return second string
|
||||
{ifte, ?EQ(n2, 0),
|
||||
?V(s1), %% Second string is empty return first string
|
||||
?LET(ret, {inline_asm, [?A(?MSIZE)]},
|
||||
{seq, [?ADD(n1, n2), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]}, %% Store total len
|
||||
?call(string_concat_inner1, [?V(n1), ?NXT(s1), ?V(n2), ?NXT(s2)]),
|
||||
{inline_asm, [?A(?POP)]}, %% Discard fun ret val
|
||||
?V(ret) %% Put the actual return value
|
||||
]})}
|
||||
}
|
||||
)),
|
||||
word}.
|
||||
|
||||
@@ -336,33 +342,33 @@ builtin_string_concat_inner1() ->
|
||||
%% 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.
|
||||
{[{"n1", word}, {"p1", pointer}, {"n2", word}, {"p2", pointer}],
|
||||
?DEREF(w1, p1,
|
||||
{ifte, ?GT(n1, 32),
|
||||
{seq, [?V(w1), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
|
||||
?call(string_concat_inner1, [?SUB(n1, 32), ?NXT(p1), ?V(n2), ?V(p2)])]},
|
||||
{ifte, ?EQ(n1, 0),
|
||||
?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)])}
|
||||
?LET(w1, ?call(string_copy, [?V(n1), ?V(p1)]),
|
||||
?LET(nx, ?MOD(n1, 32),
|
||||
{ifte, ?EQ(nx, 0),
|
||||
?LET(w2, ?call(string_copy, [?V(n2), ?V(p2)]),
|
||||
{seq, [?V(w2), {inline_asm, [?A(?MSIZE), ?A(?MSTORE), ?A(?MSIZE)]}]}),
|
||||
?call(string_shift_copy, [?V(nx), ?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}.
|
||||
|
||||
builtin_string_concat_inner2() ->
|
||||
%% Current "work in progess" word 'x', has 'o' bytes that are "free" - fill them from
|
||||
%% words of the second string.
|
||||
{[{"o", word}, {"x", word}, {"n2", word}, {"p2", pointer}],
|
||||
{ifte, ?LT(n2, 1),
|
||||
{seq, [?V(x), {inline_asm, [?A(?MSIZE), ?A(?MSTORE), ?A(?MSIZE)]}]}, %% Use MSIZE as dummy return value
|
||||
?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
|
||||
})
|
||||
},
|
||||
builtin_string_shift_copy() ->
|
||||
{[{"off", word}, {"dst", word}, {"n", word}, {"p", pointer}],
|
||||
?DEREF(w, p,
|
||||
{seq, [?ADD(dst, ?BSR(w, off)), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
|
||||
{ifte, ?GT(n, ?SUB(32, off)),
|
||||
?call(string_shift_copy, [?V(off), ?BSL(w, ?SUB(32, off)), ?SUB(n, 32), ?NXT(p)]),
|
||||
{inline_asm, [?A(?MSIZE)]}}]
|
||||
}),
|
||||
word}.
|
||||
|
||||
builtin_str_equal_p() ->
|
||||
@@ -393,6 +399,17 @@ builtin_str_equal() ->
|
||||
)),
|
||||
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() ->
|
||||
{[{"i", word}], ?call({baseX_int, 10}, [?V(i)]), word}.
|
||||
|
||||
|
||||
@@ -46,7 +46,9 @@ file(Filename) ->
|
||||
file(File, Options) ->
|
||||
case read_contract(File) of
|
||||
{ok, Bin} -> from_string(Bin, Options);
|
||||
{error, Error} -> {error, {File, Error}}
|
||||
{error, Error} ->
|
||||
ErrorString = [File,": ",file:format_error(Error)],
|
||||
{error, join_errors("File errors", [ErrorString], fun(E) -> E end)}
|
||||
end.
|
||||
|
||||
-spec from_string(binary() | string(), options()) -> {ok, map()} | {error, binary()}.
|
||||
@@ -285,4 +287,3 @@ parse_error({Line, Pos}, ErrorString) ->
|
||||
|
||||
read_contract(Name) ->
|
||||
file:read_file(Name).
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ builtin_types() ->
|
||||
Word = fun([]) -> word end,
|
||||
#{ "bool" => Word
|
||||
, "int" => Word
|
||||
, "bits" => Word
|
||||
, "string" => fun([]) -> string end
|
||||
, "address" => Word
|
||||
, "hash" => Word
|
||||
|
||||
@@ -562,6 +562,8 @@ assemble_infix('^') -> i(?EXP);
|
||||
assemble_infix('bor') -> i(?OR);
|
||||
assemble_infix('band') -> i(?AND);
|
||||
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(?SGT);
|
||||
assemble_infix('==') -> i(?EQ);
|
||||
|
||||
+3
-3
@@ -176,11 +176,11 @@ expr200() -> infixr(expr300(), binop('||')).
|
||||
expr300() -> infixr(expr400(), binop('&&')).
|
||||
expr400() -> infix(expr500(), 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)).
|
||||
expr700() -> infixl(expr750(), binop(['*', '/', mod, 'band'])).
|
||||
expr700() -> infixl(expr750(), binop(['*', '/', mod])).
|
||||
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)).
|
||||
|
||||
exprAtom() ->
|
||||
|
||||
+1
-7
@@ -359,20 +359,14 @@ bin_prec('++') -> {500, 600, 500};
|
||||
bin_prec('::') -> {500, 600, 500};
|
||||
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(mod) -> {700, 700, 750};
|
||||
bin_prec('band') -> {700, 700, 750};
|
||||
bin_prec('^') -> {750, 750, 800}.
|
||||
|
||||
-spec un_prec(aeso_syntax:un_op()) -> {integer(), integer()}.
|
||||
un_prec('-') -> {650, 650};
|
||||
un_prec('!') -> {800, 800};
|
||||
un_prec('bnot') -> {800, 800}.
|
||||
un_prec('!') -> {800, 800}.
|
||||
|
||||
equals(Ann, A, B) ->
|
||||
{app, [{format, infix} | Ann], {'=', Ann}, [A, B]}.
|
||||
|
||||
+1
-2
@@ -37,8 +37,7 @@ lexer() ->
|
||||
, {"[^/*]+|[/*]", skip()} ],
|
||||
|
||||
Keywords = ["contract", "import", "let", "rec", "switch", "type", "record", "datatype", "if", "elif", "else", "function",
|
||||
"stateful", "true", "false", "and", "mod", "public", "private", "indexed", "internal",
|
||||
"band", "bor", "bxor", "bsl", "bsr", "bnot"],
|
||||
"stateful", "true", "false", "and", "mod", "public", "private", "indexed", "internal"],
|
||||
KW = string:join(Keywords, "|"),
|
||||
|
||||
Rules =
|
||||
|
||||
+2
-2
@@ -75,10 +75,10 @@
|
||||
|
||||
-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()
|
||||
:: {lam, ann(), [arg()], expr()}
|
||||
|
||||
+24
-88
@@ -6,13 +6,7 @@
|
||||
[ {src_file, undefined, undefined, string, "Sophia source code file"}
|
||||
, {verbose, $v, "verbose", undefined, "Verbose output"}
|
||||
, {help, $h, "help", undefined, "Show this message"}
|
||||
, {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)"} ]).
|
||||
, {outfile, $o, "out", string, "Output file (experimental)"} ]).
|
||||
|
||||
usage() ->
|
||||
getopt:usage(?OPT_SPEC, "aesophia").
|
||||
@@ -20,14 +14,11 @@ usage() ->
|
||||
main(Args) ->
|
||||
case getopt:parse(?OPT_SPEC, Args) of
|
||||
{ok, {Opts, []}} ->
|
||||
IsHelp = proplists:get_value(help, Opts, false),
|
||||
CreateCallData = proplists:get_value(create_calldata, Opts, undefined),
|
||||
if IsHelp ->
|
||||
usage();
|
||||
CreateCallData /= undefined ->
|
||||
create_calldata(CreateCallData, Opts);
|
||||
case proplists:get_value(help, Opts, false) of
|
||||
false ->
|
||||
compile(Opts);
|
||||
true ->
|
||||
compile(Opts)
|
||||
usage()
|
||||
end;
|
||||
|
||||
{ok, {_, NonOpts}} ->
|
||||
@@ -53,83 +44,28 @@ compile(File, Opts) ->
|
||||
Verbose = proplists:get_value(verbose, Opts, false),
|
||||
OutFile = proplists:get_value(outfile, Opts, undefined),
|
||||
|
||||
Res =
|
||||
try aeso_compiler:file(File, [pp_ast || Verbose]) of
|
||||
{ok, Map} ->
|
||||
io:format("\nCompiled successfully!\n"),
|
||||
{ok, Map};
|
||||
{error, Reason} ->
|
||||
io:format("\nError: ~p\n\n", [Reason]),
|
||||
{error, Reason}
|
||||
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).
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
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
|
||||
try
|
||||
Res = aeso_compiler:file(File, [pp_ast || Verbose]),
|
||||
write_outfile(OutFile, Res),
|
||||
io:format("\nCompiled successfully!\n")
|
||||
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 ->
|
||||
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).
|
||||
|
||||
ErrorString = io_lib:format("Error: ~p in\n ~p", [Error,Where]),
|
||||
io:format("\n~s\n", [ErrorString])
|
||||
end.
|
||||
|
||||
write_outfile(undefined, _) -> ok;
|
||||
write_outfile(Out, Res) ->
|
||||
write_outfile(Out, ResMap) ->
|
||||
%% Lazy approach
|
||||
file:write_file(Out, term_to_binary(Res)),
|
||||
io:format("Output written to: ~s\n\n", [Out]).
|
||||
file:write_file(Out, term_to_binary(ResMap)),
|
||||
io:format("Output written to: ~s\n", [Out]).
|
||||
|
||||
@@ -36,8 +36,6 @@ contract AllSyntax =
|
||||
(x, [y, z]) => bar({x = z, y = -y + - -z * (-1)})
|
||||
(x, y :: _) => ()
|
||||
|
||||
function bitOperations(x, y) = bnot (0xff00 band x bsl 4 bxor 0xa5a5a5 bsr 4 bor y)
|
||||
|
||||
function mutual() =
|
||||
let rec recFun(x : int) = mutFun(x)
|
||||
and mutFun(x) = if(x =< 0) 1 else x * recFun(x - 1)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
contract MultiSig =
|
||||
|
||||
record pending_state = { yetNeeded : uint, ownersDone : uint, index : uint }
|
||||
record pending_state = { yetNeeded : int, ownersDone : bits, index : int }
|
||||
|
||||
datatype event =
|
||||
Confirmation (address, hash) // of { .owner : Address, .operation : Hash }
|
||||
@@ -13,18 +13,18 @@ contract MultiSig =
|
||||
| OwnerChanged (address, address) // of { .oldOwner : Address, .newOwner : Address }
|
||||
| OwnerAdded (address) // of { .newOwner : 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
|
||||
, nOwners : uint
|
||||
, owners : map(uint, address)
|
||||
, ownerIndex : map(address, uint)
|
||||
record state = { nRequired : int
|
||||
, nOwners : int
|
||||
, owners : map(int, address)
|
||||
, ownerIndex : map(address, int)
|
||||
, pending : map(hash, pending_state)
|
||||
, pendingIndex : list(address) }
|
||||
|
||||
function init (owners : list(address), nRequired : uint) : state =
|
||||
function init (owners : list(address), nRequired : int) : state =
|
||||
let n = length(owners) + 1
|
||||
{ nRequired = nRequired,
|
||||
nOwners = n,
|
||||
@@ -39,10 +39,9 @@ contract MultiSig =
|
||||
function revoke(operation : hash) =
|
||||
let ownerIx = lookup(state.ownerIndex, caller())
|
||||
let pending = lookup(state.pendingIndex, operation)
|
||||
let ownerIxBit = 1 bsl (ownerIx - 1)
|
||||
let _ = require(pending.ownersDone band ownerIxBit > 0)
|
||||
let _ = require(Bits.test(pending.ownersDone, ownerIx))
|
||||
let pending' = pending { yetNeeded = pending.yetNeeded + 1
|
||||
, ownersDone = pending.ownersDone - ownerIxBit }
|
||||
, ownersDone = Bits.clear(pending.ownersDone, ownerIx - 1) }
|
||||
put(state{ pendingIndex.operator = pending' })
|
||||
event(Revoke(caller, operation))
|
||||
|
||||
@@ -91,7 +90,7 @@ contract MultiSig =
|
||||
, pendingIx = [] },
|
||||
event = [OwnerRemoved(oldOwner)] }
|
||||
|
||||
function changeRequirement(newReq : uint) =
|
||||
function changeRequirement(newReq : int) =
|
||||
let _ = require(newReq =< state.nOwners)
|
||||
switch(check_pending(callhash()))
|
||||
CheckFail(state') => { state = state' }
|
||||
@@ -102,7 +101,7 @@ contract MultiSig =
|
||||
event = [ReqChanged(newReq)] }
|
||||
|
||||
|
||||
function getOwner(ownerIx0 : uint) =
|
||||
function getOwner(ownerIx0 : int) =
|
||||
lookup(state.owners, ownerIx0 + 1)
|
||||
|
||||
function isOwner(owner : address) =
|
||||
@@ -116,8 +115,7 @@ contract MultiSig =
|
||||
Some(pending) =>
|
||||
let _ = require(isOwner(owner))
|
||||
let ownerIx = lookup(state.ownerIndex, owner)
|
||||
let ownerIxBit = 1 bsl (ownerIx - 1)
|
||||
(pending.ownersDone band ownerIxBit) != 0
|
||||
Bits.test(pending.ownersDone, ownerIx - 1)
|
||||
|
||||
/* Leave the rest for now... */
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// - + * / mod arithmetic operators
|
||||
// bnot band bor bxor bsl bsr bitwise operators
|
||||
// ! && || logical operators
|
||||
// == != < > =< >= comparison operators
|
||||
// :: ++ list operators
|
||||
@@ -13,12 +12,6 @@ contract Operators =
|
||||
"/" => a / b
|
||||
"mod" => a mod 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) =
|
||||
switch(op)
|
||||
|
||||
Reference in New Issue
Block a user