Compare commits
174 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2311d19602 | |||
| 2a78189f31 | |||
| 4504fb8dcf | |||
| 8798e0b2c9 | |||
| 1dfc349065 | |||
| 1266d9ea99 | |||
| bbb049cb2e | |||
| 787551b8bc | |||
| ac673602b9 | |||
| 0b83422189 | |||
| 1a5017ce2b | |||
| 079b3a45c9 | |||
| c0d9759e60 | |||
| b7b242bc66 | |||
| 25fa365c29 | |||
| bb728db51b | |||
| 1fee306daa | |||
| c4eaf2249a | |||
| 6c23fd0d41 | |||
| 3d73e52d48 | |||
| 89b3ec3d17 | |||
| 7e32ef57c2 | |||
| ed5447e430 | |||
| db4de5d926 | |||
| 6b60fde2df | |||
| dd8eea0d55 | |||
| eb71abc665 | |||
| eff1ad4688 | |||
| cb2588fae2 | |||
| 08261a319b | |||
| f21717a9c0 | |||
| 9753f90034 | |||
| 8f240a7ddf | |||
| 54e43764ca | |||
| 05b87fe200 | |||
| cc07e3a638 | |||
| ea5850cf93 | |||
| d2dcb9e249 | |||
| adb3cf5406 | |||
| ad78f440d9 | |||
| 962ddf5303 | |||
| 85b151aa65 | |||
| 93341dc13b | |||
| 98036eff65 | |||
| dc977f7354 | |||
| 4f554acee6 | |||
| 48b52cb501 | |||
| 515838e2f9 | |||
| 83e03f3013 | |||
| 42cd47d1b3 | |||
| 93d2086ddf | |||
| 9487b79f42 | |||
| e64ac9396a | |||
| 4a812b6f3b | |||
| fe2d93ea8a | |||
| ecbc15db1b | |||
| d0caee24d9 | |||
| 57eb77f2f8 | |||
| 53ed60b498 | |||
| e49738c90c | |||
| a38a365181 | |||
| d7fa4d65ec | |||
| 0dddac3d86 | |||
| 3da694e798 | |||
| e98edd4eef | |||
| 2bad76314f | |||
| bd7ed2ef8c | |||
| b9acf24dca | |||
| 2bf65cfd98 | |||
| 6682b24156 | |||
| b31be6227d | |||
| bbc8555331 | |||
| 13bc821211 | |||
| 34c10e1518 | |||
| bb79e7dd89 | |||
| c3426f0e65 | |||
| db01e237c1 | |||
| 760d2841d1 | |||
| 43013ec920 | |||
| d821de6381 | |||
| efd45df820 | |||
| a6f51d23f3 | |||
| 4d4a14a9ab | |||
| 282f743925 | |||
| cf1072140e | |||
| f7abaf07fa | |||
| d019e44924 | |||
| ad54134961 | |||
| b51a79b5e1 | |||
| d844c4d276 | |||
| 64e2fff91a | |||
| d4f291f252 | |||
| b9f585ebaf | |||
| 954af13f59 | |||
| 2e4558b3b4 | |||
| a403a9d227 | |||
| c7b846cbfe | |||
| bf5e2e2443 | |||
| 75797686ad | |||
| ed9384c2af | |||
| 1c24a700dc | |||
| f2e9fbcc51 | |||
| 2d49426fe0 | |||
| f5df2c1a5f | |||
| 04445e4dee | |||
| 46a30b118f | |||
| bb1a45c557 | |||
| 0a22c7a34a | |||
| c8153f94a6 | |||
| 63d51baaa3 | |||
| cb045b0256 | |||
| c84064da7f | |||
| ad88797cef | |||
| 6c3932b10c | |||
| 8d7c637241 | |||
| a8119f1219 | |||
| d0fdd06d66 | |||
| 99ecda4b7b | |||
| e645a8d034 | |||
| 499e2f8200 | |||
| 5465b74ac9 | |||
| 6ca63e4b40 | |||
| 08b6148223 | |||
| eec70f03a5 | |||
| 8a47603b62 | |||
| d4c9d369b1 | |||
| 8984ecc32d | |||
| 025c837886 | |||
| 06e6138de1 | |||
| 7eb4423e70 | |||
| bd64260e37 | |||
| 6380e04a97 | |||
| 2be3c9194d | |||
| d0cfd9cbbe | |||
| 7f7f53e044 | |||
| 7d8a773d6a | |||
| c2c8e297ae | |||
| 5c5d3c60ef | |||
| d3f5d7f5c5 | |||
| 0b474843f9 | |||
| 1a628ab29f | |||
| 03ad1ad1dd | |||
| bfcb9ab324 | |||
| 4cc88be296 | |||
| 505603ad71 | |||
| 2d7c860e3a | |||
| 4976e0402e | |||
| 0478df72fc | |||
| 35b20800c9 | |||
| d4c5c610ee | |||
| 6868bec3ed | |||
| e5702c068c | |||
| a4b21063e3 | |||
| aca6b89fcf | |||
| 13b196568b | |||
| eba4f1c79c | |||
| 1ca3018958 | |||
| e6b5c5a526 | |||
| 47ad607dd5 | |||
| e8a54395bf | |||
| a87065c3a0 | |||
| 49f9ef955f | |||
| f42353b300 | |||
| 2a3274ba25 | |||
| 13b7bde44b | |||
| baf527b5fa | |||
| 5d23a76094 | |||
| 878140e03c | |||
| 422baa5b65 | |||
| 126e04ae42 | |||
| ac58eb4259 | |||
| 22b88bd393 | |||
| 83c3015899 | |||
| ec9434fbfd |
@@ -8,6 +8,15 @@ executors:
|
||||
working_directory: ~/aesophia
|
||||
|
||||
jobs:
|
||||
verify_rebar_lock:
|
||||
executor: aebuilder
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Ensure lock file is up-to-date
|
||||
command: |
|
||||
./rebar3 upgrade
|
||||
git diff --quiet -- rebar.lock || (echo "rebar.lock is not up-to-date" && exit 1)
|
||||
build:
|
||||
executor: aebuilder
|
||||
steps:
|
||||
@@ -35,3 +44,10 @@ jobs:
|
||||
- _build/default/rebar3_20.3.8_plt
|
||||
- store_artifacts:
|
||||
path: _build/test/logs
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
build_test:
|
||||
jobs:
|
||||
- build
|
||||
- verify_rebar_lock
|
||||
|
||||
+74
-41
@@ -9,51 +9,71 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Changed
|
||||
### 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
|
||||
### Added
|
||||
- Allow separate entrypoint/function type signature and definition, and pattern
|
||||
matching in left-hand sides:
|
||||
```
|
||||
function
|
||||
length : list('a) => int
|
||||
length([]) = 0
|
||||
length(x :: xs) = 1 + length(xs)
|
||||
```
|
||||
- Allow pattern matching in list comprehension generators (filtering out match
|
||||
failures):
|
||||
```
|
||||
function somes(xs : list(option('a))) : list('a) =
|
||||
[ x | Some(x) <- xs ]
|
||||
```
|
||||
- Allow pattern matching in let-bindings (aborting on match failures):
|
||||
```
|
||||
function test(m : map(int, int)) =
|
||||
let Some(x) = Map.lookup(m, 0)
|
||||
x
|
||||
```
|
||||
### Changed
|
||||
- FATE code generator improvements.
|
||||
- Bug fix: Handle qualified constructors in patterns.
|
||||
- Bug fix: Allow switching also on negative numbers.
|
||||
### Removed
|
||||
|
||||
## [4.1.0] - 2019-11-26
|
||||
### Added
|
||||
- Support encoding and decoding bit fields in call arguments and results.
|
||||
### Changed
|
||||
- Various improvements to FATE code generator.
|
||||
### Removed
|
||||
|
||||
## [4.0.0] - 2019-10-11
|
||||
### Added
|
||||
- `Address.to_contract` - casts an address to a (any) contract type.
|
||||
- Pragma to check compiler version, e.g. `@compiler >= 4.0`.
|
||||
### Changed
|
||||
- Nice type error if contract function is called as from a namespace.
|
||||
- Fail on function definitions in contracts other than the main contract.
|
||||
- Bug fix in variable optimization - don't discard writes to the store/state.
|
||||
### Removed
|
||||
|
||||
## [4.0.0-rc5] - 2019-09-27
|
||||
### Added
|
||||
### Changed
|
||||
- Bug fixes in error reporting.
|
||||
- Bug fix in variable liveness analysis for FATE.
|
||||
### Removed
|
||||
|
||||
## [4.0.0-rc4] - 2019-09-13
|
||||
### Added
|
||||
- Handle numeric escapes, i.e. `"\x19Ethereum Signed Message:\n"`, and similar strings.
|
||||
### Changed
|
||||
### 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)
|
||||
- [Standard libraries](https://github.com/aeternity/aesophia/blob/master/docs/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.
|
||||
@@ -70,6 +90,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
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
|
||||
- Nice type error if contract function is called as from a namespace.
|
||||
- Fail on function definitions in contracts other than the main contract.
|
||||
- Bug fix in variable optimization - don't discard writes to the store/state.
|
||||
- Bug fixes in error reporting.
|
||||
- Bug fix in variable liveness analysis for FATE.
|
||||
- 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.
|
||||
- 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,
|
||||
@@ -177,12 +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
|
||||
pass a (stubbed) contract string.
|
||||
|
||||
[Unreleased]: https://github.com/aeternity/aesophia/compare/v4.0.0...HEAD
|
||||
[4.0.0]: https://github.com/aeternity/aesophia/compare/v4.0.0...v3.2.0
|
||||
[4.0.0-rc5]: https://github.com/aeternity/aesophia/compare/v4.0.0-rc4...v4.0.0-rc5
|
||||
[4.0.0-rc4]: https://github.com/aeternity/aesophia/compare/v4.0.0-rc3...v4.0.0-rc4
|
||||
[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
|
||||
[Unreleased]: https://github.com/aeternity/aesophia/compare/v4.3.0...HEAD
|
||||
[4.3.0]: https://github.com/aeternity/aesophia/compare/v4.2.0...v4.3.0
|
||||
[4.2.0]: https://github.com/aeternity/aesophia/compare/v4.1.0...v4.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.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
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
# aesophia
|
||||
|
||||
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__ to [FATE](https://github.com/aeternity/protocol/blob/master/contracts/fate.md) instructions.
|
||||
|
||||
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).
|
||||
The compiler is currently being used three places
|
||||
- [The command line compiler](https://github.com/aeternity/aesophia_cli)
|
||||
- [The HTTP compiler](https://github.com/aeternity/aesophia_http)
|
||||
- In [Aeternity node](https://github.com/aeternity/aeternity) tests
|
||||
|
||||
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
|
||||
also be included in other systems to compile contracts coded in sophia which
|
||||
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
|
||||
|
||||
|
||||
+1163
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,68 @@
|
||||
namespace BLS12_381 =
|
||||
type fr = MCL_BLS12_381.fr
|
||||
type fp = MCL_BLS12_381.fp
|
||||
record fp2 = { x1 : fp, x2 : fp }
|
||||
record g1 = { x : fp, y : fp, z : fp }
|
||||
record g2 = { x : fp2, y : fp2, z : fp2 }
|
||||
record gt = { x1 : fp, x2 : fp, x3 : fp, x4 : fp, x5 : fp, x6 : fp,
|
||||
x7 : fp, x8 : fp, x9 : fp, x10 : fp, x11 : fp, x12 : fp }
|
||||
|
||||
function pairing_check(xs : list(g1), ys : list(g2)) =
|
||||
switch((xs, ys))
|
||||
([], []) => true
|
||||
(x :: xs, y :: ys) => pairing_check_(pairing(x, y), xs, ys)
|
||||
|
||||
function pairing_check_(acc : gt, xs : list(g1), ys : list(g2)) =
|
||||
switch((xs, ys))
|
||||
([], []) => gt_is_one(acc)
|
||||
(x :: xs, y :: ys) =>
|
||||
pairing_check_(gt_mul(acc, pairing(x, y)), xs, ys)
|
||||
|
||||
function int_to_fr(x : int) = MCL_BLS12_381.int_to_fr(x)
|
||||
function int_to_fp(x : int) = MCL_BLS12_381.int_to_fp(x)
|
||||
function fr_to_int(x : fr) = MCL_BLS12_381.fr_to_int(x)
|
||||
function fp_to_int(x : fp) = MCL_BLS12_381.fp_to_int(x)
|
||||
|
||||
function mk_g1(x : int, y : int, z : int) : g1 =
|
||||
{ x = int_to_fp(x), y = int_to_fp(y), z = int_to_fp(z) }
|
||||
|
||||
function mk_g2(x1 : int, x2 : int, y1 : int, y2 : int, z1 : int, z2 : int) : g2 =
|
||||
{ x = {x1 = int_to_fp(x1), x2 = int_to_fp(x2)},
|
||||
y = {x1 = int_to_fp(y1), x2 = int_to_fp(y2)},
|
||||
z = {x1 = int_to_fp(z1), x2 = int_to_fp(z2)} }
|
||||
|
||||
function pack_g1(t) = switch(t)
|
||||
(x, y, z) => {x = x, y = y, z = z} : g1
|
||||
function pack_g2(t) = switch(t)
|
||||
((x1, x2), (y1, y2), (z1, z2)) =>
|
||||
{x = {x1 = x1, x2 = x2}, y = {x1 = y1, x2 = y2}, z = {x1 = z1, x2 = z2}} : g2
|
||||
function pack_gt(t) = switch(t)
|
||||
(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12) =>
|
||||
{x1 = x1, x2 = x2, x3 = x3, x4 = x4, x5 = x5, x6 = x6,
|
||||
x7 = x7, x8 = x8, x9 = x9, x10 = x10, x11 = x11, x12 = x12} : gt
|
||||
|
||||
function g1_neg(p : g1) = pack_g1(MCL_BLS12_381.g1_neg((p.x, p.y, p.z)))
|
||||
function g1_norm(p : g1) = pack_g1(MCL_BLS12_381.g1_norm((p.x, p.y, p.z)))
|
||||
function g1_valid(p : g1) = MCL_BLS12_381.g1_valid((p.x, p.y, p.z))
|
||||
function g1_is_zero(p : g1) = MCL_BLS12_381.g1_is_zero((p.x, p.y, p.z))
|
||||
function g1_add(p : g1, q : g1) = pack_g1(MCL_BLS12_381.g1_add((p.x, p.y, p.z), (q.x, q.y, q.z)))
|
||||
function g1_mul(k : fr, p : g1) = pack_g1(MCL_BLS12_381.g1_mul(k, (p.x, p.y, p.z)))
|
||||
|
||||
function g2_neg(p : g2) = pack_g2(MCL_BLS12_381.g2_neg(((p.x.x1, p.x.x2), (p.y.x1, p.y.x2), (p.z.x1, p.z.x2))))
|
||||
function g2_norm(p : g2) = pack_g2(MCL_BLS12_381.g2_norm(((p.x.x1, p.x.x2), (p.y.x1, p.y.x2), (p.z.x1, p.z.x2))))
|
||||
function g2_valid(p : g2) = MCL_BLS12_381.g2_valid(((p.x.x1, p.x.x2), (p.y.x1, p.y.x2), (p.z.x1, p.z.x2)))
|
||||
function g2_is_zero(p : g2) = MCL_BLS12_381.g2_is_zero(((p.x.x1, p.x.x2), (p.y.x1, p.y.x2), (p.z.x1, p.z.x2)))
|
||||
function g2_add(p : g2, q : g2) = pack_g2(MCL_BLS12_381.g2_add(((p.x.x1, p.x.x2), (p.y.x1, p.y.x2), (p.z.x1, p.z.x2)),
|
||||
((q.x.x1, q.x.x2), (q.y.x1, q.y.x2), (q.z.x1, q.z.x2))))
|
||||
function g2_mul(k : fr, p : g2) = pack_g2(MCL_BLS12_381.g2_mul(k, ((p.x.x1, p.x.x2), (p.y.x1, p.y.x2), (p.z.x1, p.z.x2))))
|
||||
|
||||
function gt_inv(p : gt) = pack_gt(MCL_BLS12_381.gt_inv((p.x1, p.x2, p.x3, p.x4, p.x5, p.x6, p.x7, p.x8, p.x9, p.x10, p.x11, p.x12)))
|
||||
function gt_add(p : gt, q : gt) = pack_gt(MCL_BLS12_381.gt_add((p.x1, p.x2, p.x3, p.x4, p.x5, p.x6, p.x7, p.x8, p.x9, p.x10, p.x11, p.x12),
|
||||
(q.x1, q.x2, q.x3, q.x4, q.x5, q.x6, q.x7, q.x8, q.x9, q.x10, q.x11, q.x12)))
|
||||
function gt_mul(p : gt, q : gt) = pack_gt(MCL_BLS12_381.gt_mul((p.x1, p.x2, p.x3, p.x4, p.x5, p.x6, p.x7, p.x8, p.x9, p.x10, p.x11, p.x12),
|
||||
(q.x1, q.x2, q.x3, q.x4, q.x5, q.x6, q.x7, q.x8, q.x9, q.x10, q.x11, q.x12)))
|
||||
function gt_pow(p : gt, k : fr) = pack_gt(MCL_BLS12_381.gt_pow((p.x1, p.x2, p.x3, p.x4, p.x5, p.x6, p.x7, p.x8, p.x9, p.x10, p.x11, p.x12), k))
|
||||
function gt_is_one(p : gt) = MCL_BLS12_381.gt_is_one((p.x1, p.x2, p.x3, p.x4, p.x5, p.x6, p.x7, p.x8, p.x9, p.x10, p.x11, p.x12))
|
||||
function pairing(p : g1, q : g2) = pack_gt(MCL_BLS12_381.pairing((p.x, p.y, p.z), ((q.x.x1, q.x.x2), (q.y.x1, q.y.x2), (q.z.x1, q.z.x2))))
|
||||
function miller_loop(p : g1, q : g2) = pack_gt(MCL_BLS12_381.miller_loop((p.x, p.y, p.z), ((q.x.x1, q.x.x2), (q.y.x1, q.y.x2), (q.z.x1, q.z.x2))))
|
||||
function final_exp(p : gt) = pack_gt(MCL_BLS12_381.final_exp((p.x1, p.x2, p.x3, p.x4, p.x5, p.x6, p.x7, p.x8, p.x9, p.x10, p.x11, p.x12)))
|
||||
@@ -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)
|
||||
+41
-10
@@ -12,35 +12,66 @@ namespace Func =
|
||||
|
||||
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 =
|
||||
(x) => f(recur(f), x)
|
||||
|
||||
/** n-times composition with itself
|
||||
*/
|
||||
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) =
|
||||
/** 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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
(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)
|
||||
(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))
|
||||
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))
|
||||
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))
|
||||
|
||||
+128
-86
@@ -15,22 +15,42 @@ namespace List =
|
||||
_::t => Some(t)
|
||||
|
||||
function last(l : list('a)) : option('a) = switch(l)
|
||||
[] => None
|
||||
[x] => Some(x)
|
||||
[] => None
|
||||
[x] => Some(x)
|
||||
_::t => last(t)
|
||||
|
||||
function drop_last(l : list('a)) : option(list('a)) = switch(l)
|
||||
[] => None
|
||||
_ => Some(drop_last_unsafe(l))
|
||||
|
||||
function drop_last_unsafe(l : list('a)) : list('a) = switch(l)
|
||||
[_] => []
|
||||
h::t => h::drop_last_unsafe(t)
|
||||
[] => abort("drop_last_unsafe: list empty")
|
||||
|
||||
|
||||
function contains(e : 'a, l : list('a)) = switch(l)
|
||||
[] => false
|
||||
h::t => h == e || contains(e, t)
|
||||
|
||||
/** 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)
|
||||
[] => 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, [])
|
||||
/** 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)
|
||||
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)
|
||||
[] => []
|
||||
h::t =>
|
||||
let rest = find_indices_(p, t, n+1)
|
||||
if(p(h)) n::rest else rest
|
||||
|
||||
function nth(n : int, l : list('a)) : option('a) =
|
||||
switch(l)
|
||||
@@ -50,41 +70,53 @@ namespace List =
|
||||
_::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_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)
|
||||
/** 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) =
|
||||
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) =
|
||||
if(b < a) 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) =
|
||||
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) =
|
||||
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) =
|
||||
switch(l)
|
||||
[] => abort("replace_at overflow")
|
||||
h::t => if (n == 0) reverse(e::acc) ++ t
|
||||
else replace_at_(n-1, e, t, h::acc)
|
||||
h::t => if (n == 0) 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) =
|
||||
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
|
||||
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) =
|
||||
if (n == 0) e::l
|
||||
else switch(l)
|
||||
[] => abort("insert_at overflow")
|
||||
h::t => insert_at_(n-1, e, t, h::acc)
|
||||
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) =
|
||||
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)
|
||||
[] => [x]
|
||||
h::t =>
|
||||
if(cmp(x, h)) // x < h
|
||||
reverse(acc) ++ (x::l)
|
||||
x::l
|
||||
else
|
||||
insert_by_(cmp, x, t, h::acc)
|
||||
h::insert_by(cmp, x, t)
|
||||
|
||||
|
||||
function foldr(cons : ('a, 'b) => 'b, nil : 'b, l : list('a)) : 'b = switch(l)
|
||||
@@ -102,61 +134,71 @@ namespace List =
|
||||
f(e)
|
||||
foreach(l', f)
|
||||
|
||||
function reverse(l : list('a)) : list('a) = foldl((lst, el) => el :: lst, [], l)
|
||||
function reverse(l : list('a)) : list('a) = reverse_(l, [])
|
||||
private function reverse_(l : list('a), acc : list('a)) : list('a) = switch(l)
|
||||
[] => acc
|
||||
h::t => reverse_(t, h::acc)
|
||||
|
||||
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 map(f : 'a => 'b, l : list('a)) : list('b) = switch(l)
|
||||
[] => []
|
||||
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) =
|
||||
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)
|
||||
function filter(p : 'a => bool, l : list('a)) : list('a) = switch(l)
|
||||
[] => []
|
||||
h::t =>
|
||||
let rest = filter(p, t)
|
||||
if(p(h)) h::rest else rest
|
||||
|
||||
/* Take `n` first elements */
|
||||
/** Take up to `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)
|
||||
if(n < 0) abort("Take negative number of elements") else take_(n, l)
|
||||
private function take_(n : int, l : list('a)) : list('a) =
|
||||
if(n == 0) []
|
||||
else switch(l)
|
||||
[] => reverse(acc)
|
||||
h::t => take_(n-1, t, h::acc)
|
||||
[] => []
|
||||
h::t => h::take_(n-1, t)
|
||||
|
||||
/* Drop `n` first elements */
|
||||
/** Drop up to `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
|
||||
if(n < 0) abort("Drop negative number of elements") else drop_(n, l)
|
||||
private function drop_(n : int, l : list('a)) : list('a) =
|
||||
if (n == 0) l
|
||||
else switch(l)
|
||||
[] => []
|
||||
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` */
|
||||
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)
|
||||
/** 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)
|
||||
[] => []
|
||||
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)
|
||||
[] => []
|
||||
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)
|
||||
/** 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)
|
||||
[] => ([], [])
|
||||
h::t =>
|
||||
let (l, r) = partition(p, t)
|
||||
if(p(h)) (h::l, r) else (l, h::r)
|
||||
|
||||
|
||||
function flatten(ll : list(list('a))) : list('a) = foldr((l1, l2) => l1 ++ l2, [], ll)
|
||||
function flatten(l : list(list('a))) : list('a) = switch(l)
|
||||
[] => []
|
||||
h::t => h ++ flatten(t)
|
||||
|
||||
function all(p : 'a => bool, l : list('a)) : bool = switch(l)
|
||||
[] => true
|
||||
@@ -171,26 +213,26 @@ namespace List =
|
||||
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
|
||||
/** 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
|
||||
, 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)
|
||||
(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 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)
|
||||
function unzip(l : list('a * 'b)) : (list('a) * list('b)) = switch(l)
|
||||
[] => ([], [])
|
||||
(h1, h2)::t =>
|
||||
let (t1, t2) = unzip(t)
|
||||
(h1::t1, h2::t2)
|
||||
|
||||
|
||||
// TODO: Improve?
|
||||
@@ -199,16 +241,16 @@ namespace List =
|
||||
h::t => switch (partition((x) => lesser_cmp(x, h), t))
|
||||
(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)
|
||||
[] => []
|
||||
[e] => [e]
|
||||
h::t => h::delim::intersperse(delim, t)
|
||||
|
||||
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)
|
||||
|
||||
/** Effectively a zip with an infinite sequence of natural numbers
|
||||
*/
|
||||
function enumerate(l : list('a)) : list(int * 'a) = enumerate_(l, 0)
|
||||
private function enumerate_(l : list('a), n : int) : list(int * 'a) = switch(l)
|
||||
[] => []
|
||||
h::t => (n, h)::enumerate_(t, n + 1)
|
||||
+35
-13
@@ -10,14 +10,23 @@ namespace Option =
|
||||
None => false
|
||||
Some(_) => true
|
||||
|
||||
|
||||
/** Catamorphism on `option`. Also known as inlined pattern matching.
|
||||
*/
|
||||
function match(n : 'b, s : 'a => 'b, o : option('a)) : 'b = switch(o)
|
||||
None => n
|
||||
Some(x) => s(x)
|
||||
|
||||
/** Escape option providing default if `None`
|
||||
*/
|
||||
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)
|
||||
/** Assume it is `Some`
|
||||
*/
|
||||
function force(o : option('a)) : 'a = switch(o)
|
||||
None => abort("Forced None value")
|
||||
Some(x) => x
|
||||
|
||||
function contains(e : 'a, o : option('a)) = o == Some(e)
|
||||
|
||||
function on_elem(o : option('a), f : 'a => unit) : unit = match((), f, o)
|
||||
|
||||
@@ -40,10 +49,14 @@ namespace Option =
|
||||
(Some(x1), Some(x2), Some(x3)) => Some(f(x1, x2, x3))
|
||||
_ => None
|
||||
|
||||
/** Like `map`, but the function is in `option`
|
||||
*/
|
||||
function app_over(f : option ('a => 'b), o : option('a)) : option('b) = switch((f, o))
|
||||
(Some(ff), Some(xx)) => Some(ff(xx))
|
||||
_ => None
|
||||
|
||||
/** Monadic bind
|
||||
*/
|
||||
function flat_map(f : 'a => option('b), o : option('a)) : option('b) = switch(o)
|
||||
None => None
|
||||
Some(x) => f(x)
|
||||
@@ -53,24 +66,33 @@ namespace Option =
|
||||
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)
|
||||
/** 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)
|
||||
[] => []
|
||||
None::t => filter_options(t)
|
||||
Some(x)::t => x::filter_options(t)
|
||||
|
||||
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)
|
||||
/** 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)
|
||||
[] => Some([])
|
||||
None::_ => None
|
||||
Some(x)::t => switch(seq_options(t))
|
||||
None => None
|
||||
Some(st) => Some(x::st)
|
||||
|
||||
|
||||
/** Choose `Some` out of two if possible
|
||||
*/
|
||||
function choose(o1 : option('a), o2 : option('a)) : option('a) =
|
||||
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)
|
||||
[] => None
|
||||
None::t => choose_first(t)
|
||||
Some(x)::_ => Some(x)
|
||||
|
||||
|
||||
@@ -6,12 +6,18 @@ namespace Pair =
|
||||
function snd(t : ('a * 'b)) : 'b = switch(t)
|
||||
(_, y) => y
|
||||
|
||||
/** Map over first
|
||||
*/
|
||||
function map1(f : 'a => 'c, t : ('a * 'b)) : ('c * 'b) = switch(t)
|
||||
(x, y) => (f(x), y)
|
||||
|
||||
/** Map over second
|
||||
*/
|
||||
function map2(f : 'b => 'c, t : ('a * 'b)) : ('a * 'c) = switch(t)
|
||||
(x, y) => (x, f(y))
|
||||
|
||||
/** Map over both
|
||||
*/
|
||||
function bimap(f : 'a => 'c, g : 'b => 'd, t : ('a * 'b)) : ('c * 'd) = switch(t)
|
||||
(x, y) => (f(x), g(y))
|
||||
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
include "List.aes"
|
||||
namespace String =
|
||||
// Computes the SHA3/Keccak hash of the string
|
||||
function sha3(s : string) : hash = StringInternal.sha3(s)
|
||||
// Computes the SHA256 hash of the string.
|
||||
function sha256(s : string) : hash = StringInternal.sha256(s)
|
||||
// Computes the Blake2B hash of the string.
|
||||
function blake2b(s : string) : hash = StringInternal.blake2b(s)
|
||||
|
||||
// The length of a string - equivalent to List.lenght(to_list(s))
|
||||
function length(s : string) : int = StringInternal.length(s)
|
||||
// Concatenates `s1` and `s2`.
|
||||
function concat(s1 : string, s2 : string) : string = StringInternal.concat(s1, s2)
|
||||
// Concatenates a list of strings.
|
||||
function
|
||||
concats : (list(string)) => string
|
||||
concats([]) = ""
|
||||
concats(s :: ss) = List.foldl(StringInternal.concat, s, ss)
|
||||
|
||||
// Converts a `string` to a list of `char` - the code points are normalized, but
|
||||
// composite characters are possibly converted to multiple `char`s.
|
||||
function from_list(cs : list(char)) : string = StringInternal.from_list(cs)
|
||||
// Converts a list of characters into a normalized UTF-8 string.
|
||||
function to_list(s : string) : list(char) = StringInternal.to_list(s)
|
||||
|
||||
// Converts a string to lowercase.
|
||||
function to_lower(s : string) = StringInternal.to_lower(s)
|
||||
// Converts a string to uppercase.
|
||||
function to_upper(s : string) = StringInternal.to_upper(s)
|
||||
|
||||
// Splits a string at (zero-based) index `ix`.
|
||||
function split(i : int, s : string) : string * string =
|
||||
let cs = StringInternal.to_list(s)
|
||||
(StringInternal.from_list(List.take(i, cs)), StringInternal.from_list(List.drop(i, cs)))
|
||||
|
||||
// Returns the character/codepoint at (zero-based) index `ix`.
|
||||
function at(ix : int, s : string) =
|
||||
switch(List.drop(ix, StringInternal.to_list(s)))
|
||||
[] => None
|
||||
x :: _ => Some(x)
|
||||
|
||||
// Searches for `pat` in `str`, returning `Some(ix)` if `pat` is a substring
|
||||
// of `str` starting at position `ix`, otherwise returns `None`.
|
||||
function contains(str : string, substr : string) : option(int) =
|
||||
if(substr == "") Some(0)
|
||||
else
|
||||
contains_(0, StringInternal.to_list(str), StringInternal.to_list(substr))
|
||||
|
||||
// Splits `s` into tokens, `pat` is the divider of tokens.
|
||||
function tokens(s : string, pat : string) =
|
||||
require(pat != "", "String.tokens: empty pattern")
|
||||
tokens_(StringInternal.to_list(pat), StringInternal.to_list(s), [])
|
||||
|
||||
// Converts a decimal ("123", "-253") or a hexadecimal ("0xa2f", "-0xBBB") string
|
||||
// into an integer. If the string doesn't contain a valid number `None` is returned.
|
||||
function to_int(s : string) : option(int) =
|
||||
let s = StringInternal.to_list(s)
|
||||
switch(is_prefix(['-'], s))
|
||||
None => to_int_pos(s)
|
||||
Some(s) => switch(to_int_pos(s))
|
||||
None => None
|
||||
Some(x) => Some(-x)
|
||||
|
||||
// Private helper functions below
|
||||
private function to_int_pos(s : list(char)) =
|
||||
switch(is_prefix(['0', 'x'], s))
|
||||
None =>
|
||||
to_int_(s, ch_to_int_10, 0, 10)
|
||||
Some(s) =>
|
||||
to_int_(s, ch_to_int_16, 0, 16)
|
||||
|
||||
private function
|
||||
tokens_(_, [], acc) = [StringInternal.from_list(List.reverse(acc))]
|
||||
tokens_(pat, str, acc) =
|
||||
switch(is_prefix(pat, str))
|
||||
Some(str') =>
|
||||
StringInternal.from_list(List.reverse(acc)) :: tokens_(pat, str', [])
|
||||
None =>
|
||||
let c :: cs = str
|
||||
tokens_(pat, cs, c :: acc)
|
||||
|
||||
private function
|
||||
contains_(_, [], _) = None
|
||||
contains_(ix, str, substr) =
|
||||
switch(is_prefix(substr, str))
|
||||
None =>
|
||||
let _ :: str = str
|
||||
contains_(ix + 1, str, substr)
|
||||
Some(_) =>
|
||||
Some(ix)
|
||||
|
||||
private function
|
||||
is_prefix([], ys) = Some(ys)
|
||||
is_prefix(_, []) = None
|
||||
is_prefix(x :: xs, y :: ys) =
|
||||
if(x == y) is_prefix(xs, ys)
|
||||
else None
|
||||
|
||||
private function
|
||||
to_int_([], _, x, _) = Some(x)
|
||||
to_int_(i :: is, value, x, b) =
|
||||
switch(value(i))
|
||||
None => None
|
||||
Some(i) => to_int_(is, value, x * b + i, b)
|
||||
|
||||
private function ch_to_int_10(c) =
|
||||
let c = Char.to_int(c)
|
||||
if(c >= 48 && c =< 57) Some(c - 48)
|
||||
else None
|
||||
|
||||
private function ch_to_int_16(c) =
|
||||
let c = Char.to_int(c)
|
||||
if(c >= 48 && c =< 57) Some(c - 48)
|
||||
elif(c >= 65 && c =< 70) Some(c - 55)
|
||||
elif(c >= 97 && c =< 102) Some(c - 87)
|
||||
else None
|
||||
|
||||
@@ -10,15 +10,23 @@ namespace Triple =
|
||||
(_, _, z) => z
|
||||
|
||||
|
||||
/** Map over first
|
||||
*/
|
||||
function map1(f : 'a => 'm, t : ('a * 'b * 'c)) : ('m * 'b * 'c) = switch(t)
|
||||
(x, y, z) => (f(x), y, z)
|
||||
|
||||
/** Map over second
|
||||
*/
|
||||
function map2(f : 'b => 'm, t : ('a * 'b * 'c)) : ('a * 'm * 'c) = switch(t)
|
||||
(x, y, z) => (x, f(y), z)
|
||||
|
||||
/** Map over third
|
||||
*/
|
||||
function map3(f : 'c => 'm, t : ('a * 'b * 'c)) : ('a * 'b * 'm) = switch(t)
|
||||
(x, y, z) => (x, y, f(z))
|
||||
|
||||
/** Map over all elements
|
||||
*/
|
||||
function trimap( f : 'a => 'x
|
||||
, g : 'b => 'y
|
||||
, h : 'c => 'z
|
||||
@@ -29,9 +37,13 @@ namespace Triple =
|
||||
function swap(t : ('a * 'b * 'c)) : ('c * 'b * 'a) = switch(t)
|
||||
(x, y, z) => (z, y, x)
|
||||
|
||||
/** Right rotation
|
||||
*/
|
||||
function rotr(t : ('a * 'b * 'c)) : ('c * 'a * 'b) = switch(t)
|
||||
(x, y, z) => (z, x, y)
|
||||
|
||||
/** Left rotation
|
||||
*/
|
||||
function rotl(t : ('a * 'b * 'c)) : ('b * 'c * 'a) = switch(t)
|
||||
(x, y, z) => (y, z, x)
|
||||
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
|
||||
{erl_opts, [debug_info]}.
|
||||
|
||||
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"4f4d6d3"}}}
|
||||
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"7f0d309"}}}
|
||||
, {getopt, "1.0.1"}
|
||||
, {eblake2, "1.0.0"}
|
||||
, {jsx, {git, "https://github.com/talentdeficit/jsx.git",
|
||||
@@ -15,7 +15,7 @@
|
||||
{base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]}
|
||||
]}.
|
||||
|
||||
{relx, [{release, {aesophia, "4.0.0"},
|
||||
{relx, [{release, {aesophia, "4.3.0"},
|
||||
[aesophia, aebytecode, getopt]},
|
||||
|
||||
{dev_mode, true},
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{"1.1.0",
|
||||
[{<<"aebytecode">>,
|
||||
{git,"https://github.com/aeternity/aebytecode.git",
|
||||
{ref,"4f4d6d30cd2c46b3830454d650a424d513f69134"}},
|
||||
{ref,"7f0d3090d4dc6c4d5fca7645b0c21eb0e65ad208"}},
|
||||
0},
|
||||
{<<"aeserialization">>,
|
||||
{git,"https://github.com/aeternity/aeserialization.git",
|
||||
|
||||
+16
-8
@@ -14,6 +14,8 @@
|
||||
, contract_interface/2
|
||||
, contract_interface/3
|
||||
|
||||
, from_typed_ast/2
|
||||
|
||||
, render_aci_json/1
|
||||
|
||||
, json_encode_expr/1
|
||||
@@ -23,6 +25,8 @@
|
||||
-type json() :: jsx:json_term().
|
||||
-type json_text() :: binary().
|
||||
|
||||
-export_type([aci_type/0]).
|
||||
|
||||
%% External API
|
||||
-spec file(aci_type(), string()) -> {ok, json() | string()} | {error, term()}.
|
||||
file(Type, File) ->
|
||||
@@ -65,18 +69,20 @@ do_contract_interface(Type, ContractString, Options) ->
|
||||
try
|
||||
Ast = aeso_compiler:parse(ContractString, Options),
|
||||
%% io:format("~p\n", [Ast]),
|
||||
TypedAst = aeso_ast_infer_types:infer(Ast, [dont_unfold]),
|
||||
{TypedAst, _} = aeso_ast_infer_types:infer(Ast, [dont_unfold | Options]),
|
||||
%% io:format("~p\n", [TypedAst]),
|
||||
JArray = [ encode_contract(C) || C <- TypedAst ],
|
||||
|
||||
case Type of
|
||||
json -> {ok, JArray};
|
||||
string -> do_render_aci_json(JArray)
|
||||
end
|
||||
from_typed_ast(Type, TypedAst)
|
||||
catch
|
||||
throw:{error, Errors} -> {error, Errors}
|
||||
end.
|
||||
|
||||
from_typed_ast(Type, TypedAst) ->
|
||||
JArray = [ encode_contract(C) || C <- TypedAst ],
|
||||
case Type of
|
||||
json -> {ok, JArray};
|
||||
string -> do_render_aci_json(JArray)
|
||||
end.
|
||||
|
||||
encode_contract(Contract = {contract, _, {con, _, Name}, _}) ->
|
||||
C0 = #{name => encode_name(Name)},
|
||||
|
||||
@@ -129,7 +135,7 @@ encode_anon_args(Types) ->
|
||||
|
||||
encode_args(Args) -> [ encode_arg(A) || A <- Args ].
|
||||
|
||||
encode_arg({arg, _, Id, T}) ->
|
||||
encode_arg({typed, _, Id, T}) ->
|
||||
#{name => encode_type(Id),
|
||||
type => encode_type(T)}.
|
||||
|
||||
@@ -194,6 +200,8 @@ encode_expr({bytes, _, B}) ->
|
||||
encode_expr({Lit, _, L}) when Lit == oracle_pubkey; Lit == oracle_query_id;
|
||||
Lit == contract_pubkey; Lit == account_pubkey ->
|
||||
aeser_api_encoder:encode(Lit, L);
|
||||
encode_expr({app, _, {'-', _}, [{int, _, N}]}) ->
|
||||
encode_expr({int, [], -N});
|
||||
encode_expr({app, _, F, As}) ->
|
||||
Ef = encode_expr(F),
|
||||
Eas = encode_exprs(As),
|
||||
|
||||
+443
-114
@@ -12,7 +12,11 @@
|
||||
|
||||
-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()}
|
||||
| {app_t, aeso_syntax:ann(), utype(), [utype()]}
|
||||
@@ -20,6 +24,7 @@
|
||||
| aeso_syntax:id() | aeso_syntax:qid()
|
||||
| aeso_syntax:con() | aeso_syntax:qcon() %% contracts
|
||||
| aeso_syntax:tvar()
|
||||
| {if_t, aeso_syntax:ann(), aeso_syntax:id(), utype(), utype()} %% Can branch on named argument (protected)
|
||||
| uvar().
|
||||
|
||||
-type uvar() :: {uvar, aeso_syntax:ann(), reference()}.
|
||||
@@ -43,7 +48,14 @@
|
||||
name :: aeso_syntax:id(),
|
||||
type :: utype()}).
|
||||
|
||||
-type named_argument_constraint() :: #named_argument_constraint{}.
|
||||
-record(dependent_type_constraint,
|
||||
{ named_args_t :: named_args_t()
|
||||
, named_args :: [aeso_syntax:arg_expr()]
|
||||
, general_type :: utype()
|
||||
, specialized_type :: utype()
|
||||
, context :: term() }).
|
||||
|
||||
-type named_argument_constraint() :: #named_argument_constraint{} | #dependent_type_constraint{}.
|
||||
|
||||
-record(field_constraint,
|
||||
{ record_t :: utype()
|
||||
@@ -232,16 +244,21 @@ bind_fields([], Env) -> Env;
|
||||
bind_fields([{Id, Info} | Rest], Env) ->
|
||||
bind_fields(Rest, bind_field(Id, Info, Env)).
|
||||
|
||||
%% Contract entrypoints take two named arguments (gas : int = Call.gas_left(), value : int = 0).
|
||||
%% Contract entrypoints take three named arguments
|
||||
%% gas : int = Call.gas_left()
|
||||
%% value : int = 0
|
||||
%% protected : bool = false
|
||||
contract_call_type({fun_t, Ann, [], Args, Ret}) ->
|
||||
Id = fun(X) -> {id, Ann, X} end,
|
||||
Int = Id("int"),
|
||||
Typed = fun(E, T) -> {typed, Ann, E, T} end,
|
||||
Named = fun(Name, Default) -> {named_arg_t, Ann, Id(Name), Int, Default} end,
|
||||
Named = fun(Name, Default = {typed, _, _, T}) -> {named_arg_t, Ann, Id(Name), T, Default} end,
|
||||
{fun_t, Ann, [Named("gas", Typed({app, Ann, Typed({qid, Ann, ["Call", "gas_left"]},
|
||||
{fun_t, Ann, [], [], Int}),
|
||||
[]}, Int)),
|
||||
Named("value", Typed({int, Ann, 0}, Int))], Args, Ret}.
|
||||
Named("value", Typed({int, Ann, 0}, Int)),
|
||||
Named("protected", Typed({bool, Ann, false}, Id("bool")))],
|
||||
Args, {if_t, Ann, Id("protected"), {app_t, Ann, {id, Ann, "option"}, [Ret]}, Ret}}.
|
||||
|
||||
-spec bind_contract(aeso_syntax:decl(), env()) -> env().
|
||||
bind_contract({contract, Ann, Id, Contents}, Env) ->
|
||||
@@ -364,6 +381,7 @@ is_private(Ann) -> proplists:get_value(private, Ann, false).
|
||||
global_env() ->
|
||||
Ann = [{origin, system}],
|
||||
Int = {id, Ann, "int"},
|
||||
Char = {id, Ann, "char"},
|
||||
Bool = {id, Ann, "bool"},
|
||||
String = {id, Ann, "string"},
|
||||
Address = {id, Ann, "address"},
|
||||
@@ -389,6 +407,24 @@ global_env() ->
|
||||
Signature = {named_arg_t, Ann, SignId, SignId, {typed, Ann, SignDef, SignId}},
|
||||
SignFun = fun(Ts, T) -> {type_sig, [stateful|Ann], none, [Signature], Ts, T} end,
|
||||
TTL = {qid, Ann, ["Chain", "ttl"]},
|
||||
Pointee = {qid, Ann, ["AENS", "pointee"]},
|
||||
AENSName = {qid, Ann, ["AENS", "name"]},
|
||||
Fr = {qid, Ann, ["MCL_BLS12_381", "fr"]},
|
||||
Fp = {qid, Ann, ["MCL_BLS12_381", "fp"]},
|
||||
Fp2 = {tuple_t, Ann, [Fp, Fp]},
|
||||
G1 = {tuple_t, Ann, [Fp, Fp, Fp]},
|
||||
G2 = {tuple_t, Ann, [Fp2, Fp2, Fp2]},
|
||||
GT = {tuple_t, Ann, lists:duplicate(12, Fp)},
|
||||
Tx = {qid, Ann, ["Chain", "tx"]},
|
||||
GAMetaTx = {qid, Ann, ["Chain", "ga_meta_tx"]},
|
||||
BaseTx = {qid, Ann, ["Chain", "base_tx"]},
|
||||
PayForTx = {qid, Ann, ["Chain", "paying_for_tx"]},
|
||||
|
||||
FldT = fun(Id, T) -> {field_t, Ann, {id, Ann, Id}, T} end,
|
||||
TxFlds = [{"paying_for", Option(PayForTx)}, {"ga_metas", List(GAMetaTx)},
|
||||
{"actor", Address}, {"fee", Int}, {"ttl", Int}, {"tx", BaseTx}],
|
||||
TxType = {record_t, [FldT(N, T) || {N, T} <- TxFlds ]},
|
||||
|
||||
Fee = Int,
|
||||
[A, Q, R, K, V] = lists:map(TVar, ["a", "q", "r", "k", "v"]),
|
||||
|
||||
@@ -426,8 +462,36 @@ global_env() ->
|
||||
{"timestamp", Int},
|
||||
{"block_height", Int},
|
||||
{"difficulty", Int},
|
||||
{"gas_limit", Int}])
|
||||
, types = MkDefs([{"ttl", 0}]) },
|
||||
{"gas_limit", Int},
|
||||
%% Tx constructors
|
||||
{"GAMetaTx", Fun([Address, Int], GAMetaTx)},
|
||||
{"PayingForTx", Fun([Address, Int], PayForTx)},
|
||||
{"SpendTx", Fun([Address, Int, String], BaseTx)},
|
||||
{"OracleRegisterTx", BaseTx},
|
||||
{"OracleQueryTx", BaseTx},
|
||||
{"OracleResponseTx", BaseTx},
|
||||
{"OracleExtendTx", BaseTx},
|
||||
{"NamePreclaimTx", BaseTx},
|
||||
{"NameClaimTx", Fun([String], BaseTx)},
|
||||
{"NameUpdateTx", Fun([Hash], BaseTx)},
|
||||
{"NameRevokeTx", Fun([Hash], BaseTx)},
|
||||
{"NameTransferTx", Fun([Address, Hash], BaseTx)},
|
||||
{"ChannelCreateTx", Fun([Address], BaseTx)},
|
||||
{"ChannelDepositTx", Fun([Address, Int], BaseTx)},
|
||||
{"ChannelWithdrawTx", Fun([Address, Int], BaseTx)},
|
||||
{"ChannelForceProgressTx", Fun([Address], BaseTx)},
|
||||
{"ChannelCloseMutualTx", Fun([Address], BaseTx)},
|
||||
{"ChannelCloseSoloTx", Fun([Address], BaseTx)},
|
||||
{"ChannelSlashTx", Fun([Address], BaseTx)},
|
||||
{"ChannelSettleTx", Fun([Address], BaseTx)},
|
||||
{"ChannelSnapshotSoloTx", Fun([Address], BaseTx)},
|
||||
{"ContractCreateTx", Fun([Int], BaseTx)},
|
||||
{"ContractCallTx", Fun([Address, Int], BaseTx)},
|
||||
{"GAAttachTx", BaseTx}
|
||||
])
|
||||
, types = MkDefs([{"ttl", 0}, {"tx", {[], TxType}},
|
||||
{"base_tx", 0},
|
||||
{"paying_for_tx", 0}, {"ga_meta_tx", 0}]) },
|
||||
|
||||
ContractScope = #scope
|
||||
{ funs = MkDefs(
|
||||
@@ -447,6 +511,7 @@ global_env() ->
|
||||
OracleScope = #scope
|
||||
{ funs = MkDefs(
|
||||
[{"register", SignFun([Address, Fee, TTL], Oracle(Q, R))},
|
||||
{"expiry", Fun([Oracle(Q, R)], Fee)},
|
||||
{"query_fee", Fun([Oracle(Q, R)], Fee)},
|
||||
{"query", StateFun([Oracle(Q, R), Q, Fee, TTL, TTL], Query(Q, R))},
|
||||
{"get_question", Fun([Oracle(Q, R), Query(Q, R)], Q)},
|
||||
@@ -462,7 +527,18 @@ global_env() ->
|
||||
{"preclaim", SignFun([Address, Hash], Unit)},
|
||||
{"claim", SignFun([Address, String, Int, Int], Unit)},
|
||||
{"transfer", SignFun([Address, Address, String], Unit)},
|
||||
{"revoke", SignFun([Address, String], Unit)}]) },
|
||||
{"revoke", SignFun([Address, String], Unit)},
|
||||
{"update", SignFun([Address, String, Option(TTL), Option(Int), Option(Map(String, Pointee))], Unit)},
|
||||
{"lookup", Fun([String], option_t(Ann, AENSName))},
|
||||
%% AENS pointee constructors
|
||||
{"AccountPt", Fun1(Address, Pointee)},
|
||||
{"OraclePt", Fun1(Address, Pointee)},
|
||||
{"ContractPt", Fun1(Address, Pointee)},
|
||||
{"ChannelPt", Fun1(Address, Pointee)},
|
||||
%% Name object constructor
|
||||
{"Name", Fun([Address, TTL, Map(String, Pointee)], AENSName)}
|
||||
])
|
||||
, types = MkDefs([{"pointee", 0}, {"name", 0}]) },
|
||||
|
||||
MapScope = #scope
|
||||
{ funs = MkDefs(
|
||||
@@ -485,19 +561,65 @@ global_env() ->
|
||||
{"sha256", Fun1(A, Hash)},
|
||||
{"blake2b", Fun1(A, Hash)}]) },
|
||||
|
||||
%% Fancy BLS12-381 crypto operations
|
||||
MCL_BLS12_381_Scope = #scope
|
||||
{ funs = MkDefs(
|
||||
[{"g1_neg", Fun1(G1, G1)},
|
||||
{"g1_norm", Fun1(G1, G1)},
|
||||
{"g1_valid", Fun1(G1, Bool)},
|
||||
{"g1_is_zero", Fun1(G1, Bool)},
|
||||
{"g1_add", Fun ([G1, G1], G1)},
|
||||
{"g1_mul", Fun ([Fr, G1], G1)},
|
||||
|
||||
{"g2_neg", Fun1(G2, G2)},
|
||||
{"g2_norm", Fun1(G2, G2)},
|
||||
{"g2_valid", Fun1(G2, Bool)},
|
||||
{"g2_is_zero", Fun1(G2, Bool)},
|
||||
{"g2_add", Fun ([G2, G2], G2)},
|
||||
{"g2_mul", Fun ([Fr, G2], G2)},
|
||||
|
||||
{"gt_inv", Fun1(GT, GT)},
|
||||
{"gt_add", Fun ([GT, GT], GT)},
|
||||
{"gt_mul", Fun ([GT, GT], GT)},
|
||||
{"gt_pow", Fun ([GT, Fr], GT)},
|
||||
{"gt_is_one", Fun1(GT, Bool)},
|
||||
{"pairing", Fun ([G1, G2], GT)},
|
||||
{"miller_loop", Fun ([G1, G2], GT)},
|
||||
{"final_exp", Fun1(GT, GT)},
|
||||
|
||||
{"int_to_fr", Fun1(Int, Fr)},
|
||||
{"int_to_fp", Fun1(Int, Fp)},
|
||||
{"fr_to_int", Fun1(Fr, Int)},
|
||||
{"fp_to_int", Fun1(Fp, Int)}
|
||||
]),
|
||||
types = MkDefs(
|
||||
[{"fr", 0}, {"fp", 0}]) },
|
||||
|
||||
%% Authentication
|
||||
AuthScope = #scope
|
||||
{ funs = MkDefs(
|
||||
[{"tx_hash", Option(Hash)}]) },
|
||||
[{"tx_hash", Option(Hash)},
|
||||
{"tx", Option(Tx)} ]) },
|
||||
|
||||
%% Strings
|
||||
StringScope = #scope
|
||||
{ funs = MkDefs(
|
||||
[{"length", Fun1(String, Int)},
|
||||
{"concat", Fun([String, String], String)},
|
||||
{"sha3", Fun1(String, Hash)},
|
||||
{"sha256", Fun1(String, Hash)},
|
||||
{"blake2b", Fun1(String, Hash)}]) },
|
||||
[{"length", Fun1(String, Int)},
|
||||
{"concat", Fun([String, String], String)},
|
||||
{"to_list", Fun1(String, List(Char))},
|
||||
{"from_list", Fun1(List(Char), String)},
|
||||
{"to_upper", Fun1(String, String)},
|
||||
{"to_lower", Fun1(String, String)},
|
||||
{"sha3", Fun1(String, Hash)},
|
||||
{"sha256", Fun1(String, Hash)},
|
||||
{"blake2b", Fun1(String, Hash)}
|
||||
]) },
|
||||
|
||||
%% Chars
|
||||
CharScope = #scope
|
||||
{ funs = MkDefs(
|
||||
[{"to_int", Fun1(Char, Int)},
|
||||
{"from_int", Fun1(Int, Option(Char))}]) },
|
||||
|
||||
%% Bits
|
||||
BitsScope = #scope
|
||||
@@ -529,6 +651,7 @@ global_env() ->
|
||||
{"is_contract", Fun1(Address, Bool)},
|
||||
{"is_payable", Fun1(Address, Bool)}]) },
|
||||
|
||||
|
||||
#env{ scopes =
|
||||
#{ [] => TopScope
|
||||
, ["Chain"] => ChainScope
|
||||
@@ -539,27 +662,38 @@ global_env() ->
|
||||
, ["Map"] => MapScope
|
||||
, ["Auth"] => AuthScope
|
||||
, ["Crypto"] => CryptoScope
|
||||
, ["String"] => StringScope
|
||||
, ["MCL_BLS12_381"] => MCL_BLS12_381_Scope
|
||||
, ["StringInternal"] => StringScope
|
||||
, ["Char"] => CharScope
|
||||
, ["Bits"] => BitsScope
|
||||
, ["Bytes"] => BytesScope
|
||||
, ["Int"] => IntScope
|
||||
, ["Address"] => AddressScope
|
||||
} }.
|
||||
}
|
||||
, fields =
|
||||
maps:from_list([{N, [#field_info{ ann = [], field_t = T, record_t = Tx, kind = record }]}
|
||||
|| {N, T} <- TxFlds ])
|
||||
}.
|
||||
|
||||
|
||||
option_t(As, T) -> {app_t, As, {id, As, "option"}, [T]}.
|
||||
map_t(As, K, V) -> {app_t, As, {id, As, "map"}, [K, V]}.
|
||||
|
||||
-spec infer(aeso_syntax:ast()) -> aeso_syntax:ast() | {env(), aeso_syntax:ast()}.
|
||||
-spec infer(aeso_syntax:ast()) -> {aeso_syntax:ast(), aeso_syntax:ast()} | {env(), aeso_syntax:ast(), aeso_syntax:ast()}.
|
||||
infer(Contracts) ->
|
||||
infer(Contracts, []).
|
||||
|
||||
-type option() :: return_env | dont_unfold | no_code | term().
|
||||
-type option() :: return_env | dont_unfold | no_code | debug_mode | term().
|
||||
|
||||
-spec init_env(list(option())) -> env().
|
||||
init_env(_Options) -> global_env().
|
||||
|
||||
-spec infer(aeso_syntax:ast(), list(option())) ->
|
||||
aeso_syntax:ast() | {env(), aeso_syntax:ast()}.
|
||||
{aeso_syntax:ast(), aeso_syntax:ast()} | {env(), aeso_syntax:ast(), aeso_syntax:ast()}.
|
||||
infer([], Options) ->
|
||||
create_type_errors(),
|
||||
type_error({no_decls, proplists:get_value(src_file, Options, no_file)}),
|
||||
destroy_and_report_type_errors(init_env(Options));
|
||||
infer(Contracts, Options) ->
|
||||
ets_init(), %% Init the ETS table state
|
||||
try
|
||||
@@ -568,15 +702,15 @@ infer(Contracts, Options) ->
|
||||
ets_new(type_vars, [set]),
|
||||
check_modifiers(Env, Contracts),
|
||||
{Env1, Decls} = infer1(Env, Contracts, [], Options),
|
||||
{Env2, Decls2} =
|
||||
{Env2, DeclsFolded, DeclsUnfolded} =
|
||||
case proplists:get_value(dont_unfold, Options, false) of
|
||||
true -> {Env1, Decls};
|
||||
true -> {Env1, Decls, Decls};
|
||||
false -> E = on_scopes(Env1, fun(Scope) -> unfold_record_types(Env1, Scope) end),
|
||||
{E, unfold_record_types(E, Decls)}
|
||||
{E, Decls, unfold_record_types(E, Decls)}
|
||||
end,
|
||||
case proplists:get_value(return_env, Options, false) of
|
||||
false -> Decls2;
|
||||
true -> {Env2, Decls2}
|
||||
false -> {DeclsFolded, DeclsUnfolded};
|
||||
true -> {Env2, DeclsFolded, DeclsUnfolded}
|
||||
end
|
||||
after
|
||||
clean_up_ets()
|
||||
@@ -614,19 +748,27 @@ check_scope_name_clash(Env, Kind, Name) ->
|
||||
|
||||
-spec infer_contract_top(env(), main_contract | contract | namespace, [aeso_syntax:decl()], list(option())) ->
|
||||
{env(), [aeso_syntax:decl()]}.
|
||||
infer_contract_top(Env, Kind, Defs0, _Options) ->
|
||||
infer_contract_top(Env, Kind, Defs0, Options) ->
|
||||
Defs = desugar(Defs0),
|
||||
infer_contract(Env, Kind, Defs).
|
||||
infer_contract(Env, Kind, Defs, Options).
|
||||
|
||||
%% infer_contract takes a proplist mapping global names to types, and
|
||||
%% a list of definitions.
|
||||
-spec infer_contract(env(), main_contract | contract | namespace, [aeso_syntax:decl()]) -> {env(), [aeso_syntax:decl()]}.
|
||||
infer_contract(Env0, What, Defs) ->
|
||||
-spec infer_contract(env(), main_contract | contract | namespace, [aeso_syntax:decl()], list(option())) -> {env(), [aeso_syntax:decl()]}.
|
||||
infer_contract(Env0, What, Defs0, Options) ->
|
||||
create_type_errors(),
|
||||
Defs01 = process_blocks(Defs0),
|
||||
Defs = case lists:member(debug_mode, Options) of
|
||||
true -> expose_internals(Defs01, What);
|
||||
false -> Defs01
|
||||
end,
|
||||
destroy_and_report_type_errors(Env0),
|
||||
Env = Env0#env{ what = What },
|
||||
Kind = fun({type_def, _, _, _, _}) -> type;
|
||||
({letfun, _, _, _, _, _}) -> function;
|
||||
({fun_decl, _, _, _}) -> prototype;
|
||||
(_) -> unexpected
|
||||
Kind = fun({type_def, _, _, _, _}) -> type;
|
||||
({letfun, _, _, _, _, _}) -> function;
|
||||
({fun_clauses, _, _, _, _}) -> function;
|
||||
({fun_decl, _, _, _}) -> prototype;
|
||||
(_) -> unexpected
|
||||
end,
|
||||
Get = fun(K) -> [ Def || Def <- Defs, Kind(Def) == K ] end,
|
||||
{Env1, TypeDefs} = check_typedefs(Env, Get(type)),
|
||||
@@ -642,9 +784,11 @@ infer_contract(Env0, What, Defs) ->
|
||||
Env3 = bind_funs(ProtoSigs, Env2),
|
||||
Functions = Get(function),
|
||||
%% Check for duplicates in Functions (we turn it into a map below)
|
||||
_ = bind_funs([{Fun, {tuple_t, Ann, []}} || {letfun, Ann, {id, _, Fun}, _, _, _} <- Functions],
|
||||
#env{}),
|
||||
FunMap = maps:from_list([ {Fun, Def} || Def = {letfun, _, {id, _, Fun}, _, _, _} <- Functions ]),
|
||||
FunBind = fun({letfun, Ann, {id, _, Fun}, _, _, _}) -> {Fun, {tuple_t, Ann, []}};
|
||||
({fun_clauses, Ann, {id, _, Fun}, _, _}) -> {Fun, {tuple_t, Ann, []}} end,
|
||||
FunName = fun(Def) -> {Name, _} = FunBind(Def), Name end,
|
||||
_ = bind_funs(lists:map(FunBind, Functions), #env{}),
|
||||
FunMap = maps:from_list([ {FunName(Def), Def} || Def <- Functions ]),
|
||||
check_reserved_entrypoints(FunMap),
|
||||
DepGraph = maps:map(fun(_, Def) -> aeso_syntax_utils:used_ids(Def) end, FunMap),
|
||||
SCCs = aeso_utils:scc(DepGraph),
|
||||
@@ -655,6 +799,49 @@ infer_contract(Env0, What, Defs) ->
|
||||
destroy_and_report_type_errors(Env4),
|
||||
{Env4, TypeDefs ++ Decls ++ Defs1}.
|
||||
|
||||
%% Restructure blocks into multi-clause fundefs (`fun_clauses`).
|
||||
-spec process_blocks([aeso_syntax:decl()]) -> [aeso_syntax:decl()].
|
||||
process_blocks(Decls) ->
|
||||
lists:flatmap(
|
||||
fun({block, Ann, Ds}) -> process_block(Ann, Ds);
|
||||
(Decl) -> [Decl] end, Decls).
|
||||
|
||||
-spec process_block(aeso_syntax:ann(), [aeso_syntax:decl()]) -> [aeso_syntax:decl()].
|
||||
process_block(_, []) -> [];
|
||||
process_block(_, [Decl]) -> [Decl];
|
||||
process_block(_Ann, [Decl | Decls]) ->
|
||||
IsThis = fun(Name) -> fun({letfun, _, {id, _, Name1}, _, _, _}) -> Name == Name1;
|
||||
(_) -> false end end,
|
||||
case Decl of
|
||||
{fun_decl, Ann1, Id = {id, _, Name}, Type} ->
|
||||
{Clauses, Rest} = lists:splitwith(IsThis(Name), Decls),
|
||||
[type_error({mismatched_decl_in_funblock, Name, D1}) || D1 <- Rest],
|
||||
[{fun_clauses, Ann1, Id, Type, Clauses}];
|
||||
{letfun, Ann1, Id = {id, _, Name}, _, _, _} ->
|
||||
{Clauses, Rest} = lists:splitwith(IsThis(Name), [Decl | Decls]),
|
||||
[type_error({mismatched_decl_in_funblock, Name, D1}) || D1 <- Rest],
|
||||
[{fun_clauses, Ann1, Id, {id, [{origin, system} | Ann1], "_"}, Clauses}]
|
||||
end.
|
||||
|
||||
%% Turns private stuff into public stuff
|
||||
expose_internals(Defs, What) ->
|
||||
[ begin
|
||||
Ann = element(2, Def),
|
||||
NewAnn = case What of
|
||||
namespace -> [A ||A <- Ann, A /= {private, true}, A /= private];
|
||||
main_contract -> [{entrypoint, true}|Ann]; % minor duplication
|
||||
contract -> Ann
|
||||
end,
|
||||
Def1 = setelement(2, Def, NewAnn),
|
||||
case Def1 of % fix inner clauses
|
||||
{fun_clauses, Ans, Id, T, Clauses} ->
|
||||
{fun_clauses, Ans, Id, T, expose_internals(Clauses, What)};
|
||||
_ -> Def1
|
||||
end
|
||||
end
|
||||
|| Def <- Defs
|
||||
].
|
||||
|
||||
-spec check_typedefs(env(), [aeso_syntax:decl()]) -> {env(), [aeso_syntax:decl()]}.
|
||||
check_typedefs(Env = #env{ namespace = Ns }, Defs) ->
|
||||
create_type_errors(),
|
||||
@@ -787,9 +974,9 @@ check_type(Env, T) ->
|
||||
check_type(Env, T = {tvar, _, _}, Arity) ->
|
||||
[ type_error({higher_kinded_typevar, T}) || Arity /= 0 ],
|
||||
check_tvar(Env, T);
|
||||
check_type(_Env, X = {id, _, "_"}, Arity) ->
|
||||
check_type(_Env, X = {id, Ann, "_"}, Arity) ->
|
||||
ensure_base_type(X, Arity),
|
||||
X;
|
||||
fresh_uvar(Ann);
|
||||
check_type(Env, X = {Tag, _, _}, Arity) when Tag == con; Tag == qcon; Tag == id; Tag == qid ->
|
||||
case lookup_type(Env, X) of
|
||||
{Q, {_, Def}} ->
|
||||
@@ -960,8 +1147,9 @@ typesig_to_fun_t({type_sig, Ann, _Constr, Named, Args, Res}) ->
|
||||
|
||||
infer_letrec(Env, Defs) ->
|
||||
create_constraints(),
|
||||
Funs = [{Name, fresh_uvar(A)}
|
||||
|| {letfun, _, {id, A, Name}, _, _, _} <- Defs],
|
||||
Funs = lists:map(fun({letfun, _, {id, Ann, Name}, _, _, _}) -> {Name, fresh_uvar(Ann)};
|
||||
({fun_clauses, _, {id, Ann, Name}, _, _}) -> {Name, fresh_uvar(Ann)}
|
||||
end, Defs),
|
||||
ExtendEnv = bind_funs(Funs, Env),
|
||||
Inferred =
|
||||
[ begin
|
||||
@@ -980,26 +1168,51 @@ infer_letrec(Env, Defs) ->
|
||||
[print_typesig(S) || S <- TypeSigs],
|
||||
{TypeSigs, NewDefs}.
|
||||
|
||||
infer_letfun(Env0, {letfun, Attrib, Fun = {id, NameAttrib, Name}, Args, What, Body}) ->
|
||||
infer_letfun(Env, {fun_clauses, Ann, Fun = {id, _, Name}, Type, Clauses}) ->
|
||||
Type1 = check_type(Env, Type),
|
||||
{NameSigs, Clauses1} = lists:unzip([ infer_letfun1(Env, Clause) || Clause <- Clauses ]),
|
||||
{_, Sigs = [Sig | _]} = lists:unzip(NameSigs),
|
||||
_ = [ begin
|
||||
ClauseT = typesig_to_fun_t(ClauseSig),
|
||||
unify(Env, ClauseT, Type1, {check_typesig, Name, ClauseT, Type1})
|
||||
end || ClauseSig <- Sigs ],
|
||||
{{Name, Sig}, desugar_clauses(Ann, Fun, Sig, Clauses1)};
|
||||
infer_letfun(Env, LetFun = {letfun, Ann, Fun, _, _, _}) ->
|
||||
{{Name, Sig}, Clause} = infer_letfun1(Env, LetFun),
|
||||
{{Name, Sig}, desugar_clauses(Ann, Fun, Sig, [Clause])}.
|
||||
|
||||
infer_letfun1(Env0, {letfun, Attrib, Fun = {id, NameAttrib, Name}, Args, What, Body}) ->
|
||||
Env = Env0#env{ stateful = aeso_syntax:get_ann(stateful, Attrib, false),
|
||||
current_function = Fun },
|
||||
check_unique_arg_names(Fun, Args),
|
||||
ArgTypes = [{ArgName, check_type(Env, arg_type(ArgAnn, T))} || {arg, ArgAnn, ArgName, T} <- Args],
|
||||
{NewEnv, {typed, _, {tuple, _, TypedArgs}, {tuple_t, _, ArgTypes}}} = infer_pattern(Env, {tuple, [{origin, system} | NameAttrib], Args}),
|
||||
ExpectedType = check_type(Env, arg_type(NameAttrib, What)),
|
||||
NewBody={typed, _, _, ResultType} = check_expr(bind_vars(ArgTypes, Env), Body, ExpectedType),
|
||||
NewArgs = [{arg, A1, {id, A2, ArgName}, T}
|
||||
|| {{_, T}, {arg, A1, {id, A2, ArgName}, _}} <- lists:zip(ArgTypes, Args)],
|
||||
NewBody={typed, _, _, ResultType} = check_expr(NewEnv, Body, ExpectedType),
|
||||
NamedArgs = [],
|
||||
TypeSig = {type_sig, Attrib, none, NamedArgs, [T || {arg, _, _, T} <- NewArgs], ResultType},
|
||||
TypeSig = {type_sig, Attrib, none, NamedArgs, ArgTypes, ResultType},
|
||||
{{Name, TypeSig},
|
||||
{letfun, Attrib, {id, NameAttrib, Name}, NewArgs, ResultType, NewBody}}.
|
||||
{letfun, Attrib, {id, NameAttrib, Name}, TypedArgs, ResultType, NewBody}}.
|
||||
|
||||
check_unique_arg_names(Fun, Args) ->
|
||||
Name = fun({arg, _, {id, _, X}, _}) -> X end,
|
||||
Names = lists:map(Name, Args),
|
||||
Dups = lists:usort(Names -- lists:usort(Names)),
|
||||
[ type_error({repeated_arg, Fun, Arg}) || Arg <- Dups ],
|
||||
ok.
|
||||
desugar_clauses(Ann, Fun, {type_sig, _, _, _, ArgTypes, RetType}, Clauses) ->
|
||||
NeedDesugar =
|
||||
case Clauses of
|
||||
[{letfun, _, _, As, _, _}] -> lists:any(fun({typed, _, {id, _, _}, _}) -> false; (_) -> true end, As);
|
||||
_ -> true
|
||||
end,
|
||||
case NeedDesugar of
|
||||
false -> [Clause] = Clauses, Clause;
|
||||
true ->
|
||||
NoAnn = [{origin, system}],
|
||||
Args = [ {typed, NoAnn, {id, NoAnn, "x#" ++ integer_to_list(I)}, Type}
|
||||
|| {I, Type} <- indexed(1, ArgTypes) ],
|
||||
Tuple = fun([X]) -> X;
|
||||
(As) -> {typed, NoAnn, {tuple, NoAnn, As}, {tuple_t, NoAnn, ArgTypes}}
|
||||
end,
|
||||
{letfun, Ann, Fun, Args, RetType,
|
||||
{typed, NoAnn,
|
||||
{switch, NoAnn, Tuple(Args),
|
||||
[ {'case', AnnC, Tuple(ArgsC), Body}
|
||||
|| {letfun, AnnC, _, ArgsC, _, Body} <- Clauses ]}, RetType}}
|
||||
end.
|
||||
|
||||
print_typesig({Name, TypeSig}) ->
|
||||
?PRINT_TYPES("Inferred ~s : ~s\n", [Name, pp(TypeSig)]).
|
||||
@@ -1092,9 +1305,9 @@ get_call_chains(Graph, Visited, Queue, Stop, Acc) ->
|
||||
end.
|
||||
|
||||
check_expr(Env, Expr, Type) ->
|
||||
E = {typed, _, _, Type1} = infer_expr(Env, Expr),
|
||||
{typed, Ann, Expr1, Type1} = infer_expr(Env, Expr),
|
||||
unify(Env, Type1, Type, {check_expr, Expr, Type1, Type}),
|
||||
E.
|
||||
{typed, Ann, Expr1, Type}. %% Keep the user-given type
|
||||
|
||||
infer_expr(_Env, Body={bool, As, _}) ->
|
||||
{typed, As, Body, {id, As, "bool"}};
|
||||
@@ -1138,21 +1351,20 @@ infer_expr(Env, {list, As, Elems}) ->
|
||||
NewElems = [check_expr(Env, X, ElemType) || X <- Elems],
|
||||
{typed, As, {list, As, NewElems}, {app_t, As, {id, As, "list"}, [ElemType]}};
|
||||
infer_expr(Env, {list_comp, As, Yield, []}) ->
|
||||
{typed, _, TypedYield, Type} = infer_expr(Env, Yield),
|
||||
{typed, _, _, Type} = TypedYield = infer_expr(Env, Yield),
|
||||
{typed, As, {list_comp, As, TypedYield, []}, {app_t, As, {id, As, "list"}, [Type]}};
|
||||
infer_expr(Env, {list_comp, As, Yield, [{comprehension_bind, Arg, BExpr}|Rest]}) ->
|
||||
BindVarType = fresh_uvar(As),
|
||||
infer_expr(Env, {list_comp, As, Yield, [{comprehension_bind, Pat, BExpr}|Rest]}) ->
|
||||
TypedBind = {typed, As2, _, TypeBExpr} = infer_expr(Env, BExpr),
|
||||
{NewE, TypedPat = {typed, _, _, PatType}} = infer_pattern(Env, Pat),
|
||||
unify( Env
|
||||
, TypeBExpr
|
||||
, {app_t, As, {id, As, "list"}, [BindVarType]}
|
||||
, {list_comp, TypedBind, TypeBExpr, {app_t, As2, {id, As, "list"}, [BindVarType]}}),
|
||||
NewE = bind_var(Arg, BindVarType, Env),
|
||||
, {app_t, As, {id, As, "list"}, [PatType]}
|
||||
, {list_comp, TypedBind, TypeBExpr, {app_t, As2, {id, As, "list"}, [PatType]}}),
|
||||
{typed, _, {list_comp, _, TypedYield, TypedRest}, ResType} =
|
||||
infer_expr(NewE, {list_comp, As, Yield, Rest}),
|
||||
{ typed
|
||||
, As
|
||||
, {list_comp, As, TypedYield, [{comprehension_bind, {typed, Arg, BindVarType}, TypedBind}|TypedRest]}
|
||||
, {list_comp, As, TypedYield, [{comprehension_bind, TypedPat, TypedBind}|TypedRest]}
|
||||
, ResType};
|
||||
infer_expr(Env, {list_comp, AttrsL, Yield, [{comprehension_if, AttrsIF, Cond}|Rest]}) ->
|
||||
NewCond = check_expr(Env, Cond, {id, AttrsIF, "bool"}),
|
||||
@@ -1162,8 +1374,8 @@ infer_expr(Env, {list_comp, AttrsL, Yield, [{comprehension_if, AttrsIF, Cond}|Re
|
||||
, AttrsL
|
||||
, {list_comp, AttrsL, TypedYield, [{comprehension_if, AttrsIF, NewCond}|TypedRest]}
|
||||
, ResType};
|
||||
infer_expr(Env, {list_comp, AsLC, Yield, [{letval, AsLV, Pattern, Type, E}|Rest]}) ->
|
||||
NewE = {typed, _, _, PatType} = infer_expr(Env, {typed, AsLV, E, arg_type(AsLV, Type)}),
|
||||
infer_expr(Env, {list_comp, AsLC, Yield, [{letval, AsLV, Pattern, E}|Rest]}) ->
|
||||
NewE = {typed, _, _, PatType} = infer_expr(Env, E),
|
||||
BlockType = fresh_uvar(AsLV),
|
||||
{'case', _, NewPattern, NewRest} =
|
||||
infer_case( Env
|
||||
@@ -1175,7 +1387,7 @@ infer_expr(Env, {list_comp, AsLC, Yield, [{letval, AsLV, Pattern, Type, E}|Rest]
|
||||
{typed, _, {list_comp, _, TypedYield, TypedRest}, ResType} = NewRest,
|
||||
{ typed
|
||||
, AsLC
|
||||
, {list_comp, AsLC, TypedYield, [{letval, AsLV, NewPattern, Type, NewE}|TypedRest]}
|
||||
, {list_comp, AsLC, TypedYield, [{letval, AsLV, NewPattern, NewE}|TypedRest]}
|
||||
, ResType
|
||||
};
|
||||
infer_expr(Env, {list_comp, AsLC, Yield, [Def={letfun, AsLF, _, _, _, _}|Rest]}) ->
|
||||
@@ -1193,7 +1405,7 @@ infer_expr(Env, {typed, As, Body, Type}) ->
|
||||
Type1 = check_type(Env, Type),
|
||||
{typed, _, NewBody, NewType} = check_expr(Env, Body, Type1),
|
||||
{typed, As, NewBody, NewType};
|
||||
infer_expr(Env, {app, Ann, Fun, Args0}) ->
|
||||
infer_expr(Env, {app, Ann, Fun, Args0} = App) ->
|
||||
NamedArgs = [ Arg || Arg = {named_arg, _, _, _} <- Args0 ],
|
||||
Args = Args0 -- NamedArgs,
|
||||
case aeso_syntax:get_ann(format, Ann) of
|
||||
@@ -1208,8 +1420,16 @@ infer_expr(Env, {app, Ann, Fun, Args0}) ->
|
||||
NewFun={typed, _, _, FunType} = infer_expr(Env, Fun),
|
||||
NewArgs = [infer_expr(Env, A) || A <- Args],
|
||||
ArgTypes = [T || {typed, _, _, T} <- NewArgs],
|
||||
GeneralResultType = fresh_uvar(Ann),
|
||||
ResultType = fresh_uvar(Ann),
|
||||
unify(Env, FunType, {fun_t, [], NamedArgsVar, ArgTypes, ResultType}, {infer_app, Fun, Args, FunType, ArgTypes}),
|
||||
When = {infer_app, Fun, NamedArgs1, Args, FunType, ArgTypes},
|
||||
unify(Env, FunType, {fun_t, [], NamedArgsVar, ArgTypes, GeneralResultType}, When),
|
||||
add_named_argument_constraint(
|
||||
#dependent_type_constraint{ named_args_t = NamedArgsVar,
|
||||
named_args = NamedArgs1,
|
||||
general_type = GeneralResultType,
|
||||
specialized_type = ResultType,
|
||||
context = {check_return, App} }),
|
||||
{typed, Ann, {app, Ann, NewFun, NamedArgs1 ++ NewArgs}, dereference(ResultType)}
|
||||
end;
|
||||
infer_expr(Env, {'if', Attrs, Cond, Then, Else}) ->
|
||||
@@ -1290,6 +1510,16 @@ infer_expr(Env, {block, Attrs, Stmts}) ->
|
||||
BlockType = fresh_uvar(Attrs),
|
||||
NewStmts = infer_block(Env, Attrs, Stmts, BlockType),
|
||||
{typed, Attrs, {block, Attrs, NewStmts}, BlockType};
|
||||
infer_expr(_Env, {record_or_map_error, Attrs, Fields}) ->
|
||||
type_error({mixed_record_and_map, {record, Attrs, Fields}}),
|
||||
Type = fresh_uvar(Attrs),
|
||||
{typed, Attrs, {record, Attrs, []}, Type};
|
||||
infer_expr(Env, {record_or_map_error, Attrs, Expr, []}) ->
|
||||
type_error({empty_record_or_map_update, {record, Attrs, Expr, []}}),
|
||||
infer_expr(Env, Expr);
|
||||
infer_expr(Env, {record_or_map_error, Attrs, Expr, Fields}) ->
|
||||
type_error({mixed_record_and_map, {record, Attrs, Expr, Fields}}),
|
||||
infer_expr(Env, Expr);
|
||||
infer_expr(Env, {lam, Attrs, Args, Body}) ->
|
||||
ArgTypes = [fresh_uvar(As) || {arg, As, _, _} <- Args],
|
||||
ArgPatterns = [{typed, As, Pat, check_type(Env, T)} || {arg, As, Pat, T} <- Args],
|
||||
@@ -1298,7 +1528,7 @@ infer_expr(Env, {lam, Attrs, Args, Body}) ->
|
||||
infer_case(Env, Attrs, {tuple, Attrs, ArgPatterns}, {tuple_t, Attrs, ArgTypes}, Body, ResultType),
|
||||
NewArgs = [{arg, As, NewPat, NewT} || {typed, As, NewPat, NewT} <- NewArgPatterns],
|
||||
{typed, Attrs, {lam, Attrs, NewArgs, NewBody}, {fun_t, Attrs, [], ArgTypes, ResultType}};
|
||||
infer_expr(Env, Let = {letval, Attrs, _, _, _}) ->
|
||||
infer_expr(Env, Let = {letval, Attrs, _, _}) ->
|
||||
type_error({missing_body_for_let, Attrs}),
|
||||
infer_expr(Env, {block, Attrs, [Let, abort_expr(Attrs, "missing body")]});
|
||||
infer_expr(Env, Let = {letfun, Attrs, _, _, _, _}) ->
|
||||
@@ -1358,18 +1588,22 @@ infer_op(Env, As, Op, Args, InferOp) ->
|
||||
TypedArgs = [infer_expr(Env, A) || A <- Args],
|
||||
ArgTypes = [T || {typed, _, _, T} <- TypedArgs],
|
||||
Inferred = {fun_t, _, _, OperandTypes, ResultType} = InferOp(Op),
|
||||
unify(Env, ArgTypes, OperandTypes, {infer_app, Op, Args, Inferred, ArgTypes}),
|
||||
unify(Env, ArgTypes, OperandTypes, {infer_app, Op, [], Args, Inferred, ArgTypes}),
|
||||
{typed, As, {app, As, Op, TypedArgs}, ResultType}.
|
||||
|
||||
infer_case(Env, Attrs, Pattern, ExprType, Branch, SwitchType) ->
|
||||
infer_pattern(Env, Pattern) ->
|
||||
Vars = free_vars(Pattern),
|
||||
Names = [N || {id, _, N} <- Vars, N /= "_"],
|
||||
case Names -- lists:usort(Names) of
|
||||
[] -> ok;
|
||||
Nonlinear -> type_error({non_linear_pattern, Pattern, lists:usort(Nonlinear)})
|
||||
end,
|
||||
NewEnv = bind_vars([{Var, fresh_uvar(Ann)} || Var = {id, Ann, _} <- Vars], Env#env{ in_pattern = true }),
|
||||
NewPattern = {typed, _, _, PatType} = infer_expr(NewEnv, Pattern),
|
||||
NewEnv = bind_vars([{Var, fresh_uvar(Ann1)} || Var = {id, Ann1, _} <- Vars], Env#env{ in_pattern = true }),
|
||||
NewPattern = infer_expr(NewEnv, Pattern),
|
||||
{NewEnv#env{ in_pattern = Env#env.in_pattern }, NewPattern}.
|
||||
|
||||
infer_case(Env, Attrs, Pattern, ExprType, Branch, SwitchType) ->
|
||||
{NewEnv, NewPattern = {typed, _, _, PatType}} = infer_pattern(Env, Pattern),
|
||||
NewBranch = check_expr(NewEnv#env{ in_pattern = false }, Branch, SwitchType),
|
||||
unify(Env, PatType, ExprType, {case_pat, Pattern, PatType, ExprType}),
|
||||
{'case', Attrs, NewPattern, NewBranch}.
|
||||
@@ -1384,11 +1618,11 @@ infer_block(Env, Attrs, [Def={letfun, Ann, _, _, _, _}|Rest], BlockType) ->
|
||||
FunT = typesig_to_fun_t(TypeSig),
|
||||
NewE = bind_var({id, Ann, Name}, FunT, Env),
|
||||
[LetFun|infer_block(NewE, Attrs, Rest, BlockType)];
|
||||
infer_block(Env, _, [{letval, Attrs, Pattern, Type, E}|Rest], BlockType) ->
|
||||
NewE = {typed, _, _, PatType} = infer_expr(Env, {typed, Attrs, E, arg_type(aeso_syntax:get_ann(Pattern), Type)}),
|
||||
infer_block(Env, _, [{letval, Attrs, Pattern, E}|Rest], BlockType) ->
|
||||
NewE = {typed, _, _, PatType} = infer_expr(Env, E),
|
||||
{'case', _, NewPattern, {typed, _, {block, _, NewRest}, _}} =
|
||||
infer_case(Env, Attrs, Pattern, PatType, {block, Attrs, Rest}, BlockType),
|
||||
[{letval, Attrs, NewPattern, Type, NewE}|NewRest];
|
||||
[{letval, Attrs, NewPattern, NewE}|NewRest];
|
||||
infer_block(Env, Attrs, [E|Rest], BlockType) ->
|
||||
[infer_expr(Env, E)|infer_block(Env, Attrs, Rest, BlockType)].
|
||||
|
||||
@@ -1430,18 +1664,13 @@ infer_prefix({IntOp,As}) when IntOp =:= '-' ->
|
||||
abort_expr(Ann, Str) ->
|
||||
{app, Ann, {id, Ann, "abort"}, [{string, Ann, Str}]}.
|
||||
|
||||
free_vars({int, _, _}) ->
|
||||
[];
|
||||
free_vars({char, _, _}) ->
|
||||
[];
|
||||
free_vars({string, _, _}) ->
|
||||
[];
|
||||
free_vars({bool, _, _}) ->
|
||||
[];
|
||||
free_vars(Id={id, _, _}) ->
|
||||
[Id];
|
||||
free_vars({con, _, _}) ->
|
||||
[];
|
||||
free_vars({int, _, _}) -> [];
|
||||
free_vars({char, _, _}) -> [];
|
||||
free_vars({string, _, _}) -> [];
|
||||
free_vars({bool, _, _}) -> [];
|
||||
free_vars(Id={id, _, _}) -> [Id];
|
||||
free_vars({con, _, _}) -> [];
|
||||
free_vars({qcon, _, _}) -> [];
|
||||
free_vars({tuple, _, Cpts}) ->
|
||||
free_vars(Cpts);
|
||||
free_vars({list, _, Elems}) ->
|
||||
@@ -1450,6 +1679,8 @@ free_vars({app, _, {'::', _}, Args}) ->
|
||||
free_vars(Args);
|
||||
free_vars({app, _, {con, _, _}, Args}) ->
|
||||
free_vars(Args);
|
||||
free_vars({app, _, {qcon, _, _}, Args}) ->
|
||||
free_vars(Args);
|
||||
free_vars({record, _, Fields}) ->
|
||||
free_vars([E || {field, _, _, E} <- Fields]);
|
||||
free_vars({typed, _, A, _}) ->
|
||||
@@ -1528,12 +1759,12 @@ create_constraints() ->
|
||||
create_field_constraints().
|
||||
|
||||
destroy_and_report_unsolved_constraints(Env) ->
|
||||
solve_field_constraints(Env),
|
||||
solve_named_argument_constraints(Env),
|
||||
solve_bytes_constraints(Env),
|
||||
solve_field_constraints(Env),
|
||||
destroy_and_report_unsolved_field_constraints(Env),
|
||||
destroy_and_report_unsolved_bytes_constraints(Env),
|
||||
destroy_and_report_unsolved_named_argument_constraints(Env).
|
||||
destroy_and_report_unsolved_named_argument_constraints(Env),
|
||||
destroy_and_report_unsolved_field_constraints(Env).
|
||||
|
||||
%% -- Named argument constraints --
|
||||
|
||||
@@ -1573,8 +1804,43 @@ check_named_argument_constraint(Env,
|
||||
type_error({bad_named_argument, Args, Id}),
|
||||
false;
|
||||
[T] -> unify(Env, T, Type, {check_named_arg_constraint, C}), true
|
||||
end;
|
||||
check_named_argument_constraint(Env,
|
||||
#dependent_type_constraint{ named_args_t = NamedArgsT0,
|
||||
named_args = NamedArgs,
|
||||
general_type = GenType,
|
||||
specialized_type = SpecType,
|
||||
context = {check_return, App} }) ->
|
||||
NamedArgsT = dereference(NamedArgsT0),
|
||||
case dereference(NamedArgsT0) of
|
||||
[_ | _] = NamedArgsT ->
|
||||
GetVal = fun(Name, Default) ->
|
||||
hd([ Val || {named_arg, _, {id, _, N}, Val} <- NamedArgs, N == Name] ++
|
||||
[ Default ])
|
||||
end,
|
||||
ArgEnv = maps:from_list([ {Name, GetVal(Name, Default)}
|
||||
|| {named_arg_t, _, {id, _, Name}, _, Default} <- NamedArgsT ]),
|
||||
GenType1 = specialize_dependent_type(ArgEnv, GenType),
|
||||
unify(Env, GenType1, SpecType, {check_expr, App, GenType1, SpecType}),
|
||||
true;
|
||||
_ -> unify(Env, GenType, SpecType, {check_expr, App, GenType, SpecType}), true
|
||||
end.
|
||||
|
||||
specialize_dependent_type(Env, Type) ->
|
||||
case dereference(Type) of
|
||||
{if_t, _, {id, _, Arg}, Then, Else} ->
|
||||
Val = maps:get(Arg, Env),
|
||||
case Val of
|
||||
{typed, _, {bool, _, true}, _} -> Then;
|
||||
{typed, _, {bool, _, false}, _} -> Else;
|
||||
_ ->
|
||||
type_error({named_argument_must_be_literal_bool, Arg, Val}),
|
||||
fresh_uvar(aeso_syntax:get_ann(Val))
|
||||
end;
|
||||
_ -> Type %% Currently no deep dependent types
|
||||
end.
|
||||
|
||||
|
||||
destroy_and_report_unsolved_named_argument_constraints(Env) ->
|
||||
Unsolved = solve_named_argument_constraints(Env, get_named_argument_constraints()),
|
||||
[ type_error({unsolved_named_argument_constraint, C}) || C <- Unsolved ],
|
||||
@@ -1800,9 +2066,11 @@ destroy_and_report_unsolved_field_constraints(Env) ->
|
||||
{FieldCs, OtherCs} =
|
||||
lists:partition(fun(#field_constraint{}) -> true; (_) -> false end,
|
||||
get_field_constraints()),
|
||||
{CreateCs, ContractCs} =
|
||||
{CreateCs, OtherCs1} =
|
||||
lists:partition(fun(#record_create_constraint{}) -> true; (_) -> false end,
|
||||
OtherCs),
|
||||
{ContractCs, []} =
|
||||
lists:partition(fun(#is_contract_constraint{}) -> true; (_) -> false end, OtherCs1),
|
||||
Unknown = solve_known_record_types(Env, FieldCs),
|
||||
if Unknown == [] -> ok;
|
||||
true ->
|
||||
@@ -1876,7 +2144,8 @@ unfold_record_types(Env, T) ->
|
||||
unfold_types(Env, T, [unfold_record_types]).
|
||||
|
||||
unfold_types(Env, {typed, Attr, E, Type}, Options) ->
|
||||
{typed, Attr, unfold_types(Env, E, Options), unfold_types_in_type(Env, Type, Options)};
|
||||
Options1 = [{ann, Attr} | lists:keydelete(ann, 1, Options)],
|
||||
{typed, Attr, unfold_types(Env, E, Options), unfold_types_in_type(Env, Type, Options1)};
|
||||
unfold_types(Env, {arg, Attr, Id, Type}, Options) ->
|
||||
{arg, Attr, Id, unfold_types_in_type(Env, Type, Options)};
|
||||
unfold_types(Env, {type_sig, Ann, Constr, NamedArgs, Args, Ret}, Options) ->
|
||||
@@ -1902,7 +2171,8 @@ unfold_types_in_type(Env, T) ->
|
||||
|
||||
unfold_types_in_type(Env, {app_t, Ann, Id = {id, _, "map"}, Args = [KeyType0, _]}, Options) ->
|
||||
Args1 = [KeyType, _] = unfold_types_in_type(Env, Args, Options),
|
||||
[ type_error({map_in_map_key, KeyType0}) || has_maps(KeyType) ],
|
||||
Ann1 = proplists:get_value(ann, Options, aeso_syntax:get_ann(KeyType0)),
|
||||
[ type_error({map_in_map_key, Ann1, KeyType0}) || has_maps(KeyType) ],
|
||||
{app_t, Ann, Id, Args1};
|
||||
unfold_types_in_type(Env, {app_t, Ann, Id, Args}, Options) when ?is_type_id(Id) ->
|
||||
UnfoldRecords = proplists:get_value(unfold_record_types, Options, false),
|
||||
@@ -1976,8 +2246,13 @@ subst_tvars1(_Env, X) ->
|
||||
unify(_, {id, _, "_"}, _, _When) -> true;
|
||||
unify(_, _, {id, _, "_"}, _When) -> true;
|
||||
unify(Env, A, B, When) ->
|
||||
A1 = dereference(unfold_types_in_type(Env, A)),
|
||||
B1 = dereference(unfold_types_in_type(Env, B)),
|
||||
Options =
|
||||
case When of %% Improve source location for map_in_map_key errors
|
||||
{check_expr, E, _, _} -> [{ann, aeso_syntax:get_ann(E)}];
|
||||
_ -> []
|
||||
end,
|
||||
A1 = dereference(unfold_types_in_type(Env, A, Options)),
|
||||
B1 = dereference(unfold_types_in_type(Env, B, Options)),
|
||||
unify1(Env, A1, B1, When).
|
||||
|
||||
unify1(_Env, {uvar, _, R}, {uvar, _, R}, _When) ->
|
||||
@@ -2008,7 +2283,11 @@ unify1(_Env, {qcon, _, Name}, {qcon, _, Name}, _When) ->
|
||||
true;
|
||||
unify1(_Env, {bytes_t, _, Len}, {bytes_t, _, Len}, _When) ->
|
||||
true;
|
||||
unify1(Env, {fun_t, _, Named1, Args1, Result1}, {fun_t, _, Named2, Args2, Result2}, When) ->
|
||||
unify1(Env, {if_t, _, {id, _, Id}, Then1, Else1}, {if_t, _, {id, _, Id}, Then2, Else2}, When) ->
|
||||
unify(Env, Then1, Then2, When) andalso
|
||||
unify(Env, Else1, Else2, 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, Args1, Args2, When) andalso unify(Env, Result1, Result2, When);
|
||||
unify1(Env, {app_t, _, {Tag, _, F}, Args1}, {app_t, _, {Tag, _, F}, Args2}, When)
|
||||
@@ -2068,6 +2347,8 @@ occurs_check1(R, {record_t, Fields}) ->
|
||||
occurs_check(R, Fields);
|
||||
occurs_check1(R, {field_t, _, _, T}) ->
|
||||
occurs_check(R, T);
|
||||
occurs_check1(R, {if_t, _, _, Then, Else}) ->
|
||||
occurs_check(R, [Then, Else]);
|
||||
occurs_check1(R, [H | T]) ->
|
||||
occurs_check(R, H) orelse occurs_check(R, T);
|
||||
occurs_check1(_, []) -> false.
|
||||
@@ -2197,6 +2478,30 @@ mk_t_err(Pos, Msg) ->
|
||||
mk_t_err(Pos, Msg, Ctxt) ->
|
||||
aeso_errors:new(type_error, Pos, lists:flatten(Msg), lists:flatten(Ctxt)).
|
||||
|
||||
mk_error({no_decls, File}) ->
|
||||
Pos = aeso_errors:pos(File, 0, 0),
|
||||
mk_t_err(Pos, "Empty contract\n");
|
||||
mk_error({mismatched_decl_in_funblock, Name, Decl}) ->
|
||||
Msg = io_lib:format("Mismatch in the function block. Expected implementation/type declaration of ~s function\n", [Name]),
|
||||
mk_t_err(pos(Decl), Msg);
|
||||
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}) ->
|
||||
Msg = io_lib:format("Cannot unify ~s\n and ~s\n",
|
||||
[pp(instantiate(A)), pp(instantiate(B))]),
|
||||
@@ -2279,14 +2584,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",
|
||||
[pp_type("", Type), pp_loc(Type), pp_type("", Type1)]),
|
||||
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}) ->
|
||||
Msg = io_lib:format("The event constructor ~s (at ~s) has too many indexed values (max 3)\n",
|
||||
[name(Constr), pp_loc(Constr)]),
|
||||
@@ -2330,13 +2627,21 @@ mk_error({include, _, {string, Pos, Name}}) ->
|
||||
[binary_to_list(Name), pp_loc(Pos)]),
|
||||
mk_t_err(pos(Pos), Msg);
|
||||
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)]),
|
||||
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}) ->
|
||||
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)]),
|
||||
@@ -2411,10 +2716,10 @@ mk_error({new_tuple_syntax, Ann, Ts}) ->
|
||||
Msg = io_lib:format("Invalid type\n~s (at ~s)\nThe syntax of tuple types changed in Sophia version 4.0. Did you mean\n~s\n",
|
||||
[pp_type(" ", {args_t, Ann, Ts}), pp_loc(Ann), pp_type(" ", {tuple_t, Ann, Ts})]),
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({map_in_map_key, KeyType}) ->
|
||||
mk_error({map_in_map_key, Ann, KeyType}) ->
|
||||
Msg = io_lib:format("Invalid key type\n~s\n", [pp_type(" ", KeyType)]),
|
||||
Cxt = "Map keys cannot contain other maps.\n",
|
||||
mk_t_err(pos(KeyType), Msg, Cxt);
|
||||
mk_t_err(pos(Ann), Msg, Cxt);
|
||||
mk_error({cannot_call_init_function, Ann}) ->
|
||||
Msg = "The 'init' function is called exclusively by the create contract transaction\n"
|
||||
"and cannot be called from the contract code.\n",
|
||||
@@ -2456,6 +2761,17 @@ mk_error({compiler_version_mismatch, Ann, Version, Op, Bound}) ->
|
||||
"because it does not satisfy the constraint"
|
||||
" ~s ~s ~s\n", [PrintV(Version), Op, PrintV(Bound)]),
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({empty_record_or_map_update, Expr}) ->
|
||||
Msg = io_lib:format("Empty record/map update\n~s",
|
||||
[pp_expr(" ", Expr)]),
|
||||
mk_t_err(pos(Expr), Msg);
|
||||
mk_error({mixed_record_and_map, Expr}) ->
|
||||
Msg = io_lib:format("Mixed record fields and map keys in\n~s",
|
||||
[pp_expr(" ", Expr)]),
|
||||
mk_t_err(pos(Expr), Msg);
|
||||
mk_error({named_argument_must_be_literal_bool, Name, Arg}) ->
|
||||
Msg = io_lib:format("Invalid '~s' argument\n~s\nIt must be either 'true' or 'false'.", [Name, pp_expr(" ", instantiate(Arg))]),
|
||||
mk_t_err(pos(Arg), Msg);
|
||||
mk_error(Err) ->
|
||||
Msg = io_lib:format("Unknown error: ~p\n", [Err]),
|
||||
mk_t_err(pos(0, 0), Msg).
|
||||
@@ -2474,7 +2790,7 @@ pp_when({check_typesig, Name, Inferred, Given}) ->
|
||||
" inferred type: ~s\n"
|
||||
" given type: ~s\n",
|
||||
[Name, pp_loc(Given), pp(instantiate(Inferred)), pp(instantiate(Given))])};
|
||||
pp_when({infer_app, Fun, Args, Inferred0, ArgTypes0}) ->
|
||||
pp_when({infer_app, Fun, NamedArgs, Args, Inferred0, ArgTypes0}) ->
|
||||
Inferred = instantiate(Inferred0),
|
||||
ArgTypes = instantiate(ArgTypes0),
|
||||
{pos(Fun),
|
||||
@@ -2483,6 +2799,7 @@ pp_when({infer_app, Fun, Args, Inferred0, ArgTypes0}) ->
|
||||
"to arguments\n~s",
|
||||
[pp_loc(Fun),
|
||||
pp_typed(" ", Fun, Inferred),
|
||||
[ [pp_expr(" ", NamedArg), "\n"] || NamedArg <- NamedArgs ] ++
|
||||
[ [pp_typed(" ", Arg, ArgT), "\n"]
|
||||
|| {Arg, ArgT} <- lists:zip(Args, ArgTypes) ] ])};
|
||||
pp_when({field_constraint, FieldType0, InferredType0, Fld}) ->
|
||||
@@ -2559,6 +2876,12 @@ pp_when({list_comp, BindExpr, Inferred0, Expected0}) ->
|
||||
io_lib:format("when checking rvalue of list comprehension binding at ~s\n~s\n"
|
||||
"against type \n~s\n",
|
||||
[pp_loc(BindExpr), pp_typed(" ", BindExpr, Inferred), pp_type(" ", Expected)])};
|
||||
pp_when({check_named_arg_constraint, C}) ->
|
||||
{id, _, Name} = Arg = C#named_argument_constraint.name,
|
||||
[Type | _] = [ Type || {named_arg_t, _, {id, _, Name1}, Type, _} <- C#named_argument_constraint.args, Name1 == Name ],
|
||||
Err = io_lib:format("when checking named argument\n~s\nagainst inferred type\n~s",
|
||||
[pp_typed(" ", Arg, Type), pp_type(" ", C#named_argument_constraint.type)]),
|
||||
{pos(Arg), Err};
|
||||
pp_when(unknown) -> {pos(0,0), ""}.
|
||||
|
||||
-spec pp_why_record(why_record()) -> {pos(), iolist()}.
|
||||
@@ -2636,6 +2959,8 @@ pp({uvar, _, Ref}) ->
|
||||
["?u" | integer_to_list(erlang:phash2(Ref, 16384)) ];
|
||||
pp({tvar, _, Name}) ->
|
||||
Name;
|
||||
pp({if_t, _, Id, Then, Else}) ->
|
||||
["if(", pp([Id, Then, Else]), ")"];
|
||||
pp({tuple_t, _, []}) ->
|
||||
"unit";
|
||||
pp({tuple_t, _, Cpts}) ->
|
||||
@@ -2647,8 +2972,8 @@ pp({app_t, _, T, []}) ->
|
||||
pp(T);
|
||||
pp({app_t, _, Type, Args}) ->
|
||||
[pp(Type), "(", pp(Args), ")"];
|
||||
pp({named_arg_t, _, Name, Type, Default}) ->
|
||||
[pp(Name), " : ", pp(Type), " = ", pp(Default)];
|
||||
pp({named_arg_t, _, Name, Type, _Default}) ->
|
||||
[pp(Name), " : ", pp(Type)];
|
||||
pp({fun_t, _, Named = {uvar, _, _}, As, B}) ->
|
||||
["(", pp(Named), " | ", pp(As), ") => ", pp(B)];
|
||||
pp({fun_t, _, Named, As, B}) when is_list(Named) ->
|
||||
@@ -2733,3 +3058,7 @@ updates_key(Name, Updates) ->
|
||||
Updates1 = [ Upd || {Upd, false, _} <- Xs ],
|
||||
More = [ Rest || {_, true, Rest} <- Xs ],
|
||||
{More, Updates1}.
|
||||
|
||||
indexed(I, Xs) ->
|
||||
lists:zip(lists:seq(I, I + length(Xs) - 1), Xs).
|
||||
|
||||
|
||||
+627
-116
File diff suppressed because it is too large
Load Diff
+28
-10
@@ -131,7 +131,7 @@ contract_to_icode([Decl | Code], Icode) ->
|
||||
ast_id({id, _, Id}) -> Id;
|
||||
ast_id({qid, _, Id}) -> Id.
|
||||
|
||||
ast_args([{arg, _, Name, Type}|Rest], Acc, Icode) ->
|
||||
ast_args([{typed, _, Name, Type}|Rest], Acc, Icode) ->
|
||||
ast_args(Rest, [{ast_id(Name), ast_typerep1(Type, Icode)}| Acc], Icode);
|
||||
ast_args([], Acc, _Icode) -> lists:reverse(Acc).
|
||||
|
||||
@@ -318,19 +318,23 @@ ast_body({app, As, Fun, Args}, Icode) ->
|
||||
end;
|
||||
ast_body({list_comp, _, Yield, []}, Icode) ->
|
||||
#list{elems = [ast_body(Yield, Icode)]};
|
||||
ast_body({list_comp, As, Yield, [{comprehension_bind, {typed, Arg, ArgType}, BindExpr}|Rest]}, Icode) ->
|
||||
ast_body({list_comp, As, Yield, [{comprehension_bind, {typed, _, Pat, ArgType}, BindExpr}|Rest]}, Icode) ->
|
||||
Arg = "%lc",
|
||||
Body = {switch, As, {typed, As, {id, As, Arg}, ArgType},
|
||||
[{'case', As, Pat, {list_comp, As, Yield, Rest}},
|
||||
{'case', As, {id, As, "_"}, {list, As, []}}]},
|
||||
#funcall
|
||||
{ function = #var_ref{ name = ["ListInternal", "flat_map"] }
|
||||
, args =
|
||||
[ #lambda{ args=[#arg{name = ast_id(Arg), type = ast_type(ArgType, Icode)}]
|
||||
, body = ast_body({list_comp, As, Yield, Rest}, Icode)
|
||||
[ #lambda{ args=[#arg{name = Arg, type = ast_type(ArgType, Icode)}]
|
||||
, body = ast_body(Body, Icode)
|
||||
}
|
||||
, ast_body(BindExpr, Icode)
|
||||
]
|
||||
};
|
||||
ast_body({list_comp, As, Yield, [{comprehension_if, AsIF, Cond}|Rest]}, Icode) ->
|
||||
ast_body({'if', AsIF, Cond, {list_comp, As, Yield, Rest}, {list, As, []}}, Icode);
|
||||
ast_body({list_comp, As, Yield, [LV = {letval, _, _, _, _}|Rest]}, Icode) ->
|
||||
ast_body({list_comp, As, Yield, [LV = {letval, _, _, _}|Rest]}, Icode) ->
|
||||
ast_body({block, As, [LV, {list_comp, As, Yield, Rest}]}, Icode);
|
||||
ast_body({list_comp, As, Yield, [LF = {letfun, _, _, _, _, _}|Rest]}, Icode) ->
|
||||
ast_body({block, As, [LF, {list_comp, As, Yield, Rest}]}, Icode);
|
||||
@@ -344,14 +348,16 @@ ast_body({switch,_,A,Cases}, Icode) ->
|
||||
#switch{expr=ast_body(A, Icode),
|
||||
cases=[{ast_body(Pat, Icode),ast_body(Body, Icode)}
|
||||
|| {'case',_,Pat,Body} <- Cases]};
|
||||
ast_body({block, As, [{letval, _, Pat, _, E} | Rest]}, Icode) ->
|
||||
ast_body({block, As, [{letval, _, Pat, E} | Rest]}, Icode) ->
|
||||
E1 = ast_body(E, Icode),
|
||||
Pat1 = ast_body(Pat, Icode),
|
||||
Rest1 = ast_body({block, As, Rest}, Icode),
|
||||
#switch{expr = E1,
|
||||
cases = [{Pat1, Rest1}]};
|
||||
ast_body({block, As, [{letfun, Ann, F, Args, _Type, Expr} | Rest]}, Icode) ->
|
||||
ast_body({block, As, [{letval, Ann, F, unused, {lam, Ann, Args, Expr}} | Rest]}, Icode);
|
||||
ToArg = fun({typed, Ann1, Id, T}) -> {arg, Ann1, Id, T} end, %% Pattern matching has been desugared
|
||||
LamArgs = lists:map(ToArg, Args),
|
||||
ast_body({block, As, [{letval, Ann, F, {lam, Ann, LamArgs, Expr}} | Rest]}, Icode);
|
||||
ast_body({block,_,[]}, _Icode) ->
|
||||
#tuple{cpts=[]};
|
||||
ast_body({block,_,[E]}, Icode) ->
|
||||
@@ -463,6 +469,7 @@ is_builtin_fun({qid, _, ["AENS", "preclaim"]}, _Icode) ->
|
||||
is_builtin_fun({qid, _, ["AENS", "claim"]}, _Icode) -> true;
|
||||
is_builtin_fun({qid, _, ["AENS", "transfer"]}, _Icode) -> true;
|
||||
is_builtin_fun({qid, _, ["AENS", "revoke"]}, _Icode) -> true;
|
||||
is_builtin_fun({qid, _, ["AENS", "update"]}, _Icode) -> true;
|
||||
is_builtin_fun({qid, _, ["Map", "lookup"]}, _Icode) -> true;
|
||||
is_builtin_fun({qid, _, ["Map", "lookup_default"]}, _Icode) -> true;
|
||||
is_builtin_fun({qid, _, ["Map", "member"]}, _Icode) -> true;
|
||||
@@ -618,6 +625,12 @@ builtin_code(_, {qid, _, ["AENS", "revoke"]}, Args, _, _, Icode) ->
|
||||
[ast_body(Addr, Icode), ast_body(Name, Icode), ast_body(Sign, Icode)],
|
||||
[word, word, sign_t()], {tuple, []});
|
||||
|
||||
builtin_code(_, {qid, _, ["AENS", "update"]}, Args, _, _, Icode) ->
|
||||
{Sign, [Addr, Name, TTL, ClientTTL, Pointers]} = get_signature_arg(Args),
|
||||
prim_call(?PRIM_CALL_AENS_UPDATE, #integer{value = 0},
|
||||
[ast_body(Addr, Icode), ast_body(Name, Icode), ast_body(TTL, Icode), ast_body(ClientTTL, Icode), ast_body(Pointers, Icode), ast_body(Sign, Icode)],
|
||||
[word, string, word, word, word, sign_t()], {tuple, []});
|
||||
|
||||
%% -- Maps
|
||||
%% -- lookup functions
|
||||
builtin_code(_, {qid, _, ["Map", "lookup"]}, [Key, Map], _, _, Icode) ->
|
||||
@@ -800,10 +813,10 @@ check_entrypoint_type(Ann, Name, Args, Ret) ->
|
||||
true -> ok
|
||||
end end,
|
||||
[ CheckFirstOrder(T, {invalid_entrypoint, higher_order, Ann1, Name, {argument, X, T}})
|
||||
|| {arg, Ann1, X, T} <- Args ],
|
||||
|| {typed, Ann1, X, T} <- Args ],
|
||||
CheckFirstOrder(Ret, {invalid_entrypoint, higher_order, Ann, Name, {result, Ret}}),
|
||||
[ CheckMonomorphic(T, {invalid_entrypoint, polymorphic, Ann1, Name, {argument, X, T}})
|
||||
|| {arg, Ann1, X, T} <- Args ],
|
||||
|| {typed, Ann1, X, T} <- Args ],
|
||||
CheckMonomorphic(Ret, {invalid_entrypoint, polymorphic, Ann, Name, {result, Ret}}).
|
||||
|
||||
check_oracle_type(Ann, Type = ?oracle_t(QType, RType)) ->
|
||||
@@ -921,11 +934,16 @@ ast_typerep1({variant_t, Cons}, Icode) ->
|
||||
{variant, [ begin
|
||||
{constr_t, _, _, Args} = Con,
|
||||
[ ast_typerep1(Arg, Icode) || Arg <- Args ]
|
||||
end || Con <- Cons ]}.
|
||||
end || Con <- Cons ]};
|
||||
ast_typerep1({if_t, _, _, _, Else}, Icode) ->
|
||||
ast_typerep1(Else, Icode). %% protected remote calls are not in AEVM
|
||||
|
||||
ttl_t(Icode) ->
|
||||
ast_typerep({qid, [], ["Chain", "ttl"]}, Icode).
|
||||
|
||||
%% pointee_t(Icode) ->
|
||||
%% ast_typerep({qid, [], ["AENS", "pointee"]}, Icode).
|
||||
|
||||
sign_t() -> bytes_t(64).
|
||||
bytes_t(Len) when Len =< 32 -> word;
|
||||
bytes_t(Len) -> {tuple, lists:duplicate((31 + Len) div 32, word)}.
|
||||
|
||||
+154
-43
@@ -23,6 +23,7 @@
|
||||
, decode_calldata/4
|
||||
, parse/2
|
||||
, add_include_path/2
|
||||
, validate_byte_code/3
|
||||
]).
|
||||
|
||||
-include_lib("aebytecode/include/aeb_opcodes.hrl").
|
||||
@@ -37,10 +38,13 @@
|
||||
| pp_assembler
|
||||
| pp_bytecode
|
||||
| no_code
|
||||
| keep_included
|
||||
| debug_mode
|
||||
| {backend, aevm | fate}
|
||||
| {include, {file_system, [string()]} |
|
||||
{explicit_files, #{string() => binary()}}}
|
||||
| {src_file, string()}.
|
||||
| {src_file, string()}
|
||||
| {aci, aeso_aci:aci_type()}.
|
||||
-type options() :: [option()].
|
||||
|
||||
-export_type([ option/0
|
||||
@@ -114,7 +118,8 @@ from_string(Backend, ContractString, Options) ->
|
||||
end.
|
||||
|
||||
from_string1(aevm, ContractString, Options) ->
|
||||
#{icode := Icode} = string_to_code(ContractString, Options),
|
||||
#{ icode := Icode
|
||||
, folded_typed_ast := FoldedTypedAst } = string_to_code(ContractString, Options),
|
||||
TypeInfo = extract_type_info(Icode),
|
||||
Assembler = assemble(Icode, Options),
|
||||
pp_assembler(aevm, Assembler, Options),
|
||||
@@ -122,47 +127,63 @@ from_string1(aevm, ContractString, Options) ->
|
||||
ByteCode = << << B:8 >> || B <- ByteCodeList >>,
|
||||
pp_bytecode(ByteCode, Options),
|
||||
{ok, Version} = version(),
|
||||
{ok, #{byte_code => ByteCode,
|
||||
compiler_version => Version,
|
||||
contract_source => ContractString,
|
||||
type_info => TypeInfo,
|
||||
abi_version => aeb_aevm_abi:abi_version(),
|
||||
payable => maps:get(payable, Icode)
|
||||
}};
|
||||
Res = #{byte_code => ByteCode,
|
||||
compiler_version => Version,
|
||||
contract_source => ContractString,
|
||||
type_info => TypeInfo,
|
||||
abi_version => aeb_aevm_abi:abi_version(),
|
||||
payable => maps:get(payable, Icode)
|
||||
},
|
||||
{ok, maybe_generate_aci(Res, FoldedTypedAst, Options)};
|
||||
from_string1(fate, ContractString, Options) ->
|
||||
#{fcode := FCode} = string_to_code(ContractString, Options),
|
||||
#{ fcode := FCode
|
||||
, folded_typed_ast := FoldedTypedAst } = string_to_code(ContractString, Options),
|
||||
FateCode = aeso_fcode_to_fate:compile(FCode, Options),
|
||||
pp_assembler(fate, FateCode, Options),
|
||||
ByteCode = aeb_fate_code:serialize(FateCode, []),
|
||||
{ok, Version} = version(),
|
||||
{ok, #{byte_code => ByteCode,
|
||||
compiler_version => Version,
|
||||
contract_source => ContractString,
|
||||
type_info => [],
|
||||
fate_code => FateCode,
|
||||
abi_version => aeb_fate_abi:abi_version(),
|
||||
payable => maps:get(payable, FCode)
|
||||
}}.
|
||||
Res = #{byte_code => ByteCode,
|
||||
compiler_version => Version,
|
||||
contract_source => ContractString,
|
||||
type_info => [],
|
||||
fate_code => FateCode,
|
||||
abi_version => aeb_fate_abi:abi_version(),
|
||||
payable => maps:get(payable, FCode)
|
||||
},
|
||||
{ok, maybe_generate_aci(Res, FoldedTypedAst, Options)}.
|
||||
|
||||
maybe_generate_aci(Result, FoldedTypedAst, Options) ->
|
||||
case proplists:get_value(aci, Options) of
|
||||
undefined ->
|
||||
Result;
|
||||
Type ->
|
||||
{ok, Aci} = aeso_aci:from_typed_ast(Type, FoldedTypedAst),
|
||||
maps:put(aci, Aci, Result)
|
||||
end.
|
||||
|
||||
-spec string_to_code(string(), options()) -> map().
|
||||
string_to_code(ContractString, Options) ->
|
||||
Ast = parse(ContractString, Options),
|
||||
pp_sophia_code(Ast, Options),
|
||||
pp_ast(Ast, Options),
|
||||
{TypeEnv, TypedAst} = aeso_ast_infer_types:infer(Ast, [return_env | Options]),
|
||||
pp_typed_ast(TypedAst, Options),
|
||||
{TypeEnv, FoldedTypedAst, UnfoldedTypedAst} = aeso_ast_infer_types:infer(Ast, [return_env | Options]),
|
||||
pp_typed_ast(UnfoldedTypedAst, Options),
|
||||
case proplists:get_value(backend, Options, aevm) of
|
||||
aevm ->
|
||||
Icode = ast_to_icode(TypedAst, Options),
|
||||
Icode = ast_to_icode(UnfoldedTypedAst, Options),
|
||||
pp_icode(Icode, Options),
|
||||
#{ icode => Icode,
|
||||
typed_ast => TypedAst,
|
||||
type_env => TypeEnv};
|
||||
#{ icode => Icode
|
||||
, unfolded_typed_ast => UnfoldedTypedAst
|
||||
, folded_typed_ast => FoldedTypedAst
|
||||
, type_env => TypeEnv
|
||||
, ast => Ast };
|
||||
fate ->
|
||||
Fcode = aeso_ast_to_fcode:ast_to_fcode(TypedAst, Options),
|
||||
#{ fcode => Fcode,
|
||||
typed_ast => TypedAst,
|
||||
type_env => TypeEnv}
|
||||
Fcode = aeso_ast_to_fcode:ast_to_fcode(UnfoldedTypedAst, Options),
|
||||
#{ fcode => Fcode
|
||||
, unfolded_typed_ast => UnfoldedTypedAst
|
||||
, folded_typed_ast => FoldedTypedAst
|
||||
, type_env => TypeEnv
|
||||
, ast => Ast }
|
||||
end.
|
||||
|
||||
-define(CALL_NAME, "__call").
|
||||
@@ -197,9 +218,9 @@ check_call1(ContractString0, FunName, Args, Options) ->
|
||||
case proplists:get_value(backend, Options, aevm) of
|
||||
aevm ->
|
||||
%% First check the contract without the __call function
|
||||
#{} = string_to_code(ContractString0, Options),
|
||||
ContractString = insert_call_function(ContractString0, ?CALL_NAME, FunName, Args, Options),
|
||||
#{typed_ast := TypedAst,
|
||||
#{ast := Ast} = string_to_code(ContractString0, Options),
|
||||
ContractString = insert_call_function(Ast, ContractString0, ?CALL_NAME, FunName, Args),
|
||||
#{unfolded_typed_ast := TypedAst,
|
||||
icode := Icode} = string_to_code(ContractString, Options),
|
||||
{ok, {FunName, {fun_t, _, _, ArgTypes, RetType}}} = get_call_type(TypedAst),
|
||||
ArgVMTypes = [ aeso_ast_to_icode:ast_typerep(T, Icode) || T <- ArgTypes ],
|
||||
@@ -219,13 +240,14 @@ check_call1(ContractString0, FunName, Args, Options) ->
|
||||
{ok, FunName, {ArgVMTypes, RetVMType1}, ArgTerms};
|
||||
fate ->
|
||||
%% First check the contract without the __call function
|
||||
#{fcode := OrgFcode} = string_to_code(ContractString0, Options),
|
||||
#{ fcode := OrgFcode
|
||||
, ast := Ast } = string_to_code(ContractString0, Options),
|
||||
FateCode = aeso_fcode_to_fate:compile(OrgFcode, []),
|
||||
%% collect all hashes and compute the first name without hash collision to
|
||||
SymbolHashes = maps:keys(aeb_fate_code:symbols(FateCode)),
|
||||
CallName = first_none_match(?CALL_NAME, SymbolHashes,
|
||||
lists:seq($1, $9) ++ lists:seq($A, $Z) ++ lists:seq($a, $z)),
|
||||
ContractString = insert_call_function(ContractString0, CallName, FunName, Args, Options),
|
||||
ContractString = insert_call_function(Ast, ContractString0, CallName, FunName, Args),
|
||||
#{fcode := Fcode} = string_to_code(ContractString, Options),
|
||||
CallArgs = arguments_of_body(CallName, FunName, Fcode),
|
||||
{ok, FunName, CallArgs}
|
||||
@@ -251,9 +273,8 @@ first_none_match(CallName, Hashes, [Char|Chars]) ->
|
||||
end.
|
||||
|
||||
%% Add the __call function to a contract.
|
||||
-spec insert_call_function(string(), string(), string(), [string()], options()) -> string().
|
||||
insert_call_function(Code, Call, FunName, Args, Options) ->
|
||||
Ast = parse(Code, Options),
|
||||
-spec insert_call_function(aeso_syntax:ast(), string(), string(), string(), [string()]) -> string().
|
||||
insert_call_function(Ast, Code, Call, FunName, Args) ->
|
||||
Ind = last_contract_indent(Ast),
|
||||
lists:flatten(
|
||||
[ Code,
|
||||
@@ -309,7 +330,7 @@ to_sophia_value(ContractString, FunName, ok, Data, Options0) ->
|
||||
Options = [no_code | Options0],
|
||||
try
|
||||
Code = string_to_code(ContractString, Options),
|
||||
#{ typed_ast := TypedAst, type_env := TypeEnv} = Code,
|
||||
#{ unfolded_typed_ast := TypedAst, type_env := TypeEnv} = Code,
|
||||
{ok, _, Type0} = get_decode_type(FunName, TypedAst),
|
||||
Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]),
|
||||
|
||||
@@ -335,12 +356,12 @@ to_sophia_value(ContractString, FunName, ok, Data, Options0) ->
|
||||
try
|
||||
{ok, aeso_vm_decode:from_fate(Type, aeb_fate_encoding:deserialize(Data))}
|
||||
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",
|
||||
[aeb_fate_encoding:deserialize(Data), Type1]),
|
||||
{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]),
|
||||
{error, [aeso_errors:new(data_error, Msg)]}
|
||||
end
|
||||
@@ -385,11 +406,11 @@ decode_calldata(ContractString, FunName, Calldata, Options0) ->
|
||||
Options = [no_code | Options0],
|
||||
try
|
||||
Code = string_to_code(ContractString, Options),
|
||||
#{ typed_ast := TypedAst, type_env := TypeEnv} = Code,
|
||||
#{ unfolded_typed_ast := TypedAst, type_env := TypeEnv} = Code,
|
||||
|
||||
{ok, Args, _} = get_decode_type(FunName, TypedAst),
|
||||
DropArg = fun({arg, _, _, T}) -> T; (T) -> T end,
|
||||
ArgTypes = lists:map(DropArg, Args),
|
||||
GetType = fun({typed, _, _, T}) -> T; (T) -> T end,
|
||||
ArgTypes = lists:map(GetType, Args),
|
||||
Type0 = {tuple_t, [], ArgTypes},
|
||||
%% user defined data types such as variants needed to match against
|
||||
Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]),
|
||||
@@ -507,6 +528,14 @@ icode_to_term(T = {map, KT, VT}, M) ->
|
||||
#{};
|
||||
_ -> throw({todo, M})
|
||||
end;
|
||||
icode_to_term(word, {unop, 'bnot', A}) ->
|
||||
bnot icode_to_term(word, A);
|
||||
icode_to_term(word, {binop, 'bor', A, B}) ->
|
||||
icode_to_term(word, A) bor icode_to_term(word, B);
|
||||
icode_to_term(word, {binop, 'bsl', A, B}) ->
|
||||
icode_to_term(word, B) bsl icode_to_term(word, A);
|
||||
icode_to_term(word, {binop, 'band', A, B}) ->
|
||||
icode_to_term(word, A) band icode_to_term(word, B);
|
||||
icode_to_term(typerep, _) ->
|
||||
throw({todo, typerep});
|
||||
icode_to_term(T, V) ->
|
||||
@@ -558,10 +587,92 @@ pp(Code, Options, Option, PPFun) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
%% -- Byte code validation ---------------------------------------------------
|
||||
|
||||
-define(protect(Tag, Code), fun() -> try Code catch _:Err1 -> throw({Tag, Err1}) end end()).
|
||||
|
||||
-spec validate_byte_code(map(), string(), options()) -> ok | {error, [aeso_errors:error()]}.
|
||||
validate_byte_code(#{ byte_code := ByteCode, payable := Payable }, Source, Options) ->
|
||||
Fail = fun(Err) -> {error, [aeso_errors:new(data_error, Err)]} end,
|
||||
case proplists:get_value(backend, Options, aevm) of
|
||||
B when B /= fate -> Fail(io_lib:format("Unsupported backend: ~s\n", [B]));
|
||||
fate ->
|
||||
try
|
||||
FCode1 = ?protect(deserialize, aeb_fate_code:strip_init_function(aeb_fate_code:deserialize(ByteCode))),
|
||||
{FCode2, SrcPayable} =
|
||||
?protect(compile,
|
||||
begin
|
||||
{ok, #{ byte_code := SrcByteCode, payable := SrcPayable }} =
|
||||
from_string1(fate, Source, Options),
|
||||
FCode = aeb_fate_code:deserialize(SrcByteCode),
|
||||
{aeb_fate_code:strip_init_function(FCode), SrcPayable}
|
||||
end),
|
||||
case compare_fate_code(FCode1, FCode2) of
|
||||
ok when SrcPayable /= Payable ->
|
||||
Not = fun(true) -> ""; (false) -> " not" end,
|
||||
Fail(io_lib:format("Byte code contract is~s payable, but source code contract is~s.\n",
|
||||
[Not(Payable), Not(SrcPayable)]));
|
||||
ok -> ok;
|
||||
{error, Why} -> Fail(io_lib:format("Byte code does not match source code.\n~s", [Why]))
|
||||
end
|
||||
catch
|
||||
throw:{deserialize, _} -> Fail("Invalid byte code");
|
||||
throw:{compile, {error, Errs}} -> {error, Errs}
|
||||
end
|
||||
end.
|
||||
|
||||
compare_fate_code(FCode1, FCode2) ->
|
||||
Funs1 = aeb_fate_code:functions(FCode1),
|
||||
Funs2 = aeb_fate_code:functions(FCode2),
|
||||
Syms1 = aeb_fate_code:symbols(FCode1),
|
||||
Syms2 = aeb_fate_code:symbols(FCode2),
|
||||
FunHashes1 = maps:keys(Funs1),
|
||||
FunHashes2 = maps:keys(Funs2),
|
||||
case FunHashes1 == FunHashes2 of
|
||||
false ->
|
||||
InByteCode = [ binary_to_list(maps:get(H, Syms1)) || H <- FunHashes1 -- FunHashes2 ],
|
||||
InSourceCode = [ binary_to_list(maps:get(H, Syms2)) || H <- FunHashes2 -- FunHashes1 ],
|
||||
Msg = [ io_lib:format("- Functions in the byte code but not in the source code:\n"
|
||||
" ~s\n", [string:join(InByteCode, ", ")]) || InByteCode /= [] ] ++
|
||||
[ io_lib:format("- Functions in the source code but not in the byte code:\n"
|
||||
" ~s\n", [string:join(InSourceCode, ", ")]) || InSourceCode /= [] ],
|
||||
{error, Msg};
|
||||
true ->
|
||||
case lists:append([ compare_fate_fun(maps:get(H, Syms1), Fun1, Fun2)
|
||||
|| {{H, Fun1}, {_, Fun2}} <- lists:zip(maps:to_list(Funs1),
|
||||
maps:to_list(Funs2)) ]) of
|
||||
[] -> ok;
|
||||
Errs -> {error, Errs}
|
||||
end
|
||||
end.
|
||||
|
||||
compare_fate_fun(_Name, Fun, Fun) -> [];
|
||||
compare_fate_fun(Name, {Attr, Type, _}, {Attr, Type, _}) ->
|
||||
[io_lib:format("- The implementation of the function ~s is different.\n", [Name])];
|
||||
compare_fate_fun(Name, {Attr1, Type, _}, {Attr2, Type, _}) ->
|
||||
[io_lib:format("- The attributes of the function ~s differ:\n"
|
||||
" Byte code: ~s\n"
|
||||
" Source code: ~s\n",
|
||||
[Name, string:join([ atom_to_list(A) || A <- Attr1 ], ", "),
|
||||
string:join([ atom_to_list(A) || A <- Attr2 ], ", ")])];
|
||||
compare_fate_fun(Name, {_, Type1, _}, {_, Type2, _}) ->
|
||||
[io_lib:format("- The type of the function ~s differs:\n"
|
||||
" Byte code: ~s\n"
|
||||
" Source code: ~s\n",
|
||||
[Name, pp_fate_sig(Type1), pp_fate_sig(Type2)])].
|
||||
|
||||
pp_fate_sig({[Arg], Res}) ->
|
||||
io_lib:format("~s => ~s", [pp_fate_type(Arg), pp_fate_type(Res)]);
|
||||
pp_fate_sig({Args, Res}) ->
|
||||
io_lib:format("(~s) => ~s", [string:join([pp_fate_type(Arg) || Arg <- Args], ", "), pp_fate_type(Res)]).
|
||||
|
||||
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) ->
|
||||
{ok, Ast} = aeso_parser:type(String),
|
||||
Ast = aeso_parser:run_parser(aeso_parser:type(), String),
|
||||
try aeso_ast_to_icode:ast_typerep(Ast) of
|
||||
Type -> {ok, Type}
|
||||
catch _:_ -> {error, bad_type}
|
||||
|
||||
+437
-299
File diff suppressed because it is too large
Load Diff
+24
-20
@@ -73,28 +73,33 @@ new(Options) ->
|
||||
|
||||
builtin_types() ->
|
||||
Word = fun([]) -> word end,
|
||||
#{ "bool" => Word
|
||||
, "int" => Word
|
||||
, "char" => Word
|
||||
, "bits" => Word
|
||||
, "string" => fun([]) -> string end
|
||||
, "address" => Word
|
||||
, "hash" => Word
|
||||
, "unit" => fun([]) -> {tuple, []} end
|
||||
, "signature" => fun([]) -> {tuple, [word, word]} end
|
||||
, "oracle" => fun([_, _]) -> word end
|
||||
, "oracle_query" => fun([_, _]) -> word end
|
||||
, "list" => fun([A]) -> {list, A} end
|
||||
, "option" => fun([A]) -> {variant, [[], [A]]} end
|
||||
, "map" => fun([K, V]) -> map_typerep(K, V) end
|
||||
, ["Chain", "ttl"] => fun([]) -> {variant, [[word], [word]]} end
|
||||
#{ "bool" => Word
|
||||
, "int" => Word
|
||||
, "char" => Word
|
||||
, "bits" => Word
|
||||
, "string" => fun([]) -> string end
|
||||
, "address" => Word
|
||||
, "hash" => Word
|
||||
, "unit" => fun([]) -> {tuple, []} end
|
||||
, "signature" => fun([]) -> {tuple, [word, word]} end
|
||||
, "oracle" => fun([_, _]) -> word end
|
||||
, "oracle_query" => fun([_, _]) -> word end
|
||||
, "list" => fun([A]) -> {list, A} end
|
||||
, "option" => fun([A]) -> {variant, [[], [A]]} end
|
||||
, "map" => fun([K, V]) -> map_typerep(K, V) end
|
||||
, ["Chain", "ttl"] => fun([]) -> {variant, [[word], [word]]} end
|
||||
, ["AENS", "pointee"] => fun([]) -> {variant, [[word], [word], [word]]} end
|
||||
}.
|
||||
|
||||
builtin_constructors() ->
|
||||
#{ ["RelativeTTL"] => 0
|
||||
, ["FixedTTL"] => 1
|
||||
, ["None"] => 0
|
||||
, ["Some"] => 1 }.
|
||||
#{ ["RelativeTTL"] => 0
|
||||
, ["FixedTTL"] => 1
|
||||
, ["None"] => 0
|
||||
, ["Some"] => 1
|
||||
, ["AccountPointee"] => 0
|
||||
, ["OraclePointee"] => 1
|
||||
, ["ContractPointee"] => 2
|
||||
}.
|
||||
|
||||
map_typerep(K, V) ->
|
||||
{map, K, V}.
|
||||
@@ -146,4 +151,3 @@ get_constructor_tag(Name, #{constructors := Constructors}) ->
|
||||
undefined -> error({undefined_constructor, Name});
|
||||
Tag -> Tag
|
||||
end.
|
||||
|
||||
|
||||
+94
-44
@@ -3,20 +3,33 @@
|
||||
%%% Description :
|
||||
%%% Created : 1 Mar 2018 by Ulf Norell
|
||||
-module(aeso_parser).
|
||||
-compile({no_auto_import,[map_get/2]}).
|
||||
|
||||
-export([string/1,
|
||||
string/2,
|
||||
string/3,
|
||||
auto_imports/1,
|
||||
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").
|
||||
-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()}.
|
||||
|
||||
|
||||
escape_errors({ok, Ok}) ->
|
||||
Ok;
|
||||
escape_errors({error, Err}) ->
|
||||
parse_error(Err).
|
||||
|
||||
-spec string(string()) -> parse_result().
|
||||
string(String) ->
|
||||
string(String, sets:new(), []).
|
||||
@@ -30,27 +43,24 @@ string(String, Opts) ->
|
||||
|
||||
-spec string(string(), sets:set(include_hash()), aeso_compiler:options()) -> parse_result().
|
||||
string(String, Included, Opts) ->
|
||||
case parse_and_scan(file(), String, Opts) of
|
||||
{ok, AST} ->
|
||||
case expand_includes(AST, Included, Opts) of
|
||||
{ok, AST1} -> AST1;
|
||||
{error, Err} -> parse_error(Err)
|
||||
end;
|
||||
{error, Err} ->
|
||||
parse_error(Err)
|
||||
AST = run_parser(file(), String, Opts),
|
||||
case expand_includes(AST, Included, Opts) of
|
||||
{ok, AST1} -> AST1;
|
||||
{error, Err} -> parse_error(Err)
|
||||
end.
|
||||
|
||||
type(String) ->
|
||||
case parse_and_scan(type(), String, []) of
|
||||
{ok, AST} -> {ok, AST};
|
||||
{error, Err} -> {error, [mk_error(Err)]}
|
||||
end.
|
||||
|
||||
run_parser(P, Inp) ->
|
||||
escape_errors(parse_and_scan(P, Inp, [])).
|
||||
run_parser(P, Inp, Opts) ->
|
||||
escape_errors(parse_and_scan(P, Inp, Opts)).
|
||||
|
||||
parse_and_scan(P, S, Opts) ->
|
||||
set_current_file(proplists:get_value(src_file, Opts, no_file)),
|
||||
case aeso_scan:scan(S) of
|
||||
{ok, Tokens} -> aeso_parse_lib:parse(P, Tokens);
|
||||
Error -> Error
|
||||
{error, {{Input, Pos}, _}} ->
|
||||
{error, {Pos, scan_error, Input}}
|
||||
end.
|
||||
|
||||
-dialyzer({nowarn_function, parse_error/1}).
|
||||
@@ -60,8 +70,8 @@ parse_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, scan_error, Input}) ->
|
||||
mk_p_err(Pos, io_lib:format("Lexical error on input: ~s\n", [Input]));
|
||||
mk_error({Pos, parse_error, Err}) ->
|
||||
Msg = io_lib:format("~s\n", [Err]),
|
||||
mk_p_err(Pos, Msg);
|
||||
@@ -100,11 +110,19 @@ decl() ->
|
||||
, ?RULE(keyword(datatype), id(), type_vars(), tok('='), typedef(variant), {type_def, _1, _2, _3, _5})
|
||||
|
||||
%% Function declarations
|
||||
, ?RULE(modifiers(), fun_or_entry(), id(), tok(':'), type(), add_modifiers(_1, _2, {fun_decl, get_ann(_2), _3, _5}))
|
||||
, ?RULE(modifiers(), fun_or_entry(), fundef(), add_modifiers(_1, _2, set_pos(get_pos(get_ann(_2)), _3)))
|
||||
, ?RULE(keyword('let'), valdef(), set_pos(get_pos(_1), _2))
|
||||
, ?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))
|
||||
])).
|
||||
|
||||
fun_block(Mods, Kind, [Decl]) ->
|
||||
add_modifiers(Mods, Kind, set_pos(get_pos(Kind), Decl));
|
||||
fun_block(Mods, Kind, Decls) ->
|
||||
{block, get_ann(Kind), [ add_modifiers(Mods, Kind, Decl) || Decl <- Decls ]}.
|
||||
|
||||
fundef_or_decl() ->
|
||||
choice([?RULE(id(), tok(':'), type(), {fun_decl, get_ann(_1), _1, _3}),
|
||||
fundef()]).
|
||||
|
||||
pragma() ->
|
||||
Op = choice([token(T) || T <- ['<', '=<', '==', '>=', '>']]),
|
||||
?RULE(tok('@'), id("compiler"), Op, version(), {pragma, get_ann(_1), {compiler, element(1, _3), _4}}).
|
||||
@@ -116,7 +134,7 @@ mk_version({int, _, Maj}, Rest) ->
|
||||
[Maj | [N || {_, {int, _, N}} <- Rest]].
|
||||
|
||||
fun_or_entry() ->
|
||||
choice([?RULE(keyword(function), {function, _1}),
|
||||
choice([?RULE(keyword(function), {function, _1}),
|
||||
?RULE(keyword(entrypoint), {entrypoint, _1})]).
|
||||
|
||||
modifiers() ->
|
||||
@@ -163,20 +181,19 @@ letdecl() ->
|
||||
letdef() -> choice(valdef(), fundef()).
|
||||
|
||||
valdef() ->
|
||||
choice(
|
||||
?RULE(id(), tok('='), body(), {letval, [], _1, type_wildcard(), _3}),
|
||||
?RULE(id(), tok(':'), type(), tok('='), body(), {letval, [], _1, _3, _5})).
|
||||
?RULE(pattern(), tok('='), body(), {letval, [], _1, _3}).
|
||||
|
||||
fundef() ->
|
||||
choice(
|
||||
[ ?RULE(id(), args(), tok('='), body(), {letfun, [], _1, _2, type_wildcard(), _4})
|
||||
, ?RULE(id(), args(), tok(':'), type(), tok('='), body(), {letfun, [], _1, _2, _4, _6})
|
||||
[ ?RULE(id(), args(), tok('='), body(), {letfun, get_ann(_1), _1, _2, type_wildcard(get_ann(_1)), _4})
|
||||
, ?RULE(id(), args(), tok(':'), type(), tok('='), body(), {letfun, get_ann(_1), _1, _2, _4, _6})
|
||||
]).
|
||||
|
||||
args() -> paren_list(arg()).
|
||||
args() -> paren_list(pattern()).
|
||||
lam_args() -> paren_list(arg()).
|
||||
|
||||
arg() -> choice(
|
||||
?RULE(id(), {arg, get_ann(_1), _1, type_wildcard()}),
|
||||
?RULE(id(), {arg, get_ann(_1), _1, type_wildcard(get_ann(_1))}),
|
||||
?RULE(id(), tok(':'), type(), {arg, get_ann(_1), _1, _3})).
|
||||
|
||||
%% -- Types ------------------------------------------------------------------
|
||||
@@ -237,7 +254,7 @@ branch() ->
|
||||
?RULE(pattern(), keyword('=>'), body(), {'case', _2, _1, _3}).
|
||||
|
||||
pattern() ->
|
||||
?LET_P(E, expr500(), parse_pattern(E)).
|
||||
?LET_P(E, expr(), parse_pattern(E)).
|
||||
|
||||
%% -- Expressions ------------------------------------------------------------
|
||||
|
||||
@@ -247,7 +264,7 @@ expr100() ->
|
||||
Expr100 = ?LAZY_P(expr100()),
|
||||
Expr200 = ?LAZY_P(expr200()),
|
||||
choice(
|
||||
[ ?RULE(args(), keyword('=>'), body(), {lam, _2, _1, _3}) %% TODO: better location
|
||||
[ ?RULE(lam_args(), keyword('=>'), body(), {lam, _2, _1, _3}) %% TODO: better location
|
||||
, {'if', keyword('if'), parens(Expr100), Expr200, right(tok(else), Expr100)}
|
||||
, ?RULE(Expr200, optional(right(tok(':'), type())),
|
||||
case _2 of
|
||||
@@ -296,7 +313,7 @@ comprehension_if() ->
|
||||
?RULE(keyword('if'), parens(expr()), {comprehension_if, _1, _2}).
|
||||
|
||||
comprehension_bind() ->
|
||||
?RULE(id(), tok('<-'), expr(), {comprehension_bind, _1, _3}).
|
||||
?RULE(pattern(), tok('<-'), expr(), {comprehension_bind, _1, _3}).
|
||||
|
||||
arg_expr() ->
|
||||
?LAZY_P(
|
||||
@@ -348,7 +365,9 @@ record(Fs) ->
|
||||
bad_expr_err("Cannot use '@' in map construction", infix({lvalue, FAnn, LV}, {'@', Ann}, Id));
|
||||
({field, FAnn, LV, _}) ->
|
||||
bad_expr_err("Cannot use nested fields or keys in map construction", {lvalue, FAnn, LV}) end,
|
||||
{map, Ann, lists:map(KV, Fs)}
|
||||
{map, Ann, lists:map(KV, Fs)};
|
||||
record_or_map_error ->
|
||||
{record_or_map_error, get_ann(hd(Fs)), Fs}
|
||||
end.
|
||||
|
||||
record_or_map(Fields) ->
|
||||
@@ -360,9 +379,7 @@ record_or_map(Fields) ->
|
||||
case lists:usort(lists:map(Kind, Fields)) of
|
||||
[proj] -> record;
|
||||
[map_get] -> map;
|
||||
_ ->
|
||||
[{field, Ann, _, _} | _] = Fields,
|
||||
bad_expr_err("Mixed record fields and map keys in", {record, Ann, Fields})
|
||||
_ -> record_or_map_error %% Defer error until type checking
|
||||
end.
|
||||
|
||||
field_assignment() ->
|
||||
@@ -493,8 +510,8 @@ infix(L, Op, R) -> set_ann(format, infix, {app, get_ann(L), Op, [L, R]}).
|
||||
prefixes(Ops, E) -> lists:foldr(fun prefix/2, E, Ops).
|
||||
prefix(Op, E) -> set_ann(format, prefix, {app, get_ann(Op), Op, [E]}).
|
||||
|
||||
type_wildcard() ->
|
||||
{id, [{origin, system}], "_"}.
|
||||
type_wildcard(Ann) ->
|
||||
{id, [{origin, system} | Ann], "_"}.
|
||||
|
||||
block_e(Stmts) ->
|
||||
group_ifs(Stmts, []).
|
||||
@@ -544,7 +561,9 @@ list_comp_e(Ann, Expr, Binds) -> {list_comp, Ann, Expr, Binds}.
|
||||
-spec parse_pattern(aeso_syntax:expr()) -> aeso_parse_lib:parser(aeso_syntax:pat()).
|
||||
parse_pattern({app, Ann, Con = {'::', _}, Es}) ->
|
||||
{app, Ann, Con, lists:map(fun parse_pattern/1, Es)};
|
||||
parse_pattern({app, Ann, Con = {con, _, _}, Es}) ->
|
||||
parse_pattern({app, Ann, {'-', _}, [{int, _, N}]}) ->
|
||||
{int, Ann, -N};
|
||||
parse_pattern({app, Ann, Con = {Tag, _, _}, Es}) when Tag == con; Tag == qcon ->
|
||||
{app, Ann, Con, lists:map(fun parse_pattern/1, Es)};
|
||||
parse_pattern({tuple, Ann, Es}) ->
|
||||
{tuple, Ann, lists:map(fun parse_pattern/1, Es)};
|
||||
@@ -552,7 +571,10 @@ parse_pattern({list, Ann, Es}) ->
|
||||
{list, Ann, lists:map(fun parse_pattern/1, Es)};
|
||||
parse_pattern({record, Ann, Fs}) ->
|
||||
{record, Ann, lists:map(fun parse_field_pattern/1, Fs)};
|
||||
parse_pattern({typed, Ann, E, Type}) ->
|
||||
{typed, Ann, parse_pattern(E), Type};
|
||||
parse_pattern(E = {con, _, _}) -> E;
|
||||
parse_pattern(E = {qcon, _, _}) -> E;
|
||||
parse_pattern(E = {id, _, _}) -> E;
|
||||
parse_pattern(E = {int, _, _}) -> E;
|
||||
parse_pattern(E = {bool, _, _}) -> E;
|
||||
@@ -583,8 +605,13 @@ expand_includes(AST, Included, Opts) ->
|
||||
|| File <- lists:usort(auto_imports(AST)) ] ++ AST,
|
||||
expand_includes(AST1, Included, [], Opts).
|
||||
|
||||
expand_includes([], _Included, Acc, _Opts) ->
|
||||
{ok, lists:reverse(Acc)};
|
||||
expand_includes([], Included, Acc, Opts) ->
|
||||
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) ->
|
||||
case get_include_code(File, Ann, Opts) of
|
||||
{ok, Code} ->
|
||||
@@ -618,16 +645,39 @@ read_file(File, Opts) ->
|
||||
case maps:get(binary_to_list(File), Files, not_found) of
|
||||
not_found -> {error, not_found};
|
||||
Src -> {ok, Src}
|
||||
end;
|
||||
escript ->
|
||||
try
|
||||
Escript = escript:script_name(),
|
||||
{ok, Sections} = escript:extract(Escript, []),
|
||||
Archive = proplists:get_value(archive, Sections),
|
||||
FileName = binary_to_list(filename:join([aesophia, priv, stdlib, File])),
|
||||
case zip:extract(Archive, [{file_list, [FileName]}, memory]) of
|
||||
{ok, [{_, Src}]} -> {ok, Src};
|
||||
_ -> {error, not_found}
|
||||
end
|
||||
catch _:_ ->
|
||||
{error, not_found}
|
||||
end
|
||||
end.
|
||||
|
||||
stdlib_options() ->
|
||||
[{include, {file_system, [aeso_stdlib:stdlib_include_path()]}}].
|
||||
StdLibDir = aeso_stdlib:stdlib_include_path(),
|
||||
case filelib:is_dir(StdLibDir) of
|
||||
true -> [{include, {file_system, [StdLibDir]}}];
|
||||
false -> [{include, escript}]
|
||||
end.
|
||||
|
||||
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, _}} ->
|
||||
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, binary_to_list(Bin)};
|
||||
{{ok, Bin}, _} ->
|
||||
|
||||
+45
-23
@@ -145,8 +145,12 @@ decl(D, Options) ->
|
||||
with_options(Options, fun() -> decl(D) end).
|
||||
|
||||
-spec decl(aeso_syntax:decl()) -> doc().
|
||||
decl({contract, _, C, Ds}) ->
|
||||
block(follow(text("contract"), hsep(name(C), text("="))), decls(Ds));
|
||||
decl({contract, Attrs, C, 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}) ->
|
||||
block(follow(text("namespace"), hsep(name(C), text("="))), decls(Ds));
|
||||
decl({pragma, _, Pragma}) -> pragma(Pragma);
|
||||
@@ -155,13 +159,16 @@ decl({type_def, _, T, Vars, Def}) ->
|
||||
Kind = element(1, Def),
|
||||
equals(typedecl(Kind, T, Vars), typedef(Def));
|
||||
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
|
||||
true -> text("entrypoint");
|
||||
false -> text("function")
|
||||
end,
|
||||
hsep(Fun, typed(name(F), T));
|
||||
hsep(lists:map(Mod, Ann) ++ [Fun, typed(name(F), T)]);
|
||||
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));
|
||||
(_) -> empty() end,
|
||||
Fun = case aeso_syntax:get_ann(entrypoint, Attrs, false) of
|
||||
@@ -169,7 +176,11 @@ decl(D = {letfun, Attrs, _, _, _, _}) ->
|
||||
false -> "function"
|
||||
end,
|
||||
hsep(lists:map(Mod, Attrs) ++ [letdecl(Fun, D)]);
|
||||
decl(D = {letval, _, _, _, _}) -> letdecl("let", D).
|
||||
decl({fun_clauses, Ann, Name, Type, Clauses}) ->
|
||||
above([ decl(D) || D <- [{fun_decl, Ann, Name, Type} | Clauses] ]);
|
||||
decl(D = {letval, _, _, _}) -> letdecl("let", D);
|
||||
decl({block, _, Ds}) ->
|
||||
above([ decl(D) || D <- Ds ]).
|
||||
|
||||
-spec pragma(aeso_syntax:pragma()) -> doc().
|
||||
pragma({compiler, Op, Ver}) ->
|
||||
@@ -193,10 +204,10 @@ name({tvar, _, Name}) -> text(Name);
|
||||
name({typed, _, Name, _}) -> name(Name).
|
||||
|
||||
-spec letdecl(string(), aeso_syntax:letbind()) -> doc().
|
||||
letdecl(Let, {letval, _, F, T, E}) ->
|
||||
block_expr(0, hsep([text(Let), typed(name(F), T), text("=")]), E);
|
||||
letdecl(Let, {letval, _, P, E}) ->
|
||||
block_expr(0, hsep([text(Let), expr(P), text("=")]), E);
|
||||
letdecl(Let, {letfun, _, F, Args, T, E}) ->
|
||||
block_expr(0, hsep([text(Let), typed(beside(name(F), args(Args)), T), text("=")]), E).
|
||||
block_expr(0, hsep([text(Let), typed(beside(name(F), expr({tuple, [], Args})), T), text("=")]), E).
|
||||
|
||||
-spec args([aeso_syntax:arg()]) -> doc().
|
||||
args(Args) ->
|
||||
@@ -253,6 +264,8 @@ type({args_t, _, Args}) ->
|
||||
type({bytes_t, _, any}) -> text("bytes(_)");
|
||||
type({bytes_t, _, Len}) ->
|
||||
text(lists:concat(["bytes(", Len, ")"]));
|
||||
type({if_t, _, Id, Then, Else}) ->
|
||||
beside(text("if"), args_type([Id, Then, Else]));
|
||||
type({named_arg_t, _, Name, Type, _Default}) ->
|
||||
%% Drop the default value
|
||||
%% follow(hsep(typed(name(Name), Type), text("=")), expr(Default));
|
||||
@@ -279,12 +292,9 @@ tuple_type(Factors) ->
|
||||
, text(")")
|
||||
]).
|
||||
|
||||
-spec arg_expr(aeso_syntax:arg_expr()) -> doc().
|
||||
arg_expr({named_arg, _, Name, E}) ->
|
||||
follow(hsep(expr(Name), text("=")), expr(E));
|
||||
arg_expr(E) -> expr(E).
|
||||
|
||||
-spec expr_p(integer(), aeso_syntax:expr()) -> doc().
|
||||
-spec expr_p(integer(), aeso_syntax:arg_expr()) -> doc().
|
||||
expr_p(P, {named_arg, _, Name, E}) ->
|
||||
paren(P > 100, follow(hsep(expr(Name), text("=")), expr(E)));
|
||||
expr_p(P, {lam, _, Args, E}) ->
|
||||
paren(P > 100, follow(hsep(args(Args), text("=>")), expr_p(100, E)));
|
||||
expr_p(P, If = {'if', Ann, Cond, Then, Else}) ->
|
||||
@@ -305,6 +315,8 @@ expr_p(_, {tuple, _, Es}) ->
|
||||
tuple(lists:map(fun expr/1, Es));
|
||||
expr_p(_, {list, _, Es}) ->
|
||||
list(lists:map(fun expr/1, Es));
|
||||
expr_p(_, {list_comp, _, E, Binds}) ->
|
||||
list([follow(expr(E), hsep(text("|"), par(punctuate(text(","), lists:map(fun lc_bind/1, Binds)), 0)), 0)]);
|
||||
expr_p(_, {record, _, Fs}) ->
|
||||
record(lists:map(fun field/1, Fs));
|
||||
expr_p(_, {map, Ann, KVs}) ->
|
||||
@@ -358,13 +370,19 @@ expr_p(_, {Type, _, Bin})
|
||||
Type == oracle_query_id ->
|
||||
text(binary_to_list(aeser_api_encoder:encode(Type, Bin)));
|
||||
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}) ->
|
||||
case C of
|
||||
$' -> text("'\\''");
|
||||
$" -> text("'\"'");
|
||||
_ -> S = lists:flatten(io_lib:format("~p", [[C]])),
|
||||
text("'" ++ tl(lists:droplast(S)) ++ "'")
|
||||
_ when C < 16#80 ->
|
||||
S = lists:flatten(io_lib:format("~p", [[C]])),
|
||||
text("'" ++ tl(lists:droplast(S)) ++ "'");
|
||||
_ ->
|
||||
S = lists:flatten(
|
||||
io_lib:format("'~ts'", [list_to_binary(aeso_scan:utf8_encode([C]))])),
|
||||
text(S)
|
||||
end;
|
||||
%% -- Names
|
||||
expr_p(_, E = {id, _, _}) -> name(E);
|
||||
@@ -387,6 +405,13 @@ stmt_p({else, Else}) ->
|
||||
_ -> block_expr(200, text("else"), Else)
|
||||
end.
|
||||
|
||||
lc_bind({comprehension_bind, P, E}) ->
|
||||
follow(hsep(expr(P), text("<-")), expr(E));
|
||||
lc_bind({comprehension_if, _, E}) ->
|
||||
beside([text("if("), expr(E), text(")")]);
|
||||
lc_bind(Let) ->
|
||||
letdecl("let", Let).
|
||||
|
||||
-spec bin_prec(aeso_syntax:bin_op()) -> {integer(), integer(), integer()}.
|
||||
bin_prec('..') -> { 0, 0, 0}; %% Always printed inside '[ ]'
|
||||
bin_prec('=') -> { 0, 0, 0}; %% Always printed inside '[ ]'
|
||||
@@ -429,7 +454,7 @@ prefix(P, Op, A) ->
|
||||
app(P, F, Args) ->
|
||||
paren(P > 900,
|
||||
beside(expr_p(900, F),
|
||||
tuple(lists:map(fun arg_expr/1, Args)))).
|
||||
tuple(lists:map(fun expr/1, Args)))).
|
||||
|
||||
field({field, _, LV, E}) ->
|
||||
follow(hsep(lvalue(LV), text("=")), expr(E));
|
||||
@@ -450,7 +475,7 @@ elim1(Get={map_get, _, _}) -> elim(Get);
|
||||
elim1(Get={map_get, _, _, _}) -> elim(Get).
|
||||
|
||||
alt({'case', _, Pat, Body}) ->
|
||||
block_expr(0, hsep(expr_p(500, Pat), text("=>")), Body).
|
||||
block_expr(0, hsep(expr(Pat), text("=>")), Body).
|
||||
|
||||
block_expr(_, Header, {block, _, Ss}) ->
|
||||
block(Header, statements(Ss));
|
||||
@@ -460,7 +485,7 @@ block_expr(P, Header, E) ->
|
||||
statements(Stmts) ->
|
||||
above([ statement(S) || S <- Stmts ]).
|
||||
|
||||
statement(S = {letval, _, _, _, _}) -> letdecl("let", S);
|
||||
statement(S = {letval, _, _, _}) -> letdecl("let", S);
|
||||
statement(S = {letfun, _, _, _, _, _}) -> letdecl("let", S);
|
||||
statement(E) -> expr(E).
|
||||
|
||||
@@ -473,6 +498,3 @@ get_elifs(If = {'if', Ann, Cond, Then, Else}, Elifs) ->
|
||||
end;
|
||||
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]).
|
||||
|
||||
|
||||
+32
-26
@@ -7,7 +7,7 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeso_scan).
|
||||
|
||||
-export([scan/1]).
|
||||
-export([scan/1, utf8_encode/1]).
|
||||
|
||||
-import(aeso_scan_lib, [token/1, token/2, symbol/0, skip/0,
|
||||
override/2, push/2, pop/1]).
|
||||
@@ -28,7 +28,13 @@ lexer() ->
|
||||
QID = ["(", CON, "\\.)+", ID],
|
||||
QCON = ["(", CON, "\\.)+", CON],
|
||||
OP = "[=!<>+\\-*/:&|?~@^]+",
|
||||
CHAR = "'([^'\\\\]|(\\\\.))'",
|
||||
%% Five cases for a character
|
||||
%% * 1 7-bit ascii, not \ or '
|
||||
%% * 2-4 8-bit values (UTF8)
|
||||
%% * \ followed by a known modifier [aernrtv]
|
||||
%% * \xhh
|
||||
%% * \x{hhh...}
|
||||
CHAR = "'(([\\x00-\\x26\\x28-\\x5b\\x5d-\\x7f])|([\\x00-\\xff][\\x80-\\xff]{1,3})|(\\\\[befnrtv'\\\\])|(\\\\x[0-9a-fA-F]{2,2})|(\\\\x\\{[0-9a-fA-F]*\\}))'",
|
||||
STRING = "\"([^\"\\\\]|(\\\\.))*\"",
|
||||
|
||||
CommentStart = {"/\\*", push(comment, skip())},
|
||||
@@ -77,34 +83,34 @@ scan(String) ->
|
||||
%% -- Helpers ----------------------------------------------------------------
|
||||
|
||||
parse_string([$" | Chars]) ->
|
||||
unescape(Chars).
|
||||
unicode:characters_to_nfc_binary(unescape(Chars)).
|
||||
|
||||
parse_char([$', $\\, Code, $']) ->
|
||||
case Code of
|
||||
$' -> $';
|
||||
$\\ -> $\\;
|
||||
$b -> $\b;
|
||||
$e -> $\e;
|
||||
$f -> $\f;
|
||||
$n -> $\n;
|
||||
$r -> $\r;
|
||||
$t -> $\t;
|
||||
$v -> $\v;
|
||||
_ -> {error, "Bad control sequence: \\" ++ [Code]}
|
||||
end;
|
||||
parse_char([$', C, $']) -> C.
|
||||
parse_char([$' | Chars]) ->
|
||||
case unicode:characters_to_nfc_list(unescape($', Chars, [])) of
|
||||
[Char] -> Char;
|
||||
_Bad -> {error, "Bad character literal: '" ++ Chars}
|
||||
end.
|
||||
|
||||
unescape(Str) -> unescape(Str, []).
|
||||
utf8_encode(Cs) ->
|
||||
binary_to_list(unicode:characters_to_binary(Cs)).
|
||||
|
||||
unescape([$"], Acc) ->
|
||||
unescape(Str) -> unescape($", Str, []).
|
||||
|
||||
unescape(Delim, [Delim], Acc) ->
|
||||
list_to_binary(lists:reverse(Acc));
|
||||
unescape([$\\, $x, D1, D2 | Chars ], Acc) ->
|
||||
unescape(Delim, [$\\, $x, ${ | Chars ], Acc) ->
|
||||
{Ds, [_ | Cs]} = lists:splitwith(fun($}) -> false ; (_) -> true end, Chars),
|
||||
C = list_to_integer(Ds, 16),
|
||||
Utf8Cs = binary_to_list(unicode:characters_to_binary([C])),
|
||||
unescape(Delim, Cs, [Utf8Cs | Acc]);
|
||||
unescape(Delim, [$\\, $x, D1, D2 | Chars ], Acc) ->
|
||||
C = list_to_integer([D1, D2], 16),
|
||||
unescape(Chars, [C | Acc]);
|
||||
unescape([$\\, Code | Chars], Acc) ->
|
||||
Ok = fun(C) -> unescape(Chars, [C | Acc]) end,
|
||||
Utf8Cs = binary_to_list(unicode:characters_to_binary([C])),
|
||||
unescape(Delim, Chars, [Utf8Cs | Acc]);
|
||||
unescape(Delim, [$\\, Code | Chars], Acc) ->
|
||||
Ok = fun(C) -> unescape(Delim, Chars, [C | Acc]) end,
|
||||
case Code of
|
||||
$" -> Ok($");
|
||||
Delim -> Ok(Delim);
|
||||
$\\ -> Ok($\\);
|
||||
$b -> Ok($\b);
|
||||
$e -> Ok($\e);
|
||||
@@ -115,8 +121,8 @@ unescape([$\\, Code | Chars], Acc) ->
|
||||
$v -> Ok($\v);
|
||||
_ -> error("Bad control sequence: \\" ++ [Code]) %% TODO
|
||||
end;
|
||||
unescape([C | Chars], Acc) ->
|
||||
unescape(Chars, [C | Acc]).
|
||||
unescape(Delim, [C | Chars], Acc) ->
|
||||
unescape(Delim, Chars, [C | Acc]).
|
||||
|
||||
strip_underscores(S) ->
|
||||
lists:filter(fun(C) -> C /= $_ end, S).
|
||||
|
||||
+19
-9
@@ -37,18 +37,26 @@
|
||||
-type decl() :: {contract, ann(), con(), [decl()]}
|
||||
| {namespace, ann(), con(), [decl()]}
|
||||
| {pragma, ann(), pragma()}
|
||||
| {type_decl, ann(), id(), [tvar()]}
|
||||
| {type_decl, ann(), id(), [tvar()]} % Only for error msgs
|
||||
| {type_def, ann(), id(), [tvar()], typedef()}
|
||||
| {fun_decl, ann(), id(), type()}
|
||||
| letbind().
|
||||
| {fun_clauses, ann(), id(), type(), [letfun() | fundecl()]}
|
||||
| {block, ann(), [decl()]}
|
||||
| fundecl()
|
||||
| letfun()
|
||||
| letval(). % Only for error msgs
|
||||
|
||||
-type compiler_version() :: [non_neg_integer()].
|
||||
|
||||
-type pragma() :: {compiler, '==' | '<' | '>' | '=<' | '>=', compiler_version()}.
|
||||
|
||||
|
||||
-type letval() :: {letval, ann(), pat(), expr()}.
|
||||
-type letfun() :: {letfun, ann(), id(), [pat()], type(), expr()}.
|
||||
-type fundecl() :: {fun_decl, ann(), id(), type()}.
|
||||
|
||||
-type letbind()
|
||||
:: {letval, ann(), id(), type(), expr()}
|
||||
| {letfun, ann(), id(), [arg()], type(), expr()}.
|
||||
:: letfun()
|
||||
| letval().
|
||||
|
||||
-type arg() :: {arg, ann(), id(), type()}.
|
||||
|
||||
@@ -100,9 +108,8 @@
|
||||
| {list, ann(), [expr()]}
|
||||
| {list_comp, ann(), expr(), [comprehension_exp()]}
|
||||
| {typed, ann(), expr(), type()}
|
||||
| {record, ann(), [field(expr())]}
|
||||
| {record, ann(), expr(), [field(expr())]} %% record update
|
||||
| {map, ann(), expr(), [field(expr())]} %% map update
|
||||
| {record_or_map(), ann(), [field(expr())]}
|
||||
| {record_or_map(), ann(), expr(), [field(expr())]} %% record/map update
|
||||
| {map, ann(), [{expr(), expr()}]}
|
||||
| {map_get, ann(), expr(), expr()}
|
||||
| {map_get, ann(), expr(), expr(), expr()}
|
||||
@@ -111,7 +118,9 @@
|
||||
| id() | qid() | con() | qcon()
|
||||
| constant().
|
||||
|
||||
-type comprehension_exp() :: [ {comprehension_bind, id(), expr()}
|
||||
-type record_or_map() :: record | map | record_or_map_error.
|
||||
|
||||
-type comprehension_exp() :: [ {comprehension_bind, pat(), expr()}
|
||||
| {comprehension_if, ann(), expr()}
|
||||
| letbind() ].
|
||||
|
||||
@@ -139,6 +148,7 @@
|
||||
-type pat() :: {app, ann(), con() | op(), [pat()]}
|
||||
| {tuple, ann(), [pat()]}
|
||||
| {list, ann(), [pat()]}
|
||||
| {typed, ann(), pat(), type()}
|
||||
| {record, ann(), [field(pat())]}
|
||||
| constant()
|
||||
| con()
|
||||
|
||||
@@ -45,11 +45,11 @@ fold(Alg = #alg{zero = Zero, plus = Plus, scoped = Scoped}, Fun, K, X) ->
|
||||
%% decl()
|
||||
{contract, _, _, Ds} -> Decl(Ds);
|
||||
{namespace, _, _, Ds} -> Decl(Ds);
|
||||
{type_decl, _, I, _} -> BindType(I);
|
||||
{type_def, _, I, _, D} -> Plus(BindType(I), Decl(D));
|
||||
{fun_decl, _, _, T} -> Type(T);
|
||||
{letval, _, F, T, E} -> Sum([BindExpr(F), Type(T), Expr(E)]);
|
||||
{letval, _, P, E} -> Scoped(BindExpr(P), Expr(E));
|
||||
{letfun, _, F, Xs, T, E} -> Sum([BindExpr(F), Type(T), Expr(Xs ++ [E])]);
|
||||
{fun_clauses, _, _, T, Cs} -> Sum([Type(T) | [Decl(C) || C <- Cs]]);
|
||||
%% typedef()
|
||||
{alias_t, T} -> Type(T);
|
||||
{record_t, Fs} -> Type(Fs);
|
||||
@@ -76,8 +76,8 @@ fold(Alg = #alg{zero = Zero, plus = Plus, scoped = Scoped}, Fun, K, X) ->
|
||||
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 = {letval, _, Pat, _} | R]} ->
|
||||
Plus(Decl(D), Scoped(BindExpr(Pat), 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));
|
||||
|
||||
+29
-5
@@ -18,9 +18,14 @@ from_aevm(word, {id, _, "address"}, N) -> address_literal(ac
|
||||
from_aevm(word, {app_t, _, {id, _, "oracle"}, _}, N) -> address_literal(oracle_pubkey, N);
|
||||
from_aevm(word, {app_t, _, {id, _, "oracle_query"}, _}, N) -> address_literal(oracle_query_id, N);
|
||||
from_aevm(word, {con, _, _Name}, N) -> address_literal(contract_pubkey, N);
|
||||
from_aevm(word, {id, _, "int"}, N) -> <<N1:256/signed>> = <<N:256>>, {int, [], N1};
|
||||
from_aevm(word, {id, _, "bits"}, N) -> error({todo, bits, N});
|
||||
from_aevm(word, {id, _, "bool"}, N) -> {bool, [], N /= 0};
|
||||
from_aevm(word, {id, _, "int"}, N0) ->
|
||||
<<N:256/signed>> = <<N0:256>>,
|
||||
if N < 0 -> {app, [{format, prefix}], {'-', []}, [{int, [], -N}]};
|
||||
true -> {int, [], N} end;
|
||||
from_aevm(word, {id, _, "bits"}, N0) ->
|
||||
<<N:256/signed>> = <<N0:256>>,
|
||||
make_bits(N);
|
||||
from_aevm(word, {id, _, "bool"}, N) -> {bool, [], N /= 0};
|
||||
from_aevm(word, {bytes_t, _, Len}, Val) when Len =< 32 ->
|
||||
<<Bytes:Len/unit:8, _/binary>> = <<Val:32/unit:8>>,
|
||||
{bytes, [], <<Bytes:Len/unit:8>>};
|
||||
@@ -55,6 +60,7 @@ from_aevm({variant, VmCons}, {variant_t, Cons}, {variant, Tag, Args})
|
||||
VmTypes = lists:nth(Tag + 1, VmCons),
|
||||
ConType = lists:nth(Tag + 1, Cons),
|
||||
from_aevm(VmTypes, ConType, Args);
|
||||
from_aevm([], {constr_t, _, Con, []}, []) -> Con;
|
||||
from_aevm(VmTypes, {constr_t, _, Con, Types}, Args)
|
||||
when length(VmTypes) == length(Types),
|
||||
length(VmTypes) == length(Args) ->
|
||||
@@ -70,8 +76,10 @@ from_fate({app_t, _, {id, _, "oracle"}, _}, ?FATE_ORACLE(Bin)) -> {oracle_pubkey
|
||||
from_fate({app_t, _, {id, _, "oracle_query"}, _}, ?FATE_ORACLE_Q(Bin)) -> {oracle_query_id, [], Bin};
|
||||
from_fate({con, _, _Name}, ?FATE_CONTRACT(Bin)) -> {contract_pubkey, [], Bin};
|
||||
from_fate({bytes_t, _, N}, ?FATE_BYTES(Bin)) when byte_size(Bin) == N -> {bytes, [], Bin};
|
||||
from_fate({id, _, "bits"}, ?FATE_BITS(Bin)) -> error({todo, bits, Bin});
|
||||
from_fate({id, _, "int"}, N) when is_integer(N) -> {int, [], N};
|
||||
from_fate({id, _, "bits"}, ?FATE_BITS(N)) -> make_bits(N);
|
||||
from_fate({id, _, "int"}, N) when is_integer(N) ->
|
||||
if N < 0 -> {app, [{format, prefix}], {'-', []}, [{int, [], -N}]};
|
||||
true -> {int, [], N} end;
|
||||
from_fate({id, _, "bool"}, B) when is_boolean(B) -> {bool, [], B};
|
||||
from_fate({id, _, "string"}, S) when is_binary(S) -> {string, [], S};
|
||||
from_fate({app_t, _, {id, _, "list"}, [Type]}, List) when is_list(List) ->
|
||||
@@ -87,6 +95,8 @@ from_fate({tuple_t, _, Types}, ?FATE_TUPLE(Val))
|
||||
when length(Types) == tuple_size(Val) ->
|
||||
{tuple, [], [from_fate(Type, X)
|
||||
|| {Type, X} <- lists:zip(Types, tuple_to_list(Val))]};
|
||||
from_fate({record_t, [{field_t, _, FName, FType}]}, Val) ->
|
||||
{record, [], [{field, [], [{proj, [], FName}], from_fate(FType, Val)}]};
|
||||
from_fate({record_t, Fields}, ?FATE_TUPLE(Val))
|
||||
when length(Fields) == tuple_size(Val) ->
|
||||
{record, [], [ {field, [], [{proj, [], FName}], from_fate(FType, X)}
|
||||
@@ -105,9 +115,23 @@ from_fate({variant_t, Cons}, {variant, Ar, Tag, Args})
|
||||
from_fate(ConType, ArgList);
|
||||
_ -> throw(cannot_translate_to_sophia)
|
||||
end;
|
||||
from_fate({constr_t, _, Con, []}, []) -> Con;
|
||||
from_fate({constr_t, _, Con, Types}, Args)
|
||||
when length(Types) == length(Args) ->
|
||||
{app, [], Con, [ from_fate(Type, Arg)
|
||||
|| {Type, Arg} <- lists:zip(Types, Args) ]};
|
||||
from_fate(_Type, _Data) ->
|
||||
throw(cannot_translate_to_sophia).
|
||||
|
||||
|
||||
make_bits(N) ->
|
||||
Id = fun(F) -> {qid, [], ["Bits", F]} end,
|
||||
if N < 0 -> make_bits(Id("clear"), Id("all"), 0, bnot N);
|
||||
true -> make_bits(Id("set"), Id("none"), 0, N) end.
|
||||
|
||||
make_bits(_Set, Zero, _I, 0) -> Zero;
|
||||
make_bits(Set, Zero, I, N) when 0 == N rem 2 ->
|
||||
make_bits(Set, Zero, I + 1, N div 2);
|
||||
make_bits(Set, Zero, I, N) ->
|
||||
{app, [], Set, [make_bits(Set, Zero, I + 1, N div 2), {int, [], I}]}.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{application, aesophia,
|
||||
[{description, "Contract Language for aeternity"},
|
||||
{vsn, "4.0.0"},
|
||||
{vsn, "4.3.0"},
|
||||
{registered, []},
|
||||
{applications,
|
||||
[kernel,
|
||||
|
||||
@@ -62,7 +62,7 @@ encode_decode_sophia_test() ->
|
||||
Other -> Other
|
||||
end end,
|
||||
ok = Check("int", "42"),
|
||||
ok = Check("int", "-42"),
|
||||
ok = Check("int", "- 42"),
|
||||
ok = Check("bool", "true"),
|
||||
ok = Check("bool", "false"),
|
||||
ok = Check("string", "\"Hello\""),
|
||||
|
||||
+18
-11
@@ -11,7 +11,10 @@ test_contract(N) ->
|
||||
{Contract,MapACI,DecACI} = test_cases(N),
|
||||
{ok,JSON} = aeso_aci:contract_interface(json, Contract),
|
||||
?assertEqual([MapACI], JSON),
|
||||
?assertEqual({ok, DecACI}, aeso_aci:render_aci_json(JSON)).
|
||||
?assertEqual({ok, DecACI}, aeso_aci:render_aci_json(JSON)),
|
||||
%% Check if the compiler provides correct aci
|
||||
{ok,#{aci := JSON2}} = aeso_compiler:from_string(Contract, [{aci, json}]),
|
||||
?assertEqual(JSON, JSON2).
|
||||
|
||||
test_cases(1) ->
|
||||
Contract = <<"payable contract C =\n"
|
||||
@@ -80,11 +83,11 @@ test_cases(3) ->
|
||||
DecACI = <<"contract C =\n"
|
||||
" type state = unit\n"
|
||||
" datatype event = SingleEventDefined\n"
|
||||
" datatype bert('a) = Bin('a)\n"
|
||||
" entrypoint a : (C.bert(string)) => int\n">>,
|
||||
" datatype bert('a) = Bin('a)\n"
|
||||
" entrypoint a : (C.bert(string)) => int\n">>,
|
||||
{Contract,MapACI,DecACI}.
|
||||
|
||||
%% Rounttrip
|
||||
%% Roundtrip
|
||||
aci_test_() ->
|
||||
[{"Testing ACI generation for " ++ ContractName,
|
||||
fun() -> aci_test_contract(ContractName) end}
|
||||
@@ -94,8 +97,13 @@ all_contracts() -> aeso_compiler_tests:compilable_contracts().
|
||||
|
||||
aci_test_contract(Name) ->
|
||||
String = aeso_test_utils:read_contract(Name),
|
||||
Opts = [{include, {file_system, [aeso_test_utils:contract_path()]}}],
|
||||
Opts = case lists:member(Name, aeso_compiler_tests:debug_mode_contracts()) of
|
||||
true -> [debug_mode];
|
||||
false -> []
|
||||
end ++ [{include, {file_system, [aeso_test_utils:contract_path()]}}],
|
||||
{ok, JSON} = aeso_aci:contract_interface(json, String, Opts),
|
||||
{ok, #{aci := JSON1}} = aeso_compiler:from_string(String, [{aci, json}, {backend, fate} | Opts]),
|
||||
?assertEqual(JSON, JSON1),
|
||||
|
||||
io:format("JSON:\n~p\n", [JSON]),
|
||||
{ok, ContractStub} = aeso_aci:render_aci_json(JSON),
|
||||
@@ -106,7 +114,7 @@ aci_test_contract(Name) ->
|
||||
ok.
|
||||
|
||||
check_stub(Stub, Options) ->
|
||||
case aeso_parser:string(binary_to_list(Stub), Options) of
|
||||
try aeso_parser:string(binary_to_list(Stub), Options) of
|
||||
Ast ->
|
||||
try
|
||||
%% io:format("AST: ~120p\n", [Ast]),
|
||||
@@ -117,9 +125,8 @@ check_stub(Stub, Options) ->
|
||||
_:R ->
|
||||
io:format("Error: ~p\n", [R]),
|
||||
error(R)
|
||||
end;
|
||||
{error, E} ->
|
||||
io:format("Error: ~p\n", [E]),
|
||||
error({parse_error, E})
|
||||
end
|
||||
catch throw:{error, Errs} ->
|
||||
_ = [ io:format("~s\n", [aeso_errors:pp(E)]) || E <- Errs ],
|
||||
error({parse_errors, Errs})
|
||||
end.
|
||||
|
||||
|
||||
@@ -29,11 +29,10 @@ calldata_test_() ->
|
||||
true -> ast_exprs(ContractString, Fun, Args, [{backend, fate}]);
|
||||
false -> undefined
|
||||
end,
|
||||
case FateExprs == undefined orelse AevmExprs == undefined of
|
||||
true -> ok;
|
||||
false ->
|
||||
?assertEqual(FateExprs, AevmExprs)
|
||||
end
|
||||
ParsedExprs = parse_args(Fun, Args),
|
||||
[ ?assertEqual(ParsedExprs, AevmExprs) || AevmExprs /= undefined ],
|
||||
[ ?assertEqual(ParsedExprs, FateExprs) || FateExprs /= undefined ],
|
||||
ok
|
||||
end} || {ContractName, Fun, Args} <- compilable_contracts()].
|
||||
|
||||
calldata_aci_test_() ->
|
||||
@@ -53,19 +52,34 @@ calldata_aci_test_() ->
|
||||
true -> ast_exprs(ContractACI, Fun, Args, [{backend, fate}]);
|
||||
false -> undefined
|
||||
end,
|
||||
case FateExprs == undefined orelse AevmExprs == undefined of
|
||||
true -> ok;
|
||||
false ->
|
||||
?assertEqual(FateExprs, AevmExprs)
|
||||
end
|
||||
ParsedExprs = parse_args(Fun, Args),
|
||||
[ ?assertEqual(ParsedExprs, AevmExprs) || AevmExprs /= undefined ],
|
||||
[ ?assertEqual(ParsedExprs, FateExprs) || FateExprs /= undefined ],
|
||||
ok
|
||||
end} || {ContractName, Fun, Args} <- compilable_contracts()].
|
||||
|
||||
parse_args(Fun, Args) ->
|
||||
[{contract, _, _, [{letfun, _, _, _, _, {app, _, _, AST}}]}] =
|
||||
aeso_parser:string("contract Temp = function foo() = " ++ Fun ++ "(" ++ string:join(Args, ", ") ++ ")"),
|
||||
strip_ann(AST).
|
||||
|
||||
strip_ann(T) when is_tuple(T) ->
|
||||
strip_ann1(setelement(2, T, []));
|
||||
strip_ann(X) -> strip_ann1(X).
|
||||
|
||||
strip_ann1({map, [], KVs}) ->
|
||||
{map, [], [{strip_ann(K), strip_ann(V)} || {K, V} <- KVs]};
|
||||
strip_ann1(T) when is_tuple(T) ->
|
||||
list_to_tuple(strip_ann1(tuple_to_list(T)));
|
||||
strip_ann1(L) when is_list(L) ->
|
||||
lists:map(fun strip_ann/1, L);
|
||||
strip_ann1(X) -> X.
|
||||
|
||||
ast_exprs(ContractString, Fun, Args, Opts) ->
|
||||
{ok, Data} = (catch aeso_compiler:create_calldata(ContractString, Fun, Args, Opts)),
|
||||
{ok, _Types, Exprs} = (catch aeso_compiler:decode_calldata(ContractString, Fun, Data, Opts)),
|
||||
?assert(is_list(Exprs)),
|
||||
Exprs.
|
||||
strip_ann(Exprs).
|
||||
|
||||
check_errors(Expect, ErrorString) ->
|
||||
%% This removes the final single \n as well.
|
||||
@@ -85,7 +99,9 @@ compilable_contracts() ->
|
||||
{"maps", "init", []},
|
||||
{"funargs", "menot", ["false"]},
|
||||
{"funargs", "append", ["[\"false\", \" is\", \" not\", \" true\"]"]},
|
||||
%% TODO {"funargs", "bitsum", ["Bits.all"]},
|
||||
{"funargs", "bitsum", ["Bits.all"]},
|
||||
{"funargs", "bitsum", ["Bits.clear(Bits.clear(Bits.all, 4), 2)"]}, %% Order matters for test
|
||||
{"funargs", "bitsum", ["Bits.set(Bits.set(Bits.none, 4), 2)"]},
|
||||
{"funargs", "read", ["{label = \"question 1\", result = 4}"]},
|
||||
{"funargs", "sjutton", ["#0011012003100011012003100011012003"]},
|
||||
{"funargs", "sextiosju", ["#01020304050607080910111213141516171819202122232425262728293031323334353637383940"
|
||||
@@ -97,6 +113,7 @@ compilable_contracts() ->
|
||||
{"funargs", "traffic_light", ["Pantone(12)"]},
|
||||
{"funargs", "tuples", ["()"]},
|
||||
%% TODO {"funargs", "due", ["FixedTTL(1020)"]},
|
||||
{"funargs", "singleton_rec", ["{x = 1000}"]},
|
||||
{"variant_types", "init", []},
|
||||
{"basic_auth", "init", []},
|
||||
{"address_literals", "init", []},
|
||||
@@ -126,4 +143,4 @@ compilable_contracts() ->
|
||||
not_yet_compilable(fate) ->
|
||||
[];
|
||||
not_yet_compilable(aevm) ->
|
||||
[].
|
||||
["funargs", "strings"].
|
||||
|
||||
+276
-87
@@ -12,6 +12,14 @@
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
run_test(Test) ->
|
||||
TestFun = list_to_atom(lists:concat([Test, "_test_"])),
|
||||
[ begin
|
||||
io:format("~s\n", [Label]),
|
||||
Fun()
|
||||
end || {Label, Fun} <- ?MODULE:TestFun() ],
|
||||
ok.
|
||||
|
||||
%% Very simply test compile the given contracts. Only basic checks
|
||||
%% are made on the output, just that it is a binary which indicates
|
||||
%% that the compilation worked.
|
||||
@@ -31,7 +39,7 @@ simple_compile_test_() ->
|
||||
error(ErrBin)
|
||||
end
|
||||
end} || ContractName <- compilable_contracts(), Backend <- [aevm, fate],
|
||||
not lists:member(ContractName, not_yet_compilable(Backend))] ++
|
||||
not lists:member(ContractName, not_compilable_on(Backend))] ++
|
||||
[ {"Test file not found error",
|
||||
fun() ->
|
||||
{error, Errors} = aeso_compiler:file("does_not_exist.aes"),
|
||||
@@ -102,7 +110,15 @@ compile(Backend, Name) ->
|
||||
|
||||
compile(Backend, Name, Options) ->
|
||||
String = aeso_test_utils:read_contract(Name),
|
||||
case aeso_compiler:from_string(String, [{src_file, Name ++ ".aes"}, {backend, Backend} | Options]) of
|
||||
Options1 =
|
||||
case lists:member(Name, debug_mode_contracts()) of
|
||||
true -> [debug_mode];
|
||||
false -> []
|
||||
end ++
|
||||
[ {src_file, Name ++ ".aes"}, {backend, Backend}
|
||||
, {include, {file_system, [aeso_test_utils:contract_path()]}}
|
||||
] ++ Options,
|
||||
case aeso_compiler:from_string(String, Options1) of
|
||||
{ok, Map} -> Map;
|
||||
{error, ErrorString} when is_binary(ErrorString) -> ErrorString;
|
||||
{error, Errors} -> Errors
|
||||
@@ -130,6 +146,7 @@ compilable_contracts() ->
|
||||
"test",
|
||||
"builtin_bug",
|
||||
"builtin_map_get_bug",
|
||||
"lc_record_bug",
|
||||
"nodeadcode",
|
||||
"deadcode",
|
||||
"variant_types",
|
||||
@@ -137,6 +154,7 @@ compilable_contracts() ->
|
||||
"events",
|
||||
"include",
|
||||
"basic_auth",
|
||||
"basic_auth_tx",
|
||||
"bitcoin_auth",
|
||||
"address_literals",
|
||||
"bytes_equality",
|
||||
@@ -145,6 +163,7 @@ compilable_contracts() ->
|
||||
"bytes_to_x",
|
||||
"bytes_concat",
|
||||
"aens",
|
||||
"aens_update",
|
||||
"tuple_match",
|
||||
"cyclic_include",
|
||||
"stdlib_include",
|
||||
@@ -153,11 +172,27 @@ compilable_contracts() ->
|
||||
"list_comp",
|
||||
"payable",
|
||||
"unapplied_builtins",
|
||||
"underscore_number_literals"
|
||||
"underscore_number_literals",
|
||||
"pairing_crypto",
|
||||
"qualified_constructor",
|
||||
"let_patterns",
|
||||
"lhs_matching",
|
||||
"more_strings",
|
||||
"protected_call",
|
||||
"hermetization_turnoff"
|
||||
].
|
||||
|
||||
not_yet_compilable(fate) -> [];
|
||||
not_yet_compilable(aevm) -> [].
|
||||
not_compilable_on(fate) -> [];
|
||||
not_compilable_on(aevm) ->
|
||||
[ "stdlib_include", "manual_stdlib_include", "pairing_crypto"
|
||||
, "aens_update", "basic_auth_tx", "more_strings"
|
||||
, "unapplied_builtins", "bytes_to_x", "state_handling", "protected_call"
|
||||
, "hermetization_turnoff"
|
||||
|
||||
].
|
||||
|
||||
debug_mode_contracts() ->
|
||||
["hermetization_turnoff"].
|
||||
|
||||
%% Contracts that should produce type errors
|
||||
|
||||
@@ -288,9 +323,22 @@ failing_contracts() ->
|
||||
"Repeated name x in pattern\n"
|
||||
" x :: x (at line 26, column 7)">>,
|
||||
<<?Pos(44, 14)
|
||||
"Repeated argument x to function repeated_arg (at line 44, column 14).">>,
|
||||
<<?Pos(44, 14)
|
||||
"Repeated argument y to function repeated_arg (at line 44, column 14).">>,
|
||||
"Repeated names x, y in pattern\n"
|
||||
" (x : int, y, x : string, y : bool) (at line 44, column 14)">>,
|
||||
<<?Pos(44, 39)
|
||||
"Cannot unify int\n"
|
||||
" and string\n"
|
||||
"when checking the type of the expression at line 44, column 39\n"
|
||||
" x : int\n"
|
||||
"against the expected type\n"
|
||||
" string">>,
|
||||
<<?Pos(44, 72)
|
||||
"Cannot unify int\n"
|
||||
" and string\n"
|
||||
"when checking the type of the expression at line 44, column 72\n"
|
||||
" x : int\n"
|
||||
"against the expected type\n"
|
||||
" string">>,
|
||||
<<?Pos(14, 24)
|
||||
"No record type with fields y, z (at line 14, column 24)">>,
|
||||
<<?Pos(15, 26)
|
||||
@@ -346,83 +394,21 @@ failing_contracts() ->
|
||||
[<<?Pos(12, 42)
|
||||
"Cannot unify int\n"
|
||||
" and string\n"
|
||||
"when checking the record projection at line 12, column 42\n"
|
||||
" r.foo : (gas : int, value : int) => Remote.themap\n"
|
||||
"when checking the type of the expression at line 12, column 42\n"
|
||||
" r.foo() : map(int, string)\n"
|
||||
"against the expected type\n"
|
||||
" (gas : int, value : int) => map(string, int)">>])
|
||||
, ?TYPE_ERROR(bad_include_and_ns,
|
||||
[<<?Pos(2, 11)
|
||||
"Include of 'included.aes' at line 2, column 11\nnot allowed, include only allowed at top level.">>,
|
||||
<<?Pos(3, 13)
|
||||
"Nested namespace not allowed\nNamespace 'Foo' at line 3, column 13 not defined at top level.">>])
|
||||
" map(string, int)">>])
|
||||
, ?TYPE_ERROR(not_toplevel_include,
|
||||
[<<?Pos(2, 11)
|
||||
"Include of 'included.aes' at line 2, column 11\nnot allowed, include only allowed at top level.">>])
|
||||
, ?TYPE_ERROR(not_toplevel_namespace,
|
||||
[<<?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,
|
||||
[<<?Pos(32, 5)
|
||||
"The type bytes(32) is not a contract type\n"
|
||||
"when checking that the contract literal\n"
|
||||
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
|
||||
"has the type\n"
|
||||
" bytes(32)">>,
|
||||
<<?Pos(30, 5)
|
||||
"The type oracle(int, bool) is not a contract type\n"
|
||||
"when checking that the contract literal\n"
|
||||
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
|
||||
"has the type\n"
|
||||
" oracle(int, bool)">>,
|
||||
<<?Pos(28, 5)
|
||||
"The type address is not a contract type\n"
|
||||
"when checking that the contract literal\n"
|
||||
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
|
||||
"has the type\n"
|
||||
" address">>,
|
||||
<<?Pos(25, 5)
|
||||
"Cannot unify oracle_query('a, 'b)\n"
|
||||
" and Remote\n"
|
||||
"when checking the type of the expression at line 25, column 5\n"
|
||||
" oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY :\n"
|
||||
" oracle_query('a, 'b)\n"
|
||||
"against the expected type\n"
|
||||
" Remote">>,
|
||||
<<?Pos(23, 5)
|
||||
"Cannot unify oracle_query('c, 'd)\n"
|
||||
" and bytes(32)\n"
|
||||
"when checking the type of the expression at line 23, column 5\n"
|
||||
" oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY :\n"
|
||||
" oracle_query('c, 'd)\n"
|
||||
"against the expected type\n"
|
||||
" bytes(32)">>,
|
||||
<<?Pos(21, 5)
|
||||
"Cannot unify oracle_query('e, 'f)\n"
|
||||
" and oracle(int, bool)\n"
|
||||
"when checking the type of the expression at line 21, column 5\n"
|
||||
" oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY :\n"
|
||||
" oracle_query('e, 'f)\n"
|
||||
"against the expected type\n"
|
||||
" oracle(int, bool)">>,
|
||||
<<?Pos(18, 5)
|
||||
"Cannot unify oracle('g, 'h)\n"
|
||||
" and Remote\n"
|
||||
"when checking the type of the expression at line 18, column 5\n"
|
||||
" ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 :\n"
|
||||
" oracle('g, 'h)\n"
|
||||
"against the expected type\n"
|
||||
" Remote">>,
|
||||
<<?Pos(16, 5)
|
||||
"Cannot unify oracle('i, 'j)\n"
|
||||
" and bytes(32)\n"
|
||||
"when checking the type of the expression at line 16, column 5\n"
|
||||
" ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 :\n"
|
||||
" oracle('i, 'j)\n"
|
||||
"against the expected type\n"
|
||||
" bytes(32)">>,
|
||||
<<?Pos(14, 5)
|
||||
"Cannot unify oracle('k, 'l)\n"
|
||||
" and oracle_query(int, bool)\n"
|
||||
"when checking the type of the expression at line 14, column 5\n"
|
||||
" ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 :\n"
|
||||
" oracle('k, 'l)\n"
|
||||
"against the expected type\n"
|
||||
" oracle_query(int, bool)">>,
|
||||
<<?Pos(11, 5)
|
||||
[<<?Pos(11, 5)
|
||||
"Cannot unify address\n"
|
||||
" and oracle(int, bool)\n"
|
||||
"when checking the type of the expression at line 11, column 5\n"
|
||||
@@ -443,6 +429,72 @@ failing_contracts() ->
|
||||
" ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt : address\n"
|
||||
"against the expected type\n"
|
||||
" bytes(32)">>,
|
||||
<<?Pos(14, 5)
|
||||
"Cannot unify oracle('a, 'b)\n"
|
||||
" and oracle_query(int, bool)\n"
|
||||
"when checking the type of the expression at line 14, column 5\n"
|
||||
" ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 :\n"
|
||||
" oracle('a, 'b)\n"
|
||||
"against the expected type\n"
|
||||
" oracle_query(int, bool)">>,
|
||||
<<?Pos(16, 5)
|
||||
"Cannot unify oracle('c, 'd)\n"
|
||||
" and bytes(32)\n"
|
||||
"when checking the type of the expression at line 16, column 5\n"
|
||||
" ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 :\n"
|
||||
" oracle('c, 'd)\n"
|
||||
"against the expected type\n"
|
||||
" bytes(32)">>,
|
||||
<<?Pos(18, 5)
|
||||
"Cannot unify oracle('e, 'f)\n"
|
||||
" and Remote\n"
|
||||
"when checking the type of the expression at line 18, column 5\n"
|
||||
" ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 :\n"
|
||||
" oracle('e, 'f)\n"
|
||||
"against the expected type\n"
|
||||
" Remote">>,
|
||||
<<?Pos(21, 5)
|
||||
"Cannot unify oracle_query('g, 'h)\n"
|
||||
" and oracle(int, bool)\n"
|
||||
"when checking the type of the expression at line 21, column 5\n"
|
||||
" oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY :\n"
|
||||
" oracle_query('g, 'h)\n"
|
||||
"against the expected type\n"
|
||||
" oracle(int, bool)">>,
|
||||
<<?Pos(23, 5)
|
||||
"Cannot unify oracle_query('i, 'j)\n"
|
||||
" and bytes(32)\n"
|
||||
"when checking the type of the expression at line 23, column 5\n"
|
||||
" oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY :\n"
|
||||
" oracle_query('i, 'j)\n"
|
||||
"against the expected type\n"
|
||||
" bytes(32)">>,
|
||||
<<?Pos(25, 5)
|
||||
"Cannot unify oracle_query('k, 'l)\n"
|
||||
" and Remote\n"
|
||||
"when checking the type of the expression at line 25, column 5\n"
|
||||
" oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY :\n"
|
||||
" oracle_query('k, 'l)\n"
|
||||
"against the expected type\n"
|
||||
" Remote">>,
|
||||
<<?Pos(28, 5)
|
||||
"The type address is not a contract type\n"
|
||||
"when checking that the contract literal\n"
|
||||
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
|
||||
"has the type\n"
|
||||
" address">>,
|
||||
<<?Pos(30, 5)
|
||||
"The type oracle(int, bool) is not a contract type\n"
|
||||
"when checking that the contract literal\n"
|
||||
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
|
||||
"has the type\n"
|
||||
" oracle(int, bool)">>,
|
||||
<<?Pos(32, 5)
|
||||
"The type bytes(32) is not a contract type\n"
|
||||
"when checking that the contract literal\n"
|
||||
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
|
||||
"has the type\n"
|
||||
" bytes(32)">>,
|
||||
<<?Pos(34, 5),
|
||||
"The type address is not a contract type\n"
|
||||
"when checking that the call to\n"
|
||||
@@ -510,11 +562,15 @@ failing_contracts() ->
|
||||
"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)
|
||||
[<<?Pos(5, 47)
|
||||
"Invalid key type\n"
|
||||
" map(int, int)\n"
|
||||
"Map keys cannot contain other maps.">>,
|
||||
<<?Pos(6, 25)
|
||||
<<?Pos(6, 31)
|
||||
"Invalid key type\n"
|
||||
" list(map(int, int))\n"
|
||||
"Map keys cannot contain other maps.">>,
|
||||
<<?Pos(6, 31)
|
||||
"Invalid key type\n"
|
||||
" lm\n"
|
||||
"Map keys cannot contain other maps.">>])
|
||||
@@ -558,7 +614,7 @@ failing_contracts() ->
|
||||
"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"
|
||||
" - 'e (at line 12, column 25)\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"
|
||||
@@ -571,7 +627,7 @@ failing_contracts() ->
|
||||
" - '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)">>])
|
||||
" - 'a (at line 18, column 37)">>])
|
||||
, ?TYPE_ERROR(wrong_compiler_version,
|
||||
[<<?Pos(1, 1)
|
||||
"Cannot compile with this version of the compiler,\n"
|
||||
@@ -587,6 +643,93 @@ failing_contracts() ->
|
||||
[<<?Pos(5, 28)
|
||||
"Invalid call to contract entrypoint 'Foo.foo'.\n"
|
||||
"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,
|
||||
[<<?Pos(3, 16)
|
||||
"Mixed record fields and map keys in\n"
|
||||
" {x = 0, [0] = 1}">>,
|
||||
<<?Pos(4, 6)
|
||||
"Mixed record fields and map keys in\n"
|
||||
" r {x = 0, [0] = 1}">>,
|
||||
<<?Pos(5, 6)
|
||||
"Empty record/map update\n"
|
||||
" r {}">>
|
||||
])
|
||||
, ?TYPE_ERROR(bad_protected_call,
|
||||
[<<?Pos(6, 22)
|
||||
"Invalid 'protected' argument\n"
|
||||
" (0 : int) == (1 : int) : bool\n"
|
||||
"It must be either 'true' or 'false'.">>
|
||||
])
|
||||
, ?TYPE_ERROR(bad_function_block,
|
||||
[<<?Pos(4, 5)
|
||||
"Mismatch in the function block. Expected implementation/type declaration of g function">>,
|
||||
<<?Pos(5, 5)
|
||||
"Mismatch in the function block. Expected implementation/type declaration of g function">>
|
||||
])
|
||||
, ?TYPE_ERROR(just_an_empty_file,
|
||||
[<<?Pos(0, 0)
|
||||
"Empty contract">>
|
||||
])
|
||||
, ?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).
|
||||
@@ -702,3 +845,49 @@ failing_code_gen_contracts() ->
|
||||
"The state cannot contain functions in the AEVM. Use FATE if you need this.")
|
||||
].
|
||||
|
||||
validation_test_() ->
|
||||
[{"Validation fail: " ++ C1 ++ " /= " ++ C2,
|
||||
fun() ->
|
||||
Actual = case validate(C1, C2) of
|
||||
{error, Errs} -> Errs;
|
||||
ok -> #{}
|
||||
end,
|
||||
check_errors(Expect, Actual)
|
||||
end} || {C1, C2, Expect} <- validation_fails()] ++
|
||||
[{"Validation of " ++ C,
|
||||
fun() ->
|
||||
?assertEqual(ok, validate(C, C))
|
||||
end} || C <- compilable_contracts()].
|
||||
|
||||
validation_fails() ->
|
||||
[{"deadcode", "nodeadcode",
|
||||
[<<"Data error:\n"
|
||||
"Byte code does not match source code.\n"
|
||||
"- Functions in the source code but not in the byte code:\n"
|
||||
" .MyList.map2">>]},
|
||||
{"validation_test1", "validation_test2",
|
||||
[<<"Data error:\n"
|
||||
"Byte code does not match source code.\n"
|
||||
"- The implementation of the function code_fail is different.\n"
|
||||
"- The attributes of the function attr_fail differ:\n"
|
||||
" Byte code: payable\n"
|
||||
" Source code: \n"
|
||||
"- The type of the function type_fail differs:\n"
|
||||
" Byte code: integer => integer\n"
|
||||
" Source code: {tvar,0} => {tvar,0}">>]},
|
||||
{"validation_test1", "validation_test3",
|
||||
[<<"Data error:\n"
|
||||
"Byte code contract is not payable, but source code contract is.">>]}].
|
||||
|
||||
validate(Contract1, Contract2) ->
|
||||
ByteCode = #{ fate_code := FCode } = compile(fate, Contract1),
|
||||
FCode1 = aeb_fate_code:serialize(aeb_fate_code:strip_init_function(FCode)),
|
||||
Source = aeso_test_utils:read_contract(Contract2),
|
||||
aeso_compiler:validate_byte_code(
|
||||
ByteCode#{ byte_code := FCode1 }, Source,
|
||||
case lists:member(Contract2, debug_mode_contracts()) of
|
||||
true -> [debug_mode];
|
||||
false -> []
|
||||
end ++
|
||||
[{backend, fate}, {include, {file_system, [aeso_test_utils:contract_path()]}}]).
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
id(X) -> X.
|
||||
|
||||
simple_contracts_test_() ->
|
||||
{foreach,
|
||||
fun() -> ok end,
|
||||
@@ -14,7 +16,7 @@ simple_contracts_test_() ->
|
||||
" function id(x) = x\n",
|
||||
?assertMatch(
|
||||
[{contract, _, {con, _, "Identity"},
|
||||
[{letfun, _, {id, _, "id"}, [{arg, _, {id, _, "x"}, {id, _, "_"}}], {id, _, "_"},
|
||||
[{letfun, _, {id, _, "id"}, [{id, _, "x"}], {id, _, "_"},
|
||||
{id, _, "x"}}]}], parse_string(Text)),
|
||||
ok
|
||||
end},
|
||||
@@ -30,7 +32,7 @@ simple_contracts_test_() ->
|
||||
end,
|
||||
Parse = fun(S) ->
|
||||
try remove_line_numbers(parse_expr(S))
|
||||
catch _:_ -> ?assertMatch(ok, {parse_fail, S}) end
|
||||
catch _:_ -> ?assertMatch(ok, id({parse_fail, S})) end
|
||||
end,
|
||||
CheckParens = fun(Expr) ->
|
||||
?assertEqual(Parse(NoPar(Expr)), Parse(Par(Expr)))
|
||||
@@ -38,7 +40,6 @@ simple_contracts_test_() ->
|
||||
LeftAssoc = fun(Op) -> CheckParens({{a, Op, b}, Op, c}) end,
|
||||
RightAssoc = fun(Op) -> CheckParens({a, Op, {b, Op, c}}) end,
|
||||
NonAssoc = fun(Op) ->
|
||||
OpAtom = list_to_atom(Op),
|
||||
?assertThrow({error, [_]},
|
||||
parse_expr(NoPar({a, Op, {b, Op, c}}))) end,
|
||||
Stronger = fun(Op1, Op2) ->
|
||||
@@ -62,7 +63,8 @@ simple_contracts_test_() ->
|
||||
%% Parse tests of example contracts
|
||||
[ {lists:concat(["Parse the ", Contract, " contract."]),
|
||||
fun() -> roundtrip_contract(Contract) end}
|
||||
|| Contract <- [counter, voting, all_syntax, '05_greeter', aeproof, multi_sig, simple_storage, fundme, dutch_auction] ]
|
||||
|| Contract <- [counter, voting, all_syntax, '05_greeter', aeproof,
|
||||
multi_sig, simple_storage, fundme, dutch_auction, utf8] ]
|
||||
}.
|
||||
|
||||
parse_contract(Name) ->
|
||||
@@ -77,14 +79,14 @@ parse_string(Text, Opts) ->
|
||||
aeso_parser:string(Text, Opts).
|
||||
|
||||
parse_expr(Text) ->
|
||||
[{letval, _, _, _, Expr}] =
|
||||
[{letval, _, _, Expr}] =
|
||||
parse_string("let _ = " ++ Text),
|
||||
Expr.
|
||||
|
||||
round_trip(Text) ->
|
||||
Contract = parse_string(Text),
|
||||
Text1 = prettypr:format(aeso_pretty:decls(strip_stdlib(Contract))),
|
||||
Contract1 = parse_string(Text1),
|
||||
Contract1 = parse_string(aeso_scan:utf8_encode(Text1)),
|
||||
NoSrcLoc = remove_line_numbers(Contract),
|
||||
NoSrcLoc1 = remove_line_numbers(Contract1),
|
||||
?assertMatch(NoSrcLoc, diff(NoSrcLoc, NoSrcLoc1)).
|
||||
|
||||
@@ -58,8 +58,7 @@ contract Greeter =
|
||||
|
||||
let state = { greeting = "Hello" }
|
||||
|
||||
let setGreeting =
|
||||
(greeting: string) =>
|
||||
function setGreeting(greeting: string) =
|
||||
state{ greeting = greeting }
|
||||
|
||||
|
||||
|
||||
+16
-1
@@ -33,7 +33,22 @@ contract AENSTest =
|
||||
sign : signature) : unit =
|
||||
AENS.claim(addr, name, salt, name_fee, signature = sign)
|
||||
|
||||
// TODO: update() -- how to handle pointers?
|
||||
|
||||
stateful entrypoint update(owner : address,
|
||||
name : string,
|
||||
ttl : option(Chain.ttl),
|
||||
client_ttl : option(int),
|
||||
pointers : option(map(string, AENS.pointee))) : unit =
|
||||
AENS.update(owner, name, ttl, client_ttl, pointers)
|
||||
|
||||
stateful entrypoint signedUpdate(owner : address,
|
||||
name : string,
|
||||
ttl : option(Chain.ttl),
|
||||
client_ttl : option(int),
|
||||
pointers : option(map(string, AENS.pointee)),
|
||||
sign : signature) : unit =
|
||||
AENS.update(owner, name, ttl, client_ttl, pointers, signature = sign)
|
||||
|
||||
|
||||
stateful entrypoint transfer(owner : address,
|
||||
new_owner : address,
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
contract AENSUpdate =
|
||||
stateful entrypoint update_name(owner : address, name : string) =
|
||||
let p1 : AENS.pointee = AENS.AccountPt(Call.caller)
|
||||
let p2 : AENS.pointee = AENS.OraclePt(Call.caller)
|
||||
let p3 : AENS.pointee = AENS.ContractPt(Call.caller)
|
||||
let p4 : AENS.pointee = AENS.ChannelPt(Call.caller)
|
||||
AENS.update(owner, name, None, None,
|
||||
Some({ ["account_pubkey"] = p1, ["oracle_pubkey"] = p2,
|
||||
["contract_pubkey"] = p3, ["misc"] = p4 }))
|
||||
|
||||
entrypoint get_ttl(name : string) =
|
||||
switch(AENS.lookup(name))
|
||||
Some(AENS.Name(_, FixedTTL(ttl), _)) => ttl
|
||||
|
||||
entrypoint expiry(o : oracle(int, int)) : int =
|
||||
Oracle.expiry(o)
|
||||
|
||||
@@ -1,44 +1,82 @@
|
||||
// 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-
|
||||
* line
|
||||
* comment
|
||||
*/
|
||||
function foo : _
|
||||
stateful function foo : _
|
||||
entrypoint bar : int => (int * 'a)
|
||||
|
||||
|
||||
contract AllSyntax =
|
||||
|
||||
type typeDecl = int
|
||||
type paramTypeDecl('a, 'b) = (('a, 'b) => 'b) => list('a) => 'b => 'b
|
||||
datatype mickiewicz = Adam | Mickiewicz
|
||||
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 }
|
||||
record recordType = { z : nestedRecord, y : int }
|
||||
datatype variantType('a) = None | Some('a)
|
||||
type state = shakespeare(int)
|
||||
|
||||
let valWithType : map(int, int) => option(int) = (m) => Map.get(m, 42)
|
||||
let valNoType =
|
||||
if(valWithType(Map.empty) == None)
|
||||
print(42 mod 10 * 5 / 3)
|
||||
entrypoint init() = {
|
||||
johann = 1000,
|
||||
wolfgang = -10,
|
||||
|
||||
function funWithType(x : int, y) : int * list(int) = (x, 0 :: [y] ++ [])
|
||||
function funNoType() =
|
||||
let foo = (x, y : bool) =>
|
||||
if (! (y && x =< 0x0b || true)) [x]
|
||||
else [11..20]
|
||||
let setY(r : recordType) : unit = r{ y = 5 }
|
||||
let setX(r : recordType, x : int) : recordType = r { z.x = x } // nested record update
|
||||
let getY(r) = switch(r) {y = y} => y
|
||||
switch (funWithType(1, -2))
|
||||
(x, [y, z]) => bar({x = z, y = -y + - -z * (-1)})
|
||||
(x, y :: _) => ()
|
||||
/* TODO: This does not compile because of bug in the parser tester.
|
||||
von = (2 + 2, 0, List.sum([x | k <- [1,2,3]
|
||||
, let l = k + 1
|
||||
, if(l < 10)
|
||||
, let f(x) = x + 100
|
||||
, Adam <- [Adam, Mickiewicz]
|
||||
, let x = f(l)
|
||||
])),
|
||||
*/
|
||||
von = (2 + 2, 0, List.sum([1,2,3,4])),
|
||||
goethe = () }
|
||||
|
||||
let hash : address = #01ab0fff11
|
||||
let b = false
|
||||
let qcon = Mod.Con
|
||||
let str = "blabla\nfoo"
|
||||
let chr = '"'
|
||||
function f() =
|
||||
let kp = "nietzsche"
|
||||
// let p = "Пушкин" // TODO: this also doesn't do right round_trip...
|
||||
let k(x : bytes(8)) : bytes(8) = Bytes.to_int(#fedcba9876543210)
|
||||
|
||||
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
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
contract C =
|
||||
type id('a) = 'a
|
||||
entrypoint f() : id = 123
|
||||
entrypoint g() : id(int, int) = 123
|
||||
@@ -0,0 +1,5 @@
|
||||
contract C =
|
||||
function
|
||||
g(1) = 2
|
||||
f(2) = 3
|
||||
h(1) = 123
|
||||
@@ -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")
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
contract Remote =
|
||||
entrypoint id : int => int
|
||||
|
||||
contract ProtectedCall =
|
||||
entrypoint bad(r : Remote) =
|
||||
r.id(protected = 0 == 1, 18)
|
||||
@@ -0,0 +1,5 @@
|
||||
contract BadRecord =
|
||||
entrypoint foo() =
|
||||
let r = {x = 0, [0] = 1}
|
||||
r{x = 0, [0] = 1}
|
||||
r{}
|
||||
@@ -0,0 +1,5 @@
|
||||
contract C =
|
||||
entrypoint f() =
|
||||
let z = 123
|
||||
{}{ [1 = 0] = z + 1 }
|
||||
2
|
||||
@@ -0,0 +1,74 @@
|
||||
// namespace Chain =
|
||||
// record tx = { paying_for : option(Chain.paying_for_tx)
|
||||
// , ga_metas : list(Chain.ga_meta_tx)
|
||||
// , actor : address
|
||||
// , fee : int
|
||||
// , ttl : int
|
||||
// , tx : Chain.base_tx }
|
||||
|
||||
// datatype ga_meta_tx = GAMetaTx(address, int)
|
||||
// datatype paying_for_tx = PayingForTx(address, int)
|
||||
// datatype base_tx = SpendTx(address, int, string)
|
||||
// | OracleRegisterTx | OracleQueryTx | OracleResponseTx | OracleExtendTx
|
||||
// | NamePreclaimTx | NameClaimTx(hash) | NameUpdateTx(string)
|
||||
// | NameRevokeTx(hash) | NameTransferTx(address, string)
|
||||
// | ChannelCreateTx(address) | ChannelDepositTx(address, int) | ChannelWithdrawTx(address, int) |
|
||||
// | ChannelForceProgressTx(address) | ChannelCloseMutualTx(address) | ChannelCloseSoloTx(address)
|
||||
// | ChannelSlashTx(address) | ChannelSettleTx(address) | ChannelSnapshotSoloTx(address)
|
||||
// | ContractCreateTx(int) | ContractCallTx(address, int)
|
||||
// | GAAttachTx
|
||||
|
||||
|
||||
// Contract replicating "normal" Aeternity authentication
|
||||
contract BasicAuthTx =
|
||||
record state = { nonce : int, owner : address }
|
||||
datatype foo = Bar | Baz()
|
||||
|
||||
entrypoint init() = { nonce = 1, owner = Call.caller }
|
||||
|
||||
stateful entrypoint authorize(n : int, s : signature) : bool =
|
||||
require(n >= state.nonce, "Nonce too low")
|
||||
require(n =< state.nonce, "Nonce too high")
|
||||
put(state{ nonce = n + 1 })
|
||||
switch(Auth.tx_hash)
|
||||
None => abort("Not in Auth context")
|
||||
Some(tx_hash) =>
|
||||
let Some(tx0) = Auth.tx
|
||||
let x : option(Chain.paying_for_tx) = tx0.paying_for
|
||||
let x : list(Chain.ga_meta_tx) = tx0.ga_metas
|
||||
let x : int = tx0.fee + tx0.ttl
|
||||
let x : address = tx0.actor
|
||||
let x : Chain.tx = { tx = Chain.NamePreclaimTx, paying_for = None, ga_metas = [],
|
||||
fee = 123, ttl = 0, actor = Call.caller }
|
||||
switch(tx0.tx)
|
||||
Chain.SpendTx(receiver, amount, payload) => verify(tx_hash, n, s)
|
||||
Chain.OracleRegisterTx => false
|
||||
Chain.OracleQueryTx => false
|
||||
Chain.OracleResponseTx => false
|
||||
Chain.OracleExtendTx => false
|
||||
Chain.NamePreclaimTx => false
|
||||
Chain.NameClaimTx(name) => false
|
||||
Chain.NameUpdateTx(name) => false
|
||||
Chain.NameRevokeTx(name) => false
|
||||
Chain.NameTransferTx(to, name) => false
|
||||
Chain.ChannelCreateTx(other_party) => false
|
||||
Chain.ChannelDepositTx(channel, amount) => false
|
||||
Chain.ChannelWithdrawTx(channel, amount) => false
|
||||
Chain.ChannelForceProgressTx(channel) => false
|
||||
Chain.ChannelCloseMutualTx(channel) => false
|
||||
Chain.ChannelCloseSoloTx(channel) => false
|
||||
Chain.ChannelSlashTx(channel) => false
|
||||
Chain.ChannelSettleTx(channel) => false
|
||||
Chain.ChannelSnapshotSoloTx(channel) => false
|
||||
Chain.ContractCreateTx(amount) => false
|
||||
Chain.ContractCallTx(ct_address, amount) => false
|
||||
Chain.GAAttachTx => false
|
||||
|
||||
function verify(tx_hash, n, s) =
|
||||
Crypto.verify_sig(to_sign(tx_hash, n), state.owner, s)
|
||||
|
||||
entrypoint to_sign(h : hash, n : int) =
|
||||
Crypto.blake2b((h, n))
|
||||
|
||||
entrypoint weird_string() : string =
|
||||
"\x19Weird String\x42\nMore\n"
|
||||
@@ -1,4 +1,4 @@
|
||||
|
||||
include "String.aes"
|
||||
contract BytesToX =
|
||||
|
||||
entrypoint to_int(b : bytes(42)) : int = Bytes.to_int(b)
|
||||
|
||||
@@ -17,7 +17,7 @@ contract ChannelOnChainContractOracle =
|
||||
bets = {}
|
||||
}
|
||||
|
||||
public stateful function place_bet(answer: string) =
|
||||
public stateful function place_bet(answer: string) =
|
||||
switch(Map.lookup(answer, state.bets))
|
||||
None =>
|
||||
put(state{ bets = state.bets{[answer] = Call.caller}})
|
||||
@@ -25,6 +25,9 @@ contract ChannelOnChainContractOracle =
|
||||
Some(_value) =>
|
||||
"bet_already_taken"
|
||||
|
||||
public function expiry() =
|
||||
Oracle.expiry(state.oracle)
|
||||
|
||||
public function query_fee() =
|
||||
Oracle.query_fee(state.oracle)
|
||||
|
||||
@@ -35,7 +38,7 @@ contract ChannelOnChainContractOracle =
|
||||
switch(Oracle.get_answer(state.oracle, q))
|
||||
None =>
|
||||
"no response"
|
||||
Some(result) =>
|
||||
Some(result) =>
|
||||
if(state.question == Oracle.get_question(state.oracle, q))
|
||||
switch(Map.lookup(result, state.bets))
|
||||
None =>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
contract C =
|
||||
type t
|
||||
entrypoint f() = 123
|
||||
@@ -11,7 +11,7 @@ contract Factorial =
|
||||
|
||||
stateful entrypoint set_worker(worker) = put(state{worker = worker})
|
||||
|
||||
entrypoint fac(x : int) : int =
|
||||
if(x == 0) 1
|
||||
else x * state.worker.fac(x - 1)
|
||||
|
||||
entrypoint
|
||||
fac : int => int
|
||||
fac(0) = 1
|
||||
fac(x) = x * state.worker.fac(x - 1)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
|
||||
include "String.aes"
|
||||
contract FunctionArguments =
|
||||
|
||||
entrypoint sum(n : int, m: int) =
|
||||
@@ -45,3 +45,8 @@ contract FunctionArguments =
|
||||
|
||||
entrypoint due(t : Chain.ttl) =
|
||||
true
|
||||
|
||||
record singleton_r = { x : int }
|
||||
|
||||
entrypoint singleton_rec(r : singleton_r) =
|
||||
r.x
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace M =
|
||||
function mf() = mg()
|
||||
function mg() = mf()
|
||||
|
||||
namespace N =
|
||||
function nf() = ng() + M.mf() + M.mg()
|
||||
private function ng() = nf() + M.mf() + M.mg()
|
||||
|
||||
contract C =
|
||||
entrypoint f() = N.ng() + N.nf() + g()
|
||||
function g() = N.ng() + N.nf() + f()
|
||||
@@ -0,0 +1,3 @@
|
||||
contract IWantToBelieve =
|
||||
type stateT('s, 'm, 'a) = 's => 'm('a * 's)
|
||||
entrypoint s() = 123
|
||||
@@ -0,0 +1,4 @@
|
||||
contract Foo =
|
||||
record r = {x : int}
|
||||
// Crashed in the backend due to missing type annotation on the lc body.
|
||||
entrypoint lc(xs) = [ {x = x} | x <- xs ]
|
||||
@@ -0,0 +1,15 @@
|
||||
contract LetPatterns =
|
||||
|
||||
record r = {x : int, y : int, b : bool}
|
||||
|
||||
entrypoint test() = foo([1, 0], (2, 3), Some(4), {x = 5, y = 6, b = false})
|
||||
|
||||
entrypoint foo(xs : list(int), p : int * int, some : option(int), r : r) =
|
||||
let x :: _ = xs
|
||||
let (a, b) = p
|
||||
let Some(n) = some
|
||||
let {x = i, y = j} = r
|
||||
x + a + b + n + i + j
|
||||
|
||||
entrypoint lc(xs : list(option(int))) : list(int) =
|
||||
[ x | Some(x) <- xs ]
|
||||
@@ -0,0 +1,22 @@
|
||||
contract LHSMatching =
|
||||
|
||||
function from_some(Some(x)) = x
|
||||
|
||||
function
|
||||
length : list('a) => int
|
||||
length([]) = 0
|
||||
length(_ :: xs) = 1 + length(xs)
|
||||
|
||||
function
|
||||
append([], ys) = ys
|
||||
append(x :: xs, ys) = x :: append(xs, ys)
|
||||
|
||||
function local_match(xs : list('a)) =
|
||||
let null([]) = true
|
||||
let null(_ :: _) = false
|
||||
!null(xs)
|
||||
|
||||
entrypoint main() =
|
||||
from_some(Some([0]))
|
||||
++ append([length([true]), 2, 3], [4, 5, 6])
|
||||
++ [7 | if (local_match([false]))]
|
||||
@@ -0,0 +1,14 @@
|
||||
include "String.aes"
|
||||
contract StringX =
|
||||
entrypoint test() =
|
||||
let s1 = "a string"
|
||||
let s2 = "another string"
|
||||
let s = String.concat(s1, s2)
|
||||
String.sha256(s)
|
||||
String.length(s1)
|
||||
String.from_list(String.to_list(s))
|
||||
String.split(4, s1)
|
||||
String.at(2, s2)
|
||||
String.tokens(s, ",")
|
||||
String.to_upper(s1)
|
||||
String.to_lower(s2)
|
||||
@@ -15,7 +15,7 @@ contract MultiSig =
|
||||
| OwnerRemoved (address) // of { .removedOwner : Address }
|
||||
| ReqChanged (int) // of { .newReq : int }
|
||||
|
||||
let maxOwners : int = 250
|
||||
function maxOwners() : int = 250
|
||||
|
||||
record state = { nRequired : int
|
||||
, nOwners : int
|
||||
@@ -68,7 +68,7 @@ contract MultiSig =
|
||||
switch(check_pending(callhash()))
|
||||
CheckFail(state') => { state = state' }
|
||||
CheckOk(state') =>
|
||||
if(state.nOwners >= maxOwners) () /* TODO */
|
||||
if(state.nOwners >= maxOwners()) () /* TODO */
|
||||
else
|
||||
let nOwners' = state'.nOwners + 1
|
||||
{ state = state' { owners = Map.insert(nOwners', newOwner, state'.owners)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
contract C1 =
|
||||
entrypoint f : int
|
||||
|
||||
contract C =
|
||||
entrypoint f() = 123
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace BadNs =
|
||||
contract Con =
|
||||
entrypoint e : () => int
|
||||
|
||||
contract Con =
|
||||
entrypoint foo() = 43
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace BadNs =
|
||||
include "included.aes"
|
||||
|
||||
contract Con =
|
||||
entrypoint foo() = 43
|
||||
@@ -1,5 +1,4 @@
|
||||
contract Bad =
|
||||
include "included.aes"
|
||||
contract BadCon =
|
||||
namespace Foo =
|
||||
function foo() = 42
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
include "BLS12_381.aes"
|
||||
|
||||
contract GrothVerify =
|
||||
type fr = BLS12_381.fr
|
||||
type g1 = BLS12_381.g1
|
||||
type g2 = BLS12_381.g2
|
||||
|
||||
record proof = { a : g1, b : g2, c : g1 }
|
||||
record verify_key = { a : g1, b : g2, c : g2, d : g2, ic : list(g1) }
|
||||
|
||||
record state = { vk : verify_key }
|
||||
|
||||
entrypoint init(vk0 : verify_key) = {vk = vk0}
|
||||
|
||||
entrypoint verify_proof(p : proof, input : list(fr)) =
|
||||
let vk = state.vk
|
||||
let vk_x = calc_vk_x(vk.ic, input)
|
||||
|
||||
BLS12_381.pairing_check([BLS12_381.g1_neg(p.a), vk.a, vk_x, p.c],
|
||||
[p.b, vk.b, vk.c, vk.d])
|
||||
|
||||
function calc_vk_x(ics : list(g1), xs : list(fr)) =
|
||||
switch(ics)
|
||||
(ic :: ics) => calc_vk_x_(ic, ics, xs)
|
||||
|
||||
function calc_vk_x_(vk_x : g1, ics : list(g1), xs : list(fr)) =
|
||||
switch((ics, xs))
|
||||
([], []) => vk_x
|
||||
(ic :: ics, x :: xs) => calc_vk_x_(BLS12_381.g1_add(vk_x, BLS12_381.g1_mul(x, ic)), ics, xs)
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
contract Remote =
|
||||
entrypoint id : int => int
|
||||
|
||||
contract ProtectedCall =
|
||||
|
||||
function higher_order(r : Remote) =
|
||||
r.id
|
||||
|
||||
entrypoint test_ok(r : Remote) =
|
||||
let f = higher_order(r)
|
||||
let Some(n) = r.id(protected = true, 10)
|
||||
let Some(m) = f(protected = true, 5)
|
||||
n + m + r.id(protected = false, 100) + f(1)
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Foo =
|
||||
datatype x = A | B(int)
|
||||
|
||||
contract Bar =
|
||||
entrypoint f(a : Foo.x) =
|
||||
switch(a)
|
||||
Foo.A => 0
|
||||
Foo.B(n) => n
|
||||
@@ -8,10 +8,9 @@ contract Stack =
|
||||
|
||||
entrypoint init(ss : list(string)) = { stack = ss, size = length(ss) }
|
||||
|
||||
function length(xs) =
|
||||
switch(xs)
|
||||
[] => 0
|
||||
_ :: xs => length(xs) + 1
|
||||
function
|
||||
length([]) = 0
|
||||
length(_ :: xs) = length(xs) + 1
|
||||
|
||||
stateful entrypoint pop() : string =
|
||||
switch(state.stack)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
include "String.aes"
|
||||
contract Remote =
|
||||
record rstate = { i : int, s : string, m : map(int, int) }
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
include "String.aes"
|
||||
contract Strings =
|
||||
entrypoint str_len(s) = String.length(s)
|
||||
entrypoint str_concat(s1, s2) = String.concat(s1, s2)
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
contract C =
|
||||
let this_is_illegal = 2/0
|
||||
entrypoint this_is_legal() = 2/0
|
||||
@@ -7,6 +7,7 @@
|
||||
// AENS.transfer
|
||||
// AENS.revoke
|
||||
// Oracle.extend
|
||||
include "String.aes"
|
||||
contract UnappliedBuiltins =
|
||||
entrypoint main() = ()
|
||||
type o = oracle(int, int)
|
||||
@@ -21,6 +22,7 @@ contract UnappliedBuiltins =
|
||||
function b_abort() = abort
|
||||
function b_require() = require
|
||||
function oracle_query_fee() = Oracle.query_fee
|
||||
function oracle_expiry() = Oracle.expiry
|
||||
stateful function oracle_query() = Oracle.query : (o, _, _, _, _) => _
|
||||
function oracle_get_question() = Oracle.get_question : (o, _) => _
|
||||
function oracle_get_answer() = Oracle.get_answer : (o, _) => _
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
contract UTF8 =
|
||||
entrypoint f1() : char = '1'
|
||||
entrypoint f2() : char = '+'
|
||||
entrypoint f3() : char = 'd'
|
||||
entrypoint f4() : char = 'X'
|
||||
entrypoint f5() : char = 'å'
|
||||
entrypoint f6() : char = 'Ä'
|
||||
entrypoint f7() : char = 'æ'
|
||||
entrypoint f8() : char = 'ë'
|
||||
entrypoint f9() : char = 'ẻ'
|
||||
entrypoint f10() : char = '\x27'
|
||||
entrypoint f11() : char = '\x{2200}'
|
||||
entrypoint f12() : char = '💩'
|
||||
entrypoint f13() : char = '\n'
|
||||
|
||||
|
||||
|
||||
// entrypoint f13() : char = 'e̊'
|
||||
// entrypoint f14() : char = '\Ì'
|
||||
|
||||
// '💩' vs. map('a,'b)
|
||||
@@ -0,0 +1,4 @@
|
||||
contract ValidationTest =
|
||||
payable entrypoint attr_fail() = ()
|
||||
entrypoint type_fail(x : int) = x
|
||||
entrypoint code_fail(x) = x + 1
|
||||
@@ -0,0 +1,4 @@
|
||||
contract ValidationTest =
|
||||
entrypoint attr_fail() = ()
|
||||
entrypoint type_fail(x) = x
|
||||
entrypoint code_fail(x) = x - 1
|
||||
@@ -0,0 +1,4 @@
|
||||
payable contract ValidationTest =
|
||||
payable entrypoint attr_fail() = ()
|
||||
entrypoint type_fail(x : int) = x
|
||||
entrypoint code_fail(x) = x + 1
|
||||
@@ -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 =
|
||||
|
||||
// Types
|
||||
|
||||
Reference in New Issue
Block a user