Merge pull request #256 from radrow/master

Merge lima – 4.3.0
This commit is contained in:
Radosław Rowicki 2020-04-30 13:52:50 +02:00 committed by GitHub
commit ea5850cf93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 3858 additions and 140 deletions

View File

@ -9,6 +9,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
### Removed ### Removed
## [4.3.0]
### Added
- Added documentation (moved from `protocol`)
- `Frac.aes` library for rational numbers
- Added some more meaningful error messages
- Exported several parsing functionalities
- With option `keep_included` it is possible to see which files were included during the parse
- There is a function `run_parser` that be used to evaluate any parsing rule
- Exported parsers: `body`, `type` and `decl`
### Changed
- Performance improvements in the standard library
- Fixed ACI encoder to handle `-` unary operator
- Fixed including by absolute path
- Fixed variant type printing in the ACI error messages
- Fixed pretty printing of combined function clauses
### Removed
- `let` definitions are no longer supported in the toplevel of the contract
- type declarations are no longer supported
## [4.2.0] - 2020-01-15 ## [4.2.0] - 2020-01-15
### Added ### Added
- Allow separate entrypoint/function type signature and definition, and pattern - Allow separate entrypoint/function type signature and definition, and pattern
@ -53,7 +72,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
(de-)construct byte arrays. (de-)construct byte arrays.
- `[a..b]` language construct, returning the list of numbers between - `[a..b]` language construct, returning the list of numbers between
`a` and `b` (inclusive). Returns the empty list if `a` > `b`. `a` and `b` (inclusive). Returns the empty list if `a` > `b`.
- [Standard libraries] (https://github.com/aeternity/protocol/blob/master/contracts/sophia_stdlib.md) - [Standard libraries](https://github.com/aeternity/aesophia/blob/master/docs/sophia_stdlib.md)
- Checks that `init` is not called from other functions. - Checks that `init` is not called from other functions.
- FATE backend - the compiler is able to produce VM code for both `AEVM` and `FATE`. Many - 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 of the APIs now take `{backend, aevm | fate}` to decide wich backend to produce artifacts
@ -192,10 +211,11 @@ 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.2.0...HEAD [Unreleased]: https://github.com/aeternity/aesophia/compare/v4.3.0...HEAD
[4.2.0]: https://github.com/aeternity/aesophia/compare/v4.2.0...v4.1.0 [4.3.0]: https://github.com/aeternity/aesophia/compare/v4.2.0...v4.3.0
[4.1.0]: https://github.com/aeternity/aesophia/compare/v4.1.0...v4.0.0 [4.2.0]: https://github.com/aeternity/aesophia/compare/v4.1.0...v4.2.0
[4.0.0]: https://github.com/aeternity/aesophia/compare/v4.0.0...v3.2.0 [4.1.0]: https://github.com/aeternity/aesophia/compare/v4.0.0...v4.1.0
[4.0.0]: https://github.com/aeternity/aesophia/compare/v3.2.0...v4.0.0
[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

View File

@ -2,13 +2,19 @@
This is the __sophia__ compiler for the æternity system which compiles contracts written in __sophia__ code to the æternity VM code. This is the __sophia__ compiler for the æternity system which compiles contracts written in __sophia__ code to the æternity VM code.
For more information about æternity smart contracts and the sophia language see [Smart Contracts](https://github.com/aeternity/protocol/blob/master/contracts/contracts.md) and the [Sophia Language](https://github.com/aeternity/protocol/blob/master/contracts/sophia.md).
It is an OTP application written in Erlang and is by default included in It is an OTP application written in Erlang and is by default included in
[the æternity node](https://github.com/aeternity/epoch). However, it can [the æternity node](https://github.com/aeternity/epoch). However, it can
also be included in other systems to compile contracts coded in sophia which also be included in other systems to compile contracts coded in sophia which
can then be loaded into the æternity system. can then be loaded into the æternity system.
## Documentation
* [Smart Contracts on aeternity Blockchain](https://github.com/aeternity/protocol/blob/master/contracts/contracts.md).
* [Sophia Documentation](docs/sophia.md).
* [Sophia Standard Library](docs/sophia_stdlib.md).
## Versioning ## Versioning
`aesophia` has a version that is only loosely connected to the version of the `aesophia` has a version that is only loosely connected to the version of the
@ -17,6 +23,7 @@ minor/patch version. The `aesophia` compiler version MUST be bumped whenever
there is a change in how byte code is generated, but it MAY also be bumped upon there is a change in how byte code is generated, but it MAY also be bumped upon
API changes etc. API changes etc.
## Interface Modules ## Interface Modules
The basic modules for interfacing the compiler: The basic modules for interfacing the compiler:

1159
docs/sophia.md Normal file

File diff suppressed because it is too large Load Diff

2071
docs/sophia_stdlib.md Normal file

File diff suppressed because it is too large Load Diff

183
priv/stdlib/Frac.aes Normal file
View File

@ -0,0 +1,183 @@
namespace Frac =
private function gcd(a : int, b : int) =
if (b == 0) a else gcd(b, a mod b)
private function abs_int(a : int) = if (a < 0) -a else a
datatype frac = Pos(int, int) | Zero | Neg(int, int)
/** Checks if the internal representation is correct.
* Numerator and denominator must be positive.
* Exposed for debug purposes
*/
function is_sane(f : frac) : bool = switch(f)
Pos(n, d) => n > 0 && d > 0
Zero => true
Neg(n, d) => n > 0 && d > 0
function num(f : frac) : int = switch(f)
Pos(n, _) => n
Neg(n, _) => -n
Zero => 0
function den(f : frac) : int = switch(f)
Pos(_, d) => d
Neg(_, d) => d
Zero => 1
function to_pair(f : frac) : int * int = switch(f)
Pos(n, d) => (n, d)
Neg(n, d) => (-n, d)
Zero => (0, 1)
function sign(f : frac) : int = switch(f)
Pos(_, _) => 1
Neg(_, _) => -1
Zero => 0
function to_str(f : frac) : string = switch(f)
Pos(n, d) => String.concat(Int.to_str(n), if (d == 1) "" else String.concat("/", Int.to_str(d)))
Neg(n, d) => String.concat("-", to_str(Pos(n, d)))
Zero => "0"
/** Reduce fraction to normal form
*/
function simplify(f : frac) : frac =
switch(f)
Neg(n, d) =>
let cd = gcd(n, d)
Neg(n / cd, d / cd)
Zero => Zero
Pos(n, d) =>
let cd = gcd(n, d)
Pos(n / cd, d / cd)
/** Integer to rational division
*/
function make_frac(n : int, d : int) : frac =
if (d == 0) abort("Zero denominator")
elif (n == 0) Zero
elif ((n < 0) == (d < 0)) simplify(Pos(abs_int(n), abs_int(d)))
else simplify(Neg(abs_int(n), abs_int(d)))
function one() : frac = Pos(1, 1)
function zero() : frac = Zero
function eq(a : frac, b : frac) : bool =
let (na, da) = to_pair(a)
let (nb, db) = to_pair(b)
(na == nb && da == db) || na * db == nb * da // they are more likely to be normalized
function neq(a : frac, b : frac) : bool =
let (na, da) = to_pair(a)
let (nb, db) = to_pair(b)
(na != nb || da != db) && na * db != nb * da
function geq(a : frac, b : frac) : bool = num(a) * den(b) >= num(b) * den(a)
function leq(a : frac, b : frac) : bool = num(a) * den(b) =< num(b) * den(a)
function gt(a : frac, b : frac) : bool = num(a) * den(b) > num(b) * den(a)
function lt(a : frac, b : frac) : bool = num(a) * den(b) < num(b) * den(a)
function min(a : frac, b : frac) : frac = if (leq(a, b)) a else b
function max(a : frac, b : frac) : frac = if (geq(a, b)) a else b
function abs(f : frac) : frac = switch(f)
Pos(n, d) => Pos(n, d)
Zero => Zero
Neg(n, d) => Pos(n, d)
function from_int(n : int) : frac =
if (n > 0) Pos(n, 1)
elif (n < 0) Neg(-n, 1)
else Zero
function floor(f : frac) : int = switch(f)
Pos(n, d) => n / d
Zero => 0
Neg(n, d) => -(n + d - 1) / d
function ceil(f : frac) : int = switch(f)
Pos(n, d) => (n + d - 1) / d
Zero => 0
Neg(n, d) => -n / d
function round_to_zero(f : frac) : int = switch(f)
Pos(n, d) => n / d
Zero => 0
Neg(n, d) => -n / d
function round_from_zero(f : frac) : int = switch(f)
Pos(n, d) => (n + d - 1) / d
Zero => 0
Neg(n, d) => -(n + d - 1) / d
/** Round towards nearest integer. If two integers are in the same
* distance, choose the even one.
*/
function round(f : frac) : int =
let fl = floor(f)
let cl = ceil(f)
let dif_fl = abs(sub(f, from_int(fl)))
let dif_cl = abs(sub(f, from_int(cl)))
if (gt(dif_fl, dif_cl)) cl
elif (gt(dif_cl, dif_fl)) fl
elif (fl mod 2 == 0) fl
else cl
function add(a : frac, b : frac) : frac =
let (na, da) = to_pair(a)
let (nb, db) = to_pair(b)
if (da == db) make_frac(na + nb, da)
else make_frac(na * db + nb * da, da * db)
function neg(a : frac) : frac = switch(a)
Neg(n, d) => Pos(n, d)
Zero => Zero
Pos(n, d) => Neg(n, d)
function sub(a : frac, b : frac) : frac = add(a, neg(b))
function inv(a : frac) : frac = switch(a)
Neg(n, d) => Neg(d, n)
Zero => abort("Inversion of zero")
Pos(n, d) => Pos(d, n)
function mul(a : frac, b : frac) : frac = make_frac(num(a) * num(b), den(a) * den(b))
function div(a : frac, b : frac) : frac = switch(b)
Neg(n, d) => mul(a, Neg(d, n))
Zero => abort("Division by zero")
Pos(n, d) => mul(a, Pos(d, n))
/** `b` to the power of `e`
*/
function int_exp(b : frac, e : int) : frac =
if (sign(b) == 0 && e == 0) abort("Zero to the zero exponentation")
elif (e < 0) inv(int_exp_(b, -e))
else int_exp_(b, e)
private function int_exp_(b : frac, e : int) =
if (e == 0) from_int(1)
elif (e == 1) b
else
let half = int_exp_(b, e / 2)
if (e mod 2 == 1) mul(mul(half, half), b)
else mul(half, half)
/** Reduces the fraction's in-memory size by dividing its components by two until the
* the error is bigger than `loss` value
*/
function optimize(f : frac, loss : frac) : frac =
require(geq(loss, Zero), "negative loss optimize")
let s = sign(f)
mul(from_int(s), run_optimize(abs(f), abs(f), loss))
private function run_optimize(orig : frac, f : frac, loss : frac) : frac =
let (n, d) = to_pair(f)
let t = make_frac((n+1)/2, (d+1)/2)
if(gt(abs(sub(t, orig)), loss)) f
elif (eq(t, f)) f
else run_optimize(orig, t, loss)

View File

@ -12,35 +12,66 @@ namespace Func =
function rapply(x : 'a, f : 'a => 'b) : 'b = f(x) function rapply(x : 'a, f : 'a => 'b) : 'b = f(x)
/* The Z combinator - replacement for local and anonymous recursion. /** The Z combinator - replacement for local and anonymous recursion.
*/ */
function recur(f : ('arg => 'res, 'arg) => 'res) : 'arg => 'res = function recur(f : ('arg => 'res, 'arg) => 'res) : 'arg => 'res =
(x) => f(recur(f), x) (x) => f(recur(f), x)
/** n-times composition with itself
*/
function iter(n : int, f : 'a => 'a) : 'a => 'a = iter_(n, f, (x) => 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 = private function iter_(n : int, f : 'a => 'a, acc : 'a => 'a) : 'a => 'a =
if(n == 0) acc if(n == 0) acc
elif(n == 1) comp(f, acc) elif(n == 1) comp(f, acc)
else iter_(n / 2, comp(f, f), if(n mod 2 == 0) acc else 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) = /** Turns an ugly, bad and disgusting arity-n function into
* a beautiful and sweet function taking the first argument
* and returning a function watiting for the remaining ones
* in the same manner
*/
function curry2(f : ('a, 'b) => 'x) : 'a => ('b => 'x) =
(x) => (y) => f(x, y) (x) => (y) => f(x, y)
function curry3(f : ('a, 'b, 'c) => 'd) : 'a => ('b => ('c => 'd)) = function curry3(f : ('a, 'b, 'c) => 'x) : 'a => ('b => ('c => 'x)) =
(x) => (y) => (z) => f(x, y, z) (x) => (y) => (z) => f(x, y, z)
function curry4(f : ('a, 'b, 'c, 'd) => 'x) : 'a => ('b => ('c => ('d => 'x))) =
(x) => (y) => (z) => (w) => f(x, y, z, w)
function curry5(f : ('a, 'b, 'c, 'd, 'e) => 'x) : 'a => ('b => ('c => ('d => ('e => 'x)))) =
(x) => (y) => (z) => (w) => (q) => f(x, y, z, w, q)
function uncurry2(f : 'a => ('b => 'c)) : ('a, 'b) => 'c = /** Opposite of curry. Gross
*/
function uncurry2(f : 'a => ('b => 'x)) : ('a, 'b) => 'x =
(x, y) => f(x)(y) (x, y) => f(x)(y)
function uncurry3(f : 'a => ('b => ('c => 'd))) : ('a, 'b, 'c) => 'd = function uncurry3(f : 'a => ('b => ('c => 'x))) : ('a, 'b, 'c) => 'x =
(x, y, z) => f(x)(y)(z) (x, y, z) => f(x)(y)(z)
function uncurry4(f : 'a => ('b => ('c => ('d => 'x)))) : ('a, 'b, 'c, 'd) => 'x =
(x, y, z, w) => f(x)(y)(z)(w)
function uncurry5(f : 'a => ('b => ('c => ('d => ('e => 'x))))) : ('a, 'b, 'c, 'd, 'e) => 'x =
(x, y, z, w, q) => f(x)(y)(z)(w)(q)
function tuplify2(f : ('a, 'b) => 'c) : (('a * 'b)) => 'c = /** Turns an arity-n function into a function taking n-tuple
*/
function tuplify2(f : ('a, 'b) => 'x) : (('a * 'b)) => 'x =
(t) => switch(t) (t) => switch(t)
(x, y) => f(x, y) (x, y) => f(x, y)
function tuplify3(f : ('a, 'b, 'c) => 'd) : 'a * 'b * 'c => 'd = function tuplify3(f : ('a, 'b, 'c) => 'x) : 'a * 'b * 'c => 'x =
(t) => switch(t) (t) => switch(t)
(x, y, z) => f(x, y, z) (x, y, z) => f(x, y, z)
function tuplify4(f : ('a, 'b, 'c, 'd) => 'x) : 'a * 'b * 'c * 'd => 'x =
(t) => switch(t)
(x, y, z, w) => f(x, y, z, w)
function tuplify5(f : ('a, 'b, 'c, 'd, 'e) => 'x) : 'a * 'b * 'c * 'd * 'e => 'x =
(t) => switch(t)
(x, y, z, w, q) => f(x, y, z, w, q)
function untuplify2(f : 'a * 'b => 'c) : ('a, 'b) => 'c = /** Opposite of tuplify
*/
function untuplify2(f : 'a * 'b => 'x) : ('a, 'b) => 'x =
(x, y) => f((x, y)) (x, y) => f((x, y))
function untuplify3(f : 'a * 'b * 'c => 'd) : ('a, 'b, 'c) => 'd = function untuplify3(f : 'a * 'b * 'c => 'x) : ('a, 'b, 'c) => 'x =
(x, y, z) => f((x, y, z)) (x, y, z) => f((x, y, z))
function untuplify4(f : 'a * 'b * 'c * 'd => 'x) : ('a, 'b, 'c, 'd) => 'x =
(x, y, z, w) => f((x, y, z, w))
function untuplify5(f : 'a * 'b * 'c * 'd * 'e => 'x) : ('a, 'b, 'c, 'd, 'e) => 'x =
(x, y, z, w, q) => f((x, y, z, w, q))

View File

@ -28,10 +28,15 @@ namespace List =
h::t => h::drop_last_unsafe(t) h::t => h::drop_last_unsafe(t)
[] => abort("drop_last_unsafe: list empty") [] => abort("drop_last_unsafe: list empty")
/** Finds first element of `l` fulfilling predicate `p` as `Some` or `None`
* if no such element exists.
*/
function find(p : 'a => bool, l : list('a)) : option('a) = switch(l) function find(p : 'a => bool, l : list('a)) : option('a) = switch(l)
[] => None [] => None
h::t => if(p(h)) Some(h) else find(p, t) h::t => if(p(h)) Some(h) else find(p, t)
/** Returns list of all indices of elements from `l` that fulfill the predicate `p`.
*/
function find_indices(p : 'a => bool, l : list('a)) : list(int) = find_indices_(p, l, 0) function find_indices(p : 'a => bool, l : list('a)) : list(int) = find_indices_(p, l, 0)
private function find_indices_( p : 'a => bool private function find_indices_( p : 'a => bool
, l : list('a) , l : list('a)
@ -60,8 +65,15 @@ namespace List =
_::t => length_(t, acc + 1) _::t => length_(t, acc + 1)
/** Creates an ascending sequence of all integer numbers
* between `a` and `b` (including `a` and `b`)
*/
function from_to(a : int, b : int) : list(int) = [a..b] function from_to(a : int, b : int) : list(int) = [a..b]
/** Creates an ascending sequence of integer numbers betweeen
* `a` and `b` jumping by given `step`. Includes `a` and takes
* `b` only if `(b - a) mod step == 0`. `step` should be bigger than 0.
*/
function from_to_step(a : int, b : int, s : int) : list(int) = function from_to_step(a : int, b : int, s : int) : list(int) =
from_to_step_(a, b - (b-a) mod s, s, []) from_to_step_(a, b - (b-a) mod s, s, [])
private function from_to_step_(a : int, b : int, s : int, acc : list(int)) : list(int) = private function from_to_step_(a : int, b : int, s : int, acc : list(int)) : list(int) =
@ -69,8 +81,8 @@ namespace List =
else from_to_step_(a, b - s, s, b::acc) else from_to_step_(a, b - s, s, b::acc)
/** Unsafe. Replaces `n`th element of `l` with `e`. Crashes on over/underflow
/* 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) = 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) if(n<0) abort("insert_at underflow") else replace_at_(n, e, l)
private function replace_at_(n : int, e : 'a, l : list('a)) : list('a) = private function replace_at_(n : int, e : 'a, l : list('a)) : list('a) =
@ -79,7 +91,8 @@ namespace List =
h::t => if (n == 0) e::t h::t => if (n == 0) e::t
else h::replace_at_(n-1, e, t) else h::replace_at_(n-1, e, t)
/* Unsafe. Adds `e` to `l` to be its `n`th element. Crashes on over/underflow */ /** 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) = 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) if(n<0) abort("insert_at underflow") else insert_at_(n, e, l)
private function insert_at_(n : int, e : 'a, l : list('a)) : list('a) = private function insert_at_(n : int, e : 'a, l : list('a)) : list('a) =
@ -88,6 +101,9 @@ namespace List =
[] => abort("insert_at overflow") [] => abort("insert_at overflow")
h::t => h::insert_at_(n-1, e, t) h::t => h::insert_at_(n-1, e, t)
/** Assuming that cmp represents `<` comparison, inserts `x` before
* the first element in the list `l` which is greater than it
*/
function insert_by(cmp : (('a, 'a) => bool), x : 'a, l : list('a)) : list('a) = function insert_by(cmp : (('a, 'a) => bool), x : 'a, l : list('a)) : list('a) =
switch(l) switch(l)
[] => [x] [] => [x]
@ -122,6 +138,8 @@ namespace List =
[] => [] [] => []
h::t => f(h)::map(f, t) h::t => f(h)::map(f, t)
/** Effectively composition of `map` and `flatten`
*/
function flat_map(f : 'a => list('b), l : list('a)) : list('b) = function flat_map(f : 'a => list('b), l : list('a)) : list('b) =
ListInternal.flat_map(f, l) ListInternal.flat_map(f, l)
@ -131,7 +149,8 @@ namespace List =
let rest = filter(p, t) let rest = filter(p, t)
if(p(h)) h::rest else rest if(p(h)) h::rest else rest
/* Take up to `n` first elements */ /** Take up to `n` first elements
*/
function take(n : int, l : list('a)) : list('a) = function take(n : int, l : list('a)) : list('a) =
if(n < 0) abort("Take negative number of elements") else take_(n, l) if(n < 0) abort("Take negative number of elements") else take_(n, l)
private function take_(n : int, l : list('a)) : list('a) = private function take_(n : int, l : list('a)) : list('a) =
@ -140,7 +159,8 @@ namespace List =
[] => [] [] => []
h::t => h::take_(n-1, t) h::t => h::take_(n-1, t)
/* Drop up to `n` first elements */ /** Drop up to `n` first elements
*/
function drop(n : int, l : list('a)) : list('a) = function drop(n : int, l : list('a)) : list('a) =
if(n < 0) abort("Drop negative number of elements") else drop_(n, l) if(n < 0) abort("Drop negative number of elements") else drop_(n, l)
private function drop_(n : int, l : list('a)) : list('a) = private function drop_(n : int, l : list('a)) : list('a) =
@ -149,17 +169,22 @@ namespace List =
[] => [] [] => []
h::t => drop_(n-1, t) h::t => drop_(n-1, t)
/* Get the longest prefix of a list in which every element matches predicate `p` */ /** 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) = switch(l) function take_while(p : 'a => bool, l : list('a)) : list('a) = switch(l)
[] => [] [] => []
h::t => if(p(h)) h::take_while(p, t) else [] h::t => if(p(h)) h::take_while(p, t) else []
/* Drop elements from `l` until `p` holds */ /** Drop elements from `l` until `p` holds
*/
function drop_while(p : 'a => bool, l : list('a)) : list('a) = switch(l) function drop_while(p : 'a => bool, l : list('a)) : list('a) = switch(l)
[] => [] [] => []
h::t => if(p(h)) drop_while(p, t) else 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` */ /** 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)) = switch(l) function partition(p : 'a => bool, l : list('a)) : (list('a) * list('a)) = switch(l)
[] => ([], []) [] => ([], [])
h::t => h::t =>
@ -183,7 +208,9 @@ namespace List =
function product(l : list(int)) : int = foldl((a, b) => a * b, 1, 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. */ /** Zips two list by applying bimapping function on respective elements.
* Drops the tail of the longer list.
*/
private function zip_with( f : ('a, 'b) => 'c private function zip_with( f : ('a, 'b) => 'c
, l1 : list('a) , l1 : list('a)
, l2 : list('b) , l2 : list('b)
@ -191,7 +218,9 @@ namespace List =
(h1::t1, h2::t2) => f(h1, h2)::zip_with(f, t1, t2) (h1::t1, h2::t2) => f(h1, h2)::zip_with(f, t1, t2)
_ => [] _ => []
/* Zips two lists into list of pairs. Drops longer tail. */ /** Zips two lists into list of pairs.
* Drops the tail of the longer list.
*/
function zip(l1 : list('a), l2 : list('b)) : list('a * 'b) = zip_with((a, b) => (a, b), l1, l2) 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)) = switch(l) function unzip(l : list('a * 'b)) : (list('a) * list('b)) = switch(l)
@ -207,15 +236,16 @@ namespace List =
h::t => switch (partition((x) => lesser_cmp(x, h), t)) h::t => switch (partition((x) => lesser_cmp(x, h), t))
(lesser, bigger) => sort(lesser_cmp, lesser) ++ h::sort(lesser_cmp, bigger) (lesser, bigger) => sort(lesser_cmp, lesser) ++ h::sort(lesser_cmp, bigger)
/** Puts `delim` between every two members of the list
*/
function intersperse(delim : 'a, l : list('a)) : list('a) = switch(l) function intersperse(delim : 'a, l : list('a)) : list('a) = switch(l)
[] => [] [] => []
[e] => [e] [e] => [e]
h::t => h::delim::intersperse(delim, t) h::t => h::delim::intersperse(delim, t)
/** Effectively a zip with an infinite sequence of natural numbers
*/
function enumerate(l : list('a)) : list(int * 'a) = enumerate_(l, 0) function enumerate(l : list('a)) : list(int * 'a) = enumerate_(l, 0)
private function enumerate_(l : list('a), n : int) : list(int * 'a) = switch(l) private function enumerate_(l : list('a), n : int) : list(int * 'a) = switch(l)
[] => [] [] => []
h::t => (n, h)::enumerate_(t, n + 1) h::t => (n, h)::enumerate_(t, n + 1)

View File

@ -10,13 +10,18 @@ namespace Option =
None => false None => false
Some(_) => true Some(_) => true
/** Catamorphism on `option`. Also known as inlined pattern matching.
*/
function match(n : 'b, s : 'a => 'b, o : option('a)) : 'b = switch(o) function match(n : 'b, s : 'a => 'b, o : option('a)) : 'b = switch(o)
None => n None => n
Some(x) => s(x) Some(x) => s(x)
/** Escape option providing default if `None`
*/
function default(def : 'a, o : option('a)) : 'a = match(def, (x) => x, o) function default(def : 'a, o : option('a)) : 'a = match(def, (x) => x, o)
/** Assume it is `Some`
*/
function force(o : option('a)) : 'a = default(abort("Forced None value"), 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 on_elem(o : option('a), f : 'a => unit) : unit = match((), f, o)
@ -40,10 +45,14 @@ namespace Option =
(Some(x1), Some(x2), Some(x3)) => Some(f(x1, x2, x3)) (Some(x1), Some(x2), Some(x3)) => Some(f(x1, x2, x3))
_ => None _ => None
/** Like `map`, but the function is in `option`
*/
function app_over(f : option ('a => 'b), o : option('a)) : option('b) = switch((f, o)) function app_over(f : option ('a => 'b), o : option('a)) : option('b) = switch((f, o))
(Some(ff), Some(xx)) => Some(ff(xx)) (Some(ff), Some(xx)) => Some(ff(xx))
_ => None _ => None
/** Monadic bind
*/
function flat_map(f : 'a => option('b), o : option('a)) : option('b) = switch(o) function flat_map(f : 'a => option('b), o : option('a)) : option('b) = switch(o)
None => None None => None
Some(x) => f(x) Some(x) => f(x)
@ -53,11 +62,17 @@ namespace Option =
None => [] None => []
Some(x) => [x] Some(x) => [x]
/** Turns list of options into a list of elements that are under `Some`s.
* Safe.
*/
function filter_options(l : list(option('a))) : list('a) = switch(l) function filter_options(l : list(option('a))) : list('a) = switch(l)
[] => [] [] => []
None::t => filter_options(t) None::t => filter_options(t)
Some(x)::t => x::filter_options(t) Some(x)::t => x::filter_options(t)
/** Just like `filter_options` but requires all elements to be `Some` and returns
* None if any of them is not
*/
function seq_options(l : list (option('a))) : option (list('a)) = switch(l) function seq_options(l : list (option('a))) : option (list('a)) = switch(l)
[] => Some([]) [] => Some([])
None::_ => None None::_ => None
@ -66,11 +81,14 @@ namespace Option =
Some(st) => Some(x::st) Some(st) => Some(x::st)
/** Choose `Some` out of two if possible
*/
function choose(o1 : option('a), o2 : option('a)) : option('a) = function choose(o1 : option('a), o2 : option('a)) : option('a) =
if(is_some(o1)) o1 else o2 if(is_some(o1)) o1 else o2
/** Choose `Some` from list of options if possible
*/
function choose_first(l : list(option('a))) : option('a) = switch(l) function choose_first(l : list(option('a))) : option('a) = switch(l)
[] => None [] => None
None::t => choose_first(t) None::t => choose_first(t)
Some(x)::_ => Some(x) Some(x)::_ => Some(x)

View File

@ -6,12 +6,18 @@ namespace Pair =
function snd(t : ('a * 'b)) : 'b = switch(t) function snd(t : ('a * 'b)) : 'b = switch(t)
(_, y) => y (_, y) => y
/** Map over first
*/
function map1(f : 'a => 'c, t : ('a * 'b)) : ('c * 'b) = switch(t) function map1(f : 'a => 'c, t : ('a * 'b)) : ('c * 'b) = switch(t)
(x, y) => (f(x), y) (x, y) => (f(x), y)
/** Map over second
*/
function map2(f : 'b => 'c, t : ('a * 'b)) : ('a * 'c) = switch(t) function map2(f : 'b => 'c, t : ('a * 'b)) : ('a * 'c) = switch(t)
(x, y) => (x, f(y)) (x, y) => (x, f(y))
/** Map over both
*/
function bimap(f : 'a => 'c, g : 'b => 'd, t : ('a * 'b)) : ('c * 'd) = switch(t) function bimap(f : 'a => 'c, g : 'b => 'd, t : ('a * 'b)) : ('c * 'd) = switch(t)
(x, y) => (f(x), g(y)) (x, y) => (f(x), g(y))

View File

@ -10,15 +10,23 @@ namespace Triple =
(_, _, z) => z (_, _, z) => z
/** Map over first
*/
function map1(f : 'a => 'm, t : ('a * 'b * 'c)) : ('m * 'b * 'c) = switch(t) function map1(f : 'a => 'm, t : ('a * 'b * 'c)) : ('m * 'b * 'c) = switch(t)
(x, y, z) => (f(x), y, z) (x, y, z) => (f(x), y, z)
/** Map over second
*/
function map2(f : 'b => 'm, t : ('a * 'b * 'c)) : ('a * 'm * 'c) = switch(t) function map2(f : 'b => 'm, t : ('a * 'b * 'c)) : ('a * 'm * 'c) = switch(t)
(x, y, z) => (x, f(y), z) (x, y, z) => (x, f(y), z)
/** Map over third
*/
function map3(f : 'c => 'm, t : ('a * 'b * 'c)) : ('a * 'b * 'm) = switch(t) function map3(f : 'c => 'm, t : ('a * 'b * 'c)) : ('a * 'b * 'm) = switch(t)
(x, y, z) => (x, y, f(z)) (x, y, z) => (x, y, f(z))
/** Map over all elements
*/
function trimap( f : 'a => 'x function trimap( f : 'a => 'x
, g : 'b => 'y , g : 'b => 'y
, h : 'c => 'z , h : 'c => 'z
@ -29,9 +37,13 @@ namespace Triple =
function swap(t : ('a * 'b * 'c)) : ('c * 'b * 'a) = switch(t) function swap(t : ('a * 'b * 'c)) : ('c * 'b * 'a) = switch(t)
(x, y, z) => (z, y, x) (x, y, z) => (z, y, x)
/** Right rotation
*/
function rotr(t : ('a * 'b * 'c)) : ('c * 'a * 'b) = switch(t) function rotr(t : ('a * 'b * 'c)) : ('c * 'a * 'b) = switch(t)
(x, y, z) => (z, x, y) (x, y, z) => (z, x, y)
/** Left rotation
*/
function rotl(t : ('a * 'b * 'c)) : ('b * 'c * 'a) = switch(t) function rotl(t : ('a * 'b * 'c)) : ('b * 'c * 'a) = switch(t)
(x, y, z) => (y, z, x) (x, y, z) => (y, z, x)

View File

@ -15,7 +15,7 @@
{base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]} {base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]}
]}. ]}.
{relx, [{release, {aesophia, "4.2.0"}, {relx, [{release, {aesophia, "4.3.0"},
[aesophia, aebytecode, getopt]}, [aesophia, aebytecode, getopt]},
{dev_mode, true}, {dev_mode, true},

View File

@ -194,6 +194,8 @@ encode_expr({bytes, _, B}) ->
encode_expr({Lit, _, L}) when Lit == oracle_pubkey; Lit == oracle_query_id; encode_expr({Lit, _, L}) when Lit == oracle_pubkey; Lit == oracle_query_id;
Lit == contract_pubkey; Lit == account_pubkey -> Lit == contract_pubkey; Lit == account_pubkey ->
aeser_api_encoder:encode(Lit, L); aeser_api_encoder:encode(Lit, L);
encode_expr({app, _, {'-', _}, [{int, _, N}]}) ->
encode_expr({int, [], -N});
encode_expr({app, _, F, As}) -> encode_expr({app, _, F, As}) ->
Ef = encode_expr(F), Ef = encode_expr(F),
Eas = encode_exprs(As), Eas = encode_exprs(As),

View File

@ -12,7 +12,11 @@
-module(aeso_ast_infer_types). -module(aeso_ast_infer_types).
-export([infer/1, infer/2, unfold_types_in_type/3]). -export([ infer/1
, infer/2
, unfold_types_in_type/3
, pp_type/2
]).
-type utype() :: {fun_t, aeso_syntax:ann(), named_args_t(), [utype()], utype()} -type utype() :: {fun_t, aeso_syntax:ann(), named_args_t(), [utype()], utype()}
| {app_t, aeso_syntax:ann(), utype(), [utype()]} | {app_t, aeso_syntax:ann(), utype(), [utype()]}
@ -2253,7 +2257,8 @@ unify1(_Env, {bytes_t, _, Len}, {bytes_t, _, Len}, _When) ->
unify1(Env, {if_t, _, {id, _, Id}, Then1, Else1}, {if_t, _, {id, _, Id}, Then2, Else2}, When) -> unify1(Env, {if_t, _, {id, _, Id}, Then1, Else1}, {if_t, _, {id, _, Id}, Then2, Else2}, When) ->
unify(Env, Then1, Then2, When) andalso unify(Env, Then1, Then2, When) andalso
unify(Env, Else1, Else2, When); unify(Env, Else1, Else2, When);
unify1(Env, {fun_t, _, Named1, Args1, Result1}, {fun_t, _, Named2, Args2, Result2}, When) -> unify1(Env, {fun_t, _, Named1, Args1, Result1}, {fun_t, _, Named2, Args2, Result2}, When)
when length(Args1) == length(Args2) ->
unify(Env, Named1, Named2, When) andalso unify(Env, Named1, Named2, When) andalso
unify(Env, Args1, Args2, When) andalso unify(Env, Result1, Result2, When); unify(Env, Args1, Args2, When) andalso unify(Env, Result1, Result2, When);
unify1(Env, {app_t, _, {Tag, _, F}, Args1}, {app_t, _, {Tag, _, F}, Args2}, When) unify1(Env, {app_t, _, {Tag, _, F}, Args1}, {app_t, _, {Tag, _, F}, Args2}, When)
@ -2444,6 +2449,24 @@ mk_t_err(Pos, Msg) ->
mk_t_err(Pos, Msg, Ctxt) -> mk_t_err(Pos, Msg, Ctxt) ->
aeso_errors:new(type_error, Pos, lists:flatten(Msg), lists:flatten(Ctxt)). aeso_errors:new(type_error, Pos, lists:flatten(Msg), lists:flatten(Ctxt)).
mk_error({higher_kinded_typevar, T}) ->
Msg = io_lib:format("Type ~s is a higher kinded type variable\n"
"(takes another type as an argument)\n", [pp(instantiate(T))]
),
mk_t_err(pos(T), Msg);
mk_error({wrong_type_arguments, X, ArityGiven, ArityReal}) ->
Msg = io_lib:format("Arity for ~s doesn't match. Expected ~p, got ~p\n"
, [pp(instantiate(X)), ArityReal, ArityGiven]
),
mk_t_err(pos(X), Msg);
mk_error({unnamed_map_update_with_default, Upd}) ->
Msg = "Invalid map update with default\n",
mk_t_err(pos(Upd), Msg);
mk_error({fundecl_must_have_funtype, _Ann, Id, Type}) ->
Msg = io_lib:format("~s at ~s was declared with an invalid type ~s.\n"
"Entrypoints and functions must have functional types"
, [pp(Id), pp_loc(Id), pp(instantiate(Type))]),
mk_t_err(pos(Id), Msg);
mk_error({cannot_unify, A, B, When}) -> mk_error({cannot_unify, A, B, When}) ->
Msg = io_lib:format("Cannot unify ~s\n and ~s\n", Msg = io_lib:format("Cannot unify ~s\n and ~s\n",
[pp(instantiate(A)), pp(instantiate(B))]), [pp(instantiate(A)), pp(instantiate(B))]),
@ -2526,14 +2549,6 @@ mk_error({indexed_type_must_be_word, Type, Type1}) ->
Msg = io_lib:format("The indexed type ~s (at ~s) equals ~s which is not a word type\n", Msg = io_lib:format("The indexed type ~s (at ~s) equals ~s which is not a word type\n",
[pp_type("", Type), pp_loc(Type), pp_type("", Type1)]), [pp_type("", Type), pp_loc(Type), pp_type("", Type1)]),
mk_t_err(pos(Type), Msg); mk_t_err(pos(Type), Msg);
mk_error({payload_type_must_be_string, Type, Type}) ->
Msg = io_lib:format("The payload type ~s (at ~s) should be string\n",
[pp_type("", Type), pp_loc(Type)]),
mk_t_err(pos(Type), Msg);
mk_error({payload_type_must_be_string, Type, Type1}) ->
Msg = io_lib:format("The payload type ~s (at ~s) equals ~s but it should be string\n",
[pp_type("", Type), pp_loc(Type), pp_type("", Type1)]),
mk_t_err(pos(Type), Msg);
mk_error({event_0_to_3_indexed_values, Constr}) -> mk_error({event_0_to_3_indexed_values, Constr}) ->
Msg = io_lib:format("The event constructor ~s (at ~s) has too many indexed values (max 3)\n", Msg = io_lib:format("The event constructor ~s (at ~s) has too many indexed values (max 3)\n",
[name(Constr), pp_loc(Constr)]), [name(Constr), pp_loc(Constr)]),
@ -2577,13 +2592,21 @@ mk_error({include, _, {string, Pos, Name}}) ->
[binary_to_list(Name), pp_loc(Pos)]), [binary_to_list(Name), pp_loc(Pos)]),
mk_t_err(pos(Pos), Msg); mk_t_err(pos(Pos), Msg);
mk_error({namespace, _Pos, {con, Pos, Name}, _Def}) -> mk_error({namespace, _Pos, {con, Pos, Name}, _Def}) ->
Msg = io_lib:format("Nested namespace not allowed\nNamespace '~s' at ~s not defined at top level.\n", Msg = io_lib:format("Nested namespaces are not allowed\nNamespace '~s' at ~s not defined at top level.\n",
[Name, pp_loc(Pos)]),
mk_t_err(pos(Pos), Msg);
mk_error({contract, _Pos, {con, Pos, Name}, _Def}) ->
Msg = io_lib:format("Nested contracts are not allowed\nContract '~s' at ~s not defined at top level.\n",
[Name, pp_loc(Pos)]),
mk_t_err(pos(Pos), Msg);
mk_error({type_decl, _, {id, Pos, Name}, _}) ->
Msg = io_lib:format("Empty type declarations are not supported\nType ~s at ~s lacks a definition\n",
[Name, pp_loc(Pos)]),
mk_t_err(pos(Pos), Msg);
mk_error({letval, _Pos, {id, Pos, Name}, _Def}) ->
Msg = io_lib:format("Toplevel \"let\" definitions are not supported\nValue ~s at ~s could be replaced by 0-argument function\n",
[Name, pp_loc(Pos)]), [Name, pp_loc(Pos)]),
mk_t_err(pos(Pos), Msg); mk_t_err(pos(Pos), Msg);
mk_error({repeated_arg, Fun, Arg}) ->
Msg = io_lib:format("Repeated argument ~s to function ~s (at ~s).\n",
[Arg, pp(Fun), pp_loc(Fun)]),
mk_t_err(pos(Fun), Msg);
mk_error({stateful_not_allowed, Id, Fun}) -> mk_error({stateful_not_allowed, Id, Fun}) ->
Msg = io_lib:format("Cannot reference stateful function ~s (at ~s)\nin the definition of non-stateful function ~s.\n", Msg = io_lib:format("Cannot reference stateful function ~s (at ~s)\nin the definition of non-stateful function ~s.\n",
[pp(Id), pp_loc(Id), pp(Fun)]), [pp(Id), pp_loc(Id), pp(Fun)]),

View File

@ -347,7 +347,6 @@ decls_to_fcode(Env, Decls) ->
end, Env1, Decls). end, Env1, 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 = #{context := {main_contract, _}}, {fun_decl, _, Id, _}) -> decl_to_fcode(Env = #{context := {main_contract, _}}, {fun_decl, _, Id, _}) ->
case is_no_code(Env) of case is_no_code(Env) of
false -> fcode_error({missing_definition, Id}); false -> fcode_error({missing_definition, Id});

View File

@ -38,6 +38,7 @@
| pp_assembler | pp_assembler
| pp_bytecode | pp_bytecode
| no_code | no_code
| keep_included
| {backend, aevm | fate} | {backend, aevm | fate}
| {include, {file_system, [string()]} | | {include, {file_system, [string()]} |
{explicit_files, #{string() => binary()}}} {explicit_files, #{string() => binary()}}}
@ -336,12 +337,12 @@ to_sophia_value(ContractString, FunName, ok, Data, Options0) ->
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)), Type1 = prettypr:format(aeso_pretty:type(Type0)),
Msg = io_lib:format("Cannot translate FATE value ~p\n of Sophia type ~s\n", Msg = 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), Type1]),
{error, [aeso_errors:new(data_error, Msg)]}; {error, [aeso_errors:new(data_error, Msg)]};
_:_ -> _:_ ->
Type1 = prettypr:format(aeso_pretty:type(Type)), Type1 = prettypr:format(aeso_pretty:type(Type0)),
Msg = io_lib:format("Failed to decode binary as type ~s\n", [Type1]), Msg = io_lib:format("Failed to decode binary as type ~s\n", [Type1]),
{error, [aeso_errors:new(data_error, Msg)]} {error, [aeso_errors:new(data_error, Msg)]}
end end
@ -650,8 +651,9 @@ pp_fate_type(T) -> io_lib:format("~w", [T]).
%% ------------------------------------------------------------------- %% -------------------------------------------------------------------
-spec sophia_type_to_typerep(string()) -> {error, bad_type} | {ok, aeb_aevm_data:type()}.
sophia_type_to_typerep(String) -> sophia_type_to_typerep(String) ->
{ok, Ast} = aeso_parser:type(String), Ast = aeso_parser:run_parser(aeso_parser:type(), String),
try aeso_ast_to_icode:ast_typerep(Ast) of try aeso_ast_to_icode:ast_typerep(Ast) of
Type -> {ok, Type} Type -> {ok, Type}
catch _:_ -> {error, bad_type} catch _:_ -> {error, bad_type}

View File

@ -3,20 +3,33 @@
%%% Description : %%% Description :
%%% Created : 1 Mar 2018 by Ulf Norell %%% Created : 1 Mar 2018 by Ulf Norell
-module(aeso_parser). -module(aeso_parser).
-compile({no_auto_import,[map_get/2]}).
-export([string/1, -export([string/1,
string/2, string/2,
string/3, string/3,
auto_imports/1,
hash_include/2, hash_include/2,
type/1]). decl/0,
type/0,
body/0,
maybe_block/1,
run_parser/2,
run_parser/3]).
-include("aeso_parse_lib.hrl"). -include("aeso_parse_lib.hrl").
-import(aeso_parse_lib, [current_file/0, set_current_file/1]). -import(aeso_parse_lib, [current_file/0, set_current_file/1]).
-type parse_result() :: aeso_syntax:ast() | none(). -type parse_result() :: aeso_syntax:ast() | {aeso_syntax:ast(), sets:set(include_hash())} | none().
-type include_hash() :: {string(), binary()}. -type include_hash() :: {string(), binary()}.
escape_errors({ok, Ok}) ->
Ok;
escape_errors({error, Err}) ->
parse_error(Err).
-spec string(string()) -> parse_result(). -spec string(string()) -> parse_result().
string(String) -> string(String) ->
string(String, sets:new(), []). string(String, sets:new(), []).
@ -30,21 +43,17 @@ string(String, Opts) ->
-spec string(string(), sets:set(include_hash()), aeso_compiler:options()) -> parse_result(). -spec string(string(), sets:set(include_hash()), aeso_compiler:options()) -> parse_result().
string(String, Included, Opts) -> string(String, Included, Opts) ->
case parse_and_scan(file(), String, Opts) of AST = run_parser(file(), String, Opts),
{ok, AST} -> case expand_includes(AST, Included, Opts) of
case expand_includes(AST, Included, Opts) of {ok, AST1} -> AST1;
{ok, AST1} -> AST1; {error, Err} -> parse_error(Err)
{error, Err} -> parse_error(Err)
end;
{error, Err} ->
parse_error(Err)
end. end.
type(String) ->
case parse_and_scan(type(), String, []) of run_parser(P, Inp) ->
{ok, AST} -> {ok, AST}; escape_errors(parse_and_scan(P, Inp, [])).
{error, Err} -> {error, [mk_error(Err)]} run_parser(P, Inp, Opts) ->
end. escape_errors(parse_and_scan(P, Inp, Opts)).
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)),
@ -102,7 +111,7 @@ decl() ->
%% Function declarations %% Function declarations
, ?RULE(modifiers(), fun_or_entry(), maybe_block(fundef_or_decl()), fun_block(_1, _2, _3)) , ?RULE(modifiers(), fun_or_entry(), maybe_block(fundef_or_decl()), fun_block(_1, _2, _3))
, ?RULE(keyword('let'), valdef(),set_pos(get_pos(_1), _2)) , ?RULE(keyword('let'), valdef(), set_pos(get_pos(_1), _2))
])). ])).
fun_block(Mods, Kind, [Decl]) -> fun_block(Mods, Kind, [Decl]) ->
@ -596,8 +605,13 @@ expand_includes(AST, Included, Opts) ->
|| File <- lists:usort(auto_imports(AST)) ] ++ AST, || File <- lists:usort(auto_imports(AST)) ] ++ AST,
expand_includes(AST1, Included, [], Opts). expand_includes(AST1, Included, [], Opts).
expand_includes([], _Included, Acc, _Opts) -> expand_includes([], Included, Acc, Opts) ->
{ok, lists:reverse(Acc)}; case lists:member(keep_included, Opts) of
false ->
{ok, lists:reverse(Acc)};
true ->
{ok, {lists:reverse(Acc), Included}}
end;
expand_includes([{include, Ann, {string, _SAnn, File}} | AST], Included, Acc, Opts) -> expand_includes([{include, Ann, {string, _SAnn, File}} | AST], Included, Acc, Opts) ->
case get_include_code(File, Ann, Opts) of case get_include_code(File, Ann, Opts) of
{ok, Code} -> {ok, Code} ->
@ -656,8 +670,14 @@ stdlib_options() ->
get_include_code(File, Ann, Opts) -> get_include_code(File, Ann, Opts) ->
case {read_file(File, Opts), read_file(File, stdlib_options())} of case {read_file(File, Opts), read_file(File, stdlib_options())} of
{{ok, _}, {ok,_ }} -> {{ok, Bin}, {ok, _}} ->
fail(ann_pos(Ann), "Illegal redefinition of standard library " ++ File); case filename:basename(File) == File of
true -> { error
, fail( ann_pos(Ann)
, "Illegal redefinition of standard library " ++ binary_to_list(File))};
%% If a path is provided then the stdlib takes lower priority
false -> {ok, binary_to_list(Bin)}
end;
{_, {ok, Bin}} -> {_, {ok, Bin}} ->
{ok, binary_to_list(Bin)}; {ok, binary_to_list(Bin)};
{{ok, Bin}, _} -> {{ok, Bin}, _} ->

View File

@ -145,8 +145,12 @@ decl(D, Options) ->
with_options(Options, fun() -> decl(D) end). with_options(Options, fun() -> decl(D) end).
-spec decl(aeso_syntax:decl()) -> doc(). -spec decl(aeso_syntax:decl()) -> doc().
decl({contract, _, C, Ds}) -> decl({contract, Attrs, C, Ds}) ->
block(follow(text("contract"), hsep(name(C), text("="))), decls(Ds)); Mod = fun({Mod, true}) when Mod == payable ->
text(atom_to_list(Mod));
(_) -> empty() end,
block(follow( hsep(lists:map(Mod, Attrs) ++ [text("contract")])
, hsep(name(C), text("="))), decls(Ds));
decl({namespace, _, C, Ds}) -> decl({namespace, _, C, Ds}) ->
block(follow(text("namespace"), hsep(name(C), text("="))), decls(Ds)); block(follow(text("namespace"), hsep(name(C), text("="))), decls(Ds));
decl({pragma, _, Pragma}) -> pragma(Pragma); decl({pragma, _, Pragma}) -> pragma(Pragma);
@ -155,13 +159,16 @@ decl({type_def, _, T, Vars, Def}) ->
Kind = element(1, Def), Kind = element(1, Def),
equals(typedecl(Kind, T, Vars), typedef(Def)); equals(typedecl(Kind, T, Vars), typedef(Def));
decl({fun_decl, Ann, F, T}) -> decl({fun_decl, Ann, F, T}) ->
Mod = fun({Mod, true}) when Mod == private; Mod == stateful; Mod == payable ->
text(atom_to_list(Mod));
(_) -> empty() end,
Fun = case aeso_syntax:get_ann(entrypoint, Ann, false) of Fun = case aeso_syntax:get_ann(entrypoint, Ann, false) of
true -> text("entrypoint"); true -> text("entrypoint");
false -> text("function") false -> text("function")
end, end,
hsep(Fun, typed(name(F), T)); hsep(lists:map(Mod, Ann) ++ [Fun, typed(name(F), T)]);
decl(D = {letfun, Attrs, _, _, _, _}) -> decl(D = {letfun, Attrs, _, _, _, _}) ->
Mod = fun({Mod, true}) when Mod == private; Mod == stateful -> Mod = fun({Mod, true}) when Mod == private; Mod == stateful; Mod == payable ->
text(atom_to_list(Mod)); text(atom_to_list(Mod));
(_) -> empty() end, (_) -> empty() end,
Fun = case aeso_syntax:get_ann(entrypoint, Attrs, false) of Fun = case aeso_syntax:get_ann(entrypoint, Attrs, false) of
@ -363,7 +370,8 @@ expr_p(_, {Type, _, Bin})
Type == oracle_query_id -> Type == oracle_query_id ->
text(binary_to_list(aeser_api_encoder:encode(Type, Bin))); text(binary_to_list(aeser_api_encoder:encode(Type, Bin)));
expr_p(_, {string, _, <<>>}) -> text("\"\""); expr_p(_, {string, _, <<>>}) -> text("\"\"");
expr_p(_, {string, _, S}) -> term(binary_to_list(S)); expr_p(_, {string, _, S}) ->
text(io_lib:format("\"~s\"", [binary_to_list(S)]));
expr_p(_, {char, _, C}) -> expr_p(_, {char, _, C}) ->
case C of case C of
$' -> text("'\\''"); $' -> text("'\\''");
@ -490,6 +498,3 @@ get_elifs(If = {'if', Ann, Cond, Then, Else}, Elifs) ->
end; end;
get_elifs(Else, Elifs) -> {lists:reverse(Elifs), {else, Else}}. get_elifs(Else, Elifs) -> {lists:reverse(Elifs), {else, Else}}.
fmt(Fmt, Args) -> text(lists:flatten(io_lib:format(Fmt, Args))).
term(X) -> fmt("~p", [X]).

View File

@ -37,20 +37,24 @@
-type decl() :: {contract, ann(), con(), [decl()]} -type decl() :: {contract, ann(), con(), [decl()]}
| {namespace, ann(), con(), [decl()]} | {namespace, ann(), con(), [decl()]}
| {pragma, ann(), pragma()} | {pragma, ann(), pragma()}
| {type_decl, ann(), id(), [tvar()]} | {type_decl, ann(), id(), [tvar()]} % Only for error msgs
| {type_def, ann(), id(), [tvar()], typedef()} | {type_def, ann(), id(), [tvar()], typedef()}
| {fun_decl, ann(), id(), type()} | {fun_decl, ann(), id(), type()}
| {fun_clauses, ann(), id(), type(), [letbind()]} | {fun_clauses, ann(), id(), type(), [letbind()]}
| {block, ann(), [decl()]} | {block, ann(), [decl()]}
| letbind(). | letfun()
| letval(). % Only for error msgs
-type compiler_version() :: [non_neg_integer()]. -type compiler_version() :: [non_neg_integer()].
-type pragma() :: {compiler, '==' | '<' | '>' | '=<' | '>=', compiler_version()}. -type pragma() :: {compiler, '==' | '<' | '>' | '=<' | '>=', compiler_version()}.
-type letval() :: {letval, ann(), pat(), expr()}.
-type letfun() :: {letfun, ann(), id(), [pat()], type(), expr()}.
-type letbind() -type letbind()
:: {letval, ann(), pat(), expr()} :: letfun()
| {letfun, ann(), id(), [pat()], type(), expr()}. | letval().
-type arg() :: {arg, ann(), id(), type()}. -type arg() :: {arg, ann(), id(), type()}.

View File

@ -45,7 +45,6 @@ fold(Alg = #alg{zero = Zero, plus = Plus, scoped = Scoped}, Fun, K, X) ->
%% decl() %% decl()
{contract, _, _, Ds} -> Decl(Ds); {contract, _, _, Ds} -> Decl(Ds);
{namespace, _, _, Ds} -> Decl(Ds); {namespace, _, _, Ds} -> Decl(Ds);
{type_decl, _, I, _} -> BindType(I);
{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, _, P, E} -> Scoped(BindExpr(P), Expr(E)); {letval, _, P, E} -> Scoped(BindExpr(P), Expr(E));

View File

@ -1,6 +1,6 @@
{application, aesophia, {application, aesophia,
[{description, "Contract Language for aeternity"}, [{description, "Contract Language for aeternity"},
{vsn, "4.2.0"}, {vsn, "4.3.0"},
{registered, []}, {registered, []},
{applications, {applications,
[kernel, [kernel,

View File

@ -381,11 +381,15 @@ failing_contracts() ->
" r.foo() : map(int, string)\n" " r.foo() : map(int, string)\n"
"against the expected type\n" "against the expected type\n"
" map(string, int)">>]) " map(string, int)">>])
, ?TYPE_ERROR(bad_include_and_ns, , ?TYPE_ERROR(not_toplevel_include,
[<<?Pos(2, 11) [<<?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.">>])
<<?Pos(3, 13) , ?TYPE_ERROR(not_toplevel_namespace,
"Nested namespace not allowed\nNamespace 'Foo' at line 3, column 13 not defined at top level.">>]) [<<?Pos(2, 13)
"Nested namespaces are not allowed\nNamespace 'Foo' at line 2, column 13 not defined at top level.">>])
, ?TYPE_ERROR(not_toplevel_contract,
[<<?Pos(2, 12)
"Nested contracts are not allowed\nContract 'Con' at line 2, column 12 not defined at top level.">>])
, ?TYPE_ERROR(bad_address_literals, , ?TYPE_ERROR(bad_address_literals,
[<<?Pos(11, 5) [<<?Pos(11, 5)
"Cannot unify address\n" "Cannot unify address\n"
@ -622,6 +626,44 @@ failing_contracts() ->
[<<?Pos(5, 28) [<<?Pos(5, 28)
"Invalid call to contract entrypoint 'Foo.foo'.\n" "Invalid call to contract entrypoint 'Foo.foo'.\n"
"It must be called as 'c.foo' for some c : Foo.">>]) "It must be called as 'c.foo' for some c : Foo.">>])
, ?TYPE_ERROR(toplevel_let,
[<<?Pos(2, 7)
"Toplevel \"let\" definitions are not supported\n"
"Value this_is_illegal at line 2, column 7 could be replaced by 0-argument function">>])
, ?TYPE_ERROR(empty_typedecl,
[<<?Pos(2, 8)
"Empty type declarations are not supported\n"
"Type t at line 2, column 8 lacks a definition">>])
, ?TYPE_ERROR(higher_kinded_type,
[<<?Pos(2, 35)
"Type 'm is a higher kinded type variable\n"
"(takes another type as an argument)">>])
, ?TYPE_ERROR(bad_arity,
[<<?Pos(3, 20)
"Arity for id doesn't match. Expected 1, got 0">>,
<<?Pos(3, 25)
"Cannot unify int\n"
" and id\n"
"when checking the type of the expression at line 3, column 25\n"
" 123 : int\n"
"against the expected type\n"
" id">>,
<<?Pos(4, 20)
"Arity for id doesn't match. Expected 1, got 2">>,
<<?Pos(4, 35)
"Cannot unify int\n"
" and id(int, int)\n"
"when checking the type of the expression at line 4, column 35\n"
" 123 : int\n"
"against the expected type\n"
" id(int, int)">>])
, ?TYPE_ERROR(bad_unnamed_map_update_default,
[<<?Pos(4, 17)
"Invalid map update with default">>])
, ?TYPE_ERROR(non_functional_entrypoint,
[<<?Pos(2, 14)
"f at line 2, column 14 was declared with an invalid type int.\n"
"Entrypoints and functions must have functional types">>])
, ?TYPE_ERROR(bad_records, , ?TYPE_ERROR(bad_records,
[<<?Pos(3, 16) [<<?Pos(3, 16)
"Mixed record fields and map keys in\n" "Mixed record fields and map keys in\n"
@ -639,6 +681,28 @@ failing_contracts() ->
" (0 : int) == (1 : int) : bool\n" " (0 : int) == (1 : int) : bool\n"
"It must be either 'true' or 'false'.">> "It must be either 'true' or 'false'.">>
]) ])
, ?TYPE_ERROR(bad_number_of_args,
[<<?Pos(3, 39)
"Cannot unify () => unit\n"
" and (int) => 'a\n",
"when checking the application at line 3, column 39 of\n"
" f : () => unit\n"
"to arguments\n"
" 1 : int">>,
<<?Pos(4, 20)
"Cannot unify (int, string) => 'e\n"
" and (int) => 'd\n"
"when checking the application at line 4, column 20 of\n"
" g : (int, string) => 'e\n"
"to arguments\n"
" 1 : int">>,
<<?Pos(5, 20)
"Cannot unify (int, string) => 'c\n"
" and (string) => 'b\n"
"when checking the application at line 5, column 20 of\n"
" g : (int, string) => 'c\nto arguments\n"
" \"Litwo, ojczyzno moja\" : string">>
])
]. ].
-define(Path(File), "code_errors/" ??File). -define(Path(File), "code_errors/" ??File).

View File

@ -58,8 +58,7 @@ contract Greeter =
let state = { greeting = "Hello" } let state = { greeting = "Hello" }
let setGreeting = function setGreeting(greeting: string) =
(greeting: string) =>
state{ greeting = greeting } state{ greeting = greeting }

View File

@ -1,44 +1,82 @@
// Try to cover all syntactic constructs. // Try to cover all syntactic constructs.
@compiler > 0
@compiler =< 10.1.1.1.1.1.2.3.4
contract AllSyntaxType =
type typeDecl /* bla */
type paramTypeDecl('a, 'b)
namespace Ns =
datatype d('a) = D | S(int) | M('a, list('a), int)
private function fff() = 123
stateful entrypoint
f (1, x) = (_) => x
payable contract AllSyntaxType =
/** Multi- /** Multi-
* line * line
* comment * comment
*/ */
function foo : _ stateful function foo : _
entrypoint bar : int => (int * 'a)
contract AllSyntax = contract AllSyntax =
type typeDecl = int datatype mickiewicz = Adam | Mickiewicz
type paramTypeDecl('a, 'b) = (('a, 'b) => 'b) => list('a) => 'b => 'b record goethe('a, 'b) = {
johann : int,
wolfgang : 'a,
von : 'a * 'b * int,
goethe : unit
}
type dante = Ns.d(int)
type shakespeare('a) = goethe('a, 'a)
record nestedRecord = { x : int } type state = shakespeare(int)
record recordType = { z : nestedRecord, y : int }
datatype variantType('a) = None | Some('a)
let valWithType : map(int, int) => option(int) = (m) => Map.get(m, 42) entrypoint init() = {
let valNoType = johann = 1000,
if(valWithType(Map.empty) == None) wolfgang = -10,
print(42 mod 10 * 5 / 3)
function funWithType(x : int, y) : int * list(int) = (x, 0 :: [y] ++ []) /* TODO: This does not compile because of bug in the parser tester.
function funNoType() = von = (2 + 2, 0, List.sum([x | k <- [1,2,3]
let foo = (x, y : bool) => , let l = k + 1
if (! (y && x =< 0x0b || true)) [x] , if(l < 10)
else [11..20] , let f(x) = x + 100
let setY(r : recordType) : unit = r{ y = 5 } , Adam <- [Adam, Mickiewicz]
let setX(r : recordType, x : int) : recordType = r { z.x = x } // nested record update , let x = f(l)
let getY(r) = switch(r) {y = y} => y ])),
switch (funWithType(1, -2)) */
(x, [y, z]) => bar({x = z, y = -y + - -z * (-1)}) von = (2 + 2, 0, List.sum([1,2,3,4])),
(x, y :: _) => () goethe = () }
let hash : address = #01ab0fff11 function f() =
let b = false let kp = "nietzsche"
let qcon = Mod.Con // let p = "Пушкин" // TODO: this also doesn't do right round_trip...
let str = "blabla\nfoo" let k(x : bytes(8)) : bytes(8) = Bytes.to_int(#fedcba9876543210)
let chr = '"'
let f : () => address = () => ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt
if(Bits.test(Bits.all, 10))
abort("ohno")
if(true && false)
require(true, "ohyes")
elif(false || 2 == 2)
()
else
()
if(true) f(1,2)((1,2))
else switch(1::[1,2,3])
[] => 1
a::b => 123
1::2::3 => 123123
[2,3,4] => 1
_ => 13
1::[2] => 2138
put(state{johann = 1})
let m = {["foo"] = 19, /*hey wanna talk about inlined comments?*/ ["bar"] = 42}
let n = {}
m{ ["x" = 0] @ z = z + state.johann }
let sh : shakespeare(shakespeare(int)) =
{wolfgang = state}
sh{wolfgang.wolfgang = sh.wolfgang} // comment

View File

@ -0,0 +1,4 @@
contract C =
type id('a) = 'a
entrypoint f() : id = 123
entrypoint g() : id(int, int) = 123

View File

@ -0,0 +1,6 @@
contract Test =
entrypoint f() = ()
entrypoint g(x : int, y : string) = f(1)
entrypoint h() = g(1)
entrypoint i() = g("Litwo, ojczyzno moja")

View File

@ -0,0 +1,5 @@
contract C =
entrypoint f() =
let z = 123
{}{ [1 = 0] = z + 1 }
2

View File

@ -0,0 +1,3 @@
contract C =
type t
entrypoint f() = 123

View File

@ -0,0 +1,3 @@
contract IWantToBelieve =
type stateT('s, 'm, 'a) = 's => 'm('a * 's)
entrypoint s() = 123

View File

@ -15,7 +15,7 @@ contract MultiSig =
| OwnerRemoved (address) // of { .removedOwner : Address } | OwnerRemoved (address) // of { .removedOwner : Address }
| ReqChanged (int) // of { .newReq : int } | ReqChanged (int) // of { .newReq : int }
let maxOwners : int = 250 function maxOwners() : int = 250
record state = { nRequired : int record state = { nRequired : int
, nOwners : int , nOwners : int
@ -68,7 +68,7 @@ contract MultiSig =
switch(check_pending(callhash())) switch(check_pending(callhash()))
CheckFail(state') => { state = state' } CheckFail(state') => { state = state' }
CheckOk(state') => CheckOk(state') =>
if(state.nOwners >= maxOwners) () /* TODO */ if(state.nOwners >= maxOwners()) () /* TODO */
else else
let nOwners' = state'.nOwners + 1 let nOwners' = state'.nOwners + 1
{ state = state' { owners = Map.insert(nOwners', newOwner, state'.owners) { state = state' { owners = Map.insert(nOwners', newOwner, state'.owners)

View File

@ -0,0 +1,5 @@
contract C1 =
entrypoint f : int
contract C =
entrypoint f() = 123

View File

@ -0,0 +1,6 @@
namespace BadNs =
contract Con =
entrypoint e : () => int
contract Con =
entrypoint foo() = 43

View File

@ -0,0 +1,5 @@
namespace BadNs =
include "included.aes"
contract Con =
entrypoint foo() = 43

View File

@ -1,5 +1,4 @@
contract Bad = contract BadCon =
include "included.aes"
namespace Foo = namespace Foo =
function foo() = 42 function foo() = 42

View File

@ -0,0 +1,3 @@
contract C =
let this_is_illegal = 2/0
entrypoint this_is_legal() = 2/0

View File

@ -1,16 +1,3 @@
/* Contract type */
contract VotingType =
type state
function init : list(string) => state
function giveRightToVote : address => unit
function delegate : address => unit
function vote : int => unit
function winnerName : unit => string
function currentTally : unit => list(string * int)
/* Contract implementation */
contract Voting = contract Voting =
// Types // Types