Compare commits

..

2 Commits

Author SHA1 Message Date
sennui 451b3636ac change aebytecode ver to the one from full node with my changes on top 2019-08-14 15:22:30 +02:00
sennui 514d80259e add extra argument to claim for bidding 2019-08-14 15:20:09 +02:00
94 changed files with 1290 additions and 3559 deletions
+1 -54
View File
@@ -9,57 +9,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
### Removed ### Removed
## [4.0.0-rc3] - 2019-09-12
### Added
- `Bytes.concat` and `Bytes.split` are added to be able to
(de-)construct byte arrays.
- `[a..b]` language construct, returning the list of numbers between
`a` and `b` (inclusive). Returns the empty list if `a` > `b`.
- [Standard libraries] (https://github.com/aeternity/protocol/blob/master/contracts/sophia_stdlib.md)
- Checks that `init` is not called from other functions.
### Changed
- Error messages are changed into a uniform format, and more helpful
messages have been added.
- `Crypto.<hash_fun>` and `String.<hash_fun>` for byte arrays now only
hash the actual byte array - not the internal ABI format.
- More strict checks for polymorphic oracles and higher order oracles
and entrypoints.
- `AENS.claim` is updated with a `NameFee` field - to be able to do
name auctions within contracts.
- Fixed a bug in `Bytes.to_str` for AEVM.
### 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
@@ -154,9 +103,7 @@ 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/v4.0.0-rc3...HEAD [Unreleased]: https://github.com/aeternity/aesophia/compare/v3.2.0...HEAD
[4.0.0-rc3]: https://github.com/aeternity/aesophia/compare/v4.0.0-rc1...v4.0.0-rc3
[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
-46
View File
@@ -1,46 +0,0 @@
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))
-214
View File
@@ -1,214 +0,0 @@
include "ListInternal.aes"
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_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(if(n < 0) "Negative index get" else "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)
function from_to(a : int, b : int) : list(int) = [a..b]
function from_to_step(a : int, b : int, s : int) : list(int) = from_to_step_(a, b, s, [])
private function from_to_step_(a, b, s, acc) =
if (a > b) reverse(acc) else from_to_step_(a + s, b, s, a :: acc)
/* 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(cmp : (('a, 'a) => bool), x : 'a, l : list('a)) : list('a) =
insert_by_(cmp, x, l, [])
private function insert_by_(cmp : (('a, 'a) => bool), x : 'a, l : list('a), acc : list('a)) : list('a) =
switch(l)
[] => reverse(x::acc)
h::t =>
if(cmp(x, h)) // x < h
reverse(acc) ++ (x::l)
else
insert_by_(cmp, x, t, h::acc)
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(l : list('a), f : 'a => unit) : unit =
switch(l)
[] => ()
e::l' =>
f(e)
foreach(l', f)
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) =
ListInternal.flat_map(f, l)
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 flatten(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)
-16
View File
@@ -1,16 +0,0 @@
namespace ListInternal =
// -- Flatmap ----------------------------------------------------------------
function flat_map(f : 'a => list('b), xs : list('a)) : list('b) =
switch(xs)
[] => []
x :: xs => f(x) ++ flat_map(f, xs)
// -- From..to ---------------------------------------------------------------
function from_to(a : int, b : int) : list(int) = from_to_(a, b, [])
private function from_to_(a, b, acc) =
if (a > b) acc else from_to_(a, b - 1, b :: acc)
-76
View File
@@ -1,76 +0,0 @@
include "List.aes"
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(o : option('a), f : 'a => unit) : 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)
-20
View File
@@ -1,20 +0,0 @@
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)
-37
View File
@@ -1,37 +0,0 @@
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)
+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,"a66dc0a"}}} {deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"adf3664"}}}
, {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, "4.0.0-rc3"}, {relx, [{release, {aesophia, "3.2.0"},
[aesophia, aebytecode, getopt]}, [aesophia, aebytecode, getopt]},
{dev_mode, true}, {dev_mode, true},
+2 -6
View File
@@ -1,21 +1,17 @@
{"1.1.0", {"1.1.0",
[{<<"aebytecode">>, [{<<"aebytecode">>,
{git,"https://github.com/aeternity/aebytecode.git", {git,"https://github.com/aeternity/aebytecode.git",
{ref,"a66dc0a97facdeaad7e5403018ad195d989e4793"}}, {ref,"adf3664dd03626c115756f79fa0e602fda24318d"}},
0}, 0},
{<<"aeserialization">>, {<<"aeserialization">>,
{git,"https://github.com/aeternity/aeserialization.git", {git,"https://github.com/aeternity/aeserialization.git",
{ref,"47aaa8f5434b365c50a35bfd1490340b19241991"}}, {ref,"816bf994ffb5cee218c3f22dc5fea296c9e0882e"}},
1}, 1},
{<<"base58">>, {<<"base58">>,
{git,"https://github.com/aeternity/erl-base58.git", {git,"https://github.com/aeternity/erl-base58.git",
{ref,"60a335668a60328a29f9731b67c4a0e9e3d50ab6"}}, {ref,"60a335668a60328a29f9731b67c4a0e9e3d50ab6"}},
2}, 2},
{<<"eblake2">>,{pkg,<<"eblake2">>,<<"1.0.0">>},0}, {<<"eblake2">>,{pkg,<<"eblake2">>,<<"1.0.0">>},0},
{<<"enacl">>,
{git,"https://github.com/aeternity/enacl.git",
{ref,"26180f42c0b3a450905d2efd8bc7fd5fd9cece75"}},
2},
{<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},0}, {<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},0},
{<<"jsx">>, {<<"jsx">>,
{git,"https://github.com/talentdeficit/jsx.git", {git,"https://github.com/talentdeficit/jsx.git",
+19 -14
View File
@@ -74,9 +74,21 @@ do_contract_interface(Type, ContractString, Options) ->
string -> do_render_aci_json(JArray) string -> do_render_aci_json(JArray)
end end
catch catch
throw:{error, Errors} -> {error, Errors} %% The compiler errors.
error:{parse_errors, Errors} ->
{error, join_errors("Parse errors", Errors, fun(E) -> E end)};
error:{type_errors, Errors} ->
{error, join_errors("Type errors", Errors, fun(E) -> E end)};
error:{code_errors, Errors} ->
{error, join_errors("Code errors", Errors,
fun (E) -> io_lib:format("~p", [E]) end)}
%% General programming errors in the compiler just signal error.
end. end.
join_errors(Prefix, Errors, Pfun) ->
Ess = [ Pfun(E) || E <- Errors ],
list_to_binary(string:join([Prefix|Ess], "\n")).
encode_contract(Contract = {contract, _, {con, _, Name}, _}) -> encode_contract(Contract = {contract, _, {con, _, Name}, _}) ->
C0 = #{name => encode_name(Name)}, C0 = #{name => encode_name(Name)},
@@ -101,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, payable => is_payable(Contract)}}; #{contract => C3#{functions => Fdefs}};
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),
@@ -113,14 +125,12 @@ 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))],
@@ -224,13 +234,12 @@ 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,
[payable(Payable), "contract ", io_lib:format("~s", [Name])," =\n", ["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",
@@ -240,8 +249,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, payable := Payable, arguments := As, returns := T}) -> decode_func(#{name := Name, arguments := As, returns := T}) ->
[" ", payable(Payable), "entrypoint ", io_lib:format("~s", [Name]), " : ", [" 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) ->
@@ -319,9 +328,6 @@ decode_tvars(Vs) ->
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 ->
@@ -346,7 +352,6 @@ 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.
File diff suppressed because it is too large Load Diff
+84 -279
View File
@@ -16,11 +16,11 @@
-type option() :: term(). -type option() :: term().
-type attribute() :: stateful | payable | pure | private. -type attribute() :: stateful | pure | private.
-type fun_name() :: {entrypoint, binary()} -type fun_name() :: {entrypoint, binary()}
| {local_fun, [string()]} | {local_fun, [string()]}
| event. | init | event.
-type var_name() :: string(). -type var_name() :: string().
-type sophia_name() :: [string()]. -type sophia_name() :: [string()].
@@ -32,9 +32,8 @@
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_verify_sig | crypto_verify_sig_secp256k1 | contract_to_address | crypto_ecverify | crypto_ecverify_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()}
@@ -50,7 +49,7 @@
| nil | nil
| {var, var_name()} | {var, var_name()}
| {def, fun_name(), [fexpr()]} | {def, fun_name(), [fexpr()]}
| {remote, [ftype()], ftype(), fexpr(), fun_name(), [fexpr()]} | {remote, fexpr(), fun_name(), [fexpr()]}
| {builtin, builtin(), [fexpr()]} | {builtin, builtin(), [fexpr()]}
| {con, arities(), tag(), [fexpr()]} | {con, arities(), tag(), [fexpr()]}
| {tuple, [fexpr()]} | {tuple, [fexpr()]}
@@ -65,9 +64,8 @@
%% 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, [ftype()], ftype(), fexpr(), fun_name()} | {remote_u, fexpr(), fun_name(), arity()}
| {builtin_u, builtin(), arity()} | {builtin_u, builtin(), arity()}
| {builtin_u, builtin(), arity(), [fexpr()]} %% Typerep arguments to be added after normal args.
| {lam, [var_name()], fexpr()}. | {lam, [var_name()], fexpr()}.
-type fsplit() :: {split, ftype(), var_name(), [fcase()]} -type fsplit() :: {split, ftype(), var_name(), [fcase()]}
@@ -110,8 +108,7 @@
-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()).
@@ -141,7 +138,6 @@
functions := #{ fun_name() => fun_def() } }. functions := #{ fun_name() => fun_def() } }.
-define(HASH_BYTES, 32). -define(HASH_BYTES, 32).
%% -- Entrypoint ------------------------------------------------------------- %% -- Entrypoint -------------------------------------------------------------
%% Main entrypoint. Takes typed syntax produced by aeso_ast_infer_types:infer/1,2 %% Main entrypoint. Takes typed syntax produced by aeso_ast_infer_types:infer/1,2
@@ -191,16 +187,15 @@ 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"], [{"verify_sig", 3}, {"verify_sig_secp256k1", 3}, {["Crypto"], [{"ecverify", 3}, {"ecverify_secp256k1", 3}, {"sha3", 1},
{"ecverify_secp256k1", 3}, {"ecrecover_secp256k1", 2}, {"sha256", 1}, {"blake2b", 1}]},
{"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}, {"concat", 2}, {"split", 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}, {"is_payable", 1}]} {["Address"], [{"to_str", 1}, {"is_oracle", 1}, {"is_contract", 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,
@@ -228,13 +223,10 @@ 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, Attrs, MainCon = {con, _, Main}, Decls}]) -> to_fcode(Env, [{contract, _, {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},
@@ -244,15 +236,11 @@ to_fcode(Env, [{contract, Attrs, MainCon = {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,
payable => Payable, functions => add_init_function(Env1,
functions => add_init_function(Env1, MainCon, StateType,
add_event_function(Env1, EventType, Funs)) }; add_event_function(Env1, EventType, Funs)) };
to_fcode(_Env, [NotContract]) ->
fcode_error({last_declaration_must_be_contract, NotContract});
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);
@@ -274,34 +262,30 @@ 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, _, Id, _}) -> decl_to_fcode(Env = #{context := {main_contract, _}}, {fun_decl, Ann, {id, _, Name}, _}) ->
case is_no_code(Env) of case proplists:get_value(no_code, maps:get(options, Env, []), false) of
false -> fcode_error({missing_definition, Id}); false -> fcode_error({missing_definition, Name, lists:keydelete(entrypoint, 1, Ann)});
true -> Env true -> Env
end; 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);
decl_to_fcode(Env = #{ functions := Funs }, {letfun, Ann, Id = {id, _, Name}, Args, Ret, Body}) -> decl_to_fcode(Env = #{ functions := Funs }, {letfun, Ann, {id, _, Name}, Args, Ret, Body}) ->
Attrs = get_attributes(Ann), Attrs = get_attributes(Ann),
FName = lookup_fun(Env, qname(Env, Name)), FName = lookup_fun(Env, qname(Env, Name)),
FArgs = args_to_fcode(Env, Args), FArgs = args_to_fcode(Env, Args),
FRet = type_to_fcode(Env, Ret),
FBody = expr_to_fcode(Env#{ vars => [X || {X, _} <- FArgs] }, Body), FBody = expr_to_fcode(Env#{ vars => [X || {X, _} <- FArgs] }, Body),
[ ensure_first_order_entrypoint(Ann, Id, Args, Ret, FArgs, FRet)
|| aeso_syntax:get_ann(entrypoint, Ann, false) ],
Def = #{ attrs => Attrs, Def = #{ attrs => Attrs,
args => FArgs, args => FArgs,
return => FRet, return => type_to_fcode(Env, Ret),
body => FBody }, body => FBody },
NewFuns = Funs#{ FName => Def }, NewFuns = Funs#{ FName => Def },
Env#{ functions := NewFuns }. Env#{ functions := NewFuns }.
-spec typedef_to_fcode(env(), aeso_syntax:id(), [aeso_syntax:tvar()], aeso_syntax:typedef()) -> env(). -spec typedef_to_fcode(env(), aeso_syntax:id(), [aeso_syntax:tvar()], aeso_syntax:typedef()) -> env().
typedef_to_fcode(Env, Id = {id, _, Name}, Xs, Def) -> typedef_to_fcode(Env, {id, _, Name}, Xs, Def) ->
check_state_and_event_types(Env, Id, Xs),
Q = qname(Env, Name), Q = qname(Env, Name),
FDef = fun(Args) when length(Args) == length(Xs) -> FDef = fun(Args) ->
Sub = maps:from_list(lists:zip([X || {tvar, _, X} <- Xs], Args)), Sub = maps:from_list(lists:zip([X || {tvar, _, X} <- Xs], Args)),
case Def of case Def of
{record_t, Fields} -> {todo, Xs, Args, record_t, Fields}; {record_t, Fields} -> {todo, Xs, Args, record_t, Fields};
@@ -312,9 +296,7 @@ typedef_to_fcode(Env, Id = {id, _, Name}, Xs, Def) ->
end || Con <- Cons ], end || Con <- Cons ],
{variant, FCons}; {variant, FCons};
{alias_t, Type} -> {todo, Xs, Args, alias_t, Type} {alias_t, Type} -> {todo, Xs, Args, alias_t, Type}
end; end end,
(Args) -> internal_error({type_arity_mismatch, Name, length(Args), length(Xs)})
end,
Constructors = Constructors =
case Def of case Def of
{variant_t, Cons} -> {variant_t, Cons} ->
@@ -335,14 +317,6 @@ typedef_to_fcode(Env, Id = {id, _, Name}, Xs, Def) ->
end, end,
bind_type(Env2, Q, FDef). bind_type(Env2, Q, FDef).
check_state_and_event_types(#{ context := {main_contract, _} }, Id, [_ | _]) ->
case Id of
{id, _, "state"} -> fcode_error({parameterized_state, Id});
{id, _, "event"} -> fcode_error({parameterized_event, Id});
_ -> ok
end;
check_state_and_event_types(_, _, _) -> ok.
-spec type_to_fcode(env(), aeso_syntax:type()) -> ftype(). -spec type_to_fcode(env(), aeso_syntax:type()) -> ftype().
type_to_fcode(Env, Type) -> type_to_fcode(Env, Type) ->
type_to_fcode(Env, #{}, Type). type_to_fcode(Env, #{}, Type).
@@ -407,31 +381,7 @@ expr_to_fcode(_Env, _Type, {bytes, _, B}) -> {lit, {bytes, B}};
%% Variables %% Variables
expr_to_fcode(Env, _Type, {id, _, X}) -> resolve_var(Env, [X]); expr_to_fcode(Env, _Type, {id, _, X}) -> resolve_var(Env, [X]);
expr_to_fcode(Env, Type, {qid, Ann, X}) -> expr_to_fcode(Env, _Type, {qid, _, X}) -> resolve_var(Env, X);
case resolve_var(Env, X) of
{builtin_u, B, Ar} when B =:= oracle_query;
B =:= oracle_get_question;
B =:= oracle_get_answer;
B =:= oracle_respond;
B =:= oracle_register;
B =:= oracle_check;
B =:= oracle_check_query ->
OType = get_oracle_type(B, Type),
{oracle, QType, RType} = type_to_fcode(Env, OType),
validate_oracle_type(Ann, OType, QType, RType),
TypeArgs = [{lit, {typerep, QType}}, {lit, {typerep, RType}}],
{builtin_u, B, Ar, TypeArgs};
{builtin_u, B = aens_resolve, Ar} ->
{fun_t, _, _, _, ResType} = Type,
AensType = type_to_fcode(Env, ResType),
validate_aens_resolve_type(Ann, ResType, AensType),
TypeArgs = [{lit, {typerep, AensType}}],
{builtin_u, B, Ar, TypeArgs};
{builtin_u, B = bytes_split, Ar} ->
{fun_t, _, _, _, {tuple_t, _, [{bytes_t, _, N}, _]}} = Type,
{builtin_u, B, Ar, [{lit, {int, N}}]};
Other -> Other
end;
%% Constructors %% Constructors
expr_to_fcode(Env, Type, {C, _, _} = Con) when C == con; C == qcon -> expr_to_fcode(Env, Type, {C, _, _} = Con) when C == con; C == qcon ->
@@ -441,7 +391,7 @@ expr_to_fcode(Env, _Type, {app, _, {typed, _, {C, _, _} = Con, _}, Args}) when C
Arity = lists:nth(I + 1, Arities), Arity = lists:nth(I + 1, Arities),
case length(Args) == Arity of case length(Args) == Arity of
true -> {con, Arities, I, [expr_to_fcode(Env, Arg) || Arg <- Args]}; true -> {con, Arities, I, [expr_to_fcode(Env, Arg) || Arg <- Args]};
false -> internal_error({constructor_arity_mismatch, Con, length(Args), Arity}) false -> fcode_error({constructor_arity_mismatch, Con, length(Args), Arity})
end; end;
%% Tuples %% Tuples
@@ -454,10 +404,9 @@ 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, _, _, Args, Ret} = Type, {fun_t, _, Named, Args, _} = Type,
FArgs = [type_to_fcode(Env, Arg) || Arg <- Args], Arity = length(Named) + length(Args),
{remote_u, FArgs, type_to_fcode(Env, Ret), expr_to_fcode(Env, Rec), {remote_u, expr_to_fcode(Env, Rec), {entrypoint, list_to_binary(X)}, Arity};
{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;
@@ -495,27 +444,6 @@ 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, {app, _, {'..', _}, [A, B]}) ->
{def_u, FromTo, _} = resolve_fun(Env, ["ListInternal", "from_to"]),
{def, FromTo, [expr_to_fcode(Env, A), expr_to_fcode(Env, B)]};
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, ["ListInternal", "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),
@@ -552,14 +480,30 @@ expr_to_fcode(Env, _Type, {app, _Ann, {Op, _}, [A]}) when is_atom(Op) ->
end; end;
%% Function calls %% Function calls
expr_to_fcode(Env, _Type, {app, _, Fun = {typed, _, _, {fun_t, _, NamedArgsT, _, _}}, Args}) -> expr_to_fcode(Env, Type, {app, _Ann, Fun = {typed, _, _, {fun_t, _, NamedArgsT, _, _}}, Args}) ->
Args1 = get_named_args(NamedArgsT, Args), Args1 = get_named_args(NamedArgsT, Args),
FArgs = [expr_to_fcode(Env, Arg) || Arg <- Args1], FArgs = [expr_to_fcode(Env, Arg) || Arg <- Args1],
case expr_to_fcode(Env, Fun) of case expr_to_fcode(Env, Fun) of
{builtin_u, B, _Ar, TypeArgs} -> builtin_to_fcode(B, FArgs ++ TypeArgs); {builtin_u, B, _} when B =:= oracle_query;
B =:= oracle_get_question;
B =:= oracle_get_answer;
B =:= oracle_respond;
B =:= oracle_register;
B =:= oracle_check;
B =:= oracle_check_query ->
%% Get the type of the oracle from the args or the expression itself
OType = get_oracle_type(B, Type, Args1),
{oracle, QType, RType} = type_to_fcode(Env, OType),
TypeArgs = [{lit, {typerep, QType}}, {lit, {typerep, RType}}],
builtin_to_fcode(B, FArgs ++ TypeArgs);
{builtin_u, B, _} when B =:= aens_resolve ->
%% Get the type we are assuming the name resolves to
AensType = type_to_fcode(Env, Type),
TypeArgs = [{lit, {typerep, AensType}}],
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, ArgsT, RetT, Ct, RFun} -> {remote, ArgsT, RetT, Ct, RFun, FArgs}; {remote_u, Ct, RFun, _Ar} -> {remote, 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
@@ -622,60 +566,13 @@ make_if(Cond, Then, Else) ->
{'let', X, Cond, make_if({var, X}, Then, Else)}. {'let', X, Cond, make_if({var, X}, Then, Else)}.
get_oracle_type(oracle_register, {fun_t, _, _, _, OType}) -> OType; get_oracle_type(oracle_register, OType, _Args) -> OType;
get_oracle_type(oracle_query, {fun_t, _, _, [OType | _], _}) -> OType; get_oracle_type(oracle_query, _Type, [{typed, _, _Expr, OType} | _]) -> OType;
get_oracle_type(oracle_get_question, {fun_t, _, _, [OType | _], _}) -> OType; get_oracle_type(oracle_get_question, _Type, [{typed, _, _Expr, OType} | _]) -> OType;
get_oracle_type(oracle_get_answer, {fun_t, _, _, [OType | _], _}) -> OType; get_oracle_type(oracle_get_answer, _Type, [{typed, _, _Expr, OType} | _]) -> OType;
get_oracle_type(oracle_check, {fun_t, _, _, [OType | _], _}) -> OType; get_oracle_type(oracle_check, _Type, [{typed, _, _Expr, OType}]) -> OType;
get_oracle_type(oracle_check_query, {fun_t, _, _, [OType | _], _}) -> OType; get_oracle_type(oracle_check_query, _Type, [{typed, _, _Expr, OType} | _]) -> OType;
get_oracle_type(oracle_respond, {fun_t, _, _, [OType | _], _}) -> OType. get_oracle_type(oracle_respond, _Type, [_, {typed, _,_Expr, OType} | _]) -> OType.
validate_oracle_type(Ann, Type, QType, RType) ->
ensure_monomorphic(QType, {invalid_oracle_type, polymorphic, query, Ann, Type}),
ensure_monomorphic(RType, {invalid_oracle_type, polymorphic, response, Ann, Type}),
ensure_first_order(QType, {invalid_oracle_type, higher_order, query, Ann, Type}),
ensure_first_order(RType, {invalid_oracle_type, higher_order, response, Ann, Type}),
ok.
validate_aens_resolve_type(Ann, {app_t, _, _, [Type]}, {variant, [[], [FType]]}) ->
case FType of
string -> ok;
address -> ok;
contract -> ok;
{oracle, _, _} -> ok;
oracle_query -> ok;
_ -> fcode_error({invalid_aens_resolve_type, Ann, Type})
end.
ensure_first_order_entrypoint(Ann, Id = {id, _, Name}, Args, Ret, FArgs, FRet) ->
[ ensure_first_order(FT, {invalid_entrypoint, higher_order, Ann1, Id, {argument, X, T}})
|| {{arg, Ann1, X, T}, {_, FT}} <- lists:zip(Args, FArgs) ],
[ ensure_first_order(FRet, {invalid_entrypoint, higher_order, Ann, Id, {result, Ret}})
|| Name /= "init" ], %% init can return higher-order values, since they're written to the store
%% rather than being returned.
ok.
ensure_monomorphic(Type, Err) ->
case is_monomorphic(Type) of
true -> ok;
false -> fcode_error(Err)
end.
ensure_first_order(Type, Err) ->
case is_first_order(Type) of
true -> ok;
false -> fcode_error(Err)
end.
is_monomorphic({tvar, _}) -> false;
is_monomorphic(Ts) when is_list(Ts) -> lists:all(fun is_monomorphic/1, Ts);
is_monomorphic(Tup) when is_tuple(Tup) -> is_monomorphic(tuple_to_list(Tup));
is_monomorphic(_) -> true.
is_first_order({function, _, _}) -> false;
is_first_order(Ts) when is_list(Ts) -> lists:all(fun is_first_order/1, Ts);
is_first_order(Tup) when is_tuple(Tup) -> is_first_order(tuple_to_list(Tup));
is_first_order(_) -> true.
%% -- Pattern matching -- %% -- Pattern matching --
@@ -903,10 +800,8 @@ 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_verify_sig, bits_difference, int_to_str, address_to_str, crypto_ecverify,
crypto_verify_sig_secp256k1, crypto_sha3, crypto_sha256, crypto_blake2b, crypto_ecverify_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]});
@@ -932,29 +827,16 @@ builtin_to_fcode(Builtin, Args) ->
%% -- Init function -- %% -- Init function --
add_init_function(Env, Main, StateType, Funs0) -> add_init_function(_Env, Funs) ->
case is_no_code(Env) of
true -> Funs0;
false ->
Funs = add_default_init_function(Env, Main, StateType, Funs0),
InitName = {entrypoint, <<"init">>},
InitFun = #{ body := InitBody} = maps:get(InitName, Funs),
Funs#{ InitName => InitFun#{ return => {tuple, []},
body => {builtin, set_state, [InitBody]} } }
end.
add_default_init_function(_Env, Main, StateType, Funs) ->
InitName = {entrypoint, <<"init">>}, InitName = {entrypoint, <<"init">>},
InitFun = #{ args := InitArgs } =
case maps:get(InitName, Funs, none) of case maps:get(InitName, Funs, none) of
%% Only add default init function if state is unit. none -> #{ attrs => [], args => [], return => {tuple, []}, body => {tuple, []} };
none when StateType == {tuple, []} -> Info -> Info
Funs#{ InitName => #{attrs => [], end,
args => [], Vars = [ {var, X} || {X, _} <- InitArgs ],
return => {tuple, []}, Funs#{ init => InitFun#{ return => {tuple, []},
body => {tuple, []}} }; body => {builtin, set_state, [{def, InitName, Vars}]} } }.
none -> fcode_error({missing_init_function, Main});
_ -> Funs
end.
%% -- Event function -- %% -- Event function --
@@ -1034,26 +916,20 @@ make_closure(FVs, Xs, Body) ->
lambda_lift_expr({lam, Xs, Body}) -> lambda_lift_expr({lam, Xs, Body}) ->
FVs = free_vars({lam, Xs, Body}), FVs = free_vars({lam, Xs, Body}),
make_closure(FVs, Xs, lambda_lift_expr(Body)); make_closure(FVs, Xs, lambda_lift_expr(Body));
lambda_lift_expr(UExpr) when element(1, UExpr) == def_u; element(1, UExpr) == builtin_u -> lambda_lift_expr({Tag, F, Ar}) when Tag == def_u; Tag == builtin_u ->
[Tag, F, Ar | _] = tuple_to_list(UExpr),
ExtraArgs = case UExpr of
{builtin_u, _, _, TypeArgs} -> TypeArgs;
_ -> []
end,
Xs = [ lists:concat(["arg", I]) || I <- lists:seq(1, Ar) ], Xs = [ lists:concat(["arg", I]) || I <- lists:seq(1, Ar) ],
Args = [{var, X} || X <- Xs] ++ ExtraArgs, Args = [{var, X} || X <- Xs],
Body = case Tag of Body = case Tag of
builtin_u -> builtin_to_fcode(F, Args); builtin_u -> builtin_to_fcode(F, Args);
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, ArgsT, RetT, Ct, F}) -> lambda_lift_expr({remote_u, Ct, F, Ar}) ->
FVs = free_vars(Ct), FVs = free_vars(Ct),
Ct1 = lambda_lift_expr(Ct), Ct1 = lambda_lift_expr(Ct),
GasAndValueArgs = 2, Xs = [ lists:concat(["arg", I]) || I <- lists:seq(1, Ar) ],
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, ArgsT, RetT, Ct1, F, Args}); make_closure(FVs, Xs, {remote, Ct1, F, Args});
lambda_lift_expr(Expr) -> lambda_lift_expr(Expr) ->
case Expr of case Expr of
{lit, _} -> Expr; {lit, _} -> Expr;
@@ -1062,7 +938,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, ArgsT, RetT, Ct, F, As} -> {remote, ArgsT, RetT, lambda_lift_expr(Ct), F, lambda_lift_exprs(As)}; {remote, Ct, F, As} -> {remote, 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};
@@ -1088,16 +964,13 @@ 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 }) ->
Code1 = Code#{ functions := maps:map(fun(Name, Def) -> optimize_fun(Code, Name, Def) end, Funs) }, 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
@@ -1110,33 +983,6 @@ 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 --
@@ -1148,7 +994,7 @@ lookup_type(Env, {qid, _, Name}, Args) ->
lookup_type(Env, Name, Args); lookup_type(Env, Name, Args);
lookup_type(Env, Name, Args) -> lookup_type(Env, Name, Args) ->
case lookup_type(Env, Name, Args, not_found) of case lookup_type(Env, Name, Args, not_found) of
not_found -> internal_error({unknown_type, Name}); not_found -> error({unknown_type, Name});
Type -> Type Type -> Type
end. end.
@@ -1233,7 +1079,7 @@ resolve_var(Env, Q) -> resolve_fun(Env, Q).
resolve_fun(#{ fun_env := Funs, builtins := Builtin }, Q) -> resolve_fun(#{ fun_env := Funs, builtins := Builtin }, Q) ->
case {maps:get(Q, Funs, not_found), maps:get(Q, Builtin, not_found)} of case {maps:get(Q, Funs, not_found), maps:get(Q, Builtin, not_found)} of
{not_found, not_found} -> internal_error({unbound_variable, Q}); {not_found, not_found} -> fcode_error({unbound_variable, Q});
{_, {B, none}} -> {builtin, B, []}; {_, {B, none}} -> {builtin, B, []};
{_, {B, Ar}} -> {builtin_u, B, Ar}; {_, {B, Ar}} -> {builtin_u, B, Ar};
{{Fun, Ar}, _} -> {def_u, Fun, Ar} {{Fun, Ar}, _} -> {def_u, Fun, Ar}
@@ -1287,11 +1133,10 @@ 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, _, _} -> [];
{builtin_u, _, _, _} -> []; %% Typereps are always literals
{con, _, _, As} -> free_vars(As); {con, _, _, As} -> free_vars(As);
{tuple, As} -> free_vars(As); {tuple, As} -> free_vars(As);
{proj, A, _} -> free_vars(A); {proj, A, _} -> free_vars(A);
@@ -1307,35 +1152,6 @@ free_vars(Expr) ->
{'case', P, A} -> free_vars(A) -- lists:sort(fsplit_pat_vars(P)) {'case', P, A} -> free_vars(A) -- lists:sort(fsplit_pat_vars(P))
end. 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, _, _} -> [];
{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.
get_named_args(NamedArgsT, Args) -> get_named_args(NamedArgsT, Args) ->
IsNamed = fun({named_arg, _, _, _}) -> true; IsNamed = fun({named_arg, _, _, _}) -> true;
(_) -> false end, (_) -> false end,
@@ -1361,9 +1177,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;
{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};
@@ -1467,23 +1282,15 @@ 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 --
indexed(Xs) -> indexed(Xs) ->
lists:zip(lists:seq(1, length(Xs)), Xs). lists:zip(lists:seq(1, length(Xs)), Xs).
-dialyzer({nowarn_function, [fcode_error/1, internal_error/1]}). fcode_error(Err) ->
error(Err).
fcode_error(Error) ->
aeso_errors:throw(aeso_code_errors:format(Error)).
internal_error(Error) ->
Msg = lists:flatten(io_lib:format("~p\n", [Error])),
aeso_errors:throw(aeso_errors:new(internal_error, aeso_errors:pos(0, 0), Msg)).
%% -- Pretty printing -------------------------------------------------------- %% -- Pretty printing --------------------------------------------------------
@@ -1501,6 +1308,7 @@ 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(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, ".")).
@@ -1508,8 +1316,7 @@ 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) when is_list(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_text(A) when is_atom(A) -> prettypr:text(atom_to_list(A)).
pp_text(N) when is_integer(N) -> prettypr:text(integer_to_list(N)).
pp_int(I) -> prettypr:text(integer_to_list(I)). pp_int(I) -> prettypr:text(integer_to_list(I)).
@@ -1583,14 +1390,12 @@ pp_fexpr({'let', X, A, B}) ->
pp_fexpr(B)]); pp_fexpr(B)]);
pp_fexpr({builtin_u, B, N}) -> 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_u, B, N, TypeArgs}) ->
pp_beside([pp_text(B), pp_text("@"), pp_fexpr({tuple, TypeArgs}), 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, ArgsT, RetT, Ct, Fun}) -> pp_fexpr({remote_u, Ct, Fun, Ar}) ->
pp_beside([pp_fexpr(Ct), pp_text("."), pp_fun_name(Fun), pp_text(" : "), pp_ftype({function, ArgsT, RetT})]); pp_beside([pp_fexpr(Ct), pp_text("."), pp_fun_name(Fun), pp_text("/"), pp_int(Ar)]);
pp_fexpr({remote, ArgsT, RetT, Ct, Fun, As}) -> pp_fexpr({remote, Ct, 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_call(pp_beside([pp_fexpr(Ct), pp_text("."), pp_fun_name(Fun)]), 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).
+314 -464
View File
File diff suppressed because it is too large Load Diff
+21 -128
View File
@@ -10,7 +10,6 @@
-module(aeso_builtins). -module(aeso_builtins).
-export([ builtin_function/1 -export([ builtin_function/1
, bytes_to_raw_string/2
, check_event_type/1 , check_event_type/1
, used_builtins/1 ]). , used_builtins/1 ]).
@@ -45,7 +44,7 @@ builtin_deps1(addr_to_str) -> [{baseX_int, 58}];
builtin_deps1({baseX_int, X}) -> [{baseX_int_pad, X}]; builtin_deps1({baseX_int, X}) -> [{baseX_int_pad, X}];
builtin_deps1({baseX_int_pad, X}) -> [{baseX_int_encode, X}]; builtin_deps1({baseX_int_pad, X}) -> [{baseX_int_encode, X}];
builtin_deps1({baseX_int_encode, X}) -> [{baseX_int_encode_, X}, {baseX_tab, X}, {baseX_digits, X}]; builtin_deps1({baseX_int_encode, X}) -> [{baseX_int_encode_, X}, {baseX_tab, X}, {baseX_digits, X}];
builtin_deps1({bytes_to_str, _}) -> [bytes_to_str_worker, bytes_to_str_worker_x]; builtin_deps1({bytes_to_str, _}) -> [bytes_to_str_worker];
builtin_deps1(string_reverse) -> [string_reverse_]; builtin_deps1(string_reverse) -> [string_reverse_];
builtin_deps1(require) -> [abort]; builtin_deps1(require) -> [abort];
builtin_deps1(_) -> []. builtin_deps1(_) -> [].
@@ -90,13 +89,7 @@ option_some(X) -> {tuple, [{integer, 1}, X]}.
-define(BSL(X, B), op('bsl', ?MUL(B, 8), X)). -define(BSL(X, B), op('bsl', ?MUL(B, 8), X)).
-define(BSR(X, B), op('bsr', ?MUL(B, 8), X)). -define(BSR(X, B), op('bsr', ?MUL(B, 8), X)).
op(Op, A, B) -> simpl({binop, Op, operand(A), operand(B)}). op(Op, A, B) -> {binop, Op, operand(A), operand(B)}.
%% We generate a lot of B * 8 for integer B from BSL and BSR.
simpl({binop, '*', {integer, A}, {integer, B}}) when A >= 0, B >= 0, A * B < 1 bsl 256 ->
{integer, A * B};
simpl(Op) -> Op.
operand(A) when is_atom(A) -> v(A); operand(A) when is_atom(A) -> v(A);
operand(I) when is_integer(I) -> {integer, I}; operand(I) when is_integer(I) -> {integer, I};
@@ -168,10 +161,7 @@ builtin_function(BF) ->
{baseX_int_encode_, X} -> bfun(BF, builtin_baseX_int_encode_(X)); {baseX_int_encode_, X} -> bfun(BF, builtin_baseX_int_encode_(X));
{bytes_to_int, N} -> bfun(BF, builtin_bytes_to_int(N)); {bytes_to_int, N} -> bfun(BF, builtin_bytes_to_int(N));
{bytes_to_str, N} -> bfun(BF, builtin_bytes_to_str(N)); {bytes_to_str, N} -> bfun(BF, builtin_bytes_to_str(N));
{bytes_concat, A, B} -> bfun(BF, builtin_bytes_concat(A, B));
{bytes_split, A, B} -> bfun(BF, builtin_bytes_split(A, B));
bytes_to_str_worker -> bfun(BF, builtin_bytes_to_str_worker()); bytes_to_str_worker -> bfun(BF, builtin_bytes_to_str_worker());
bytes_to_str_worker_x -> bfun(BF, builtin_bytes_to_str_worker_x());
string_reverse -> bfun(BF, builtin_string_reverse()); string_reverse -> bfun(BF, builtin_string_reverse());
string_reverse_ -> bfun(BF, builtin_string_reverse_()) string_reverse_ -> bfun(BF, builtin_string_reverse_())
end. end.
@@ -522,60 +512,40 @@ builtin_bytes_to_int(N) when N > 32 ->
end, end,
{[{"b", pointer}], Body, word}. {[{"b", pointer}], Body, word}.
%% Two versions of this helper function, worker for sections not even 16 bytes long
%% and worker_x for the full sized chunks.
builtin_bytes_to_str_worker_x() ->
<<Tab:256>> = <<"0123456789ABCDEF________________">>,
{[{"w", word}, {"offs", word}, {"acc", word}],
{ifte, ?EQ(offs, 16), {seq, [?V(acc), {inline_asm, [?A(?MSIZE), ?A(?MSTORE), ?A(?MSIZE)]}]},
?LET(b, ?BYTE(offs, w),
?LET(lo, ?BYTE(?MOD(b, 16), Tab),
?LET(hi, ?BYTE(op('bsr', 4 , b), Tab),
?call(bytes_to_str_worker_x, [?V(w), ?ADD(offs, 1), ?ADD(?BSL(acc, 2), ?ADD(?BSL(hi, 1), lo))]))))
},
word}.
builtin_bytes_to_str_worker() -> builtin_bytes_to_str_worker() ->
<<Tab:256>> = <<"0123456789ABCDEF________________">>, <<Tab:256>> = <<"0123456789ABCDEF________________">>,
{[{"w", word}, {"offs", word}, {"acc", word}, {"stop", word}], {[{"w", word}, {"offs", word}, {"acc", word}],
{ifte, ?EQ(stop, offs), {seq, [?BSL(acc, ?MUL(2, ?SUB(16, offs))), {inline_asm, [?A(?MSIZE), ?A(?MSTORE), ?A(?MSIZE)]}]}, {seq, [{ifte, ?AND(?GT(offs, 0), ?EQ(0, ?MOD(offs, 16))),
{seq, [?V(acc), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]}]},
{inline_asm, []}},
{ifte, ?EQ(offs, 32), {inline_asm, [?A(?MSIZE)]},
?LET(b, ?BYTE(offs, w), ?LET(b, ?BYTE(offs, w),
?LET(lo, ?BYTE(?MOD(b, 16), Tab), ?LET(lo, ?BYTE(?MOD(b, 16), Tab),
?LET(hi, ?BYTE(op('bsr', 4 , b), Tab), ?LET(hi, ?BYTE(op('bsr', 4 , b), Tab),
?call(bytes_to_str_worker, [?V(w), ?ADD(offs, 1), ?ADD(?BSL(acc, 2), ?ADD(?BSL(hi, 1), lo)), ?V(stop)])))) ?call(bytes_to_str_worker,
}, [?V(w), ?ADD(offs, 1), ?ADD(?BSL(acc, 2), ?ADD(?BSL(hi, 1), lo))]))))
}
]},
word}. word}.
builtin_bytes_to_str_body(Var, N) when N < 16 ->
[?call(bytes_to_str_worker, [?V(Var), ?I(0), ?I(0), ?I(N)])];
builtin_bytes_to_str_body(Var, 16) ->
[?call(bytes_to_str_worker_x, [?V(Var), ?I(0), ?I(0)])];
builtin_bytes_to_str_body(Var, N) when N < 32 ->
builtin_bytes_to_str_body(Var, 16) ++ [{inline_asm, [?A(?POP)]}] ++
[?call(bytes_to_str_worker, [?BSL(Var, 16), ?I(0), ?I(0), ?I(N - 16)])];
builtin_bytes_to_str_body(Var, 32) ->
builtin_bytes_to_str_body(Var, 16) ++ [{inline_asm, [?A(?POP)]}] ++
[?call(bytes_to_str_worker_x, [?BSL(Var, 16), ?I(0), ?I(0)])];
builtin_bytes_to_str_body(Var, N) when N > 32 ->
WholeWords = ((N + 31) div 32) - 1,
lists:append(
[ [?DEREF(w, ?ADD(Var, 32 * I), {seq, builtin_bytes_to_str_body(w, 32)}), {inline_asm, [?A(?POP)]}]
|| I <- lists:seq(0, WholeWords - 1) ]) ++
[ ?DEREF(w, ?ADD(Var, 32 * WholeWords), {seq, builtin_bytes_to_str_body(w, N - WholeWords * 32)}) ].
builtin_bytes_to_str(N) when N =< 32 -> builtin_bytes_to_str(N) when N =< 32 ->
{[{"w", word}], {[{"w", word}],
?LET(ret, {inline_asm, [?A(?MSIZE)]}, ?LET(ret, {inline_asm, [?A(?MSIZE)]},
{seq, [?I(N * 2), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]}] ++ {seq, [?I(N * 2), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
builtin_bytes_to_str_body(w, N) ++ ?call(bytes_to_str_worker, [?V(w), ?I(0), ?I(0)]),
[{inline_asm, [?A(?POP)]}, ?V(ret)]}), {inline_asm, [?A(?POP)]},
?V(ret)]}),
string}; string};
builtin_bytes_to_str(N) when N > 32 -> builtin_bytes_to_str(N) when N > 32 ->
Work = fun(I) ->
[?DEREF(w, ?ADD(p, 32 * I), ?call(bytes_to_str_worker, [?V(w), ?I(0), ?I(0)])),
{inline_asm, [?A(?POP)]}]
end,
{[{"p", pointer}], {[{"p", pointer}],
?LET(ret, {inline_asm, [?A(?MSIZE)]}, ?LET(ret, {inline_asm, [?A(?MSIZE)]},
{seq, [?I(N * 2), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]}] ++ {seq, [?I(N * 2), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]}] ++
builtin_bytes_to_str_body(p, N) ++ lists:append([ Work(I) || I <- lists:seq(0, (N + 31) div 32 - 1) ]) ++
[{inline_asm, [?A(?POP)]}, ?V(ret)]}), [?V(ret)]}),
string}. string}.
builtin_string_reverse() -> builtin_string_reverse() ->
@@ -605,80 +575,3 @@ builtin_string_reverse_() ->
builtin_addr_to_str() -> builtin_addr_to_str() ->
{[{"a", word}], ?call({baseX_int, 58}, [?V(a)]), word}. {[{"a", word}], ?call({baseX_int, 58}, [?V(a)]), word}.
%% At most one word
%% | ..... | ========= | ........ |
%% Offs ^ ^- Len -^ TotalLen ^
bytes_slice(Offs, Len, TotalLen, Bytes) when TotalLen =< 32 ->
%% Bytes are packed into a single word
Masked =
case Offs of
0 -> Bytes;
_ -> ?MOD(Bytes, 1 bsl ((32 - Offs) * 8))
end,
Unpadded =
case 32 - (Offs + Len) of
0 -> Masked;
N -> ?BSR(Masked, N)
end,
case Len of
32 -> Unpadded;
_ -> ?BSL(Unpadded, 32 - Len)
end;
bytes_slice(Offs, Len, TotalLen, Bytes) when TotalLen > 32 ->
%% Bytes is a pointer to memory. The VM can read at non-aligned addresses.
%% Might read one word more than necessary.
Word = op('!', Offs, Bytes),
case Len == 32 of
true -> Word;
_ -> ?BSL(?BSR(Word, 32 - Len), 32 - Len)
end.
builtin_bytes_concat(A, B) ->
Type = fun(N) when N =< 32 -> word; (_) -> pointer end,
MkBytes = fun([W]) -> W;
(Ws) -> {tuple, Ws} end,
Words = fun(N) -> (N + 31) div 32 end,
WordsRes = Words(A + B),
Word = fun(I) when 32 * (I + 1) =< A -> bytes_slice(I * 32, 32, A, ?V(a));
(I) when 32 * I < A ->
Len = A rem 32,
Hi = bytes_slice(32 * I, Len, A, ?V(a)),
Lo = bytes_slice(0, min(32 - Len, B), B, ?V(b)),
?ADD(Hi, ?BSR(Lo, Len));
(I) ->
Offs = 32 * I - A,
Len = min(32, B - Offs),
bytes_slice(Offs, Len, B, ?V(b))
end,
Body =
case {A, B} of
{0, _} -> ?V(b);
{_, 0} -> ?V(a);
_ -> MkBytes([ Word(I) || I <- lists:seq(0, WordsRes - 1) ])
end,
{[{"a", Type(A)}, {"b", Type(B)}], Body, Type(A + B)}.
builtin_bytes_split(A, B) ->
Type = fun(N) when N =< 32 -> word; (_) -> pointer end,
MkBytes = fun([W]) -> W;
(Ws) -> {tuple, Ws} end,
Word = fun(I, Max) ->
bytes_slice(I, min(32, Max - I), A + B, ?V(c))
end,
Body =
case {A, B} of
{0, _} -> [?I(0), ?V(c)];
{_, 0} -> [?V(c), ?I(0)];
_ -> [MkBytes([ Word(I, A) || I <- lists:seq(0, A - 1, 32) ]),
MkBytes([ Word(I, A + B) || I <- lists:seq(A, A + B - 1, 32) ])]
end,
{[{"c", Type(A + B)}], {tuple, Body}, {tuple, [Type(A), Type(B)]}}.
bytes_to_raw_string(N, Term) when N =< 32 ->
{tuple, [?I(N), Term]};
bytes_to_raw_string(N, Term) when N > 32 ->
Elem = fun(I) -> #binop{op = '!', left = ?I(32 * I), right = ?V(bin)}
end,
Words = (N + 31) div 32,
?LET(bin, Term, {tuple, [?I(N) | [Elem(I) || I <- lists:seq(0, Words - 1)]]}).
-120
View File
@@ -1,120 +0,0 @@
%%%-------------------------------------------------------------------
%%% @author Ulf Norell
%%% @copyright (C) 2019, Aeternity Anstalt
%%% @doc
%%% Formatting of code generation errors.
%%% @end
%%%
%%%-------------------------------------------------------------------
-module(aeso_code_errors).
-export([format/1, pos/1]).
format({last_declaration_must_be_contract, Decl = {namespace, _, {con, _, C}, _}}) ->
Msg = io_lib:format("Expected a contract as the last declaration instead of the namespace '~s'\n",
[C]),
mk_err(pos(Decl), Msg);
format({missing_init_function, Con}) ->
Msg = io_lib:format("Missing init function for the contract '~s'.\n", [pp_expr(Con)]),
Cxt = "The 'init' function can only be omitted if the state type is 'unit'.\n",
mk_err(pos(Con), Msg, Cxt);
format({missing_definition, Id}) ->
Msg = io_lib:format("Missing definition of function '~s'.\n", [pp_expr(Id)]),
mk_err(pos(Id), Msg);
format({parameterized_state, Decl}) ->
Msg = "The state type cannot be parameterized.\n",
mk_err(pos(Decl), Msg);
format({parameterized_event, Decl}) ->
Msg = "The event type cannot be parameterized.\n",
mk_err(pos(Decl), Msg);
format({invalid_entrypoint, Why, Ann, {id, _, Name}, Thing}) ->
What = case Why of higher_order -> "higher-order (contains function types)";
polymorphic -> "polymorphic (contains type variables)" end,
ThingS = case Thing of
{argument, X, T} -> io_lib:format("argument\n~s\n", [pp_typed(X, T)]);
{result, T} -> io_lib:format("return type\n~s\n", [pp_type(2, T)])
end,
Bad = case Thing of
{argument, _, _} -> io_lib:format("has a ~s type", [What]);
{result, _} -> io_lib:format("is ~s", [What])
end,
Msg = io_lib:format("The ~sof entrypoint '~s' ~s.\n",
[ThingS, Name, Bad]),
case Why of
polymorphic -> mk_err(pos(Ann), Msg, "Use the FATE backend if you want polymorphic entrypoints.\n");
higher_order -> mk_err(pos(Ann), Msg)
end;
format({cant_compare_type_aevm, Ann, Op, Type}) ->
StringAndTuple = [ "- type string\n"
"- tuple or record of word type\n" || lists:member(Op, ['==', '!=']) ],
Msg = io_lib:format("Cannot compare values of type\n"
"~s\n"
"The AEVM only supports '~s' on values of\n"
"- word type (int, bool, bits, address, oracle(_, _), etc)\n"
"~s",
[pp_type(2, Type), Op, StringAndTuple]),
Cxt = "Use FATE if you need to compare arbitrary types.\n",
mk_err(pos(Ann), Msg, Cxt);
format({invalid_aens_resolve_type, Ann, T}) ->
Msg = io_lib:format("Invalid return type of AENS.resolve:\n"
"~s\n"
"It must be a string or a pubkey type (address, oracle, etc).\n",
[pp_type(2, T)]),
mk_err(pos(Ann), Msg);
format({unapplied_contract_call, Contract}) ->
Msg = io_lib:format("The AEVM does not support unapplied contract call to\n"
"~s\n", [pp_expr(2, Contract)]),
Cxt = "Use FATE if you need this.\n",
mk_err(pos(Contract), Msg, Cxt);
format({unapplied_builtin, Id}) ->
Msg = io_lib:format("The AEVM does not support unapplied use of ~s.\n", [pp_expr(0, Id)]),
Cxt = "Use FATE if you need this.\n",
mk_err(pos(Id), Msg, Cxt);
format({invalid_map_key_type, Why, Ann, Type}) ->
Msg = io_lib:format("Invalid map key type\n~s\n", [pp_type(2, Type)]),
Cxt = case Why of
polymorphic -> "Map keys cannot be polymorphic in the AEVM. Use FATE if you need this.\n";
function -> "Map keys cannot be higher-order.\n"
end,
mk_err(pos(Ann), Msg, Cxt);
format({invalid_oracle_type, Why, What, Ann, Type}) ->
WhyS = case Why of higher_order -> "higher-order (contain function types)";
polymorphic -> "polymorphic (contain type variables)" end,
Msg = io_lib:format("Invalid oracle type\n~s\n", [pp_type(2, Type)]),
Cxt = io_lib:format("The ~s type must not be ~s.\n", [What, WhyS]),
mk_err(pos(Ann), Msg, Cxt);
format({higher_order_state, {type_def, Ann, _, _, State}}) ->
Msg = io_lib:format("Invalid state type\n~s\n", [pp_type(2, State)]),
Cxt = "The state cannot contain functions in the AEVM. Use FATE if you need this.\n",
mk_err(pos(Ann), Msg, Cxt);
format(Err) ->
mk_err(aeso_errors:pos(0, 0), io_lib:format("Unknown error: ~p\n", [Err])).
pos(Ann) ->
File = aeso_syntax:get_ann(file, Ann, no_file),
Line = aeso_syntax:get_ann(line, Ann, 0),
Col = aeso_syntax:get_ann(col, Ann, 0),
aeso_errors:pos(File, Line, Col).
pp_typed(E, T) ->
prettypr:format(prettypr:nest(2,
lists:foldr(fun prettypr:beside/2, prettypr:empty(),
[aeso_pretty:expr(E), prettypr:text(" : "),
aeso_pretty:type(T)]))).
pp_expr(E) ->
pp_expr(0, E).
pp_expr(N, E) ->
prettypr:format(prettypr:nest(N, aeso_pretty:expr(E))).
pp_type(N, T) ->
prettypr:format(prettypr:nest(N, aeso_pretty:type(T))).
mk_err(Pos, Msg) ->
aeso_errors:new(code_error, Pos, lists:flatten(Msg)).
mk_err(Pos, Msg, Cxt) ->
aeso_errors:new(code_error, Pos, lists:flatten(Msg), lists:flatten(Cxt)).
+123 -79
View File
@@ -65,18 +65,18 @@ version() ->
{ok, list_to_binary(VsnString)} {ok, list_to_binary(VsnString)}
end. end.
-spec file(string()) -> {ok, map()} | {error, [aeso_errors:error()]}. -spec file(string()) -> {ok, map()} | {error, binary()}.
file(Filename) -> file(Filename) ->
file(Filename, []). file(Filename, []).
-spec file(string(), options()) -> {ok, map()} | {error, [aeso_errors:error()]}. -spec file(string(), options()) -> {ok, map()} | {error, binary()}.
file(File, Options0) -> file(File, Options0) ->
Options = add_include_path(File, Options0), Options = add_include_path(File, Options0),
case read_contract(File) of case read_contract(File) of
{ok, Bin} -> from_string(Bin, [{src_file, File} | Options]); {ok, Bin} -> from_string(Bin, [{src_file, File} | Options]);
{error, Error} -> {error, Error} ->
Msg = lists:flatten([File,": ",file:format_error(Error)]), ErrorString = [File,": ",file:format_error(Error)],
{error, [aeso_errors:new(file_error, Msg)]} {error, join_errors("File errors", [ErrorString], fun(E) -> E end)}
end. end.
add_include_path(File, Options) -> add_include_path(File, Options) ->
@@ -88,7 +88,7 @@ add_include_path(File, Options) ->
[{include, {file_system, [Cwd, Dir]}} | Options] [{include, {file_system, [Cwd, Dir]}} | Options]
end. end.
-spec from_string(binary() | string(), options()) -> {ok, map()} | {error, [aeso_errors:error()]}. -spec from_string(binary() | string(), options()) -> {ok, map()} | {error, binary()}.
from_string(Contract, Options) -> from_string(Contract, Options) ->
from_string(proplists:get_value(backend, Options, aevm), Contract, Options). from_string(proplists:get_value(backend, Options, aevm), Contract, Options).
@@ -98,14 +98,22 @@ from_string(Backend, ContractString, Options) ->
try try
from_string1(Backend, ContractString, Options) from_string1(Backend, ContractString, Options)
catch catch
throw:{error, Errors} -> {error, Errors} %% The compiler errors.
error:{parse_errors, Errors} ->
{error, join_errors("Parse errors", Errors, fun(E) -> E end)};
error:{type_errors, Errors} ->
{error, join_errors("Type errors", Errors, fun(E) -> E end)};
error:{code_errors, Errors} ->
{error, join_errors("Code errors", Errors,
fun (E) -> io_lib:format("~p", [E]) end)}
%% General programming errors in the compiler just signal error.
end. end.
from_string1(aevm, ContractString, Options) -> from_string1(aevm, ContractString, Options) ->
#{icode := Icode} = string_to_code(ContractString, Options), #{icode := Icode} = string_to_code(ContractString, Options),
TypeInfo = extract_type_info(Icode), TypeInfo = extract_type_info(Icode),
Assembler = assemble(Icode, Options), Assembler = assemble(Icode, Options),
pp_assembler(aevm, Assembler, Options), pp_assembler(Assembler, Options),
ByteCodeList = to_bytecode(Assembler, Options), ByteCodeList = to_bytecode(Assembler, Options),
ByteCode = << << B:8 >> || B <- ByteCodeList >>, ByteCode = << << B:8 >> || B <- ByteCodeList >>,
pp_bytecode(ByteCode, Options), pp_bytecode(ByteCode, Options),
@@ -114,13 +122,11 @@ from_string1(aevm, ContractString, Options) ->
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(), 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),
FateCode = aeso_fcode_to_fate:compile(FCode, Options), FateCode = aeso_fcode_to_fate:compile(FCode, Options),
pp_assembler(fate, FateCode, Options),
ByteCode = aeb_fate_code:serialize(FateCode, []), ByteCode = aeb_fate_code:serialize(FateCode, []),
{ok, Version} = version(), {ok, Version} = version(),
{ok, #{byte_code => ByteCode, {ok, #{byte_code => ByteCode,
@@ -128,8 +134,7 @@ from_string1(fate, ContractString, Options) ->
contract_source => ContractString, contract_source => ContractString,
type_info => [], type_info => [],
fate_code => FateCode, fate_code => FateCode,
abi_version => aeb_fate_abi:abi_version(), abi_version => aeb_fate_abi:abi_version()
payable => maps:get(payable, FCode)
}}. }}.
-spec string_to_code(string(), options()) -> map(). -spec string_to_code(string(), options()) -> map().
@@ -137,7 +142,7 @@ string_to_code(ContractString, Options) ->
Ast = parse(ContractString, Options), Ast = parse(ContractString, Options),
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 | Options]), {TypeEnv, TypedAst} = aeso_ast_infer_types:infer(Ast, [return_env]),
pp_typed_ast(TypedAst, Options), pp_typed_ast(TypedAst, Options),
case proplists:get_value(backend, Options, aevm) of case proplists:get_value(backend, Options, aevm) of
aevm -> aevm ->
@@ -153,6 +158,10 @@ string_to_code(ContractString, Options) ->
type_env => TypeEnv} type_env => TypeEnv}
end. end.
join_errors(Prefix, Errors, Pfun) ->
Ess = [ Pfun(E) || E <- Errors ],
list_to_binary(string:join([Prefix|Ess], "\n")).
-define(CALL_NAME, "__call"). -define(CALL_NAME, "__call").
-define(DECODE_NAME, "__decode"). -define(DECODE_NAME, "__decode").
@@ -164,7 +173,7 @@ string_to_code(ContractString, Options) ->
%% a special return type (typerep, T) %% a special return type (typerep, T)
-spec check_call(string(), string(), [string()], options()) -> {ok, string(), {[Type], Type}, [term()]} -spec check_call(string(), string(), [string()], options()) -> {ok, string(), {[Type], Type}, [term()]}
| {ok, string(), [term()]} | {ok, string(), [term()]}
| {error, [aeso_errors:error()]} | {error, term()}
when Type :: term(). when Type :: term().
check_call(Source, "init" = FunName, Args, Options) -> check_call(Source, "init" = FunName, Args, Options) ->
case check_call1(Source, FunName, Args, Options) of case check_call1(Source, FunName, Args, Options) of
@@ -219,7 +228,16 @@ check_call1(ContractString0, FunName, Args, Options) ->
{ok, FunName, CallArgs} {ok, FunName, CallArgs}
end end
catch catch
throw:{error, Errors} -> {error, Errors} error:{parse_errors, Errors} ->
{error, join_errors("Parse errors", Errors, fun (E) -> E end)};
error:{type_errors, Errors} ->
{error, join_errors("Type errors", Errors, fun (E) -> E end)};
error:{badmatch, {error, missing_call_function}} ->
{error, join_errors("Type errors", ["missing __call function"],
fun (E) -> E end)};
throw:Error -> %Don't ask
{error, join_errors("Code errors", [Error],
fun (E) -> io_lib:format("~p", [E]) end)}
end. end.
arguments_of_body(CallName, _FunName, Fcode) -> arguments_of_body(CallName, _FunName, Fcode) ->
@@ -267,31 +285,24 @@ last_contract_indent(Decls) ->
end. end.
-spec to_sophia_value(string(), string(), ok | error | revert, aeb_aevm_data:data()) -> -spec to_sophia_value(string(), string(), ok | error | revert, aeb_aevm_data:data()) ->
{ok, aeso_syntax:expr()} | {error, [aeso_errors:error()]}. {ok, aeso_syntax:expr()} | {error, term()}.
to_sophia_value(ContractString, Fun, ResType, Data) -> to_sophia_value(ContractString, Fun, ResType, Data) ->
to_sophia_value(ContractString, Fun, ResType, Data, [{backend, aevm}]). to_sophia_value(ContractString, Fun, ResType, Data, [{backend, aevm}]).
-spec to_sophia_value(string(), string(), ok | error | revert, binary(), options()) -> -spec to_sophia_value(string(), string(), ok | error | revert, binary(), options()) ->
{ok, aeso_syntax:expr()} | {error, [aeso_errors:error()]}. {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 proplists:get_value(backend, Options, aevm) of case proplists:get_value(backend, Options, aevm) of
aevm -> aevm ->
case aeb_heap:from_binary(string, Data) of case aeb_heap:from_binary(string, Data) of
{ok, Err} -> {ok, Err} -> {ok, {app, [], {id, [], "abort"}, [{string, [], Err}]}};
{ok, {app, [], {id, [], "abort"}, [{string, [], Err}]}}; {error, _} = Err -> Err
{error, _} ->
Msg = "Could not interpret the revert message\n",
{error, [aeso_errors:new(data_error, Msg)]}
end; end;
fate -> fate ->
try aeb_fate_encoding:deserialize(Data) of Err = aeb_fate_encoding:deserialize(Data),
Err -> {ok, {app, [], {id, [], "abort"}, [{string, [], Err}]}} {ok, {app, [], {id, [], "abort"}, [{string, [], Err}]}}
catch _:_ ->
Msg = "Could not deserialize the revert message\n",
{error, [aeso_errors:new(data_error, Msg)]}
end
end; end;
to_sophia_value(ContractString, FunName, ok, Data, Options0) -> to_sophia_value(ContractString, FunName, ok, Data, Options0) ->
Options = [no_code | Options0], Options = [no_code | Options0],
@@ -311,41 +322,49 @@ to_sophia_value(ContractString, FunName, ok, Data, Options0) ->
{ok, aeso_vm_decode:from_aevm(VmType, Type, VmValue)} {ok, aeso_vm_decode:from_aevm(VmType, Type, VmValue)}
catch throw:cannot_translate_to_sophia -> catch throw:cannot_translate_to_sophia ->
Type0Str = prettypr:format(aeso_pretty:type(Type0)), Type0Str = prettypr:format(aeso_pretty:type(Type0)),
Msg = io_lib:format("Cannot translate VM value ~p\n of type ~p\n to Sophia type ~s\n", {error, join_errors("Translation error", [lists:flatten(io_lib:format("Cannot translate VM value ~p\n of type ~p\n to Sophia type ~s\n",
[Data, VmType, Type0Str]), [Data, VmType, Type0Str]))],
{error, [aeso_errors:new(data_error, Msg)]} fun (E) -> E end)}
end; end;
{error, _Err} -> {error, _Err} ->
Msg = io_lib:format("Failed to decode binary as type ~p\n", [VmType]), {error, join_errors("Decode errors", [lists:flatten(io_lib:format("Failed to decode binary at type ~p", [VmType]))],
{error, [aeso_errors:new(data_error, Msg)]} fun(E) -> E end)}
end; end;
fate -> fate ->
try try
{ok, aeso_vm_decode:from_fate(Type, aeb_fate_encoding:deserialize(Data))} {ok, aeso_vm_decode:from_fate(Type, aeb_fate_encoding:deserialize(Data))}
catch throw:cannot_translate_to_sophia -> catch throw:cannot_translate_to_sophia ->
Type1 = prettypr:format(aeso_pretty:type(Type)), {error, join_errors("Translation error",
Msg = io_lib:format("Cannot translate FATE value ~p\n of Sophia type ~s\n", [lists:flatten(io_lib:format("Cannot translate fate value ~p\n of Sophia type ~s\n",
[aeb_fate_encoding:deserialize(Data), Type1]), [aeb_fate_encoding:deserialize(Data), Type]))],
{error, [aeso_errors:new(data_error, Msg)]}; fun (E) -> E end)};
_:_ -> _:R ->
Type1 = prettypr:format(aeso_pretty:type(Type)), {error, iolist_to_binary(io_lib:format("Decode error ~p: ~p\n", [R, erlang:get_stacktrace()]))}
Msg = io_lib:format("Failed to decode binary as type ~s\n", [Type1]),
{error, [aeso_errors:new(data_error, Msg)]}
end end
end end
catch catch
throw:{error, Errors} -> {error, Errors} error:{parse_errors, Errors} ->
{error, join_errors("Parse errors", Errors, fun (E) -> E end)};
error:{type_errors, Errors} ->
{error, join_errors("Type errors", Errors, fun (E) -> E end)};
error:{badmatch, {error, missing_function}} ->
{error, join_errors("Type errors", ["no function: '" ++ FunName ++ "'"],
fun (E) -> E end)};
throw:Error -> %Don't ask
{error, join_errors("Code errors", [Error],
fun (E) -> io_lib:format("~p", [E]) end)}
end. end.
-spec create_calldata(string(), string(), [string()]) -> -spec create_calldata(string(), string(), [string()]) ->
{ok, binary(), aeb_aevm_data:type(), aeb_aevm_data:type()} {ok, binary(), aeb_aevm_data:type(), aeb_aevm_data:type()}
| {error, [aeso_errors:error()]}. | {error, term()}.
create_calldata(Code, Fun, Args) -> create_calldata(Code, Fun, Args) ->
create_calldata(Code, Fun, Args, [{backend, aevm}]). create_calldata(Code, Fun, Args, [{backend, aevm}]).
-spec create_calldata(string(), string(), [string()], [{atom(), any()}]) -> -spec create_calldata(string(), string(), [string()], [{atom(), any()}]) ->
{ok, binary()} | {error, [aeso_errors:error()]}. {ok, binary()}
| {error, term()}.
create_calldata(Code, Fun, Args, Options0) -> create_calldata(Code, Fun, Args, Options0) ->
Options = [no_code | Options0], Options = [no_code | Options0],
case proplists:get_value(backend, Options, aevm) of case proplists:get_value(backend, Options, aevm) of
@@ -365,7 +384,7 @@ create_calldata(Code, Fun, Args, Options0) ->
-spec decode_calldata(string(), string(), binary()) -> -spec decode_calldata(string(), string(), binary()) ->
{ok, [aeso_syntax:type()], [aeso_syntax:expr()]} {ok, [aeso_syntax:type()], [aeso_syntax:expr()]}
| {error, [aeso_errors:error()]}. | {error, term()}.
decode_calldata(ContractString, FunName, Calldata) -> decode_calldata(ContractString, FunName, Calldata) ->
decode_calldata(ContractString, FunName, Calldata, [{backend, aevm}]). decode_calldata(ContractString, FunName, Calldata, [{backend, aevm}]).
@@ -393,13 +412,14 @@ decode_calldata(ContractString, FunName, Calldata, Options0) ->
{ok, ArgTypes, Values} {ok, ArgTypes, Values}
catch throw:cannot_translate_to_sophia -> catch throw:cannot_translate_to_sophia ->
Type0Str = prettypr:format(aeso_pretty:type(Type0)), Type0Str = prettypr:format(aeso_pretty:type(Type0)),
Msg = io_lib:format("Cannot translate VM value ~p\n of type ~p\n to Sophia type ~s\n", {error, join_errors("Translation error",
[VmValue, VmType, Type0Str]), [lists:flatten(io_lib:format("Cannot translate VM value ~p\n of type ~p\n to Sophia type ~s\n",
{error, [aeso_errors:new(data_error, Msg)]} [VmValue, VmType, Type0Str]))],
fun (E) -> E end)}
end; end;
{error, _Err} -> {error, _Err} ->
Msg = io_lib:format("Failed to decode calldata as type ~p\n", [VmType]), {error, join_errors("Decode errors", [lists:flatten(io_lib:format("Failed to decode binary at type ~p", [VmType]))],
{error, [aeso_errors:new(data_error, Msg)]} fun(E) -> E end)}
end; end;
fate -> fate ->
case aeb_fate_abi:decode_calldata(FunName, Calldata) of case aeb_fate_abi:decode_calldata(FunName, Calldata) of
@@ -411,30 +431,35 @@ decode_calldata(ContractString, FunName, Calldata, Options0) ->
{ok, ArgTypes, AstArgs} {ok, ArgTypes, AstArgs}
catch throw:cannot_translate_to_sophia -> catch throw:cannot_translate_to_sophia ->
Type0Str = prettypr:format(aeso_pretty:type(Type0)), Type0Str = prettypr:format(aeso_pretty:type(Type0)),
Msg = io_lib:format("Cannot translate FATE value ~p\n to Sophia type ~s\n", {error, join_errors("Translation error",
[FateArgs, Type0Str]), [lists:flatten(io_lib:format("Cannot translate fate value ~p\n of Sophia type ~s\n",
{error, [aeso_errors:new(data_error, Msg)]} [FateArgs, Type0Str]))],
fun (E) -> E end)}
end; end;
{error, _} -> {error, _} ->
Msg = io_lib:format("Failed to decode calldata binary\n", []), {error, join_errors("Decode errors", ["Failed to decode binary"],
{error, [aeso_errors:new(data_error, Msg)]} fun(E) -> E end)}
end end
end end
catch catch
throw:{error, Errors} -> {error, Errors} error:{parse_errors, Errors} ->
{error, join_errors("Parse errors", Errors, fun (E) -> E end)};
error:{type_errors, Errors} ->
{error, join_errors("Type errors", Errors, fun (E) -> E end)};
error:{badmatch, {error, missing_function}} ->
{error, join_errors("Type errors", ["no function: '" ++ FunName ++ "'"],
fun (E) -> E end)};
throw:Error -> %Don't ask
{error, join_errors("Code errors", [Error],
fun (E) -> io_lib:format("~p", [E]) end)}
end. end.
get_arg_icode(Funs) -> get_arg_icode(Funs) ->
case [ Args || {[_, ?CALL_NAME], _, _, {funcall, _, Args}, _} <- Funs ] of case [ Args || {[_, ?CALL_NAME], _, _, {funcall, _, Args}, _} <- Funs ] of
[Args] -> Args; [Args] -> Args;
[] -> error_missing_call_function() [] -> error({missing_call_function, Funs})
end. end.
-dialyzer({nowarn_function, error_missing_call_function/0}).
error_missing_call_function() ->
Msg = "Internal error: missing '__call'-function",
aeso_errors:throw(aeso_errors:new(internal_error, Msg)).
get_call_type([{contract, _, _, Defs}]) -> get_call_type([{contract, _, _, Defs}]) ->
case [ {lists:last(QFunName), FunType} case [ {lists:last(QFunName), FunType}
|| {letfun, _, {id, _, ?CALL_NAME}, [], _Ret, || {letfun, _, {id, _, ?CALL_NAME}, [], _Ret,
@@ -442,14 +467,13 @@ get_call_type([{contract, _, _, Defs}]) ->
{app, _, {app, _,
{typed, _, {qid, _, QFunName}, FunType}, _}, _}} <- Defs ] of {typed, _, {qid, _, QFunName}, FunType}, _}, _}} <- Defs ] of
[Call] -> {ok, Call}; [Call] -> {ok, Call};
[] -> error_missing_call_function() [] -> {error, missing_call_function}
end; end;
get_call_type([_ | Contracts]) -> get_call_type([_ | Contracts]) ->
%% The __call should be in the final contract %% The __call should be in the final contract
get_call_type(Contracts). get_call_type(Contracts).
-dialyzer({nowarn_function, get_decode_type/2}). get_decode_type(FunName, [{contract, _, _, Defs}]) ->
get_decode_type(FunName, [{contract, Ann, _, Defs}]) ->
GetType = fun({letfun, _, {id, _, Name}, Args, Ret, _}) when Name == FunName -> [{Args, Ret}]; GetType = fun({letfun, _, {id, _, Name}, Args, Ret, _}) when Name == FunName -> [{Args, Ret}];
({fun_decl, _, {id, _, Name}, {fun_t, _, _, Args, Ret}}) when Name == FunName -> [{Args, Ret}]; ({fun_decl, _, {id, _, Name}, {fun_t, _, _, Args, Ret}}) when Name == FunName -> [{Args, Ret}];
(_) -> [] end, (_) -> [] end,
@@ -458,10 +482,7 @@ get_decode_type(FunName, [{contract, Ann, _, Defs}]) ->
[] -> [] ->
case FunName of case FunName of
"init" -> {ok, [], {tuple_t, [], []}}; "init" -> {ok, [], {tuple_t, [], []}};
_ -> _ -> {error, missing_function}
Msg = io_lib:format("Function '~s' is missing in contract\n", [FunName]),
Pos = aeso_code_errors:pos(Ann),
aeso_errors:throw(aeso_errors:new(data_error, Pos, Msg))
end end
end; end;
get_decode_type(FunName, [_ | Contracts]) -> get_decode_type(FunName, [_ | Contracts]) ->
@@ -518,9 +539,8 @@ 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)),
Payable(Attrs), ArgTypesOnly(Args), TypeRep) 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)
@@ -533,11 +553,9 @@ pp_sophia_code(C, Opts)-> pp(C, Opts, pp_sophia_code, fun(Code) ->
pp_ast(C, Opts) -> pp(C, Opts, pp_ast, fun aeso_ast:pp/1). pp_ast(C, Opts) -> pp(C, Opts, pp_ast, fun aeso_ast:pp/1).
pp_typed_ast(C, Opts)-> pp(C, Opts, pp_typed_ast, fun aeso_ast:pp_typed/1). pp_typed_ast(C, Opts)-> pp(C, Opts, pp_typed_ast, fun aeso_ast:pp_typed/1).
pp_icode(C, Opts) -> pp(C, Opts, pp_icode, fun aeso_icode:pp/1). pp_icode(C, Opts) -> pp(C, Opts, pp_icode, fun aeso_icode:pp/1).
pp_assembler(C, Opts)-> pp(C, Opts, pp_assembler, fun aeb_asm:pp/1).
pp_bytecode(C, Opts) -> pp(C, Opts, pp_bytecode, fun aeb_disassemble:pp/1). pp_bytecode(C, Opts) -> pp(C, Opts, pp_bytecode, fun aeb_disassemble:pp/1).
pp_assembler(aevm, C, Opts) -> pp(C, Opts, pp_assembler, fun aeb_asm:pp/1);
pp_assembler(fate, C, Opts) -> pp(C, Opts, pp_assembler, fun(Asm) -> io:format("~s", [aeb_fate_asm:pp(Asm)]) end).
pp(Code, Options, Option, PPFun) -> pp(Code, Options, Option, PPFun) ->
case proplists:lookup(Option, Options) of case proplists:lookup(Option, Options) of
{Option, true} -> {Option, true} ->
@@ -546,7 +564,9 @@ pp(Code, Options, Option, PPFun) ->
ok ok
end. end.
%% ------------------------------------------------------------------- %% -------------------------------------------------------------------
%% TODO: Tempoary parser hook below...
sophia_type_to_typerep(String) -> sophia_type_to_typerep(String) ->
{ok, Ast} = aeso_parser:type(String), {ok, Ast} = aeso_parser:type(String),
@@ -555,13 +575,37 @@ 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). %% Try and return something sensible here!
case aeso_parser:string(Text, Options) of
%% Yay, it worked!
{ok, Contract} -> Contract;
%% Scan errors.
{error, {Pos, scan_error}} ->
parse_error(Pos, "scan error");
{error, {Pos, scan_error_no_state}} ->
parse_error(Pos, "scan error");
%% Parse errors.
{error, {Pos, parse_error, Error}} ->
parse_error(Pos, Error);
{error, {Pos, ambiguous_parse, As}} ->
ErrorString = io_lib:format("Ambiguous ~p", [As]),
parse_error(Pos, ErrorString);
%% Include error
{error, {Pos, include_error, File}} ->
parse_error(Pos, io_lib:format("could not find include file '~s'", [File]))
end.
-spec parse(string(), sets:set(), aeso_compiler:options()) -> none() | aeso_syntax:ast(). parse_error(Pos, ErrorString) ->
parse(Text, Included, Options) -> Error = io_lib:format("~s: ~s", [pos_error(Pos), ErrorString]),
aeso_parser:string(Text, Included, Options). error({parse_errors, [Error]}).
read_contract(Name) -> read_contract(Name) ->
file:read_file(Name). file:read_file(Name).
pos_error({Line, Pos}) ->
io_lib:format("line ~p, column ~p", [Line, Pos]);
pos_error({no_file, Line, Pos}) ->
pos_error({Line, Pos});
pos_error({File, Line, Pos}) ->
io_lib:format("file ~s, line ~p, column ~p", [File, Line, Pos]).
+42
View File
@@ -0,0 +1,42 @@
-module(aeso_constants).
-export([string/1, get_type/1]).
string(Str) ->
case aeso_parser:string("let _ = " ++ Str) of
{ok, [{letval, _, _, _, E}]} -> {ok, E};
{ok, Other} -> error({internal_error, should_be_letval, Other});
Err -> Err
end.
get_type(Str) ->
case aeso_parser:string("let _ = " ++ Str) of
{ok, [Ast]} ->
AstT = aeso_ast_infer_types:infer_constant(Ast),
T = ast_to_type(AstT),
{ok, T};
{ok, Other} -> error({internal_error, should_be_letval, Other});
Err -> Err
end.
ast_to_type({id, _, T}) ->
T;
ast_to_type({tuple_t, _, []}) -> "()";
ast_to_type({tuple_t, _, Ts}) ->
"(" ++ list_ast_to_type(Ts) ++ ")";
ast_to_type({app_t,_, {id, _, "list"}, [T]}) ->
lists:flatten("list(" ++ ast_to_type(T) ++ ")");
ast_to_type({app_t,_, {id, _, "option"}, [T]}) ->
lists:flatten("option(" ++ ast_to_type(T) ++ ")").
list_ast_to_type([T]) ->
ast_to_type(T);
list_ast_to_type([T|Ts]) ->
ast_to_type(T)
++ ", "
++ list_ast_to_type(Ts).
-112
View File
@@ -1,112 +0,0 @@
%%%-------------------------------------------------------------------
%%% @copyright (C) 2019, Aeternity Anstalt
%%% @doc ADT for structured error messages + formatting.
%%%
%%% @end
%%%-------------------------------------------------------------------
-module(aeso_errors).
-type src_file() :: no_file | iolist().
-record(pos, { file = no_file :: src_file()
, line = 0 :: non_neg_integer()
, col = 0 :: non_neg_integer()
}).
-type pos() :: #pos{}.
-type error_type() :: type_error | parse_error | code_error
| file_error | data_error | internal_error.
-record(err, { pos = #pos{} :: pos()
, type :: error_type()
, message :: iolist()
, context = none :: none | iolist()
}).
-opaque error() :: #err{}.
-export_type([error/0, pos/0]).
-export([ err_msg/1
, msg/1
, new/2
, new/3
, new/4
, pos/2
, pos/3
, pp/1
, to_json/1
, throw/1
, type/1
]).
new(Type, Msg) ->
new(Type, pos(0, 0), Msg).
new(Type, Pos, Msg) ->
#err{ type = Type, pos = Pos, message = Msg }.
new(Type, Pos, Msg, Ctxt) ->
#err{ type = Type, pos = Pos, message = Msg, context = Ctxt }.
pos(Line, Col) ->
#pos{ line = Line, col = Col }.
pos(File, Line, Col) ->
#pos{ file = File, line = Line, col = Col }.
-spec throw(_) -> ok | no_return().
throw([]) -> ok;
throw(Errs) when is_list(Errs) ->
SortedErrs = lists:sort(fun(E1, E2) -> E1#err.pos =< E2#err.pos end, Errs),
erlang:throw({error, SortedErrs});
throw(#err{} = Err) ->
erlang:throw({error, [Err]}).
msg(#err{ message = Msg, context = none }) -> Msg;
msg(#err{ message = Msg, context = Ctxt }) -> Msg ++ Ctxt.
err_msg(#err{ pos = Pos } = Err) ->
lists:flatten(io_lib:format("~s~s", [str_pos(Pos), msg(Err)])).
str_pos(#pos{file = no_file, line = L, col = C}) ->
io_lib:format("~p:~p:", [L, C]);
str_pos(#pos{file = F, line = L, col = C}) ->
io_lib:format("~s:~p:~p:", [F, L, C]).
type(#err{ type = Type }) -> Type.
pp(#err{ type = Kind, pos = Pos } = Err) ->
lists:flatten(io_lib:format("~s~s:\n~s", [pp_kind(Kind), pp_pos(Pos), msg(Err)])).
pp_kind(type_error) -> "Type error";
pp_kind(parse_error) -> "Parse error";
pp_kind(code_error) -> "Code generation error";
pp_kind(file_error) -> "File error";
pp_kind(data_error) -> "Data error";
pp_kind(internal_error) -> "Internal error".
pp_pos(#pos{file = no_file, line = 0, col = 0}) ->
"";
pp_pos(#pos{file = no_file, line = L, col = C}) ->
io_lib:format(" at line ~p, col ~p", [L, C]);
pp_pos(#pos{file = F, line = L, col = C}) ->
io_lib:format(" in '~s' at line ~p, col ~p", [F, L, C]).
to_json(#err{pos = Pos, type = Type, message = Msg, context = Cxt}) ->
Json = #{ pos => pos_to_json(Pos),
type => atom_to_binary(Type, utf8),
message => iolist_to_binary(Msg) },
case Cxt of
none -> Json;
_ -> Json#{ context => iolist_to_binary(Cxt) }
end.
pos_to_json(#pos{ file = File, line = Line, col = Col }) ->
Json = #{ line => Line, col => Col },
case File of
no_file -> Json;
_ -> Json#{ file => iolist_to_binary(File) }
end.
+67 -144
View File
@@ -98,34 +98,26 @@
Op =:= 'SHA3' orelse Op =:= 'SHA3' orelse
Op =:= 'SHA256' orelse Op =:= 'SHA256' orelse
Op =:= 'BLAKE2B' orelse Op =:= 'BLAKE2B' orelse
Op =:= 'VERIFY_SIG' orelse Op =:= 'ECVERIFY' orelse
Op =:= 'VERIFY_SIG_SECP256K1' orelse
Op =:= 'ECVERIFY_SECP256K1' orelse Op =:= 'ECVERIFY_SECP256K1' orelse
Op =:= 'ECRECOVER_SECP256K1' orelse
Op =:= 'CONTRACT_TO_ADDRESS' orelse Op =:= 'CONTRACT_TO_ADDRESS' orelse
Op =:= 'AUTH_TX_HASH' orelse Op =:= 'AUTH_TX_HASH' orelse
Op =:= 'BYTES_TO_INT' orelse Op =:= 'BYTES_TO_INT' orelse
Op =:= 'BYTES_TO_STR' orelse Op =:= 'BYTES_TO_STR' orelse
Op =:= 'BYTES_CONCAT' orelse
Op =:= 'BYTES_SPLIT' orelse
Op =:= 'ORACLE_CHECK' orelse Op =:= 'ORACLE_CHECK' orelse
Op =:= 'ORACLE_CHECK_QUERY' orelse Op =:= 'ORACLE_CHECK_QUERY' orelse
Op =:= 'IS_ORACLE' orelse Op =:= 'IS_ORACLE' orelse
Op =:= 'IS_CONTRACT' orelse Op =:= 'IS_CONTRACT' orelse
Op =:= 'IS_PAYABLE' orelse
Op =:= 'CREATOR' orelse Op =:= 'CREATOR' orelse
false)). false)).
-record(env, { contract, vars = [], locals = [], current_function, tailpos = true }). -record(env, { contract, vars = [], locals = [], tailpos = true }).
%% -- Debugging -------------------------------------------------------------- %% -- Debugging --------------------------------------------------------------
is_debug(Tag, Options) ->
Tags = proplists:get_value(debug, Options, []),
Tags == all orelse lists:member(Tag, Tags).
debug(Tag, Options, Fmt, Args) -> debug(Tag, Options, Fmt, Args) ->
case is_debug(Tag, Options) of Tags = proplists:get_value(debug, Options, []),
case Tags == all orelse lists:member(Tag, Tags) of
true -> io:format(Fmt, Args); true -> io:format(Fmt, Args);
false -> ok false -> ok
end. end.
@@ -135,16 +127,19 @@ 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),
FateCode = to_basic_blocks(SFuns1), SFuns2 = add_default_init_function(SFuns1, StateType),
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(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, ".")).
@@ -152,17 +147,15 @@ 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, Attrs, Args, Body, Type, Options)} [ {make_function_name(Name), function_to_scode(ContractName, FunNames, Name, 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, Attrs0, Args, Body, ResType, _Options) -> function_to_scode(ContractName, Functions, _Name, Args, Body, ResType, _Options) ->
{ArgTypes, ResType1} = typesig_to_scode(Args, ResType), {ArgTypes, ResType1} = typesig_to_scode(Args, ResType),
Attrs = Attrs0 -- [stateful], %% Only track private and payable from here. SCode = to_scode(init_env(ContractName, Functions, Args), Body),
SCode = to_scode(init_env(ContractName, Functions, Name, Args), Body), {{ArgTypes, ResType1}, SCode}.
{Attrs, {ArgTypes, ResType1}, SCode}.
-define(tvars, '$tvars'). -define(tvars, '$tvars').
@@ -198,16 +191,30 @@ 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({entrypoint, <<"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
%% -- Environment functions -- %% -- Environment functions --
init_env(ContractName, FunNames, Name, Args) -> init_env(ContractName, FunNames, Args) ->
#env{ vars = [ {X, {arg, I}} || {I, {X, _}} <- with_ixs(Args) ], #env{ vars = [ {X, {arg, I}} || {I, {X, _}} <- with_ixs(Args) ],
contract = ContractName, contract = ContractName,
locals = FunNames, locals = FunNames,
current_function = Name,
tailpos = true }. tailpos = true }.
next_var(#env{ vars = Vars }) -> next_var(#env{ vars = Vars }) ->
@@ -310,24 +317,6 @@ to_scode(Env, {'let', X, Expr, Body}) ->
aeb_fate_ops:store({var, I}, {stack, 0}), aeb_fate_ops:store({var, I}, {stack, 0}),
to_scode(Env1, Body) ]; to_scode(Env1, Body) ];
to_scode(Env = #env{ current_function = Fun, tailpos = true }, {def, Fun, Args}) ->
%% Tail-call to current function, f(e0..en). Compile to
%% [ let xi = ei ]
%% [ STORE argi xi ]
%% jump 0
{Vars, Code, _Env} =
lists:foldl(fun(Arg, {Is, Acc, Env1}) ->
{I, Env2} = bind_local("_", Env1),
ArgCode = to_scode(notail(Env2), Arg),
Acc1 = [Acc, ArgCode,
aeb_fate_ops:store({var, I}, ?a)],
{[I | Is], Acc1, Env2}
end, {[], [], Env}, Args),
[ Code,
[ aeb_fate_ops:store({arg, I}, {var, J})
|| {I, J} <- lists:zip(lists:seq(0, length(Vars) - 1),
lists:reverse(Vars)) ],
loop ];
to_scode(Env, {def, Fun, Args}) -> to_scode(Env, {def, Fun, Args}) ->
FName = make_function_id(Fun), FName = make_function_id(Fun),
Lbl = aeb_fate_data:make_string(FName), Lbl = aeb_fate_data:make_string(FName),
@@ -338,19 +327,21 @@ 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, ArgsT, RetT, Ct, Fun, [Gas, Value | Args]}) -> to_scode(Env, {remote, Ct, Fun, [{builtin, call_gas_left, _}, Value | Args]}) ->
%% Gas is not limited.
Lbl = make_function_id(Fun), Lbl = make_function_id(Fun),
{ArgTypes, RetType0} = typesig_to_scode([{"_", T} || T <- ArgsT], RetT), Call = if Env#env.tailpos -> aeb_fate_ops:call_tr(?a, Lbl, ?a);
ArgType = ?i(aeb_fate_data:make_typerep({tuple, ArgTypes})), true -> aeb_fate_ops:call_r(?a, Lbl, ?a)
RetType = ?i(aeb_fate_data:make_typerep(RetType0)), end,
case Gas of
{builtin, call_gas_left, _} ->
Call = aeb_fate_ops:call_r(?a, Lbl, ArgType, RetType, ?a),
call_to_scode(Env, Call, [Ct, Value | Args]); call_to_scode(Env, Call, [Ct, Value | Args]);
_ ->
Call = aeb_fate_ops:call_gr(?a, Lbl, ArgType, RetType, ?a, ?a), to_scode(Env, {remote, Ct, Fun, [Gas, Value | Args]}) ->
call_to_scode(Env, Call, [Ct, Value, Gas | Args]) %% Gas is limited.
end; Lbl = make_function_id(Fun),
Call = if Env#env.tailpos -> aeb_fate_ops:call_gtr(?a, Lbl, ?a, ?a);
true -> aeb_fate_ops:call_gr(?a, Lbl, ?a, ?a)
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]});
@@ -492,10 +483,6 @@ builtin_to_scode(Env, bytes_to_int, [_] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_to_int(?a, ?a), Args); call_to_scode(Env, aeb_fate_ops:bytes_to_int(?a, ?a), Args);
builtin_to_scode(Env, bytes_to_str, [_] = Args) -> builtin_to_scode(Env, bytes_to_str, [_] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_to_str(?a, ?a), Args); call_to_scode(Env, aeb_fate_ops:bytes_to_str(?a, ?a), Args);
builtin_to_scode(Env, bytes_concat, [_, _] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_concat(?a, ?a, ?a), Args);
builtin_to_scode(Env, bytes_split, [_, _] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_split(?a, ?a, ?a), Args);
builtin_to_scode(Env, abort, [_] = Args) -> builtin_to_scode(Env, abort, [_] = Args) ->
call_to_scode(Env, aeb_fate_ops:abort(?a), Args); call_to_scode(Env, aeb_fate_ops:abort(?a), Args);
builtin_to_scode(Env, chain_spend, [_, _] = Args) -> builtin_to_scode(Env, chain_spend, [_, _] = Args) ->
@@ -555,8 +542,6 @@ 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) ->
@@ -611,10 +596,8 @@ 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_verify_sig) -> aeb_fate_ops:verify_sig(?a, ?a, ?a, ?a); op_to_scode(crypto_ecverify) -> aeb_fate_ops:ecverify(?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_ecverify_secp256k1) -> aeb_fate_ops:ecverify_secp256k1(?a, ?a, ?a, ?a); op_to_scode(crypto_ecverify_secp256k1) -> aeb_fate_ops:ecverify_secp256k1(?a, ?a, ?a, ?a);
op_to_scode(crypto_ecrecover_secp256k1) -> aeb_fate_ops:ecrecover_secp256k1(?a, ?a, ?a);
op_to_scode(crypto_sha3) -> aeb_fate_ops:sha3(?a, ?a); op_to_scode(crypto_sha3) -> aeb_fate_ops:sha3(?a, ?a);
op_to_scode(crypto_sha256) -> aeb_fate_ops:sha256(?a, ?a); op_to_scode(crypto_sha256) -> aeb_fate_ops:sha256(?a, ?a);
op_to_scode(crypto_blake2b) -> aeb_fate_ops:blake2b(?a, ?a); op_to_scode(crypto_blake2b) -> aeb_fate_ops:blake2b(?a, ?a);
@@ -645,12 +628,12 @@ flatten_s(I) -> I.
-define(MAX_SIMPL_ITERATIONS, 10). -define(MAX_SIMPL_ITERATIONS, 10).
optimize_fun(_Funs, Name, {Attrs, Sig, Code}, Options) -> optimize_fun(_Funs, Name, {{Args, Res}, 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),
{Attrs, Sig, Code2}. {{Args, Res}, 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]),
@@ -689,15 +672,12 @@ pp_ann(Ind, [{i, #{ live_in := In, live_out := Out }, I} | Code]) ->
Fmt = fun([]) -> "()"; Fmt = fun([]) -> "()";
(Xs) -> string:join([lists:concat(["var", N]) || {var, N} <- Xs], " ") (Xs) -> string:join([lists:concat(["var", N]) || {var, N} <- Xs], " ")
end, end,
Op = [Ind, pp_op(I)], Op = [Ind, aeb_fate_pp:format_op(I, #{})],
Ann = [[" % ", Fmt(In), " -> ", Fmt(Out)] || In ++ Out /= []], Ann = [[" % ", Fmt(In), " -> ", Fmt(Out)] || In ++ Out /= []],
[io_lib:format("~-40s~s\n", [Op, Ann]), [io_lib:format("~-40s~s\n", [Op, Ann]),
pp_ann(Ind, Code)]; pp_ann(Ind, Code)];
pp_ann(_, []) -> []. pp_ann(_, []) -> [].
pp_op(loop) -> "LOOP";
pp_op(I) ->
aeb_fate_pp:format_op(I, #{}).
pp_arg(?i(I)) -> io_lib:format("~w", [I]); pp_arg(?i(I)) -> io_lib:format("~w", [I]);
pp_arg({arg, N}) -> io_lib:format("arg~p", [N]); pp_arg({arg, N}) -> io_lib:format("arg~p", [N]);
@@ -764,13 +744,14 @@ attributes(I) ->
Pure = fun(W, R) -> Attr(W, R, true) end, Pure = fun(W, R) -> Attr(W, R, true) end,
Impure = fun(W, R) -> Attr(W, R, false) end, Impure = fun(W, R) -> Attr(W, R, false) end,
case I of case I of
loop -> Impure(pc, []);
'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, C, D} -> Impure(?a, [A, B, C, D]); {'CALL_R', A, _, B} -> Impure(?a, [A, B]);
{'CALL_GR', A, _, B, C, D, E} -> Impure(?a, [A, B, C, D, E]); {'CALL_GR', A, _, B, C} -> Impure(?a, [A, B, C]);
{'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);
@@ -844,21 +825,16 @@ 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]);
{'VERIFY_SIG', A, B, C, D} -> Pure(A, [B, C, D]); {'ECVERIFY', 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]);
{'BYTES_TO_STR', A, B} -> Pure(A, [B]); {'BYTES_TO_STR', A, B} -> Pure(A, [B]);
{'BYTES_CONCAT', A, B, C} -> Pure(A, [B, C]);
{'BYTES_SPLIT', A, B, C} -> Pure(A, [B, C]);
{'ORACLE_CHECK', A, B, C, D} -> Impure(A, [B, C, D]); {'ORACLE_CHECK', A, B, C, D} -> Impure(A, [B, C, D]);
{'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, []);
@@ -969,29 +945,15 @@ simpl_s({switch, Arg, Type, Alts, Def}, Options) ->
{switch, Arg, Type, [simplify(A, Options) || A <- Alts], simplify(Def, Options)}; {switch, Arg, Type, [simplify(A, Options) || A <- Alts], simplify(Def, Options)};
simpl_s(I, _) -> I. simpl_s(I, _) -> I.
%% Safe-guard against loops in the rewriting. Shouldn't happen so throw an
%% error if we run out.
-define(SIMPL_FUEL, 5000).
simpl_top(I, Code, Options) -> simpl_top(I, Code, Options) ->
simpl_top(?SIMPL_FUEL, I, Code, Options). apply_rules(rules(), I, Code, Options).
simpl_top(0, I, Code, _Options) -> apply_rules(Rules, I, Code, Options) ->
error({out_of_fuel, I, Code}); Cons = fun(X, Xs) -> simpl_top(X, Xs, Options) end,
simpl_top(Fuel, I, Code, Options) ->
apply_rules(Fuel, rules(), I, Code, Options).
apply_rules(Fuel, Rules, I, Code, Options) ->
Cons = fun(X, Xs) -> simpl_top(Fuel - 1, X, Xs, Options) end,
case apply_rules_once(Rules, I, Code) of case apply_rules_once(Rules, I, Code) of
false -> [I | Code]; false -> [I | Code];
{RName, New, Rest} -> {RName, New, Rest} ->
case is_debug(opt_rules, Options) of debug(opt_rules, Options, " Applied ~p:\n~s ==>\n~s\n", [RName, pp_ann(" ", [I | Code]), pp_ann(" ", New ++ Rest)]),
true ->
{OldCode, NewCode} = drop_common_suffix([I | Code], New ++ Rest),
debug(opt_rules, Options, " Applied ~p:\n~s ==>\n~s\n", [RName, pp_ann(" ", OldCode), pp_ann(" ", NewCode)]);
false -> ok
end,
lists:foldr(Cons, Rest, New) lists:foldr(Cons, Rest, New)
end. end.
@@ -1015,7 +977,6 @@ merge_rules() ->
rules() -> rules() ->
merge_rules() ++ merge_rules() ++
[?RULE(r_swap_push), [?RULE(r_swap_push),
?RULE(r_swap_pop),
?RULE(r_swap_write), ?RULE(r_swap_write),
?RULE(r_constant_propagation), ?RULE(r_constant_propagation),
?RULE(r_prune_impossible_branches), ?RULE(r_prune_impossible_branches),
@@ -1053,12 +1014,11 @@ inline_push(Ann1, Arg, Stack, [{i, Ann2, I} = AI | Code], Acc) ->
{As0, As1} = split_stack_arg(Stack, As), {As0, As1} = split_stack_arg(Stack, As),
Acc1 = [{i, merge_ann(Ann1, Ann2), from_op_view(Op, R, As0 ++ [Arg] ++ As1)} | Acc], Acc1 = [{i, merge_ann(Ann1, Ann2), from_op_view(Op, R, As0 ++ [Arg] ++ As1)} | Acc],
{lists:reverse(Acc1), Code}; {lists:reverse(Acc1), Code};
false when Arg /= R -> false ->
{AI1, {i, Ann1b, _}} = swap_instrs({i, Ann1, {'STORE', ?a, Arg}}, AI), {AI1, {i, Ann1b, _}} = swap_instrs({i, Ann1, {'STORE', ?a, Arg}}, AI),
inline_push(Ann1b, Arg, Stack + Produces - Consumes, Code, [AI1 | Acc]); inline_push(Ann1b, Arg, Stack + Produces - Consumes, Code, [AI1 | Acc])
false -> false
end; end;
_ -> false false -> false
end; end;
inline_push(_, _, _, _, _) -> false. inline_push(_, _, _, _, _) -> false.
@@ -1070,42 +1030,16 @@ split_stack_arg(N, [A | As], Acc) ->
true -> N end, true -> N end,
split_stack_arg(N1, As, [A | Acc]). split_stack_arg(N1, As, [A | Acc]).
%% Move PUSHes past non-stack instructions. %% Move PUSH A past non-stack instructions.
r_swap_push(Push = {i, _, PushI}, [I | Code]) -> r_swap_push(Push = {i, _, {'STORE', ?a, _}}, [I | Code]) ->
case op_view(PushI) of
{_, ?a, _} ->
case independent(Push, I) of case independent(Push, I) of
true -> true ->
{I1, Push1} = swap_instrs(Push, I), {I1, Push1} = swap_instrs(Push, I),
{[I1, Push1], Code}; {[I1, Push1], Code};
false -> false false -> false
end; end;
_ -> false
end;
r_swap_push(_, _) -> false. r_swap_push(_, _) -> false.
%% Move non-stack instruction past POPs.
r_swap_pop(IA = {i, _, I}, [JA = {i, _, J} | Code]) ->
case independent(IA, JA) of
true ->
case {op_view(I), op_view(J)} of
{false, _} -> false;
{_, false} -> false;
{{_, IR, IAs}, {_, RJ, JAs}} ->
NonStackI = not lists:member(?a, [IR | IAs]),
%% RJ /= ?a to not conflict with r_swap_push
PopJ = RJ /= ?a andalso lists:member(?a, JAs),
case NonStackI andalso PopJ of
false -> false;
true ->
{JA1, IA1} = swap_instrs(IA, JA),
{[JA1, IA1], Code}
end
end;
false -> false
end;
r_swap_pop(_, _) -> false.
%% Match up writes to variables with instructions further down. %% Match up writes to variables with instructions further down.
r_swap_write(I = {i, _, _}, [J | Code]) -> r_swap_write(I = {i, _, _}, [J | Code]) ->
case {var_writes(I), independent(I, J)} of case {var_writes(I), independent(I, J)} of
@@ -1241,8 +1175,6 @@ r_float_switch_body(I = {i, _, _}, [switch_body | Code]) ->
r_float_switch_body(_, _) -> false. r_float_switch_body(_, _) -> false.
%% Inline stores %% Inline stores
r_inline_store({i, _, {'STORE', R, R}}, Code) ->
{[], Code};
r_inline_store(I = {i, _, {'STORE', R = {var, _}, A}}, Code) -> r_inline_store(I = {i, _, {'STORE', R = {var, _}, A}}, Code) ->
%% Not when A is var unless updating the annotations properly. %% Not when A is var unless updating the annotations properly.
Inline = case A of Inline = case A of
@@ -1336,11 +1268,7 @@ unannotate({i, _Ann, I}) -> [I].
%% Desugar and specialize %% Desugar and specialize
desugar({'ADD', ?a, ?i(1), ?a}) -> [aeb_fate_ops:inc()]; desugar({'ADD', ?a, ?i(1), ?a}) -> [aeb_fate_ops:inc()];
desugar({'ADD', A, ?i(1), A}) -> [aeb_fate_ops:inc(A)];
desugar({'ADD', ?a, ?a, ?i(1)}) -> [aeb_fate_ops:inc()];
desugar({'ADD', A, A, ?i(1)}) -> [aeb_fate_ops:inc(A)];
desugar({'SUB', ?a, ?a, ?i(1)}) -> [aeb_fate_ops:dec()]; desugar({'SUB', ?a, ?a, ?i(1)}) -> [aeb_fate_ops:dec()];
desugar({'SUB', A, A, ?i(1)}) -> [aeb_fate_ops:dec(A)];
desugar({'STORE', ?a, A}) -> [aeb_fate_ops:push(A)]; desugar({'STORE', ?a, A}) -> [aeb_fate_ops:push(A)];
desugar({switch, Arg, Type, Alts, Def}) -> desugar({switch, Arg, Type, Alts, Def}) ->
[{switch, Arg, Type, [desugar(A) || A <- Alts], desugar(Def)}]; [{switch, Arg, Type, [desugar(A) || A <- Alts], desugar(Def)}];
@@ -1355,9 +1283,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, {Attrs, Sig, Code}}|Left], Acc) -> to_basic_blocks([{Name, {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, Attrs, Sig, BB, Acc)); to_basic_blocks(Left, aeb_fate_code:insert_fun(Name, Sig, BB, Acc));
to_basic_blocks([], Acc) -> to_basic_blocks([], Acc) ->
Acc. Acc.
@@ -1485,6 +1413,8 @@ 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);
[{'CALL_GTR', _, _, _}|_] -> reorder_blocks(Blocks, Acc1);
[{'EXIT', _}|_] -> reorder_blocks(Blocks, Acc1); [{'EXIT', _}|_] -> reorder_blocks(Blocks, Acc1);
[{'ABORT', _}|_] -> reorder_blocks(Blocks, Acc1); [{'ABORT', _}|_] -> reorder_blocks(Blocks, Acc1);
[{switch, _, _}|_] -> reorder_blocks(Blocks, Acc1); [{switch, _, _}|_] -> reorder_blocks(Blocks, Acc1);
@@ -1526,6 +1456,9 @@ chase_labels([L | Ls], Map, Live) ->
%% 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 = [{'CALL_GT', _} | _]]) -> Code;
tweak_returns(['RETURN' | Code = [{'CALL_GTR', _, _, _, _} | _]]) -> Code;
tweak_returns(['RETURN' | Code = [{'ABORT', _} | _]]) -> Code; tweak_returns(['RETURN' | Code = [{'ABORT', _} | _]]) -> Code;
tweak_returns(['RETURN' | Code = [{'EXIT', _} | _]]) -> Code; tweak_returns(['RETURN' | Code = [{'EXIT', _} | _]]) -> Code;
tweak_returns(Code) -> Code. tweak_returns(Code) -> Code.
@@ -1541,8 +1474,7 @@ split_calls(Ref, [], Acc, Blocks) ->
split_calls(Ref, [I | Code], Acc, Blocks) when element(1, I) == 'CALL'; split_calls(Ref, [I | Code], Acc, Blocks) when element(1, I) == 'CALL';
element(1, I) == 'CALL_R'; element(1, I) == 'CALL_R';
element(1, I) == 'CALL_GR'; element(1, I) == 'CALL_GR';
element(1, I) == 'jumpif'; element(1, I) == 'jumpif' ->
I == loop ->
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]);
@@ -1555,7 +1487,6 @@ split_calls(Ref, [I | Code], Acc, Blocks) ->
set_labels(Labels, {Ref, Code}) when is_reference(Ref) -> set_labels(Labels, {Ref, Code}) when is_reference(Ref) ->
{maps:get(Ref, Labels), [ set_labels(Labels, I) || I <- Code ]}; {maps:get(Ref, Labels), [ set_labels(Labels, I) || I <- Code ]};
set_labels(_Labels, loop) -> aeb_fate_ops:jump(0);
set_labels(Labels, {jump, Ref}) -> aeb_fate_ops:jump(maps:get(Ref, Labels)); set_labels(Labels, {jump, Ref}) -> aeb_fate_ops:jump(maps:get(Ref, Labels));
set_labels(Labels, {jumpif, Arg, Ref}) -> aeb_fate_ops:jumpif(Arg, maps:get(Ref, Labels)); set_labels(Labels, {jumpif, Arg, Ref}) -> aeb_fate_ops:jumpif(Arg, maps:get(Ref, Labels));
set_labels(Labels, {switch, Arg, Refs}) -> set_labels(Labels, {switch, Arg, Refs}) ->
@@ -1570,11 +1501,3 @@ set_labels(_, I) -> I.
with_ixs(Xs) -> with_ixs(Xs) ->
lists:zip(lists:seq(0, length(Xs) - 1), Xs). lists:zip(lists:seq(0, length(Xs) - 1), Xs).
drop_common_suffix(Xs, Ys) ->
drop_common_suffix_r(lists:reverse(Xs), lists:reverse(Ys)).
drop_common_suffix_r([X | Xs], [X | Ys]) ->
drop_common_suffix_r(Xs, Ys);
drop_common_suffix_r(Xs, Ys) ->
{lists:reverse(Xs), lists:reverse(Ys)}.
+1 -15
View File
@@ -13,10 +13,8 @@
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,
in_main_contract/1,
qualify/2, qualify/2,
set_functions/2, set_functions/2,
map_typerep/2, map_typerep/2,
@@ -50,7 +48,6 @@
, 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) ->
@@ -68,19 +65,16 @@ 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,
#{ "bool" => Word #{ "bool" => Word
, "int" => Word , "int" => Word
, "char" => Word
, "bits" => Word , "bits" => Word
, "string" => fun([]) -> string end , "string" => fun([]) -> string end
, "address" => Word , "address" => Word
, "hash" => Word , "hash" => Word
, "unit" => fun([]) -> {tuple, []} end
, "signature" => fun([]) -> {tuple, [word, word]} end , "signature" => fun([]) -> {tuple, [word, word]} end
, "oracle" => fun([_, _]) -> word end , "oracle" => fun([_, _]) -> word end
, "oracle_query" => fun([_, _]) -> word end , "oracle_query" => fun([_, _]) -> word end
@@ -109,10 +103,6 @@ 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 }.
@@ -122,10 +112,6 @@ enter_namespace(NS, Icode = #{ namespace := NS1 }) ->
enter_namespace(NS, Icode) -> enter_namespace(NS, Icode) ->
Icode#{ namespace => NS }. Icode#{ namespace => NS }.
-spec in_main_contract(icode()) -> boolean().
in_main_contract(#{ namespace := {con, _, Main}, contract_name := Main }) -> true;
in_main_contract(_Icode) -> false.
-spec get_namespace(icode()) -> false | aeso_syntax:con() | aeso_syntax:qcon(). -spec get_namespace(icode()) -> false | aeso_syntax:con() | aeso_syntax:qcon().
get_namespace(Icode) -> maps:get(namespace, Icode, false). get_namespace(Icode) -> maps:get(namespace, Icode, false).
+2 -2
View File
@@ -27,7 +27,7 @@ convert(#{ contract_name := _ContractName
}, },
_Options) -> _Options) ->
%% Create a function dispatcher %% Create a function dispatcher
DispatchFun = {"%main", [], [{"arg", "_"}], DispatchFun = {"_main", [], [{"arg", "_"}],
{switch, {var_ref, "arg"}, {switch, {var_ref, "arg"},
[{{tuple, [fun_hash(Fun), [{{tuple, [fun_hash(Fun),
{tuple, make_args(Args)}]}, {tuple, make_args(Args)}]},
@@ -44,7 +44,7 @@ convert(#{ contract_name := _ContractName
%% taken from the stack %% taken from the stack
StopLabel = make_ref(), StopLabel = make_ref(),
StatefulStopLabel = make_ref(), StatefulStopLabel = make_ref(),
MainFunction = lookup_fun(Funs, "%main"), MainFunction = lookup_fun(Funs, "_main"),
StateTypeValue = aeso_ast_to_icode:type_value(StateType), StateTypeValue = aeso_ast_to_icode:type_value(StateType),
+4 -60
View File
@@ -9,14 +9,12 @@
-module(aeso_parse_lib). -module(aeso_parse_lib).
-export([parse/2, -export([parse/2,
return/1, fail/0, fail/1, fail/2, map/2, bind/2, return/1, fail/0, fail/1, map/2, bind/2,
lazy/1, choice/1, choice/2, tok/1, layout/0, lazy/1, choice/1, choice/2, tok/1, layout/0,
left/2, right/2, between/3, optional/1, left/2, right/2, between/3, optional/1,
many/1, many1/1, sep/2, sep1/2, many/1, many1/1, sep/2, sep1/2,
infixl/2, infixr/2]). infixl/2, infixr/2]).
-export([current_file/0, set_current_file/1]).
%% -- Types ------------------------------------------------------------------ %% -- Types ------------------------------------------------------------------
-export_type([parser/1, parser_expr/1, pos/0, token/0, tokens/0]). -export_type([parser/1, parser_expr/1, pos/0, token/0, tokens/0]).
@@ -100,10 +98,6 @@ apply_p(X, K) -> K(X).
-spec lazy(fun(() -> parser(A))) -> parser(A). -spec lazy(fun(() -> parser(A))) -> parser(A).
lazy(Delayed) -> ?lazy(Delayed). lazy(Delayed) -> ?lazy(Delayed).
%% @doc A parser that always fails at a known location.
-spec fail(pos(), term()) -> parser(none()).
fail(Pos, Err) -> ?fail({Pos, Err}).
%% @doc A parser that always fails. %% @doc A parser that always fails.
-spec fail(term()) -> parser(none()). -spec fail(term()) -> parser(none()).
fail(Err) -> ?fail(Err). fail(Err) -> ?fail(Err).
@@ -161,7 +155,7 @@ layout() -> ?layout.
-spec parse(parser(A), tokens()) -> {ok, A} | {error, term()}. -spec parse(parser(A), tokens()) -> {ok, A} | {error, term()}.
parse(P, S) -> parse(P, S) ->
case parse1(apply_p(P, fun(X) -> {return_plus, X, {fail, no_error}} end), S) of case parse1(apply_p(P, fun(X) -> {return_plus, X, {fail, no_error}} end), S) of
{[], {Pos, Err}} -> {error, {add_current_file(Pos), parse_error, flatten_error(Err)}}; {[], {Pos, Err}} -> {error, {Pos, parse_error, flatten_error(Err)}};
{[A], _} -> {ok, A}; {[A], _} -> {ok, A};
{As, _} -> {error, {{1, 1}, ambiguous_parse, As}} {As, _} -> {error, {{1, 1}, ambiguous_parse, As}}
end. end.
@@ -291,7 +285,7 @@ parse1({tok_bind, Map}, Ts, Acc, Err) ->
%% y + y)(4) %% y + y)(4)
case maps:get(vclose, Map, '$not_found') of case maps:get(vclose, Map, '$not_found') of
'$not_found' -> '$not_found' ->
{Acc, unexpected_token_error(Ts, maps:keys(Map), T)}; {Acc, unexpected_token_error(Ts, T)};
F -> F ->
VClose = {vclose, pos(T)}, VClose = {vclose, pos(T)},
Ts2 = pop_layout(VClose, Ts#ts{ last = VClose }), Ts2 = pop_layout(VClose, Ts#ts{ last = VClose }),
@@ -328,52 +322,12 @@ current_pos(#ts{ tokens = [T | _] }) -> pos(T);
current_pos(#ts{ last = T }) -> end_pos(pos(T)). current_pos(#ts{ last = T }) -> end_pos(pos(T)).
-spec mk_error(#ts{}, term()) -> error(). -spec mk_error(#ts{}, term()) -> error().
mk_error(_Ts, {Pos, Err}) ->
{Pos, Err};
mk_error(Ts, Err) -> mk_error(Ts, Err) ->
{current_pos(Ts), Err}. {current_pos(Ts), Err}.
-spec unexpected_token_error(#ts{}, token()) -> error(). -spec unexpected_token_error(#ts{}, token()) -> error().
unexpected_token_error(Ts, T) -> unexpected_token_error(Ts, T) ->
unexpected_token_error(Ts, [], T). mk_error(Ts, io_lib:format("Unexpected token ~p", [tag(T)])).
unexpected_token_error(Ts, Expect, {Tag, _}) when Tag == vclose; Tag == vsemi ->
Braces = [')', ']', '}'],
Fix = case lists:filter(fun(T) -> lists:member(T, Braces) end, Expect) of
[] -> " Probable causes:\n"
" - something is missing in the previous statement, or\n"
" - this line should be indented more.";
[T | _] -> io_lib:format(" Did you forget a ~p?", [T])
end,
Msg = io_lib:format("Unexpected indentation.~s", [Fix]),
mk_error(Ts, Msg);
unexpected_token_error(Ts, Expect, T) ->
ExpectCon = lists:member(con, Expect),
ExpectId = lists:member(id, Expect),
Fix = case T of
{id, _, X} when ExpectCon, hd(X) /= $_ -> io_lib:format(" Did you mean ~s?", [mk_upper(X)]);
{con, _, X} when ExpectId -> io_lib:format(" Did you mean ~s?", [mk_lower(X)]);
{qcon, _, Xs} when ExpectCon -> io_lib:format(" Did you mean ~s?", [lists:last(Xs)]);
{qid, _, Xs} when ExpectId -> io_lib:format(" Did you mean ~s?", [lists:last(Xs)]);
_ -> ""
end,
mk_error(Ts, io_lib:format("Unexpected ~s.~s", [describe(T), Fix])).
mk_upper([C | Rest]) -> string:to_upper([C]) ++ Rest.
mk_lower([C | Rest]) -> string:to_lower([C]) ++ Rest.
describe({id, _, X}) -> io_lib:format("identifier ~s", [X]);
describe({con, _, X}) -> io_lib:format("identifier ~s", [X]);
describe({qid, _, Xs}) -> io_lib:format("qualified identifier ~s", [string:join(Xs, ".")]);
describe({qcon, _, Xs}) -> io_lib:format("qualified identifier ~s", [string:join(Xs, ".")]);
describe({tvar, _, X}) -> io_lib:format("type variable ~s", [X]);
describe({char, _, _}) -> "character literal";
describe({string, _, _}) -> "string literal";
describe({hex, _, _}) -> "integer literal";
describe({int, _, _}) -> "integer literal";
describe({bytes, _, _}) -> "bytes literal";
describe(T) -> io_lib:format("token '~s'", [tag(T)]).
%% Get the next token from a token stream. Inserts layout tokens if necessary. %% Get the next token from a token stream. Inserts layout tokens if necessary.
-spec next_token(#ts{}) -> false | {token(), #ts{}}. -spec next_token(#ts{}) -> false | {token(), #ts{}}.
@@ -457,13 +411,3 @@ merge_with(Fun, Map1, Map2) ->
end, Map2, maps:to_list(Map1)) end, Map2, maps:to_list(Map1))
end. end.
%% Current source file
current_file() ->
get('$current_file').
set_current_file(File) ->
put('$current_file', File).
add_current_file({L, C}) -> {current_file(), L, C};
add_current_file(Pos) -> Pos.
+1 -1
View File
@@ -19,7 +19,7 @@
-import(aeso_parse_lib, -import(aeso_parse_lib,
[tok/1, tok/2, between/3, many/1, many1/1, sep/2, sep1/2, [tok/1, tok/2, between/3, many/1, many1/1, sep/2, sep1/2,
infixl/1, infixr/1, choice/1, choice/2, return/1, layout/0, infixl/1, infixr/1, choice/1, choice/2, return/1, layout/0,
fail/0, fail/1, fail/2, map/2, infixl/2, infixr/2, infixl1/2, infixr1/2, fail/0, fail/1, map/2, infixl/2, infixr/2, infixl1/2, infixr1/2,
left/2, right/2, optional/1]). left/2, right/2, optional/1]).
+42 -125
View File
@@ -6,45 +6,29 @@
-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").
-import(aeso_parse_lib, [current_file/0, set_current_file/1]).
-type parse_result() :: aeso_syntax:ast() | none(). -type parse_result() :: {ok, aeso_syntax:ast()}
| {error, {aeso_parse_lib:pos(), atom(), term()}}
-type include_hash() :: {string(), binary()}. | {error, {aeso_parse_lib:pos(), atom()}}.
-spec string(string()) -> parse_result(). -spec string(string()) -> parse_result().
string(String) -> string(String) ->
string(String, sets:new(), []). string(String, []).
-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} ->
case expand_includes(AST, Included, Opts) of expand_includes(AST, Opts);
{ok, AST1} -> AST1; Err = {error, _} ->
{error, Err} -> parse_error(Err) Err
end;
{error, Err} ->
parse_error(Err)
end. end.
type(String) -> type(String) ->
case parse_and_scan(type(), String, []) of parse_and_scan(type(), String, []).
{ok, AST} -> {ok, AST};
{error, Err} -> {error, [mk_error(Err)]}
end.
parse_and_scan(P, S, Opts) -> parse_and_scan(P, S, Opts) ->
set_current_file(proplists:get_value(src_file, Opts, no_file)), set_current_file(proplists:get_value(src_file, Opts, no_file)),
@@ -53,28 +37,6 @@ parse_and_scan(P, S, Opts) ->
Error -> Error Error -> Error
end. end.
-dialyzer({nowarn_function, parse_error/1}).
parse_error(Err) ->
aeso_errors:throw(mk_error(Err)).
mk_p_err(Pos, Msg) ->
aeso_errors:new(parse_error, mk_pos(Pos), lists:flatten(Msg)).
mk_error({Pos, ScanE}) when ScanE == scan_error; ScanE == scan_error_no_state ->
mk_p_err(Pos, "Scan error\n");
mk_error({Pos, parse_error, Err}) ->
Msg = io_lib:format("~s\n", [Err]),
mk_p_err(Pos, Msg);
mk_error({Pos, ambiguous_parse, As}) ->
Msg = io_lib:format("Ambiguous parse result: ~p\n", [As]),
mk_p_err(Pos, Msg);
mk_error({Pos, include_error, File}) ->
Msg = io_lib:format("Couldn't find include file '~s'\n", [File]),
mk_p_err(Pos, Msg).
mk_pos({Line, Col}) -> aeso_errors:pos(Line, Col);
mk_pos({File, Line, Col}) -> aeso_errors:pos(File, Line, Col).
%% -- Parsing rules ---------------------------------------------------------- %% -- Parsing rules ----------------------------------------------------------
file() -> choice([], block(decl())). file() -> choice([], block(decl())).
@@ -84,7 +46,6 @@ 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})
@@ -109,7 +70,7 @@ fun_or_entry() ->
?RULE(keyword(entrypoint), {entrypoint, _1})]). ?RULE(keyword(entrypoint), {entrypoint, _1})]).
modifiers() -> modifiers() ->
many(choice([token(stateful), token(payable), token(private), token(public)])). many(choice([token(stateful), 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);
@@ -177,7 +138,7 @@ type() -> ?LAZY_P(type100()).
type100() -> type200(). type100() -> type200().
type200() -> type200() ->
?RULE(many({type300(), keyword('=>')}), type300(), fun_t(_1, _2)). ?RULE(many({fun_domain(), keyword('=>')}), type300(), fun_t(_1, _2)).
type300() -> type300() ->
?RULE(sep1(type400(), tok('*')), tuple_t(get_ann(lists:nth(1, _1)), _1)). ?RULE(sep1(type400(), tok('*')), tuple_t(get_ann(lists:nth(1, _1)), _1)).
@@ -196,15 +157,16 @@ type400() ->
typeAtom() -> typeAtom() ->
?LAZY_P(choice( ?LAZY_P(choice(
[ parens(type()) [ parens(type())
, args_t()
, id(), token(con), token(qcon), token(qid), tvar() , id(), token(con), token(qcon), token(qid), tvar()
])). ])).
args_t() -> fun_domain() -> ?LAZY_P(choice(
?LAZY_P(choice( [ ?RULE(tok('('), tok(')'), [])
[ ?RULE(tok('('), tok(')'), {args_t, get_ann(_1), []}) %% Note avoidance of ambiguity: `(int)` can be treated as:
%% Singleton case handled separately %% - literally `int`
, ?RULE(tok('('), type(), tok(','), sep1(type(), tok(',')), tok(')'), {args_t, get_ann(_1), [_2|_4]}) %% - 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 -------------------------------------------------------------
@@ -268,25 +230,11 @@ 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})
@@ -452,6 +400,12 @@ bracket_list(P) -> brackets(comma_sep(P)).
-spec pos_ann(ann_line(), ann_col()) -> ann(). -spec pos_ann(ann_line(), ann_col()) -> ann().
pos_ann(Line, Col) -> [{file, current_file()}, {line, Line}, {col, Col}]. pos_ann(Line, Col) -> [{file, current_file()}, {line, Line}, {col, Col}].
current_file() ->
get('$current_file').
set_current_file(File) ->
put('$current_file', File).
ann_pos(Ann) -> ann_pos(Ann) ->
{proplists:get_value(file, Ann), {proplists:get_value(file, Ann),
proplists:get_value(line, Ann), proplists:get_value(line, Ann),
@@ -521,15 +475,12 @@ tuple_t(_Ann, [Type]) -> Type; %% Not a tuple
tuple_t(Ann, Types) -> {tuple_t, Ann, Types}. tuple_t(Ann, Types) -> {tuple_t, Ann, Types}.
fun_t(Domains, Type) -> fun_t(Domains, Type) ->
lists:foldr(fun({{args_t, _, Dom}, Ann}, T) -> {fun_t, Ann, [], Dom, T}; lists:foldr(fun({Dom, Ann}, T) -> {fun_t, Ann, [], Dom, T} end,
({Dom, Ann}, T) -> {fun_t, Ann, [], [Dom], T} end,
Type, Domains). Type, Domains).
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}.
list_comp_e(Ann, Expr, Binds) -> {list_comp, Ann, Expr, Binds}.
-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}) ->
{app, Ann, Con, lists:map(fun parse_pattern/1, Es)}; {app, Ann, Con, lists:map(fun parse_pattern/1, Es)};
@@ -554,9 +505,14 @@ parse_pattern(E) -> bad_expr_err("Not a valid pattern", E).
parse_field_pattern({field, Ann, F, E}) -> parse_field_pattern({field, Ann, F, E}) ->
{field, Ann, F, parse_pattern(E)}. {field, Ann, F, parse_pattern(E)}.
return_error({no_file, L, C}, Err) ->
fail(io_lib:format("~p:~p:\n~s", [L, C, Err]));
return_error({F, L, C}, Err) ->
fail(io_lib:format("In ~s at ~p:~p:\n~s", [F, L, C, Err])).
-spec ret_doc_err(ann(), prettypr:document()) -> aeso_parse_lib:parser(none()). -spec ret_doc_err(ann(), prettypr:document()) -> aeso_parse_lib:parser(none()).
ret_doc_err(Ann, Doc) -> ret_doc_err(Ann, Doc) ->
fail(ann_pos(Ann), prettypr:format(Doc)). return_error(ann_pos(Ann), prettypr:format(Doc)).
-spec bad_expr_err(string(), aeso_syntax:expr()) -> aeso_parse_lib:parser(none()). -spec bad_expr_err(string(), aeso_syntax:expr()) -> aeso_parse_lib:parser(none()).
bad_expr_err(Reason, E) -> bad_expr_err(Reason, E) ->
@@ -565,37 +521,26 @@ 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, [], Opts).
expand_includes(AST, Included, Opts) -> expand_includes([], Acc, _Opts) ->
Ann = [{origin, system}],
AST1 = [ {include, Ann, {string, Ann, File}}
|| File <- lists:usort(auto_imports(AST)) ] ++ AST,
expand_includes(AST1, Included, [], Opts).
expand_includes([], _Included, Acc, _Opts) ->
{ok, lists:reverse(Acc)}; {ok, lists:reverse(Acc)};
expand_includes([{include, Ann, {string, _SAnn, File}} | AST], Included, Acc, Opts) -> expand_includes([{include, _, S = {string, _, File}} | AST], Acc, Opts) ->
case get_include_code(File, Ann, Opts) of case read_file(File, Opts) of
{ok, Code} -> {ok, Bin} ->
Hashed = hash_include(File, Code),
case sets:is_element(Hashed, Included) of
false ->
Opts1 = lists:keystore(src_file, 1, Opts, {src_file, File}), Opts1 = lists:keystore(src_file, 1, Opts, {src_file, File}),
Included1 = sets:add_element(Hashed, Included), case string(binary_to_list(Bin), Opts1) of
case parse_and_scan(file(), Code, Opts1) of
{ok, AST1} -> {ok, AST1} ->
expand_includes(AST1 ++ AST, Included1, Acc, Opts); expand_includes(AST1 ++ AST, Acc, Opts);
Err = {error, _} -> Err = {error, _} ->
Err Err
end; end;
true -> {error, _} ->
expand_includes(AST, Included, Acc, Opts) {error, {get_pos(S), include_error, File}}
end; end;
Err = {error, _} -> expand_includes([E | AST], Acc, Opts) ->
Err expand_includes(AST, [E | Acc], Opts).
end;
expand_includes([E | AST], Included, 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
@@ -610,31 +555,3 @@ read_file(File, Opts) ->
end end
end. end.
stdlib_options() ->
[{include, {file_system, [aeso_stdlib:stdlib_include_path()]}}].
get_include_code(File, Ann, Opts) ->
case {read_file(File, Opts), read_file(File, stdlib_options())} of
{{ok, _}, {ok,_ }} ->
fail(ann_pos(Ann), "Illegal redefinition of standard library " ++ File);
{_, {ok, Bin}} ->
{ok, binary_to_list(Bin)};
{{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)}.
auto_imports({comprehension_bind, _, _}) -> [<<"ListInternal.aes">>];
auto_imports({'..', _}) -> [<<"ListInternal.aes">>];
auto_imports(L) when is_list(L) ->
lists:flatmap(fun auto_imports/1, L);
auto_imports(T) when is_tuple(T) ->
auto_imports(tuple_to_list(T));
auto_imports(_) -> [].
-4
View File
@@ -235,16 +235,12 @@ 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)); follow(hsep(args_type(Named ++ Args), text("=>")), type(Ret));
type({type_sig, _, Named, Args, 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), args_type(Args)); beside(type(Type), args_type(Args));
type({tuple_t, _, Args}) -> type({tuple_t, _, Args}) ->
tuple_type(Args); tuple_type(Args);
type({args_t, _, Args}) ->
args_type(Args);
type({bytes_t, _, any}) -> text("bytes(_)"); type({bytes_t, _, any}) -> text("bytes(_)");
type({bytes_t, _, Len}) -> type({bytes_t, _, Len}) ->
text(lists:concat(["bytes(", Len, ")"])); text(lists:concat(["bytes(", Len, ")"]));
+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", "payable", "true", "false", "mod", "public", "entrypoint", "private", "indexed", "namespace"], "stateful", "true", "false", "mod", "public", "entrypoint", "private", "indexed", "namespace"],
KW = string:join(Keywords, "|"), KW = string:join(Keywords, "|"),
Rules = Rules =
-17
View File
@@ -1,17 +0,0 @@
%%%-------------------------------------------------------------------
%%% @author Radosław Rowicki
%%% @copyright (C) 2019, Aeternity Anstalt
%%% @doc
%%% Standard library for Sophia
%%% @end
%%% Created : 6 July 2019
%%%
%%%-------------------------------------------------------------------
-module(aeso_stdlib).
-export([stdlib_include_path/0]).
stdlib_include_path() ->
filename:join([code:priv_dir(aesophia), "stdlib"]).
-6
View File
@@ -59,7 +59,6 @@
-type type() :: {fun_t, ann(), [named_arg_t()], [type()], type()} -type type() :: {fun_t, ann(), [named_arg_t()], [type()], type()}
| {app_t, ann(), type(), [type()]} | {app_t, ann(), type(), [type()]}
| {tuple_t, ann(), [type()]} | {tuple_t, ann(), [type()]}
| {args_t, ann(), [type()]} %% old tuple syntax, old for error messages
| {bytes_t, ann(), integer() | any} | {bytes_t, ann(), integer() | any}
| id() | qid() | id() | qid()
| con() | qcon() %% contracts | con() | qcon() %% contracts
@@ -93,7 +92,6 @@
| {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
@@ -106,10 +104,6 @@
| id() | qid() | con() | qcon() | id() | qid() | con() | qcon()
| constant(). | constant().
-type comprehension_exp() :: [ {comprehension_bind, id(), expr()}
| {comprehension_if, ann(), 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
+6 -15
View File
@@ -49,7 +49,7 @@ fold(Alg = #alg{zero = Zero, plus = Plus, scoped = Scoped}, Fun, K, X) ->
{type_def, _, I, _, D} -> Plus(BindType(I), Decl(D)); {type_def, _, I, _, D} -> Plus(BindType(I), Decl(D));
{fun_decl, _, _, T} -> Type(T); {fun_decl, _, _, T} -> Type(T);
{letval, _, F, T, E} -> Sum([BindExpr(F), Type(T), Expr(E)]); {letval, _, F, T, E} -> Sum([BindExpr(F), Type(T), Expr(E)]);
{letfun, _, F, Xs, T, E} -> Sum([BindExpr(F), Type(T), Expr(Xs ++ [E])]); {letfun, _, F, Xs, T, E} -> Sum([BindExpr(F), Type(T), Scoped(BindExpr(Xs), Expr(E))]);
%% typedef() %% typedef()
{alias_t, T} -> Type(T); {alias_t, T} -> Type(T);
{record_t, Fs} -> Type(Fs); {record_t, Fs} -> Type(Fs);
@@ -71,15 +71,6 @@ 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]);
@@ -92,7 +83,7 @@ fold(Alg = #alg{zero = Zero, plus = Plus, scoped = Scoped}, Fun, K, X) ->
{field, _, LV, E} -> Expr([LV, E]); {field, _, LV, E} -> Expr([LV, E]);
{field, _, LV, _, E} -> Expr([LV, E]); {field, _, LV, _, E} -> Expr([LV, E]);
%% arg() %% arg()
{arg, _, Y, T} -> Plus(BindExpr(Y), Type(T)); {arg, _, X, T} -> Plus(Expr(X), Type(T));
%% alt() %% alt()
{'case', _, P, E} -> Scoped(BindExpr(P), Expr(E)); {'case', _, P, E} -> Scoped(BindExpr(P), Expr(E));
%% elim() %% elim()
@@ -124,12 +115,12 @@ used_types([Top] = _CurrentNS, T) ->
entity_alg() -> entity_alg() ->
IsBound = fun({K, _}) -> lists:member(K, [bound_term, bound_type]) end, IsBound = fun({K, _}) -> lists:member(K, [bound_term, bound_type]) end,
Unbind = fun(bound_term) -> term; (bound_type) -> type end, Unbind = fun(bound_term) -> term; (bound_type) -> type end,
Remove = fun(Keys, Map) -> maps:without(Keys, Map) end, Remove = fun(Keys, Map) -> lists:foldl(fun maps:remove/2, Map, Keys) end,
Scoped = fun(Xs, Ys) -> Scoped = fun(Xs, Ys) ->
Bound = [E || E <- maps:keys(Xs), IsBound(E)], Bound = [E || E <- maps:keys(Ys), IsBound(E)],
Others = Remove(Bound, Ys),
Bound1 = [ {Unbind(Tag), X} || {Tag, X} <- Bound ], Bound1 = [ {Unbind(Tag), X} || {Tag, X} <- Bound ],
Others = Remove(Bound1, Ys), maps:merge(Remove(Bound1, Xs), Others)
maps:merge(Remove(Bound, Xs), Others)
end, end,
#alg{ zero = #{} #alg{ zero = #{}
, plus = fun maps:merge/2 , plus = fun maps:merge/2
+1 -1
View File
@@ -1,6 +1,6 @@
{application, aesophia, {application, aesophia,
[{description, "Contract Language for aeternity"}, [{description, "Contract Language for aeternity"},
{vsn, "4.0.0-rc3"}, {vsn, "3.2.0"},
{registered, []}, {registered, []},
{applications, {applications,
[kernel, [kernel,
+7 -61
View File
@@ -72,58 +72,6 @@ encode_decode_sophia_test() ->
ok = Check("r", "{x = (\"foo\", 0), y = Red}"), ok = Check("r", "{x = (\"foo\", 0), y = Red}"),
ok. ok.
to_sophia_value_neg_test() ->
Code = [ "contract Foo =\n"
" entrypoint x(y : int) : string = \"hello\"\n" ],
{error, [Err1]} = aeso_compiler:to_sophia_value(Code, "x", ok, encode(12)),
?assertEqual("Data error:\nFailed to decode binary as type string\n", aeso_errors:pp(Err1)),
{error, [Err2]} = aeso_compiler:to_sophia_value(Code, "x", ok, encode(12), [{backend, fate}]),
?assertEqual("Data error:\nFailed to decode binary as type string\n", aeso_errors:pp(Err2)),
{error, [Err3]} = aeso_compiler:to_sophia_value(Code, "x", revert, encode(12)),
?assertEqual("Data error:\nCould not interpret the revert message\n", aeso_errors:pp(Err3)),
{error, [Err4]} = aeso_compiler:to_sophia_value(Code, "x", revert, encode(12), [{backend, fate}]),
?assertEqual("Data error:\nCould not deserialize the revert message\n", aeso_errors:pp(Err4)),
ok.
encode_calldata_neg_test() ->
Code = [ "contract Foo =\n"
" entrypoint x(y : int) : string = \"hello\"\n" ],
ExpErr1 = "Type error at line 5, col 34:\nCannot unify int\n and bool\n"
"when checking the application at line 5, column 34 of\n"
" x : (int) => string\nto arguments\n true : bool\n",
{error, [Err1]} = aeso_compiler:create_calldata(Code, "x", ["true"]),
?assertEqual(ExpErr1, aeso_errors:pp(Err1)),
{error, [Err2]} = aeso_compiler:create_calldata(Code, "x", ["true"], [{backend, fate}]),
?assertEqual(ExpErr1, aeso_errors:pp(Err2)),
ok.
decode_calldata_neg_test() ->
Code1 = [ "contract Foo =\n"
" entrypoint x(y : int) : string = \"hello\"\n" ],
Code2 = [ "contract Foo =\n"
" entrypoint x(y : string) : int = 42\n" ],
{ok, CallDataAEVM} = aeso_compiler:create_calldata(Code1, "x", ["42"]),
{ok, CallDataFATE} = aeso_compiler:create_calldata(Code1, "x", ["42"], [{backend, fate}]),
{error, [Err1]} = aeso_compiler:decode_calldata(Code2, "x", CallDataAEVM),
?assertEqual("Data error:\nFailed to decode calldata as type {tuple,[string]}\n", aeso_errors:pp(Err1)),
{error, [Err2]} = aeso_compiler:decode_calldata(Code2, "x", <<1,2,3>>, [{backend, fate}]),
?assertEqual("Data error:\nFailed to decode calldata binary\n", aeso_errors:pp(Err2)),
{error, [Err3]} = aeso_compiler:decode_calldata(Code2, "x", CallDataFATE, [{backend, fate}]),
?assertEqual("Data error:\nCannot translate FATE value \"*\"\n to Sophia type (string)\n", aeso_errors:pp(Err3)),
{error, [Err4]} = aeso_compiler:decode_calldata(Code2, "y", CallDataAEVM),
?assertEqual("Data error at line 1, col 1:\nFunction 'y' is missing in contract\n", aeso_errors:pp(Err4)),
{error, [Err5]} = aeso_compiler:decode_calldata(Code2, "y", CallDataFATE, [{backend, fate}]),
?assertEqual("Data error at line 1, col 1:\nFunction 'y' is missing in contract\n", aeso_errors:pp(Err5)),
ok.
encode_decode_sophia_string(SophiaType, String) -> 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"
@@ -132,11 +80,11 @@ encode_decode_sophia_string(SophiaType, String) ->
, " 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], [no_code]) of case aeso_compiler:check_call(lists:flatten(Code), "foo", [String], []) 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, [no_code]) of case aeso_compiler:to_sophia_value(Code, "foo", ok, Data) 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} ->
@@ -204,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"], [no_code]), "oq_1111111111111111111111111111113AFEFpt5"], []),
ok. ok.
@@ -213,10 +161,8 @@ permissive_literals_fail_test() ->
"contract OracleTest =\n" "contract OracleTest =\n"
" 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, [Err]} = {error, <<"Type errors\nCannot unify", _/binary>>} =
aeso_compiler:check_call(Contract, "haxx", ["#123"], []), aeso_compiler:check_call(Contract, "haxx", ["#123"], []),
?assertMatch("Type error at line 3, col 5:\nCannot unify" ++ _, aeso_errors:pp(Err)),
?assertEqual(type_error, aeso_errors:type(Err)),
ok. ok.
encode_decode_calldata(FunName, Types, Args) -> encode_decode_calldata(FunName, Types, Args) ->
@@ -227,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),
{ok, _, {ArgTypes, RetType}, _} = aeso_compiler:check_call(Code, FunName, Args, [{backend, aevm}, no_code]), {ok, _, {ArgTypes, RetType}, _} = aeso_compiler:check_call(Code, FunName, Args, [{backend, aevm}]),
?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),
@@ -236,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),
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,
+11 -14
View File
@@ -14,22 +14,20 @@ test_contract(N) ->
?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 = <<"payable contract C =\n" Contract = <<"contract C =\n"
" payable stateful entrypoint a(i : int) = i+1\n">>, " 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 => true, stateful => false}]}},
payable => true}]}}, DecACI = <<"contract C =\n"
DecACI = <<"payable contract C =\n" " entrypoint a : (int) => int\n">>,
" payable entrypoint a : (int) => int\n">>,
{Contract,MapACI,DecACI}; {Contract,MapACI,DecACI};
test_cases(2) -> test_cases(2) ->
@@ -37,7 +35,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">>, payable => false, #{name => <<"C">>,
type_defs => type_defs =>
[#{name => <<"allan">>, [#{name => <<"allan">>,
typedef => <<"int">>, typedef => <<"int">>,
@@ -48,8 +46,7 @@ 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">>,
@@ -67,8 +64,8 @@ test_cases(3) ->
type => type =>
#{<<"C.bert">> => [<<"string">>]}}], #{<<"C.bert">> => [<<"string">>]}}],
name => <<"a">>,returns => <<"int">>, name => <<"a">>,returns => <<"int">>,
stateful => false, payable => false}], stateful => false}],
name => <<"C">>, payable => false, name => <<"C">>,
event => #{variant => [#{<<"SingleEventDefined">> => []}]}, event => #{variant => [#{<<"SingleEventDefined">> => []}]},
state => <<"unit">>, state => <<"unit">>,
type_defs => type_defs =>
@@ -107,11 +104,11 @@ aci_test_contract(Name) ->
check_stub(Stub, Options) -> check_stub(Stub, Options) ->
case aeso_parser:string(binary_to_list(Stub), Options) of case aeso_parser:string(binary_to_list(Stub), Options) of
Ast -> {ok, Ast} ->
try try
%% io:format("AST: ~120p\n", [Ast]), %% io:format("AST: ~120p\n", [Ast]),
aeso_ast_infer_types:infer(Ast, []) aeso_ast_infer_types:infer(Ast, [])
catch throw:{type_errors, TE} -> catch _:{type_errors, TE} ->
io:format("Type error:\n~s\n", [TE]), io:format("Type error:\n~s\n", [TE]),
error(TE); error(TE);
_:R -> _:R ->
+2 -3
View File
@@ -116,11 +116,10 @@ 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", "#0102030405060708090a0b0c0d0e0f101718192021222324252627282930313233343536373839401a1b1c1d1e1f20212223242526272829303132333435363738"]}, {"bitcoin_auth", "authorize", ["1", "#0102030405060708090a0b0c0d0e0f101718192021222324252627282930313233343536373839401a1b1c1d1e1f202122232425262728293031323334353637"]},
{"bitcoin_auth", "to_sign", ["#0102030405060708090a0b0c0d0e0f1017181920212223242526272829303132", "2"]}, {"bitcoin_auth", "to_sign", ["#0102030405060708090a0b0c0d0e0f1017181920212223242526272829303132", "2"]},
{"stub", "foo", ["42"]}, {"stub", "foo", ["42"]},
{"stub", "foo", ["-42"]}, {"stub", "foo", ["-42"]}
{"payable", "foo", ["42"]}
]. ].
not_yet_compilable(fate) -> not_yet_compilable(fate) ->
+114 -430
View File
@@ -32,34 +32,16 @@ simple_compile_test_() ->
end end
end} || ContractName <- compilable_contracts(), Backend <- [aevm, fate], end} || ContractName <- compilable_contracts(), Backend <- [aevm, fate],
not lists:member(ContractName, not_yet_compilable(Backend))] ++ not lists:member(ContractName, not_yet_compilable(Backend))] ++
[ {"Test file not found error",
fun() ->
{error, Errors} = aeso_compiler:file("does_not_exist.aes"),
ExpErr = <<"File error:\ndoes_not_exist.aes: no such file or directory">>,
check_errors([ExpErr], Errors)
end} ] ++
[ {"Testing error messages of " ++ ContractName, [ {"Testing error messages of " ++ ContractName,
fun() -> fun() ->
Errors = compile(aevm, ContractName), case compile(aevm, ContractName) of
check_errors(ExpectedErrors, Errors) <<"Type errors\n", ErrorString/binary>> ->
check_errors(lists:sort(ExpectedErrors), ErrorString);
<<"Parse errors\n", ErrorString/binary>> ->
check_errors(lists:sort(ExpectedErrors), ErrorString)
end
end} || end} ||
{ContractName, ExpectedErrors} <- failing_contracts() ] ++ {ContractName, ExpectedErrors} <- failing_contracts() ] ++
[ {"Testing " ++ atom_to_list(Backend) ++ " code generation error messages of " ++ ContractName,
fun() ->
Errors = compile(Backend, ContractName),
Expect =
case is_binary(ExpectedError) of
true -> [ExpectedError];
false ->
case proplists:get_value(Backend, ExpectedError, no_error) of
no_error -> no_error;
Err -> [Err]
end
end,
check_errors(Expect, Errors)
end} ||
{ContractName, ExpectedError} <- failing_code_gen_contracts(),
Backend <- [aevm, fate] ] ++
[ {"Testing include with explicit files", [ {"Testing include with explicit files",
fun() -> fun() ->
FileSystem = maps:from_list( FileSystem = maps:from_list(
@@ -71,25 +53,19 @@ 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 for " ++ atom_to_list(Backend), [ {"Testing deadcode elimination",
fun() -> fun() ->
#{ byte_code := NoDeadCode } = compile(Backend, "nodeadcode"), #{ byte_code := NoDeadCode } = compile(aevm, "nodeadcode"),
#{ byte_code := DeadCode } = compile(Backend, "deadcode"), #{ byte_code := DeadCode } = compile(aevm, "deadcode"),
SizeNoDeadCode = byte_size(NoDeadCode), SizeNoDeadCode = byte_size(NoDeadCode),
SizeDeadCode = byte_size(DeadCode), SizeDeadCode = byte_size(DeadCode),
Delta = if Backend == aevm -> 40; ?assertMatch({_, _, true}, {SizeDeadCode, SizeNoDeadCode, SizeDeadCode + 40 < SizeNoDeadCode}),
Backend == fate -> 20 end,
?assertMatch({_, _, true}, {SizeDeadCode, SizeNoDeadCode, SizeDeadCode + Delta < SizeNoDeadCode}),
ok ok
end} || Backend <- [aevm, fate] ] ++ end} ].
[].
check_errors(no_error, Actual) -> ?assertMatch(#{}, Actual); check_errors(Expect, ErrorString) ->
check_errors(Expect, #{}) -> %% This removes the final single \n as well.
?assertEqual({error, Expect}, ok); Actual = binary:split(<<ErrorString/binary,$\n>>, <<"\n\n">>, [global,trim]),
check_errors(Expect0, Actual0) ->
Expect = lists:sort(Expect0),
Actual = [ list_to_binary(string:trim(aeso_errors:pp(Err))) || Err <- Actual0 ],
case {Expect -- Actual, Actual -- Expect} of case {Expect -- Actual, Actual -- Expect} of
{[], Extra} -> ?assertMatch({unexpected, []}, {unexpected, Extra}); {[], Extra} -> ?assertMatch({unexpected, []}, {unexpected, Extra});
{Missing, []} -> ?assertMatch({missing, []}, {missing, Missing}); {Missing, []} -> ?assertMatch({missing, []}, {missing, Missing});
@@ -97,15 +73,13 @@ check_errors(Expect0, Actual0) ->
end. end.
compile(Backend, Name) -> compile(Backend, Name) ->
compile(Backend, Name, compile(Backend, Name, [{include, {file_system, [aeso_test_utils:contract_path()]}}]).
[{include, {file_system, [aeso_test_utils:contract_path()]}}]).
compile(Backend, Name, Options) -> compile(Backend, Name, Options) ->
String = aeso_test_utils:read_contract(Name), String = aeso_test_utils:read_contract(Name),
case aeso_compiler:from_string(String, [{src_file, Name ++ ".aes"}, {backend, Backend} | Options]) of case aeso_compiler:from_string(String, [{src_file, Name}, {backend, Backend} | Options]) of
{ok, Map} -> Map; {ok, Map} -> Map;
{error, ErrorString} when is_binary(ErrorString) -> ErrorString; {error, ErrorString} -> ErrorString
{error, Errors} -> Errors
end. end.
%% compilable_contracts() -> [ContractName]. %% compilable_contracts() -> [ContractName].
@@ -143,16 +117,8 @@ compilable_contracts() ->
"address_chain", "address_chain",
"namespace_bug", "namespace_bug",
"bytes_to_x", "bytes_to_x",
"bytes_concat",
"aens", "aens",
"tuple_match", "tuple_match"
"cyclic_include",
"stdlib_include",
"double_include",
"manual_stdlib_include",
"list_comp",
"payable",
"unapplied_builtins"
]. ].
not_yet_compilable(fate) -> []; not_yet_compilable(fate) -> [];
@@ -160,514 +126,232 @@ not_yet_compilable(aevm) -> [].
%% Contracts that should produce type errors %% Contracts that should produce type errors
-define(Pos(Kind, File, Line, Col), (list_to_binary(Kind))/binary, " error in '",
(list_to_binary(File))/binary, ".aes' at line " ??Line ", col " ??Col ":\n").
-define(Pos(Line, Col), ?Pos(__Kind, __File, Line, Col)).
-define(ERROR(Kind, Name, Errs),
(fun() ->
__Kind = Kind,
__File = ??Name,
{__File, Errs}
end)()).
-define(TYPE_ERROR(Name, Errs), ?ERROR("Type", Name, Errs)).
-define(PARSE_ERROR(Name, Errs), ?ERROR("Parse", Name, Errs)).
failing_contracts() -> failing_contracts() ->
%% Parse errors [ {"name_clash",
[ ?PARSE_ERROR(field_parse_error, [<<"Duplicate definitions of abort at\n"
[<<?Pos(5, 26)
"Cannot use nested fields or keys in record construction: p.x">>])
, ?PARSE_ERROR(vsemi, [<<?Pos(3, 3) "Unexpected indentation. Did you forget a '}'?">>])
, ?PARSE_ERROR(vclose, [<<?Pos(4, 3) "Unexpected indentation. Did you forget a ']'?">>])
, ?PARSE_ERROR(indent_fail, [<<?Pos(3, 2) "Unexpected token 'entrypoint'.">>])
%% Type errors
, ?TYPE_ERROR(name_clash,
[<<?Pos(14, 3)
"Duplicate definitions of abort at\n"
" - (builtin location)\n" " - (builtin location)\n"
" - line 14, column 3">>, " - line 14, column 3">>,
<<?Pos(15, 3) <<"Duplicate definitions of require at\n"
"Duplicate definitions of require at\n"
" - (builtin location)\n" " - (builtin location)\n"
" - line 15, column 3">>, " - line 15, column 3">>,
<<?Pos(11, 3) <<"Duplicate definitions of double_def at\n"
"Duplicate definitions of double_def at\n"
" - line 10, column 3\n" " - line 10, column 3\n"
" - line 11, column 3">>, " - line 11, column 3">>,
<<?Pos(5, 3) <<"Duplicate definitions of double_proto at\n"
"Duplicate definitions of double_proto at\n"
" - line 4, column 3\n" " - line 4, column 3\n"
" - line 5, column 3">>, " - line 5, column 3">>,
<<?Pos(8, 3) <<"Duplicate definitions of proto_and_def at\n"
"Duplicate definitions of proto_and_def at\n"
" - line 7, column 3\n" " - line 7, column 3\n"
" - line 8, column 3">>, " - line 8, column 3">>,
<<?Pos(16, 3) <<"Duplicate definitions of put at\n"
"Duplicate definitions of put at\n"
" - (builtin location)\n" " - (builtin location)\n"
" - line 16, column 3">>, " - line 16, column 3">>,
<<?Pos(17, 3) <<"Duplicate definitions of state at\n"
"Duplicate definitions of state at\n"
" - (builtin location)\n" " - (builtin location)\n"
" - line 17, column 3">>]) " - line 17, column 3">>]}
, ?TYPE_ERROR(type_errors, , {"type_errors",
[<<?Pos(17, 23) [<<"Unbound variable zz at line 17, column 23">>,
"Unbound variable zz at line 17, column 23">>, <<"Cannot unify int\n"
<<?Pos(26, 9)
"Cannot unify int\n"
" and list(int)\n" " and list(int)\n"
"when checking the application at line 26, column 9 of\n" "when checking the application at line 26, column 9 of\n"
" (::) : (int, list(int)) => list(int)\n" " (::) : (int, list(int)) => list(int)\n"
"to arguments\n" "to arguments\n"
" x : int\n" " x : int\n"
" x : int">>, " x : int">>,
<<?Pos(9, 48) <<"Cannot unify string\n"
"Cannot unify string\n"
" and int\n" " and int\n"
"when checking the assignment of the field\n" "when checking the assignment of the field\n"
" x : map(string, string) (at line 9, column 48)\n" " x : map(string, string) (at line 9, column 48)\n"
"to the old value __x and the new value\n" "to the old value __x and the new value\n"
" __x {[\"foo\"] @ x = x + 1} : map(string, int)">>, " __x {[\"foo\"] @ x = x + 1} : map(string, int)">>,
<<?Pos(34, 47) <<"Cannot unify int\n"
"Cannot unify int\n"
" and string\n" " and string\n"
"when checking the type of the expression at line 34, column 47\n" "when checking the type of the expression at line 34, column 47\n"
" 1 : int\n" " 1 : int\n"
"against the expected type\n" "against the expected type\n"
" string">>, " string">>,
<<?Pos(34, 52) <<"Cannot unify string\n"
"Cannot unify string\n"
" and int\n" " and int\n"
"when checking the type of the expression at line 34, column 52\n" "when checking the type of the expression at line 34, column 52\n"
" \"bla\" : string\n" " \"bla\" : string\n"
"against the expected type\n" "against the expected type\n"
" int">>, " int">>,
<<?Pos(32, 18) <<"Cannot unify string\n"
"Cannot unify string\n"
" and int\n" " and int\n"
"when checking the type of the expression at line 32, column 18\n" "when checking the type of the expression at line 32, column 18\n"
" \"x\" : string\n" " \"x\" : string\n"
"against the expected type\n" "against the expected type\n"
" int">>, " int">>,
<<?Pos(11, 58) <<"Cannot unify string\n"
"Cannot unify string\n"
" and int\n" " and int\n"
"when checking the type of the expression at line 11, column 58\n" "when checking the type of the expression at line 11, column 58\n"
" \"foo\" : string\n" " \"foo\" : string\n"
"against the expected type\n" "against the expected type\n"
" int">>, " int">>,
<<?Pos(38, 13) <<"Cannot unify int\n"
"Cannot unify int\n"
" and string\n" " and string\n"
"when comparing the types of the if-branches\n" "when comparing the types of the if-branches\n"
" - w : int (at line 38, column 13)\n" " - w : int (at line 38, column 13)\n"
" - z : string (at line 39, column 10)">>, " - z : string (at line 39, column 10)">>,
<<?Pos(22, 40) <<"Not a record type: string\n"
"Not a record type: string\n"
"arising from the projection of the field y (at line 22, column 40)">>, "arising from the projection of the field y (at line 22, column 40)">>,
<<?Pos(21, 44) <<"Not a record type: string\n"
"Not a record type: string\n"
"arising from an assignment of the field y (at line 21, column 44)">>, "arising from an assignment of the field y (at line 21, column 44)">>,
<<?Pos(20, 40) <<"Not a record type: string\n"
"Not a record type: string\n"
"arising from an assignment of the field y (at line 20, column 40)">>, "arising from an assignment of the field y (at line 20, column 40)">>,
<<?Pos(19, 37) <<"Not a record type: string\n"
"Not a record type: string\n"
"arising from an assignment of the field y (at line 19, column 37)">>, "arising from an assignment of the field y (at line 19, column 37)">>,
<<?Pos(13, 27) <<"Ambiguous record type with field y (at line 13, column 27) could be one of\n"
"Ambiguous record type with field y (at line 13, column 27) could be one of\n"
" - r (at line 4, column 10)\n" " - r (at line 4, column 10)\n"
" - r' (at line 5, column 10)">>, " - r' (at line 5, column 10)">>,
<<?Pos(26, 7) <<"Repeated name x in pattern\n"
"Repeated name x in pattern\n"
" x :: x (at line 26, column 7)">>, " x :: x (at line 26, column 7)">>,
<<?Pos(44, 14) <<"Repeated argument x to function repeated_arg (at line 44, column 14).">>,
"Repeated argument x to function repeated_arg (at line 44, column 14).">>, <<"Repeated argument y to function repeated_arg (at line 44, column 14).">>,
<<?Pos(44, 14) <<"No record type with fields y, z (at line 14, column 24)">>,
"Repeated argument y to function repeated_arg (at line 44, column 14).">>, <<"The field z is missing when constructing an element of type r2 (at line 15, column 26)">>,
<<?Pos(14, 24) <<"Record type r2 does not have field y (at line 15, column 24)">>,
"No record type with fields y, z (at line 14, column 24)">>, <<"Let binding at line 47, column 5 must be followed by an expression">>,
<<?Pos(15, 26) <<"Let binding at line 50, column 5 must be followed by an expression">>,
"The field z is missing when constructing an element of type r2 (at line 15, column 26)">>, <<"Let binding at line 54, column 5 must be followed by an expression">>,
<<?Pos(15, 24) <<"Let binding at line 58, column 5 must be followed by an expression">>]}
"Record type r2 does not have field y (at line 15, column 24)">>, , {"init_type_error",
<<?Pos(47, 5) [<<"Cannot unify string\n"
"Let binding at line 47, column 5 must be followed by an expression">>,
<<?Pos(50, 5)
"Let binding at line 50, column 5 must be followed by an expression">>,
<<?Pos(54, 5)
"Let binding at line 54, column 5 must be followed by an expression">>,
<<?Pos(58, 5)
"Let binding at line 58, column 5 must be followed by an expression">>])
, ?TYPE_ERROR(init_type_error,
[<<?Pos(7, 3)
"Cannot unify string\n"
" and map(int, int)\n" " and map(int, int)\n"
"when checking that 'init' returns a value of type 'state' at line 7, column 3">>]) "when checking that 'init' returns a value of type 'state' at line 7, column 3">>]}
, ?TYPE_ERROR(missing_state_type, , {"missing_state_type",
[<<?Pos(5, 3) [<<"Cannot unify string\n"
"Cannot unify string\n"
" and unit\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">>]}
, ?TYPE_ERROR(missing_fields_in_record_expression, , {"missing_fields_in_record_expression",
[<<?Pos(7, 42) [<<"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)">>, <<"The field y is missing when constructing an element of type r(int) (at line 8, column 42)">>,
<<?Pos(8, 42) <<"The fields y, z are missing when constructing an element of type r('a) (at line 6, column 42)">>]}
"The field y is missing when constructing an element of type r(int) (at line 8, column 42)">>, , {"namespace_clash",
<<?Pos(6, 42) [<<"The contract Call (at line 4, column 10) has the same name as a namespace at (builtin location)">>]}
"The fields y, z are missing when constructing an element of type r('a) (at line 6, column 42)">>]) , {"bad_events",
, ?TYPE_ERROR(namespace_clash, [<<"The indexed type string (at line 9, column 25) is not a word type">>,
[<<?Pos(4, 10) <<"The indexed type alias_string (at line 10, column 25) equals string which is not a word type">>]}
"The contract Call (at line 4, column 10) has the same name as a namespace at (builtin location)">>]) , {"bad_events2",
, ?TYPE_ERROR(bad_events, [<<"The event constructor BadEvent1 (at line 9, column 7) has too many non-indexed values (max 1)">>,
[<<?Pos(9, 25) <<"The event constructor BadEvent2 (at line 10, column 7) has too many indexed values (max 3)">>]}
"The indexed type string (at line 9, column 25) is not a word type">>, , {"type_clash",
<<?Pos(10, 25) [<<"Cannot unify int\n"
"The indexed type alias_string (at line 10, column 25) equals string which is not a word type">>])
, ?TYPE_ERROR(bad_events2,
[<<?Pos(9, 7)
"The event constructor BadEvent1 (at line 9, column 7) has too many non-indexed values (max 1)">>,
<<?Pos(10, 7)
"The event constructor BadEvent2 (at line 10, column 7) has too many indexed values (max 3)">>])
, ?TYPE_ERROR(type_clash,
[<<?Pos(12, 42)
"Cannot unify int\n"
" and string\n" " and string\n"
"when checking the record projection at line 12, column 42\n" "when checking the record projection at line 12, column 42\n"
" r.foo : (gas : int, value : int) => Remote.themap\n" " r.foo : (gas : int, value : int) => Remote.themap\n"
"against the expected type\n" "against the expected type\n"
" (gas : int, value : int) => map(string, int)">>]) " (gas : int, value : int) => map(string, int)">>]}
, ?TYPE_ERROR(bad_include_and_ns, , {"bad_include_and_ns",
[<<?Pos(2, 11) [<<"Include of 'included.aes' at line 2, column 11\nnot allowed, include only allowed at top level.">>,
"Include of 'included.aes' at line 2, column 11\nnot allowed, include only allowed at top level.">>, <<"Nested namespace not allowed\nNamespace 'Foo' at line 3, column 13 not defined at top level.">>]}
<<?Pos(3, 13) , {"bad_address_literals",
"Nested namespace not allowed\nNamespace 'Foo' at line 3, column 13 not defined at top level.">>]) [<<"The type bytes(32) is not a contract type\n"
, ?TYPE_ERROR(bad_address_literals,
[<<?Pos(32, 5)
"The type bytes(32) is not a contract type\n"
"when checking that the contract literal at line 32, column 5\n" "when checking that the contract literal at line 32, column 5\n"
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n" " ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
"has the type\n" "has the type\n"
" bytes(32)">>, " bytes(32)">>,
<<?Pos(30, 5) <<"The type oracle(int, bool) is not a contract type\n"
"The type oracle(int, bool) is not a contract type\n"
"when checking that the contract literal at line 30, column 5\n" "when checking that the contract literal at line 30, column 5\n"
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n" " ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
"has the type\n" "has the type\n"
" oracle(int, bool)">>, " oracle(int, bool)">>,
<<?Pos(28, 5) <<"The type address is not a contract type\n"
"The type address is not a contract type\n"
"when checking that the contract literal at line 28, column 5\n" "when checking that the contract literal at line 28, column 5\n"
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n" " ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
"has the type\n" "has the type\n"
" address">>, " address">>,
<<?Pos(25, 5) <<"Cannot unify oracle_query('a, 'b)\n"
"Cannot unify oracle_query('a, 'b)\n"
" and Remote\n" " and Remote\n"
"when checking the type of the expression at line 25, column 5\n" "when checking the type of the expression at line 25, column 5\n"
" oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY :\n" " oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY :\n"
" oracle_query('a, 'b)\n" " oracle_query('a, 'b)\n"
"against the expected type\n" "against the expected type\n"
" Remote">>, " Remote">>,
<<?Pos(23, 5) <<"Cannot unify oracle_query('c, 'd)\n"
"Cannot unify oracle_query('c, 'd)\n"
" and bytes(32)\n" " and bytes(32)\n"
"when checking the type of the expression at line 23, column 5\n" "when checking the type of the expression at line 23, column 5\n"
" oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY :\n" " oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY :\n"
" oracle_query('c, 'd)\n" " oracle_query('c, 'd)\n"
"against the expected type\n" "against the expected type\n"
" bytes(32)">>, " bytes(32)">>,
<<?Pos(21, 5) <<"Cannot unify oracle_query('e, 'f)\n"
"Cannot unify oracle_query('e, 'f)\n"
" and oracle(int, bool)\n" " and oracle(int, bool)\n"
"when checking the type of the expression at line 21, column 5\n" "when checking the type of the expression at line 21, column 5\n"
" oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY :\n" " oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY :\n"
" oracle_query('e, 'f)\n" " oracle_query('e, 'f)\n"
"against the expected type\n" "against the expected type\n"
" oracle(int, bool)">>, " oracle(int, bool)">>,
<<?Pos(18, 5) <<"Cannot unify oracle('g, 'h)\n"
"Cannot unify oracle('g, 'h)\n"
" and Remote\n" " and Remote\n"
"when checking the type of the expression at line 18, column 5\n" "when checking the type of the expression at line 18, column 5\n"
" ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 :\n" " ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 :\n"
" oracle('g, 'h)\n" " oracle('g, 'h)\n"
"against the expected type\n" "against the expected type\n"
" Remote">>, " Remote">>,
<<?Pos(16, 5) <<"Cannot unify oracle('i, 'j)\n"
"Cannot unify oracle('i, 'j)\n"
" and bytes(32)\n" " and bytes(32)\n"
"when checking the type of the expression at line 16, column 5\n" "when checking the type of the expression at line 16, column 5\n"
" ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 :\n" " ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 :\n"
" oracle('i, 'j)\n" " oracle('i, 'j)\n"
"against the expected type\n" "against the expected type\n"
" bytes(32)">>, " bytes(32)">>,
<<?Pos(14, 5) <<"Cannot unify oracle('k, 'l)\n"
"Cannot unify oracle('k, 'l)\n"
" and oracle_query(int, bool)\n" " and oracle_query(int, bool)\n"
"when checking the type of the expression at line 14, column 5\n" "when checking the type of the expression at line 14, column 5\n"
" ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 :\n" " ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 :\n"
" oracle('k, 'l)\n" " oracle('k, 'l)\n"
"against the expected type\n" "against the expected type\n"
" oracle_query(int, bool)">>, " oracle_query(int, bool)">>,
<<?Pos(11, 5) <<"Cannot unify address\n"
"Cannot unify address\n"
" and oracle(int, bool)\n" " and oracle(int, bool)\n"
"when checking the type of the expression at line 11, column 5\n" "when checking the type of the expression at line 11, column 5\n"
" ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt : address\n" " ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt : address\n"
"against the expected type\n" "against the expected type\n"
" oracle(int, bool)">>, " oracle(int, bool)">>,
<<?Pos(9, 5) <<"Cannot unify address\n"
"Cannot unify address\n"
" and Remote\n" " and Remote\n"
"when checking the type of the expression at line 9, column 5\n" "when checking the type of the expression at line 9, column 5\n"
" ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt : address\n" " ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt : address\n"
"against the expected type\n" "against the expected type\n"
" Remote">>, " Remote">>,
<<?Pos(7, 5) <<"Cannot unify address\n"
"Cannot unify address\n"
" and bytes(32)\n" " and bytes(32)\n"
"when checking the type of the expression at line 7, column 5\n" "when checking the type of the expression at line 7, column 5\n"
" ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt : address\n" " ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt : address\n"
"against the expected type\n" "against the expected type\n"
" bytes(32)">>]) " bytes(32)">>]}
, ?TYPE_ERROR(stateful, , {"stateful",
[<<?Pos(13, 35) [<<"Cannot reference stateful function Chain.spend (at line 13, column 35)\nin the definition of non-stateful function fail1.">>,
"Cannot reference stateful function Chain.spend (at line 13, column 35)\nin the definition of non-stateful function fail1.">>, <<"Cannot reference stateful function local_spend (at line 14, column 35)\nin the definition of non-stateful function fail2.">>,
<<?Pos(14, 35) <<"Cannot reference stateful function Chain.spend (at line 16, column 15)\nin the definition of non-stateful function fail3.">>,
"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 20, column 31)\nin the definition of non-stateful function fail4.">>,
<<?Pos(16, 15) <<"Cannot reference stateful function Chain.spend (at line 35, column 47)\nin the definition of non-stateful function fail5.">>,
"Cannot reference stateful function Chain.spend (at line 16, column 15)\nin the definition of non-stateful function fail3.">>, <<"Cannot pass non-zero value argument 1000 (at line 48, column 57)\nin the definition of non-stateful function fail6.">>,
<<?Pos(20, 31) <<"Cannot pass non-zero value argument 1000 (at line 49, column 56)\nin the definition of non-stateful function fail7.">>,
"Cannot reference stateful function Chain.spend (at line 20, column 31)\nin the definition of non-stateful function fail4.">>, <<"Cannot pass non-zero value argument 1000 (at line 52, column 17)\nin the definition of non-stateful function fail8.">>]}
<<?Pos(35, 47) , {"bad_init_state_access",
"Cannot reference stateful function Chain.spend (at line 35, column 47)\nin the definition of non-stateful function fail5.">>, [<<"The init function should return the initial state as its result and cannot write the state,\n"
<<?Pos(48, 57)
"Cannot pass non-zero value argument 1000 (at line 48, column 57)\nin the definition of non-stateful function fail6.">>,
<<?Pos(49, 56)
"Cannot pass non-zero value argument 1000 (at line 49, column 56)\nin the definition of non-stateful function fail7.">>,
<<?Pos(52, 17)
"Cannot pass non-zero value argument 1000 (at line 52, column 17)\nin the definition of non-stateful function fail8.">>])
, ?TYPE_ERROR(bad_init_state_access,
[<<?Pos(11, 5)
"The init function should return the initial state as its result and cannot write the state,\n"
"but it calls\n" "but it calls\n"
" - set_state (at line 11, column 5), which calls\n" " - set_state (at line 11, column 5), which calls\n"
" - roundabout (at line 8, column 38), which calls\n" " - roundabout (at line 8, column 38), which calls\n"
" - put (at line 7, column 39)">>, " - put (at line 7, column 39)">>,
<<?Pos(12, 5) <<"The init function should return the initial state as its result and cannot read the state,\n"
"The init function should return the initial state as its result and cannot read the state,\n"
"but it calls\n" "but it calls\n"
" - new_state (at line 12, column 5), which calls\n" " - new_state (at line 12, column 5), which calls\n"
" - state (at line 5, column 29)">>, " - state (at line 5, column 29)">>,
<<?Pos(13, 13) <<"The init function should return the initial state as its result and cannot read the state,\n"
"The init function should return the initial state as its result and cannot read the state,\n"
"but it calls\n" "but it calls\n"
" - state (at line 13, column 13)">>]) " - state (at line 13, column 13)">>]}
, ?TYPE_ERROR(modifier_checks, , {"field_parse_error",
[<<?Pos(11, 3) [<<"line 6, column 1: In field_parse_error at 5:26:\n"
"The function all_the_things (at line 11, column 3) cannot be both public and private.">>, "Cannot use nested fields or keys in record construction: p.x\n">>]}
<<?Pos(3, 3) , {"modifier_checks",
"Namespaces cannot contain entrypoints (at line 3, column 3). Use 'function' instead.">>, [<<"The function all_the_things (at line 11, column 3) cannot be both public and private.">>,
<<?Pos(5, 10) <<"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'.">>,
<<?Pos(12, 3) <<"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 : () => unit">>,
<<?Pos(6, 3) <<"Use 'entrypoint' instead of 'function' for public function foo (at line 10, 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 6, column 3):\n entrypoint foo : () => unit">>]}
<<?Pos(10, 3)
"Use 'entrypoint' instead of 'function' for public function foo (at line 10, column 3):\n entrypoint foo() = ()">>,
<<?Pos(6, 3)
"Use 'entrypoint' instead of 'function' for public function foo (at line 6, column 3):\n entrypoint foo : () => unit">>])
, ?TYPE_ERROR(list_comp_not_a_list,
[<<?Pos(2, 36)
"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)">>
])
, ?TYPE_ERROR(list_comp_if_not_bool,
[<<?Pos(2, 44)
"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">>
])
, ?TYPE_ERROR(list_comp_bad_shadow,
[<<?Pos(2, 53)
"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">>
])
, ?TYPE_ERROR(map_as_map_key,
[<<?Pos(5, 25)
"Invalid key type\n"
" map(int, int)\n"
"Map keys cannot contain other maps.">>,
<<?Pos(6, 25)
"Invalid key type\n"
" lm\n"
"Map keys cannot contain other maps.">>])
, ?TYPE_ERROR(calling_init_function,
[<<?Pos(7, 28)
"The 'init' function is called exclusively by the create contract transaction\n"
"and cannot be called from the contract code.">>])
, ?TYPE_ERROR(bad_top_level_decl,
[<<?Pos(1, 1) "The definition of 'square' must appear inside a contract or namespace.">>])
, ?TYPE_ERROR(missing_event_type,
[<<?Pos(3, 5)
"Unbound variable Chain.event at line 3, column 5\n"
"Did you forget to define the event type?">>])
, ?TYPE_ERROR(bad_bytes_concat,
[<<?Pos(12, 40)
"Failed to resolve byte array lengths in call to Bytes.concat with arguments of type\n"
" - 'g (at line 12, column 20)\n"
" - 'h (at line 12, column 23)\n"
"and result type\n"
" - bytes(10) (at line 12, column 28)">>,
<<?Pos(13, 28)
"Failed to resolve byte array lengths in call to Bytes.concat with arguments of type\n"
" - 'd (at line 13, column 20)\n"
" - 'e (at line 13, column 23)\n"
"and result type\n"
" - 'f (at line 13, column 14)">>,
<<?Pos(15, 5)
"Cannot unify bytes(26)\n"
" and bytes(25)\n"
"at line 15, column 5">>,
<<?Pos(17, 5)
"Failed to resolve byte array lengths in call to Bytes.concat with arguments of type\n"
" - bytes(6) (at line 16, column 24)\n"
" - 'b (at line 16, column 34)\n"
"and result type\n"
" - 'c (at line 16, column 39)">>,
<<?Pos(19, 25)
"Cannot resolve length of byte array.">>])
, ?TYPE_ERROR(bad_bytes_split,
[<<?Pos(13, 5)
"Failed to resolve byte array lengths in call to Bytes.split with argument of type\n"
" - 'f (at line 12, column 20)\n"
"and result types\n"
" - 'e (at line 13, column 5)\n"
" - bytes(20) (at line 12, column 29)">>,
<<?Pos(16, 5)
"Failed to resolve byte array lengths in call to Bytes.split with argument of type\n"
" - bytes(15) (at line 15, column 24)\n"
"and result types\n"
" - 'c (at line 16, column 5)\n"
" - 'd (at line 16, column 5)">>,
<<?Pos(19, 5)
"Failed to resolve byte array lengths in call to Bytes.split with argument of type\n"
" - 'b (at line 18, column 20)\n"
"and result types\n"
" - bytes(20) (at line 18, column 25)\n"
" - 'a (at line 19, column 5)">>])
]. ].
-define(Path(File), "code_errors/" ??File).
-define(Msg(File, Line, Col, Err), <<?Pos("Code generation", ?Path(File), Line, Col) Err>>).
-define(SAME(File, Line, Col, Err), {?Path(File), ?Msg(File, Line, Col, Err)}).
-define(AEVM(File, Line, Col, Err), {?Path(File), [{aevm, ?Msg(File, Line, Col, Err)}]}).
-define(FATE(File, Line, Col, Err), {?Path(File), [{fate, ?Msg(File, Line, Col, Err)}]}).
-define(BOTH(File, Line, Col, ErrAEVM, ErrFATE),
{?Path(File), [{aevm, ?Msg(File, Line, Col, ErrAEVM)},
{fate, ?Msg(File, Line, Col, ErrFATE)}]}).
failing_code_gen_contracts() ->
[ ?SAME(last_declaration_must_be_contract, 1, 1,
"Expected a contract as the last declaration instead of the namespace 'LastDeclarationIsNotAContract'")
, ?SAME(missing_definition, 2, 14,
"Missing definition of function 'foo'.")
, ?AEVM(polymorphic_entrypoint, 2, 17,
"The argument\n"
" x : 'a\n"
"of entrypoint 'id' has a polymorphic (contains type variables) type.\n"
"Use the FATE backend if you want polymorphic entrypoints.")
, ?AEVM(polymorphic_entrypoint_return, 2, 3,
"The return type\n"
" 'a\n"
"of entrypoint 'fail' is polymorphic (contains type variables).\n"
"Use the FATE backend if you want polymorphic entrypoints.")
, ?SAME(higher_order_entrypoint, 2, 20,
"The argument\n"
" f : (int) => int\n"
"of entrypoint 'apply' has a higher-order (contains function types) type.")
, ?SAME(higher_order_entrypoint_return, 2, 3,
"The return type\n"
" (int) => int\n"
"of entrypoint 'add' is higher-order (contains function types).")
, ?SAME(missing_init_function, 1, 10,
"Missing init function for the contract 'MissingInitFunction'.\n"
"The 'init' function can only be omitted if the state type is 'unit'.")
, ?SAME(parameterised_state, 3, 8,
"The state type cannot be parameterized.")
, ?SAME(parameterised_event, 3, 12,
"The event type cannot be parameterized.")
, ?SAME(polymorphic_aens_resolve, 4, 5,
"Invalid return type of AENS.resolve:\n"
" 'a\n"
"It must be a string or a pubkey type (address, oracle, etc).")
, ?SAME(bad_aens_resolve, 6, 5,
"Invalid return type of AENS.resolve:\n"
" list(int)\n"
"It must be a string or a pubkey type (address, oracle, etc).")
, ?AEVM(polymorphic_compare, 4, 5,
"Cannot compare values of type\n"
" 'a\n"
"The AEVM only supports '==' on values of\n"
"- word type (int, bool, bits, address, oracle(_, _), etc)\n"
"- type string\n"
"- tuple or record of word type\n"
"Use FATE if you need to compare arbitrary types.")
, ?AEVM(complex_compare, 4, 5,
"Cannot compare values of type\n"
" (string * int)\n"
"The AEVM only supports '!=' on values of\n"
"- word type (int, bool, bits, address, oracle(_, _), etc)\n"
"- type string\n"
"- tuple or record of word type\n"
"Use FATE if you need to compare arbitrary types.")
, ?AEVM(complex_compare_leq, 4, 5,
"Cannot compare values of type\n"
" (int * int)\n"
"The AEVM only supports '=<' on values of\n"
"- word type (int, bool, bits, address, oracle(_, _), etc)\n"
"Use FATE if you need to compare arbitrary types.")
, ?AEVM(higher_order_compare, 4, 5,
"Cannot compare values of type\n"
" (int) => int\n"
"The AEVM only supports '<' on values of\n"
"- word type (int, bool, bits, address, oracle(_, _), etc)\n"
"Use FATE if you need to compare arbitrary types.")
, ?AEVM(unapplied_contract_call, 6, 19,
"The AEVM does not support unapplied contract call to\n"
" r : Remote\n"
"Use FATE if you need this.")
, ?AEVM(unapplied_named_arg_builtin, 4, 15,
"The AEVM does not support unapplied use of Oracle.register.\n"
"Use FATE if you need this.")
, ?AEVM(polymorphic_map_keys, 4, 34,
"Invalid map key type\n"
" 'a\n"
"Map keys cannot be polymorphic in the AEVM. Use FATE if you need this.")
, ?AEVM(higher_order_map_keys, 4, 42,
"Invalid map key type\n"
" (int) => int\n"
"Map keys cannot be higher-order.")
, ?SAME(polymorphic_query_type, 3, 5,
"Invalid oracle type\n"
" oracle('a, 'b)\n"
"The query type must not be polymorphic (contain type variables).")
, ?SAME(polymorphic_response_type, 3, 5,
"Invalid oracle type\n"
" oracle(string, 'r)\n"
"The response type must not be polymorphic (contain type variables).")
, ?SAME(higher_order_query_type, 3, 5,
"Invalid oracle type\n"
" oracle((int) => int, string)\n"
"The query type must not be higher-order (contain function types).")
, ?SAME(higher_order_response_type, 3, 5,
"Invalid oracle type\n"
" oracle(string, (int) => int)\n"
"The response type must not be higher-order (contain function types).")
, ?AEVM(higher_order_state, 3, 3,
"Invalid state type\n"
" {f : (int) => int}\n"
"The state cannot contain functions in the AEVM. Use FATE if you need this.")
].
+7 -10
View File
@@ -39,7 +39,7 @@ simple_contracts_test_() ->
RightAssoc = fun(Op) -> CheckParens({a, Op, {b, Op, c}}) end, RightAssoc = fun(Op) -> CheckParens({a, Op, {b, Op, c}}) end,
NonAssoc = fun(Op) -> NonAssoc = fun(Op) ->
OpAtom = list_to_atom(Op), OpAtom = list_to_atom(Op),
?assertThrow({error, [_]}, ?assertError({error, {_, parse_error, _}},
parse_expr(NoPar({a, Op, {b, Op, c}}))) end, parse_expr(NoPar({a, Op, {b, Op, c}}))) end,
Stronger = fun(Op1, Op2) -> Stronger = fun(Op1, Op2) ->
CheckParens({{a, Op1, b}, Op2, c}), CheckParens({{a, Op1, b}, Op2, c}),
@@ -71,10 +71,11 @@ 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) -> {ok, Contract} -> Contract;
aeso_parser:string(Text, Opts). Err -> error(Err)
end.
parse_expr(Text) -> parse_expr(Text) ->
[{letval, _, _, _, Expr}] = [{letval, _, _, _, Expr}] =
@@ -83,16 +84,12 @@ parse_expr(Text) ->
round_trip(Text) -> round_trip(Text) ->
Contract = parse_string(Text), Contract = parse_string(Text),
Text1 = prettypr:format(aeso_pretty:decls(strip_stdlib(Contract))), Text1 = prettypr:format(aeso_pretty:decls(Contract)),
Contract1 = parse_string(Text1), Contract1 = parse_string(Text1),
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)).
strip_stdlib([{namespace, _, {con, _, "ListInternal"}, _} | Decls]) ->
strip_stdlib(Decls);
strip_stdlib(Decls) -> Decls.
remove_line_numbers({line, _L}) -> {line, 0}; remove_line_numbers({line, _L}) -> {line, 0};
remove_line_numbers({col, _C}) -> {col, 0}; remove_line_numbers({col, _C}) -> {col, 0};
remove_line_numbers([H|T]) -> remove_line_numbers([H|T]) ->
-3
View File
@@ -31,6 +31,3 @@ contract AddrChain =
entrypoint c_creator() : address = entrypoint c_creator() : address =
Contract.creator Contract.creator
entrypoint is_payable(a : address) : bool =
Address.is_payable(a)
-19
View File
@@ -1,19 +0,0 @@
contract BytesConcat =
entrypoint test1(x : bytes(10), y : bytes(20)) =
Bytes.concat(x, y)
entrypoint test2(x : bytes(10), y) : bytes(15) =
Bytes.concat(x, y)
entrypoint test3(x, y : bytes(20)) : bytes(25) =
Bytes.concat(x, y)
entrypoint fail1(x, y) : bytes(10) = Bytes.concat(x, y)
entrypoint fail2(x, y) = Bytes.concat(x, y)
entrypoint fail3(x : bytes(6), y : bytes(20)) : bytes(25) =
Bytes.concat(x, y)
entrypoint fail4(x : bytes(6), y) : _ =
Bytes.concat(x, y)
entrypoint fail5(x) = Bytes.to_str(x)
-20
View File
@@ -1,20 +0,0 @@
contract BytesSplit =
entrypoint test1(x) : bytes(10) * bytes(20) =
Bytes.split(x)
entrypoint test2(x : bytes(15)) : bytes(10) * _ =
Bytes.split(x)
entrypoint test3(x : bytes(25)) : _ * bytes(20) =
Bytes.split(x)
entrypoint fail1(x) : _ * bytes(20) =
Bytes.split(x)
entrypoint fail2(x : bytes(15)) : _ =
Bytes.split(x)
entrypoint fail3(x) : bytes(20) * _ =
Bytes.split(x)
-3
View File
@@ -1,3 +0,0 @@
function square(x) = x ^ 2
contract Main =
entrypoint main() = square(10)
+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.verify_sig(to_sign(tx_hash, n), state.owner, s) Some(tx_hash) => Crypto.ecverify(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(20) } record state = { nonce : int, owner : bytes(64) }
entrypoint init(owner' : bytes(20)) = { nonce = 1, owner = owner' } entrypoint init(owner' : bytes(64)) = { nonce = 1, owner = owner' }
stateful entrypoint authorize(n : int, s : bytes(65)) : bool = stateful entrypoint authorize(n : int, s : signature) : 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
View File
@@ -1,4 +0,0 @@
contract BytesConcat =
entrypoint rot(a : bytes(3)) =
switch (Bytes.split(a))
(b, c) => Bytes.concat(c : bytes(2), b)
-7
View File
@@ -1,7 +0,0 @@
contract CallingInitFunction =
type state = int * int
entrypoint init() = (1, 2)
entrypoint call_init() = init()
@@ -1,9 +0,0 @@
contract BadAENSresolve =
type t('a) = option(list('a))
function fail() : t(int) =
AENS.resolve("foo.aet", "whatever")
entrypoint main() = ()
@@ -1,4 +0,0 @@
contract ComplexCompare =
entrypoint test(x : string * int) =
("foo", 1) != x
@@ -1,4 +0,0 @@
contract ComplexCompare =
entrypoint test(x : int) =
(1, 2) =< (x, x + 1)
@@ -1,8 +0,0 @@
contract HigherOrderCompare =
function cmp(x : int => int, y) : bool =
x < y
entrypoint test() =
let f(x) = (y) => x + y
cmp(f(1), f(2))
@@ -1,2 +0,0 @@
contract HigherOrderEntrypoint =
entrypoint apply(f : int => int, x : int) = f(x)
@@ -1,2 +0,0 @@
contract HigherOrderEntrypoint =
entrypoint add(x : int) = (y) => x + y
@@ -1,6 +0,0 @@
contract MapAsMapKey =
type t('key) = map('key, int)
function foo(m) : t(int => int) = {[m] = 0}
entrypoint main() = ()
@@ -1,5 +0,0 @@
contract HigherOrderQueryType =
stateful function foo(o) : oracle_query(_, string ) =
Oracle.query(o, (x) => x + 1, 100, RelativeTTL(100), RelativeTTL(100))
entrypoint main() = ()
@@ -1,5 +0,0 @@
contract HigherOrderResponseType =
stateful function foo(o, q : oracle_query(string, _)) =
Oracle.respond(o, q, (x) => x + 1)
entrypoint main() = ()
@@ -1,7 +0,0 @@
contract HigherOrderState =
record state = {f : int => int}
entrypoint init() = {f = (x) => x}
entrypoint apply(n) = state.f(n)
stateful entrypoint inc() = put(state{ f = (x) => state.f(x + 1) })
@@ -1,2 +0,0 @@
namespace LastDeclarationIsNotAContract =
function add(x, y) = x + y
@@ -1,3 +0,0 @@
contract MissingDefinition =
entrypoint foo : int => int
entrypoint main() = foo(0)
@@ -1,3 +0,0 @@
contract MissingInitFunction =
type state = int * int
@@ -1,4 +0,0 @@
contract ParameterisedEvent =
datatype event('a) = Event(int)
@@ -1,4 +0,0 @@
contract ParameterisedState =
type state('a) = list('a)
@@ -1,7 +0,0 @@
contract PolymorphicAENSresolve =
function fail() : option('a) =
AENS.resolve("foo.aet", "whatever")
entrypoint main() = ()
@@ -1,7 +0,0 @@
contract PolymorphicCompare =
function cmp(x : 'a, y : 'a) : bool =
x == y
entrypoint test() =
cmp(4, 6) && cmp(true, false)
@@ -1,3 +0,0 @@
contract PolymorphicEntrypoint =
entrypoint id(x : 'a) : 'a = x
@@ -1,3 +0,0 @@
contract PolymorphicEntrypoint =
entrypoint fail() : 'a = abort("fail")
@@ -1,6 +0,0 @@
contract MapAsMapKey =
type t('key) = map('key, int)
function foo(m) : t('a) = {[m] = 0}
entrypoint main() = ()
@@ -1,5 +0,0 @@
contract PolymorphicQueryType =
stateful function is_oracle(o) =
Oracle.check(o)
entrypoint main() = ()
@@ -1,5 +0,0 @@
contract PolymorphicResponseType =
function is_oracle(o : oracle(string, 'r)) =
Oracle.check(o)
entrypoint main(o : oracle(string, int)) = is_oracle(o)
@@ -1,9 +0,0 @@
contract Remote =
entrypoint foo : int => int
contract UnappliedContractCall =
function f(r) = r.foo
entrypoint test(r) = f(r)(0)
@@ -1,5 +0,0 @@
contract UnappliedNamedArgBuiltin =
// Allowed in FATE, but not AEVM
stateful entrypoint main(s) =
let reg = Oracle.register
reg(signature = s, Contract.address, 100, RelativeTTL(100)) : oracle(int, int)
+1 -1
View File
@@ -50,7 +50,7 @@ contract ComplexTypes =
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)
function map(f, xs) = entrypoint map(f, xs) =
switch(xs) switch(xs)
[] => [] [] => []
x :: xs => f(x) :: map(f, xs) x :: xs => f(x) :: map(f, xs)
-4
View File
@@ -1,4 +0,0 @@
include "cyclic_include_forth.aes"
contract CI =
entrypoint ci() = Back.back() + Forth.forth()
-4
View File
@@ -1,4 +0,0 @@
include "cyclic_include_forth.aes"
namespace Back =
function back() = 2
-4
View File
@@ -1,4 +0,0 @@
include "cyclic_include_back.aes"
namespace Forth =
function forth() = 3
+3 -3
View File
@@ -1,5 +1,5 @@
namespace MyList = namespace List =
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 MyList =
contract Deadcode = contract Deadcode =
entrypoint inc1(xs : list(int)) : list(int) = entrypoint inc1(xs : list(int)) : list(int) =
MyList.map1((x) => x + 1, xs) List.map1((x) => x + 1, xs)
entrypoint inc2(xs : list(int)) : list(int) = entrypoint inc2(xs : list(int)) : list(int) =
MyList.map1((x) => x + 1, xs) List.map1((x) => x + 1, xs)
-7
View File
@@ -1,7 +0,0 @@
include "included.aes"
include "../contracts/included.aes"
contract Include =
entrypoint foo() =
Included.foo()
-3
View File
@@ -1,3 +0,0 @@
contract IndentFail =
entrypoint twoSpace() = ()
entrypoint oneSpace() = ()
-23
View File
@@ -1,23 +0,0 @@
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..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
@@ -1,2 +0,0 @@
contract BadComp =
entrypoint failing() = [x + 1 | x <- [1,2,3], let x = "XD"]
-2
View File
@@ -1,2 +0,0 @@
contract BadComp =
entrypoint failing() = [x | x <- [], if (3)]
-2
View File
@@ -1,2 +0,0 @@
contract ListCompBad =
entrypoint failing() = [x | x <- 1]
-7
View File
@@ -1,7 +0,0 @@
contract Fail =
entrypoint tttt() : bool * int =
let f(x : 'a) : 'a = x
(f(true), f(1))
-6
View File
@@ -1,6 +0,0 @@
// This should include Lists.aes implicitly, since Option.aes does.
include "Option.aes"
contract Test =
entrypoint i_should_build() =
List.is_empty(Option.to_list(None))
-6
View File
@@ -1,6 +0,0 @@
contract MapAsMapKey =
type t('key) = map('key, int)
type lm = list(map(int, int))
entrypoint foo(m) : t(map(int, int)) = {[m] = 0}
entrypoint bar(m) : t(lm) = Map.delete(m, {})
-3
View File
@@ -1,3 +0,0 @@
contract MissingEventType =
entrypoint main() =
Chain.event("MAIN")
+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(MyList.zip([1..n], caller() :: owners)), owners = Map.from_list(List.zip([1..n], caller() :: owners)),
ownerIndex = Map.from_list(MyList.zip(caller() :: owners, [1..n])) } ownerIndex = Map.from_list(List.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 MyList = namespace List =
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 MyList =
contract Deadcode = contract Deadcode =
entrypoint inc1(xs : list(int)) : list(int) = entrypoint inc1(xs : list(int)) : list(int) =
MyList.map1((x) => x + 1, xs) List.map1((x) => x + 1, xs)
entrypoint inc2(xs : list(int)) : list(int) = entrypoint inc2(xs : list(int)) : list(int) =
MyList.map2((x) => x + 1, xs) List.map2((x) => x + 1, xs)
-3
View File
@@ -1,3 +0,0 @@
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: MyList.map((name) => {name: name, voteCount: 0}, proposalNames) proposals: List.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 = MyList.nth(state().proposals, candidate); let proposal = List.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() =
MyList.map((p) => (p.name, p.voteCount), state().proposals); List.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());
MyList.map(((name, count)) => Printf.printf("%s: %d\n", name, count), tally); List.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);
}; };
-6
View File
@@ -1,6 +0,0 @@
include "List.aes"
include "Func.aes"
contract StdInc =
entrypoint test() = List.map((x) => Func.id(x), [1,2,3,4])
+2 -3
View File
@@ -91,11 +91,10 @@ contract Identity =
// } // }
// let id(x) = x // let id(x) = x
// let main(xs) = map(double,xs) // let main(xs) = map(double,xs)
function z(f,x) = x entrypoint z(f,x) = x
function s(n) = (f,x)=>f(n(f,x)) function s(n) = (f,x)=>f(n(f,x))
function add(m,n) = (f,x)=>m(f,n(f,x)) function add(m,n) = (f,x)=>m(f,n(f,x))
entrypoint main(_) =
entrypoint main() =
let three=s(s(s(z))) let three=s(s(s(z)))
add(three,three) add(three,three)
(((i)=>i+1),0) (((i)=>i+1),0)
+1 -1
View File
@@ -1,6 +1,6 @@
contract TuplesMatch = contract TuplesMatch =
function tuplify3() = (t) => switch(t) entrypoint tuplify3() = (t) => switch(t)
(x, y, z) => 3 (x, y, z) => 3
entrypoint fst(p : int * string) = entrypoint fst(p : int * string) =
-63
View File
@@ -1,63 +0,0 @@
// Builtins without named arguments can appear unapplied in both AEVM and FATE.
// Named argument builtins are:
// Oracle.register
// Oracle.respond
// AENS.preclaim
// AENS.claim
// AENS.transfer
// AENS.revoke
// Oracle.extend
contract UnappliedBuiltins =
entrypoint main() = ()
type o = oracle(int, int)
type t = list(int * string)
type m = map(int, int)
datatype event = Event(int)
stateful function chain_spend() = Chain.spend
function chain_event() = Chain.event
function chain_balance() = Chain.balance
function chain_block_hash() = Chain.block_hash
function call_gas_left() = Call.gas_left
function b_abort() = abort
function b_require() = require
function oracle_query_fee() = Oracle.query_fee
stateful function oracle_query() = Oracle.query : (o, _, _, _, _) => _
function oracle_get_question() = Oracle.get_question : (o, _) => _
function oracle_get_answer() = Oracle.get_answer : (o, _) => _
function oracle_check() = Oracle.check : o => _
function oracle_check_query() = Oracle.check_query : (o, _) => _
function aens_resolve() = AENS.resolve : (_, _) => option(string)
function map_lookup() = Map.lookup : (_, m) => _
function map_lookup_default() = Map.lookup_default : (_, m, _) => _
function map_member() = Map.member : (_, m) => _
function map_size() = Map.size : m => _
function map_delete() = Map.delete : (_, m) => _
function map_from_list() = Map.from_list : _ => m
function map_to_list() = Map.to_list : m => _
function crypto_verify_sig() = Crypto.verify_sig
function crypto_verify_sig_secp256k1() = Crypto.verify_sig_secp256k1
function crypto_ecverify_secp256k1() = Crypto.ecverify_secp256k1
function crypto_ecrecover_secp256k1() = Crypto.ecrecover_secp256k1
function crypto_sha3() = Crypto.sha3 : t => _
function crypto_sha256() = Crypto.sha256 : t => _
function crypto_blake2b() = Crypto.blake2b : t => _
function string_sha256() = String.sha256
function string_blake2b() = String.blake2b
function string_length() = String.length
function string_concat() = String.concat
function string_sha3() = String.sha3
function bits_test() = Bits.test
function bits_set() = Bits.set
function bits_clear() = Bits.clear
function bits_union() = Bits.union
function bits_intersection() = Bits.intersection
function bits_difference() = Bits.difference
function bits_sum() = Bits.sum
function int_to_str() = Int.to_str
function address_to_str() = Address.to_str
function address_is_oracle() = Address.is_oracle
function address_is_contract() = Address.is_contract
function address_is_payable() = Address.is_payable
function bytes_to_int() = Bytes.to_int : bytes(10) => int
function bytes_to_str() = Bytes.to_str : bytes(99) => string
-4
View File
@@ -1,4 +0,0 @@
contract VClose =
entrypoint missing_bracket() =
let x = [1, 2, 3
entrypoint bar() = ()
+3 -3
View File
@@ -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 = MyList.map((name) => {name = name, voteCount = 0}, proposalNames) } proposals = List.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 = MyList.nth(state.proposals, candidate) let proposal = List.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() =
MyList.map((p) => (p.name, p.voteCount), state.proposals) List.map((p) => (p.name, p.voteCount), state.proposals)
-3
View File
@@ -1,3 +0,0 @@
contract VSemi =
record missing_brace = { x : int
entrypoint foo() = ()