Compare commits
52 Commits
v2
..
quickcheck-ci
| Author | SHA1 | Date | |
|---|---|---|---|
| be9935cd7e | |||
| 026ff52528 | |||
| 3ba89d9f55 | |||
| 0b4c2f14fe | |||
| b65c4edd19 | |||
| 515f444e7c | |||
| 267fef3a5b | |||
| 362373c0d7 | |||
| 65b6791176 | |||
| 87e5562f74 | |||
| d3fa04483e | |||
| b8cb7ab1b5 | |||
| ceb7de2119 | |||
| 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 | |||
| 90e45e63d4 | |||
| 0d78a5e4a0 | |||
| b61e3270f9 | |||
| 7503ef2f3c | |||
| 028334ecb6 | |||
| 783d74dff1 | |||
| 335cd4743a | |||
| 77212b5eb3 | |||
| 79307c34df | |||
| 9187659a1e |
@@ -0,0 +1,37 @@
|
|||||||
|
version: 2.1
|
||||||
|
|
||||||
|
executors:
|
||||||
|
aebuilder:
|
||||||
|
docker:
|
||||||
|
- image: aeternity/builder
|
||||||
|
user: builder
|
||||||
|
working_directory: ~/aesophia
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
executor: aebuilder
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- restore_cache:
|
||||||
|
keys:
|
||||||
|
- dialyzer-cache-v2-{{ .Branch }}-{{ .Revision }}
|
||||||
|
- dialyzer-cache-v2-{{ .Branch }}-
|
||||||
|
- dialyzer-cache-v2-
|
||||||
|
- run:
|
||||||
|
name: Build
|
||||||
|
command: rebar3 compile
|
||||||
|
- run:
|
||||||
|
name: Static Analysis
|
||||||
|
command: rebar3 dialyzer
|
||||||
|
- run:
|
||||||
|
name: Eunit
|
||||||
|
command: rebar3 eunit
|
||||||
|
- run:
|
||||||
|
name: Common Tests
|
||||||
|
command: rebar3 ct
|
||||||
|
- save_cache:
|
||||||
|
key: dialyzer-cache-v2-{{ .Branch }}-{{ .Revision }}
|
||||||
|
paths:
|
||||||
|
- _build/default/rebar3_20.3.8_plt
|
||||||
|
- store_artifacts:
|
||||||
|
path: _build/test/logs
|
||||||
@@ -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
|
.idea
|
||||||
*.iml
|
*.iml
|
||||||
rebar3.crashdump
|
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).
|
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)
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
+23
-6
@@ -1,15 +1,32 @@
|
|||||||
{erl_opts, [debug_info]}.
|
{erl_opts, [debug_info]}.
|
||||||
|
|
||||||
%% NOTE: When possible deps are referenced by Git ref to ensure consistency between builds.
|
|
||||||
{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"}
|
||||||
% waiting for https://github.com/jlouis/enacl/pull/40 to be merged
|
|
||||||
{enacl, {git, "https://github.com/aeternity/enacl.git",
|
|
||||||
{ref, "26180f4"}}}
|
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
|
|
||||||
|
{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}.
|
||||||
|
{escript_emu_args, "%%! +sbtu +A0\n"}.
|
||||||
|
{provider_hooks, [{post, [{compile, escriptize}]}]}.
|
||||||
|
|
||||||
|
{post_hooks, [{"(linux|darwin|solaris|freebsd|netbsd|openbsd)",
|
||||||
|
escriptize,
|
||||||
|
"cp \"$REBAR_BUILD_DIR/bin/aesophia\" ./aesophia"},
|
||||||
|
{"win32",
|
||||||
|
escriptize,
|
||||||
|
"robocopy \"%REBAR_BUILD_DIR%/bin/\" ./ aesophia* "
|
||||||
|
"/njs /njh /nfl /ndl & exit /b 0"} % silence things
|
||||||
|
]}.
|
||||||
|
|
||||||
{dialyzer, [
|
{dialyzer, [
|
||||||
{warnings, [unknown]},
|
{warnings, [unknown]},
|
||||||
{plt_apps, all_deps},
|
{plt_apps, all_deps},
|
||||||
|
|||||||
+7
-5
@@ -1,8 +1,10 @@
|
|||||||
|
{"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},
|
||||||
{<<"enacl">>,
|
{<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},0}]}.
|
||||||
{git,"https://github.com/aeternity/enacl.git",
|
[
|
||||||
{ref,"26180f42c0b3a450905d2efd8bc7fd5fd9cece75"}},
|
{pkg_hash,[
|
||||||
0}].
|
{<<"getopt">>, <<"C73A9FA687B217F2FF79F68A3B637711BB1936E712B521D8CE466B29CBF7808A">>}]}
|
||||||
|
].
|
||||||
|
|||||||
+6
-6
@@ -27,8 +27,8 @@
|
|||||||
-type typerep() :: aeso_sophia:type().
|
-type typerep() :: aeso_sophia:type().
|
||||||
-type function_type_info() :: { FunctionHash :: hash()
|
-type function_type_info() :: { FunctionHash :: hash()
|
||||||
, FunctionName :: function_name()
|
, FunctionName :: function_name()
|
||||||
, ArgType :: aeso_sophia:heap() %% binary typerep
|
, ArgType :: binary() %% binary typerep
|
||||||
, OutType :: aeso_sophia:heap() %% binary typerep
|
, OutType :: binary() %% binary typerep
|
||||||
}.
|
}.
|
||||||
-type type_info() :: [function_type_info()].
|
-type type_info() :: [function_type_info()].
|
||||||
|
|
||||||
@@ -84,8 +84,8 @@ check_given_type(FunName, GivenArgs, GivenRet, CalldataType, ExpectRet) ->
|
|||||||
{expected, ExpectArgs, '=>', ExpectRet}}}
|
{expected, ExpectArgs, '=>', ExpectRet}}}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec check_calldata(aeso_sophia:heap(), type_info()) ->
|
-spec check_calldata(binary(), type_info()) ->
|
||||||
{'ok', typerep()} | {'error', atom()}.
|
{'ok', typerep(), typerep()} | {'error', atom()}.
|
||||||
check_calldata(CallData, TypeInfo) ->
|
check_calldata(CallData, TypeInfo) ->
|
||||||
%% The first element of the CallData should be the function name
|
%% The first element of the CallData should be the function name
|
||||||
case get_function_hash_from_calldata(CallData) of
|
case get_function_hash_from_calldata(CallData) of
|
||||||
@@ -136,7 +136,7 @@ function_type_hash(Name, ArgType, OutType) when is_binary(Name) ->
|
|||||||
, aeso_heap:to_binary(OutType)
|
, aeso_heap:to_binary(OutType)
|
||||||
]),
|
]),
|
||||||
%% Calculate a 256 bit digest BLAKE2b hash value of a binary
|
%% Calculate a 256 bit digest BLAKE2b hash value of a binary
|
||||||
{ok, Hash} = enacl:generichash(?HASH_SIZE, Bin),
|
{ok, Hash} = aeso_blake2:blake2b(?HASH_SIZE, Bin),
|
||||||
Hash.
|
Hash.
|
||||||
|
|
||||||
-spec arg_typerep_from_function(function_name(), type_info()) ->
|
-spec arg_typerep_from_function(function_name(), type_info()) ->
|
||||||
@@ -153,7 +153,7 @@ arg_typerep_from_function(Function, TypeInfo) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
-spec typereps_from_type_hash(hash(), type_info()) ->
|
-spec typereps_from_type_hash(hash(), type_info()) ->
|
||||||
{'ok', typerep()} | {'error', 'bad_type_data' | 'unknown_function'}.
|
{'ok', typerep(), typerep()} | {'error', 'bad_type_data' | 'unknown_function'}.
|
||||||
typereps_from_type_hash(TypeHash, TypeInfo) ->
|
typereps_from_type_hash(TypeHash, TypeInfo) ->
|
||||||
case lists:keyfind(TypeHash, 1, TypeInfo) of
|
case lists:keyfind(TypeHash, 1, TypeInfo) of
|
||||||
{TypeHash,_Function, ArgTypeBin, OutTypeBin} ->
|
{TypeHash,_Function, ArgTypeBin, OutTypeBin} ->
|
||||||
|
|||||||
+5
-4
@@ -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) ->
|
||||||
|
%% 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("Typed tree:\n~p\n",[TypedAst]),
|
io:format("Type ast:\n~s\n",[String]).
|
||||||
io:format("Type info:\n~s\n",[String]).
|
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,13 @@
|
|||||||
, kind :: project | create | update %% Projection constraints can match contract
|
, kind :: project | create | update %% Projection constraints can match contract
|
||||||
, context :: why_record() }). %% types, but field constraints only record types.
|
, context :: why_record() }). %% types, but field constraints only record types.
|
||||||
|
|
||||||
-type field_constraint() :: #field_constraint{}.
|
%% Constraint checking that 'record_t' has precisely 'fields'.
|
||||||
|
-record(record_create_constraint,
|
||||||
|
{ record_t :: utype()
|
||||||
|
, fields :: [aeso_syntax:id()]
|
||||||
|
, context :: why_record() }).
|
||||||
|
|
||||||
|
-type field_constraint() :: #field_constraint{} | #record_create_constraint{}.
|
||||||
|
|
||||||
-record(field_info,
|
-record(field_info,
|
||||||
{ field_t :: utype()
|
{ field_t :: utype()
|
||||||
@@ -73,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, []},
|
||||||
@@ -142,12 +149,29 @@ 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)}
|
||||||
].
|
].
|
||||||
|
|
||||||
@@ -514,10 +538,15 @@ infer_expr(Env, {record, Attrs, Fields}) ->
|
|||||||
RecordType = fresh_uvar(Attrs),
|
RecordType = fresh_uvar(Attrs),
|
||||||
NewFields = [{field, A, FieldName, infer_expr(Env, Expr)}
|
NewFields = [{field, A, FieldName, infer_expr(Env, Expr)}
|
||||||
|| {field, A, FieldName, Expr} <- Fields],
|
|| {field, A, FieldName, Expr} <- Fields],
|
||||||
constrain([begin
|
RecordType1 = unfold_types_in_type(RecordType),
|
||||||
|
constrain([ #record_create_constraint{
|
||||||
|
record_t = RecordType1,
|
||||||
|
fields = [ FieldName || {field, _, [{proj, _, FieldName}], _} <- Fields ],
|
||||||
|
context = Attrs } ] ++
|
||||||
|
[begin
|
||||||
[{proj, _, FieldName}] = LV,
|
[{proj, _, FieldName}] = LV,
|
||||||
#field_constraint{
|
#field_constraint{
|
||||||
record_t = unfold_types_in_type(RecordType),
|
record_t = RecordType1,
|
||||||
field = FieldName,
|
field = FieldName,
|
||||||
field_t = T,
|
field_t = T,
|
||||||
kind = create,
|
kind = create,
|
||||||
@@ -673,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})
|
||||||
@@ -696,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}.
|
||||||
|
|
||||||
@@ -971,7 +998,32 @@ get_field_constraints() ->
|
|||||||
ets_tab2list(field_constraints).
|
ets_tab2list(field_constraints).
|
||||||
|
|
||||||
solve_field_constraints() ->
|
solve_field_constraints() ->
|
||||||
solve_field_constraints(get_field_constraints()).
|
FieldCs =
|
||||||
|
lists:filter(fun(#field_constraint{}) -> true; (_) -> false end,
|
||||||
|
get_field_constraints()),
|
||||||
|
solve_field_constraints(FieldCs).
|
||||||
|
|
||||||
|
check_record_create_constraints([]) -> ok;
|
||||||
|
check_record_create_constraints([C | Cs]) ->
|
||||||
|
#record_create_constraint{
|
||||||
|
record_t = Type,
|
||||||
|
fields = Fields,
|
||||||
|
context = When } = C,
|
||||||
|
Type1 = unfold_types_in_type(instantiate(Type)),
|
||||||
|
try lookup_type(record_type_name(Type1)) of
|
||||||
|
{_, {record_t, RecFields}} ->
|
||||||
|
ActualNames = [ Fld || {field_t, _, {id, _, Fld}, _} <- RecFields ],
|
||||||
|
GivenNames = [ Fld || {id, _, Fld} <- Fields ],
|
||||||
|
case ActualNames -- GivenNames of %% We know already that we don't have too many fields
|
||||||
|
[] -> ok;
|
||||||
|
Missing -> type_error({missing_fields, When, Type1, Missing})
|
||||||
|
end;
|
||||||
|
_ -> %% We can get here if there are other type errors.
|
||||||
|
ok
|
||||||
|
catch _:_ -> %% Might be unsolved, we get a different error in that case
|
||||||
|
ok
|
||||||
|
end,
|
||||||
|
check_record_create_constraints(Cs).
|
||||||
|
|
||||||
-spec solve_field_constraints([field_constraint()]) -> ok.
|
-spec solve_field_constraints([field_constraint()]) -> ok.
|
||||||
solve_field_constraints(Constraints) ->
|
solve_field_constraints(Constraints) ->
|
||||||
@@ -1071,8 +1123,10 @@ solve_known_record_types(Constraints) ->
|
|||||||
DerefConstraints--SolvedConstraints.
|
DerefConstraints--SolvedConstraints.
|
||||||
|
|
||||||
destroy_and_report_unsolved_field_constraints() ->
|
destroy_and_report_unsolved_field_constraints() ->
|
||||||
Unsolved = get_field_constraints(),
|
{FieldCs, CreateCs} =
|
||||||
Unknown = solve_known_record_types(Unsolved),
|
lists:partition(fun(#field_constraint{}) -> true; (_) -> false end,
|
||||||
|
get_field_constraints()),
|
||||||
|
Unknown = solve_known_record_types(FieldCs),
|
||||||
if Unknown == [] -> ok;
|
if Unknown == [] -> ok;
|
||||||
true ->
|
true ->
|
||||||
case solve_unknown_record_types(Unknown) of
|
case solve_unknown_record_types(Unknown) of
|
||||||
@@ -1080,6 +1134,7 @@ destroy_and_report_unsolved_field_constraints() ->
|
|||||||
Errors -> [ type_error(Err) || Err <- Errors ]
|
Errors -> [ type_error(Err) || Err <- Errors ]
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
check_record_create_constraints(CreateCs),
|
||||||
destroy_field_constraints(),
|
destroy_field_constraints(),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
@@ -1400,6 +1455,12 @@ pp_error({ambiguous_record, Fields = [{_, First} | _], Candidates}) ->
|
|||||||
[ [" - ", pp(C), " (at ", pp_loc(C), ")\n"] || C <- Candidates ]]);
|
[ [" - ", pp(C), " (at ", pp_loc(C), ")\n"] || C <- Candidates ]]);
|
||||||
pp_error({missing_field, Field, Rec}) ->
|
pp_error({missing_field, Field, Rec}) ->
|
||||||
io_lib:format("Record type ~s does not have field ~s (at ~s)\n", [pp(Rec), pp(Field), pp_loc(Field)]);
|
io_lib:format("Record type ~s does not have field ~s (at ~s)\n", [pp(Rec), pp(Field), pp_loc(Field)]);
|
||||||
|
pp_error({missing_fields, Ann, RecType, Fields}) ->
|
||||||
|
Many = length(Fields) > 1,
|
||||||
|
S = [ "s" || Many ],
|
||||||
|
Are = if Many -> "are"; true -> "is" end,
|
||||||
|
io_lib:format("The field~s ~s ~s missing when constructing an element of type ~s (at ~s)\n",
|
||||||
|
[S, string:join(Fields, ", "), Are, pp(RecType), pp_loc(Ann)]);
|
||||||
pp_error({no_records_with_all_fields, Fields = [{_, First} | _]}) ->
|
pp_error({no_records_with_all_fields, Fields = [{_, First} | _]}) ->
|
||||||
S = [ "s" || length(Fields) > 1 ],
|
S = [ "s" || length(Fields) > 1 ],
|
||||||
io_lib:format("No record type with field~s ~s (at ~s)\n",
|
io_lib:format("No record type with field~s ~s (at ~s)\n",
|
||||||
|
|||||||
@@ -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) ->
|
||||||
|
|||||||
@@ -0,0 +1,149 @@
|
|||||||
|
%%%=============================================================================
|
||||||
|
%%% @copyright (C) 2019, Aeternity Anstalt
|
||||||
|
%%% @doc
|
||||||
|
%%% BLAKE2b implementation in Erlang - for details see: https://blake2.net
|
||||||
|
%%% @end
|
||||||
|
%%%=============================================================================
|
||||||
|
|
||||||
|
-module(aeso_blake2).
|
||||||
|
|
||||||
|
-export([ blake2b/2
|
||||||
|
, blake2b/3
|
||||||
|
]).
|
||||||
|
|
||||||
|
-define(MAX_64BIT, 16#ffffffffffffffff).
|
||||||
|
|
||||||
|
-spec blake2b(HashLen :: integer(), Msg :: binary()) -> {ok, binary()}.
|
||||||
|
blake2b(HashLen, Msg) ->
|
||||||
|
blake2b(HashLen, Msg, <<>>).
|
||||||
|
|
||||||
|
-spec blake2b(HashLen :: integer(), Msg :: binary(), Key :: binary()) -> {ok, binary()}.
|
||||||
|
blake2b(HashLen, Msg0, Key) ->
|
||||||
|
%% If message should be keyed, prepend message with padded key.
|
||||||
|
Msg = <<(pad(128, Key))/binary, Msg0/binary>>,
|
||||||
|
|
||||||
|
%% Set up the initial state
|
||||||
|
Init = (16#01010000 + (byte_size(Key) bsl 8) + HashLen),
|
||||||
|
<<H0:64, H1_7/binary>> = blake_iv(),
|
||||||
|
H = <<(H0 bxor Init):64, H1_7/binary>>,
|
||||||
|
|
||||||
|
%% Perform the compression - message will be chopped into 128-byte chunks.
|
||||||
|
State = blake2b_compress(H, Msg, 0),
|
||||||
|
|
||||||
|
%% Just return the requested part of the hash
|
||||||
|
{ok, binary_part(to_little_endian(State), {0, HashLen})}.
|
||||||
|
|
||||||
|
blake2b_compress(H, <<Chunk:(128*8), Rest/binary>>, BCompr) when Rest /= <<>> ->
|
||||||
|
H1 = blake2b_compress(H, <<Chunk:(128*8)>>, BCompr + 128, false),
|
||||||
|
blake2b_compress(H1, Rest, BCompr + 128);
|
||||||
|
blake2b_compress(H, SmallChunk, BCompr) ->
|
||||||
|
Size = byte_size(SmallChunk),
|
||||||
|
FillSize = (128 - Size) * 8,
|
||||||
|
blake2b_compress(H, <<SmallChunk/binary, 0:FillSize>>, BCompr + Size, true).
|
||||||
|
|
||||||
|
blake2b_compress(H, Chunk0, BCompr, Last) ->
|
||||||
|
Chunk = to_big_endian(Chunk0),
|
||||||
|
<<V0_11:(12*64), V12:64, V13:64, V14:64, V15:64>> = <<H/binary, (blake_iv())/binary>>,
|
||||||
|
V12_ = V12 bxor (BCompr band ?MAX_64BIT),
|
||||||
|
V13_ = V13 bxor ((BCompr bsr 64) band ?MAX_64BIT),
|
||||||
|
V14_ = case Last of
|
||||||
|
false -> V14;
|
||||||
|
true -> V14 bxor ?MAX_64BIT
|
||||||
|
end,
|
||||||
|
V = <<V0_11:(12*64), V12_:64, V13_:64, V14_:64, V15:64>>,
|
||||||
|
|
||||||
|
<<VLow:(8*64), VHigh:(8*64)>> =
|
||||||
|
lists:foldl(fun(Round, Vx) -> blake2b_mix(Round, Chunk, Vx) end, V, lists:seq(0, 11)),
|
||||||
|
|
||||||
|
<<HInt:(8*64)>> = H,
|
||||||
|
<<((HInt bxor VLow) bxor VHigh):(8*64)>>.
|
||||||
|
|
||||||
|
blake2b_mix(Rnd, Chunk, V) ->
|
||||||
|
<<V0:64, V1:64, V2:64, V3:64, V4:64, V5:64, V6:64, V7:64, V8:64,
|
||||||
|
V9:64, V10:64, V11:64, V12:64, V13:64, V14:64, V15:64>> = V,
|
||||||
|
<<M0:64, M1:64, M2:64, M3:64, M4:64, M5:64, M6:64, M7:64, M8:64,
|
||||||
|
M9:64, M10:64, M11:64, M12:64, M13:64, M14:64, M15:64>> = Chunk,
|
||||||
|
Ms = {M0, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13, M14, M15},
|
||||||
|
M = fun(Ix) -> element(Ix+1, Ms) end,
|
||||||
|
|
||||||
|
[S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13, S14, S15] = sigma(Rnd rem 10),
|
||||||
|
|
||||||
|
{Vx0, Vx4, Vx8, Vx12} = blake2b_mix(V0, V4, V8, V12, M(S0), M(S1)),
|
||||||
|
{Vx1, Vx5, Vx9, Vx13} = blake2b_mix(V1, V5, V9, V13, M(S2), M(S3)),
|
||||||
|
{Vx2, Vx6, Vx10, Vx14} = blake2b_mix(V2, V6, V10, V14, M(S4), M(S5)),
|
||||||
|
{Vx3, Vx7, Vx11, Vx15} = blake2b_mix(V3, V7, V11, V15, M(S6), M(S7)),
|
||||||
|
|
||||||
|
{Vy0, Vy5, Vy10, Vy15} = blake2b_mix(Vx0, Vx5, Vx10, Vx15, M(S8), M(S9)),
|
||||||
|
{Vy1, Vy6, Vy11, Vy12} = blake2b_mix(Vx1, Vx6, Vx11, Vx12, M(S10), M(S11)),
|
||||||
|
{Vy2, Vy7, Vy8, Vy13} = blake2b_mix(Vx2, Vx7, Vx8, Vx13, M(S12), M(S13)),
|
||||||
|
{Vy3, Vy4, Vy9, Vy14} = blake2b_mix(Vx3, Vx4, Vx9, Vx14, M(S14), M(S15)),
|
||||||
|
|
||||||
|
<<Vy0:64, Vy1:64, Vy2:64, Vy3:64, Vy4:64, Vy5:64, Vy6:64, Vy7:64, Vy8:64,
|
||||||
|
Vy9:64, Vy10:64, Vy11:64, Vy12:64, Vy13:64, Vy14:64, Vy15:64>>.
|
||||||
|
|
||||||
|
blake2b_mix(Va, Vb, Vc, Vd, X, Y) ->
|
||||||
|
Va1 = (Va + Vb + X) band ?MAX_64BIT,
|
||||||
|
Vd1 = rotr64(32, Vd bxor Va1),
|
||||||
|
|
||||||
|
Vc1 = (Vc + Vd1) band ?MAX_64BIT,
|
||||||
|
Vb1 = rotr64(24, Vb bxor Vc1),
|
||||||
|
|
||||||
|
Va2 = (Va1 + Vb1 + Y) band ?MAX_64BIT,
|
||||||
|
Vd2 = rotr64(16, Va2 bxor Vd1),
|
||||||
|
|
||||||
|
Vc2 = (Vc1 + Vd2) band ?MAX_64BIT,
|
||||||
|
Vb2 = rotr64(63, Vb1 bxor Vc2),
|
||||||
|
|
||||||
|
{Va2, Vb2, Vc2, Vd2}.
|
||||||
|
|
||||||
|
blake_iv() ->
|
||||||
|
IV0 = 16#6A09E667F3BCC908,
|
||||||
|
IV1 = 16#BB67AE8584CAA73B,
|
||||||
|
IV2 = 16#3C6EF372FE94F82B,
|
||||||
|
IV3 = 16#A54FF53A5F1D36F1,
|
||||||
|
IV4 = 16#510E527FADE682D1,
|
||||||
|
IV5 = 16#9B05688C2B3E6C1F,
|
||||||
|
IV6 = 16#1F83D9ABFB41BD6B,
|
||||||
|
IV7 = 16#5BE0CD19137E2179,
|
||||||
|
<<IV0:64, IV1:64, IV2:64, IV3:64, IV4:64, IV5:64, IV6:64, IV7:64>>.
|
||||||
|
|
||||||
|
sigma(N) ->
|
||||||
|
{_, Row} = lists:keyfind(N, 1, sigma()), Row.
|
||||||
|
|
||||||
|
sigma() ->
|
||||||
|
[{0, [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]},
|
||||||
|
{1, [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3]},
|
||||||
|
{2, [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4]},
|
||||||
|
{3, [ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8]},
|
||||||
|
{4, [ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13]},
|
||||||
|
{5, [ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9]},
|
||||||
|
{6, [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11]},
|
||||||
|
{7, [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10]},
|
||||||
|
{8, [ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5]},
|
||||||
|
{9, [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0]}].
|
||||||
|
|
||||||
|
rotr64(N, I64) ->
|
||||||
|
<<I64rot:64>> = rotr641(N, <<I64:64>>),
|
||||||
|
I64rot.
|
||||||
|
|
||||||
|
rotr641(16, <<X:(64-16), Y:16>>) -> <<Y:16, X:(64-16)>>;
|
||||||
|
rotr641(24, <<X:(64-24), Y:24>>) -> <<Y:24, X:(64-24)>>;
|
||||||
|
rotr641(32, <<X:(64-32), Y:32>>) -> <<Y:32, X:(64-32)>>;
|
||||||
|
rotr641(63, <<X:(64-63), Y:63>>) -> <<Y:63, X:(64-63)>>.
|
||||||
|
|
||||||
|
pad(N, Bin) ->
|
||||||
|
case (N - (byte_size(Bin) rem N)) rem N of
|
||||||
|
0 -> Bin;
|
||||||
|
Pad -> <<Bin/binary, 0:(Pad *8)>>
|
||||||
|
end.
|
||||||
|
|
||||||
|
to_big_endian(Bin) -> to_big_endian(Bin, <<>>).
|
||||||
|
to_big_endian(<<>>, Acc) -> Acc;
|
||||||
|
to_big_endian(<<UInt64:1/little-unsigned-integer-unit:64, Rest/binary>>, Acc) ->
|
||||||
|
to_big_endian(Rest, <<Acc/binary, UInt64:1/big-unsigned-integer-unit:64>>).
|
||||||
|
|
||||||
|
to_little_endian(Bin) -> to_little_endian(Bin, <<>>).
|
||||||
|
to_little_endian(<<>>, Acc) -> Acc;
|
||||||
|
to_little_endian(<<UInt64:1/big-unsigned-integer-unit:64, Rest/binary>>, Acc) ->
|
||||||
|
to_little_endian(Rest, <<Acc/binary, UInt64:1/little-unsigned-integer-unit:64>>).
|
||||||
|
|
||||||
+255
-265
@@ -38,11 +38,12 @@ 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) -> [int_to_str_, int_digits];
|
builtin_deps1(int_to_str) -> [{baseX_int, 10}];
|
||||||
builtin_deps1(addr_to_str) -> [base58_int, string_concat];
|
builtin_deps1(addr_to_str) -> [{baseX_int, 58}];
|
||||||
builtin_deps1(base58_int) -> [base58_int_encode, base58_int_pad, string_reverse, string_concat];
|
builtin_deps1({baseX_int, X}) -> [{baseX_int_pad, X}];
|
||||||
builtin_deps1(base58_int_encode) -> [base58_int_encode_, base58_tab];
|
builtin_deps1({baseX_int_pad, X}) -> [{baseX_int_encode, X}];
|
||||||
|
builtin_deps1({baseX_int_encode, X}) -> [{baseX_int_encode_, X}, {baseX_tab, X}, {baseX_digits, X}];
|
||||||
builtin_deps1(string_reverse) -> [string_reverse_];
|
builtin_deps1(string_reverse) -> [string_reverse_];
|
||||||
builtin_deps1(_) -> [].
|
builtin_deps1(_) -> [].
|
||||||
|
|
||||||
@@ -80,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)}.
|
||||||
|
|
||||||
@@ -108,8 +110,6 @@ check_event_type(Evts, Icode) ->
|
|||||||
|| {constr_t, _, {con, _, Name}, Types} <- Evts, T <- Types ].
|
|| {constr_t, _, {con, _, Name}, Types} <- Evts, T <- Types ].
|
||||||
|
|
||||||
check_event_type(EvtName, Type, Icode) ->
|
check_event_type(EvtName, Type, Icode) ->
|
||||||
io:format("~p: ~p??\n", [EvtName, Type]),
|
|
||||||
io:format("=> ~p\n", [aeso_ast_to_icode:ast_typerep(Type, Icode)]),
|
|
||||||
VMType =
|
VMType =
|
||||||
try
|
try
|
||||||
aeso_ast_to_icode:ast_typerep(Type, Icode)
|
aeso_ast_to_icode:ast_typerep(Type, Icode)
|
||||||
@@ -123,11 +123,49 @@ check_event_type(EvtName, Type, Icode) ->
|
|||||||
false -> error({EvtName, payload_should_be_string, is, VMType})
|
false -> error({EvtName, payload_should_be_string, is, VMType})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
bfun(B, {IArgs, IExpr, IRet}) ->
|
||||||
|
{{builtin, B}, [private], IArgs, IExpr, IRet}.
|
||||||
|
|
||||||
|
builtin_function(BF) ->
|
||||||
|
case BF of
|
||||||
|
{event, EventT} -> bfun(BF, builtin_event(EventT));
|
||||||
|
abort -> bfun(BF, builtin_abort());
|
||||||
|
{map_lookup, Type} -> bfun(BF, builtin_map_lookup(Type));
|
||||||
|
map_put -> bfun(BF, builtin_map_put());
|
||||||
|
map_delete -> bfun(BF, builtin_map_delete());
|
||||||
|
map_size -> bfun(BF, builtin_map_size());
|
||||||
|
{map_get, Type} -> bfun(BF, builtin_map_get(Type));
|
||||||
|
{map_lookup_default, Type} -> bfun(BF, builtin_map_lookup_default(Type));
|
||||||
|
map_member -> bfun(BF, builtin_map_member());
|
||||||
|
{map_upd, Type} -> bfun(BF, builtin_map_upd(Type));
|
||||||
|
{map_upd_default, Type} -> bfun(BF, builtin_map_upd_default(Type));
|
||||||
|
map_from_list -> bfun(BF, builtin_map_from_list());
|
||||||
|
list_concat -> bfun(BF, builtin_list_concat());
|
||||||
|
string_length -> bfun(BF, builtin_string_length());
|
||||||
|
string_concat -> bfun(BF, builtin_string_concat());
|
||||||
|
string_concat_inner1 -> bfun(BF, builtin_string_concat_inner1());
|
||||||
|
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));
|
||||||
|
{baseX_digits, X} -> bfun(BF, builtin_baseX_digits(X));
|
||||||
|
{baseX_tab, X} -> bfun(BF, builtin_baseX_tab(X));
|
||||||
|
{baseX_int_pad, X} -> bfun(BF, builtin_baseX_int_pad(X));
|
||||||
|
{baseX_int_encode, X} -> bfun(BF, builtin_baseX_int_encode(X));
|
||||||
|
{baseX_int_encode_, X} -> bfun(BF, builtin_baseX_int_encode_(X));
|
||||||
|
string_reverse -> bfun(BF, builtin_string_reverse());
|
||||||
|
string_reverse_ -> bfun(BF, builtin_string_reverse_())
|
||||||
|
end.
|
||||||
|
|
||||||
%% Event primitive (dependent on Event type)
|
%% Event primitive (dependent on Event type)
|
||||||
%%
|
%%
|
||||||
%% We need to switch on the event and prepare the correct #event for icode_to_asm
|
%% We need to switch on the event and prepare the correct #event for icode_to_asm
|
||||||
%% NOTE: we assume all errors are already checked!
|
%% NOTE: we assume all errors are already checked!
|
||||||
builtin_function(Builtin = {event, EventT}) ->
|
builtin_event(EventT) ->
|
||||||
A = fun(X) -> aeb_opcodes:mnemonic(X) end,
|
A = fun(X) -> aeb_opcodes:mnemonic(X) end,
|
||||||
VIx = fun(Ix) -> v(lists:concat(["v", Ix])) end,
|
VIx = fun(Ix) -> v(lists:concat(["v", Ix])) end,
|
||||||
ArgPats = fun(Ts) -> [ VIx(Ix) || Ix <- lists:seq(0, length(Ts) - 1) ] end,
|
ArgPats = fun(Ts) -> [ VIx(Ix) || Ix <- lists:seq(0, length(Ts) - 1) ] end,
|
||||||
@@ -149,149 +187,132 @@ builtin_function(Builtin = {event, EventT}) ->
|
|||||||
{variant_t, Cons} = EventT,
|
{variant_t, Cons} = EventT,
|
||||||
Tags = lists:seq(0, length(Cons) - 1),
|
Tags = lists:seq(0, length(Cons) - 1),
|
||||||
|
|
||||||
{{builtin, Builtin}, [private],
|
{[{"e", event}],
|
||||||
[{"e", event}],
|
{switch, v(e),
|
||||||
{switch, v(e),
|
[{Pat(Tag, Types), Clause(Tag, Con, Types)}
|
||||||
[{Pat(Tag, Types), Clause(Tag, Con, Types)}
|
|| {Tag, {constr_t, _, Con, Types}} <- lists:zip(Tags, Cons) ]},
|
||||||
|| {Tag, {constr_t, _, Con, Types}} <- lists:zip(Tags, Cons) ]},
|
{tuple, []}}.
|
||||||
{tuple, []}};
|
|
||||||
|
|
||||||
%% Abort primitive.
|
%% Abort primitive.
|
||||||
builtin_function(abort) ->
|
builtin_abort() ->
|
||||||
A = fun(X) -> aeb_opcodes:mnemonic(X) end,
|
A = fun(X) -> aeb_opcodes:mnemonic(X) end,
|
||||||
{{builtin, abort}, [private],
|
{[{"s", string}],
|
||||||
[{"s", string}],
|
|
||||||
{inline_asm, [A(?PUSH1),0, %% Push a dummy 0 for the first arg
|
{inline_asm, [A(?PUSH1),0, %% Push a dummy 0 for the first arg
|
||||||
A(?REVERT)]}, %% Stack: 0,Ptr
|
A(?REVERT)]}, %% Stack: 0,Ptr
|
||||||
{tuple,[]}};
|
{tuple,[]}}.
|
||||||
|
|
||||||
%% Map primitives
|
%% Map primitives
|
||||||
builtin_function(Builtin = {map_lookup, Type}) ->
|
builtin_map_lookup(Type) ->
|
||||||
Ret = aeso_icode:option_typerep(Type),
|
Ret = aeso_icode:option_typerep(Type),
|
||||||
{{builtin, Builtin}, [private],
|
{[{"m", word}, {"k", word}],
|
||||||
[{"m", word}, {"k", word}],
|
prim_call(?PRIM_CALL_MAP_GET, #integer{value = 0},
|
||||||
prim_call(?PRIM_CALL_MAP_GET, #integer{value = 0},
|
[#var_ref{name = "m"}, #var_ref{name = "k"}],
|
||||||
[#var_ref{name = "m"}, #var_ref{name = "k"}],
|
[word, word], Ret),
|
||||||
[word, word], Ret),
|
Ret}.
|
||||||
Ret};
|
|
||||||
|
|
||||||
builtin_function(Builtin = map_put) ->
|
builtin_map_put() ->
|
||||||
%% We don't need the types for put.
|
%% We don't need the types for put.
|
||||||
{{builtin, Builtin}, [private],
|
{[{"m", word}, {"k", word}, {"v", word}],
|
||||||
[{"m", word}, {"k", word}, {"v", word}],
|
prim_call(?PRIM_CALL_MAP_PUT, #integer{value = 0},
|
||||||
prim_call(?PRIM_CALL_MAP_PUT, #integer{value = 0},
|
[v(m), v(k), v(v)], [word, word, word], word),
|
||||||
[v(m), v(k), v(v)],
|
word}.
|
||||||
[word, word, word], word),
|
|
||||||
word};
|
|
||||||
|
|
||||||
builtin_function(Builtin = map_delete) ->
|
builtin_map_delete() ->
|
||||||
{{builtin, Builtin}, [private],
|
{[{"m", word}, {"k", word}],
|
||||||
[{"m", word}, {"k", word}],
|
prim_call(?PRIM_CALL_MAP_DELETE, #integer{value = 0},
|
||||||
prim_call(?PRIM_CALL_MAP_DELETE, #integer{value = 0},
|
[v(m), v(k)], [word, word], word),
|
||||||
[v(m), v(k)],
|
word}.
|
||||||
[word, word], word),
|
|
||||||
word};
|
|
||||||
|
|
||||||
builtin_function(Builtin = map_size) ->
|
builtin_map_size() ->
|
||||||
Name = {builtin, Builtin},
|
{[{"m", word}],
|
||||||
{Name, [private], [{"m", word}],
|
prim_call(?PRIM_CALL_MAP_SIZE, #integer{value = 0},
|
||||||
prim_call(?PRIM_CALL_MAP_SIZE, #integer{value = 0},
|
[v(m)], [word], word),
|
||||||
[v(m)], [word], word),
|
word}.
|
||||||
word};
|
|
||||||
|
|
||||||
%% Map builtins
|
%% Map builtins
|
||||||
builtin_function(Builtin = {map_get, Type}) ->
|
builtin_map_get(Type) ->
|
||||||
%% function map_get(m, k) =
|
%% function map_get(m, k) =
|
||||||
%% switch(map_lookup(m, k))
|
%% switch(map_lookup(m, k))
|
||||||
%% Some(v) => v
|
%% Some(v) => v
|
||||||
{{builtin, Builtin}, [private],
|
{[{"m", word}, {"k", word}],
|
||||||
[{"m", word}, {"k", word}],
|
{switch, ?call({map_lookup, Type}, [v(m), v(k)]), [{option_some(v(v)), v(v)}]},
|
||||||
{switch, ?call({map_lookup, Type}, [v(m), v(k)]),
|
Type}.
|
||||||
[{option_some(v(v)), v(v)}]},
|
|
||||||
Type};
|
|
||||||
|
|
||||||
builtin_function(Builtin = {map_lookup_default, Type}) ->
|
builtin_map_lookup_default(Type) ->
|
||||||
%% function map_lookup_default(m, k, default) =
|
%% function map_lookup_default(m, k, default) =
|
||||||
%% switch(map_lookup(m, k))
|
%% switch(map_lookup(m, k))
|
||||||
%% None => default
|
%% None => default
|
||||||
%% Some(v) => v
|
%% Some(v) => v
|
||||||
{{builtin, Builtin}, [private],
|
{[{"m", word}, {"k", word}, {"default", Type}],
|
||||||
[{"m", word}, {"k", word}, {"default", Type}],
|
{switch, ?call({map_lookup, Type}, [v(m), v(k)]),
|
||||||
{switch, ?call({map_lookup, Type}, [v(m), v(k)]),
|
[{option_none(), v(default)},
|
||||||
[{option_none(), v(default)},
|
{option_some(v(v)), v(v)}]},
|
||||||
{option_some(v(v)), v(v)}]},
|
Type}.
|
||||||
Type};
|
|
||||||
|
|
||||||
builtin_function(Builtin = map_member) ->
|
builtin_map_member() ->
|
||||||
%% function map_member(m, k) : bool =
|
%% function map_member(m, k) : bool =
|
||||||
%% switch(Map.lookup(m, k))
|
%% switch(Map.lookup(m, k))
|
||||||
%% None => false
|
%% None => false
|
||||||
%% _ => true
|
%% _ => true
|
||||||
{{builtin, Builtin}, [private],
|
{[{"m", word}, {"k", word}],
|
||||||
[{"m", word}, {"k", word}],
|
{switch, ?call({map_lookup, word}, [v(m), v(k)]),
|
||||||
{switch, ?call({map_lookup, word}, [v(m), v(k)]),
|
[{option_none(), {integer, 0}},
|
||||||
[{option_none(), {integer, 0}},
|
{{var_ref, "_"}, {integer, 1}}]},
|
||||||
{{var_ref, "_"}, {integer, 1}}]},
|
word}.
|
||||||
word};
|
|
||||||
|
|
||||||
builtin_function(Builtin = {map_upd, Type}) ->
|
builtin_map_upd(Type) ->
|
||||||
%% function map_upd(map, key, fun) =
|
%% function map_upd(map, key, fun) =
|
||||||
%% map_put(map, key, fun(map_get(map, key)))
|
%% map_put(map, key, fun(map_get(map, key)))
|
||||||
{{builtin, Builtin}, [private],
|
{[{"map", word}, {"key", word}, {"valfun", word}],
|
||||||
[{"map", word}, {"key", word}, {"valfun", word}],
|
|
||||||
?call(map_put,
|
?call(map_put,
|
||||||
[v(map), v(key),
|
[v(map), v(key),
|
||||||
#funcall{ function = v(valfun),
|
#funcall{ function = v(valfun),
|
||||||
args = [?call({map_get, Type}, [v(map), v(key)])] }]),
|
args = [?call({map_get, Type}, [v(map), v(key)])] }]),
|
||||||
word};
|
word}.
|
||||||
|
|
||||||
builtin_function(Builtin = {map_upd_default, Type}) ->
|
builtin_map_upd_default(Type) ->
|
||||||
%% function map_upd(map, key, val, fun) =
|
%% function map_upd(map, key, val, fun) =
|
||||||
%% map_put(map, key, fun(map_lookup_default(map, key, val)))
|
%% map_put(map, key, fun(map_lookup_default(map, key, val)))
|
||||||
{{builtin, Builtin}, [private],
|
{[{"map", word}, {"key", word}, {"val", word}, {"valfun", word}],
|
||||||
[{"map", word}, {"key", word}, {"val", word}, {"valfun", word}],
|
|
||||||
?call(map_put,
|
?call(map_put,
|
||||||
[v(map), v(key),
|
[v(map), v(key),
|
||||||
#funcall{ function = v(valfun),
|
#funcall{ function = v(valfun),
|
||||||
args = [?call({map_lookup_default, Type}, [v(map), v(key), v(val)])] }]),
|
args = [?call({map_lookup_default, Type}, [v(map), v(key), v(val)])] }]),
|
||||||
word};
|
word}.
|
||||||
|
|
||||||
builtin_function(Builtin = map_from_list) ->
|
builtin_map_from_list() ->
|
||||||
%% function map_from_list(xs, acc) =
|
%% function map_from_list(xs, acc) =
|
||||||
%% switch(xs)
|
%% switch(xs)
|
||||||
%% [] => acc
|
%% [] => acc
|
||||||
%% (k, v) :: xs => map_from_list(xs, acc { [k] = v })
|
%% (k, v) :: xs => map_from_list(xs, acc { [k] = v })
|
||||||
{{builtin, Builtin}, [private],
|
{[{"xs", {list, {tuple, [word, word]}}}, {"acc", word}],
|
||||||
[{"xs", {list, {tuple, [word, word]}}}, {"acc", word}],
|
|
||||||
{switch, v(xs),
|
{switch, v(xs),
|
||||||
[{{list, []}, v(acc)},
|
[{{list, []}, v(acc)},
|
||||||
{{binop, '::', {tuple, [v(k), v(v)]}, v(ys)},
|
{{binop, '::', {tuple, [v(k), v(v)]}, v(ys)},
|
||||||
?call(map_from_list,
|
?call(map_from_list,
|
||||||
[v(ys), ?call(map_put, [v(acc), v(k), v(v)])])}]},
|
[v(ys), ?call(map_put, [v(acc), v(k), v(v)])])}]},
|
||||||
word};
|
word}.
|
||||||
|
|
||||||
%% list_concat
|
%% list_concat
|
||||||
%%
|
%%
|
||||||
%% Concatenates two lists.
|
%% Concatenates two lists.
|
||||||
builtin_function(list_concat) ->
|
builtin_list_concat() ->
|
||||||
{{builtin, list_concat}, [private],
|
{[{"l1", {list, word}}, {"l2", {list, word}}],
|
||||||
[{"l1", {list, word}}, {"l2", {list, word}}],
|
|
||||||
{switch, v(l1),
|
{switch, v(l1),
|
||||||
[{{list, []}, v(l2)},
|
[{{list, []}, v(l2)},
|
||||||
{{binop, '::', v(hd), v(tl)},
|
{{binop, '::', v(hd), v(tl)},
|
||||||
{binop, '::', v(hd), ?call(list_concat, [v(tl), v(l2)])}}
|
{binop, '::', v(hd), ?call(list_concat, [v(tl), v(l2)])}}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
word};
|
word}.
|
||||||
|
|
||||||
builtin_function(string_length) ->
|
builtin_string_length() ->
|
||||||
%% function length(str) =
|
%% function length(str) =
|
||||||
%% switch(str)
|
%% switch(str)
|
||||||
%% {n} -> n // (ab)use the representation
|
%% {n} -> n // (ab)use the representation
|
||||||
{{builtin, string_length}, [private],
|
{[{"s", string}],
|
||||||
[{"s", string}],
|
?DEREF(n, s, ?V(n)),
|
||||||
?DEREF(n, s, ?V(n)),
|
word}.
|
||||||
word};
|
|
||||||
|
|
||||||
%% str_concat - concatenate two strings
|
%% str_concat - concatenate two strings
|
||||||
%%
|
%%
|
||||||
@@ -299,212 +320,181 @@ builtin_function(string_length) ->
|
|||||||
%% top of the Heap and the address to it is returned. The tricky bit is when
|
%% top of the Heap and the address to it is returned. The tricky bit is when
|
||||||
%% the words from the second string has to be shifted to fit next to the first
|
%% the words from the second string has to be shifted to fit next to the first
|
||||||
%% string.
|
%% string.
|
||||||
builtin_function(string_concat) ->
|
builtin_string_concat() ->
|
||||||
{{builtin, string_concat}, [private],
|
{[{"s1", string}, {"s2", string}],
|
||||||
[{"s1", string}, {"s2", string}],
|
|
||||||
?DEREF(n1, s1,
|
?DEREF(n1, s1,
|
||||||
?DEREF(n2, s2,
|
?DEREF(n2, s2,
|
||||||
{ifte, ?EQ(n2, 0),
|
{ifte, ?EQ(n1, 0),
|
||||||
?V(s1), %% Second string is empty return first string
|
?V(s2), %% First string is empty return second string
|
||||||
?LET(ret, {inline_asm, [?A(?MSIZE)]},
|
{ifte, ?EQ(n2, 0),
|
||||||
{seq, [?ADD(n1, n2), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]}, %% Store total len
|
?V(s1), %% Second string is empty return first string
|
||||||
?call(string_concat_inner1, [?V(n1), ?NXT(s1), ?V(n2), ?NXT(s2)]),
|
?LET(ret, {inline_asm, [?A(?MSIZE)]},
|
||||||
{inline_asm, [?A(?POP)]}, %% Discard fun ret val
|
{seq, [?ADD(n1, n2), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]}, %% Store total len
|
||||||
?V(ret) %% Put the actual return value
|
?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};
|
word}.
|
||||||
|
|
||||||
builtin_function(string_concat_inner1) ->
|
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.
|
||||||
{{builtin, string_concat_inner1}, [private],
|
{[{"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_function(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,
|
||||||
{{builtin, string_concat_inner2}, [private],
|
{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),
|
word}.
|
||||||
{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};
|
|
||||||
|
|
||||||
builtin_function(str_equal_p) ->
|
builtin_str_equal_p() ->
|
||||||
%% function str_equal_p(n, p1, p2) =
|
%% function str_equal_p(n, p1, p2) =
|
||||||
%% if(n =< 0) true
|
%% if(n =< 0) true
|
||||||
%% else
|
%% else
|
||||||
%% let w1 = *p1
|
%% let w1 = *p1
|
||||||
%% let w2 = *p2
|
%% let w2 = *p2
|
||||||
%% w1 == w2 && str_equal_p(n - 32, p1 + 32, p2 + 32)
|
%% w1 == w2 && str_equal_p(n - 32, p1 + 32, p2 + 32)
|
||||||
{{builtin, str_equal_p}, [private],
|
{[{"n", word}, {"p1", pointer}, {"p2", pointer}],
|
||||||
[{"n", word}, {"p1", pointer}, {"p2", pointer}],
|
{ifte, ?LT(n, 1),
|
||||||
{ifte, ?LT(n, 1),
|
?I(1),
|
||||||
?I(1),
|
?DEREF(w1, p1,
|
||||||
?DEREF(w1, p1,
|
?DEREF(w2, p2,
|
||||||
?DEREF(w2, p2,
|
?AND(?EQ(w1, w2),
|
||||||
?AND(?EQ(w1, w2),
|
?call(str_equal_p, [?SUB(n, 32), ?NXT(p1), ?NXT(p2)]))))},
|
||||||
?call(str_equal_p, [?SUB(n, 32), ?NXT(p1), ?NXT(p2)]))))},
|
word}.
|
||||||
word};
|
|
||||||
|
|
||||||
builtin_function(str_equal) ->
|
builtin_str_equal() ->
|
||||||
%% function str_equal(s1, s2) =
|
%% function str_equal(s1, s2) =
|
||||||
%% let n1 = length(s1)
|
%% let n1 = length(s1)
|
||||||
%% let n2 = length(s2)
|
%% let n2 = length(s2)
|
||||||
%% n1 == n2 && str_equal_p(n1, s1 + 32, s2 + 32)
|
%% n1 == n2 && str_equal_p(n1, s1 + 32, s2 + 32)
|
||||||
{{builtin, str_equal}, [private],
|
{[{"s1", string}, {"s2", string}],
|
||||||
[{"s1", string}, {"s2", string}],
|
?DEREF(n1, s1,
|
||||||
?DEREF(n1, s1,
|
?DEREF(n2, s2,
|
||||||
?DEREF(n2, s2,
|
?AND(?EQ(n1, n2), ?call(str_equal_p, [?V(n1), ?NXT(s1), ?NXT(s2)]))
|
||||||
?AND(?EQ(n1, n2), ?call(str_equal_p, [?V(n1), ?NXT(s1), ?NXT(s2)]))
|
)),
|
||||||
)),
|
word}.
|
||||||
word};
|
|
||||||
|
|
||||||
builtin_function(int_to_str) ->
|
%% Count the number of 1s in a bit field.
|
||||||
{{builtin, int_to_str}, [private],
|
builtin_popcount() ->
|
||||||
[{"i0", word}],
|
%% function popcount(bits, acc) =
|
||||||
{switch, {ifte, ?LT(i0, 0),
|
%% if (bits == 0) acc
|
||||||
{tuple, [?I(2), ?NEG(i0), ?BSL(45, 31)]},
|
%% else popcount(bits bsr 1, acc + bits band 1)
|
||||||
{tuple, [?I(1), ?V(i0), ?I(0)]}},
|
{[{"bits", word}, {"acc", word}],
|
||||||
[{{tuple, [v(off), v(i), v(x)]},
|
{ifte, ?EQ(bits, 0),
|
||||||
?LET(ret, {inline_asm, [?A(?MSIZE)]},
|
?V(acc),
|
||||||
?LET(n, ?call(int_digits, [?DIV(i, 10), ?I(0)]),
|
?call(popcount, [op('bsr', 1, bits), ?ADD(acc, op('band', bits, 1))])
|
||||||
?LET(fac, ?EXP(10, n),
|
}, word}.
|
||||||
{seq, [?ADD(n, off), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]}, %% Store str len
|
|
||||||
?call(int_to_str_,
|
|
||||||
[?MOD(i, fac), ?ADD(x, ?BSL(?ADD(48, ?DIV(i, fac)), ?SUB(32, off))), ?DIV(fac, 10), ?V(off)]),
|
|
||||||
{inline_asm, [?A(?POP)]}, ?V(ret)]}
|
|
||||||
)))}]},
|
|
||||||
word};
|
|
||||||
|
|
||||||
builtin_function(int_to_str_) ->
|
builtin_int_to_str() ->
|
||||||
{{builtin, int_to_str_}, [private],
|
{[{"i", word}], ?call({baseX_int, 10}, [?V(i)]), word}.
|
||||||
[{"x", word}, {"y", word}, {"fac", word}, {"n", word}],
|
|
||||||
{ifte, ?EQ(fac, 0),
|
|
||||||
{seq, [?V(y), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]}, ?V(n)]},
|
|
||||||
{ifte, ?EQ(?MOD(n, 32), 0),
|
|
||||||
%% We've filled a word, write it and start on new word
|
|
||||||
{seq, [?V(y), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
|
|
||||||
?call(int_to_str_,
|
|
||||||
[?MOD(x, fac), ?BSL(?ADD(48, ?DIV(x, fac)), 31),
|
|
||||||
?DIV(fac, 10), ?I(1)])]},
|
|
||||||
?call(int_to_str_,
|
|
||||||
[?MOD(x, fac), ?ADD(y, ?BSL(?ADD(48, ?DIV(x, fac)), ?SUB(31, n))),
|
|
||||||
?DIV(fac, 10), ?ADD(n, 1)])}
|
|
||||||
},
|
|
||||||
word};
|
|
||||||
|
|
||||||
builtin_function(int_digits) ->
|
builtin_baseX_tab(_X = 10) ->
|
||||||
{{builtin, int_digits}, [private],
|
{[{"ix", word}], ?ADD($0, ix), word};
|
||||||
[{"x", word}, {"n", word}],
|
builtin_baseX_tab(_X = 58) ->
|
||||||
{ifte, ?EQ(x, 0), ?V(n), ?call(int_digits, [?DIV(x, 10), ?ADD(n, 1)])},
|
<<Fst32:256>> = <<"123456789ABCDEFGHJKLMNPQRSTUVWXY">>,
|
||||||
word};
|
<<Lst26:256>> = <<"Zabcdefghijkmnopqrstuvwxyz", 0:48>>,
|
||||||
|
{[{"ix", word}],
|
||||||
|
{ifte, ?LT(ix, 32),
|
||||||
|
?BYTE(ix, Fst32),
|
||||||
|
?BYTE(?SUB(ix, 32), Lst26)
|
||||||
|
},
|
||||||
|
word}.
|
||||||
|
|
||||||
builtin_function(base58_tab) ->
|
builtin_baseX_int(X) ->
|
||||||
Fst32 = 22252025330403739761829862310514590177935513752035045390683118730099851483225,
|
{[{"w", word}],
|
||||||
Lst26 = 40880219588527126470443504235291962205031881694701834176631306799289575931904,
|
?LET(ret, {inline_asm, [?A(?MSIZE)]},
|
||||||
{{builtin, base58_tab}, [private],
|
{seq, [?call({baseX_int_pad, X}, [?V(w), ?I(0), ?I(0)]), {inline_asm, [?A(?POP)]}, ?V(ret)]}),
|
||||||
[{"ix", word}],
|
word}.
|
||||||
{ifte, ?LT(ix, 32),
|
|
||||||
?BYTE(ix, Fst32),
|
|
||||||
?BYTE(?SUB(ix, 32), Lst26)
|
|
||||||
}, word};
|
|
||||||
|
|
||||||
builtin_function(base58_int) ->
|
builtin_baseX_int_pad(X = 10) ->
|
||||||
{{builtin, base58_int}, [private],
|
{[{"src", word}, {"ix", word}, {"dst", word}],
|
||||||
[{"w", word}],
|
{ifte, ?LT(src, 0),
|
||||||
?LET(str0, ?call(base58_int_encode, [?V(w)]),
|
?call({baseX_int_encode, X}, [?NEG(src), ?I(1), ?BSL($-, 31)]),
|
||||||
?LET(str1, ?call(base58_int_pad, [?V(w), ?I(0), ?I(0)]),
|
?call({baseX_int_encode, X}, [?V(src), ?V(ix), ?V(dst)])},
|
||||||
?LET(str2, ?call(string_concat, [?V(str0), ?V(str1)]),
|
word};
|
||||||
?call(string_reverse, [?V(str2)])
|
builtin_baseX_int_pad(X = 58) ->
|
||||||
))),
|
{[{"src", word}, {"ix", word}, {"dst", word}],
|
||||||
word};
|
{ifte, ?GT(?ADD(?DIV(ix, 31), ?BYTE(ix, src)), 0),
|
||||||
|
?call({baseX_int_encode, X}, [?V(src), ?V(ix), ?V(dst)]),
|
||||||
|
?call({baseX_int_pad, X}, [?V(src), ?ADD(ix, 1), ?ADD(dst, ?BSL($1, ?SUB(31, ix)))])},
|
||||||
|
word}.
|
||||||
|
|
||||||
builtin_function(string_reverse) ->
|
builtin_baseX_int_encode(X) ->
|
||||||
{{builtin, string_reverse}, [private],
|
{[{"src", word}, {"ix", word}, {"dst", word}],
|
||||||
[{"s", string}],
|
?LET(n, ?call({baseX_digits, X}, [?V(src), ?I(0)]),
|
||||||
?DEREF(n, s,
|
{seq, [?ADD(n, ?ADD(ix, 1)), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
|
||||||
?LET(ret, {inline_asm, [?A(?MSIZE)]},
|
?call({baseX_int_encode_, X}, [?V(src), ?V(dst), ?EXP(X, n), ?V(ix)])]}),
|
||||||
{seq, [?V(n), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
|
word}.
|
||||||
?call(string_reverse_, [?NXT(s), ?I(0), ?I(31), ?SUB(?V(n), 1)]),
|
|
||||||
{inline_asm, [?A(?POP)]}, ?V(ret)]})),
|
|
||||||
word};
|
|
||||||
|
|
||||||
builtin_function(string_reverse_) ->
|
builtin_baseX_int_encode_(X) ->
|
||||||
{{builtin, string_reverse_}, [private],
|
{[{"src", word}, {"dst", word}, {"fac", word}, {"ix", word}],
|
||||||
[{"p", pointer}, {"x", word}, {"i1", word}, {"i2", word}],
|
{ifte, ?EQ(fac, 0),
|
||||||
{ifte, ?LT(i2, 0),
|
{seq, [?V(dst), {inline_asm, [?A(?MSIZE), ?A(?MSTORE), ?A(?MSIZE)]}]},
|
||||||
{seq, [?V(x), {inline_asm, [?A(?MSIZE), ?A(?MSTORE), ?A(?MSIZE)]}]},
|
{ifte, ?EQ(ix, 32),
|
||||||
?LET(p1, ?ADD(p, ?MUL(?DIV(i2, 32), 32)),
|
%% We've filled a word, write it and start on new word
|
||||||
?DEREF(w, p1,
|
{seq, [?V(dst), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
|
||||||
?LET(b, ?BYTE(?MOD(i2, 32), w),
|
?call({baseX_int_encode_, X}, [?V(src), ?I(0), ?V(fac), ?I(0)])]},
|
||||||
{ifte, ?LT(i1, 0),
|
?call({baseX_int_encode_, X},
|
||||||
{seq, [?V(x), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
|
[?MOD(src, fac), ?ADD(dst, ?BSL(?call({baseX_tab, X}, [?DIV(src, fac)]), ?SUB(31, ix))),
|
||||||
?call(string_reverse_,
|
?DIV(fac, X), ?ADD(ix, 1)])}
|
||||||
[?V(p), ?BSL(b, 31), ?I(30), ?SUB(i2, 1)])]},
|
},
|
||||||
?call(string_reverse_,
|
word}.
|
||||||
[?V(p), ?ADD(x, ?BSL(b, i1)), ?SUB(i1, 1), ?SUB(i2, 1)])})))},
|
|
||||||
word};
|
|
||||||
|
|
||||||
builtin_function(base58_int_pad) ->
|
builtin_baseX_digits(X) ->
|
||||||
{{builtin, base58_int_pad}, [private],
|
{[{"x0", word}, {"dgts", word}],
|
||||||
[{"w", word}, {"i", word}, {"x", word}],
|
?LET(x1, ?DIV(x0, X),
|
||||||
{ifte, ?GT(?BYTE(i, w), 0),
|
{ifte, ?EQ(x1, 0), ?V(dgts), ?call({baseX_digits, X}, [?V(x1), ?ADD(dgts, 1)])}),
|
||||||
{seq, [?V(i), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
|
word}.
|
||||||
?V(x), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
|
|
||||||
{inline_asm, [?A(?PUSH1), 64, ?A(?MSIZE), ?A(?SUB)]}]},
|
|
||||||
?call(base58_int_pad, [?V(w), ?ADD(i, 1),
|
|
||||||
?ADD(x, ?BSL(49, ?SUB(31, i)))])},
|
|
||||||
word};
|
|
||||||
|
|
||||||
builtin_function(base58_int_encode) ->
|
builtin_string_reverse() ->
|
||||||
{{builtin, base58_int_encode}, [private],
|
{[{"s", string}],
|
||||||
[{"w", word}],
|
?DEREF(n, s,
|
||||||
?LET(ret, {inline_asm, [?A(?MSIZE), ?A(?PUSH1), 0, ?A(?MSIZE), ?A(?MSTORE)]}, %% write placeholder
|
?LET(ret, {inline_asm, [?A(?MSIZE)]},
|
||||||
?LET(n, ?call(base58_int_encode_, [?V(w), ?I(0), ?I(0), ?I(31)]),
|
{seq, [?V(n), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
|
||||||
{seq, [?V(ret), {inline_asm, [?A(?DUP2), ?A(?SWAP1), ?A(?MSTORE)]},
|
?call(string_reverse_, [?NXT(s), ?I(0), ?I(31), ?SUB(?V(n), 1)]),
|
||||||
?V(ret)]})),
|
{inline_asm, [?A(?POP)]}, ?V(ret)]})),
|
||||||
word};
|
word}.
|
||||||
|
|
||||||
builtin_function(base58_int_encode_) ->
|
builtin_string_reverse_() ->
|
||||||
{{builtin, base58_int_encode_}, [private],
|
{[{"p", pointer}, {"x", word}, {"i1", word}, {"i2", word}],
|
||||||
[{"w", word}, {"x", word}, {"n", word}, {"i", word}],
|
{ifte, ?LT(i2, 0),
|
||||||
{ifte, ?EQ(w, 0),
|
{seq, [?V(x), {inline_asm, [?A(?MSIZE), ?A(?MSTORE), ?A(?MSIZE)]}]},
|
||||||
{seq, [?V(x), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]}, ?V(n)]},
|
?LET(p1, ?ADD(p, ?MUL(?DIV(i2, 32), 32)),
|
||||||
{ifte, ?LT(i, 0),
|
?DEREF(w, p1,
|
||||||
{seq, [?V(x), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
|
?LET(b, ?BYTE(?MOD(i2, 32), w),
|
||||||
?call(base58_int_encode_,
|
{ifte, ?LT(i1, 0),
|
||||||
[?DIV(w, 58), ?BSL(?call(base58_tab, [?MOD(w, 58)]), 31),
|
{seq, [?V(x), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
|
||||||
?ADD(n, 1), ?I(30)])]},
|
?call(string_reverse_,
|
||||||
?call(base58_int_encode_,
|
[?V(p), ?BSL(b, 31), ?I(30), ?SUB(i2, 1)])]},
|
||||||
[?DIV(w, 58), ?ADD(x, ?BSL(?call(base58_tab, [?MOD(w, 58)]), i)),
|
?call(string_reverse_,
|
||||||
?ADD(n, 1), ?SUB(i, 1)])}},
|
[?V(p), ?ADD(x, ?BSL(b, i1)), ?SUB(i1, 1), ?SUB(i2, 1)])})))},
|
||||||
word};
|
word}.
|
||||||
|
|
||||||
|
builtin_addr_to_str() ->
|
||||||
builtin_function(addr_to_str) ->
|
{[{"a", word}], ?call({baseX_int, 58}, [?V(a)]), word}.
|
||||||
{{builtin, addr_to_str}, [private],
|
|
||||||
[{"a", word}],
|
|
||||||
?call(base58_int, [?V(a)]),
|
|
||||||
word}.
|
|
||||||
|
|
||||||
|
|||||||
+85
-55
@@ -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,34 +38,58 @@
|
|||||||
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) ->
|
||||||
Ast = parse(ContractString, Options),
|
try
|
||||||
ok = pp_sophia_code(Ast, Options),
|
Ast = parse(ContractString, Options),
|
||||||
ok = pp_ast(Ast, Options),
|
ok = pp_sophia_code(Ast, Options),
|
||||||
TypedAst = aeso_ast_infer_types:infer(Ast, Options),
|
ok = pp_ast(Ast, Options),
|
||||||
%% pp_types is handled inside aeso_ast_infer_types.
|
TypedAst = aeso_ast_infer_types:infer(Ast, Options),
|
||||||
ok = pp_typed_ast(TypedAst, Options),
|
%% pp_types is handled inside aeso_ast_infer_types.
|
||||||
ICode = to_icode(TypedAst, Options),
|
ok = pp_typed_ast(TypedAst, Options),
|
||||||
TypeInfo = extract_type_info(ICode),
|
ICode = to_icode(TypedAst, Options),
|
||||||
ok = pp_icode(ICode, Options),
|
TypeInfo = extract_type_info(ICode),
|
||||||
Assembler = assemble(ICode, Options),
|
ok = pp_icode(ICode, Options),
|
||||||
ok = pp_assembler(Assembler, Options),
|
Assembler = assemble(ICode, Options),
|
||||||
ByteCodeList = to_bytecode(Assembler, Options),
|
ok = pp_assembler(Assembler, Options),
|
||||||
ByteCode = << << B:8 >> || B <- ByteCodeList >>,
|
ByteCodeList = to_bytecode(Assembler, Options),
|
||||||
ok = pp_bytecode(ByteCode, Options),
|
ByteCode = << << B:8 >> || B <- ByteCodeList >>,
|
||||||
#{byte_code => ByteCode, type_info => TypeInfo,
|
ok = pp_bytecode(ByteCode, Options),
|
||||||
contract_source => ContractString,
|
{ok, #{byte_code => ByteCode,
|
||||||
compiler_version => version()}.
|
compiler_version => version(),
|
||||||
|
contract_source => ContractString,
|
||||||
|
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,30 +100,40 @@ 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) ->
|
||||||
Ast = parse(ContractString, Options),
|
try
|
||||||
ok = pp_sophia_code(Ast, Options),
|
Ast = parse(ContractString, Options),
|
||||||
ok = pp_ast(Ast, Options),
|
ok = pp_sophia_code(Ast, Options),
|
||||||
TypedAst = aeso_ast_infer_types:infer(Ast, [permissive_address_literals]),
|
ok = pp_ast(Ast, Options),
|
||||||
{ok, {FunName, {fun_t, _, _, ArgTypes, RetType}}} = get_call_type(TypedAst),
|
TypedAst = aeso_ast_infer_types:infer(Ast, [permissive_address_literals]),
|
||||||
ok = pp_typed_ast(TypedAst, Options),
|
{ok, {FunName, {fun_t, _, _, ArgTypes, RetType}}} = get_call_type(TypedAst),
|
||||||
Icode = to_icode(TypedAst, Options),
|
ok = pp_typed_ast(TypedAst, Options),
|
||||||
ArgVMTypes = [ aeso_ast_to_icode:ast_typerep(T, Icode) || T <- ArgTypes ],
|
Icode = to_icode(TypedAst, Options),
|
||||||
RetVMType = case RetType of
|
ArgVMTypes = [ aeso_ast_to_icode:ast_typerep(T, Icode) || T <- ArgTypes ],
|
||||||
{id, _, "_"} -> any;
|
RetVMType = case RetType of
|
||||||
_ -> aeso_ast_to_icode:ast_typerep(RetType, Icode)
|
{id, _, "_"} -> any;
|
||||||
end,
|
_ -> aeso_ast_to_icode:ast_typerep(RetType, Icode)
|
||||||
ok = pp_icode(Icode, Options),
|
end,
|
||||||
#{ functions := Funs } = Icode,
|
ok = pp_icode(Icode, Options),
|
||||||
ArgIcode = get_arg_icode(Funs),
|
#{ functions := Funs } = Icode,
|
||||||
try [ icode_to_term(T, Arg) || {T, Arg} <- lists:zip(ArgVMTypes, ArgIcode) ] of
|
ArgIcode = get_arg_icode(Funs),
|
||||||
ArgTerms ->
|
ArgTerms = [ icode_to_term(T, Arg) ||
|
||||||
{ok, FunName, {ArgVMTypes, RetVMType}, ArgTerms}
|
{T, Arg} <- lists:zip(ArgVMTypes, ArgIcode) ],
|
||||||
catch throw:Err ->
|
{ok, FunName, {ArgVMTypes, RetVMType}, ArgTerms}
|
||||||
{error, Err}
|
catch
|
||||||
|
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()) ->
|
||||||
{ok, aeso_sophia:heap(), aeso_sophia:type(), aeso_sophia:type()}
|
{ok, binary(), aeso_sophia:type(), aeso_sophia:type()}
|
||||||
| {error, argument_syntax_error}.
|
| {error, argument_syntax_error}.
|
||||||
create_calldata(Contract, "", CallCode) when is_map(Contract) ->
|
create_calldata(Contract, "", CallCode) when is_map(Contract) ->
|
||||||
case check_call(CallCode, []) of
|
case check_call(CallCode, []) of
|
||||||
@@ -113,7 +147,7 @@ create_calldata(Contract, Function, Argument) when is_map(Contract) ->
|
|||||||
%% Function should be "foo : type", and
|
%% Function should be "foo : type", and
|
||||||
%% Argument should be "Arg1, Arg2, .., ArgN" (no parens)
|
%% Argument should be "Arg1, Arg2, .., ArgN" (no parens)
|
||||||
case string:lexemes(Function, ": ") of
|
case string:lexemes(Function, ": ") of
|
||||||
%% If function is a single word fallback to old calldata generation
|
%% If function is a single word fallback to old calldata generation
|
||||||
[FunName] -> aeso_abi:old_create_calldata(Contract, FunName, Argument);
|
[FunName] -> aeso_abi:old_create_calldata(Contract, FunName, Argument);
|
||||||
[FunName | _] ->
|
[FunName | _] ->
|
||||||
Args = lists:map(fun($\n) -> 32; (X) -> X end, Argument), %% newline to space
|
Args = lists:map(fun($\n) -> 32; (X) -> X end, Argument), %% newline to space
|
||||||
@@ -247,13 +281,9 @@ parse_string(Text) ->
|
|||||||
parse_error(Pos, ErrorString)
|
parse_error(Pos, ErrorString)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
parse_error({Line,Pos}, ErrorString) ->
|
parse_error({Line, Pos}, ErrorString) ->
|
||||||
Error = io_lib:format("line ~p, column ~p: ~s", [Line,Pos,ErrorString]),
|
Error = io_lib:format("line ~p, column ~p: ~s", [Line, Pos, ErrorString]),
|
||||||
error({parse_errors,[Error]}).
|
error({parse_errors, [Error]}).
|
||||||
|
|
||||||
read_contract(Name) ->
|
read_contract(Name) ->
|
||||||
{ok, Bin} = file:read_file(filename:join(contract_path(), lists:concat([Name, ".aes"]))),
|
file:read_file(Name).
|
||||||
binary_to_list(Bin).
|
|
||||||
|
|
||||||
contract_path() ->
|
|
||||||
"apps/aesophia/test/contracts".
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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() ->
|
||||||
|
|||||||
+2
-8
@@ -170,7 +170,7 @@ expr(E) -> expr_p(0, E).
|
|||||||
|
|
||||||
%% -- Not exported -----------------------------------------------------------
|
%% -- Not exported -----------------------------------------------------------
|
||||||
|
|
||||||
-spec name(aeso_syntax:id() | aeso_syntax:con() | aeso_syntax:tvar()) -> doc().
|
-spec name(aeso_syntax:id() | aeso_syntax:qid() | aeso_syntax:con() | aeso_syntax:qcon() | aeso_syntax:tvar()) -> doc().
|
||||||
name({id, _, Name}) -> text(Name);
|
name({id, _, Name}) -> text(Name);
|
||||||
name({con, _, Name}) -> text(Name);
|
name({con, _, Name}) -> text(Name);
|
||||||
name({qid, _, Names}) -> text(string:join(Names, "."));
|
name({qid, _, Names}) -> text(string:join(Names, "."));
|
||||||
@@ -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
@@ -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
@@ -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()}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
{applications,
|
{applications,
|
||||||
[kernel,
|
[kernel,
|
||||||
stdlib,
|
stdlib,
|
||||||
enacl,
|
|
||||||
syntax_tools,
|
syntax_tools,
|
||||||
|
getopt,
|
||||||
aebytecode
|
aebytecode
|
||||||
]},
|
]},
|
||||||
{env,[]},
|
{env,[]},
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
-module(aesophia).
|
||||||
|
|
||||||
|
-export([main/1]).
|
||||||
|
|
||||||
|
-define(OPT_SPEC,
|
||||||
|
[ {src_file, undefined, undefined, string, "Sophia source code file"}
|
||||||
|
, {verbose, $v, "verbose", undefined, "Verbose output"}
|
||||||
|
, {help, $h, "help", undefined, "Show this message"}
|
||||||
|
, {outfile, $o, "out", string, "Output file (experimental)"} ]).
|
||||||
|
|
||||||
|
usage() ->
|
||||||
|
getopt:usage(?OPT_SPEC, "aesophia").
|
||||||
|
|
||||||
|
main(Args) ->
|
||||||
|
case getopt:parse(?OPT_SPEC, Args) of
|
||||||
|
{ok, {Opts, []}} ->
|
||||||
|
case proplists:get_value(help, Opts, false) of
|
||||||
|
false ->
|
||||||
|
compile(Opts);
|
||||||
|
true ->
|
||||||
|
usage()
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ok, {_, NonOpts}} ->
|
||||||
|
io:format("Can't understand ~p\n\n", [NonOpts]),
|
||||||
|
usage();
|
||||||
|
|
||||||
|
{error, {Reason, Data}} ->
|
||||||
|
io:format("Error: ~s ~p\n\n", [Reason, Data]),
|
||||||
|
usage()
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
compile(Opts) ->
|
||||||
|
case proplists:get_value(src_file, Opts, undefined) of
|
||||||
|
undefined ->
|
||||||
|
io:format("Error: no input source file\n\n"),
|
||||||
|
usage();
|
||||||
|
File ->
|
||||||
|
compile(File, Opts)
|
||||||
|
end.
|
||||||
|
|
||||||
|
compile(File, Opts) ->
|
||||||
|
Verbose = proplists:get_value(verbose, Opts, false),
|
||||||
|
OutFile = proplists:get_value(outfile, Opts, undefined),
|
||||||
|
|
||||||
|
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("\n~s\n", [ErrorString])
|
||||||
|
end.
|
||||||
|
|
||||||
|
write_outfile(undefined, _) -> ok;
|
||||||
|
write_outfile(Out, ResMap) ->
|
||||||
|
%% Lazy approach
|
||||||
|
file:write_file(Out, term_to_binary(ResMap)),
|
||||||
|
io:format("Output written to: ~s\n", [Out]).
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
%%%=============================================================================
|
||||||
|
%%% @copyright (C) 2019, Aeternity Anstalt
|
||||||
|
%%% @doc
|
||||||
|
%%% Unit tests for the aeso_blake2 module
|
||||||
|
%%%
|
||||||
|
%%% In addition the aeso_blake2 module was compared to the C reference
|
||||||
|
%%% implementation by writing a QuickCheck property.
|
||||||
|
%%% @end
|
||||||
|
%%%=============================================================================
|
||||||
|
-module(aeso_blake2_tests).
|
||||||
|
|
||||||
|
-ifdef(TEST).
|
||||||
|
|
||||||
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
|
blake2b_test_() ->
|
||||||
|
{"Tests for BLAKE2b hash implementation",
|
||||||
|
[ fun() -> blake2b(Data) end || Data <- test_data_blake2b() ]}.
|
||||||
|
|
||||||
|
blake2b({Msg0, Key0, ExpectedOut0}) ->
|
||||||
|
Msg = mk_binary(Msg0),
|
||||||
|
Key = mk_binary(Key0),
|
||||||
|
ExpectedOut = mk_binary(ExpectedOut0),
|
||||||
|
Result = aeso_blake2:blake2b(byte_size(ExpectedOut), Msg, Key),
|
||||||
|
?assertEqual(Result, {ok, ExpectedOut}).
|
||||||
|
|
||||||
|
mk_binary(Bin) when is_binary(Bin) -> Bin;
|
||||||
|
mk_binary(HexStr) when is_list(HexStr) ->
|
||||||
|
<< << (erlang:list_to_integer([H], 16)):4 >> || H <- HexStr >>.
|
||||||
|
|
||||||
|
test_data_blake2b() ->
|
||||||
|
[ %% {Message, Key, ExpectedHash}
|
||||||
|
%% From Wikipedia
|
||||||
|
%% https://en.wikipedia.org/wiki/BLAKE_(hash_function)#BLAKE2
|
||||||
|
{<<>>,
|
||||||
|
<<>>,
|
||||||
|
"786A02F742015903C6C6FD852552D272912F4740E15847618A86E217F71F5419D25E1031AFEE585313896444934EB04B903A685B1448B755D56F701AFE9BE2CE"}
|
||||||
|
, {<<"The quick brown fox jumps over the lazy dog">>,
|
||||||
|
<<>>,
|
||||||
|
"A8ADD4BDDDFD93E4877D2746E62817B116364A1FA7BC148D95090BC7333B3673F82401CF7AA2E4CB1ECD90296E3F14CB5413F8ED77BE73045B13914CDCD6A918"}
|
||||||
|
|
||||||
|
%% From reference implementation testvectors
|
||||||
|
%% https://github.com/BLAKE2/BLAKE2/tree/master/testvectors
|
||||||
|
%%
|
||||||
|
%% Non-keyed
|
||||||
|
, {"00",
|
||||||
|
"",
|
||||||
|
"2FA3F686DF876995167E7C2E5D74C4C7B6E48F8068FE0E44208344D480F7904C36963E44115FE3EB2A3AC8694C28BCB4F5A0F3276F2E79487D8219057A506E4B"}
|
||||||
|
, {"0001",
|
||||||
|
"",
|
||||||
|
"1C08798DC641ABA9DEE435E22519A4729A09B2BFE0FF00EF2DCD8ED6F8A07D15EAF4AEE52BBF18AB5608A6190F70B90486C8A7D4873710B1115D3DEBBB4327B5"}
|
||||||
|
, {"00010203040506070809",
|
||||||
|
"",
|
||||||
|
"29102511D749DB3CC9B4E335FA1F5E8FACA8421D558F6A3F3321D50D044A248BA595CFC3EFD3D2ADC97334DA732413F5CBF4751C362BA1D53862AC1E8DABEEE8"}
|
||||||
|
|
||||||
|
%% Keyed
|
||||||
|
, {"",
|
||||||
|
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
|
||||||
|
"10ebb67700b1868efb4417987acf4690ae9d972fb7a590c2f02871799aaa4786b5e996e8f0f4eb981fc214b005f42d2ff4233499391653df7aefcbc13fc51568"}
|
||||||
|
, {"00",
|
||||||
|
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
|
||||||
|
"961f6dd1e4dd30f63901690c512e78e4b45e4742ed197c3c5e45c549fd25f2e4187b0bc9fe30492b16b0d0bc4ef9b0f34c7003fac09a5ef1532e69430234cebd"}
|
||||||
|
, {"0001",
|
||||||
|
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
|
||||||
|
"da2cfbe2d8409a0f38026113884f84b50156371ae304c4430173d08a99d9fb1b983164a3770706d537f49e0c916d9f32b95cc37a95b99d857436f0232c88a965"}
|
||||||
|
, {"00010203040506070809",
|
||||||
|
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
|
||||||
|
"4fe181f54ad63a2983feaaf77d1e7235c2beb17fa328b6d9505bda327df19fc37f02c4b6f0368ce23147313a8e5738b5fa2a95b29de1c7f8264eb77b69f585cd"}
|
||||||
|
].
|
||||||
|
|
||||||
|
|
||||||
|
-endif.
|
||||||
|
|
||||||
+102
-76
@@ -28,17 +28,26 @@ 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),
|
||||||
?assertEqual(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, 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
|
||||||
|
{[], Extra} -> ?assertMatch({unexpected, []}, {unexpected, Extra});
|
||||||
|
{Missing, []} -> ?assertMatch({missing, []}, {missing, Missing});
|
||||||
|
{Missing, Extra} -> ?assertEqual(Missing, Extra)
|
||||||
|
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].
|
||||||
@@ -68,77 +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)">>,
|
||||||
"Repeated name x in pattern\n"
|
<<"The field z is missing when constructing an element of type r2 (at line 15, column 24)">>,
|
||||||
" x :: x (at line 26, column 7)\n",
|
<<"Repeated name x in pattern\n"
|
||||||
"No record type with fields y, z (at line 14, column 22)\n"]}
|
" x :: x (at line 26, column 7)">>,
|
||||||
|
<<"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",
|
||||||
|
[<<"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)">>,
|
||||||
|
<<"The fields y, z are missing when constructing an element of type r('1) (at line 6, column 40)">>]}
|
||||||
].
|
].
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
contract MissingFieldsInRecordExpr =
|
||||||
|
|
||||||
|
record r('a) = {x : int, y : string, z : 'a}
|
||||||
|
type alias('a) = r('a)
|
||||||
|
|
||||||
|
function fail1() = { x = 0 }
|
||||||
|
function fail2(z : 'a) : r('a) = { y = "string", z = z }
|
||||||
|
function fail3() : alias(int) = { x = 0, z = 1 }
|
||||||
@@ -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... */
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user