Compare commits

..

61 Commits

Author SHA1 Message Date
Hans Svensson d4c6187739 Merge pull request #130 from aeternity/PT-168026424-prepare_sophia_4_0_RC1
Preparing 4.0.0-rc1
2019-08-22 16:02:06 +02:00
Hans Svensson 2620aa64b4 Add some no_implicit_stdlib for now 2019-08-22 15:21:41 +02:00
Ulf Norell a9617a025f Merge pull request #129 from aeternity/deadcode-elim
Deadcode elimination for FATE
2019-08-22 14:25:31 +02:00
Hans Svensson bde76c8580 Preparing 4.0.0-rc1 2019-08-22 13:30:00 +02:00
Ulf Norell e94b5379ed Deadcode elimination pass 2019-08-21 12:19:01 +02:00
Ulf Norell cbc8909954 Add default init function in fcode pass instead of in assembler 2019-08-21 11:51:36 +02:00
Ulf Norell cfd036b199 Test deadcode elimination for FATE backend 2019-08-21 11:51:36 +02:00
Ulf Norell bbf043f4ee Merge pull request #126 from radrow/listcompfixes
Fixed comprehension dependencies
2019-08-21 11:51:16 +02:00
Ulf Norell ba41ab457d Merge pull request #127 from aeternity/radrow-patch-1
Fixed intersperse in stdlib
2019-08-21 11:51:04 +02:00
Hans Svensson 49634a6024 Merge pull request #128 from aeternity/PT-167996886-a_proper_ecverify
PT-167996886 A proper ecverify
2019-08-21 11:19:48 +02:00
Hans Svensson 2dbef80249 aebytecode commit 2019-08-21 11:09:44 +02:00
Hans Svensson ebdd38c505 Change ecverify into verify_sig and then add an actual ecverify 2019-08-21 09:29:40 +02:00
Radosław Rowicki 5dbca47d34 Fixed intersperse in stdlib 2019-08-20 20:16:52 +02:00
radrow 79d491e4a8 Fixed comprehension dependencies 2019-08-20 18:44:47 +02:00
Hans Svensson 73b9a54172 Merge pull request #125 from aeternity/PT-162578406-payable_modifier
PT-162578406 Add payable modifier
2019-08-19 16:27:18 +02:00
Hans Svensson bb0c3b54df ACI should also track payable 2019-08-19 08:57:31 +02:00
Hans Svensson d0485304b6 Please dialyzer 2019-08-19 08:57:31 +02:00
Hans Svensson 86aeaa40ef Set aebytecode commit 2019-08-19 08:57:31 +02:00
Hans Svensson e9505e240f Add Address.is_payable(address) 2019-08-19 08:57:31 +02:00
Hans Svensson f27d37d624 Add payable modifier for contracts and entrypoints 2019-08-19 08:57:31 +02:00
Ulf Norell e566186800 Merge pull request #123 from aeternity/PT-167221635-remote-type-check
PT-167221635 remote type check
2019-08-16 09:22:30 +02:00
Ulf Norell 956b78fb01 aebytecode commit 2019-08-16 09:14:51 +02:00
Ulf Norell 522d977be9 Remote calls now take typerep arguments 2019-08-16 09:12:04 +02:00
Tino Breddin dd26649f7d [PT-167805291] Add opcode for ecrecover (#122)
* Add opcode for ecrecover

* Update aebytecode

* Extend signature bytes type used for ecrecover

* Add ecrecover to changelog

* Add some type specs

* Please dialyzer
2019-08-14 21:02:46 +02:00
Radosław Rowicki b669d2df1e Added list comprehensions and standard List, Option, Func, Pair, and Triple library (#105)
* Added standard List library and list comprehensions

Added List library.

Flatmaps WIP

Fixed dependency in flat_map

fcode generation

Updated tests to use custom list lib

Added comprehension test

Added stdlib

sanity

Test

* Extended stdlib for lists. Added error message for redefinition of stdlibx

* Fixed type template

* Improved stdlib

* More functions

* Fixed cyclic includes

* Refixed imports and added few tests

* Added fail test

* Undelete removed type spec

* Remove typo

* Fix iter function

* Fixed typo

* Added if guards and let statements in list comp

* Added more fail tests

* Option stliv

* 2 and 3 tuple stdlib

* Updated stdlib to new syntax. Added recursor and changed all/any functions

* Fixed performance issues. Changed include management

* Fixed hash type
2019-08-14 13:53:58 +02:00
Ulf Norell 69ad8ce9bc Merge pull request #121 from aeternity/PT-166788647-fate-efficient-maps
Don't generate remote tail calls
2019-08-14 08:58:54 +02:00
Ulf Norell 3877174acb aebytecode commit 2019-08-13 15:53:49 +02:00
Ulf Norell 448adb8890 Don't generate remote tail calls 2019-08-13 15:52:47 +02:00
Hans Svensson 864a94c59e Merge pull request #119 from radrow/patch-2
Mention tuple syntax change in changelog
2019-08-12 16:59:58 +02:00
Radosław Rowicki 518ae8e659 Mention tuple syntax change in changelog 2019-08-12 16:42:34 +02:00
Thomas Arts df12f6af91 Merge pull request #118 from aeternity/PT-167126818
Pt 167126818
2019-08-06 15:30:47 +02:00
Thomas Arts 6aed5dfacb Upgrade to newer aebytecode version 2019-08-06 13:20:03 +02:00
Thomas Arts cecc977898 Compiler returns abi_version 2019-08-06 13:11:31 +02:00
Ulf Norell 74933b0616 Merge pull request #117 from aeternity/PT-167701356-split-store
PT-167701356 split store
2019-08-06 12:27:18 +02:00
Hans Svensson 6a27c4a68b Merge pull request #116 from aeternity/another_no_code
to_sophia_value should also use no_code option
2019-08-05 15:21:27 +02:00
Hans Svensson d526e55c26 to_sophia_value should also use no_code option 2019-08-05 15:19:22 +02:00
Hans Svensson 6f7f5fa13c Merge pull request #115 from aeternity/PT-166731397-add_compiler_backend
PT-166731397 Add compiler backend
2019-08-05 15:00:32 +02:00
Ulf Norell 2d6381dc6f Generate INIT function which writes the state instead of returning it 2019-08-05 14:36:59 +02:00
Hans Svensson 3663b4e5d0 Add no_code option to aeso_compile (for encode/decode calldata) 2019-08-05 14:33:53 +02:00
Hans Svensson 4478fee6e6 Add ACI calldata test 2019-08-05 14:33:53 +02:00
Ulf Norell 79ae92a068 Add missing error message 2019-08-05 14:01:35 +02:00
Ulf Norell eb968d3cb9 Merge pull request #114 from aeternity/latest-aebytecode
Update to latest aebytecode
2019-08-05 11:54:01 +02:00
Ulf Norell 054a5a4867 Update to latest aebytecode 2019-08-05 11:51:10 +02:00
Ulf Norell e198dd8311 Merge pull request #113 from aeternity/test-fixes
Update test case
2019-08-05 09:31:31 +02:00
Ulf Norell 46a996ead8 Update test case 2019-08-05 09:20:09 +02:00
Hans Svensson 2bf6ab7655 Merge pull request #110 from radrow/patch-1
Covered qid case in ast_typerep
2019-08-05 09:15:12 +02:00
Ulf Norell 5ff7aa5821 Merge pull request #109 from radrow/tuple-type
Change tuple typing syntax
2019-08-05 09:13:30 +02:00
Radosław Rowicki 045df292be Fixed pretty printing and pattern split (#111) 2019-07-29 14:27:01 +03:00
Radosław Rowicki c97eb99921 Fixed double parens in ACI pp
Co-Authored-By: Ulf Norell <ulf.norell@gmail.com>
2019-07-29 11:01:38 +02:00
Radosław Rowicki 4c78ab3aee Covered qid case in ast_typerep
Because `qid` was not accepted the following code 

```
contract Test = 
   datatype myOption('a) = MyNone | MySome('a)
   entrypoint optionFn(v: myOption(string)): myOption(string) = v
```
Used to lead to `function_clause` error.

Bug copyright (c) @nduchak
2019-07-26 15:31:06 +02:00
radrow 5ff983b0b3 Updated tests 2019-07-22 13:56:45 +02:00
Hans Svensson 4bf382a997 Merge pull request #107 from radrow/master
Changed 'require' function return type to Unit
2019-07-20 21:05:40 +02:00
radrow 4c72045a86 Changed tuple type parsing rule 2019-07-20 19:00:53 +02:00
radrow 7daf218b2a Changed 'require' function return type to Unit 2019-07-19 18:10:54 +02:00
radrow 973850e6a6 Fix pat split 2019-07-11 18:28:50 +02:00
Tobias Lindahl 19948c6aad Merge pull request #104 from aeternity/PT-166868668-decode-fate-revert
Properly decode revert strings for fate as well
2019-07-09 16:52:35 +02:00
Tobias Lindahl c4660fe0cf Properly decode revert strings for fate as well 2019-07-09 16:24:56 +02:00
Tobias Lindahl e326908623 Merge pull request #103 from aeternity/PT-166868668-fate-abort
Use EXIT for internal errors to burn all gas
2019-07-09 15:47:46 +02:00
Tobias Lindahl 9be528a579 Use EXIT for internal errors to burn all gas 2019-07-09 14:36:09 +02:00
Tobias Lindahl f67d7354a2 Merge pull request #102 from aeternity/PT-166927306-names-as-strings
Use a name string rather than a name hash in transfer and revoke
2019-07-01 10:55:38 +02:00
Tobias Lindahl 6f873e45b8 Use a name string rather than a name hash in transfer and revoke 2019-07-01 07:42:08 +02:00
61 changed files with 1329 additions and 318 deletions
+2
View File
@@ -19,3 +19,5 @@ rebar3.crashdump
*.erl~ *.erl~
*.aes~ *.aes~
aesophia aesophia
.qcci
current_counterexample.eqc
+33 -1
View File
@@ -9,6 +9,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
### Removed ### Removed
## [4.0.0-rc1] - 2019-08-22
### Added
- FATE backend - the compiler is able to produce VM code for both `AEVM` and `FATE`. Many
of the APIs now take `{backend, aevm | fate}` to decide wich backend to produce artifacts
for.
- New builtin functions `Crypto.ecrecover_secp256k1: (hash, bytes(65)) => option(bytes(20))`
and `Crypto.ecverify_secp256k1 : (hash, bytes(20), bytes(65)) => bool` for recovering
and verifying an Ethereum address for a message hash and a signature.
- Sophia supports list comprehensions known from languages like Python, Haskell or Erlang.
Example syntax:
```
[x + y | x <- [1,2,3,4,5], let k = x*x, if (k > 5), y <- [k, k+1, k+2]]
// yields [12,13,14,20,21,22,30,31,32]
```
- A new contract, and endpoint, modifier `payable` is introduced. Contracts, and enpoints,
that shall be able to receive funds should be marked as payable. `Address.is_payable(a)`
can be used to check if an (contract) address is payable or not.
### Changed
- New syntax for tuple types. Now 0-tuple type is encoded as `unit` instead of `()` and
regular tuples are encoded by interspersing inner types with `*`, for instance `int * string`.
Parens are not necessary. Note it only affects the types, values remain as their were before,
so `(1, "a") : int * string`
- The `AENS.transfer` and `AENS.revoke` functions have been updated to take a name `string`
instead of a name `hash`.
- Fixed a bug where the `AEVM` backend complained about a missing `init` function when
trying to generate calldata from an ACI-generated interface.
- Compiler now returns the ABI-version in the compiler result map.
- Renamed `Crypto.ecverify` and `Crypto.ecverify_secp256k1` into `Crypto.verify_sig` and
`Crypto.verify_sig_secp256k1` respectively.
### Removed
## [3.2.0] - 2019-06-28 ## [3.2.0] - 2019-06-28
### Added ### Added
- New builtin function `require : (bool, string) => ()`. Defined as - New builtin function `require : (bool, string) => ()`. Defined as
@@ -103,7 +134,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Simplify calldata creation - instead of passing a compiled contract, simply - Simplify calldata creation - instead of passing a compiled contract, simply
pass a (stubbed) contract string. pass a (stubbed) contract string.
[Unreleased]: https://github.com/aeternity/aesophia/compare/v3.2.0...HEAD [Unreleased]: https://github.com/aeternity/aesophia/compare/v4.0.0-rc1...HEAD
[4.0.0-rc1]: https://github.com/aeternity/aesophia/compare/v3.2.0...v4.0.0-rc1
[3.2.0]: https://github.com/aeternity/aesophia/compare/v3.1.0...v3.2.0 [3.2.0]: https://github.com/aeternity/aesophia/compare/v3.1.0...v3.2.0
[3.1.0]: https://github.com/aeternity/aesophia/compare/v3.0.0...v3.1.0 [3.1.0]: https://github.com/aeternity/aesophia/compare/v3.0.0...v3.1.0
[3.0.0]: https://github.com/aeternity/aesophia/compare/v2.1.0...v3.0.0 [3.0.0]: https://github.com/aeternity/aesophia/compare/v2.1.0...v3.0.0
+2 -2
View File
@@ -2,7 +2,7 @@
{erl_opts, [debug_info]}. {erl_opts, [debug_info]}.
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"c63ac88"}}} {deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"10cc127"}}}
, {getopt, "1.0.1"} , {getopt, "1.0.1"}
, {eblake2, "1.0.0"} , {eblake2, "1.0.0"}
, {jsx, {git, "https://github.com/talentdeficit/jsx.git", , {jsx, {git, "https://github.com/talentdeficit/jsx.git",
@@ -15,7 +15,7 @@
{base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]} {base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]}
]}. ]}.
{relx, [{release, {aesophia, "3.2.0"}, {relx, [{release, {aesophia, "4.0.0-rc1"},
[aesophia, aebytecode, getopt]}, [aesophia, aebytecode, getopt]},
{dev_mode, true}, {dev_mode, true},
+1 -1
View File
@@ -1,7 +1,7 @@
{"1.1.0", {"1.1.0",
[{<<"aebytecode">>, [{<<"aebytecode">>,
{git,"https://github.com/aeternity/aebytecode.git", {git,"https://github.com/aeternity/aebytecode.git",
{ref,"c63ac888dd71c305cbc6d4f70953a176bf1f78f7"}}, {ref,"10cc1278831ad7e90138533466ceef4bcafd74a9"}},
0}, 0},
{<<"aeserialization">>, {<<"aeserialization">>,
{git,"https://github.com/aeternity/aeserialization.git", {git,"https://github.com/aeternity/aeserialization.git",
+18 -8
View File
@@ -113,7 +113,7 @@ encode_contract(Contract = {contract, _, {con, _, Name}, _}) ->
|| F <- sort_decls(contract_funcs(Contract)), || F <- sort_decls(contract_funcs(Contract)),
is_entrypoint(F) ], is_entrypoint(F) ],
#{contract => C3#{functions => Fdefs}}; #{contract => C3#{functions => Fdefs, payable => is_payable(Contract)}};
encode_contract(Namespace = {namespace, _, {con, _, Name}, _}) -> encode_contract(Namespace = {namespace, _, {con, _, Name}, _}) ->
Tdefs = [ encode_typedef(T) || T <- sort_decls(contract_types(Namespace)) ], Tdefs = [ encode_typedef(T) || T <- sort_decls(contract_types(Namespace)) ],
#{namespace => #{name => encode_name(Name), #{namespace => #{name => encode_name(Name),
@@ -125,12 +125,14 @@ encode_function(FDef = {letfun, _, {id, _, Name}, Args, Type, _}) ->
#{name => encode_name(Name), #{name => encode_name(Name),
arguments => encode_args(Args), arguments => encode_args(Args),
returns => encode_type(Type), returns => encode_type(Type),
stateful => is_stateful(FDef)}; stateful => is_stateful(FDef),
payable => is_payable(FDef)};
encode_function(FDecl = {fun_decl, _, {id, _, Name}, {fun_t, _, _, Args, Type}}) -> encode_function(FDecl = {fun_decl, _, {id, _, Name}, {fun_t, _, _, Args, Type}}) ->
#{name => encode_name(Name), #{name => encode_name(Name),
arguments => encode_anon_args(Args), arguments => encode_anon_args(Args),
returns => encode_type(Type), returns => encode_type(Type),
stateful => is_stateful(FDecl)}. stateful => is_stateful(FDecl),
payable => is_payable(FDecl)}.
encode_anon_args(Types) -> encode_anon_args(Types) ->
Anons = [ list_to_binary("_" ++ integer_to_list(X)) || X <- lists:seq(1, length(Types))], Anons = [ list_to_binary("_" ++ integer_to_list(X)) || X <- lists:seq(1, length(Types))],
@@ -234,12 +236,13 @@ do_render_aci_json(Json) ->
{ok, list_to_binary(string:join(DecodedContracts, "\n"))}. {ok, list_to_binary(string:join(DecodedContracts, "\n"))}.
decode_contract(#{contract := #{name := Name, decode_contract(#{contract := #{name := Name,
payable := Payable,
type_defs := Ts0, type_defs := Ts0,
functions := Fs} = C}) -> functions := Fs} = C}) ->
MkTDef = fun(N, T) -> #{name => N, vars => [], typedef => T} end, MkTDef = fun(N, T) -> #{name => N, vars => [], typedef => T} end,
Ts = [ MkTDef(<<"state">>, maps:get(state, C)) || maps:is_key(state, C) ] ++ Ts = [ MkTDef(<<"state">>, maps:get(state, C)) || maps:is_key(state, C) ] ++
[ MkTDef(<<"event">>, maps:get(event, C)) || maps:is_key(event, C) ] ++ Ts0, [ MkTDef(<<"event">>, maps:get(event, C)) || maps:is_key(event, C) ] ++ Ts0,
["contract ", io_lib:format("~s", [Name])," =\n", [payable(Payable), "contract ", io_lib:format("~s", [Name])," =\n",
decode_tdefs(Ts), decode_funcs(Fs)]; decode_tdefs(Ts), decode_funcs(Fs)];
decode_contract(#{namespace := #{name := Name, type_defs := Ts}}) when Ts /= [] -> decode_contract(#{namespace := #{name := Name, type_defs := Ts}}) when Ts /= [] ->
["namespace ", io_lib:format("~s", [Name])," =\n", ["namespace ", io_lib:format("~s", [Name])," =\n",
@@ -249,8 +252,8 @@ decode_contract(_) -> [].
decode_funcs(Fs) -> [ decode_func(F) || F <- Fs ]. decode_funcs(Fs) -> [ decode_func(F) || F <- Fs ].
%% decode_func(#{name := init}) -> []; %% decode_func(#{name := init}) -> [];
decode_func(#{name := Name, arguments := As, returns := T}) -> decode_func(#{name := Name, payable := Payable, arguments := As, returns := T}) ->
[" entrypoint", " ", io_lib:format("~s", [Name]), " : ", [" ", payable(Payable), "entrypoint ", io_lib:format("~s", [Name]), " : ",
decode_args(As), " => ", decode_type(T), $\n]. decode_args(As), " => ", decode_type(T), $\n].
decode_args(As) -> decode_args(As) ->
@@ -264,7 +267,10 @@ decode_types(Ets) ->
decode_type(#{tuple := Ets}) -> decode_type(#{tuple := Ets}) ->
Ts = decode_types(Ets), Ts = decode_types(Ets),
[$(,lists:join(",", Ts),$)]; case Ts of
[] -> ["unit"];
_ -> [$(,lists:join(" * ", Ts),$)]
end;
decode_type(#{record := Efs}) -> decode_type(#{record := Efs}) ->
Fs = decode_fields(Efs), Fs = decode_fields(Efs),
[${,lists:join(",", Fs),$}]; [${,lists:join(",", Fs),$}];
@@ -318,13 +324,16 @@ decode_deftype(#{record := _Efs}) -> "record";
decode_deftype(#{variant := _}) -> "datatype"; decode_deftype(#{variant := _}) -> "datatype";
decode_deftype(_T) -> "type". decode_deftype(_T) -> "type".
decode_tvars([]) -> []; %No tvars, no parentheses decode_tvars([]) -> []; %No tvars, no parentheses
decode_tvars(Vs) -> decode_tvars(Vs) ->
Dvs = [ decode_tvar(V) || V <- Vs ], Dvs = [ decode_tvar(V) || V <- Vs ],
[$(,lists:join(", ", Dvs),$)]. [$(,lists:join(", ", Dvs),$)].
decode_tvar(#{name := N}) -> io_lib:format("~s", [N]). decode_tvar(#{name := N}) -> io_lib:format("~s", [N]).
payable(true) -> "payable ";
payable(false) -> "".
%% #contract{Ann, Con, [Declarations]}. %% #contract{Ann, Con, [Declarations]}.
contract_funcs({C, _, _, Decls}) when C == contract; C == namespace -> contract_funcs({C, _, _, Decls}) when C == contract; C == namespace ->
@@ -349,6 +358,7 @@ sort_decls(Ds) ->
is_entrypoint(Node) -> aeso_syntax:get_ann(entrypoint, Node, false). is_entrypoint(Node) -> aeso_syntax:get_ann(entrypoint, Node, false).
is_stateful(Node) -> aeso_syntax:get_ann(stateful, Node, false). is_stateful(Node) -> aeso_syntax:get_ann(stateful, Node, false).
is_payable(Node) -> aeso_syntax:get_ann(payable, Node, false).
typedef_name({type_def, _, {id, _, Name}, _, _}) -> Name. typedef_name({type_def, _, {id, _, Name}, _, _}) -> Name.
+80 -11
View File
@@ -83,7 +83,7 @@
-type fun_info() :: {aeso_syntax:ann(), typesig() | type()}. -type fun_info() :: {aeso_syntax:ann(), typesig() | type()}.
-type type_info() :: {aeso_syntax:ann(), typedef()}. -type type_info() :: {aeso_syntax:ann(), typedef()}.
-type var_info() :: {aeso_syntax:ann(), type()}. -type var_info() :: {aeso_syntax:ann(), utype()}.
-type fun_env() :: [{name(), fun_info()}]. -type fun_env() :: [{name(), fun_info()}].
-type type_env() :: [{name(), type_info()}]. -type type_env() :: [{name(), type_info()}].
@@ -139,11 +139,11 @@ on_current_scope(Env = #env{ namespace = NS, scopes = Scopes }, Fun) ->
on_scopes(Env = #env{ scopes = Scopes }, Fun) -> on_scopes(Env = #env{ scopes = Scopes }, Fun) ->
Env#env{ scopes = maps:map(fun(_, Scope) -> Fun(Scope) end, Scopes) }. Env#env{ scopes = maps:map(fun(_, Scope) -> Fun(Scope) end, Scopes) }.
-spec bind_var(aeso_syntax:id(), type(), env()) -> env(). -spec bind_var(aeso_syntax:id(), utype(), env()) -> env().
bind_var({id, Ann, X}, T, Env) -> bind_var({id, Ann, X}, T, Env) ->
Env#env{ vars = [{X, {Ann, T}} | Env#env.vars] }. Env#env{ vars = [{X, {Ann, T}} | Env#env.vars] }.
-spec bind_vars([{aeso_syntax:id(), type()}], env()) -> env(). -spec bind_vars([{aeso_syntax:id(), utype()}], env()) -> env().
bind_vars([], Env) -> Env; bind_vars([], Env) -> Env;
bind_vars([{X, T} | Vars], Env) -> bind_vars([{X, T} | Vars], Env) ->
bind_vars(Vars, bind_var(X, T, Env)). bind_vars(Vars, bind_var(X, T, Env)).
@@ -156,7 +156,7 @@ bind_tvars(Xs, Env) ->
check_tvar(#env{ typevars = TVars}, T = {tvar, _, X}) -> check_tvar(#env{ typevars = TVars}, T = {tvar, _, X}) ->
case TVars == unrestricted orelse lists:member(X, TVars) of case TVars == unrestricted orelse lists:member(X, TVars) of
true -> ok; true -> ok;
false -> type_error({unbound_type_variable, T}) false -> type_error({unbound_type, T})
end, end,
T. T.
@@ -363,6 +363,8 @@ global_env() ->
Pair = fun(A, B) -> {tuple_t, Ann, [A, B]} end, Pair = fun(A, B) -> {tuple_t, Ann, [A, B]} end,
Fun = fun(Ts, T) -> {type_sig, Ann, [], Ts, T} end, Fun = fun(Ts, T) -> {type_sig, Ann, [], Ts, T} end,
Fun1 = fun(S, T) -> Fun([S], T) end, Fun1 = fun(S, T) -> Fun([S], T) end,
%% Lambda = fun(Ts, T) -> {fun_t, Ann, [], Ts, T} end,
%% Lambda1 = fun(S, T) -> Lambda([S], T) end,
StateFun = fun(Ts, T) -> {type_sig, [stateful|Ann], [], Ts, T} end, StateFun = fun(Ts, T) -> {type_sig, [stateful|Ann], [], Ts, T} end,
TVar = fun(X) -> {tvar, Ann, "'" ++ X} end, TVar = fun(X) -> {tvar, Ann, "'" ++ X} end,
SignId = {id, Ann, "signature"}, SignId = {id, Ann, "signature"},
@@ -385,9 +387,10 @@ global_env() ->
{"FixedTTL", Fun1(Int, TTL)}, {"FixedTTL", Fun1(Int, TTL)},
%% Abort %% Abort
{"abort", Fun1(String, A)}, {"abort", Fun1(String, A)},
{"require", Fun([Bool, String], A)}]) {"require", Fun([Bool, String], Unit)}])
, types = MkDefs( , types = MkDefs(
[{"int", 0}, {"bool", 0}, {"char", 0}, {"string", 0}, {"address", 0}, [{"int", 0}, {"bool", 0}, {"char", 0}, {"string", 0}, {"address", 0},
{"unit", {[], {alias_t, Unit}}},
{"hash", {[], {alias_t, Bytes(32)}}}, {"hash", {[], {alias_t, Bytes(32)}}},
{"signature", {[], {alias_t, Bytes(64)}}}, {"signature", {[], {alias_t, Bytes(64)}}},
{"bits", 0}, {"bits", 0},
@@ -441,8 +444,8 @@ global_env() ->
[{"resolve", Fun([String, String], option_t(Ann, A))}, [{"resolve", Fun([String, String], option_t(Ann, A))},
{"preclaim", SignFun([Address, Hash], Unit)}, {"preclaim", SignFun([Address, Hash], Unit)},
{"claim", SignFun([Address, String, Int], Unit)}, {"claim", SignFun([Address, String, Int], Unit)},
{"transfer", SignFun([Address, Address, Hash], Unit)}, {"transfer", SignFun([Address, Address, String], Unit)},
{"revoke", SignFun([Address, Hash], Unit)}]) }, {"revoke", SignFun([Address, String], Unit)}]) },
MapScope = #scope MapScope = #scope
{ funs = MkDefs( { funs = MkDefs(
@@ -457,8 +460,10 @@ global_env() ->
%% Crypto/Curve operations %% Crypto/Curve operations
CryptoScope = #scope CryptoScope = #scope
{ funs = MkDefs( { funs = MkDefs(
[{"ecverify", Fun([Hash, Address, SignId], Bool)}, [{"verify_sig", Fun([Hash, Address, SignId], Bool)},
{"ecverify_secp256k1", Fun([Hash, Bytes(64), Bytes(64)], Bool)}, {"verify_sig_secp256k1", Fun([Hash, Bytes(64), SignId], Bool)},
{"ecverify_secp256k1", Fun([Hash, Bytes(20), Bytes(65)], Bool)},
{"ecrecover_secp256k1", Fun([Hash, Bytes(65)], Option(Bytes(20)))},
{"sha3", Fun1(A, Hash)}, {"sha3", Fun1(A, Hash)},
{"sha256", Fun1(A, Hash)}, {"sha256", Fun1(A, Hash)},
{"blake2b", Fun1(A, Hash)}]) }, {"blake2b", Fun1(A, Hash)}]) },
@@ -500,7 +505,8 @@ global_env() ->
IntScope = #scope{ funs = MkDefs([{"to_str", Fun1(Int, String)}]) }, IntScope = #scope{ funs = MkDefs([{"to_str", Fun1(Int, String)}]) },
AddressScope = #scope{ funs = MkDefs([{"to_str", Fun1(Address, String)}, AddressScope = #scope{ funs = MkDefs([{"to_str", Fun1(Address, String)},
{"is_oracle", Fun1(Address, Bool)}, {"is_oracle", Fun1(Address, Bool)},
{"is_contract", Fun1(Address, Bool)}]) }, {"is_contract", Fun1(Address, Bool)},
{"is_payable", Fun1(Address, Bool)}]) },
#env{ scopes = #env{ scopes =
#{ [] => TopScope #{ [] => TopScope
@@ -1073,6 +1079,58 @@ infer_expr(Env, {list, As, Elems}) ->
ElemType = fresh_uvar(As), ElemType = fresh_uvar(As),
NewElems = [check_expr(Env, X, ElemType) || X <- Elems], NewElems = [check_expr(Env, X, ElemType) || X <- Elems],
{typed, As, {list, As, NewElems}, {app_t, As, {id, As, "list"}, [ElemType]}}; {typed, As, {list, As, NewElems}, {app_t, As, {id, As, "list"}, [ElemType]}};
infer_expr(Env, {list_comp, As, Yield, []}) ->
{typed, _, TypedYield, Type} = infer_expr(Env, Yield),
{typed, As, {list_comp, As, TypedYield, []}, {app_t, As, {id, As, "list"}, [Type]}};
infer_expr(Env, {list_comp, As, Yield, [{comprehension_bind, Arg, BExpr}|Rest]}) ->
BindVarType = fresh_uvar(As),
TypedBind = {typed, As2, _, TypeBExpr} = infer_expr(Env, BExpr),
unify( Env
, TypeBExpr
, {app_t, As, {id, As, "list"}, [BindVarType]}
, {list_comp, TypedBind, TypeBExpr, {app_t, As2, {id, As, "list"}, [BindVarType]}}),
NewE = bind_var(Arg, BindVarType, Env),
{typed, _, {list_comp, _, TypedYield, TypedRest}, ResType} =
infer_expr(NewE, {list_comp, As, Yield, Rest}),
{ typed
, As
, {list_comp, As, TypedYield, [{comprehension_bind, {typed, Arg, BindVarType}, TypedBind}|TypedRest]}
, ResType};
infer_expr(Env, {list_comp, AttrsL, Yield, [{comprehension_if, AttrsIF, Cond}|Rest]}) ->
NewCond = check_expr(Env, Cond, {id, AttrsIF, "bool"}),
{typed, _, {list_comp, _, TypedYield, TypedRest}, ResType} =
infer_expr(Env, {list_comp, AttrsL, Yield, Rest}),
{ typed
, AttrsL
, {list_comp, AttrsL, TypedYield, [{comprehension_if, AttrsIF, NewCond}|TypedRest]}
, ResType};
infer_expr(Env, {list_comp, AsLC, Yield, [{letval, AsLV, Pattern, Type, E}|Rest]}) ->
NewE = {typed, _, _, PatType} = infer_expr(Env, {typed, AsLV, E, arg_type(Type)}),
BlockType = fresh_uvar(AsLV),
{'case', _, NewPattern, NewRest} =
infer_case( Env
, AsLC
, Pattern
, PatType
, {list_comp, AsLC, Yield, Rest}
, BlockType),
{typed, _, {list_comp, _, TypedYield, TypedRest}, ResType} = NewRest,
{ typed
, AsLC
, {list_comp, AsLC, TypedYield, [{letval, AsLV, NewPattern, Type, NewE}|TypedRest]}
, ResType
};
infer_expr(Env, {list_comp, AsLC, Yield, [Def={letfun, AsLF, _, _, _, _}|Rest]}) ->
{{Name, TypeSig}, LetFun} = infer_letfun(Env, Def),
FunT = freshen_type(AsLF, typesig_to_fun_t(TypeSig)),
NewE = bind_var({id, AsLF, Name}, FunT, Env),
{typed, _, {list_comp, _, TypedYield, TypedRest}, ResType} =
infer_expr(NewE, {list_comp, AsLC, Yield, Rest}),
{ typed
, AsLC
, {list_comp, AsLC, TypedYield, [LetFun|TypedRest]}
, ResType
};
infer_expr(Env, {typed, As, Body, Type}) -> infer_expr(Env, {typed, As, Body, Type}) ->
Type1 = check_type(Env, Type), Type1 = check_type(Env, Type),
{typed, _, NewBody, NewType} = check_expr(Env, Body, Type1), {typed, _, NewBody, NewType} = check_expr(Env, Body, Type1),
@@ -2159,6 +2217,8 @@ pp_error({contract_has_no_entrypoints, Con}) ->
io_lib:format("The contract ~s (at ~s) has no entrypoints. Since Sophia version 3.2, public\n" io_lib:format("The contract ~s (at ~s) has no entrypoints. Since Sophia version 3.2, public\n"
"contract functions must be declared with the 'entrypoint' keyword instead of\n" "contract functions must be declared with the 'entrypoint' keyword instead of\n"
"'function'.\n", [pp_expr("", Con), pp_loc(Con)]); "'function'.\n", [pp_expr("", Con), pp_loc(Con)]);
pp_error({unbound_type, Type}) ->
io_lib:format("Unbound type ~s (at ~s).\n", [pp_type("", Type), pp_loc(Type)]);
pp_error(Err) -> pp_error(Err) ->
io_lib:format("Unknown error: ~p\n", [Err]). io_lib:format("Unknown error: ~p\n", [Err]).
@@ -2250,6 +2310,13 @@ pp_when({check_expr, Expr, Inferred0, Expected0}) ->
pp_when({checking_init_type, Ann}) -> pp_when({checking_init_type, Ann}) ->
io_lib:format("when checking that 'init' returns a value of type 'state' at ~s\n", io_lib:format("when checking that 'init' returns a value of type 'state' at ~s\n",
[pp_loc(Ann)]); [pp_loc(Ann)]);
pp_when({list_comp, BindExpr, Inferred0, Expected0}) ->
{Inferred, Expected} = instantiate({Inferred0, Expected0}),
io_lib:format("when checking rvalue of list comprehension binding at ~s\n~s\n"
"against type \n~s\n",
[pp_loc(BindExpr), pp_typed(" ", BindExpr, Inferred), pp_type(" ", Expected)]
);
pp_when(unknown) -> "". pp_when(unknown) -> "".
-spec pp_why_record(why_record()) -> iolist(). -spec pp_why_record(why_record()) -> iolist().
@@ -2320,8 +2387,10 @@ pp({uvar, _, Ref}) ->
["?u" | integer_to_list(erlang:phash2(Ref, 16384)) ]; ["?u" | integer_to_list(erlang:phash2(Ref, 16384)) ];
pp({tvar, _, Name}) -> pp({tvar, _, Name}) ->
Name; Name;
pp({tuple_t, _, []}) ->
"unit";
pp({tuple_t, _, Cpts}) -> pp({tuple_t, _, Cpts}) ->
["(", pp(Cpts), ")"]; ["(", string:join(lists:map(fun pp/1, Cpts), " * "), ")"];
pp({bytes_t, _, any}) -> "bytes(_)"; pp({bytes_t, _, any}) -> "bytes(_)";
pp({bytes_t, _, Len}) -> pp({bytes_t, _, Len}) ->
["bytes(", integer_to_list(Len), ")"]; ["bytes(", integer_to_list(Len), ")"];
+176 -40
View File
@@ -16,7 +16,7 @@
-type option() :: term(). -type option() :: term().
-type attribute() :: stateful | pure | private. -type attribute() :: stateful | payable | pure | private.
-type fun_name() :: {entrypoint, binary()} -type fun_name() :: {entrypoint, binary()}
| {local_fun, [string()]} | {local_fun, [string()]}
@@ -32,8 +32,9 @@
map_delete | map_member | map_size | string_length | map_delete | map_member | map_size | string_length |
string_concat | bits_set | bits_clear | bits_test | bits_sum | string_concat | bits_set | bits_clear | bits_test | bits_sum |
bits_intersection | bits_union | bits_difference | bits_intersection | bits_union | bits_difference |
contract_to_address | crypto_ecverify | crypto_ecverify_secp256k1 | contract_to_address | crypto_verify_sig | crypto_verify_sig_secp256k1 |
crypto_sha3 | crypto_sha256 | crypto_blake2b. crypto_sha3 | crypto_sha256 | crypto_blake2b |
crypto_ecverify_secp256k1 | crypto_ecrecover_secp256k1.
-type flit() :: {int, integer()} -type flit() :: {int, integer()}
| {string, binary()} | {string, binary()}
@@ -49,7 +50,7 @@
| nil | nil
| {var, var_name()} | {var, var_name()}
| {def, fun_name(), [fexpr()]} | {def, fun_name(), [fexpr()]}
| {remote, fexpr(), fun_name(), [fexpr()]} | {remote, [ftype()], ftype(), fexpr(), fun_name(), [fexpr()]}
| {builtin, builtin(), [fexpr()]} | {builtin, builtin(), [fexpr()]}
| {con, arities(), tag(), [fexpr()]} | {con, arities(), tag(), [fexpr()]}
| {tuple, [fexpr()]} | {tuple, [fexpr()]}
@@ -64,7 +65,7 @@
%% lambdas) are generated by the fcode compiler, but translated %% lambdas) are generated by the fcode compiler, but translated
%% to closures by the lambda lifter. %% to closures by the lambda lifter.
| {def_u, fun_name(), arity()} | {def_u, fun_name(), arity()}
| {remote_u, fexpr(), fun_name(), arity()} | {remote_u, [ftype()], ftype(), fexpr(), fun_name()}
| {builtin_u, builtin(), arity()} | {builtin_u, builtin(), arity()}
| {lam, [var_name()], fexpr()}. | {lam, [var_name()], fexpr()}.
@@ -108,7 +109,8 @@
-type fcode() :: #{ contract_name := string(), -type fcode() :: #{ contract_name := string(),
state_type := ftype(), state_type := ftype(),
event_type := ftype() | none, event_type := ftype() | none,
functions := #{ fun_name() => fun_def() } }. functions := #{ fun_name() => fun_def() },
payable := boolean() }.
-type type_def() :: fun(([ftype()]) -> ftype()). -type type_def() :: fun(([ftype()]) -> ftype()).
@@ -187,15 +189,16 @@ builtins() ->
{"revoke", 3}]}, {"revoke", 3}]},
{["Map"], [{"from_list", 1}, {"to_list", 1}, {"lookup", 2}, {["Map"], [{"from_list", 1}, {"to_list", 1}, {"lookup", 2},
{"lookup_default", 3}, {"delete", 2}, {"member", 2}, {"size", 1}]}, {"lookup_default", 3}, {"delete", 2}, {"member", 2}, {"size", 1}]},
{["Crypto"], [{"ecverify", 3}, {"ecverify_secp256k1", 3}, {"sha3", 1}, {["Crypto"], [{"verify_sig", 3}, {"verify_sig_secp256k1", 3},
{"sha256", 1}, {"blake2b", 1}]}, {"ecverify_secp256k1", 3}, {"ecrecover_secp256k1", 2},
{"sha3", 1}, {"sha256", 1}, {"blake2b", 1}]},
{["Auth"], [{"tx_hash", none}]}, {["Auth"], [{"tx_hash", none}]},
{["String"], [{"length", 1}, {"concat", 2}, {"sha3", 1}, {"sha256", 1}, {"blake2b", 1}]}, {["String"], [{"length", 1}, {"concat", 2}, {"sha3", 1}, {"sha256", 1}, {"blake2b", 1}]},
{["Bits"], [{"set", 2}, {"clear", 2}, {"test", 2}, {"sum", 1}, {"intersection", 2}, {["Bits"], [{"set", 2}, {"clear", 2}, {"test", 2}, {"sum", 1}, {"intersection", 2},
{"union", 2}, {"difference", 2}, {"none", none}, {"all", none}]}, {"union", 2}, {"difference", 2}, {"none", none}, {"all", none}]},
{["Bytes"], [{"to_int", 1}, {"to_str", 1}]}, {["Bytes"], [{"to_int", 1}, {"to_str", 1}]},
{["Int"], [{"to_str", 1}]}, {["Int"], [{"to_str", 1}]},
{["Address"], [{"to_str", 1}, {"is_oracle", 1}, {"is_contract", 1}]} {["Address"], [{"to_str", 1}, {"is_oracle", 1}, {"is_contract", 1}, {"is_payable", 1}]}
], ],
maps:from_list([ {NS ++ [Fun], {MkName(NS, Fun), Arity}} maps:from_list([ {NS ++ [Fun], {MkName(NS, Fun), Arity}}
|| {NS, Funs} <- Scopes, || {NS, Funs} <- Scopes,
@@ -223,10 +226,13 @@ init_type_env() ->
["Chain", "ttl"] => ?type({variant, [[integer], [integer]]}) ["Chain", "ttl"] => ?type({variant, [[integer], [integer]]})
}. }.
is_no_code(Env) ->
proplists:get_value(no_code, maps:get(options, Env, []), false).
%% -- Compilation ------------------------------------------------------------ %% -- Compilation ------------------------------------------------------------
-spec to_fcode(env(), aeso_syntax:ast()) -> fcode(). -spec to_fcode(env(), aeso_syntax:ast()) -> fcode().
to_fcode(Env, [{contract, _, {con, _, Main}, Decls}]) -> to_fcode(Env, [{contract, Attrs, {con, _, Main}, Decls}]) ->
#{ builtins := Builtins } = Env, #{ builtins := Builtins } = Env,
MainEnv = Env#{ context => {main_contract, Main}, MainEnv = Env#{ context => {main_contract, Main},
builtins => Builtins#{[Main, "state"] => {get_state, none}, builtins => Builtins#{[Main, "state"] => {get_state, none},
@@ -236,10 +242,13 @@ to_fcode(Env, [{contract, _, {con, _, Main}, Decls}]) ->
decls_to_fcode(MainEnv, Decls), decls_to_fcode(MainEnv, Decls),
StateType = lookup_type(Env1, [Main, "state"], [], {tuple, []}), StateType = lookup_type(Env1, [Main, "state"], [], {tuple, []}),
EventType = lookup_type(Env1, [Main, "event"], [], none), EventType = lookup_type(Env1, [Main, "event"], [], none),
Payable = proplists:get_value(payable, Attrs, false),
#{ contract_name => Main, #{ contract_name => Main,
state_type => StateType, state_type => StateType,
event_type => EventType, event_type => EventType,
functions => add_event_function(Env1, EventType, Funs) }; payable => Payable,
functions => add_init_function(Env1, StateType,
add_event_function(Env1, EventType, Funs)) };
to_fcode(Env, [{contract, _, {con, _, Con}, Decls} | Code]) -> to_fcode(Env, [{contract, _, {con, _, Con}, Decls} | Code]) ->
Env1 = decls_to_fcode(Env#{ context => {abstract_contract, Con} }, Decls), Env1 = decls_to_fcode(Env#{ context => {abstract_contract, Con} }, Decls),
to_fcode(Env1, Code); to_fcode(Env1, Code);
@@ -261,6 +270,11 @@ decls_to_fcode(Env, Decls) ->
-spec decl_to_fcode(env(), aeso_syntax:decl()) -> env(). -spec decl_to_fcode(env(), aeso_syntax:decl()) -> env().
decl_to_fcode(Env, {type_decl, _, _, _}) -> Env; decl_to_fcode(Env, {type_decl, _, _, _}) -> Env;
decl_to_fcode(Env = #{context := {main_contract, _}}, {fun_decl, Ann, {id, _, Name}, _}) ->
case is_no_code(Env) of
false -> fcode_error({missing_definition, Name, lists:keydelete(entrypoint, 1, Ann)});
true -> Env
end;
decl_to_fcode(Env, {fun_decl, _, _, _}) -> Env; decl_to_fcode(Env, {fun_decl, _, _, _}) -> Env;
decl_to_fcode(Env, {type_def, _Ann, Name, Args, Def}) -> decl_to_fcode(Env, {type_def, _Ann, Name, Args, Def}) ->
typedef_to_fcode(Env, Name, Args, Def); typedef_to_fcode(Env, Name, Args, Def);
@@ -398,9 +412,10 @@ expr_to_fcode(Env, Type, {proj, _Ann, Rec = {typed, _, _, RecType}, {id, _, X}})
{con, _, _} when X == "address" -> {con, _, _} when X == "address" ->
{op, contract_to_address, [expr_to_fcode(Env, Rec)]}; {op, contract_to_address, [expr_to_fcode(Env, Rec)]};
{con, _, _} -> {con, _, _} ->
{fun_t, _, Named, Args, _} = Type, {fun_t, _, _, Args, Ret} = Type,
Arity = length(Named) + length(Args), FArgs = [type_to_fcode(Env, Arg) || Arg <- Args],
{remote_u, expr_to_fcode(Env, Rec), {entrypoint, list_to_binary(X)}, Arity}; {remote_u, FArgs, type_to_fcode(Env, Ret), expr_to_fcode(Env, Rec),
{entrypoint, list_to_binary(X)}};
{record_t, _} -> {record_t, _} ->
{proj, expr_to_fcode(Env, Rec), field_index(Rec, X)} {proj, expr_to_fcode(Env, Rec), field_index(Rec, X)}
end; end;
@@ -438,6 +453,23 @@ expr_to_fcode(Env, _Type, {list, _, Es}) ->
lists:foldr(fun(E, L) -> {op, '::', [expr_to_fcode(Env, E), L]} end, lists:foldr(fun(E, L) -> {op, '::', [expr_to_fcode(Env, E), L]} end,
nil, Es); nil, Es);
expr_to_fcode(Env, _Type, {list_comp, _, Yield, []}) ->
{op, '::', [expr_to_fcode(Env, Yield), nil]};
expr_to_fcode(Env, _Type, {list_comp, As, Yield, [{comprehension_bind, {typed, {id, _, Arg}, _}, BindExpr}|Rest]}) ->
Env1 = bind_var(Env, Arg),
Bind = {lam, [Arg], expr_to_fcode(Env1, {list_comp, As, Yield, Rest})},
{def_u, FlatMap, _} = resolve_fun(Env, ["List", "flat_map"]),
{def, FlatMap, [Bind, expr_to_fcode(Env, BindExpr)]};
expr_to_fcode(Env, Type, {list_comp, As, Yield, [{comprehension_if, _, Cond}|Rest]}) ->
make_if(expr_to_fcode(Env, Cond),
expr_to_fcode(Env, Type, {list_comp, As, Yield, Rest}),
nil
);
expr_to_fcode(Env, Type, {list_comp, As, Yield, [LV = {letval, _, _, _, _}|Rest]}) ->
expr_to_fcode(Env, Type, {block, As, [LV, {list_comp, As, Yield, Rest}]});
expr_to_fcode(Env, Type, {list_comp, As, Yield, [LF = {letfun, _, _, _, _, _}|Rest]}) ->
expr_to_fcode(Env, Type, {block, As, [LF, {list_comp, As, Yield, Rest}]});
%% Conditionals %% Conditionals
expr_to_fcode(Env, _Type, {'if', _, Cond, Then, Else}) -> expr_to_fcode(Env, _Type, {'if', _, Cond, Then, Else}) ->
make_if(expr_to_fcode(Env, Cond), make_if(expr_to_fcode(Env, Cond),
@@ -497,7 +529,7 @@ expr_to_fcode(Env, Type, {app, _Ann, Fun = {typed, _, _, {fun_t, _, NamedArgsT,
builtin_to_fcode(B, FArgs ++ TypeArgs); builtin_to_fcode(B, FArgs ++ TypeArgs);
{builtin_u, B, _Ar} -> builtin_to_fcode(B, FArgs); {builtin_u, B, _Ar} -> builtin_to_fcode(B, FArgs);
{def_u, F, _Ar} -> {def, F, FArgs}; {def_u, F, _Ar} -> {def, F, FArgs};
{remote_u, Ct, RFun, _Ar} -> {remote, Ct, RFun, FArgs}; {remote_u, ArgsT, RetT, Ct, RFun} -> {remote, ArgsT, RetT, Ct, RFun, FArgs};
FFun -> FFun ->
%% FFun is a closure, with first component the function name and %% FFun is a closure, with first component the function name and
%% second component the environment %% second component the environment
@@ -794,8 +826,10 @@ op_builtins() ->
[map_from_list, map_to_list, map_delete, map_member, map_size, [map_from_list, map_to_list, map_delete, map_member, map_size,
string_length, string_concat, string_sha3, string_sha256, string_blake2b, string_length, string_concat, string_sha3, string_sha256, string_blake2b,
bits_set, bits_clear, bits_test, bits_sum, bits_intersection, bits_union, bits_set, bits_clear, bits_test, bits_sum, bits_intersection, bits_union,
bits_difference, int_to_str, address_to_str, crypto_ecverify, bits_difference, int_to_str, address_to_str, crypto_verify_sig,
crypto_ecverify_secp256k1, crypto_sha3, crypto_sha256, crypto_blake2b]. crypto_verify_sig_secp256k1, crypto_sha3, crypto_sha256, crypto_blake2b,
crypto_ecverify_secp256k1, crypto_ecrecover_secp256k1
].
builtin_to_fcode(require, [Cond, Msg]) -> builtin_to_fcode(require, [Cond, Msg]) ->
make_if(Cond, {tuple, []}, {builtin, abort, [Msg]}); make_if(Cond, {tuple, []}, {builtin, abort, [Msg]});
@@ -819,6 +853,33 @@ builtin_to_fcode(Builtin, Args) ->
false -> {builtin, Builtin, Args} false -> {builtin, Builtin, Args}
end. end.
%% -- Init function --
add_init_function(Env, StateType, Funs0) ->
case is_no_code(Env) of
true -> Funs0;
false ->
Funs = add_default_init_function(Env, StateType, Funs0),
InitName = {entrypoint, <<"init">>},
InitFun = #{ args := InitArgs } = maps:get(InitName, Funs, none),
Vars = [ {var, X} || {X, _} <- InitArgs ],
Funs#{ init => InitFun#{ return => {tuple, []},
body => {builtin, set_state, [{def, InitName, Vars}]} } }
end.
add_default_init_function(_Env, StateType, Funs) ->
InitName = {entrypoint, <<"init">>},
case maps:get(InitName, Funs, none) of
%% Only add default init function if state is unit.
none when StateType == {tuple, []} ->
Funs#{ InitName => #{attrs => [],
args => [],
return => {tuple, []},
body => {tuple, []}} };
none -> fcode_error(missing_init_function);
_ -> Funs
end.
%% -- Event function -- %% -- Event function --
add_event_function(_Env, none, Funs) -> Funs; add_event_function(_Env, none, Funs) -> Funs;
@@ -905,12 +966,13 @@ lambda_lift_expr({Tag, F, Ar}) when Tag == def_u; Tag == builtin_u ->
def_u -> {def, F, Args} def_u -> {def, F, Args}
end, end,
make_closure([], Xs, Body); make_closure([], Xs, Body);
lambda_lift_expr({remote_u, Ct, F, Ar}) -> lambda_lift_expr({remote_u, ArgsT, RetT, Ct, F}) ->
FVs = free_vars(Ct), FVs = free_vars(Ct),
Ct1 = lambda_lift_expr(Ct), Ct1 = lambda_lift_expr(Ct),
Xs = [ lists:concat(["arg", I]) || I <- lists:seq(1, Ar) ], GasAndValueArgs = 2,
Xs = [ lists:concat(["arg", I]) || I <- lists:seq(1, length(ArgsT) + GasAndValueArgs) ],
Args = [{var, X} || X <- Xs], Args = [{var, X} || X <- Xs],
make_closure(FVs, Xs, {remote, Ct1, F, Args}); make_closure(FVs, Xs, {remote, ArgsT, RetT, Ct1, F, Args});
lambda_lift_expr(Expr) -> lambda_lift_expr(Expr) ->
case Expr of case Expr of
{lit, _} -> Expr; {lit, _} -> Expr;
@@ -919,7 +981,7 @@ lambda_lift_expr(Expr) ->
{closure, _, _} -> Expr; {closure, _, _} -> Expr;
{def, D, As} -> {def, D, lambda_lift_exprs(As)}; {def, D, As} -> {def, D, lambda_lift_exprs(As)};
{builtin, B, As} -> {builtin, B, lambda_lift_exprs(As)}; {builtin, B, As} -> {builtin, B, lambda_lift_exprs(As)};
{remote, Ct, F, As} -> {remote, lambda_lift_expr(Ct), F, lambda_lift_exprs(As)}; {remote, ArgsT, RetT, Ct, F, As} -> {remote, ArgsT, RetT, lambda_lift_expr(Ct), F, lambda_lift_exprs(As)};
{con, Ar, C, As} -> {con, Ar, C, lambda_lift_exprs(As)}; {con, Ar, C, As} -> {con, Ar, C, lambda_lift_exprs(As)};
{tuple, As} -> {tuple, lambda_lift_exprs(As)}; {tuple, As} -> {tuple, lambda_lift_exprs(As)};
{proj, A, I} -> {proj, lambda_lift_expr(A), I}; {proj, A, I} -> {proj, lambda_lift_expr(A), I};
@@ -945,13 +1007,16 @@ lambda_lift_exprs(As) -> [lambda_lift_expr(A) || A <- As].
-spec optimize_fcode(fcode()) -> fcode(). -spec optimize_fcode(fcode()) -> fcode().
optimize_fcode(Code = #{ functions := Funs }) -> optimize_fcode(Code = #{ functions := Funs }) ->
Code#{ functions := maps:map(fun(Name, Def) -> optimize_fun(Code, Name, Def) end, Funs) }. Code1 = Code#{ functions := maps:map(fun(Name, Def) -> optimize_fun(Code, Name, Def) end, Funs) },
eliminate_dead_code(Code1).
-spec optimize_fun(fcode(), fun_name(), fun_def()) -> fun_def(). -spec optimize_fun(fcode(), fun_name(), fun_def()) -> fun_def().
optimize_fun(Fcode, Fun, Def = #{ body := Body }) -> optimize_fun(Fcode, Fun, Def = #{ body := Body }) ->
%% io:format("Optimizing ~p =\n~s\n", [_Fun, prettypr:format(pp_fexpr(_Body))]), %% io:format("Optimizing ~p =\n~s\n", [_Fun, prettypr:format(pp_fexpr(_Body))]),
Def#{ body := inliner(Fcode, Fun, Body) }. Def#{ body := inliner(Fcode, Fun, Body) }.
%% --- Inlining ---
-spec inliner(fcode(), fun_name(), fexpr()) -> fexpr(). -spec inliner(fcode(), fun_name(), fexpr()) -> fexpr().
inliner(Fcode, Fun, {def, Fun1, Args} = E) when Fun1 /= Fun -> inliner(Fcode, Fun, {def, Fun1, Args} = E) when Fun1 /= Fun ->
case should_inline(Fcode, Fun1) of case should_inline(Fcode, Fun1) of
@@ -964,6 +1029,33 @@ should_inline(_Fcode, _Fun1) -> false == list_to_atom("true"). %% Dialyzer
inline(_Fcode, Fun, Args) -> {def, Fun, Args}. %% TODO inline(_Fcode, Fun, Args) -> {def, Fun, Args}. %% TODO
%% --- Deadcode elimination ---
-spec eliminate_dead_code(fcode()) -> fcode().
eliminate_dead_code(Code = #{ functions := Funs }) ->
UsedFuns = used_functions(Funs),
Code#{ functions := maps:filter(fun(Name, _) -> maps:is_key(Name, UsedFuns) end,
Funs) }.
-spec used_functions(#{ fun_name() => fun_def() }) -> #{ fun_name() => true }.
used_functions(Funs) ->
Exported = [ Fun || {Fun, #{ attrs := Attrs }} <- maps:to_list(Funs),
not lists:member(private, Attrs) ],
used_functions(#{}, Exported, Funs).
used_functions(Used, [], _) -> Used;
used_functions(Used, [Name | Rest], Defs) ->
case maps:is_key(Name, Used) of
true -> used_functions(Used, Rest, Defs);
false ->
New =
case maps:get(Name, Defs, undef) of
undef -> []; %% We might be compiling a stub
#{ body := Body } -> used_defs(Body)
end,
used_functions(Used#{ Name => true }, New ++ Rest, Defs)
end.
%% -- Helper functions ------------------------------------------------------- %% -- Helper functions -------------------------------------------------------
%% -- Types -- %% -- Types --
@@ -1001,6 +1093,8 @@ add_fun_env(Env = #{ context := {abstract_contract, _} }, _) -> Env; %% no func
add_fun_env(Env = #{ fun_env := FunEnv }, Decls) -> add_fun_env(Env = #{ fun_env := FunEnv }, Decls) ->
Entry = fun({letfun, Ann, {id, _, Name}, Args, _, _}) -> Entry = fun({letfun, Ann, {id, _, Name}, Args, _, _}) ->
[{qname(Env, Name), {make_fun_name(Env, Ann, Name), length(Args)}}]; [{qname(Env, Name), {make_fun_name(Env, Ann, Name), length(Args)}}];
({fun_decl, Ann, {id, _, Name}, {fun_t, _, _, ArgTypes, _}}) ->
[{qname(Env, Name), {make_fun_name(Env, Ann, Name), length(ArgTypes)}}];
(_) -> [] end, (_) -> [] end,
FunEnv1 = maps:from_list(lists:flatmap(Entry, Decls)), FunEnv1 = maps:from_list(lists:flatmap(Entry, Decls)),
Env#{ fun_env := maps:merge(FunEnv, FunEnv1) }. Env#{ fun_env := maps:merge(FunEnv, FunEnv1) }.
@@ -1009,8 +1103,7 @@ make_fun_name(#{ context := Context }, Ann, Name) ->
Entrypoint = proplists:get_value(entrypoint, Ann, false), Entrypoint = proplists:get_value(entrypoint, Ann, false),
case Context of case Context of
{main_contract, Main} -> {main_contract, Main} ->
if Name == "init" -> init; if Entrypoint -> {entrypoint, list_to_binary(Name)};
Entrypoint -> {entrypoint, list_to_binary(Name)};
true -> {local_fun, [Main, Name]} true -> {local_fun, [Main, Name]}
end; end;
{namespace, Lib} -> {namespace, Lib} ->
@@ -1094,6 +1187,16 @@ pat_vars({tuple, Ps}) -> pat_vars(Ps);
pat_vars({con, _, _, Ps}) -> pat_vars(Ps); pat_vars({con, _, _, Ps}) -> pat_vars(Ps);
pat_vars(Ps) when is_list(Ps) -> [X || P <- Ps, X <- pat_vars(P)]. pat_vars(Ps) when is_list(Ps) -> [X || P <- Ps, X <- pat_vars(P)].
-spec fsplit_pat_vars(fsplit_pat()) -> [var_name()].
fsplit_pat_vars({var, X}) -> [X || X /= "_"];
fsplit_pat_vars({bool, _}) -> [];
fsplit_pat_vars({int, _}) -> [];
fsplit_pat_vars({string, _}) -> [];
fsplit_pat_vars(nil) -> [];
fsplit_pat_vars({'::', P, Q}) -> [P, Q];
fsplit_pat_vars({tuple, Ps}) -> Ps;
fsplit_pat_vars({con, _, _, Ps}) -> Ps.
free_vars(Xs) when is_list(Xs) -> free_vars(Xs) when is_list(Xs) ->
lists:umerge([ free_vars(X) || X <- Xs ]); lists:umerge([ free_vars(X) || X <- Xs ]);
free_vars(Expr) -> free_vars(Expr) ->
@@ -1103,8 +1206,8 @@ free_vars(Expr) ->
nil -> []; nil -> [];
{def, _, As} -> free_vars(As); {def, _, As} -> free_vars(As);
{def_u, _, _} -> []; {def_u, _, _} -> [];
{remote, Ct, _, As} -> free_vars([Ct | As]); {remote, _, _, Ct, _, As} -> free_vars([Ct | As]);
{remote_u, Ct, _, _} -> free_vars(Ct); {remote_u, _, _, Ct, _} -> free_vars(Ct);
{builtin, _, As} -> free_vars(As); {builtin, _, As} -> free_vars(As);
{builtin_u, _, _} -> []; {builtin_u, _, _} -> [];
{con, _, _, As} -> free_vars(As); {con, _, _, As} -> free_vars(As);
@@ -1119,7 +1222,35 @@ free_vars(Expr) ->
{switch, A} -> free_vars(A); {switch, A} -> free_vars(A);
{split, _, X, As} -> free_vars([{var, X} | As]); {split, _, X, As} -> free_vars([{var, X} | As]);
{nosplit, A} -> free_vars(A); {nosplit, A} -> free_vars(A);
{'case', P, A} -> free_vars(A) -- lists:sort(pat_vars(P)) {'case', P, A} -> free_vars(A) -- lists:sort(fsplit_pat_vars(P))
end.
used_defs(Xs) when is_list(Xs) ->
lists:umerge([ used_defs(X) || X <- Xs ]);
used_defs(Expr) ->
case Expr of
{var, _} -> [];
{lit, _} -> [];
nil -> [];
{def, F, As} -> lists:umerge([F], used_defs(As));
{def_u, F, _} -> [F];
{remote, _, _, Ct, _, As} -> used_defs([Ct | As]);
{remote_u, _, _, Ct, _} -> used_defs(Ct);
{builtin, _, As} -> used_defs(As);
{builtin_u, _, _} -> [];
{con, _, _, As} -> used_defs(As);
{tuple, As} -> used_defs(As);
{proj, A, _} -> used_defs(A);
{set_proj, A, _, B} -> used_defs([A, B]);
{op, _, As} -> used_defs(As);
{'let', _, A, B} -> used_defs([A, B]);
{funcall, A, Bs} -> used_defs([A | Bs]);
{lam, _, B} -> used_defs(B);
{closure, F, A} -> lists:umerge([F], used_defs(A));
{switch, A} -> used_defs(A);
{split, _, _, As} -> used_defs(As);
{nosplit, A} -> used_defs(A);
{'case', _, A} -> used_defs(A)
end. end.
get_named_args(NamedArgsT, Args) -> get_named_args(NamedArgsT, Args) ->
@@ -1147,8 +1278,8 @@ rename(Ren, Expr) ->
{def_u, _, _} -> Expr; {def_u, _, _} -> Expr;
{builtin, B, Es} -> {builtin, B, [rename(Ren, E) || E <- Es]}; {builtin, B, Es} -> {builtin, B, [rename(Ren, E) || E <- Es]};
{builtin_u, _, _} -> Expr; {builtin_u, _, _} -> Expr;
{remote, Ct, F, Es} -> {remote, rename(Ren, Ct), F, [rename(Ren, E) || E <- Es]}; {remote, ArgsT, RetT, Ct, F, Es} -> {remote, ArgsT, RetT, rename(Ren, Ct), F, [rename(Ren, E) || E <- Es]};
{remote_u, Ct, F, Ar} -> {remote_u, rename(Ren, Ct), F, Ar}; {remote_u, ArgsT, RetT, Ct, F} -> {remote_u, ArgsT, RetT, rename(Ren, Ct), F};
{con, Ar, I, Es} -> {con, Ar, I, [rename(Ren, E) || E <- Es]}; {con, Ar, I, Es} -> {con, Ar, I, [rename(Ren, E) || E <- Es]};
{tuple, Es} -> {tuple, [rename(Ren, E) || E <- Es]}; {tuple, Es} -> {tuple, [rename(Ren, E) || E <- Es]};
{proj, E, I} -> {proj, rename(Ren, E), I}; {proj, E, I} -> {proj, rename(Ren, E), I};
@@ -1252,7 +1383,9 @@ field_value({field_t, _, {id, _, X}, _}, Fields) ->
%% -- Attributes -- %% -- Attributes --
get_attributes(Ann) -> get_attributes(Ann) ->
[stateful || proplists:get_value(stateful, Ann, false)]. [stateful || proplists:get_value(stateful, Ann, false)] ++
[payable || proplists:get_value(payable, Ann, false)] ++
[private || not proplists:get_value(entrypoint, Ann, false)].
%% -- Basic utilities -- %% -- Basic utilities --
@@ -1278,14 +1411,17 @@ pp_fun(Name, #{ args := Args, return := Return, body := Body }) ->
pp_text(" : "), pp_ftype(Return), pp_text(" =")]), pp_text(" : "), pp_ftype(Return), pp_text(" =")]),
prettypr:nest(2, pp_fexpr(Body))). prettypr:nest(2, pp_fexpr(Body))).
pp_fun_name(init) -> pp_text(init); pp_fun_name(init) -> pp_text('INIT');
pp_fun_name(event) -> pp_text(event); pp_fun_name(event) -> pp_text(event);
pp_fun_name({entrypoint, E}) -> pp_text(binary_to_list(E)); pp_fun_name({entrypoint, E}) -> pp_text(binary_to_list(E));
pp_fun_name({local_fun, Q}) -> pp_text(string:join(Q, ".")). pp_fun_name({local_fun, Q}) -> pp_text(string:join(Q, ".")).
pp_text(<<>>) -> prettypr:text("\"\""); pp_text(<<>>) -> prettypr:text("\"\"");
pp_text(Bin) when is_binary(Bin) -> prettypr:text(lists:flatten(io_lib:format("~p", [binary_to_list(Bin)]))); pp_text(Bin) when is_binary(Bin) -> prettypr:text(lists:flatten(io_lib:format("~p", [binary_to_list(Bin)])));
pp_text(S) -> prettypr:text(lists:concat([S])). pp_text(S) when is_list(S) -> prettypr:text(lists:concat([S]));
pp_text(A) when is_atom(A) -> prettypr:text(atom_to_list(A)).
pp_int(I) -> prettypr:text(integer_to_list(I)).
pp_beside([]) -> prettypr:empty(); pp_beside([]) -> prettypr:empty();
pp_beside([X]) -> X; pp_beside([X]) -> X;
@@ -1317,18 +1453,18 @@ pp_fexpr(nil) ->
pp_fexpr({var, X}) -> pp_text(X); pp_fexpr({var, X}) -> pp_text(X);
pp_fexpr({def, Fun}) -> pp_fun_name(Fun); pp_fexpr({def, Fun}) -> pp_fun_name(Fun);
pp_fexpr({def_u, Fun, Ar}) -> pp_fexpr({def_u, Fun, Ar}) ->
pp_beside([pp_fun_name(Fun), pp_text("/"), pp_text(Ar)]); pp_beside([pp_fun_name(Fun), pp_text("/"), pp_int(Ar)]);
pp_fexpr({def, Fun, Args}) -> pp_fexpr({def, Fun, Args}) ->
pp_call(pp_fun_name(Fun), Args); pp_call(pp_fun_name(Fun), Args);
pp_fexpr({con, _, I, []}) -> pp_fexpr({con, _, I, []}) ->
pp_beside(pp_text("C"), pp_text(I)); pp_beside(pp_text("C"), pp_int(I));
pp_fexpr({con, _, I, Es}) -> pp_fexpr({con, _, I, Es}) ->
pp_beside(pp_fexpr({con, [], I, []}), pp_beside(pp_fexpr({con, [], I, []}),
pp_fexpr({tuple, Es})); pp_fexpr({tuple, Es}));
pp_fexpr({tuple, Es}) -> pp_fexpr({tuple, Es}) ->
pp_parens(pp_par(pp_punctuate(pp_text(","), [pp_fexpr(E) || E <- Es]))); pp_parens(pp_par(pp_punctuate(pp_text(","), [pp_fexpr(E) || E <- Es])));
pp_fexpr({proj, E, I}) -> pp_fexpr({proj, E, I}) ->
pp_beside([pp_fexpr(E), pp_text("."), pp_text(I)]); pp_beside([pp_fexpr(E), pp_text("."), pp_int(I)]);
pp_fexpr({lam, Xs, A}) -> pp_fexpr({lam, Xs, A}) ->
pp_par([pp_fexpr({tuple, [{var, X} || X <- Xs]}), pp_text("=>"), pp_par([pp_fexpr({tuple, [{var, X} || X <- Xs]}), pp_text("=>"),
prettypr:nest(2, pp_fexpr(A))]); prettypr:nest(2, pp_fexpr(A))]);
@@ -1339,7 +1475,7 @@ pp_fexpr({closure, Fun, ClEnv}) ->
end, end,
pp_call(pp_text("__CLOSURE__"), [{def, Fun} | FVs]); pp_call(pp_text("__CLOSURE__"), [{def, Fun} | FVs]);
pp_fexpr({set_proj, E, I, A}) -> pp_fexpr({set_proj, E, I, A}) ->
pp_beside(pp_fexpr(E), pp_braces(pp_beside([pp_text(I), pp_text(" = "), pp_fexpr(A)]))); pp_beside(pp_fexpr(E), pp_braces(pp_beside([pp_int(I), pp_text(" = "), pp_fexpr(A)])));
pp_fexpr({op, Op, [A, B] = Args}) -> pp_fexpr({op, Op, [A, B] = Args}) ->
case is_infix(Op) of case is_infix(Op) of
false -> pp_call(pp_text(Op), Args); false -> pp_call(pp_text(Op), Args);
@@ -1359,10 +1495,10 @@ pp_fexpr({builtin_u, B, N}) ->
pp_beside([pp_text(B), pp_text("/"), pp_text(N)]); pp_beside([pp_text(B), pp_text("/"), pp_text(N)]);
pp_fexpr({builtin, B, As}) -> pp_fexpr({builtin, B, As}) ->
pp_call(pp_text(B), As); pp_call(pp_text(B), As);
pp_fexpr({remote_u, Ct, Fun, Ar}) -> pp_fexpr({remote_u, ArgsT, RetT, Ct, Fun}) ->
pp_beside([pp_fexpr(Ct), pp_text("."), pp_fun_name(Fun), pp_text("/"), pp_text(Ar)]); pp_beside([pp_fexpr(Ct), pp_text("."), pp_fun_name(Fun), pp_text(" : "), pp_ftype({function, ArgsT, RetT})]);
pp_fexpr({remote, Ct, Fun, As}) -> pp_fexpr({remote, ArgsT, RetT, Ct, Fun, As}) ->
pp_call(pp_beside([pp_fexpr(Ct), pp_text("."), pp_fun_name(Fun)]), As); pp_call(pp_parens(pp_beside([pp_fexpr(Ct), pp_text("."), pp_fun_name(Fun), pp_text(" : "), pp_ftype({function, ArgsT, RetT})])), As);
pp_fexpr({funcall, Fun, As}) -> pp_fexpr({funcall, Fun, As}) ->
pp_call(pp_fexpr(Fun), As); pp_call(pp_fexpr(Fun), As);
pp_fexpr({switch, Split}) -> pp_split(Split). pp_fexpr({switch, Split}) -> pp_split(Split).
+60 -21
View File
@@ -17,22 +17,27 @@
-spec convert_typed(aeso_syntax:ast(), list()) -> aeso_icode:icode(). -spec convert_typed(aeso_syntax:ast(), list()) -> aeso_icode:icode().
convert_typed(TypedTree, Options) -> convert_typed(TypedTree, Options) ->
Name = case lists:last(TypedTree) of {Payable, Name} =
{contract, _, {con, _, Con}, _} -> Con; case lists:last(TypedTree) of
_ -> gen_error(last_declaration_must_be_contract) {contract, Attrs, {con, _, Con}, _} ->
{proplists:get_value(payable, Attrs, false), Con};
_ ->
gen_error(last_declaration_must_be_contract)
end, end,
Icode = code(TypedTree, aeso_icode:set_name(Name, aeso_icode:new(Options))), NewIcode = aeso_icode:set_payable(Payable,
aeso_icode:set_name(Name, aeso_icode:new(Options))),
Icode = code(TypedTree, NewIcode, Options),
deadcode_elimination(Icode). deadcode_elimination(Icode).
code([{contract, _Attribs, Con, Code}|Rest], Icode) -> code([{contract, _Attribs, Con, Code}|Rest], Icode, Options) ->
NewIcode = contract_to_icode(Code, aeso_icode:set_namespace(Con, Icode)), NewIcode = contract_to_icode(Code, aeso_icode:set_namespace(Con, Icode)),
code(Rest, NewIcode); code(Rest, NewIcode, Options);
code([{namespace, _Ann, Name, Code}|Rest], Icode) -> code([{namespace, _Ann, Name, Code}|Rest], Icode, Options) ->
%% TODO: nested namespaces %% TODO: nested namespaces
NewIcode = contract_to_icode(Code, aeso_icode:set_namespace(Name, Icode)), NewIcode = contract_to_icode(Code, aeso_icode:set_namespace(Name, Icode)),
code(Rest, NewIcode); code(Rest, NewIcode, Options);
code([], Icode) -> code([], Icode, Options) ->
add_default_init_function(add_builtins(Icode)). add_default_init_function(add_builtins(Icode), Options).
%% Generate error on correct format. %% Generate error on correct format.
@@ -40,10 +45,12 @@ gen_error(Error) ->
error({code_errors, [Error]}). error({code_errors, [Error]}).
%% Create default init function (only if state is unit). %% Create default init function (only if state is unit).
add_default_init_function(Icode = #{functions := Funs, state_type := State}) -> add_default_init_function(Icode = #{functions := Funs, state_type := State}, Options) ->
NoCode = proplists:get_value(no_code, Options, false),
{_, _, QInit} = aeso_icode:qualify({id, [], "init"}, Icode), {_, _, QInit} = aeso_icode:qualify({id, [], "init"}, Icode),
case lists:keymember(QInit, 1, Funs) of case lists:keymember(QInit, 1, Funs) of
true -> Icode; true -> Icode;
false when NoCode -> Icode;
false when State /= {tuple, []} -> false when State /= {tuple, []} ->
gen_error(missing_init_function); gen_error(missing_init_function);
false -> false ->
@@ -84,6 +91,7 @@ contract_to_icode([{type_def, _Attrib, Id = {id, _, Name}, Args, Def} | Rest],
contract_to_icode(Rest, Icode2); contract_to_icode(Rest, Icode2);
contract_to_icode([{letfun, Attrib, Name, Args, _What, Body={typed,_,_,T}}|Rest], Icode) -> contract_to_icode([{letfun, Attrib, Name, Args, _What, Body={typed,_,_,T}}|Rest], Icode) ->
FunAttrs = [ stateful || proplists:get_value(stateful, Attrib, false) ] ++ FunAttrs = [ stateful || proplists:get_value(stateful, Attrib, false) ] ++
[ payable || proplists:get_value(payable, Attrib, false) ] ++
[ private || is_private(Attrib, Icode) ], [ private || is_private(Attrib, Icode) ],
%% TODO: Handle types %% TODO: Handle types
FunName = ast_id(Name), FunName = ast_id(Name),
@@ -266,15 +274,15 @@ ast_body(?qid_app(["AENS", "claim"], Args, _, _), Icode) ->
[word, string, word, sign_t()], {tuple, []}); [word, string, word, sign_t()], {tuple, []});
ast_body(?qid_app(["AENS", "transfer"], Args, _, _), Icode) -> ast_body(?qid_app(["AENS", "transfer"], Args, _, _), Icode) ->
{Sign, [FromAddr, ToAddr, NameHash]} = get_signature_arg(Args), {Sign, [FromAddr, ToAddr, Name]} = get_signature_arg(Args),
prim_call(?PRIM_CALL_AENS_TRANSFER, #integer{value = 0}, prim_call(?PRIM_CALL_AENS_TRANSFER, #integer{value = 0},
[ast_body(FromAddr, Icode), ast_body(ToAddr, Icode), ast_body(NameHash, Icode), ast_body(Sign, Icode)], [ast_body(FromAddr, Icode), ast_body(ToAddr, Icode), ast_body(Name, Icode), ast_body(Sign, Icode)],
[word, word, word, sign_t()], {tuple, []}); [word, word, word, sign_t()], {tuple, []});
ast_body(?qid_app(["AENS", "revoke"], Args, _, _), Icode) -> ast_body(?qid_app(["AENS", "revoke"], Args, _, _), Icode) ->
{Sign, [Addr, NameHash]} = get_signature_arg(Args), {Sign, [Addr, Name]} = get_signature_arg(Args),
prim_call(?PRIM_CALL_AENS_REVOKE, #integer{value = 0}, prim_call(?PRIM_CALL_AENS_REVOKE, #integer{value = 0},
[ast_body(Addr, Icode), ast_body(NameHash, Icode), ast_body(Sign, Icode)], [ast_body(Addr, Icode), ast_body(Name, Icode), ast_body(Sign, Icode)],
[word, word, sign_t()], {tuple, []}); [word, word, sign_t()], {tuple, []});
ast_body({qid, _, ["AENS", "resolve"]}, _Icode) -> gen_error({underapplied_primitive, 'AENS.resolve'}); ast_body({qid, _, ["AENS", "resolve"]}, _Icode) -> gen_error({underapplied_primitive, 'AENS.resolve'});
@@ -347,16 +355,26 @@ 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 %% Crypto
ast_body(?qid_app(["Crypto", "ecverify"], [Msg, PK, Sig], _, _), Icode) -> ast_body(?qid_app(["Crypto", "verify_sig"], [Msg, PK, Sig], _, _), Icode) ->
prim_call(?PRIM_CALL_CRYPTO_ECVERIFY, #integer{value = 0}, prim_call(?PRIM_CALL_CRYPTO_VERIFY_SIG, #integer{value = 0},
[ast_body(Msg, Icode), ast_body(PK, Icode), ast_body(Sig, Icode)], [ast_body(Msg, Icode), ast_body(PK, Icode), ast_body(Sig, Icode)],
[word, word, sign_t()], word); [word, word, sign_t()], word);
ast_body(?qid_app(["Crypto", "ecverify_secp256k1"], [Msg, PK, Sig], _, _), Icode) -> ast_body(?qid_app(["Crypto", "verify_sig_secp256k1"], [Msg, PK, Sig], _, _), Icode) ->
prim_call(?PRIM_CALL_CRYPTO_ECVERIFY_SECP256K1, #integer{value = 0}, prim_call(?PRIM_CALL_CRYPTO_VERIFY_SIG_SECP256K1, #integer{value = 0},
[ast_body(Msg, Icode), ast_body(PK, Icode), ast_body(Sig, Icode)], [ast_body(Msg, Icode), ast_body(PK, Icode), ast_body(Sig, Icode)],
[bytes_t(32), bytes_t(64), bytes_t(64)], word); [bytes_t(32), bytes_t(64), bytes_t(64)], word);
ast_body(?qid_app(["Crypto", "ecverify_secp256k1"], [Msg, Addr, Sig], _, _), Icode) ->
prim_call(?PRIM_CALL_CRYPTO_ECVERIFY_SECP256K1, #integer{value = 0},
[ast_body(Msg, Icode), ast_body(Addr, Icode), ast_body(Sig, Icode)],
[word, bytes_t(20), bytes_t(65)], word);
ast_body(?qid_app(["Crypto", "ecrecover_secp256k1"], [Msg, Sig], _, _), Icode) ->
prim_call(?PRIM_CALL_CRYPTO_ECRECOVER_SECP256K1, #integer{value = 0},
[ast_body(Msg, Icode), ast_body(Sig, Icode)],
[word, bytes_t(65)], aeso_icode:option_typerep(bytes_t(20)));
ast_body(?qid_app(["Crypto", "sha3"], [Term], [Type], _), Icode) -> ast_body(?qid_app(["Crypto", "sha3"], [Term], [Type], _), Icode) ->
generic_hash_primop(?PRIM_CALL_CRYPTO_SHA3, Term, Type, Icode); generic_hash_primop(?PRIM_CALL_CRYPTO_SHA3, Term, Type, Icode);
ast_body(?qid_app(["Crypto", "sha256"], [Term], [Type], _), Icode) -> ast_body(?qid_app(["Crypto", "sha256"], [Term], [Type], _), Icode) ->
@@ -420,6 +438,9 @@ ast_body(?qid_app(["Address", "is_oracle"], [Addr], _, _), Icode) ->
ast_body(?qid_app(["Address", "is_contract"], [Addr], _, _), Icode) -> ast_body(?qid_app(["Address", "is_contract"], [Addr], _, _), Icode) ->
prim_call(?PRIM_CALL_ADDR_IS_CONTRACT, #integer{value = 0}, prim_call(?PRIM_CALL_ADDR_IS_CONTRACT, #integer{value = 0},
[ast_body(Addr, Icode)], [word], word); [ast_body(Addr, Icode)], [word], word);
ast_body(?qid_app(["Address", "is_payable"], [Addr], _, _), Icode) ->
prim_call(?PRIM_CALL_ADDR_IS_PAYABLE, #integer{value = 0},
[ast_body(Addr, Icode)], [word], word);
ast_body(?qid_app(["Bytes", "to_int"], [Bytes], _, _), Icode) -> ast_body(?qid_app(["Bytes", "to_int"], [Bytes], _, _), Icode) ->
{typed, _, _, {bytes_t, _, N}} = Bytes, {typed, _, _, {bytes_t, _, N}} = Bytes,
@@ -516,6 +537,24 @@ ast_body({app,As,Fun,Args}, Icode) ->
#funcall{function=ast_body(Fun, Icode), #funcall{function=ast_body(Fun, Icode),
args=[ast_body(A, Icode) || A <- Args]} args=[ast_body(A, Icode) || A <- Args]}
end; end;
ast_body({list_comp, _, Yield, []}, Icode) ->
#list{elems = [ast_body(Yield, Icode)]};
ast_body({list_comp, As, Yield, [{comprehension_bind, {typed, Arg, ArgType}, BindExpr}|Rest]}, Icode) ->
#funcall
{ function = #var_ref{ name = ["List", "flat_map"] }
, args =
[ #lambda{ args=[#arg{name = ast_id(Arg), type = ast_type(ArgType, Icode)}]
, body = ast_body({list_comp, As, Yield, Rest}, Icode)
}
, ast_body(BindExpr, Icode)
]
};
ast_body({list_comp, As, Yield, [{comprehension_if, AsIF, Cond}|Rest]}, Icode) ->
ast_body({'if', AsIF, Cond, {list_comp, As, Yield, Rest}, {list, As, []}}, Icode);
ast_body({list_comp, As, Yield, [LV = {letval, _, _, _, _}|Rest]}, Icode) ->
ast_body({block, As, [LV, {list_comp, As, Yield, Rest}]}, Icode);
ast_body({list_comp, As, Yield, [LF = {letfun, _, _, _, _, _}|Rest]}, Icode) ->
ast_body({block, As, [LF, {list_comp, As, Yield, Rest}]}, Icode);
ast_body({'if',_,Dec,Then,Else}, Icode) -> ast_body({'if',_,Dec,Then,Else}, Icode) ->
#ifte{decision = ast_body(Dec, Icode) #ifte{decision = ast_body(Dec, Icode)
,then = ast_body(Then, Icode) ,then = ast_body(Then, Icode)
@@ -728,7 +767,7 @@ ast_typerep({con, _, _}, _) ->
word; %% Contract type word; %% Contract type
ast_typerep({bytes_t, _, Len}, _) -> ast_typerep({bytes_t, _, Len}, _) ->
bytes_t(Len); bytes_t(Len);
ast_typerep({app_t, _, {id, _, Name}, Args}, Icode) -> ast_typerep({app_t, _, {I, _, Name}, Args}, Icode) when I =:= id; I =:= qid ->
ArgReps = [ ast_typerep(Arg, Icode) || Arg <- Args ], ArgReps = [ ast_typerep(Arg, Icode) || Arg <- Args ],
lookup_type_id(Name, ArgReps, Icode); lookup_type_id(Name, ArgReps, Icode);
ast_typerep({tvar,_,A}, #{ type_vars := TypeVars }) -> ast_typerep({tvar,_,A}, #{ type_vars := TypeVars }) ->
+53 -15
View File
@@ -35,6 +35,8 @@
| pp_icode | pp_icode
| pp_assembler | pp_assembler
| pp_bytecode | pp_bytecode
| no_code
| no_implicit_stdlib
| {backend, aevm | fate} | {backend, aevm | fate}
| {include, {file_system, [string()]} | | {include, {file_system, [string()]} |
{explicit_files, #{string() => binary()}}} {explicit_files, #{string() => binary()}}}
@@ -120,7 +122,9 @@ from_string1(aevm, ContractString, Options) ->
{ok, #{byte_code => ByteCode, {ok, #{byte_code => ByteCode,
compiler_version => Version, compiler_version => Version,
contract_source => ContractString, contract_source => ContractString,
type_info => TypeInfo type_info => TypeInfo,
abi_version => aeb_aevm_abi:abi_version(),
payable => maps:get(payable, Icode)
}}; }};
from_string1(fate, ContractString, Options) -> from_string1(fate, ContractString, Options) ->
#{fcode := FCode} = string_to_code(ContractString, Options), #{fcode := FCode} = string_to_code(ContractString, Options),
@@ -131,12 +135,23 @@ from_string1(fate, ContractString, Options) ->
compiler_version => Version, compiler_version => Version,
contract_source => ContractString, contract_source => ContractString,
type_info => [], type_info => [],
fate_code => FateCode fate_code => FateCode,
abi_version => aeb_fate_abi:abi_version(),
payable => maps:get(payable, FCode)
}}. }}.
-spec string_to_code(string(), [option()]) -> map(). -spec string_to_code(string(), options()) -> map().
string_to_code(ContractString, Options) -> string_to_code(ContractString, Options) ->
Ast = parse(ContractString, Options), Ast = case lists:member(no_implicit_stdlib, Options) of
true -> parse(ContractString, Options);
false ->
IncludedSTD = sets:from_list(
[aeso_parser:hash_include(F, C)
|| {F, C} <- aeso_stdlib:stdlib_list()]),
InitAst = parse(ContractString, IncludedSTD, Options),
STD = parse_stdlib(),
STD ++ InitAst
end,
pp_sophia_code(Ast, Options), pp_sophia_code(Ast, Options),
pp_ast(Ast, Options), pp_ast(Ast, Options),
{TypeEnv, TypedAst} = aeso_ast_infer_types:infer(Ast, [return_env]), {TypeEnv, TypedAst} = aeso_ast_infer_types:infer(Ast, [return_env]),
@@ -290,12 +305,19 @@ to_sophia_value(ContractString, Fun, ResType, Data) ->
{ok, aeso_syntax:expr()} | {error, term()}. {ok, aeso_syntax:expr()} | {error, term()}.
to_sophia_value(_, _, error, Err, _Options) -> to_sophia_value(_, _, error, Err, _Options) ->
{ok, {app, [], {id, [], "error"}, [{string, [], Err}]}}; {ok, {app, [], {id, [], "error"}, [{string, [], Err}]}};
to_sophia_value(_, _, revert, Data, _Options) -> to_sophia_value(_, _, revert, Data, Options) ->
case aeb_heap:from_binary(string, Data) of case proplists:get_value(backend, Options, aevm) of
{ok, Err} -> {ok, {app, [], {id, [], "abort"}, [{string, [], Err}]}}; aevm ->
{error, _} = Err -> Err case aeb_heap:from_binary(string, Data) of
{ok, Err} -> {ok, {app, [], {id, [], "abort"}, [{string, [], Err}]}};
{error, _} = Err -> Err
end;
fate ->
Err = aeb_fate_encoding:deserialize(Data),
{ok, {app, [], {id, [], "abort"}, [{string, [], Err}]}}
end; end;
to_sophia_value(ContractString, FunName, ok, Data, Options) -> to_sophia_value(ContractString, FunName, ok, Data, Options0) ->
Options = [no_implicit_stdlib, no_code | Options0],
try try
Code = string_to_code(ContractString, Options), Code = string_to_code(ContractString, Options),
#{ typed_ast := TypedAst, type_env := TypeEnv} = Code, #{ typed_ast := TypedAst, type_env := TypeEnv} = Code,
@@ -355,7 +377,8 @@ create_calldata(Code, Fun, Args) ->
-spec create_calldata(string(), string(), [string()], [{atom(), any()}]) -> -spec create_calldata(string(), string(), [string()], [{atom(), any()}]) ->
{ok, binary()} {ok, binary()}
| {error, term()}. | {error, term()}.
create_calldata(Code, Fun, Args, Options) -> create_calldata(Code, Fun, Args, Options0) ->
Options = [no_implicit_stdlib, no_code | Options0],
case proplists:get_value(backend, Options, aevm) of case proplists:get_value(backend, Options, aevm) of
aevm -> aevm ->
case check_call(Code, Fun, Args, Options) of case check_call(Code, Fun, Args, Options) of
@@ -377,7 +400,8 @@ create_calldata(Code, Fun, Args, Options) ->
decode_calldata(ContractString, FunName, Calldata) -> decode_calldata(ContractString, FunName, Calldata) ->
decode_calldata(ContractString, FunName, Calldata, [{backend, aevm}]). decode_calldata(ContractString, FunName, Calldata, [{backend, aevm}]).
decode_calldata(ContractString, FunName, Calldata, Options) -> decode_calldata(ContractString, FunName, Calldata, Options0) ->
Options = [no_implicit_stdlib, no_code | Options0],
try try
Code = string_to_code(ContractString, Options), Code = string_to_code(ContractString, Options),
#{ typed_ast := TypedAst, type_env := TypeEnv} = Code, #{ typed_ast := TypedAst, type_env := TypeEnv} = Code,
@@ -527,8 +551,9 @@ to_bytecode([], _) -> [].
extract_type_info(#{functions := Functions} =_Icode) -> extract_type_info(#{functions := Functions} =_Icode) ->
ArgTypesOnly = fun(As) -> [ T || {_, T} <- As ] end, ArgTypesOnly = fun(As) -> [ T || {_, T} <- As ] end,
Payable = fun(Attrs) -> proplists:get_value(payable, Attrs, false) end,
TypeInfo = [aeb_aevm_abi:function_type_info(list_to_binary(lists:last(Name)), TypeInfo = [aeb_aevm_abi:function_type_info(list_to_binary(lists:last(Name)),
ArgTypesOnly(Args), TypeRep) Payable(Attrs), ArgTypesOnly(Args), TypeRep)
|| {Name, Attrs, Args,_Body, TypeRep} <- Functions, || {Name, Attrs, Args,_Body, TypeRep} <- Functions,
not is_tuple(Name), not is_tuple(Name),
not lists:member(private, Attrs) not lists:member(private, Attrs)
@@ -552,9 +577,16 @@ pp(Code, Options, Option, PPFun) ->
ok ok
end. end.
%% ------------------------------------------------------------------- %% -------------------------------------------------------------------
%% TODO: Tempoary parser hook below...
-spec parse_stdlib() -> none() | aeso_syntax:ast().
parse_stdlib() ->
lists:foldr(
fun ({Lib, LibCode}, Acc) ->
parse(LibCode, [{src_file, binary_to_list(Lib)}]) ++ Acc
end,
[],
aeso_stdlib:stdlib_list()).
sophia_type_to_typerep(String) -> sophia_type_to_typerep(String) ->
{ok, Ast} = aeso_parser:type(String), {ok, Ast} = aeso_parser:type(String),
@@ -563,9 +595,14 @@ sophia_type_to_typerep(String) ->
catch _:_ -> {error, bad_type} catch _:_ -> {error, bad_type}
end. end.
-spec parse(string(), aeso_compiler:options()) -> none() | aeso_syntax:ast().
parse(Text, Options) -> parse(Text, Options) ->
parse(Text, sets:new(), Options).
-spec parse(string(), sets:set(), aeso_compiler:options()) -> none() | aeso_syntax:ast().
parse(Text, Included, Options) ->
%% Try and return something sensible here! %% Try and return something sensible here!
case aeso_parser:string(Text, Options) of case aeso_parser:string(Text, Included, Options) of
%% Yay, it worked! %% Yay, it worked!
{ok, Contract} -> Contract; {ok, Contract} -> Contract;
%% Scan errors. %% Scan errors.
@@ -584,6 +621,7 @@ parse(Text, Options) ->
parse_error(Pos, io_lib:format("could not find include file '~s'", [File])) parse_error(Pos, io_lib:format("could not find include file '~s'", [File]))
end. end.
-spec parse_error(aeso_parse_lib:pos(), string()) -> none().
parse_error(Pos, ErrorString) -> parse_error(Pos, ErrorString) ->
Error = io_lib:format("~s: ~s", [pos_error(Pos), ErrorString]), Error = io_lib:format("~s: ~s", [pos_error(Pos), ErrorString]),
error({parse_errors, [Error]}). error({parse_errors, [Error]}).
+65 -75
View File
@@ -98,17 +98,20 @@
Op =:= 'SHA3' orelse Op =:= 'SHA3' orelse
Op =:= 'SHA256' orelse Op =:= 'SHA256' orelse
Op =:= 'BLAKE2B' orelse Op =:= 'BLAKE2B' orelse
Op =:= 'ECVERIFY' orelse Op =:= 'VERIFY_SIG' orelse
Op =:= 'ECVERIFY_SECP256K1' orelse Op =:= 'VERIFY_SIG_SECP256K1' orelse
Op =:= 'CONTRACT_TO_ADDRESS' orelse Op =:= 'ECVERIFY_SECP256K1' orelse
Op =:= 'AUTH_TX_HASH' orelse Op =:= 'ECRECOVER_SECP256K1' orelse
Op =:= 'BYTES_TO_INT' orelse Op =:= 'CONTRACT_TO_ADDRESS' orelse
Op =:= 'BYTES_TO_STR' orelse Op =:= 'AUTH_TX_HASH' orelse
Op =:= 'ORACLE_CHECK' orelse Op =:= 'BYTES_TO_INT' orelse
Op =:= 'ORACLE_CHECK_QUERY' orelse Op =:= 'BYTES_TO_STR' orelse
Op =:= 'IS_ORACLE' orelse Op =:= 'ORACLE_CHECK' orelse
Op =:= 'IS_CONTRACT' orelse Op =:= 'ORACLE_CHECK_QUERY' orelse
Op =:= 'CREATOR' orelse Op =:= 'IS_ORACLE' orelse
Op =:= 'IS_CONTRACT' orelse
Op =:= 'IS_PAYABLE' orelse
Op =:= 'CREATOR' orelse
false)). false)).
-record(env, { contract, vars = [], locals = [], tailpos = true }). -record(env, { contract, vars = [], locals = [], tailpos = true }).
@@ -127,19 +130,17 @@ debug(Tag, Options, Fmt, Args) ->
%% @doc Main entry point. %% @doc Main entry point.
compile(FCode, Options) -> compile(FCode, Options) ->
#{ contract_name := ContractName, #{ contract_name := ContractName,
state_type := StateType,
functions := Functions } = FCode, functions := Functions } = FCode,
SFuns = functions_to_scode(ContractName, Functions, Options), SFuns = functions_to_scode(ContractName, Functions, Options),
SFuns1 = optimize_scode(SFuns, Options), SFuns1 = optimize_scode(SFuns, Options),
SFuns2 = add_default_init_function(SFuns1, StateType), FateCode = to_basic_blocks(SFuns1),
FateCode = to_basic_blocks(SFuns2),
debug(compile, Options, "~s\n", [aeb_fate_asm:pp(FateCode)]), debug(compile, Options, "~s\n", [aeb_fate_asm:pp(FateCode)]),
FateCode. FateCode.
make_function_id(X) -> make_function_id(X) ->
aeb_fate_code:symbol_identifier(make_function_name(X)). aeb_fate_code:symbol_identifier(make_function_name(X)).
make_function_name(init) -> <<"init">>; make_function_name(init) -> <<"INIT">>;
make_function_name(event) -> <<"Chain.event">>; make_function_name(event) -> <<"Chain.event">>;
make_function_name({entrypoint, Name}) -> Name; make_function_name({entrypoint, Name}) -> Name;
make_function_name({local_fun, Xs}) -> list_to_binary("." ++ string:join(Xs, ".")). make_function_name({local_fun, Xs}) -> list_to_binary("." ++ string:join(Xs, ".")).
@@ -147,15 +148,17 @@ make_function_name({local_fun, Xs}) -> list_to_binary("." ++ string:join(Xs,
functions_to_scode(ContractName, Functions, Options) -> functions_to_scode(ContractName, Functions, Options) ->
FunNames = maps:keys(Functions), FunNames = maps:keys(Functions),
maps:from_list( maps:from_list(
[ {make_function_name(Name), function_to_scode(ContractName, FunNames, Name, Args, Body, Type, Options)} [ {make_function_name(Name), function_to_scode(ContractName, FunNames, Name, Attrs, Args, Body, Type, Options)}
|| {Name, #{args := Args, || {Name, #{args := Args,
body := Body, body := Body,
attrs := Attrs,
return := Type}} <- maps:to_list(Functions)]). return := Type}} <- maps:to_list(Functions)]).
function_to_scode(ContractName, Functions, _Name, Args, Body, ResType, _Options) -> function_to_scode(ContractName, Functions, _Name, Attrs0, Args, Body, ResType, _Options) ->
{ArgTypes, ResType1} = typesig_to_scode(Args, ResType), {ArgTypes, ResType1} = typesig_to_scode(Args, ResType),
SCode = to_scode(init_env(ContractName, Functions, Args), Body), Attrs = Attrs0 -- [stateful], %% Only track private and payable from here.
{{ArgTypes, ResType1}, SCode}. SCode = to_scode(init_env(ContractName, Functions, Args), Body),
{Attrs, {ArgTypes, ResType1}, SCode}.
-define(tvars, '$tvars'). -define(tvars, '$tvars').
@@ -191,21 +194,6 @@ type_to_scode({tvar, X}) ->
J -> {tvar, J} J -> {tvar, J}
end. end.
add_default_init_function(SFuns, StateType) when StateType /= {tuple, []} ->
%% Only add default if the type is unit.
SFuns;
add_default_init_function(SFuns, {tuple, []}) ->
%% Only add default if the init function is not present
InitName = make_function_name(init),
case maps:find(InitName, SFuns) of
{ok, _} ->
SFuns;
error ->
Sig = {[], {tuple, []}},
Body = [tuple(0)],
SFuns#{ InitName => {Sig, Body} }
end.
%% -- Phase I ---------------------------------------------------------------- %% -- Phase I ----------------------------------------------------------------
%% Icode to structured assembly %% Icode to structured assembly
@@ -327,21 +315,19 @@ to_scode(Env, {funcall, Fun, Args}) ->
to_scode(Env, {builtin, B, Args}) -> to_scode(Env, {builtin, B, Args}) ->
builtin_to_scode(Env, B, Args); builtin_to_scode(Env, B, Args);
to_scode(Env, {remote, Ct, Fun, [{builtin, call_gas_left, _}, Value | Args]}) -> to_scode(Env, {remote, ArgsT, RetT, Ct, Fun, [Gas, Value | Args]}) ->
%% Gas is not limited.
Lbl = make_function_id(Fun), Lbl = make_function_id(Fun),
Call = if Env#env.tailpos -> aeb_fate_ops:call_tr(?a, Lbl, ?a); {ArgTypes, RetType0} = typesig_to_scode([{"_", T} || T <- ArgsT], RetT),
true -> aeb_fate_ops:call_r(?a, Lbl, ?a) ArgType = ?i(aeb_fate_data:make_typerep({tuple, ArgTypes})),
end, RetType = ?i(aeb_fate_data:make_typerep(RetType0)),
call_to_scode(Env, Call, [Ct, Value | Args]); case Gas of
{builtin, call_gas_left, _} ->
to_scode(Env, {remote, Ct, Fun, [Gas, Value | Args]}) -> Call = aeb_fate_ops:call_r(?a, Lbl, ArgType, RetType, ?a),
%% Gas is limited. call_to_scode(Env, Call, [Ct, Value | Args]);
Lbl = make_function_id(Fun), _ ->
Call = if Env#env.tailpos -> aeb_fate_ops:call_gtr(?a, Lbl, ?a, ?a); Call = aeb_fate_ops:call_gr(?a, Lbl, ArgType, RetType, ?a, ?a),
true -> aeb_fate_ops:call_gr(?a, Lbl, ?a, ?a) call_to_scode(Env, Call, [Ct, Value, Gas | Args])
end, end;
call_to_scode(Env, Call, [Ct, Value, Gas | Args]);
to_scode(Env, {closure, Fun, FVs}) -> to_scode(Env, {closure, Fun, FVs}) ->
to_scode(Env, {tuple, [{lit, {string, make_function_id(Fun)}}, FVs]}); to_scode(Env, {tuple, [{lit, {string, make_function_id(Fun)}}, FVs]});
@@ -542,6 +528,8 @@ builtin_to_scode(Env, address_is_oracle, [_] = Args) ->
call_to_scode(Env, aeb_fate_ops:is_oracle(?a, ?a), Args); call_to_scode(Env, aeb_fate_ops:is_oracle(?a, ?a), Args);
builtin_to_scode(Env, address_is_contract, [_] = Args) -> builtin_to_scode(Env, address_is_contract, [_] = Args) ->
call_to_scode(Env, aeb_fate_ops:is_contract(?a, ?a), Args); call_to_scode(Env, aeb_fate_ops:is_contract(?a, ?a), Args);
builtin_to_scode(Env, address_is_payable, [_] = Args) ->
call_to_scode(Env, aeb_fate_ops:is_payable(?a, ?a), Args);
builtin_to_scode(Env, aens_resolve, [_Name, _Key, _Type] = Args) -> builtin_to_scode(Env, aens_resolve, [_Name, _Key, _Type] = Args) ->
call_to_scode(Env, aeb_fate_ops:aens_resolve(?a, ?a, ?a, ?a), Args); call_to_scode(Env, aeb_fate_ops:aens_resolve(?a, ?a, ?a, ?a), Args);
builtin_to_scode(Env, aens_preclaim, [_Sign, _Account, _Hash] = Args) -> builtin_to_scode(Env, aens_preclaim, [_Sign, _Account, _Hash] = Args) ->
@@ -595,15 +583,17 @@ op_to_scode(bits_union) -> aeb_fate_ops:bits_or(?a, ?a, ?a);
op_to_scode(bits_difference) -> aeb_fate_ops:bits_diff(?a, ?a, ?a); op_to_scode(bits_difference) -> aeb_fate_ops:bits_diff(?a, ?a, ?a);
op_to_scode(address_to_str) -> aeb_fate_ops:addr_to_str(?a, ?a); op_to_scode(address_to_str) -> aeb_fate_ops:addr_to_str(?a, ?a);
op_to_scode(int_to_str) -> aeb_fate_ops:int_to_str(?a, ?a); op_to_scode(int_to_str) -> aeb_fate_ops:int_to_str(?a, ?a);
op_to_scode(contract_to_address) -> aeb_fate_ops:contract_to_address(?a, ?a); op_to_scode(contract_to_address) -> aeb_fate_ops:contract_to_address(?a, ?a);
op_to_scode(crypto_ecverify) -> aeb_fate_ops:ecverify(?a, ?a, ?a, ?a); op_to_scode(crypto_verify_sig) -> aeb_fate_ops:verify_sig(?a, ?a, ?a, ?a);
op_to_scode(crypto_ecverify_secp256k1) -> aeb_fate_ops:ecverify_secp256k1(?a, ?a, ?a, ?a); op_to_scode(crypto_verify_sig_secp256k1) -> aeb_fate_ops:verify_sig_secp256k1(?a, ?a, ?a, ?a);
op_to_scode(crypto_sha3) -> aeb_fate_ops:sha3(?a, ?a); op_to_scode(crypto_ecverify_secp256k1) -> aeb_fate_ops:ecverify_secp256k1(?a, ?a, ?a, ?a);
op_to_scode(crypto_sha256) -> aeb_fate_ops:sha256(?a, ?a); op_to_scode(crypto_ecrecover_secp256k1) -> aeb_fate_ops:ecrecover_secp256k1(?a, ?a, ?a);
op_to_scode(crypto_blake2b) -> aeb_fate_ops:blake2b(?a, ?a); op_to_scode(crypto_sha3) -> aeb_fate_ops:sha3(?a, ?a);
op_to_scode(string_sha3) -> aeb_fate_ops:sha3(?a, ?a); op_to_scode(crypto_sha256) -> aeb_fate_ops:sha256(?a, ?a);
op_to_scode(string_sha256) -> aeb_fate_ops:sha256(?a, ?a); op_to_scode(crypto_blake2b) -> aeb_fate_ops:blake2b(?a, ?a);
op_to_scode(string_blake2b) -> aeb_fate_ops:blake2b(?a, ?a). op_to_scode(string_sha3) -> aeb_fate_ops:sha3(?a, ?a);
op_to_scode(string_sha256) -> aeb_fate_ops:sha256(?a, ?a);
op_to_scode(string_blake2b) -> aeb_fate_ops:blake2b(?a, ?a).
%% PUSH and STORE ?a are the same, so we use STORE to make optimizations %% PUSH and STORE ?a are the same, so we use STORE to make optimizations
%% easier, and specialize to PUSH (which is cheaper) at the end. %% easier, and specialize to PUSH (which is cheaper) at the end.
@@ -628,12 +618,12 @@ flatten_s(I) -> I.
-define(MAX_SIMPL_ITERATIONS, 10). -define(MAX_SIMPL_ITERATIONS, 10).
optimize_fun(_Funs, Name, {{Args, Res}, Code}, Options) -> optimize_fun(_Funs, Name, {Attrs, Sig, Code}, Options) ->
Code0 = flatten(Code), Code0 = flatten(Code),
debug(opt, Options, "Optimizing ~s\n", [Name]), debug(opt, Options, "Optimizing ~s\n", [Name]),
Code1 = simpl_loop(0, Code0, Options), Code1 = simpl_loop(0, Code0, Options),
Code2 = desugar(Code1), Code2 = desugar(Code1),
{{Args, Res}, Code2}. {Attrs, Sig, Code2}.
simpl_loop(N, Code, Options) when N >= ?MAX_SIMPL_ITERATIONS -> simpl_loop(N, Code, Options) when N >= ?MAX_SIMPL_ITERATIONS ->
debug(opt, Options, " No simpl_loop fixed_point after ~p iterations.\n\n", [N]), debug(opt, Options, " No simpl_loop fixed_point after ~p iterations.\n\n", [N]),
@@ -747,11 +737,9 @@ attributes(I) ->
'RETURN' -> Impure(pc, []); 'RETURN' -> Impure(pc, []);
{'RETURNR', A} -> Impure(pc, A); {'RETURNR', A} -> Impure(pc, A);
{'CALL', _} -> Impure(?a, []); {'CALL', _} -> Impure(?a, []);
{'CALL_R', A, _, B} -> Impure(?a, [A, B]); {'CALL_R', A, _, B, C, D} -> Impure(?a, [A, B, C, D]);
{'CALL_GR', A, _, B, C} -> Impure(?a, [A, B, C]); {'CALL_GR', A, _, B, C, D, E} -> Impure(?a, [A, B, C, D, E]);
{'CALL_T', _} -> Impure(pc, []); {'CALL_T', _} -> Impure(pc, []);
{'CALL_TR', A, _, B} -> Impure(pc, [A, B]);
{'CALL_GTR', A, _, B, C} -> Impure(pc, [A, B, C]);
{'CALL_VALUE', A} -> Pure(A, []); {'CALL_VALUE', A} -> Pure(A, []);
{'JUMP', _} -> Impure(pc, []); {'JUMP', _} -> Impure(pc, []);
{'JUMPIF', A, _} -> Impure(pc, A); {'JUMPIF', A, _} -> Impure(pc, A);
@@ -825,8 +813,10 @@ attributes(I) ->
{'SHA3', A, B} -> Pure(A, [B]); {'SHA3', A, B} -> Pure(A, [B]);
{'SHA256', A, B} -> Pure(A, [B]); {'SHA256', A, B} -> Pure(A, [B]);
{'BLAKE2B', A, B} -> Pure(A, [B]); {'BLAKE2B', A, B} -> Pure(A, [B]);
{'ECVERIFY', A, B, C, D} -> Pure(A, [B, C, D]); {'VERIFY_SIG', A, B, C, D} -> Pure(A, [B, C, D]);
{'VERIFY_SIG_SECP256K1', A, B, C, D} -> Pure(A, [B, C, D]);
{'ECVERIFY_SECP256K1', A, B, C, D} -> Pure(A, [B, C, D]); {'ECVERIFY_SECP256K1', A, B, C, D} -> Pure(A, [B, C, D]);
{'ECRECOVER_SECP256K1', A, B, C} -> Pure(A, [B, C]);
{'CONTRACT_TO_ADDRESS', A, B} -> Pure(A, [B]); {'CONTRACT_TO_ADDRESS', A, B} -> Pure(A, [B]);
{'AUTH_TX_HASH', A} -> Pure(A, []); {'AUTH_TX_HASH', A} -> Pure(A, []);
{'BYTES_TO_INT', A, B} -> Pure(A, [B]); {'BYTES_TO_INT', A, B} -> Pure(A, [B]);
@@ -835,6 +825,7 @@ attributes(I) ->
{'ORACLE_CHECK_QUERY', A, B, C, D, E} -> Impure(A, [B, C, D, E]); {'ORACLE_CHECK_QUERY', A, B, C, D, E} -> Impure(A, [B, C, D, E]);
{'IS_ORACLE', A, B} -> Impure(A, [B]); {'IS_ORACLE', A, B} -> Impure(A, [B]);
{'IS_CONTRACT', A, B} -> Impure(A, [B]); {'IS_CONTRACT', A, B} -> Impure(A, [B]);
{'IS_PAYABLE', A, B} -> Impure(A, [B]);
{'CREATOR', A} -> Pure(A, []); {'CREATOR', A} -> Pure(A, []);
{'ADDRESS', A} -> Pure(A, []); {'ADDRESS', A} -> Pure(A, []);
{'BALANCE', A} -> Impure(A, []); {'BALANCE', A} -> Impure(A, []);
@@ -1283,9 +1274,9 @@ desugar(I) -> [I].
to_basic_blocks(Funs) -> to_basic_blocks(Funs) ->
to_basic_blocks(maps:to_list(Funs), aeb_fate_code:new()). to_basic_blocks(maps:to_list(Funs), aeb_fate_code:new()).
to_basic_blocks([{Name, {Sig, Code}}|Left], Acc) -> to_basic_blocks([{Name, {Attrs, Sig, Code}}|Left], Acc) ->
BB = bb(Name, Code ++ [aeb_fate_ops:return()]), BB = bb(Name, Code ++ [aeb_fate_ops:return()]),
to_basic_blocks(Left, aeb_fate_code:insert_fun(Name, Sig, BB, Acc)); to_basic_blocks(Left, aeb_fate_code:insert_fun(Name, Attrs, Sig, BB, Acc));
to_basic_blocks([], Acc) -> to_basic_blocks([], Acc) ->
Acc. Acc.
@@ -1340,7 +1331,7 @@ block(Blk = #blk{code = [{switch, Arg, Type, Alts, Default} | Code],
{DefRef, DefBlk} = {DefRef, DefBlk} =
case Default of case Default of
missing when Catchall == none -> missing when Catchall == none ->
FreshBlk([aeb_fate_ops:abort(?i(<<"Incomplete patterns">>))], none); FreshBlk([aeb_fate_ops:exit(?i(<<"Incomplete patterns">>))], none);
missing -> {Catchall, []}; missing -> {Catchall, []};
_ -> FreshBlk(Default ++ [{jump, RestRef}], Catchall) _ -> FreshBlk(Default ++ [{jump, RestRef}], Catchall)
%% ^ fall-through to the outer catchall %% ^ fall-through to the outer catchall
@@ -1413,8 +1404,7 @@ reorder_blocks(Ref, Code, Blocks, Acc) ->
['RETURN'|_] -> reorder_blocks(Blocks, Acc1); ['RETURN'|_] -> reorder_blocks(Blocks, Acc1);
[{'RETURNR', _}|_] -> reorder_blocks(Blocks, Acc1); [{'RETURNR', _}|_] -> reorder_blocks(Blocks, Acc1);
[{'CALL_T', _}|_] -> reorder_blocks(Blocks, Acc1); [{'CALL_T', _}|_] -> reorder_blocks(Blocks, Acc1);
[{'CALL_TR', _, _, _}|_] -> reorder_blocks(Blocks, Acc1); [{'EXIT', _}|_] -> reorder_blocks(Blocks, Acc1);
[{'CALL_GTR', _, _, _}|_] -> reorder_blocks(Blocks, Acc1);
[{'ABORT', _}|_] -> reorder_blocks(Blocks, Acc1); [{'ABORT', _}|_] -> reorder_blocks(Blocks, Acc1);
[{switch, _, _}|_] -> reorder_blocks(Blocks, Acc1); [{switch, _, _}|_] -> reorder_blocks(Blocks, Acc1);
[{jump, L}|_] -> [{jump, L}|_] ->
@@ -1453,12 +1443,10 @@ chase_labels([L | Ls], Map, Live) ->
chase_labels(New ++ Ls, Map, Live#{ L => true }). chase_labels(New ++ Ls, Map, Live#{ L => true }).
%% Replace PUSH, RETURN by RETURNR, drop returns after tail calls. %% Replace PUSH, RETURN by RETURNR, drop returns after tail calls.
tweak_returns(['RETURN', {'PUSH', A} | Code]) -> [{'RETURNR', A} | Code]; tweak_returns(['RETURN', {'PUSH', A} | Code]) -> [{'RETURNR', A} | Code];
tweak_returns(['RETURN' | Code = [{'CALL_T', _} | _]]) -> Code; tweak_returns(['RETURN' | Code = [{'CALL_T', _} | _]]) -> Code;
tweak_returns(['RETURN' | Code = [{'CALL_TR', _, _, _} | _]]) -> Code; tweak_returns(['RETURN' | Code = [{'ABORT', _} | _]]) -> Code;
tweak_returns(['RETURN' | Code = [{'CALL_GT', _} | _]]) -> Code; tweak_returns(['RETURN' | Code = [{'EXIT', _} | _]]) -> Code;
tweak_returns(['RETURN' | Code = [{'CALL_GTR', _, _, _, _} | _]]) -> Code;
tweak_returns(['RETURN' | Code = [{'ABORT', _} | _]]) -> Code;
tweak_returns(Code) -> Code. tweak_returns(Code) -> Code.
%% -- Split basic blocks at CALL instructions -- %% -- Split basic blocks at CALL instructions --
@@ -1476,6 +1464,8 @@ split_calls(Ref, [I | Code], Acc, Blocks) when element(1, I) == 'CALL';
split_calls(make_ref(), Code, [], [{Ref, lists:reverse([I | Acc])} | Blocks]); split_calls(make_ref(), Code, [], [{Ref, lists:reverse([I | Acc])} | Blocks]);
split_calls(Ref, [{'ABORT', _} = I | _Code], Acc, Blocks) -> split_calls(Ref, [{'ABORT', _} = I | _Code], Acc, Blocks) ->
lists:reverse([{Ref, lists:reverse([I | Acc])} | Blocks]); lists:reverse([{Ref, lists:reverse([I | Acc])} | Blocks]);
split_calls(Ref, [{'EXIT', _} = I | _Code], Acc, Blocks) ->
lists:reverse([{Ref, lists:reverse([I | Acc])} | Blocks]);
split_calls(Ref, [I | Code], Acc, Blocks) -> split_calls(Ref, [I | Code], Acc, Blocks) ->
split_calls(Ref, Code, [I | Acc], Blocks). split_calls(Ref, Code, [I | Acc], Blocks).
+8 -1
View File
@@ -13,6 +13,7 @@
pp/1, pp/1,
set_name/2, set_name/2,
set_namespace/2, set_namespace/2,
set_payable/2,
enter_namespace/2, enter_namespace/2,
get_namespace/1, get_namespace/1,
qualify/2, qualify/2,
@@ -48,6 +49,7 @@
, type_vars => #{ string() => aeb_aevm_data:type() } , type_vars => #{ string() => aeb_aevm_data:type() }
, constructors => #{ [string()] => integer() } %% name to tag , constructors => #{ [string()] => integer() } %% name to tag
, options => [any()] , options => [any()]
, payable => boolean()
}. }.
pp(Icode) -> pp(Icode) ->
@@ -65,7 +67,8 @@ new(Options) ->
, types => builtin_types() , types => builtin_types()
, type_vars => #{} , type_vars => #{}
, constructors => builtin_constructors() , constructors => builtin_constructors()
, options => Options}. , options => Options
, payable => false }.
builtin_types() -> builtin_types() ->
Word = fun([]) -> word end, Word = fun([]) -> word end,
@@ -103,6 +106,10 @@ new_env() ->
set_name(Name, Icode) -> set_name(Name, Icode) ->
maps:put(contract_name, Name, Icode). maps:put(contract_name, Name, Icode).
-spec set_payable(boolean(), icode()) -> icode().
set_payable(Payable, Icode) ->
maps:put(payable, Payable, Icode).
-spec set_namespace(aeso_syntax:con() | aeso_syntax:qcon(), icode()) -> icode(). -spec set_namespace(aeso_syntax:con() | aeso_syntax:qcon(), icode()) -> icode().
set_namespace(NS, Icode) -> Icode#{ namespace => NS }. set_namespace(NS, Icode) -> Icode#{ namespace => NS }.
+85 -26
View File
@@ -6,6 +6,8 @@
-export([string/1, -export([string/1,
string/2, string/2,
string/3,
hash_include/2,
type/1]). type/1]).
-include("aeso_parse_lib.hrl"). -include("aeso_parse_lib.hrl").
@@ -14,15 +16,24 @@
| {error, {aeso_parse_lib:pos(), atom(), term()}} | {error, {aeso_parse_lib:pos(), atom(), term()}}
| {error, {aeso_parse_lib:pos(), atom()}}. | {error, {aeso_parse_lib:pos(), atom()}}.
-type include_hash() :: {string(), binary()}.
-spec string(string()) -> parse_result(). -spec string(string()) -> parse_result().
string(String) -> string(String) ->
string(String, []). string(String, sets:new(), []).
-spec string(string(), aeso_compiler:options()) -> parse_result(). -spec string(string(), aeso_compiler:options()) -> parse_result().
string(String, Opts) -> string(String, Opts) ->
case lists:keyfind(src_file, 1, Opts) of
{src_file, File} -> string(String, sets:add_element(File, sets:new()), Opts);
false -> string(String, sets:new(), Opts)
end.
-spec string(string(), sets:set(include_hash()), aeso_compiler:options()) -> parse_result().
string(String, Included, Opts) ->
case parse_and_scan(file(), String, Opts) of case parse_and_scan(file(), String, Opts) of
{ok, AST} -> {ok, AST} ->
expand_includes(AST, Opts); expand_includes(AST, Included, Opts);
Err = {error, _} -> Err = {error, _} ->
Err Err
end. end.
@@ -46,6 +57,7 @@ decl() ->
choice( choice(
%% Contract declaration %% Contract declaration
[ ?RULE(keyword(contract), con(), tok('='), maybe_block(decl()), {contract, _1, _2, _4}) [ ?RULE(keyword(contract), con(), tok('='), maybe_block(decl()), {contract, _1, _2, _4})
, ?RULE(token(payable), keyword(contract), con(), tok('='), maybe_block(decl()), add_modifiers([_1], {contract, _2, _3, _5}))
, ?RULE(keyword(namespace), con(), tok('='), maybe_block(decl()), {namespace, _1, _2, _4}) , ?RULE(keyword(namespace), con(), tok('='), maybe_block(decl()), {namespace, _1, _2, _4})
, ?RULE(keyword(include), str(), {include, get_ann(_1), _2}) , ?RULE(keyword(include), str(), {include, get_ann(_1), _2})
@@ -70,7 +82,7 @@ fun_or_entry() ->
?RULE(keyword(entrypoint), {entrypoint, _1})]). ?RULE(keyword(entrypoint), {entrypoint, _1})]).
modifiers() -> modifiers() ->
many(choice([token(stateful), token(private), token(public)])). many(choice([token(stateful), token(payable), token(private), token(public)])).
add_modifiers(Mods, Entry = {entrypoint, _}, Node) -> add_modifiers(Mods, Entry = {entrypoint, _}, Node) ->
add_modifiers(Mods ++ [Entry], Node); add_modifiers(Mods ++ [Entry], Node);
@@ -140,7 +152,8 @@ type100() -> type200().
type200() -> type200() ->
?RULE(many({fun_domain(), keyword('=>')}), type300(), fun_t(_1, _2)). ?RULE(many({fun_domain(), keyword('=>')}), type300(), fun_t(_1, _2)).
type300() -> type400(). type300() ->
?RULE(sep1(type400(), tok('*')), tuple_t(get_ann(lists:nth(1, _1)), _1)).
type400() -> type400() ->
choice( choice(
@@ -155,11 +168,18 @@ type400() ->
typeAtom() -> typeAtom() ->
?LAZY_P(choice( ?LAZY_P(choice(
[ id(), token(con), token(qcon), token(qid), tvar() [ parens(type())
, ?RULE(keyword('('), comma_sep(type()), tok(')'), tuple_t(_1, _2)) , id(), token(con), token(qcon), token(qid), tvar()
])). ])).
fun_domain() -> ?RULE(?LAZY_P(type300()), fun_domain(_1)). fun_domain() -> ?LAZY_P(choice(
[ ?RULE(tok('('), tok(')'), [])
%% Note avoidance of ambiguity: `(int)` can be treated as:
%% - literally `int`
%% - list of arguments with just one element int. This approach is dropped.
, ?RULE(tok('('), type(), tok(','), sep1(type(), tok(',')), tok(')'), [_2|_4])
, ?RULE(type300(), [_1])
])).
%% -- Statements ------------------------------------------------------------- %% -- Statements -------------------------------------------------------------
@@ -222,11 +242,25 @@ exprAtom() ->
, {bool, keyword(false), false} , {bool, keyword(false), false}
, ?LET_P(Fs, brace_list(?LAZY_P(field_assignment())), record(Fs)) , ?LET_P(Fs, brace_list(?LAZY_P(field_assignment())), record(Fs))
, {list, [], bracket_list(Expr)} , {list, [], bracket_list(Expr)}
, ?RULE(keyword('['), Expr, token('|'), comma_sep(comprehension_exp()), tok(']'), list_comp_e(_1, _2, _4))
, ?RULE(tok('['), Expr, binop('..'), Expr, tok(']'), _3(_2, _4)) , ?RULE(tok('['), Expr, binop('..'), Expr, tok(']'), _3(_2, _4))
, ?RULE(keyword('('), comma_sep(Expr), tok(')'), tuple_e(_1, _2)) , ?RULE(keyword('('), comma_sep(Expr), tok(')'), tuple_e(_1, _2))
]) ])
end). end).
comprehension_exp() ->
?LAZY_P(choice(
[ comprehension_bind()
, letdecl()
, comprehension_if()
])).
comprehension_if() ->
?RULE(keyword('if'), parens(expr()), {comprehension_if, _1, _2}).
comprehension_bind() ->
?RULE(id(), tok('<-'), expr(), {comprehension_bind, _1, _3}).
arg_expr() -> arg_expr() ->
?LAZY_P( ?LAZY_P(
choice([ ?RULE(id(), tok('='), expr(), {named_arg, [], _1, _3}) choice([ ?RULE(id(), tok('='), expr(), {named_arg, [], _1, _3})
@@ -473,9 +507,7 @@ fun_t(Domains, Type) ->
tuple_e(_Ann, [Expr]) -> Expr; %% Not a tuple tuple_e(_Ann, [Expr]) -> Expr; %% Not a tuple
tuple_e(Ann, Exprs) -> {tuple, Ann, Exprs}. tuple_e(Ann, Exprs) -> {tuple, Ann, Exprs}.
%% TODO: not nice list_comp_e(Ann, Expr, Binds) -> {list_comp, Ann, Expr, Binds}.
fun_domain({tuple_t, _, Args}) -> Args;
fun_domain(T) -> [T].
-spec parse_pattern(aeso_syntax:expr()) -> aeso_parse_lib:parser(aeso_syntax:pat()). -spec parse_pattern(aeso_syntax:expr()) -> aeso_parse_lib:parser(aeso_syntax:pat()).
parse_pattern({app, Ann, Con = {'::', _}, Es}) -> parse_pattern({app, Ann, Con = {'::', _}, Es}) ->
@@ -517,26 +549,36 @@ bad_expr_err(Reason, E) ->
prettypr:nest(2, aeso_pretty:expr(E))])). prettypr:nest(2, aeso_pretty:expr(E))])).
%% -- Helper functions ------------------------------------------------------- %% -- Helper functions -------------------------------------------------------
expand_includes(AST, Opts) -> expand_includes(AST, Included, Opts) ->
expand_includes(AST, [], Opts). expand_includes(AST, Included, [], Opts).
expand_includes([], Acc, _Opts) -> expand_includes([], _Included, Acc, _Opts) ->
{ok, lists:reverse(Acc)}; {ok, lists:reverse(Acc)};
expand_includes([{include, _, S = {string, _, File}} | AST], Acc, Opts) -> expand_includes([{include, Ann, {string, SAnn, File}} | AST], Included, Acc, Opts) ->
case read_file(File, Opts) of case get_include_code(File, Ann, Opts) of
{ok, Bin} -> {ok, Code} ->
Opts1 = lists:keystore(src_file, 1, Opts, {src_file, File}), Hashed = hash_include(File, Code),
case string(binary_to_list(Bin), Opts1) of case sets:is_element(Hashed, Included) of
{ok, AST1} -> false ->
expand_includes(AST1 ++ AST, Acc, Opts); Opts1 = lists:keystore(src_file, 1, Opts, {src_file, File}),
Err = {error, _} -> Included1 = sets:add_element(Hashed, Included),
Err case string(Code, Included1, Opts1) of
{ok, AST1} ->
Dependencies = [ {include, Ann, {string, SAnn, Dep}}
|| Dep <- aeso_stdlib:dependencies(File)
],
expand_includes(Dependencies ++ AST1 ++ AST, Included1, Acc, Opts);
Err = {error, _} ->
Err
end;
true ->
expand_includes(AST, Included, Acc, Opts)
end; end;
{error, _} -> Err = {error, _} ->
{error, {get_pos(S), include_error, File}} Err
end; end;
expand_includes([E | AST], Acc, Opts) -> expand_includes([E | AST], Included, Acc, Opts) ->
expand_includes(AST, [E | Acc], Opts). expand_includes(AST, Included, [E | Acc], Opts).
read_file(File, Opts) -> read_file(File, Opts) ->
case proplists:get_value(include, Opts, {explicit_files, #{}}) of case proplists:get_value(include, Opts, {explicit_files, #{}}) of
@@ -551,3 +593,20 @@ read_file(File, Opts) ->
end end
end. end.
get_include_code(File, Ann, Opts) ->
case {read_file(File, Opts), maps:find(File, aeso_stdlib:stdlib())} of
{{ok, _}, {ok,_ }} ->
return_error(ann_pos(Ann), "Illegal redefinition of standard library " ++ File);
{_, {ok, Lib}} ->
{ok, Lib};
{{ok, Bin}, _} ->
{ok, binary_to_list(Bin)};
{_, _} ->
{error, {ann_pos(Ann), include_error, File}}
end.
-spec hash_include(string() | binary(), string()) -> include_hash().
hash_include(File, Code) when is_binary(File) ->
hash_include(binary_to_list(File), Code);
hash_include(File, Code) when is_list(File) ->
{filename:basename(File), crypto:hash(sha256, Code)}.
+16 -4
View File
@@ -222,7 +222,7 @@ typedef({variant_t, Constructors}) ->
-spec constructor_t(aeso_syntax:constructor_t()) -> doc(). -spec constructor_t(aeso_syntax:constructor_t()) -> doc().
constructor_t({constr_t, _, C, []}) -> name(C); constructor_t({constr_t, _, C, []}) -> name(C);
constructor_t({constr_t, _, C, Args}) -> beside(name(C), tuple_type(Args)). constructor_t({constr_t, _, C, Args}) -> beside(name(C), args_type(Args)).
-spec field_t(aeso_syntax:field_t()) -> doc(). -spec field_t(aeso_syntax:field_t()) -> doc().
field_t({field_t, _, Name, Type}) -> field_t({field_t, _, Name, Type}) ->
@@ -234,11 +234,13 @@ type(Type, Options) ->
-spec type(aeso_syntax:type()) -> doc(). -spec type(aeso_syntax:type()) -> doc().
type({fun_t, _, Named, Args, Ret}) -> type({fun_t, _, Named, Args, Ret}) ->
follow(hsep(args_type(Named ++ Args), text("=>")), type(Ret));
type({type_sig, _, Named, Args, Ret}) ->
follow(hsep(tuple_type(Named ++ Args), text("=>")), type(Ret)); follow(hsep(tuple_type(Named ++ Args), text("=>")), type(Ret));
type({app_t, _, Type, []}) -> type({app_t, _, Type, []}) ->
type(Type); type(Type);
type({app_t, _, Type, Args}) -> type({app_t, _, Type, Args}) ->
beside(type(Type), tuple_type(Args)); beside(type(Type), args_type(Args));
type({tuple_t, _, Args}) -> type({tuple_t, _, Args}) ->
tuple_type(Args); tuple_type(Args);
type({bytes_t, _, any}) -> text("bytes(_)"); type({bytes_t, _, any}) -> text("bytes(_)");
@@ -256,10 +258,20 @@ type(T = {con, _, _}) -> name(T);
type(T = {qcon, _, _}) -> name(T); type(T = {qcon, _, _}) -> name(T);
type(T = {tvar, _, _}) -> name(T). type(T = {tvar, _, _}) -> name(T).
-spec tuple_type([aeso_syntax:type()]) -> doc(). -spec args_type([aeso_syntax:type()]) -> doc().
tuple_type(Args) -> args_type(Args) ->
tuple(lists:map(fun type/1, Args)). tuple(lists:map(fun type/1, Args)).
-spec tuple_type([aeso_syntax:type()]) -> doc().
tuple_type([]) ->
text("unit");
tuple_type(Factors) ->
beside(
[ text("(")
, par(punctuate(text(" *"), lists:map(fun type/1, Factors)), 0)
, text(")")
]).
-spec arg_expr(aeso_syntax:arg_expr()) -> doc(). -spec arg_expr(aeso_syntax:arg_expr()) -> doc().
arg_expr({named_arg, _, Name, E}) -> arg_expr({named_arg, _, Name, E}) ->
follow(hsep(expr(Name), text("=")), expr(E)); follow(hsep(expr(Name), text("=")), expr(E));
+1 -1
View File
@@ -37,7 +37,7 @@ lexer() ->
, {"[^/*]+|[/*]", skip()} ], , {"[^/*]+|[/*]", skip()} ],
Keywords = ["contract", "include", "let", "switch", "type", "record", "datatype", "if", "elif", "else", "function", Keywords = ["contract", "include", "let", "switch", "type", "record", "datatype", "if", "elif", "else", "function",
"stateful", "true", "false", "mod", "public", "entrypoint", "private", "indexed", "namespace"], "stateful", "payable", "true", "false", "mod", "public", "entrypoint", "private", "indexed", "namespace"],
KW = string:join(Keywords, "|"), KW = string:join(Keywords, "|"),
Rules = Rules =
+434
View File
@@ -0,0 +1,434 @@
%%%-------------------------------------------------------------------
%%% @author Radosław Rowicki
%%% @copyright (C) 2019, Aeternity Anstalt
%%% @doc
%%% Standard library for Sophia
%%% @end
%%% Created : 6 July 2019
%%%
%%%-------------------------------------------------------------------
-module(aeso_stdlib).
-export([stdlib/0, stdlib_list/0, dependencies/1]).
stdlib() ->
maps:from_list(stdlib_list()).
stdlib_list() ->
[ {<<"List.aes">>, std_list()}
, {<<"Func.aes">>, std_func()}
, {<<"Option.aes">>, std_option()}
, {<<"Pair.aes">>, std_pair()}
, {<<"Triple.aes">>, std_triple()}
].
dependencies(Q) ->
case Q of
<<"Option.aes">> ->
[<<"List.aes">>];
_ -> []
end.
std_func() ->
"
namespace Func =
function id(x : 'a) : 'a = x
function const(x : 'a) : 'b => 'a = (y) => x
function flip(f : ('a, 'b) => 'c) : ('b, 'a) => 'c = (b, a) => f(a, b)
function comp(f : 'b => 'c, g : 'a => 'b) : 'a => 'c = (x) => f(g(x))
function pipe(f : 'a => 'b, g : 'b => 'c) : 'a => 'c = (x) => g(f(x))
function rapply(x : 'a, f : 'a => 'b) : 'b = f(x)
/* The Z combinator - replacement for local and anonymous recursion.
*/
function recur(f : ('arg => 'res, 'arg) => 'res) : 'arg => 'res =
(x) => f(recur(f), x)
function iter(n : int, f : 'a => 'a) : 'a => 'a = iter_(n, f, (x) => x)
private function iter_(n : int, f : 'a => 'a, acc : 'a => 'a) : 'a => 'a =
if(n == 0) acc
elif(n == 1) comp(f, acc)
else iter_(n / 2, comp(f, f), if(n mod 2 == 0) acc else comp(f, acc))
function curry2(f : ('a, 'b) => 'c) : 'a => ('b => 'c) =
(x) => (y) => f(x, y)
function curry3(f : ('a, 'b, 'c) => 'd) : 'a => ('b => ('c => 'd)) =
(x) => (y) => (z) => f(x, y, z)
function uncurry2(f : 'a => ('b => 'c)) : ('a, 'b) => 'c =
(x, y) => f(x)(y)
function uncurry3(f : 'a => ('b => ('c => 'd))) : ('a, 'b, 'c) => 'd =
(x, y, z) => f(x)(y)(z)
function tuplify2(f : ('a, 'b) => 'c) : (('a * 'b)) => 'c =
(t) => switch(t)
(x, y) => f(x, y)
function tuplify3(f : ('a, 'b, 'c) => 'd) : 'a * 'b * 'c => 'd =
(t) => switch(t)
(x, y, z) => f(x, y, z)
function untuplify2(f : 'a * 'b => 'c) : ('a, 'b) => 'c =
(x, y) => f((x, y))
function untuplify3(f : 'a * 'b * 'c => 'd) : ('a, 'b, 'c) => 'd =
(x, y, z) => f((x, y, z))
".
std_list() ->"
namespace List =
function is_empty(l : list('a)) : bool = switch(l)
[] => true
_ => false
function first(l : list('a)) : option('a) = switch(l)
[] => None
h::_ => Some(h)
function tail(l : list('a)) : option(list('a)) = switch(l)
[] => None
_::t => Some(t)
function last(l : list('a)) : option('a) = switch(l)
[] => None
[x] => Some(x)
_::t => last(t)
function find(p : 'a => bool, l : list('a)) : option('a) = switch(l)
[] => None
h::t => if(p(h)) Some(h) else find(p, t)
function find_all(p : 'a => bool, l : list('a)) : list('a) = find_all_(p, l, [])
private function find_all_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l)
[] => reverse(acc)
h::t => find_all_(p, t, if(p(h)) h::acc else acc)
function find_indices(p : 'a => bool, l : list('a)) : list(int) = find_indices_(p, l, 0, [])
private function find_indices_( p : 'a => bool
, l : list('a)
, n : int
, acc : list(int)
) : list(int) = switch(l)
[] => reverse(acc)
h::t => find_indices_(p, t, n+1, if(p(h)) n::acc else acc)
function nth(n : int, l : list('a)) : option('a) = switch(l)
[] => None
h::t => if(n == 0) Some(h) else nth(n-1, t)
/* Unsafe version of `nth` */
function get(n : int, l : list('a)) : 'a = switch(l)
[] => abort(\"Out of index get\")
h::t => if(n == 0) h else get(n-1, t)
function length(l : list('a)) : int = length_(l, 0)
private function length_(l : list('a), acc : int) : int = switch(l)
[] => acc
_::t => length_(t, acc + 1)
/* Unsafe. Replaces `n`th element of `l` with `e`. Crashes on over/underflow */
function replace_at(n : int, e : 'a, l : list('a)) : list('a) =
if(n<0) abort(\"insert_at underflow\") else replace_at_(n, e, l, [])
private function replace_at_(n : int, e : 'a, l : list('a), acc : list('a)) : list('a) =
switch(l)
[] => abort(\"replace_at overflow\")
h::t => if (n == 0) reverse(e::acc) ++ t
else replace_at_(n-1, e, t, h::acc)
/* Unsafe. Adds `e` to `l` to be its `n`th element. Crashes on over/underflow */
function insert_at(n : int, e : 'a, l : list('a)) : list('a) =
if(n<0) abort(\"insert_at underflow\") else insert_at_(n, e, l, [])
private function insert_at_(n : int, e : 'a, l : list('a), acc : list('a)) : list('a) =
if (n == 0) reverse(e::acc) ++ l
else switch(l)
[] => abort(\"insert_at overflow\")
h::t => insert_at_(n-1, e, t, h::acc)
function insert_by(f : (('a, 'a) => bool), x : 'a, l : list('a)) : list('a) =
switch(l)
[] => [x]
(e :: l') =>
if(f(x, e))
e :: insert_by(f, x, l')
else
x :: l
function foldr(cons : ('a, 'b) => 'b, nil : 'b, l : list('a)) : 'b = switch(l)
[] => nil
h::t => cons(h, foldr(cons, nil, t))
function foldl(rcons : ('b, 'a) => 'b, acc : 'b, l : list('a)) : 'b = switch(l)
[] => acc
h::t => foldl(rcons, rcons(acc, h), t)
function foreach(f : 'a => unit, l : list('a)) : unit =
switch(l)
[] => ()
e :: l' =>
f(e)
foreach(f, l')
function reverse(l : list('a)) : list('a) = foldl((lst, el) => el :: lst, [], l)
function map(f : 'a => 'b, l : list('a)) : list('b) = map_(f, l, [])
private function map_(f : 'a => 'b, l : list('a), acc : list('b)) : list('b) = switch(l)
[] => reverse(acc)
h::t => map_(f, t, f(h)::acc)
function flat_map(f : 'a => list('b), l : list('a)) : list('b) = flat_map_(f, l, [])
private function flat_map_(f : 'a => list('b), l : list('a), acc : list('b)) : list('b) = switch(l)
[] => reverse(acc)
h::t => flat_map_(f, t, reverse(f(h)) ++ acc)
function filter(p : 'a => bool, l : list('a)) : list('a) = filter_(p, l, [])
private function filter_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l)
[] => reverse(acc)
h::t => filter_(p, t, if(p(h)) h::acc else acc)
/* Take `n` first elements */
function take(n : int, l : list('a)) : list('a) =
if(n < 0) abort(\"Take negative number of elements\") else take_(n, l, [])
private function take_(n : int, l : list('a), acc : list('a)) : list('a) =
if(n == 0) reverse(acc)
else switch(l)
[] => reverse(acc)
h::t => take_(n-1, t, h::acc)
/* Drop `n` first elements */
function drop(n : int, l : list('a)) : list('a) =
if(n < 0) abort(\"Drop negative number of elements\")
elif (n == 0) l
else switch(l)
[] => []
h::t => drop(n-1, t)
/* Get the longest prefix of a list in which every element matches predicate `p` */
function take_while(p : 'a => bool, l : list('a)) : list('a) = take_while_(p, l, [])
private function take_while_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l)
[] => reverse(acc)
h::t => if(p(h)) take_while_(p, t, h::acc) else reverse(acc)
/* Drop elements from `l` until `p` holds */
function drop_while(p : 'a => bool, l : list('a)) : list('a) = switch(l)
[] => []
h::t => if(p(h)) drop_while(p, t) else l
/* Splits list into two lists of elements that respectively match and don't match predicate `p` */
function partition(p : 'a => bool, l : list('a)) : (list('a) * list('a)) = partition_(p, l, [], [])
private function partition_( p : 'a => bool
, l : list('a)
, acc_t : list('a)
, acc_f : list('a)
) : (list('a) * list('a)) = switch(l)
[] => (reverse(acc_t), reverse(acc_f))
h::t => if(p(h)) partition_(p, t, h::acc_t, acc_f) else partition_(p, t, acc_t, h::acc_f)
function concats(ll : list(list('a))) : list('a) = foldr((l1, l2) => l1 ++ l2, [], ll)
function all(p : 'a => bool, l : list('a)) : bool = switch(l)
[] => true
h::t => if(p(h)) all(p, t) else false
function any(p : 'a => bool, l : list('a)) : bool = switch(l)
[] => false
h::t => if(p(h)) true else any(p, t)
function sum(l : list(int)) : int = foldl ((a, b) => a + b, 0, l)
function product(l : list(int)) : int = foldl((a, b) => a * b, 1, l)
/* Zips two list by applying bimapping function on respective elements. Drops longer tail. */
function zip_with(f : ('a, 'b) => 'c, l1 : list('a), l2 : list('b)) : list('c) = zip_with_(f, l1, l2, [])
private function zip_with_( f : ('a, 'b) => 'c
, l1 : list('a)
, l2 : list('b)
, acc : list('c)
) : list('c) = switch ((l1, l2))
(h1::t1, h2::t2) => zip_with_(f, t1, t2, f(h1, h2)::acc)
_ => reverse(acc)
/* Zips two lists into list of pairs. Drops longer tail. */
function zip(l1 : list('a), l2 : list('b)) : list('a * 'b) = zip_with((a, b) => (a, b), l1, l2)
function unzip(l : list('a * 'b)) : list('a) * list('b) = unzip_(l, [], [])
private function unzip_( l : list('a * 'b)
, acc_l : list('a)
, acc_r : list('b)
) : (list('a) * list('b)) = switch(l)
[] => (reverse(acc_l), reverse(acc_r))
(left, right)::t => unzip_(t, left::acc_l, right::acc_r)
// TODO: Improve?
function sort(lesser_cmp : ('a, 'a) => bool, l : list('a)) : list('a) = switch(l)
[] => []
h::t => switch (partition((x) => lesser_cmp(x, h), t))
(lesser, bigger) => sort(lesser_cmp, lesser) ++ h::sort(lesser_cmp, bigger)
function intersperse(delim : 'a, l : list('a)) : list('a) = intersperse_(delim, l, [])
private function intersperse_(delim : 'a, l : list('a), acc : list('a)) : list('a) = switch(l)
[] => reverse(acc)
[e] => reverse(e::acc)
h::t => intersperse_(delim, t, delim::h::acc)
function enumerate(l : list('a)) : list(int * 'a) = enumerate_(l, 0, [])
private function enumerate_(l : list('a), n : int, acc : list(int * 'a)) : list(int * 'a) = switch(l)
[] => reverse(acc)
h::t => enumerate_(t, n + 1, (n, h)::acc)
".
std_option() -> "
namespace Option =
function is_none(o : option('a)) : bool = switch(o)
None => true
Some(_) => false
function is_some(o : option('a)) : bool = switch(o)
None => false
Some(_) => true
function match(n : 'b, s : 'a => 'b, o : option('a)) : 'b = switch(o)
None => n
Some(x) => s(x)
function default(def : 'a, o : option('a)) : 'a = match(def, (x) => x, o)
function force(o : option('a)) : 'a = default(abort(\"Forced None value\"), o)
function on_elem(f : 'a => unit, o : option('a)) : unit = match((), f, o)
function map(f : 'a => 'b, o : option('a)) : option('b) = switch(o)
None => None
Some(x) => Some(f(x))
function map2(f : ('a, 'b) => 'c
, o1 : option('a)
, o2 : option('b)
) : option('c) = switch((o1, o2))
(Some(x1), Some(x2)) => Some(f(x1, x2))
_ => None
function map3( f : ('a, 'b, 'c) => 'd
, o1 : option('a)
, o2 : option('b)
, o3 : option('c)
) : option('d) = switch((o1, o2, o3))
(Some(x1), Some(x2), Some(x3)) => Some(f(x1, x2, x3))
_ => None
function app_over(f : option ('a => 'b), o : option('a)) : option('b) = switch((f, o))
(Some(ff), Some(xx)) => Some(ff(xx))
_ => None
function flat_map(f : 'a => option('b), o : option('a)) : option('b) = switch(o)
None => None
Some(x) => f(x)
function to_list(o : option('a)) : list('a) = switch(o)
None => []
Some(x) => [x]
function filter_options(l : list(option('a))) : list('a) = filter_options_(l, [])
private function filter_options_(l : list (option('a)), acc : list('a)) : list('a) = switch(l)
[] => List.reverse(acc)
None::t => filter_options_(t, acc)
Some(x)::t => filter_options_(t, x::acc)
function seq_options(l : list (option('a))) : option (list('a)) = seq_options_(l, [])
private function seq_options_(l : list (option('a)), acc : list('a)) : option(list('a)) = switch(l)
[] => Some(List.reverse(acc))
None::t => None
Some(x)::t => seq_options_(t, x::acc)
function choose(o1 : option('a), o2 : option('a)) : option('a) =
if(is_some(o1)) o1 else o2
function choose_first(l : list(option('a))) : option('a) = switch(l)
[] => None
None::t => choose_first(t)
Some(x)::_ => Some(x)
".
std_pair() -> "
namespace Pair =
function fst(t : ('a * 'b)) : 'a = switch(t)
(x, _) => x
function snd(t : ('a * 'b)) : 'b = switch(t)
(_, y) => y
function map1(f : 'a => 'c, t : ('a * 'b)) : ('c * 'b) = switch(t)
(x, y) => (f(x), y)
function map2(f : 'b => 'c, t : ('a * 'b)) : ('a * 'c) = switch(t)
(x, y) => (x, f(y))
function bimap(f : 'a => 'c, g : 'b => 'd, t : ('a * 'b)) : ('c * 'd) = switch(t)
(x, y) => (f(x), g(y))
function swap(t : ('a * 'b)) : ('b * 'a) = switch(t)
(x, y) => (y, x)
".
std_triple() -> "
namespace Triple =
function fst(t : ('a * 'b * 'c)) : 'a = switch(t)
(x, _, _) => x
function snd(t : ('a * 'b * 'c)) : 'b = switch(t)
(_, y, _) => y
function thd(t : ('a * 'b * 'c)) : 'c = switch(t)
(_, _, z) => z
function map1(f : 'a => 'm, t : ('a * 'b * 'c)) : ('m * 'b * 'c) = switch(t)
(x, y, z) => (f(x), y, z)
function map2(f : 'b => 'm, t : ('a * 'b * 'c)) : ('a * 'm * 'c) = switch(t)
(x, y, z) => (x, f(y), z)
function map3(f : 'c => 'm, t : ('a * 'b * 'c)) : ('a * 'b * 'm) = switch(t)
(x, y, z) => (x, y, f(z))
function trimap( f : 'a => 'x
, g : 'b => 'y
, h : 'c => 'z
, t : ('a * 'b * 'c)
) : ('x * 'y * 'z) = switch(t)
(x, y, z) => (f(x), g(y), h(z))
function swap(t : ('a * 'b * 'c)) : ('c * 'b * 'a) = switch(t)
(x, y, z) => (z, y, x)
function rotr(t : ('a * 'b * 'c)) : ('c * 'a * 'b) = switch(t)
(x, y, z) => (z, x, y)
function rotl(t : ('a * 'b * 'c)) : ('b * 'c * 'a) = switch(t)
(x, y, z) => (y, z, x)
".
+5
View File
@@ -92,6 +92,7 @@
| {proj, ann(), expr(), id()} | {proj, ann(), expr(), id()}
| {tuple, ann(), [expr()]} | {tuple, ann(), [expr()]}
| {list, ann(), [expr()]} | {list, ann(), [expr()]}
| {list_comp, ann(), expr(), [comprehension_exp()]}
| {typed, ann(), expr(), type()} | {typed, ann(), expr(), type()}
| {record, ann(), [field(expr())]} | {record, ann(), [field(expr())]}
| {record, ann(), expr(), [field(expr())]} %% record update | {record, ann(), expr(), [field(expr())]} %% record update
@@ -104,6 +105,10 @@
| id() | qid() | con() | qcon() | id() | qid() | con() | qcon()
| constant(). | constant().
-type comprehension_exp() :: [{ comprehension_bind, ann(), id(), expr()}
| {comprehension_if, expr()}
| letbind()].
-type arg_expr() :: expr() | {named_arg, ann(), id(), expr()}. -type arg_expr() :: expr() | {named_arg, ann(), id(), expr()}.
%% When lvalue is a projection this is sugar for accessing fields in nested %% When lvalue is a projection this is sugar for accessing fields in nested
+9
View File
@@ -71,6 +71,15 @@ fold(Alg = #alg{zero = Zero, plus = Plus, scoped = Scoped}, Fun, K, X) ->
{proj, _, E, _} -> Expr(E); {proj, _, E, _} -> Expr(E);
{tuple, _, As} -> Expr(As); {tuple, _, As} -> Expr(As);
{list, _, As} -> Expr(As); {list, _, As} -> Expr(As);
{list_comp, _, Y, []} -> Expr(Y);
{list_comp, A, Y, [{comprehension_bind, I, E}|R]} ->
Plus(Expr(E), Scoped(BindExpr(I), Expr({list_comp, A, Y, R})));
{list_comp, A, Y, [{comprehension_if, E}|R]} ->
Plus(Expr(E), Expr({list_comp, A, Y, R}));
{list_comp, A, Y, [D = {letval, _, F, _, _} | R]} ->
Plus(Decl(D), Scoped(BindExpr(F), Expr({list_comp, A, Y, R})));
{list_comp, A, Y, [D = {letfun, _, F, _, _, _} | R]} ->
Plus(Decl(D), Scoped(BindExpr(F), Expr({list_comp, A, Y, R})));
{typed, _, E, T} -> Plus(Expr(E), Type(T)); {typed, _, E, T} -> Plus(Expr(E), Type(T));
{record, _, Fs} -> Expr(Fs); {record, _, Fs} -> Expr(Fs);
{record, _, E, Fs} -> Expr([E | Fs]); {record, _, E, Fs} -> Expr([E | Fs]);
+1 -1
View File
@@ -1,6 +1,6 @@
{application, aesophia, {application, aesophia,
[{description, "Contract Language for aeternity"}, [{description, "Contract Language for aeternity"},
{vsn, "3.2.0"}, {vsn, "4.0.0-rc1"},
{registered, []}, {registered, []},
{applications, {applications,
[kernel, [kernel,
+11 -11
View File
@@ -66,7 +66,7 @@ encode_decode_sophia_test() ->
ok = Check("bool", "true"), ok = Check("bool", "true"),
ok = Check("bool", "false"), ok = Check("bool", "false"),
ok = Check("string", "\"Hello\""), ok = Check("string", "\"Hello\""),
ok = Check("(string, list(int), option(bool))", ok = Check("string * list(int) * option(bool)",
"(\"Hello\", [1, 2, 3], Some(true))"), "(\"Hello\", [1, 2, 3], Some(true))"),
ok = Check("variant", "Blue({[\"x\"] = 1})"), ok = Check("variant", "Blue({[\"x\"] = 1})"),
ok = Check("r", "{x = (\"foo\", 0), y = Red}"), ok = Check("r", "{x = (\"foo\", 0), y = Red}"),
@@ -76,15 +76,15 @@ encode_decode_sophia_string(SophiaType, String) ->
io:format("String ~p~n", [String]), io:format("String ~p~n", [String]),
Code = [ "contract MakeCall =\n" Code = [ "contract MakeCall =\n"
, " type arg_type = ", SophiaType, "\n" , " type arg_type = ", SophiaType, "\n"
, " type an_alias('a) = (string, 'a)\n" , " type an_alias('a) = string * 'a\n"
, " record r = {x : an_alias(int), y : variant}\n" , " record r = {x : an_alias(int), y : variant}\n"
, " datatype variant = Red | Blue(map(string, int))\n" , " datatype variant = Red | Blue(map(string, int))\n"
, " entrypoint foo : arg_type => arg_type\n" ], , " entrypoint foo : arg_type => arg_type\n" ],
case aeso_compiler:check_call(lists:flatten(Code), "foo", [String], []) of case aeso_compiler:check_call(lists:flatten(Code), "foo", [String], [no_implicit_stdlib]) of
{ok, _, {[Type], _}, [Arg]} -> {ok, _, {[Type], _}, [Arg]} ->
io:format("Type ~p~n", [Type]), io:format("Type ~p~n", [Type]),
Data = encode(Arg), Data = encode(Arg),
case aeso_compiler:to_sophia_value(Code, "foo", ok, Data) of case aeso_compiler:to_sophia_value(Code, "foo", ok, Data, [no_implicit_stdlib]) of
{ok, Sophia} -> {ok, Sophia} ->
lists:flatten(io_lib:format("~s", [prettypr:format(aeso_pretty:expr(Sophia))])); lists:flatten(io_lib:format("~s", [prettypr:format(aeso_pretty:expr(Sophia))]));
{error, Err} -> {error, Err} ->
@@ -137,10 +137,10 @@ parameterized_contract(FunName, Types) ->
parameterized_contract(ExtraCode, FunName, Types) -> parameterized_contract(ExtraCode, FunName, Types) ->
lists:flatten( lists:flatten(
["contract Remote =\n" ["contract Remote =\n"
" entrypoint bla : () => ()\n\n" " entrypoint bla : () => unit\n\n"
"contract Dummy =\n", "contract Dummy =\n",
ExtraCode, "\n", ExtraCode, "\n",
" type an_alias('a) = (string, 'a)\n" " type an_alias('a) = string * 'a\n"
" record r = {x : an_alias(int), y : variant}\n" " record r = {x : an_alias(int), y : variant}\n"
" datatype variant = Red | Blue(map(string, int))\n" " datatype variant = Red | Blue(map(string, int))\n"
" entrypoint ", FunName, " : (", string:join(Types, ", "), ") => int\n" ]). " entrypoint ", FunName, " : (", string:join(Types, ", "), ") => int\n" ]).
@@ -152,7 +152,7 @@ oracle_test() ->
" Oracle.get_question(o, q)\n", " Oracle.get_question(o, q)\n",
{ok, _, {[word, word], {list, string}}, [16#123, 16#456]} = {ok, _, {[word, word], {list, string}}, [16#123, 16#456]} =
aeso_compiler:check_call(Contract, "question", ["ok_111111111111111111111111111111ZrdqRz9", aeso_compiler:check_call(Contract, "question", ["ok_111111111111111111111111111111ZrdqRz9",
"oq_1111111111111111111111111111113AFEFpt5"], []), "oq_1111111111111111111111111111113AFEFpt5"], [no_implicit_stdlib]),
ok. ok.
@@ -162,7 +162,7 @@ permissive_literals_fail_test() ->
" stateful entrypoint haxx(o : oracle(list(string), option(int))) =\n" " stateful entrypoint haxx(o : oracle(list(string), option(int))) =\n"
" Chain.spend(o, 1000000)\n", " Chain.spend(o, 1000000)\n",
{error, <<"Type errors\nCannot unify", _/binary>>} = {error, <<"Type errors\nCannot unify", _/binary>>} =
aeso_compiler:check_call(Contract, "haxx", ["#123"], []), aeso_compiler:check_call(Contract, "haxx", ["#123"], [no_implicit_stdlib]),
ok. ok.
encode_decode_calldata(FunName, Types, Args) -> encode_decode_calldata(FunName, Types, Args) ->
@@ -173,8 +173,8 @@ encode_decode_calldata(FunName, Types, Args, RetType) ->
encode_decode_calldata_(Code, FunName, Args, RetType). encode_decode_calldata_(Code, FunName, Args, RetType).
encode_decode_calldata_(Code, FunName, Args, RetVMType) -> encode_decode_calldata_(Code, FunName, Args, RetVMType) ->
{ok, Calldata} = aeso_compiler:create_calldata(Code, FunName, Args), {ok, Calldata} = aeso_compiler:create_calldata(Code, FunName, Args, [no_implicit_stdlib]),
{ok, _, {ArgTypes, RetType}, _} = aeso_compiler:check_call(Code, FunName, Args, [{backend, aevm}]), {ok, _, {ArgTypes, RetType}, _} = aeso_compiler:check_call(Code, FunName, Args, [{backend, aevm}, no_implicit_stdlib]),
?assertEqual(RetType, RetVMType), ?assertEqual(RetType, RetVMType),
CalldataType = {tuple, [word, {tuple, ArgTypes}]}, CalldataType = {tuple, [word, {tuple, ArgTypes}]},
{ok, {_Hash, ArgTuple}} = aeb_heap:from_binary(CalldataType, Calldata), {ok, {_Hash, ArgTuple}} = aeb_heap:from_binary(CalldataType, Calldata),
@@ -182,7 +182,7 @@ encode_decode_calldata_(Code, FunName, Args, RetVMType) ->
"init" -> "init" ->
ok; ok;
_ -> _ ->
{ok, _ArgTypes, ValueASTs} = aeso_compiler:decode_calldata(Code, FunName, Calldata), {ok, _ArgTypes, ValueASTs} = aeso_compiler:decode_calldata(Code, FunName, Calldata, [no_implicit_stdlib]),
Values = [ prettypr:format(aeso_pretty:expr(V)) || V <- ValueASTs ], Values = [ prettypr:format(aeso_pretty:expr(V)) || V <- ValueASTs ],
?assertMatch({X, X}, {Args, Values}) ?assertMatch({X, X}, {Args, Values})
end, end,
+18 -14
View File
@@ -9,25 +9,27 @@ simple_aci_test_() ->
test_contract(N) -> test_contract(N) ->
{Contract,MapACI,DecACI} = test_cases(N), {Contract,MapACI,DecACI} = test_cases(N),
{ok,JSON} = aeso_aci:contract_interface(json, Contract), {ok,JSON} = aeso_aci:contract_interface(json, Contract, [no_implicit_stdlib]),
?assertEqual([MapACI], JSON), ?assertEqual([MapACI], JSON),
?assertEqual({ok, DecACI}, aeso_aci:render_aci_json(JSON)). ?assertEqual({ok, DecACI}, aeso_aci:render_aci_json(JSON)).
test_cases(1) -> test_cases(1) ->
Contract = <<"contract C =\n" Contract = <<"payable contract C =\n"
" entrypoint a(i : int) = i+1\n">>, " payable stateful entrypoint a(i : int) = i+1\n">>,
MapACI = #{contract => MapACI = #{contract =>
#{name => <<"C">>, #{name => <<"C">>,
type_defs => [], type_defs => [],
payable => true,
functions => functions =>
[#{name => <<"a">>, [#{name => <<"a">>,
arguments => arguments =>
[#{name => <<"i">>, [#{name => <<"i">>,
type => <<"int">>}], type => <<"int">>}],
returns => <<"int">>, returns => <<"int">>,
stateful => false}]}}, stateful => true,
DecACI = <<"contract C =\n" payable => true}]}},
" entrypoint a : (int) => int\n">>, DecACI = <<"payable contract C =\n"
" payable entrypoint a : (int) => int\n">>,
{Contract,MapACI,DecACI}; {Contract,MapACI,DecACI};
test_cases(2) -> test_cases(2) ->
@@ -35,7 +37,7 @@ test_cases(2) ->
" type allan = int\n" " type allan = int\n"
" entrypoint a(i : allan) = i+1\n">>, " entrypoint a(i : allan) = i+1\n">>,
MapACI = #{contract => MapACI = #{contract =>
#{name => <<"C">>, #{name => <<"C">>, payable => false,
type_defs => type_defs =>
[#{name => <<"allan">>, [#{name => <<"allan">>,
typedef => <<"int">>, typedef => <<"int">>,
@@ -46,14 +48,15 @@ test_cases(2) ->
type => <<"C.allan">>}], type => <<"C.allan">>}],
name => <<"a">>, name => <<"a">>,
returns => <<"int">>, returns => <<"int">>,
stateful => false}]}}, stateful => false,
payable => false}]}},
DecACI = <<"contract C =\n" DecACI = <<"contract C =\n"
" type allan = int\n" " type allan = int\n"
" entrypoint a : (C.allan) => int\n">>, " entrypoint a : (C.allan) => int\n">>,
{Contract,MapACI,DecACI}; {Contract,MapACI,DecACI};
test_cases(3) -> test_cases(3) ->
Contract = <<"contract C =\n" Contract = <<"contract C =\n"
" type state = ()\n" " type state = unit\n"
" datatype event = SingleEventDefined\n" " datatype event = SingleEventDefined\n"
" datatype bert('a) = Bin('a)\n" " datatype bert('a) = Bin('a)\n"
" entrypoint a(i : bert(string)) = 1\n">>, " entrypoint a(i : bert(string)) = 1\n">>,
@@ -64,10 +67,10 @@ test_cases(3) ->
type => type =>
#{<<"C.bert">> => [<<"string">>]}}], #{<<"C.bert">> => [<<"string">>]}}],
name => <<"a">>,returns => <<"int">>, name => <<"a">>,returns => <<"int">>,
stateful => false}], stateful => false, payable => false}],
name => <<"C">>, name => <<"C">>, payable => false,
event => #{variant => [#{<<"SingleEventDefined">> => []}]}, event => #{variant => [#{<<"SingleEventDefined">> => []}]},
state => #{tuple => []}, state => <<"unit">>,
type_defs => type_defs =>
[#{name => <<"bert">>, [#{name => <<"bert">>,
typedef => typedef =>
@@ -75,7 +78,7 @@ test_cases(3) ->
[#{<<"Bin">> => [<<"'a">>]}]}, [#{<<"Bin">> => [<<"'a">>]}]},
vars => [#{name => <<"'a">>}]}]}}, vars => [#{name => <<"'a">>}]}]}},
DecACI = <<"contract C =\n" DecACI = <<"contract C =\n"
" type state = ()\n" " type state = unit\n"
" datatype event = SingleEventDefined\n" " datatype event = SingleEventDefined\n"
" datatype bert('a) = Bin('a)\n" " datatype bert('a) = Bin('a)\n"
" entrypoint a : (C.bert(string)) => int\n">>, " entrypoint a : (C.bert(string)) => int\n">>,
@@ -87,7 +90,8 @@ aci_test_() ->
fun() -> aci_test_contract(ContractName) end} fun() -> aci_test_contract(ContractName) end}
|| ContractName <- all_contracts()]. || ContractName <- all_contracts()].
all_contracts() -> aeso_compiler_tests:compilable_contracts(). all_contracts() -> [C || C <- aeso_compiler_tests:compilable_contracts()
, not aeso_compiler_tests:wants_stdlib(C)].
aci_test_contract(Name) -> aci_test_contract(Name) ->
String = aeso_test_utils:read_contract(Name), String = aeso_test_utils:read_contract(Name),
+39 -8
View File
@@ -21,12 +21,40 @@ calldata_test_() ->
ContractString = aeso_test_utils:read_contract(ContractName), ContractString = aeso_test_utils:read_contract(ContractName),
AevmExprs = AevmExprs =
case not lists:member(ContractName, not_yet_compilable(aevm)) of case not lists:member(ContractName, not_yet_compilable(aevm)) of
true -> ast_exprs(ContractString, Fun, Args, [{backend, aevm}]); true -> ast_exprs(ContractString, Fun, Args, [{backend, aevm}]
++ [no_implicit_stdlib || not aeso_compiler_tests:wants_stdlib(ContractName)]);
false -> undefined false -> undefined
end, end,
FateExprs = FateExprs =
case not lists:member(ContractName, not_yet_compilable(fate)) of case not lists:member(ContractName, not_yet_compilable(fate)) of
true -> ast_exprs(ContractString, Fun, Args, [{backend, fate}]); true -> ast_exprs(ContractString, Fun, Args, [{backend, fate}]
++ [no_implicit_stdlib || not aeso_compiler_tests:wants_stdlib(ContractName)]);
false -> undefined
end,
case FateExprs == undefined orelse AevmExprs == undefined of
true -> ok;
false ->
?assertEqual(FateExprs, AevmExprs)
end
end} || {ContractName, Fun, Args} <- compilable_contracts()].
calldata_aci_test_() ->
[ {"Testing " ++ ContractName ++ " contract calling " ++ Fun,
fun() ->
ContractString = aeso_test_utils:read_contract(ContractName),
{ok, ContractACIBin} = aeso_aci:contract_interface(string, ContractString),
ContractACI = binary_to_list(ContractACIBin),
io:format("ACI:\n~s\n", [ContractACIBin]),
AevmExprs =
case not lists:member(ContractName, not_yet_compilable(aevm)) of
true -> ast_exprs(ContractACI, Fun, Args, [{backend, aevm}]
++ [no_implicit_stdlib || not aeso_compiler_tests:wants_stdlib(ContractName)]);
false -> undefined
end,
FateExprs =
case not lists:member(ContractName, not_yet_compilable(fate)) of
true -> ast_exprs(ContractACI, Fun, Args, [{backend, fate}]
++ [no_implicit_stdlib || not aeso_compiler_tests:wants_stdlib(ContractName)]);
false -> undefined false -> undefined
end, end,
case FateExprs == undefined orelse AevmExprs == undefined of case FateExprs == undefined orelse AevmExprs == undefined of
@@ -38,8 +66,8 @@ calldata_test_() ->
ast_exprs(ContractString, Fun, Args, Opts) -> ast_exprs(ContractString, Fun, Args, Opts) ->
{ok, Data} = aeso_compiler:create_calldata(ContractString, Fun, Args, Opts), {ok, Data} = (catch aeso_compiler:create_calldata(ContractString, Fun, Args, Opts)),
{ok, _Types, Exprs} = aeso_compiler:decode_calldata(ContractString, Fun, Data, Opts), {ok, _Types, Exprs} = (catch aeso_compiler:decode_calldata(ContractString, Fun, Data, Opts)),
?assert(is_list(Exprs)), ?assert(is_list(Exprs)),
Exprs. Exprs.
@@ -92,11 +120,14 @@ compilable_contracts() ->
{"complex_types", "filter_some", ["[Some(11), Some(12), None]"]}, {"complex_types", "filter_some", ["[Some(11), Some(12), None]"]},
{"complex_types", "init", ["ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ"]}, {"complex_types", "init", ["ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ"]},
{"__call" "init", []}, {"__call" "init", []},
{"bitcoin_auth", "authorize", ["1", "#0102030405060708090a0b0c0d0e0f101718192021222324252627282930313233343536373839401a1b1c1d1e1f202122232425262728293031323334353637"]}, {"bitcoin_auth", "authorize", ["1", "#0102030405060708090a0b0c0d0e0f101718192021222324252627282930313233343536373839401a1b1c1d1e1f20212223242526272829303132333435363738"]},
{"bitcoin_auth", "to_sign", ["#0102030405060708090a0b0c0d0e0f1017181920212223242526272829303132", "2"]} {"bitcoin_auth", "to_sign", ["#0102030405060708090a0b0c0d0e0f1017181920212223242526272829303132", "2"]},
{"stub", "foo", ["42"]},
{"stub", "foo", ["-42"]},
{"payable", "foo", ["42"]}
]. ].
not_yet_compilable(fate) -> not_yet_compilable(fate) ->
["address_chain"]; [];
not_yet_compilable(aevm) -> not_yet_compilable(aevm) ->
["__call"]. [].
+41 -11
View File
@@ -53,15 +53,17 @@ simple_compile_test_() ->
#{byte_code := Code2} = compile(aevm, "include"), #{byte_code := Code2} = compile(aevm, "include"),
?assertMatch(true, Code1 == Code2) ?assertMatch(true, Code1 == Code2)
end} ] ++ end} ] ++
[ {"Testing deadcode elimination", [ {"Testing deadcode elimination for " ++ atom_to_list(Backend),
fun() -> fun() ->
#{ byte_code := NoDeadCode } = compile(aevm, "nodeadcode"), #{ byte_code := NoDeadCode } = compile(Backend, "nodeadcode"),
#{ byte_code := DeadCode } = compile(aevm, "deadcode"), #{ byte_code := DeadCode } = compile(Backend, "deadcode"),
SizeNoDeadCode = byte_size(NoDeadCode), SizeNoDeadCode = byte_size(NoDeadCode),
SizeDeadCode = byte_size(DeadCode), SizeDeadCode = byte_size(DeadCode),
?assertMatch({_, _, true}, {SizeDeadCode, SizeNoDeadCode, SizeDeadCode + 40 < SizeNoDeadCode}), Delta = if Backend == aevm -> 40;
Backend == fate -> 20 end,
?assertMatch({_, _, true}, {SizeDeadCode, SizeNoDeadCode, SizeDeadCode + Delta < SizeNoDeadCode}),
ok ok
end} ]. end} || Backend <- [aevm, fate] ].
check_errors(Expect, ErrorString) -> check_errors(Expect, ErrorString) ->
%% This removes the final single \n as well. %% This removes the final single \n as well.
@@ -73,7 +75,9 @@ check_errors(Expect, ErrorString) ->
end. end.
compile(Backend, Name) -> compile(Backend, Name) ->
compile(Backend, Name, [{include, {file_system, [aeso_test_utils:contract_path()]}}]). compile(Backend, Name,
[{include, {file_system, [aeso_test_utils:contract_path()]}}]
++ [no_implicit_stdlib || not wants_stdlib(Name)]).
compile(Backend, Name, Options) -> compile(Backend, Name, Options) ->
String = aeso_test_utils:read_contract(Name), String = aeso_test_utils:read_contract(Name),
@@ -117,7 +121,14 @@ compilable_contracts() ->
"address_chain", "address_chain",
"namespace_bug", "namespace_bug",
"bytes_to_x", "bytes_to_x",
"aens" "aens",
"tuple_match",
"cyclic_include",
"stdlib_include",
"double_include",
"manual_stdlib_include",
"list_comp",
"payable"
]. ].
not_yet_compilable(fate) -> []; not_yet_compilable(fate) -> [];
@@ -220,7 +231,7 @@ failing_contracts() ->
"when checking that 'init' returns a value of type 'state' at line 7, column 3">>]} "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 unit\n"
"when checking that 'init' returns a value of type 'state' at line 5, column 3">>]} "when checking that 'init' returns a value of type 'state' at line 5, column 3">>]}
, {"missing_fields_in_record_expression", , {"missing_fields_in_record_expression",
[<<"The field x is missing when constructing an element of type r('a) (at line 7, column 42)">>, [<<"The field x is missing when constructing an element of type r('a) (at line 7, column 42)">>,
@@ -325,7 +336,7 @@ failing_contracts() ->
<<"Cannot reference stateful function local_spend (at line 14, column 35)\nin the definition of non-stateful function fail2.">>, <<"Cannot reference stateful function local_spend (at line 14, column 35)\nin the definition of non-stateful function fail2.">>,
<<"Cannot reference stateful function Chain.spend (at line 16, column 15)\nin the definition of non-stateful function fail3.">>, <<"Cannot reference stateful function Chain.spend (at line 16, column 15)\nin the definition of non-stateful function fail3.">>,
<<"Cannot reference stateful function Chain.spend (at line 20, column 31)\nin the definition of non-stateful function fail4.">>, <<"Cannot reference stateful function Chain.spend (at line 20, column 31)\nin the definition of non-stateful function fail4.">>,
<<"Cannot reference stateful function Chain.spend (at line 35, column 45)\nin the definition of non-stateful function fail5.">>, <<"Cannot reference stateful function Chain.spend (at line 35, column 47)\nin the definition of non-stateful function fail5.">>,
<<"Cannot pass non-zero value argument 1000 (at line 48, column 57)\nin the definition of non-stateful function fail6.">>, <<"Cannot pass non-zero value argument 1000 (at line 48, column 57)\nin the definition of non-stateful function fail6.">>,
<<"Cannot pass non-zero value argument 1000 (at line 49, column 56)\nin the definition of non-stateful function fail7.">>, <<"Cannot pass non-zero value argument 1000 (at line 49, column 56)\nin the definition of non-stateful function fail7.">>,
<<"Cannot pass non-zero value argument 1000 (at line 52, column 17)\nin the definition of non-stateful function fail8.">>]} <<"Cannot pass non-zero value argument 1000 (at line 52, column 17)\nin the definition of non-stateful function fail8.">>]}
@@ -350,7 +361,26 @@ failing_contracts() ->
<<"Namespaces cannot contain entrypoints (at line 3, column 3). Use 'function' instead.">>, <<"Namespaces cannot contain entrypoints (at line 3, column 3). Use 'function' instead.">>,
<<"The contract Remote (at line 5, column 10) has no entrypoints. Since Sophia version 3.2, public\ncontract functions must be declared with the 'entrypoint' keyword instead of\n'function'.">>, <<"The contract Remote (at line 5, column 10) has no entrypoints. Since Sophia version 3.2, public\ncontract functions must be declared with the 'entrypoint' keyword instead of\n'function'.">>,
<<"The entrypoint wha (at line 12, column 3) cannot be private. Use 'function' instead.">>, <<"The entrypoint wha (at line 12, column 3) cannot be private. Use 'function' instead.">>,
<<"Use 'entrypoint' for declaration of foo (at line 6, column 3):\n entrypoint foo : () => ()">>, <<"Use 'entrypoint' for declaration of foo (at line 6, column 3):\n entrypoint foo : () => unit">>,
<<"Use 'entrypoint' instead of 'function' for public function foo (at line 10, column 3):\n entrypoint foo() = ()">>, <<"Use 'entrypoint' instead of 'function' for public function foo (at line 10, column 3):\n entrypoint foo() = ()">>,
<<"Use 'entrypoint' instead of 'function' for public function foo (at line 6, column 3):\n entrypoint foo : () => ()">>]} <<"Use 'entrypoint' instead of 'function' for public function foo (at line 6, column 3):\n entrypoint foo : () => unit">>]}
, {"list_comp_not_a_list",
[<<"Cannot unify int\n and list('a)\nwhen checking rvalue of list comprehension binding at line 2, column 36\n 1 : int\nagainst type \n list('a)">>
]}
, {"list_comp_if_not_bool",
[<<"Cannot unify int\n and bool\nwhen checking the type of the expression at line 2, column 44\n 3 : int\nagainst the expected type\n bool">>
]}
, {"list_comp_bad_shadow",
[<<"Cannot unify int\n and string\nwhen checking the type of the pattern at line 2, column 53\n x : int\nagainst the expected type\n string">>
]}
]. ].
wants_stdlib(Name) ->
lists:member
(Name,
[ "stdlib_include",
"list_comp",
"list_comp_not_a_list",
"list_comp_if_not_bool",
"list_comp_bad_shadow"
]).
+8 -6
View File
@@ -15,7 +15,7 @@ simple_contracts_test_() ->
?assertMatch( ?assertMatch(
[{contract, _, {con, _, "Identity"}, [{contract, _, {con, _, "Identity"},
[{letfun, _, {id, _, "id"}, [{arg, _, {id, _, "x"}, {id, _, "_"}}], {id, _, "_"}, [{letfun, _, {id, _, "id"}, [{arg, _, {id, _, "x"}, {id, _, "_"}}], {id, _, "_"},
{id, _, "x"}}]}], parse_string(Text)), {id, _, "x"}}]}], parse_string(Text, [no_implicit_stdlib])),
ok ok
end}, end},
{"Operator precedence test.", {"Operator precedence test.",
@@ -71,21 +71,23 @@ parse_contract(Name) ->
roundtrip_contract(Name) -> roundtrip_contract(Name) ->
round_trip(aeso_test_utils:read_contract(Name)). round_trip(aeso_test_utils:read_contract(Name)).
parse_string(Text) -> parse_string(Text) -> parse_string(Text, []).
case aeso_parser:string(Text) of
parse_string(Text, Opts) ->
case aeso_parser:string(Text, Opts) of
{ok, Contract} -> Contract; {ok, Contract} -> Contract;
Err -> error(Err) Err -> error(Err)
end. end.
parse_expr(Text) -> parse_expr(Text) ->
[{letval, _, _, _, Expr}] = [{letval, _, _, _, Expr}] =
parse_string("let _ = " ++ Text), parse_string("let _ = " ++ Text, [no_implicit_stdlib]),
Expr. Expr.
round_trip(Text) -> round_trip(Text) ->
Contract = parse_string(Text), Contract = parse_string(Text, [no_implicit_stdlib]),
Text1 = prettypr:format(aeso_pretty:decls(Contract)), Text1 = prettypr:format(aeso_pretty:decls(Contract)),
Contract1 = parse_string(Text1), Contract1 = parse_string(Text1, [no_implicit_stdlib]),
NoSrcLoc = remove_line_numbers(Contract), NoSrcLoc = remove_line_numbers(Contract),
NoSrcLoc1 = remove_line_numbers(Contract1), NoSrcLoc1 = remove_line_numbers(Contract1),
?assertMatch(NoSrcLoc, diff(NoSrcLoc, NoSrcLoc1)). ?assertMatch(NoSrcLoc, diff(NoSrcLoc, NoSrcLoc1)).
+1 -1
View File
@@ -8,7 +8,7 @@ contract AbortTest =
{ value = v } { value = v }
// Aborting // Aborting
public function do_abort(v : int, s : string) : () = public function do_abort(v : int, s : string) : unit =
put_value(v) put_value(v)
revert_abort(s) revert_abort(s)
+3 -3
View File
@@ -1,9 +1,9 @@
contract Interface = contract Interface =
function do_abort : (int, string) => () function do_abort : (int, string) => unit
function get_value : () => int function get_value : () => int
function put_value : (int) => () function put_value : (int) => unit
function get_values : () => list(int) function get_values : () => list(int)
function put_values : (int) => () function put_values : (int) => unit
contract AbortTestInt = contract AbortTestInt =
+4 -1
View File
@@ -1,5 +1,5 @@
contract Remote = contract Remote =
entrypoint main : (int) => () entrypoint main : (int) => unit
contract AddrChain = contract AddrChain =
type o_type = oracle(string, map(string, int)) type o_type = oracle(string, map(string, int))
@@ -31,3 +31,6 @@ contract AddrChain =
entrypoint c_creator() : address = entrypoint c_creator() : address =
Contract.creator Contract.creator
entrypoint is_payable(a : address) : bool =
Address.is_payable(a)
+1 -1
View File
@@ -1,6 +1,6 @@
contract Remote = contract Remote =
entrypoint foo : () => () entrypoint foo : () => unit
contract AddressLiterals = contract AddressLiterals =
entrypoint addr() : address = entrypoint addr() : address =
+15 -15
View File
@@ -12,44 +12,44 @@ contract AENSTest =
// Transactions // Transactions
stateful entrypoint preclaim(addr : address, // Claim on behalf of this account (can be Contract.address) stateful entrypoint preclaim(addr : address, // Claim on behalf of this account (can be Contract.address)
chash : hash) : () = // Commitment hash chash : hash) : unit = // Commitment hash
AENS.preclaim(addr, chash) AENS.preclaim(addr, chash)
stateful entrypoint signedPreclaim(addr : address, // Claim on behalf of this account (can be Contract.address) stateful entrypoint signedPreclaim(addr : address, // Claim on behalf of this account (can be Contract.address)
chash : hash, // Commitment hash chash : hash, // Commitment hash
sign : signature) : () = // Signed by addr (if not Contract.address) sign : signature) : unit = // Signed by addr (if not Contract.address)
AENS.preclaim(addr, chash, signature = sign) AENS.preclaim(addr, chash, signature = sign)
stateful entrypoint claim(addr : address, stateful entrypoint claim(addr : address,
name : string, name : string,
salt : int) : () = salt : int) : unit =
AENS.claim(addr, name, salt) AENS.claim(addr, name, salt)
stateful entrypoint signedClaim(addr : address, stateful entrypoint signedClaim(addr : address,
name : string, name : string,
salt : int, salt : int,
sign : signature) : () = sign : signature) : unit =
AENS.claim(addr, name, salt, signature = sign) AENS.claim(addr, name, salt, signature = sign)
// TODO: update() -- how to handle pointers? // TODO: update() -- how to handle pointers?
stateful entrypoint transfer(owner : address, stateful entrypoint transfer(owner : address,
new_owner : address, new_owner : address,
name_hash : hash) : () = name : string) : unit =
AENS.transfer(owner, new_owner, name_hash) AENS.transfer(owner, new_owner, name)
stateful entrypoint signedTransfer(owner : address, stateful entrypoint signedTransfer(owner : address,
new_owner : address, new_owner : address,
name_hash : hash, name : string,
sign : signature) : () = sign : signature) : unit =
AENS.transfer(owner, new_owner, name_hash, signature = sign) AENS.transfer(owner, new_owner, name, signature = sign)
stateful entrypoint revoke(owner : address, stateful entrypoint revoke(owner : address,
name_hash : hash) : () = name : string) : unit =
AENS.revoke(owner, name_hash) AENS.revoke(owner, name)
stateful entrypoint signedRevoke(owner : address, stateful entrypoint signedRevoke(owner : address,
name_hash : hash, name : string,
sign : signature) : () = sign : signature) : unit =
AENS.revoke(owner, name_hash, signature = sign) AENS.revoke(owner, name, signature = sign)
+1 -1
View File
@@ -24,7 +24,7 @@ contract AllSyntax =
if(valWithType(Map.empty) == None) if(valWithType(Map.empty) == None)
print(42 mod 10 * 5 / 3) print(42 mod 10 * 5 / 3)
function funWithType(x : int, y) : (int, list(int)) = (x, 0 :: [y] ++ []) function funWithType(x : int, y) : int * list(int) = (x, 0 :: [y] ++ [])
function funNoType() = function funNoType() =
let foo = (x, y : bool) => let foo = (x, y : bool) =>
if (! (y && x =< 0x0b || true)) [x] if (! (y && x =< 0x0b || true)) [x]
+1 -1
View File
@@ -1,6 +1,6 @@
contract Remote = contract Remote =
entrypoint foo : () => () entrypoint foo : () => unit
contract AddressLiterals = contract AddressLiterals =
entrypoint addr1() : bytes(32) = entrypoint addr1() : bytes(32) =
+1 -1
View File
@@ -10,7 +10,7 @@ contract BasicAuth =
put(state{ nonce = n + 1 }) put(state{ nonce = n + 1 })
switch(Auth.tx_hash) switch(Auth.tx_hash)
None => abort("Not in Auth context") None => abort("Not in Auth context")
Some(tx_hash) => Crypto.ecverify(to_sign(tx_hash, n), state.owner, s) Some(tx_hash) => Crypto.verify_sig(to_sign(tx_hash, n), state.owner, s)
entrypoint to_sign(h : hash, n : int) = entrypoint to_sign(h : hash, n : int) =
Crypto.blake2b((h, n)) Crypto.blake2b((h, n))
+3 -3
View File
@@ -1,9 +1,9 @@
contract BitcoinAuth = contract BitcoinAuth =
record state = { nonce : int, owner : bytes(64) } record state = { nonce : int, owner : bytes(20) }
entrypoint init(owner' : bytes(64)) = { nonce = 1, owner = owner' } entrypoint init(owner' : bytes(20)) = { nonce = 1, owner = owner' }
stateful entrypoint authorize(n : int, s : signature) : bool = stateful entrypoint authorize(n : int, s : bytes(65)) : bool =
require(n >= state.nonce, "Nonce too low") require(n >= state.nonce, "Nonce too low")
require(n =< state.nonce, "Nonce too high") require(n =< state.nonce, "Nonce too high")
put(state{ nonce = n + 1 }) put(state{ nonce = n + 1 })
+4 -4
View File
@@ -3,8 +3,8 @@ contract Remote =
entrypoint up_to : (int) => list(int) entrypoint up_to : (int) => list(int)
entrypoint sum : (list(int)) => int entrypoint sum : (list(int)) => int
entrypoint some_string : () => string entrypoint some_string : () => string
entrypoint pair : (int, string) => (int, string) entrypoint pair : (int, string) => int * string
entrypoint squares : (int) => list((int, int)) entrypoint squares : (int) => list(int * int)
entrypoint filter_some : (list(option(int))) => list(int) entrypoint filter_some : (list(option(int))) => list(int)
entrypoint all_some : (list(option(int))) => option(list(int)) entrypoint all_some : (list(option(int))) => option(list(int))
@@ -47,7 +47,7 @@ contract ComplexTypes =
entrypoint pair(x : int, y : string) = (x, y) entrypoint pair(x : int, y : string) = (x, y)
entrypoint remote_pair(n : int, s : string) : (int, string) = entrypoint remote_pair(n : int, s : string) : int * string =
state.worker.pair(gas = 10000, n, s) state.worker.pair(gas = 10000, n, s)
entrypoint map(f, xs) = entrypoint map(f, xs) =
@@ -58,7 +58,7 @@ contract ComplexTypes =
entrypoint squares(n) = entrypoint squares(n) =
map((i) => (i, i * i), up_to(n)) map((i) => (i, i * i), up_to(n))
entrypoint remote_squares(n) : list((int, int)) = entrypoint remote_squares(n) : list(int * int) =
state.worker.squares(n) state.worker.squares(n)
// option types // option types
+4
View File
@@ -0,0 +1,4 @@
include "cyclic_include_forth.aes"
contract CI =
entrypoint ci() = Back.back() + Forth.forth()
+4
View File
@@ -0,0 +1,4 @@
include "cyclic_include_forth.aes"
namespace Back =
function back() = 2
+4
View File
@@ -0,0 +1,4 @@
include "cyclic_include_back.aes"
namespace Forth =
function forth() = 3
+3 -3
View File
@@ -1,5 +1,5 @@
namespace List = namespace MyList =
function map1(f : 'a => 'b, xs : list('a)) = function map1(f : 'a => 'b, xs : list('a)) =
switch(xs) switch(xs)
@@ -14,8 +14,8 @@ namespace List =
contract Deadcode = contract Deadcode =
entrypoint inc1(xs : list(int)) : list(int) = entrypoint inc1(xs : list(int)) : list(int) =
List.map1((x) => x + 1, xs) MyList.map1((x) => x + 1, xs)
entrypoint inc2(xs : list(int)) : list(int) = entrypoint inc2(xs : list(int)) : list(int) =
List.map1((x) => x + 1, xs) MyList.map1((x) => x + 1, xs)
+7
View File
@@ -0,0 +1,7 @@
include "included.aes"
include "../contracts/included.aes"
contract Include =
entrypoint foo() =
Included.foo()
+1 -1
View File
@@ -1,5 +1,5 @@
contract Remote = contract Remote =
entrypoint dummy : () => () entrypoint dummy : () => unit
contract Events = contract Events =
+1 -1
View File
@@ -40,7 +40,7 @@ contract FunctionArguments =
entrypoint traffic_light(c : colour) = entrypoint traffic_light(c : colour) =
Red Red
entrypoint tuples(t : ()) = entrypoint tuples(t : unit) =
t t
entrypoint due(t : Chain.ttl) = entrypoint due(t : Chain.ttl) =
+23
View File
@@ -0,0 +1,23 @@
contract ListComp =
entrypoint sample1() = [1,2,3]
entrypoint sample2() = [4,5]
entrypoint l1() = [x | x <- sample1()]
entrypoint l1_true() = [1,2,3]
entrypoint l2() = [x + y | x <- sample1(), y <- sample2()]
entrypoint l2_true() = [5,6,6,7,7,8]
entrypoint l3() = [x ++ y | x <- [[":)"] | x <- [1,2]]
, y <- [[":("]]]
entrypoint l3_true() = [[":)", ":("], [":)", ":("]]
entrypoint l4() = [(a, b, c) | let is_pit(a, b, c) = a*a + b*b == c*c
, let base = [1,2,3,4,5,6,7,8,9,10]
, a <- base
, b <- base, if (b >= a)
, c <- base, if (c >= b)
, if (is_pit(a, b, c))
]
entrypoint l4_true() = [(3, 4, 5), (6, 8, 10)]
+2
View File
@@ -0,0 +1,2 @@
contract BadComp =
entrypoint failing() = [x + 1 | x <- [1,2,3], let x = "XD"]
+2
View File
@@ -0,0 +1,2 @@
contract BadComp =
entrypoint failing() = [x | x <- [], if (3)]
+2
View File
@@ -0,0 +1,2 @@
contract ListCompBad =
entrypoint failing() = [x | x <- 1]
+7
View File
@@ -0,0 +1,7 @@
// This contract should be compiled with no_implicit_stdlib option.
// It should include Lists.aes implicitly however, because Option.aes depends on it.
include "Option.aes"
contract Test =
entrypoint i_should_build() =
List.is_empty(Option.to_list(None))
+2 -2
View File
@@ -93,8 +93,8 @@ contract Maps =
entrypoint tolist_state_s() = tolist_s(state.map_s) entrypoint tolist_state_s() = tolist_s(state.map_s)
// Map.from_list // Map.from_list
entrypoint fromlist_i(xs : list((int, pt))) = Map.from_list(xs) entrypoint fromlist_i(xs : list(int * pt)) = Map.from_list(xs)
entrypoint fromlist_s(xs : list((string, pt))) = Map.from_list(xs) entrypoint fromlist_s(xs : list(string * pt)) = Map.from_list(xs)
stateful entrypoint fromlist_state_i(xs) = put(state{ map_i = fromlist_i(xs) }) stateful entrypoint fromlist_state_i(xs) = put(state{ map_i = fromlist_i(xs) })
stateful entrypoint fromlist_state_s(xs) = put(state{ map_s = fromlist_s(xs) }) stateful entrypoint fromlist_state_s(xs) = put(state{ map_s = fromlist_s(xs) })
+1 -1
View File
@@ -3,7 +3,7 @@ namespace Lib =
entrypoint foo() = () entrypoint foo() = ()
contract Remote = contract Remote =
public function foo : () => () public function foo : () => unit
function bla() = () function bla() = ()
contract Contract = contract Contract =
+2 -2
View File
@@ -28,8 +28,8 @@ contract MultiSig =
let n = length(owners) + 1 let n = length(owners) + 1
{ nRequired = nRequired, { nRequired = nRequired,
nOwners = n, nOwners = n,
owners = Map.from_list(List.zip([1..n], caller() :: owners)), owners = Map.from_list(MyList.zip([1..n], caller() :: owners)),
ownerIndex = Map.from_list(List.zip(caller() :: owners, [1..n])) } ownerIndex = Map.from_list(MyList.zip(caller() :: owners, [1..n])) }
function lookup(map, key) = function lookup(map, key) =
switch(Map.get(key, map)) switch(Map.get(key, map))
+3 -3
View File
@@ -1,5 +1,5 @@
namespace List = namespace MyList =
function map1(f : 'a => 'b, xs : list('a)) = function map1(f : 'a => 'b, xs : list('a)) =
switch(xs) switch(xs)
@@ -14,8 +14,8 @@ namespace List =
contract Deadcode = contract Deadcode =
entrypoint inc1(xs : list(int)) : list(int) = entrypoint inc1(xs : list(int)) : list(int) =
List.map1((x) => x + 1, xs) MyList.map1((x) => x + 1, xs)
entrypoint inc2(xs : list(int)) : list(int) = entrypoint inc2(xs : list(int)) : list(int) =
List.map2((x) => x + 1, xs) MyList.map2((x) => x + 1, xs)
+4 -4
View File
@@ -60,23 +60,23 @@ contract Oracles =
res res
stateful entrypoint extendOracle(o : oracle_id, stateful entrypoint extendOracle(o : oracle_id,
ttl : ttl) : () = ttl : ttl) : unit =
Oracle.extend(o, ttl) Oracle.extend(o, ttl)
stateful entrypoint signedExtendOracle(o : oracle_id, stateful entrypoint signedExtendOracle(o : oracle_id,
sign : signature, // Signed oracle address sign : signature, // Signed oracle address
ttl : ttl) : () = ttl : ttl) : unit =
Oracle.extend(o, signature = sign, ttl) Oracle.extend(o, signature = sign, ttl)
stateful entrypoint respond(o : oracle_id, stateful entrypoint respond(o : oracle_id,
q : query_id, q : query_id,
r : answer_t) : () = r : answer_t) : unit =
Oracle.respond(o, q, r) Oracle.respond(o, q, r)
stateful entrypoint signedRespond(o : oracle_id, stateful entrypoint signedRespond(o : oracle_id,
q : query_id, q : query_id,
sign : signature, sign : signature,
r : answer_t) : () = r : answer_t) : unit =
Oracle.respond(o, q, signature = sign, r) Oracle.respond(o, q, signature = sign, r)
entrypoint getQuestion(o : oracle_id, entrypoint getQuestion(o : oracle_id,
+1 -1
View File
@@ -21,7 +21,7 @@ contract Oracles =
function respond(o : oracle_id, function respond(o : oracle_id,
q : query_id, q : query_id,
sign : signature, sign : signature,
r : answer_t) : () = r : answer_t) : unit =
Oracle.respond(o, q, signature = sign, r) Oracle.respond(o, q, signature = sign, r)
+3
View File
@@ -0,0 +1,3 @@
payable contract Test =
payable entrypoint foo(x : int) = ()
function bar() = 42
+3 -3
View File
@@ -47,7 +47,7 @@ module Voting : Voting = {
let init(proposalNames: args): state = let init(proposalNames: args): state =
{ chairPerson: caller(), { chairPerson: caller(),
voters: AddrMap.empty, voters: AddrMap.empty,
proposals: List.map((name) => {name: name, voteCount: 0}, proposalNames) proposals: MyList.map((name) => {name: name, voteCount: 0}, proposalNames)
}; };
/* Boilerplate */ /* Boilerplate */
@@ -73,7 +73,7 @@ module Voting : Voting = {
}; };
let addVote(candidate, weight) = { let addVote(candidate, weight) = {
let proposal = List.nth(state().proposals, candidate); let proposal = MyList.nth(state().proposals, candidate);
proposal.voteCount = proposal.voteCount + weight; proposal.voteCount = proposal.voteCount + weight;
}; };
@@ -121,6 +121,6 @@ module Voting : Voting = {
/* const */ /* const */
let currentTally() = let currentTally() =
List.map((p) => (p.name, p.voteCount), state().proposals); MyList.map((p) => (p.name, p.voteCount), state().proposals);
} }
+1 -1
View File
@@ -13,7 +13,7 @@ open Voting;
let print_tally() = { let print_tally() = {
let tally = call(other, () => currentTally()); let tally = call(other, () => currentTally());
List.map(((name, count)) => Printf.printf("%s: %d\n", name, count), tally); MyList.map(((name, count)) => Printf.printf("%s: %d\n", name, count), tally);
let winner = call(other, () => winnerName()); let winner = call(other, () => winnerName());
Printf.printf("Winner: %s\n", winner); Printf.printf("Winner: %s\n", winner);
}; };
+1 -1
View File
@@ -7,7 +7,7 @@ contract Remote2 =
contract Remote3 = contract Remote3 =
entrypoint get : () => int entrypoint get : () => int
entrypoint tick : () => () entrypoint tick : () => unit
contract RemoteCall = contract RemoteCall =
+1 -1
View File
@@ -1,7 +1,7 @@
contract Remote = contract Remote =
record rstate = { i : int, s : string, m : map(int, int) } record rstate = { i : int, s : string, m : map(int, int) }
entrypoint look_at : (rstate) => () entrypoint look_at : (rstate) => unit
entrypoint return_s : (bool) => string entrypoint return_s : (bool) => string
entrypoint return_m : (bool) => map(int, int) entrypoint return_m : (bool) => map(int, int)
entrypoint get : (rstate) => rstate entrypoint get : (rstate) => rstate
+2 -2
View File
@@ -1,6 +1,6 @@
contract Remote = contract Remote =
stateful entrypoint remote_spend : (address, int) => () stateful entrypoint remote_spend : (address, int) => unit
entrypoint remote_pure : int => int entrypoint remote_pure : int => int
contract Stateful = contract Stateful =
@@ -32,7 +32,7 @@ contract Stateful =
entrypoint ok4(a : address) = fail4(a) entrypoint ok4(a : address) = fail4(a)
// Lamdbas are checked at the construction site // Lamdbas are checked at the construction site
function fail5() : address => () = (a) => Chain.spend(a, 1000) function fail5() : address => unit = (a) => Chain.spend(a, 1000)
// .. so you can pass a stateful lambda to a non-stateful higher-order // .. so you can pass a stateful lambda to a non-stateful higher-order
// function: // function:
+3
View File
@@ -0,0 +1,3 @@
contract StdInc =
entrypoint test() = List.map((x) => Func.id(x), [1,2,3,4])
+2
View File
@@ -0,0 +1,2 @@
contract Stub =
entrypoint foo : (int) => int
+36
View File
@@ -0,0 +1,36 @@
contract TuplesMatch =
entrypoint tuplify3() = (t) => switch(t)
(x, y, z) => 3
entrypoint fst(p : int * string) =
switch(p)
(x, y) => x
entrypoint fst'(p : int * string) =
switch(p)
(x, _) => x
entrypoint snd(p : int * string) =
switch(p)
(x, y) => y
entrypoint snd'(p : int * string) =
switch(p)
(_, y) => y
entrypoint sum(p) =
switch(p)
(x, y) => x + y
entrypoint swap(p : int * string) =
switch(p)
(x, y) => (y, x)
entrypoint id(p : int * int * string) =
switch(p)
(x, y, z) => (x, y, z)
entrypoint nest(p : (int * int) * string) =
switch(p)
(xy, z) => switch(xy) (x, y) => (x, y, z)
entrypoint deep(p : (int * int) * (int * int)) =
switch(p)
((x, y), (z, w)) => (x, y, z, w)
entrypoint deep_sum(p : (int * int) * (int * int)) =
switch(p)
((x, y), (z, w)) => x + y + z + w
+4 -4
View File
@@ -8,7 +8,7 @@ contract VotingType =
function delegate : address => unit function delegate : address => unit
function vote : int => unit function vote : int => unit
function winnerName : unit => string function winnerName : unit => string
function currentTally : unit => list((string, int)) function currentTally : unit => list(string * int)
/* Contract implementation */ /* Contract implementation */
contract Voting = contract Voting =
@@ -36,7 +36,7 @@ contract Voting =
function init(proposalNames: list(string)): state = function init(proposalNames: list(string)): state =
{ chairPerson = caller(), { chairPerson = caller(),
voters = Map.empty, voters = Map.empty,
proposals = List.map((name) => {name = name, voteCount = 0}, proposalNames) } proposals = MyList.map((name) => {name = name, voteCount = 0}, proposalNames) }
function initVoter() = { weight = 1, vote = NotVoted} function initVoter() = { weight = 1, vote = NotVoted}
@@ -53,7 +53,7 @@ contract Voting =
_ => delegate _ => delegate
function addVote(candidate, weight) = function addVote(candidate, weight) =
let proposal = List.nth(state.proposals, candidate) let proposal = MyList.nth(state.proposals, candidate)
proposal{ voteCount = proposal.voteCount + weight } proposal{ voteCount = proposal.voteCount + weight }
function delegateVote(delegateTo: address, weight: uint) = function delegateVote(delegateTo: address, weight: uint) =
@@ -93,5 +93,5 @@ contract Voting =
// const // const
function currentTally() = function currentTally() =
List.map((p) => (p.name, p.voteCount), state.proposals) MyList.map((p) => (p.name, p.voteCount), state.proposals)