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
|
working_directory: ~/aesophia
|
||||||
|
|
||||||
jobs:
|
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:
|
build:
|
||||||
executor: aebuilder
|
executor: aebuilder
|
||||||
steps:
|
steps:
|
||||||
@@ -35,3 +44,10 @@ jobs:
|
|||||||
- _build/default/rebar3_20.3.8_plt
|
- _build/default/rebar3_20.3.8_plt
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: _build/test/logs
|
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
|
### Changed
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
## [4.3.0]
|
||||||
|
### Added
|
||||||
|
- Added documentation (moved from `protocol`)
|
||||||
|
- `Frac.aes` – library for rational numbers
|
||||||
|
- Added some more meaningful error messages
|
||||||
|
- Exported several parsing functionalities
|
||||||
|
- With option `keep_included` it is possible to see which files were included during the parse
|
||||||
|
- There is a function `run_parser` that be used to evaluate any parsing rule
|
||||||
|
- Exported parsers: `body`, `type` and `decl`
|
||||||
|
### Changed
|
||||||
|
- Performance improvements in the standard library
|
||||||
|
- Fixed ACI encoder to handle `-` unary operator
|
||||||
|
- Fixed including by absolute path
|
||||||
|
- Fixed variant type printing in the ACI error messages
|
||||||
|
- Fixed pretty printing of combined function clauses
|
||||||
|
### Removed
|
||||||
|
- `let` definitions are no longer supported in the toplevel of the contract
|
||||||
|
- type declarations are no longer supported
|
||||||
|
|
||||||
|
## [4.2.0] - 2020-01-15
|
||||||
|
### 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
|
## [4.0.0] - 2019-10-11
|
||||||
### Added
|
### Added
|
||||||
- `Address.to_contract` - casts an address to a (any) contract type.
|
- `Address.to_contract` - casts an address to a (any) contract type.
|
||||||
- Pragma to check compiler version, e.g. `@compiler >= 4.0`.
|
- 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.
|
- 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
|
- `Bytes.concat` and `Bytes.split` are added to be able to
|
||||||
(de-)construct byte arrays.
|
(de-)construct byte arrays.
|
||||||
- `[a..b]` language construct, returning the list of numbers between
|
- `[a..b]` language construct, returning the list of numbers between
|
||||||
`a` and `b` (inclusive). Returns the empty list if `a` > `b`.
|
`a` and `b` (inclusive). Returns the empty list if `a` > `b`.
|
||||||
- [Standard libraries] (https://github.com/aeternity/protocol/blob/master/contracts/sophia_stdlib.md)
|
- [Standard libraries](https://github.com/aeternity/aesophia/blob/master/docs/sophia_stdlib.md)
|
||||||
- Checks that `init` is not called from other functions.
|
- Checks that `init` is not called from other functions.
|
||||||
### 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
|
- FATE backend - the compiler is able to produce VM code for both `AEVM` and `FATE`. Many
|
||||||
of the APIs now take `{backend, aevm | fate}` to decide wich backend to produce artifacts
|
of the APIs now take `{backend, aevm | fate}` to decide wich backend to produce artifacts
|
||||||
for.
|
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)`
|
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.
|
can be used to check if an (contract) address is payable or not.
|
||||||
### Changed
|
### 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
|
- 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`.
|
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,
|
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
|
- Simplify calldata creation - instead of passing a compiled contract, simply
|
||||||
pass a (stubbed) contract string.
|
pass a (stubbed) contract string.
|
||||||
|
|
||||||
[Unreleased]: https://github.com/aeternity/aesophia/compare/v4.0.0...HEAD
|
[Unreleased]: https://github.com/aeternity/aesophia/compare/v4.3.0...HEAD
|
||||||
[4.0.0]: https://github.com/aeternity/aesophia/compare/v4.0.0...v3.2.0
|
[4.3.0]: https://github.com/aeternity/aesophia/compare/v4.2.0...v4.3.0
|
||||||
[4.0.0-rc5]: https://github.com/aeternity/aesophia/compare/v4.0.0-rc4...v4.0.0-rc5
|
[4.2.0]: https://github.com/aeternity/aesophia/compare/v4.1.0...v4.2.0
|
||||||
[4.0.0-rc4]: https://github.com/aeternity/aesophia/compare/v4.0.0-rc3...v4.0.0-rc4
|
[4.1.0]: https://github.com/aeternity/aesophia/compare/v4.0.0...v4.1.0
|
||||||
[4.0.0-rc3]: https://github.com/aeternity/aesophia/compare/v4.0.0-rc1...v4.0.0-rc3
|
[4.0.0]: https://github.com/aeternity/aesophia/compare/v3.2.0...v4.0.0
|
||||||
[4.0.0-rc1]: https://github.com/aeternity/aesophia/compare/v3.2.0...v4.0.0-rc1
|
|
||||||
[3.2.0]: https://github.com/aeternity/aesophia/compare/v3.1.0...v3.2.0
|
[3.2.0]: https://github.com/aeternity/aesophia/compare/v3.1.0...v3.2.0
|
||||||
[3.1.0]: https://github.com/aeternity/aesophia/compare/v3.0.0...v3.1.0
|
[3.1.0]: https://github.com/aeternity/aesophia/compare/v3.0.0...v3.1.0
|
||||||
[3.0.0]: https://github.com/aeternity/aesophia/compare/v2.1.0...v3.0.0
|
[3.0.0]: https://github.com/aeternity/aesophia/compare/v2.1.0...v3.0.0
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
# aesophia
|
# 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
|
## Documentation
|
||||||
[the æternity node](https://github.com/aeternity/epoch). However, it can
|
|
||||||
also be included in other systems to compile contracts coded in sophia which
|
* [Smart Contracts on aeternity Blockchain](https://github.com/aeternity/protocol/blob/master/contracts/contracts.md).
|
||||||
can then be loaded into the æternity system.
|
* [Sophia Documentation](docs/sophia.md).
|
||||||
|
* [Sophia Standard Library](docs/sophia_stdlib.md).
|
||||||
|
|
||||||
## Versioning
|
## 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)
|
function rapply(x : 'a, f : 'a => 'b) : 'b = f(x)
|
||||||
|
|
||||||
/* The Z combinator - replacement for local and anonymous recursion.
|
/** The Z combinator - replacement for local and anonymous recursion.
|
||||||
*/
|
*/
|
||||||
function recur(f : ('arg => 'res, 'arg) => 'res) : 'arg => 'res =
|
function recur(f : ('arg => 'res, 'arg) => 'res) : 'arg => 'res =
|
||||||
(x) => f(recur(f), x)
|
(x) => f(recur(f), x)
|
||||||
|
|
||||||
|
/** n-times composition with itself
|
||||||
|
*/
|
||||||
function iter(n : int, f : 'a => 'a) : 'a => 'a = iter_(n, f, (x) => x)
|
function iter(n : int, f : 'a => 'a) : 'a => 'a = iter_(n, f, (x) => x)
|
||||||
private function iter_(n : int, f : 'a => 'a, acc : 'a => 'a) : 'a => 'a =
|
private function iter_(n : int, f : 'a => 'a, acc : 'a => 'a) : 'a => 'a =
|
||||||
if(n == 0) acc
|
if(n == 0) acc
|
||||||
elif(n == 1) comp(f, acc)
|
elif(n == 1) comp(f, acc)
|
||||||
else iter_(n / 2, comp(f, f), if(n mod 2 == 0) acc else comp(f, acc))
|
else iter_(n / 2, comp(f, f), if(n mod 2 == 0) acc else comp(f, acc))
|
||||||
|
|
||||||
function curry2(f : ('a, 'b) => 'c) : 'a => ('b => 'c) =
|
/** Turns an ugly, bad and disgusting arity-n function into
|
||||||
|
* a beautiful and sweet function taking the first argument
|
||||||
|
* and returning a function watiting for the remaining ones
|
||||||
|
* in the same manner
|
||||||
|
*/
|
||||||
|
function curry2(f : ('a, 'b) => 'x) : 'a => ('b => 'x) =
|
||||||
(x) => (y) => f(x, y)
|
(x) => (y) => f(x, y)
|
||||||
function curry3(f : ('a, 'b, 'c) => 'd) : 'a => ('b => ('c => 'd)) =
|
function curry3(f : ('a, 'b, 'c) => 'x) : 'a => ('b => ('c => 'x)) =
|
||||||
(x) => (y) => (z) => f(x, y, z)
|
(x) => (y) => (z) => f(x, y, z)
|
||||||
|
function curry4(f : ('a, 'b, 'c, 'd) => 'x) : 'a => ('b => ('c => ('d => 'x))) =
|
||||||
|
(x) => (y) => (z) => (w) => f(x, y, z, w)
|
||||||
|
function curry5(f : ('a, 'b, 'c, 'd, 'e) => 'x) : 'a => ('b => ('c => ('d => ('e => 'x)))) =
|
||||||
|
(x) => (y) => (z) => (w) => (q) => f(x, y, z, w, q)
|
||||||
|
|
||||||
function uncurry2(f : 'a => ('b => 'c)) : ('a, 'b) => 'c =
|
/** Opposite of curry. Gross
|
||||||
|
*/
|
||||||
|
function uncurry2(f : 'a => ('b => 'x)) : ('a, 'b) => 'x =
|
||||||
(x, y) => f(x)(y)
|
(x, y) => f(x)(y)
|
||||||
function uncurry3(f : 'a => ('b => ('c => 'd))) : ('a, 'b, 'c) => 'd =
|
function uncurry3(f : 'a => ('b => ('c => 'x))) : ('a, 'b, 'c) => 'x =
|
||||||
(x, y, z) => f(x)(y)(z)
|
(x, y, z) => f(x)(y)(z)
|
||||||
|
function uncurry4(f : 'a => ('b => ('c => ('d => 'x)))) : ('a, 'b, 'c, 'd) => 'x =
|
||||||
|
(x, y, z, w) => f(x)(y)(z)(w)
|
||||||
|
function uncurry5(f : 'a => ('b => ('c => ('d => ('e => 'x))))) : ('a, 'b, 'c, 'd, 'e) => 'x =
|
||||||
|
(x, y, z, w, q) => f(x)(y)(z)(w)(q)
|
||||||
|
|
||||||
function tuplify2(f : ('a, 'b) => 'c) : (('a * 'b)) => 'c =
|
/** Turns an arity-n function into a function taking n-tuple
|
||||||
|
*/
|
||||||
|
function tuplify2(f : ('a, 'b) => 'x) : (('a * 'b)) => 'x =
|
||||||
(t) => switch(t)
|
(t) => switch(t)
|
||||||
(x, y) => f(x, y)
|
(x, y) => f(x, y)
|
||||||
function tuplify3(f : ('a, 'b, 'c) => 'd) : 'a * 'b * 'c => 'd =
|
function tuplify3(f : ('a, 'b, 'c) => 'x) : 'a * 'b * 'c => 'x =
|
||||||
(t) => switch(t)
|
(t) => switch(t)
|
||||||
(x, y, z) => f(x, y, z)
|
(x, y, z) => f(x, y, z)
|
||||||
|
function tuplify4(f : ('a, 'b, 'c, 'd) => 'x) : 'a * 'b * 'c * 'd => 'x =
|
||||||
|
(t) => switch(t)
|
||||||
|
(x, y, z, w) => f(x, y, z, w)
|
||||||
|
function tuplify5(f : ('a, 'b, 'c, 'd, 'e) => 'x) : 'a * 'b * 'c * 'd * 'e => 'x =
|
||||||
|
(t) => switch(t)
|
||||||
|
(x, y, z, w, q) => f(x, y, z, w, q)
|
||||||
|
|
||||||
function untuplify2(f : 'a * 'b => 'c) : ('a, 'b) => 'c =
|
/** Opposite of tuplify
|
||||||
|
*/
|
||||||
|
function untuplify2(f : 'a * 'b => 'x) : ('a, 'b) => 'x =
|
||||||
(x, y) => f((x, y))
|
(x, y) => f((x, y))
|
||||||
function untuplify3(f : 'a * 'b * 'c => 'd) : ('a, 'b, 'c) => 'd =
|
function untuplify3(f : 'a * 'b * 'c => 'x) : ('a, 'b, 'c) => 'x =
|
||||||
(x, y, z) => f((x, y, z))
|
(x, y, z) => f((x, y, z))
|
||||||
|
function untuplify4(f : 'a * 'b * 'c * 'd => 'x) : ('a, 'b, 'c, 'd) => 'x =
|
||||||
|
(x, y, z, w) => f((x, y, z, w))
|
||||||
|
function untuplify5(f : 'a * 'b * 'c * 'd * 'e => 'x) : ('a, 'b, 'c, 'd, 'e) => 'x =
|
||||||
|
(x, y, z, w, q) => f((x, y, z, w, q))
|
||||||
|
|||||||
+128
-86
@@ -15,22 +15,42 @@ namespace List =
|
|||||||
_::t => Some(t)
|
_::t => Some(t)
|
||||||
|
|
||||||
function last(l : list('a)) : option('a) = switch(l)
|
function last(l : list('a)) : option('a) = switch(l)
|
||||||
[] => None
|
[] => None
|
||||||
[x] => Some(x)
|
[x] => Some(x)
|
||||||
_::t => last(t)
|
_::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)
|
function find(p : 'a => bool, l : list('a)) : option('a) = switch(l)
|
||||||
[] => None
|
[] => None
|
||||||
h::t => if(p(h)) Some(h) else find(p, t)
|
h::t => if(p(h)) Some(h) else find(p, t)
|
||||||
|
|
||||||
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
|
private function find_indices_( p : 'a => bool
|
||||||
, l : list('a)
|
, l : list('a)
|
||||||
, n : int
|
, n : int
|
||||||
, acc : list(int)
|
|
||||||
) : list(int) = switch(l)
|
) : 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) =
|
function nth(n : int, l : list('a)) : option('a) =
|
||||||
switch(l)
|
switch(l)
|
||||||
@@ -50,41 +70,53 @@ namespace List =
|
|||||||
_::t => length_(t, acc + 1)
|
_::t => length_(t, acc + 1)
|
||||||
|
|
||||||
|
|
||||||
|
/** Creates an ascending sequence of all integer numbers
|
||||||
|
* between `a` and `b` (including `a` and `b`)
|
||||||
|
*/
|
||||||
function from_to(a : int, b : int) : list(int) = [a..b]
|
function from_to(a : int, b : int) : list(int) = [a..b]
|
||||||
|
|
||||||
function from_to_step(a : int, b : int, s : int) : list(int) = from_to_step_(a, b, s, [])
|
/** Creates an ascending sequence of integer numbers betweeen
|
||||||
private function from_to_step_(a, b, s, acc) =
|
* `a` and `b` jumping by given `step`. Includes `a` and takes
|
||||||
if (a > b) reverse(acc) else from_to_step_(a + s, b, s, a :: acc)
|
* `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) =
|
function replace_at(n : int, e : 'a, l : list('a)) : list('a) =
|
||||||
if(n<0) abort("insert_at underflow") else replace_at_(n, e, l, [])
|
if(n<0) abort("insert_at underflow") else replace_at_(n, e, l)
|
||||||
private function replace_at_(n : int, e : 'a, l : list('a), acc : list('a)) : list('a) =
|
private function replace_at_(n : int, e : 'a, l : list('a)) : list('a) =
|
||||||
switch(l)
|
switch(l)
|
||||||
[] => abort("replace_at overflow")
|
[] => abort("replace_at overflow")
|
||||||
h::t => if (n == 0) reverse(e::acc) ++ t
|
h::t => if (n == 0) e::t
|
||||||
else replace_at_(n-1, e, t, h::acc)
|
else h::replace_at_(n-1, e, t)
|
||||||
|
|
||||||
/* Unsafe. Adds `e` to `l` to be its `n`th element. Crashes on over/underflow */
|
/** Unsafe. Adds `e` to `l` to be its `n`th element. Crashes on over/underflow
|
||||||
|
*/
|
||||||
function insert_at(n : int, e : 'a, l : list('a)) : list('a) =
|
function insert_at(n : int, e : 'a, l : list('a)) : list('a) =
|
||||||
if(n<0) abort("insert_at underflow") else insert_at_(n, e, l, [])
|
if(n<0) abort("insert_at underflow") else insert_at_(n, e, l)
|
||||||
private function insert_at_(n : int, e : 'a, l : list('a), acc : list('a)) : list('a) =
|
private function insert_at_(n : int, e : 'a, l : list('a)) : list('a) =
|
||||||
if (n == 0) reverse(e::acc) ++ l
|
if (n == 0) e::l
|
||||||
else switch(l)
|
else switch(l)
|
||||||
[] => abort("insert_at overflow")
|
[] => 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) =
|
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)
|
switch(l)
|
||||||
[] => reverse(x::acc)
|
[] => [x]
|
||||||
h::t =>
|
h::t =>
|
||||||
if(cmp(x, h)) // x < h
|
if(cmp(x, h)) // x < h
|
||||||
reverse(acc) ++ (x::l)
|
x::l
|
||||||
else
|
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)
|
function foldr(cons : ('a, 'b) => 'b, nil : 'b, l : list('a)) : 'b = switch(l)
|
||||||
@@ -102,61 +134,71 @@ namespace List =
|
|||||||
f(e)
|
f(e)
|
||||||
foreach(l', f)
|
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, [])
|
function map(f : 'a => 'b, l : list('a)) : list('b) = switch(l)
|
||||||
private function map_(f : 'a => 'b, l : list('a), acc : list('b)) : list('b) = switch(l)
|
[] => []
|
||||||
[] => reverse(acc)
|
h::t => f(h)::map(f, t)
|
||||||
h::t => map_(f, t, f(h)::acc)
|
|
||||||
|
|
||||||
|
/** Effectively composition of `map` and `flatten`
|
||||||
|
*/
|
||||||
function flat_map(f : 'a => list('b), l : list('a)) : list('b) =
|
function flat_map(f : 'a => list('b), l : list('a)) : list('b) =
|
||||||
ListInternal.flat_map(f, l)
|
ListInternal.flat_map(f, l)
|
||||||
|
|
||||||
function filter(p : 'a => bool, l : list('a)) : list('a) = filter_(p, l, [])
|
function filter(p : 'a => bool, l : list('a)) : list('a) = switch(l)
|
||||||
private function filter_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l)
|
[] => []
|
||||||
[] => reverse(acc)
|
h::t =>
|
||||||
h::t => filter_(p, t, if(p(h)) h::acc else acc)
|
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) =
|
function take(n : int, l : list('a)) : list('a) =
|
||||||
if(n < 0) abort("Take negative number of elements") else take_(n, l, [])
|
if(n < 0) abort("Take negative number of elements") else take_(n, l)
|
||||||
private function take_(n : int, l : list('a), acc : list('a)) : list('a) =
|
private function take_(n : int, l : list('a)) : list('a) =
|
||||||
if(n == 0) reverse(acc)
|
if(n == 0) []
|
||||||
else switch(l)
|
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) =
|
function drop(n : int, l : list('a)) : list('a) =
|
||||||
if(n < 0) abort("Drop negative number of elements")
|
if(n < 0) abort("Drop negative number of elements") else drop_(n, l)
|
||||||
elif (n == 0) l
|
private function drop_(n : int, l : list('a)) : list('a) =
|
||||||
|
if (n == 0) l
|
||||||
else switch(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` */
|
/** Get the longest prefix of a list in which every element
|
||||||
function take_while(p : 'a => bool, l : list('a)) : list('a) = take_while_(p, l, [])
|
* matches predicate `p`
|
||||||
private function take_while_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l)
|
*/
|
||||||
[] => reverse(acc)
|
function take_while(p : 'a => bool, l : list('a)) : list('a) = switch(l)
|
||||||
h::t => if(p(h)) take_while_(p, t, h::acc) else reverse(acc)
|
[] => []
|
||||||
|
h::t => if(p(h)) h::take_while(p, t) else []
|
||||||
|
|
||||||
/* Drop elements from `l` until `p` holds */
|
/** Drop elements from `l` until `p` holds
|
||||||
|
*/
|
||||||
function drop_while(p : 'a => bool, l : list('a)) : list('a) = switch(l)
|
function drop_while(p : 'a => bool, l : list('a)) : list('a) = switch(l)
|
||||||
[] => []
|
[] => []
|
||||||
h::t => if(p(h)) drop_while(p, t) else l
|
h::t => if(p(h)) drop_while(p, t) else l
|
||||||
|
|
||||||
/* Splits list into two lists of elements that respectively match and don't match predicate `p` */
|
/** Splits list into two lists of elements that respectively
|
||||||
function partition(p : 'a => bool, l : list('a)) : (list('a) * list('a)) = partition_(p, l, [], [])
|
* match and don't match predicate `p`
|
||||||
private function partition_( p : 'a => bool
|
*/
|
||||||
, l : list('a)
|
function partition(p : 'a => bool, l : list('a)) : (list('a) * list('a)) = switch(l)
|
||||||
, acc_t : list('a)
|
[] => ([], [])
|
||||||
, acc_f : list('a)
|
h::t =>
|
||||||
) : (list('a) * list('a)) = switch(l)
|
let (l, r) = partition(p, t)
|
||||||
[] => (reverse(acc_t), reverse(acc_f))
|
if(p(h)) (h::l, r) else (l, h::r)
|
||||||
h::t => if(p(h)) partition_(p, t, h::acc_t, acc_f) else partition_(p, t, acc_t, h::acc_f)
|
|
||||||
|
|
||||||
|
function flatten(l : list(list('a))) : list('a) = switch(l)
|
||||||
function flatten(ll : list(list('a))) : list('a) = foldr((l1, l2) => l1 ++ l2, [], ll)
|
[] => []
|
||||||
|
h::t => h ++ flatten(t)
|
||||||
|
|
||||||
function all(p : 'a => bool, l : list('a)) : bool = switch(l)
|
function all(p : 'a => bool, l : list('a)) : bool = switch(l)
|
||||||
[] => true
|
[] => true
|
||||||
@@ -171,26 +213,26 @@ namespace List =
|
|||||||
function product(l : list(int)) : int = foldl((a, b) => a * b, 1, l)
|
function product(l : list(int)) : int = foldl((a, b) => a * b, 1, l)
|
||||||
|
|
||||||
|
|
||||||
/* Zips two list by applying bimapping function on respective elements. Drops longer tail. */
|
/** Zips two list by applying bimapping function on respective elements.
|
||||||
function zip_with(f : ('a, 'b) => 'c, l1 : list('a), l2 : list('b)) : list('c) = zip_with_(f, l1, l2, [])
|
* Drops the tail of the longer list.
|
||||||
private function zip_with_( f : ('a, 'b) => 'c
|
*/
|
||||||
|
private function zip_with( f : ('a, 'b) => 'c
|
||||||
, l1 : list('a)
|
, l1 : list('a)
|
||||||
, l2 : list('b)
|
, l2 : list('b)
|
||||||
, acc : list('c)
|
|
||||||
) : list('c) = switch ((l1, l2))
|
) : list('c) = switch ((l1, l2))
|
||||||
(h1::t1, h2::t2) => zip_with_(f, t1, t2, f(h1, h2)::acc)
|
(h1::t1, h2::t2) => f(h1, h2)::zip_with(f, t1, t2)
|
||||||
_ => reverse(acc)
|
_ => []
|
||||||
|
|
||||||
/* Zips two lists into list of pairs. Drops longer tail. */
|
/** Zips two lists into list of pairs.
|
||||||
|
* Drops the tail of the longer list.
|
||||||
|
*/
|
||||||
function zip(l1 : list('a), l2 : list('b)) : list('a * 'b) = zip_with((a, b) => (a, b), l1, l2)
|
function zip(l1 : list('a), l2 : list('b)) : list('a * 'b) = zip_with((a, b) => (a, b), l1, l2)
|
||||||
|
|
||||||
function unzip(l : list('a * 'b)) : list('a) * list('b) = unzip_(l, [], [])
|
function unzip(l : list('a * 'b)) : (list('a) * list('b)) = switch(l)
|
||||||
private function unzip_( l : list('a * 'b)
|
[] => ([], [])
|
||||||
, acc_l : list('a)
|
(h1, h2)::t =>
|
||||||
, acc_r : list('b)
|
let (t1, t2) = unzip(t)
|
||||||
) : (list('a) * list('b)) = switch(l)
|
(h1::t1, h2::t2)
|
||||||
[] => (reverse(acc_l), reverse(acc_r))
|
|
||||||
(left, right)::t => unzip_(t, left::acc_l, right::acc_r)
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: Improve?
|
// TODO: Improve?
|
||||||
@@ -199,16 +241,16 @@ namespace List =
|
|||||||
h::t => switch (partition((x) => lesser_cmp(x, h), t))
|
h::t => switch (partition((x) => lesser_cmp(x, h), t))
|
||||||
(lesser, bigger) => sort(lesser_cmp, lesser) ++ h::sort(lesser_cmp, bigger)
|
(lesser, bigger) => sort(lesser_cmp, lesser) ++ h::sort(lesser_cmp, bigger)
|
||||||
|
|
||||||
|
/** Puts `delim` between every two members of the list
|
||||||
|
*/
|
||||||
|
function intersperse(delim : 'a, l : list('a)) : list('a) = switch(l)
|
||||||
|
[] => []
|
||||||
|
[e] => [e]
|
||||||
|
h::t => h::delim::intersperse(delim, t)
|
||||||
|
|
||||||
function intersperse(delim : 'a, l : list('a)) : list('a) = intersperse_(delim, l, [])
|
/** Effectively a zip with an infinite sequence of natural numbers
|
||||||
private function intersperse_(delim : 'a, l : list('a), acc : list('a)) : list('a) = switch(l)
|
*/
|
||||||
[] => reverse(acc)
|
function enumerate(l : list('a)) : list(int * 'a) = enumerate_(l, 0)
|
||||||
[e] => reverse(e::acc)
|
private function enumerate_(l : list('a), n : int) : list(int * 'a) = switch(l)
|
||||||
h::t => intersperse_(delim, t, delim::h::acc)
|
[] => []
|
||||||
|
h::t => (n, h)::enumerate_(t, n + 1)
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
+35
-13
@@ -10,14 +10,23 @@ namespace Option =
|
|||||||
None => false
|
None => false
|
||||||
Some(_) => true
|
Some(_) => true
|
||||||
|
|
||||||
|
/** Catamorphism on `option`. Also known as inlined pattern matching.
|
||||||
|
*/
|
||||||
function match(n : 'b, s : 'a => 'b, o : option('a)) : 'b = switch(o)
|
function match(n : 'b, s : 'a => 'b, o : option('a)) : 'b = switch(o)
|
||||||
None => n
|
None => n
|
||||||
Some(x) => s(x)
|
Some(x) => s(x)
|
||||||
|
|
||||||
|
/** Escape option providing default if `None`
|
||||||
|
*/
|
||||||
function default(def : 'a, o : option('a)) : 'a = match(def, (x) => x, o)
|
function default(def : 'a, o : option('a)) : 'a = match(def, (x) => x, o)
|
||||||
|
|
||||||
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)
|
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))
|
(Some(x1), Some(x2), Some(x3)) => Some(f(x1, x2, x3))
|
||||||
_ => None
|
_ => None
|
||||||
|
|
||||||
|
/** Like `map`, but the function is in `option`
|
||||||
|
*/
|
||||||
function app_over(f : option ('a => 'b), o : option('a)) : option('b) = switch((f, o))
|
function app_over(f : option ('a => 'b), o : option('a)) : option('b) = switch((f, o))
|
||||||
(Some(ff), Some(xx)) => Some(ff(xx))
|
(Some(ff), Some(xx)) => Some(ff(xx))
|
||||||
_ => None
|
_ => None
|
||||||
|
|
||||||
|
/** Monadic bind
|
||||||
|
*/
|
||||||
function flat_map(f : 'a => option('b), o : option('a)) : option('b) = switch(o)
|
function flat_map(f : 'a => option('b), o : option('a)) : option('b) = switch(o)
|
||||||
None => None
|
None => None
|
||||||
Some(x) => f(x)
|
Some(x) => f(x)
|
||||||
@@ -53,24 +66,33 @@ namespace Option =
|
|||||||
None => []
|
None => []
|
||||||
Some(x) => [x]
|
Some(x) => [x]
|
||||||
|
|
||||||
function filter_options(l : list(option('a))) : list('a) = filter_options_(l, [])
|
/** Turns list of options into a list of elements that are under `Some`s.
|
||||||
private function filter_options_(l : list (option('a)), acc : list('a)) : list('a) = switch(l)
|
* Safe.
|
||||||
[] => List.reverse(acc)
|
*/
|
||||||
None::t => filter_options_(t, acc)
|
function filter_options(l : list(option('a))) : list('a) = switch(l)
|
||||||
Some(x)::t => filter_options_(t, x::acc)
|
[] => []
|
||||||
|
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, [])
|
/** Just like `filter_options` but requires all elements to be `Some` and returns
|
||||||
private function seq_options_(l : list (option('a)), acc : list('a)) : option(list('a)) = switch(l)
|
* None if any of them is not
|
||||||
[] => Some(List.reverse(acc))
|
*/
|
||||||
None::t => None
|
function seq_options(l : list (option('a))) : option (list('a)) = switch(l)
|
||||||
Some(x)::t => seq_options_(t, x::acc)
|
[] => 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) =
|
function choose(o1 : option('a), o2 : option('a)) : option('a) =
|
||||||
if(is_some(o1)) o1 else o2
|
if(is_some(o1)) o1 else o2
|
||||||
|
|
||||||
|
/** Choose `Some` from list of options if possible
|
||||||
|
*/
|
||||||
function choose_first(l : list(option('a))) : option('a) = switch(l)
|
function choose_first(l : list(option('a))) : option('a) = switch(l)
|
||||||
[] => None
|
[] => None
|
||||||
None::t => choose_first(t)
|
None::t => choose_first(t)
|
||||||
Some(x)::_ => Some(x)
|
Some(x)::_ => Some(x)
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,18 @@ namespace Pair =
|
|||||||
function snd(t : ('a * 'b)) : 'b = switch(t)
|
function snd(t : ('a * 'b)) : 'b = switch(t)
|
||||||
(_, y) => y
|
(_, y) => y
|
||||||
|
|
||||||
|
/** Map over first
|
||||||
|
*/
|
||||||
function map1(f : 'a => 'c, t : ('a * 'b)) : ('c * 'b) = switch(t)
|
function map1(f : 'a => 'c, t : ('a * 'b)) : ('c * 'b) = switch(t)
|
||||||
(x, y) => (f(x), y)
|
(x, y) => (f(x), y)
|
||||||
|
|
||||||
|
/** Map over second
|
||||||
|
*/
|
||||||
function map2(f : 'b => 'c, t : ('a * 'b)) : ('a * 'c) = switch(t)
|
function map2(f : 'b => 'c, t : ('a * 'b)) : ('a * 'c) = switch(t)
|
||||||
(x, y) => (x, f(y))
|
(x, y) => (x, f(y))
|
||||||
|
|
||||||
|
/** Map over both
|
||||||
|
*/
|
||||||
function bimap(f : 'a => 'c, g : 'b => 'd, t : ('a * 'b)) : ('c * 'd) = switch(t)
|
function bimap(f : 'a => 'c, g : 'b => 'd, t : ('a * 'b)) : ('c * 'd) = switch(t)
|
||||||
(x, y) => (f(x), g(y))
|
(x, y) => (f(x), g(y))
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
(_, _, z) => z
|
||||||
|
|
||||||
|
|
||||||
|
/** Map over first
|
||||||
|
*/
|
||||||
function map1(f : 'a => 'm, t : ('a * 'b * 'c)) : ('m * 'b * 'c) = switch(t)
|
function map1(f : 'a => 'm, t : ('a * 'b * 'c)) : ('m * 'b * 'c) = switch(t)
|
||||||
(x, y, z) => (f(x), y, z)
|
(x, y, z) => (f(x), y, z)
|
||||||
|
|
||||||
|
/** Map over second
|
||||||
|
*/
|
||||||
function map2(f : 'b => 'm, t : ('a * 'b * 'c)) : ('a * 'm * 'c) = switch(t)
|
function map2(f : 'b => 'm, t : ('a * 'b * 'c)) : ('a * 'm * 'c) = switch(t)
|
||||||
(x, y, z) => (x, f(y), z)
|
(x, y, z) => (x, f(y), z)
|
||||||
|
|
||||||
|
/** Map over third
|
||||||
|
*/
|
||||||
function map3(f : 'c => 'm, t : ('a * 'b * 'c)) : ('a * 'b * 'm) = switch(t)
|
function map3(f : 'c => 'm, t : ('a * 'b * 'c)) : ('a * 'b * 'm) = switch(t)
|
||||||
(x, y, z) => (x, y, f(z))
|
(x, y, z) => (x, y, f(z))
|
||||||
|
|
||||||
|
/** Map over all elements
|
||||||
|
*/
|
||||||
function trimap( f : 'a => 'x
|
function trimap( f : 'a => 'x
|
||||||
, g : 'b => 'y
|
, g : 'b => 'y
|
||||||
, h : 'c => 'z
|
, h : 'c => 'z
|
||||||
@@ -29,9 +37,13 @@ namespace Triple =
|
|||||||
function swap(t : ('a * 'b * 'c)) : ('c * 'b * 'a) = switch(t)
|
function swap(t : ('a * 'b * 'c)) : ('c * 'b * 'a) = switch(t)
|
||||||
(x, y, z) => (z, y, x)
|
(x, y, z) => (z, y, x)
|
||||||
|
|
||||||
|
/** Right rotation
|
||||||
|
*/
|
||||||
function rotr(t : ('a * 'b * 'c)) : ('c * 'a * 'b) = switch(t)
|
function rotr(t : ('a * 'b * 'c)) : ('c * 'a * 'b) = switch(t)
|
||||||
(x, y, z) => (z, x, y)
|
(x, y, z) => (z, x, y)
|
||||||
|
|
||||||
|
/** Left rotation
|
||||||
|
*/
|
||||||
function rotl(t : ('a * 'b * 'c)) : ('b * 'c * 'a) = switch(t)
|
function rotl(t : ('a * 'b * 'c)) : ('b * 'c * 'a) = switch(t)
|
||||||
(x, y, z) => (y, z, x)
|
(x, y, z) => (y, z, x)
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
{erl_opts, [debug_info]}.
|
{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"}
|
, {getopt, "1.0.1"}
|
||||||
, {eblake2, "1.0.0"}
|
, {eblake2, "1.0.0"}
|
||||||
, {jsx, {git, "https://github.com/talentdeficit/jsx.git",
|
, {jsx, {git, "https://github.com/talentdeficit/jsx.git",
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
{base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]}
|
{base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]}
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
{relx, [{release, {aesophia, "4.0.0"},
|
{relx, [{release, {aesophia, "4.3.0"},
|
||||||
[aesophia, aebytecode, getopt]},
|
[aesophia, aebytecode, getopt]},
|
||||||
|
|
||||||
{dev_mode, true},
|
{dev_mode, true},
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{"1.1.0",
|
{"1.1.0",
|
||||||
[{<<"aebytecode">>,
|
[{<<"aebytecode">>,
|
||||||
{git,"https://github.com/aeternity/aebytecode.git",
|
{git,"https://github.com/aeternity/aebytecode.git",
|
||||||
{ref,"4f4d6d30cd2c46b3830454d650a424d513f69134"}},
|
{ref,"7f0d3090d4dc6c4d5fca7645b0c21eb0e65ad208"}},
|
||||||
0},
|
0},
|
||||||
{<<"aeserialization">>,
|
{<<"aeserialization">>,
|
||||||
{git,"https://github.com/aeternity/aeserialization.git",
|
{git,"https://github.com/aeternity/aeserialization.git",
|
||||||
|
|||||||
+16
-8
@@ -14,6 +14,8 @@
|
|||||||
, contract_interface/2
|
, contract_interface/2
|
||||||
, contract_interface/3
|
, contract_interface/3
|
||||||
|
|
||||||
|
, from_typed_ast/2
|
||||||
|
|
||||||
, render_aci_json/1
|
, render_aci_json/1
|
||||||
|
|
||||||
, json_encode_expr/1
|
, json_encode_expr/1
|
||||||
@@ -23,6 +25,8 @@
|
|||||||
-type json() :: jsx:json_term().
|
-type json() :: jsx:json_term().
|
||||||
-type json_text() :: binary().
|
-type json_text() :: binary().
|
||||||
|
|
||||||
|
-export_type([aci_type/0]).
|
||||||
|
|
||||||
%% External API
|
%% External API
|
||||||
-spec file(aci_type(), string()) -> {ok, json() | string()} | {error, term()}.
|
-spec file(aci_type(), string()) -> {ok, json() | string()} | {error, term()}.
|
||||||
file(Type, File) ->
|
file(Type, File) ->
|
||||||
@@ -65,18 +69,20 @@ do_contract_interface(Type, ContractString, Options) ->
|
|||||||
try
|
try
|
||||||
Ast = aeso_compiler:parse(ContractString, Options),
|
Ast = aeso_compiler:parse(ContractString, Options),
|
||||||
%% io:format("~p\n", [Ast]),
|
%% 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]),
|
%% io:format("~p\n", [TypedAst]),
|
||||||
JArray = [ encode_contract(C) || C <- TypedAst ],
|
from_typed_ast(Type, TypedAst)
|
||||||
|
|
||||||
case Type of
|
|
||||||
json -> {ok, JArray};
|
|
||||||
string -> do_render_aci_json(JArray)
|
|
||||||
end
|
|
||||||
catch
|
catch
|
||||||
throw:{error, Errors} -> {error, Errors}
|
throw:{error, Errors} -> {error, Errors}
|
||||||
end.
|
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}, _}) ->
|
encode_contract(Contract = {contract, _, {con, _, Name}, _}) ->
|
||||||
C0 = #{name => encode_name(Name)},
|
C0 = #{name => encode_name(Name)},
|
||||||
|
|
||||||
@@ -129,7 +135,7 @@ encode_anon_args(Types) ->
|
|||||||
|
|
||||||
encode_args(Args) -> [ encode_arg(A) || A <- Args ].
|
encode_args(Args) -> [ encode_arg(A) || A <- Args ].
|
||||||
|
|
||||||
encode_arg({arg, _, Id, T}) ->
|
encode_arg({typed, _, Id, T}) ->
|
||||||
#{name => encode_type(Id),
|
#{name => encode_type(Id),
|
||||||
type => encode_type(T)}.
|
type => encode_type(T)}.
|
||||||
|
|
||||||
@@ -194,6 +200,8 @@ encode_expr({bytes, _, B}) ->
|
|||||||
encode_expr({Lit, _, L}) when Lit == oracle_pubkey; Lit == oracle_query_id;
|
encode_expr({Lit, _, L}) when Lit == oracle_pubkey; Lit == oracle_query_id;
|
||||||
Lit == contract_pubkey; Lit == account_pubkey ->
|
Lit == contract_pubkey; Lit == account_pubkey ->
|
||||||
aeser_api_encoder:encode(Lit, L);
|
aeser_api_encoder:encode(Lit, L);
|
||||||
|
encode_expr({app, _, {'-', _}, [{int, _, N}]}) ->
|
||||||
|
encode_expr({int, [], -N});
|
||||||
encode_expr({app, _, F, As}) ->
|
encode_expr({app, _, F, As}) ->
|
||||||
Ef = encode_expr(F),
|
Ef = encode_expr(F),
|
||||||
Eas = encode_exprs(As),
|
Eas = encode_exprs(As),
|
||||||
|
|||||||
+443
-114
@@ -12,7 +12,11 @@
|
|||||||
|
|
||||||
-module(aeso_ast_infer_types).
|
-module(aeso_ast_infer_types).
|
||||||
|
|
||||||
-export([infer/1, infer/2, unfold_types_in_type/3]).
|
-export([ infer/1
|
||||||
|
, infer/2
|
||||||
|
, unfold_types_in_type/3
|
||||||
|
, pp_type/2
|
||||||
|
]).
|
||||||
|
|
||||||
-type utype() :: {fun_t, aeso_syntax:ann(), named_args_t(), [utype()], utype()}
|
-type utype() :: {fun_t, aeso_syntax:ann(), named_args_t(), [utype()], utype()}
|
||||||
| {app_t, aeso_syntax:ann(), utype(), [utype()]}
|
| {app_t, aeso_syntax:ann(), utype(), [utype()]}
|
||||||
@@ -20,6 +24,7 @@
|
|||||||
| aeso_syntax:id() | aeso_syntax:qid()
|
| aeso_syntax:id() | aeso_syntax:qid()
|
||||||
| aeso_syntax:con() | aeso_syntax:qcon() %% contracts
|
| aeso_syntax:con() | aeso_syntax:qcon() %% contracts
|
||||||
| aeso_syntax:tvar()
|
| aeso_syntax:tvar()
|
||||||
|
| {if_t, aeso_syntax:ann(), aeso_syntax:id(), utype(), utype()} %% Can branch on named argument (protected)
|
||||||
| uvar().
|
| uvar().
|
||||||
|
|
||||||
-type uvar() :: {uvar, aeso_syntax:ann(), reference()}.
|
-type uvar() :: {uvar, aeso_syntax:ann(), reference()}.
|
||||||
@@ -43,7 +48,14 @@
|
|||||||
name :: aeso_syntax:id(),
|
name :: aeso_syntax:id(),
|
||||||
type :: utype()}).
|
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(field_constraint,
|
||||||
{ record_t :: utype()
|
{ record_t :: utype()
|
||||||
@@ -232,16 +244,21 @@ bind_fields([], Env) -> Env;
|
|||||||
bind_fields([{Id, Info} | Rest], Env) ->
|
bind_fields([{Id, Info} | Rest], Env) ->
|
||||||
bind_fields(Rest, bind_field(Id, Info, 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}) ->
|
contract_call_type({fun_t, Ann, [], Args, Ret}) ->
|
||||||
Id = fun(X) -> {id, Ann, X} end,
|
Id = fun(X) -> {id, Ann, X} end,
|
||||||
Int = Id("int"),
|
Int = Id("int"),
|
||||||
Typed = fun(E, T) -> {typed, Ann, E, T} end,
|
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, [Named("gas", Typed({app, Ann, Typed({qid, Ann, ["Call", "gas_left"]},
|
||||||
{fun_t, Ann, [], [], Int}),
|
{fun_t, Ann, [], [], Int}),
|
||||||
[]}, 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().
|
-spec bind_contract(aeso_syntax:decl(), env()) -> env().
|
||||||
bind_contract({contract, Ann, Id, Contents}, Env) ->
|
bind_contract({contract, Ann, Id, Contents}, Env) ->
|
||||||
@@ -364,6 +381,7 @@ is_private(Ann) -> proplists:get_value(private, Ann, false).
|
|||||||
global_env() ->
|
global_env() ->
|
||||||
Ann = [{origin, system}],
|
Ann = [{origin, system}],
|
||||||
Int = {id, Ann, "int"},
|
Int = {id, Ann, "int"},
|
||||||
|
Char = {id, Ann, "char"},
|
||||||
Bool = {id, Ann, "bool"},
|
Bool = {id, Ann, "bool"},
|
||||||
String = {id, Ann, "string"},
|
String = {id, Ann, "string"},
|
||||||
Address = {id, Ann, "address"},
|
Address = {id, Ann, "address"},
|
||||||
@@ -389,6 +407,24 @@ global_env() ->
|
|||||||
Signature = {named_arg_t, Ann, SignId, SignId, {typed, Ann, SignDef, SignId}},
|
Signature = {named_arg_t, Ann, SignId, SignId, {typed, Ann, SignDef, SignId}},
|
||||||
SignFun = fun(Ts, T) -> {type_sig, [stateful|Ann], none, [Signature], Ts, T} end,
|
SignFun = fun(Ts, T) -> {type_sig, [stateful|Ann], none, [Signature], Ts, T} end,
|
||||||
TTL = {qid, Ann, ["Chain", "ttl"]},
|
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,
|
Fee = Int,
|
||||||
[A, Q, R, K, V] = lists:map(TVar, ["a", "q", "r", "k", "v"]),
|
[A, Q, R, K, V] = lists:map(TVar, ["a", "q", "r", "k", "v"]),
|
||||||
|
|
||||||
@@ -426,8 +462,36 @@ global_env() ->
|
|||||||
{"timestamp", Int},
|
{"timestamp", Int},
|
||||||
{"block_height", Int},
|
{"block_height", Int},
|
||||||
{"difficulty", Int},
|
{"difficulty", Int},
|
||||||
{"gas_limit", Int}])
|
{"gas_limit", Int},
|
||||||
, types = MkDefs([{"ttl", 0}]) },
|
%% 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
|
ContractScope = #scope
|
||||||
{ funs = MkDefs(
|
{ funs = MkDefs(
|
||||||
@@ -447,6 +511,7 @@ global_env() ->
|
|||||||
OracleScope = #scope
|
OracleScope = #scope
|
||||||
{ funs = MkDefs(
|
{ funs = MkDefs(
|
||||||
[{"register", SignFun([Address, Fee, TTL], Oracle(Q, R))},
|
[{"register", SignFun([Address, Fee, TTL], Oracle(Q, R))},
|
||||||
|
{"expiry", Fun([Oracle(Q, R)], Fee)},
|
||||||
{"query_fee", Fun([Oracle(Q, R)], Fee)},
|
{"query_fee", Fun([Oracle(Q, R)], Fee)},
|
||||||
{"query", StateFun([Oracle(Q, R), Q, Fee, TTL, TTL], Query(Q, R))},
|
{"query", StateFun([Oracle(Q, R), Q, Fee, TTL, TTL], Query(Q, R))},
|
||||||
{"get_question", Fun([Oracle(Q, R), Query(Q, R)], Q)},
|
{"get_question", Fun([Oracle(Q, R), Query(Q, R)], Q)},
|
||||||
@@ -462,7 +527,18 @@ global_env() ->
|
|||||||
{"preclaim", SignFun([Address, Hash], Unit)},
|
{"preclaim", SignFun([Address, Hash], Unit)},
|
||||||
{"claim", SignFun([Address, String, Int, Int], Unit)},
|
{"claim", SignFun([Address, String, Int, Int], Unit)},
|
||||||
{"transfer", SignFun([Address, Address, String], 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
|
MapScope = #scope
|
||||||
{ funs = MkDefs(
|
{ funs = MkDefs(
|
||||||
@@ -485,19 +561,65 @@ global_env() ->
|
|||||||
{"sha256", Fun1(A, Hash)},
|
{"sha256", Fun1(A, Hash)},
|
||||||
{"blake2b", 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
|
%% Authentication
|
||||||
AuthScope = #scope
|
AuthScope = #scope
|
||||||
{ funs = MkDefs(
|
{ funs = MkDefs(
|
||||||
[{"tx_hash", Option(Hash)}]) },
|
[{"tx_hash", Option(Hash)},
|
||||||
|
{"tx", Option(Tx)} ]) },
|
||||||
|
|
||||||
%% Strings
|
%% Strings
|
||||||
StringScope = #scope
|
StringScope = #scope
|
||||||
{ funs = MkDefs(
|
{ funs = MkDefs(
|
||||||
[{"length", Fun1(String, Int)},
|
[{"length", Fun1(String, Int)},
|
||||||
{"concat", Fun([String, String], String)},
|
{"concat", Fun([String, String], String)},
|
||||||
{"sha3", Fun1(String, Hash)},
|
{"to_list", Fun1(String, List(Char))},
|
||||||
{"sha256", Fun1(String, Hash)},
|
{"from_list", Fun1(List(Char), String)},
|
||||||
{"blake2b", Fun1(String, Hash)}]) },
|
{"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
|
%% Bits
|
||||||
BitsScope = #scope
|
BitsScope = #scope
|
||||||
@@ -529,6 +651,7 @@ global_env() ->
|
|||||||
{"is_contract", Fun1(Address, Bool)},
|
{"is_contract", Fun1(Address, Bool)},
|
||||||
{"is_payable", Fun1(Address, Bool)}]) },
|
{"is_payable", Fun1(Address, Bool)}]) },
|
||||||
|
|
||||||
|
|
||||||
#env{ scopes =
|
#env{ scopes =
|
||||||
#{ [] => TopScope
|
#{ [] => TopScope
|
||||||
, ["Chain"] => ChainScope
|
, ["Chain"] => ChainScope
|
||||||
@@ -539,27 +662,38 @@ global_env() ->
|
|||||||
, ["Map"] => MapScope
|
, ["Map"] => MapScope
|
||||||
, ["Auth"] => AuthScope
|
, ["Auth"] => AuthScope
|
||||||
, ["Crypto"] => CryptoScope
|
, ["Crypto"] => CryptoScope
|
||||||
, ["String"] => StringScope
|
, ["MCL_BLS12_381"] => MCL_BLS12_381_Scope
|
||||||
|
, ["StringInternal"] => StringScope
|
||||||
|
, ["Char"] => CharScope
|
||||||
, ["Bits"] => BitsScope
|
, ["Bits"] => BitsScope
|
||||||
, ["Bytes"] => BytesScope
|
, ["Bytes"] => BytesScope
|
||||||
, ["Int"] => IntScope
|
, ["Int"] => IntScope
|
||||||
, ["Address"] => AddressScope
|
, ["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]}.
|
option_t(As, T) -> {app_t, As, {id, As, "option"}, [T]}.
|
||||||
map_t(As, K, V) -> {app_t, As, {id, As, "map"}, [K, V]}.
|
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) ->
|
||||||
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().
|
-spec init_env(list(option())) -> env().
|
||||||
init_env(_Options) -> global_env().
|
init_env(_Options) -> global_env().
|
||||||
|
|
||||||
-spec infer(aeso_syntax:ast(), list(option())) ->
|
-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) ->
|
infer(Contracts, Options) ->
|
||||||
ets_init(), %% Init the ETS table state
|
ets_init(), %% Init the ETS table state
|
||||||
try
|
try
|
||||||
@@ -568,15 +702,15 @@ infer(Contracts, Options) ->
|
|||||||
ets_new(type_vars, [set]),
|
ets_new(type_vars, [set]),
|
||||||
check_modifiers(Env, Contracts),
|
check_modifiers(Env, Contracts),
|
||||||
{Env1, Decls} = infer1(Env, Contracts, [], Options),
|
{Env1, Decls} = infer1(Env, Contracts, [], Options),
|
||||||
{Env2, Decls2} =
|
{Env2, DeclsFolded, DeclsUnfolded} =
|
||||||
case proplists:get_value(dont_unfold, Options, false) of
|
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),
|
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,
|
end,
|
||||||
case proplists:get_value(return_env, Options, false) of
|
case proplists:get_value(return_env, Options, false) of
|
||||||
false -> Decls2;
|
false -> {DeclsFolded, DeclsUnfolded};
|
||||||
true -> {Env2, Decls2}
|
true -> {Env2, DeclsFolded, DeclsUnfolded}
|
||||||
end
|
end
|
||||||
after
|
after
|
||||||
clean_up_ets()
|
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())) ->
|
-spec infer_contract_top(env(), main_contract | contract | namespace, [aeso_syntax:decl()], list(option())) ->
|
||||||
{env(), [aeso_syntax:decl()]}.
|
{env(), [aeso_syntax:decl()]}.
|
||||||
infer_contract_top(Env, Kind, Defs0, _Options) ->
|
infer_contract_top(Env, Kind, Defs0, Options) ->
|
||||||
Defs = desugar(Defs0),
|
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
|
%% infer_contract takes a proplist mapping global names to types, and
|
||||||
%% a list of definitions.
|
%% a list of definitions.
|
||||||
-spec infer_contract(env(), main_contract | contract | namespace, [aeso_syntax:decl()]) -> {env(), [aeso_syntax:decl()]}.
|
-spec infer_contract(env(), main_contract | contract | namespace, [aeso_syntax:decl()], list(option())) -> {env(), [aeso_syntax:decl()]}.
|
||||||
infer_contract(Env0, What, Defs) ->
|
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 },
|
Env = Env0#env{ what = What },
|
||||||
Kind = fun({type_def, _, _, _, _}) -> type;
|
Kind = fun({type_def, _, _, _, _}) -> type;
|
||||||
({letfun, _, _, _, _, _}) -> function;
|
({letfun, _, _, _, _, _}) -> function;
|
||||||
({fun_decl, _, _, _}) -> prototype;
|
({fun_clauses, _, _, _, _}) -> function;
|
||||||
(_) -> unexpected
|
({fun_decl, _, _, _}) -> prototype;
|
||||||
|
(_) -> unexpected
|
||||||
end,
|
end,
|
||||||
Get = fun(K) -> [ Def || Def <- Defs, Kind(Def) == K ] end,
|
Get = fun(K) -> [ Def || Def <- Defs, Kind(Def) == K ] end,
|
||||||
{Env1, TypeDefs} = check_typedefs(Env, Get(type)),
|
{Env1, TypeDefs} = check_typedefs(Env, Get(type)),
|
||||||
@@ -642,9 +784,11 @@ infer_contract(Env0, What, Defs) ->
|
|||||||
Env3 = bind_funs(ProtoSigs, Env2),
|
Env3 = bind_funs(ProtoSigs, Env2),
|
||||||
Functions = Get(function),
|
Functions = Get(function),
|
||||||
%% Check for duplicates in Functions (we turn it into a map below)
|
%% Check for duplicates in Functions (we turn it into a map below)
|
||||||
_ = bind_funs([{Fun, {tuple_t, Ann, []}} || {letfun, Ann, {id, _, Fun}, _, _, _} <- Functions],
|
FunBind = fun({letfun, Ann, {id, _, Fun}, _, _, _}) -> {Fun, {tuple_t, Ann, []}};
|
||||||
#env{}),
|
({fun_clauses, Ann, {id, _, Fun}, _, _}) -> {Fun, {tuple_t, Ann, []}} end,
|
||||||
FunMap = maps:from_list([ {Fun, Def} || Def = {letfun, _, {id, _, Fun}, _, _, _} <- Functions ]),
|
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),
|
check_reserved_entrypoints(FunMap),
|
||||||
DepGraph = maps:map(fun(_, Def) -> aeso_syntax_utils:used_ids(Def) end, FunMap),
|
DepGraph = maps:map(fun(_, Def) -> aeso_syntax_utils:used_ids(Def) end, FunMap),
|
||||||
SCCs = aeso_utils:scc(DepGraph),
|
SCCs = aeso_utils:scc(DepGraph),
|
||||||
@@ -655,6 +799,49 @@ infer_contract(Env0, What, Defs) ->
|
|||||||
destroy_and_report_type_errors(Env4),
|
destroy_and_report_type_errors(Env4),
|
||||||
{Env4, TypeDefs ++ Decls ++ Defs1}.
|
{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()]}.
|
-spec check_typedefs(env(), [aeso_syntax:decl()]) -> {env(), [aeso_syntax:decl()]}.
|
||||||
check_typedefs(Env = #env{ namespace = Ns }, Defs) ->
|
check_typedefs(Env = #env{ namespace = Ns }, Defs) ->
|
||||||
create_type_errors(),
|
create_type_errors(),
|
||||||
@@ -787,9 +974,9 @@ check_type(Env, T) ->
|
|||||||
check_type(Env, T = {tvar, _, _}, Arity) ->
|
check_type(Env, T = {tvar, _, _}, Arity) ->
|
||||||
[ type_error({higher_kinded_typevar, T}) || Arity /= 0 ],
|
[ type_error({higher_kinded_typevar, T}) || Arity /= 0 ],
|
||||||
check_tvar(Env, T);
|
check_tvar(Env, T);
|
||||||
check_type(_Env, X = {id, _, "_"}, Arity) ->
|
check_type(_Env, X = {id, Ann, "_"}, Arity) ->
|
||||||
ensure_base_type(X, 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 ->
|
check_type(Env, X = {Tag, _, _}, Arity) when Tag == con; Tag == qcon; Tag == id; Tag == qid ->
|
||||||
case lookup_type(Env, X) of
|
case lookup_type(Env, X) of
|
||||||
{Q, {_, Def}} ->
|
{Q, {_, Def}} ->
|
||||||
@@ -960,8 +1147,9 @@ typesig_to_fun_t({type_sig, Ann, _Constr, Named, Args, Res}) ->
|
|||||||
|
|
||||||
infer_letrec(Env, Defs) ->
|
infer_letrec(Env, Defs) ->
|
||||||
create_constraints(),
|
create_constraints(),
|
||||||
Funs = [{Name, fresh_uvar(A)}
|
Funs = lists:map(fun({letfun, _, {id, Ann, Name}, _, _, _}) -> {Name, fresh_uvar(Ann)};
|
||||||
|| {letfun, _, {id, A, Name}, _, _, _} <- Defs],
|
({fun_clauses, _, {id, Ann, Name}, _, _}) -> {Name, fresh_uvar(Ann)}
|
||||||
|
end, Defs),
|
||||||
ExtendEnv = bind_funs(Funs, Env),
|
ExtendEnv = bind_funs(Funs, Env),
|
||||||
Inferred =
|
Inferred =
|
||||||
[ begin
|
[ begin
|
||||||
@@ -980,26 +1168,51 @@ infer_letrec(Env, Defs) ->
|
|||||||
[print_typesig(S) || S <- TypeSigs],
|
[print_typesig(S) || S <- TypeSigs],
|
||||||
{TypeSigs, NewDefs}.
|
{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),
|
Env = Env0#env{ stateful = aeso_syntax:get_ann(stateful, Attrib, false),
|
||||||
current_function = Fun },
|
current_function = Fun },
|
||||||
check_unique_arg_names(Fun, Args),
|
{NewEnv, {typed, _, {tuple, _, TypedArgs}, {tuple_t, _, ArgTypes}}} = infer_pattern(Env, {tuple, [{origin, system} | NameAttrib], Args}),
|
||||||
ArgTypes = [{ArgName, check_type(Env, arg_type(ArgAnn, T))} || {arg, ArgAnn, ArgName, T} <- Args],
|
|
||||||
ExpectedType = check_type(Env, arg_type(NameAttrib, What)),
|
ExpectedType = check_type(Env, arg_type(NameAttrib, What)),
|
||||||
NewBody={typed, _, _, ResultType} = check_expr(bind_vars(ArgTypes, Env), Body, ExpectedType),
|
NewBody={typed, _, _, ResultType} = check_expr(NewEnv, Body, ExpectedType),
|
||||||
NewArgs = [{arg, A1, {id, A2, ArgName}, T}
|
|
||||||
|| {{_, T}, {arg, A1, {id, A2, ArgName}, _}} <- lists:zip(ArgTypes, Args)],
|
|
||||||
NamedArgs = [],
|
NamedArgs = [],
|
||||||
TypeSig = {type_sig, Attrib, none, NamedArgs, [T || {arg, _, _, T} <- NewArgs], ResultType},
|
TypeSig = {type_sig, Attrib, none, NamedArgs, ArgTypes, ResultType},
|
||||||
{{Name, TypeSig},
|
{{Name, TypeSig},
|
||||||
{letfun, Attrib, {id, NameAttrib, Name}, NewArgs, ResultType, NewBody}}.
|
{letfun, Attrib, {id, NameAttrib, Name}, TypedArgs, ResultType, NewBody}}.
|
||||||
|
|
||||||
check_unique_arg_names(Fun, Args) ->
|
desugar_clauses(Ann, Fun, {type_sig, _, _, _, ArgTypes, RetType}, Clauses) ->
|
||||||
Name = fun({arg, _, {id, _, X}, _}) -> X end,
|
NeedDesugar =
|
||||||
Names = lists:map(Name, Args),
|
case Clauses of
|
||||||
Dups = lists:usort(Names -- lists:usort(Names)),
|
[{letfun, _, _, As, _, _}] -> lists:any(fun({typed, _, {id, _, _}, _}) -> false; (_) -> true end, As);
|
||||||
[ type_error({repeated_arg, Fun, Arg}) || Arg <- Dups ],
|
_ -> true
|
||||||
ok.
|
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_typesig({Name, TypeSig}) ->
|
||||||
?PRINT_TYPES("Inferred ~s : ~s\n", [Name, pp(TypeSig)]).
|
?PRINT_TYPES("Inferred ~s : ~s\n", [Name, pp(TypeSig)]).
|
||||||
@@ -1092,9 +1305,9 @@ get_call_chains(Graph, Visited, Queue, Stop, Acc) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
check_expr(Env, Expr, Type) ->
|
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}),
|
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, _}) ->
|
infer_expr(_Env, Body={bool, As, _}) ->
|
||||||
{typed, As, Body, {id, As, "bool"}};
|
{typed, As, Body, {id, As, "bool"}};
|
||||||
@@ -1138,21 +1351,20 @@ infer_expr(Env, {list, As, Elems}) ->
|
|||||||
NewElems = [check_expr(Env, X, ElemType) || X <- Elems],
|
NewElems = [check_expr(Env, X, ElemType) || X <- Elems],
|
||||||
{typed, As, {list, As, NewElems}, {app_t, As, {id, As, "list"}, [ElemType]}};
|
{typed, As, {list, As, NewElems}, {app_t, As, {id, As, "list"}, [ElemType]}};
|
||||||
infer_expr(Env, {list_comp, As, Yield, []}) ->
|
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]}};
|
{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]}) ->
|
infer_expr(Env, {list_comp, As, Yield, [{comprehension_bind, Pat, BExpr}|Rest]}) ->
|
||||||
BindVarType = fresh_uvar(As),
|
|
||||||
TypedBind = {typed, As2, _, TypeBExpr} = infer_expr(Env, BExpr),
|
TypedBind = {typed, As2, _, TypeBExpr} = infer_expr(Env, BExpr),
|
||||||
|
{NewE, TypedPat = {typed, _, _, PatType}} = infer_pattern(Env, Pat),
|
||||||
unify( Env
|
unify( Env
|
||||||
, TypeBExpr
|
, TypeBExpr
|
||||||
, {app_t, As, {id, As, "list"}, [BindVarType]}
|
, {app_t, As, {id, As, "list"}, [PatType]}
|
||||||
, {list_comp, TypedBind, TypeBExpr, {app_t, As2, {id, As, "list"}, [BindVarType]}}),
|
, {list_comp, TypedBind, TypeBExpr, {app_t, As2, {id, As, "list"}, [PatType]}}),
|
||||||
NewE = bind_var(Arg, BindVarType, Env),
|
|
||||||
{typed, _, {list_comp, _, TypedYield, TypedRest}, ResType} =
|
{typed, _, {list_comp, _, TypedYield, TypedRest}, ResType} =
|
||||||
infer_expr(NewE, {list_comp, As, Yield, Rest}),
|
infer_expr(NewE, {list_comp, As, Yield, Rest}),
|
||||||
{ typed
|
{ typed
|
||||||
, As
|
, As
|
||||||
, {list_comp, As, TypedYield, [{comprehension_bind, {typed, Arg, BindVarType}, TypedBind}|TypedRest]}
|
, {list_comp, As, TypedYield, [{comprehension_bind, TypedPat, TypedBind}|TypedRest]}
|
||||||
, ResType};
|
, ResType};
|
||||||
infer_expr(Env, {list_comp, AttrsL, Yield, [{comprehension_if, AttrsIF, Cond}|Rest]}) ->
|
infer_expr(Env, {list_comp, AttrsL, Yield, [{comprehension_if, AttrsIF, Cond}|Rest]}) ->
|
||||||
NewCond = check_expr(Env, Cond, {id, AttrsIF, "bool"}),
|
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
|
, AttrsL
|
||||||
, {list_comp, AttrsL, TypedYield, [{comprehension_if, AttrsIF, NewCond}|TypedRest]}
|
, {list_comp, AttrsL, TypedYield, [{comprehension_if, AttrsIF, NewCond}|TypedRest]}
|
||||||
, ResType};
|
, ResType};
|
||||||
infer_expr(Env, {list_comp, AsLC, Yield, [{letval, AsLV, Pattern, Type, E}|Rest]}) ->
|
infer_expr(Env, {list_comp, AsLC, Yield, [{letval, AsLV, Pattern, E}|Rest]}) ->
|
||||||
NewE = {typed, _, _, PatType} = infer_expr(Env, {typed, AsLV, E, arg_type(AsLV, Type)}),
|
NewE = {typed, _, _, PatType} = infer_expr(Env, E),
|
||||||
BlockType = fresh_uvar(AsLV),
|
BlockType = fresh_uvar(AsLV),
|
||||||
{'case', _, NewPattern, NewRest} =
|
{'case', _, NewPattern, NewRest} =
|
||||||
infer_case( Env
|
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, _, {list_comp, _, TypedYield, TypedRest}, ResType} = NewRest,
|
||||||
{ typed
|
{ typed
|
||||||
, AsLC
|
, AsLC
|
||||||
, {list_comp, AsLC, TypedYield, [{letval, AsLV, NewPattern, Type, NewE}|TypedRest]}
|
, {list_comp, AsLC, TypedYield, [{letval, AsLV, NewPattern, NewE}|TypedRest]}
|
||||||
, ResType
|
, ResType
|
||||||
};
|
};
|
||||||
infer_expr(Env, {list_comp, AsLC, Yield, [Def={letfun, AsLF, _, _, _, _}|Rest]}) ->
|
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),
|
Type1 = check_type(Env, Type),
|
||||||
{typed, _, NewBody, NewType} = check_expr(Env, Body, Type1),
|
{typed, _, NewBody, NewType} = check_expr(Env, Body, Type1),
|
||||||
{typed, As, NewBody, NewType};
|
{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 ],
|
NamedArgs = [ Arg || Arg = {named_arg, _, _, _} <- Args0 ],
|
||||||
Args = Args0 -- NamedArgs,
|
Args = Args0 -- NamedArgs,
|
||||||
case aeso_syntax:get_ann(format, Ann) of
|
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),
|
NewFun={typed, _, _, FunType} = infer_expr(Env, Fun),
|
||||||
NewArgs = [infer_expr(Env, A) || A <- Args],
|
NewArgs = [infer_expr(Env, A) || A <- Args],
|
||||||
ArgTypes = [T || {typed, _, _, T} <- NewArgs],
|
ArgTypes = [T || {typed, _, _, T} <- NewArgs],
|
||||||
|
GeneralResultType = fresh_uvar(Ann),
|
||||||
ResultType = 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)}
|
{typed, Ann, {app, Ann, NewFun, NamedArgs1 ++ NewArgs}, dereference(ResultType)}
|
||||||
end;
|
end;
|
||||||
infer_expr(Env, {'if', Attrs, Cond, Then, Else}) ->
|
infer_expr(Env, {'if', Attrs, Cond, Then, Else}) ->
|
||||||
@@ -1290,6 +1510,16 @@ infer_expr(Env, {block, Attrs, Stmts}) ->
|
|||||||
BlockType = fresh_uvar(Attrs),
|
BlockType = fresh_uvar(Attrs),
|
||||||
NewStmts = infer_block(Env, Attrs, Stmts, BlockType),
|
NewStmts = infer_block(Env, Attrs, Stmts, BlockType),
|
||||||
{typed, Attrs, {block, Attrs, NewStmts}, 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}) ->
|
infer_expr(Env, {lam, Attrs, Args, Body}) ->
|
||||||
ArgTypes = [fresh_uvar(As) || {arg, As, _, _} <- Args],
|
ArgTypes = [fresh_uvar(As) || {arg, As, _, _} <- Args],
|
||||||
ArgPatterns = [{typed, As, Pat, check_type(Env, T)} || {arg, As, Pat, T} <- 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),
|
infer_case(Env, Attrs, {tuple, Attrs, ArgPatterns}, {tuple_t, Attrs, ArgTypes}, Body, ResultType),
|
||||||
NewArgs = [{arg, As, NewPat, NewT} || {typed, As, NewPat, NewT} <- NewArgPatterns],
|
NewArgs = [{arg, As, NewPat, NewT} || {typed, As, NewPat, NewT} <- NewArgPatterns],
|
||||||
{typed, Attrs, {lam, Attrs, NewArgs, NewBody}, {fun_t, Attrs, [], ArgTypes, ResultType}};
|
{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}),
|
type_error({missing_body_for_let, Attrs}),
|
||||||
infer_expr(Env, {block, Attrs, [Let, abort_expr(Attrs, "missing body")]});
|
infer_expr(Env, {block, Attrs, [Let, abort_expr(Attrs, "missing body")]});
|
||||||
infer_expr(Env, Let = {letfun, Attrs, _, _, _, _}) ->
|
infer_expr(Env, Let = {letfun, Attrs, _, _, _, _}) ->
|
||||||
@@ -1358,18 +1588,22 @@ infer_op(Env, As, Op, Args, InferOp) ->
|
|||||||
TypedArgs = [infer_expr(Env, A) || A <- Args],
|
TypedArgs = [infer_expr(Env, A) || A <- Args],
|
||||||
ArgTypes = [T || {typed, _, _, T} <- TypedArgs],
|
ArgTypes = [T || {typed, _, _, T} <- TypedArgs],
|
||||||
Inferred = {fun_t, _, _, OperandTypes, ResultType} = InferOp(Op),
|
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}.
|
{typed, As, {app, As, Op, TypedArgs}, ResultType}.
|
||||||
|
|
||||||
infer_case(Env, Attrs, Pattern, ExprType, Branch, SwitchType) ->
|
infer_pattern(Env, Pattern) ->
|
||||||
Vars = free_vars(Pattern),
|
Vars = free_vars(Pattern),
|
||||||
Names = [N || {id, _, N} <- Vars, N /= "_"],
|
Names = [N || {id, _, N} <- Vars, N /= "_"],
|
||||||
case Names -- lists:usort(Names) of
|
case Names -- lists:usort(Names) of
|
||||||
[] -> ok;
|
[] -> ok;
|
||||||
Nonlinear -> type_error({non_linear_pattern, Pattern, lists:usort(Nonlinear)})
|
Nonlinear -> type_error({non_linear_pattern, Pattern, lists:usort(Nonlinear)})
|
||||||
end,
|
end,
|
||||||
NewEnv = bind_vars([{Var, fresh_uvar(Ann)} || Var = {id, Ann, _} <- Vars], Env#env{ in_pattern = true }),
|
NewEnv = bind_vars([{Var, fresh_uvar(Ann1)} || Var = {id, Ann1, _} <- Vars], Env#env{ in_pattern = true }),
|
||||||
NewPattern = {typed, _, _, PatType} = infer_expr(NewEnv, Pattern),
|
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),
|
NewBranch = check_expr(NewEnv#env{ in_pattern = false }, Branch, SwitchType),
|
||||||
unify(Env, PatType, ExprType, {case_pat, Pattern, PatType, ExprType}),
|
unify(Env, PatType, ExprType, {case_pat, Pattern, PatType, ExprType}),
|
||||||
{'case', Attrs, NewPattern, NewBranch}.
|
{'case', Attrs, NewPattern, NewBranch}.
|
||||||
@@ -1384,11 +1618,11 @@ infer_block(Env, Attrs, [Def={letfun, Ann, _, _, _, _}|Rest], BlockType) ->
|
|||||||
FunT = typesig_to_fun_t(TypeSig),
|
FunT = typesig_to_fun_t(TypeSig),
|
||||||
NewE = bind_var({id, Ann, Name}, FunT, Env),
|
NewE = bind_var({id, Ann, Name}, FunT, Env),
|
||||||
[LetFun|infer_block(NewE, Attrs, Rest, BlockType)];
|
[LetFun|infer_block(NewE, Attrs, Rest, BlockType)];
|
||||||
infer_block(Env, _, [{letval, Attrs, Pattern, Type, E}|Rest], BlockType) ->
|
infer_block(Env, _, [{letval, Attrs, Pattern, E}|Rest], BlockType) ->
|
||||||
NewE = {typed, _, _, PatType} = infer_expr(Env, {typed, Attrs, E, arg_type(aeso_syntax:get_ann(Pattern), Type)}),
|
NewE = {typed, _, _, PatType} = infer_expr(Env, E),
|
||||||
{'case', _, NewPattern, {typed, _, {block, _, NewRest}, _}} =
|
{'case', _, NewPattern, {typed, _, {block, _, NewRest}, _}} =
|
||||||
infer_case(Env, Attrs, Pattern, PatType, {block, Attrs, Rest}, BlockType),
|
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_block(Env, Attrs, [E|Rest], BlockType) ->
|
||||||
[infer_expr(Env, E)|infer_block(Env, Attrs, 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) ->
|
abort_expr(Ann, Str) ->
|
||||||
{app, Ann, {id, Ann, "abort"}, [{string, Ann, Str}]}.
|
{app, Ann, {id, Ann, "abort"}, [{string, Ann, Str}]}.
|
||||||
|
|
||||||
free_vars({int, _, _}) ->
|
free_vars({int, _, _}) -> [];
|
||||||
[];
|
free_vars({char, _, _}) -> [];
|
||||||
free_vars({char, _, _}) ->
|
free_vars({string, _, _}) -> [];
|
||||||
[];
|
free_vars({bool, _, _}) -> [];
|
||||||
free_vars({string, _, _}) ->
|
free_vars(Id={id, _, _}) -> [Id];
|
||||||
[];
|
free_vars({con, _, _}) -> [];
|
||||||
free_vars({bool, _, _}) ->
|
free_vars({qcon, _, _}) -> [];
|
||||||
[];
|
|
||||||
free_vars(Id={id, _, _}) ->
|
|
||||||
[Id];
|
|
||||||
free_vars({con, _, _}) ->
|
|
||||||
[];
|
|
||||||
free_vars({tuple, _, Cpts}) ->
|
free_vars({tuple, _, Cpts}) ->
|
||||||
free_vars(Cpts);
|
free_vars(Cpts);
|
||||||
free_vars({list, _, Elems}) ->
|
free_vars({list, _, Elems}) ->
|
||||||
@@ -1450,6 +1679,8 @@ free_vars({app, _, {'::', _}, Args}) ->
|
|||||||
free_vars(Args);
|
free_vars(Args);
|
||||||
free_vars({app, _, {con, _, _}, Args}) ->
|
free_vars({app, _, {con, _, _}, Args}) ->
|
||||||
free_vars(Args);
|
free_vars(Args);
|
||||||
|
free_vars({app, _, {qcon, _, _}, Args}) ->
|
||||||
|
free_vars(Args);
|
||||||
free_vars({record, _, Fields}) ->
|
free_vars({record, _, Fields}) ->
|
||||||
free_vars([E || {field, _, _, E} <- Fields]);
|
free_vars([E || {field, _, _, E} <- Fields]);
|
||||||
free_vars({typed, _, A, _}) ->
|
free_vars({typed, _, A, _}) ->
|
||||||
@@ -1528,12 +1759,12 @@ create_constraints() ->
|
|||||||
create_field_constraints().
|
create_field_constraints().
|
||||||
|
|
||||||
destroy_and_report_unsolved_constraints(Env) ->
|
destroy_and_report_unsolved_constraints(Env) ->
|
||||||
|
solve_field_constraints(Env),
|
||||||
solve_named_argument_constraints(Env),
|
solve_named_argument_constraints(Env),
|
||||||
solve_bytes_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_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 --
|
%% -- Named argument constraints --
|
||||||
|
|
||||||
@@ -1573,8 +1804,43 @@ check_named_argument_constraint(Env,
|
|||||||
type_error({bad_named_argument, Args, Id}),
|
type_error({bad_named_argument, Args, Id}),
|
||||||
false;
|
false;
|
||||||
[T] -> unify(Env, T, Type, {check_named_arg_constraint, C}), true
|
[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.
|
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) ->
|
destroy_and_report_unsolved_named_argument_constraints(Env) ->
|
||||||
Unsolved = solve_named_argument_constraints(Env, get_named_argument_constraints()),
|
Unsolved = solve_named_argument_constraints(Env, get_named_argument_constraints()),
|
||||||
[ type_error({unsolved_named_argument_constraint, C}) || C <- Unsolved ],
|
[ type_error({unsolved_named_argument_constraint, C}) || C <- Unsolved ],
|
||||||
@@ -1800,9 +2066,11 @@ destroy_and_report_unsolved_field_constraints(Env) ->
|
|||||||
{FieldCs, OtherCs} =
|
{FieldCs, OtherCs} =
|
||||||
lists:partition(fun(#field_constraint{}) -> true; (_) -> false end,
|
lists:partition(fun(#field_constraint{}) -> true; (_) -> false end,
|
||||||
get_field_constraints()),
|
get_field_constraints()),
|
||||||
{CreateCs, ContractCs} =
|
{CreateCs, OtherCs1} =
|
||||||
lists:partition(fun(#record_create_constraint{}) -> true; (_) -> false end,
|
lists:partition(fun(#record_create_constraint{}) -> true; (_) -> false end,
|
||||||
OtherCs),
|
OtherCs),
|
||||||
|
{ContractCs, []} =
|
||||||
|
lists:partition(fun(#is_contract_constraint{}) -> true; (_) -> false end, OtherCs1),
|
||||||
Unknown = solve_known_record_types(Env, FieldCs),
|
Unknown = solve_known_record_types(Env, FieldCs),
|
||||||
if Unknown == [] -> ok;
|
if Unknown == [] -> ok;
|
||||||
true ->
|
true ->
|
||||||
@@ -1876,7 +2144,8 @@ unfold_record_types(Env, T) ->
|
|||||||
unfold_types(Env, T, [unfold_record_types]).
|
unfold_types(Env, T, [unfold_record_types]).
|
||||||
|
|
||||||
unfold_types(Env, {typed, Attr, E, Type}, Options) ->
|
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) ->
|
unfold_types(Env, {arg, Attr, Id, Type}, Options) ->
|
||||||
{arg, Attr, Id, unfold_types_in_type(Env, Type, Options)};
|
{arg, Attr, Id, unfold_types_in_type(Env, Type, Options)};
|
||||||
unfold_types(Env, {type_sig, Ann, Constr, NamedArgs, Args, Ret}, 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) ->
|
unfold_types_in_type(Env, {app_t, Ann, Id = {id, _, "map"}, Args = [KeyType0, _]}, Options) ->
|
||||||
Args1 = [KeyType, _] = unfold_types_in_type(Env, Args, 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};
|
{app_t, Ann, Id, Args1};
|
||||||
unfold_types_in_type(Env, {app_t, Ann, Id, Args}, Options) when ?is_type_id(Id) ->
|
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),
|
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(_, _, {id, _, "_"}, _When) -> true;
|
unify(_, _, {id, _, "_"}, _When) -> true;
|
||||||
unify(Env, A, B, When) ->
|
unify(Env, A, B, When) ->
|
||||||
A1 = dereference(unfold_types_in_type(Env, A)),
|
Options =
|
||||||
B1 = dereference(unfold_types_in_type(Env, B)),
|
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, A1, B1, When).
|
||||||
|
|
||||||
unify1(_Env, {uvar, _, R}, {uvar, _, R}, _When) ->
|
unify1(_Env, {uvar, _, R}, {uvar, _, R}, _When) ->
|
||||||
@@ -2008,7 +2283,11 @@ unify1(_Env, {qcon, _, Name}, {qcon, _, Name}, _When) ->
|
|||||||
true;
|
true;
|
||||||
unify1(_Env, {bytes_t, _, Len}, {bytes_t, _, Len}, _When) ->
|
unify1(_Env, {bytes_t, _, Len}, {bytes_t, _, Len}, _When) ->
|
||||||
true;
|
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, Named1, Named2, When) andalso
|
||||||
unify(Env, Args1, Args2, When) andalso unify(Env, Result1, Result2, When);
|
unify(Env, Args1, Args2, When) andalso unify(Env, Result1, Result2, When);
|
||||||
unify1(Env, {app_t, _, {Tag, _, F}, Args1}, {app_t, _, {Tag, _, F}, Args2}, When)
|
unify1(Env, {app_t, _, {Tag, _, F}, Args1}, {app_t, _, {Tag, _, F}, Args2}, When)
|
||||||
@@ -2068,6 +2347,8 @@ occurs_check1(R, {record_t, Fields}) ->
|
|||||||
occurs_check(R, Fields);
|
occurs_check(R, Fields);
|
||||||
occurs_check1(R, {field_t, _, _, T}) ->
|
occurs_check1(R, {field_t, _, _, T}) ->
|
||||||
occurs_check(R, T);
|
occurs_check(R, T);
|
||||||
|
occurs_check1(R, {if_t, _, _, Then, Else}) ->
|
||||||
|
occurs_check(R, [Then, Else]);
|
||||||
occurs_check1(R, [H | T]) ->
|
occurs_check1(R, [H | T]) ->
|
||||||
occurs_check(R, H) orelse occurs_check(R, T);
|
occurs_check(R, H) orelse occurs_check(R, T);
|
||||||
occurs_check1(_, []) -> false.
|
occurs_check1(_, []) -> false.
|
||||||
@@ -2197,6 +2478,30 @@ mk_t_err(Pos, Msg) ->
|
|||||||
mk_t_err(Pos, Msg, Ctxt) ->
|
mk_t_err(Pos, Msg, Ctxt) ->
|
||||||
aeso_errors:new(type_error, Pos, lists:flatten(Msg), lists:flatten(Ctxt)).
|
aeso_errors:new(type_error, Pos, lists:flatten(Msg), lists:flatten(Ctxt)).
|
||||||
|
|
||||||
|
mk_error({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}) ->
|
mk_error({cannot_unify, A, B, When}) ->
|
||||||
Msg = io_lib:format("Cannot unify ~s\n and ~s\n",
|
Msg = io_lib:format("Cannot unify ~s\n and ~s\n",
|
||||||
[pp(instantiate(A)), pp(instantiate(B))]),
|
[pp(instantiate(A)), pp(instantiate(B))]),
|
||||||
@@ -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",
|
Msg = io_lib:format("The indexed type ~s (at ~s) equals ~s which is not a word type\n",
|
||||||
[pp_type("", Type), pp_loc(Type), pp_type("", Type1)]),
|
[pp_type("", Type), pp_loc(Type), pp_type("", Type1)]),
|
||||||
mk_t_err(pos(Type), Msg);
|
mk_t_err(pos(Type), Msg);
|
||||||
mk_error({payload_type_must_be_string, Type, Type}) ->
|
|
||||||
Msg = io_lib:format("The payload type ~s (at ~s) should be string\n",
|
|
||||||
[pp_type("", Type), pp_loc(Type)]),
|
|
||||||
mk_t_err(pos(Type), Msg);
|
|
||||||
mk_error({payload_type_must_be_string, Type, Type1}) ->
|
|
||||||
Msg = io_lib:format("The payload type ~s (at ~s) equals ~s but it should be string\n",
|
|
||||||
[pp_type("", Type), pp_loc(Type), pp_type("", Type1)]),
|
|
||||||
mk_t_err(pos(Type), Msg);
|
|
||||||
mk_error({event_0_to_3_indexed_values, Constr}) ->
|
mk_error({event_0_to_3_indexed_values, Constr}) ->
|
||||||
Msg = io_lib:format("The event constructor ~s (at ~s) has too many indexed values (max 3)\n",
|
Msg = io_lib:format("The event constructor ~s (at ~s) has too many indexed values (max 3)\n",
|
||||||
[name(Constr), pp_loc(Constr)]),
|
[name(Constr), pp_loc(Constr)]),
|
||||||
@@ -2330,13 +2627,21 @@ mk_error({include, _, {string, Pos, Name}}) ->
|
|||||||
[binary_to_list(Name), pp_loc(Pos)]),
|
[binary_to_list(Name), pp_loc(Pos)]),
|
||||||
mk_t_err(pos(Pos), Msg);
|
mk_t_err(pos(Pos), Msg);
|
||||||
mk_error({namespace, _Pos, {con, Pos, Name}, _Def}) ->
|
mk_error({namespace, _Pos, {con, Pos, Name}, _Def}) ->
|
||||||
Msg = io_lib:format("Nested namespace not allowed\nNamespace '~s' at ~s not defined at top level.\n",
|
Msg = io_lib:format("Nested namespaces are not allowed\nNamespace '~s' at ~s not defined at top level.\n",
|
||||||
|
[Name, pp_loc(Pos)]),
|
||||||
|
mk_t_err(pos(Pos), Msg);
|
||||||
|
mk_error({contract, _Pos, {con, Pos, Name}, _Def}) ->
|
||||||
|
Msg = io_lib:format("Nested contracts are not allowed\nContract '~s' at ~s not defined at top level.\n",
|
||||||
|
[Name, pp_loc(Pos)]),
|
||||||
|
mk_t_err(pos(Pos), Msg);
|
||||||
|
mk_error({type_decl, _, {id, Pos, Name}, _}) ->
|
||||||
|
Msg = io_lib:format("Empty type declarations are not supported\nType ~s at ~s lacks a definition\n",
|
||||||
|
[Name, pp_loc(Pos)]),
|
||||||
|
mk_t_err(pos(Pos), Msg);
|
||||||
|
mk_error({letval, _Pos, {id, Pos, Name}, _Def}) ->
|
||||||
|
Msg = io_lib:format("Toplevel \"let\" definitions are not supported\nValue ~s at ~s could be replaced by 0-argument function\n",
|
||||||
[Name, pp_loc(Pos)]),
|
[Name, pp_loc(Pos)]),
|
||||||
mk_t_err(pos(Pos), Msg);
|
mk_t_err(pos(Pos), Msg);
|
||||||
mk_error({repeated_arg, Fun, Arg}) ->
|
|
||||||
Msg = io_lib:format("Repeated argument ~s to function ~s (at ~s).\n",
|
|
||||||
[Arg, pp(Fun), pp_loc(Fun)]),
|
|
||||||
mk_t_err(pos(Fun), Msg);
|
|
||||||
mk_error({stateful_not_allowed, Id, Fun}) ->
|
mk_error({stateful_not_allowed, Id, Fun}) ->
|
||||||
Msg = io_lib:format("Cannot reference stateful function ~s (at ~s)\nin the definition of non-stateful function ~s.\n",
|
Msg = io_lib:format("Cannot reference stateful function ~s (at ~s)\nin the definition of non-stateful function ~s.\n",
|
||||||
[pp(Id), pp_loc(Id), pp(Fun)]),
|
[pp(Id), pp_loc(Id), pp(Fun)]),
|
||||||
@@ -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",
|
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})]),
|
[pp_type(" ", {args_t, Ann, Ts}), pp_loc(Ann), pp_type(" ", {tuple_t, Ann, Ts})]),
|
||||||
mk_t_err(pos(Ann), Msg);
|
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)]),
|
Msg = io_lib:format("Invalid key type\n~s\n", [pp_type(" ", KeyType)]),
|
||||||
Cxt = "Map keys cannot contain other maps.\n",
|
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}) ->
|
mk_error({cannot_call_init_function, Ann}) ->
|
||||||
Msg = "The 'init' function is called exclusively by the create contract transaction\n"
|
Msg = "The 'init' function is called exclusively by the create contract transaction\n"
|
||||||
"and cannot be called from the contract code.\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"
|
"because it does not satisfy the constraint"
|
||||||
" ~s ~s ~s\n", [PrintV(Version), Op, PrintV(Bound)]),
|
" ~s ~s ~s\n", [PrintV(Version), Op, PrintV(Bound)]),
|
||||||
mk_t_err(pos(Ann), Msg);
|
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) ->
|
mk_error(Err) ->
|
||||||
Msg = io_lib:format("Unknown error: ~p\n", [Err]),
|
Msg = io_lib:format("Unknown error: ~p\n", [Err]),
|
||||||
mk_t_err(pos(0, 0), Msg).
|
mk_t_err(pos(0, 0), Msg).
|
||||||
@@ -2474,7 +2790,7 @@ pp_when({check_typesig, Name, Inferred, Given}) ->
|
|||||||
" inferred type: ~s\n"
|
" inferred type: ~s\n"
|
||||||
" given type: ~s\n",
|
" given type: ~s\n",
|
||||||
[Name, pp_loc(Given), pp(instantiate(Inferred)), pp(instantiate(Given))])};
|
[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),
|
Inferred = instantiate(Inferred0),
|
||||||
ArgTypes = instantiate(ArgTypes0),
|
ArgTypes = instantiate(ArgTypes0),
|
||||||
{pos(Fun),
|
{pos(Fun),
|
||||||
@@ -2483,6 +2799,7 @@ pp_when({infer_app, Fun, Args, Inferred0, ArgTypes0}) ->
|
|||||||
"to arguments\n~s",
|
"to arguments\n~s",
|
||||||
[pp_loc(Fun),
|
[pp_loc(Fun),
|
||||||
pp_typed(" ", Fun, Inferred),
|
pp_typed(" ", Fun, Inferred),
|
||||||
|
[ [pp_expr(" ", NamedArg), "\n"] || NamedArg <- NamedArgs ] ++
|
||||||
[ [pp_typed(" ", Arg, ArgT), "\n"]
|
[ [pp_typed(" ", Arg, ArgT), "\n"]
|
||||||
|| {Arg, ArgT} <- lists:zip(Args, ArgTypes) ] ])};
|
|| {Arg, ArgT} <- lists:zip(Args, ArgTypes) ] ])};
|
||||||
pp_when({field_constraint, FieldType0, InferredType0, Fld}) ->
|
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"
|
io_lib:format("when checking rvalue of list comprehension binding at ~s\n~s\n"
|
||||||
"against type \n~s\n",
|
"against type \n~s\n",
|
||||||
[pp_loc(BindExpr), pp_typed(" ", BindExpr, Inferred), pp_type(" ", Expected)])};
|
[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), ""}.
|
pp_when(unknown) -> {pos(0,0), ""}.
|
||||||
|
|
||||||
-spec pp_why_record(why_record()) -> {pos(), iolist()}.
|
-spec pp_why_record(why_record()) -> {pos(), iolist()}.
|
||||||
@@ -2636,6 +2959,8 @@ pp({uvar, _, Ref}) ->
|
|||||||
["?u" | integer_to_list(erlang:phash2(Ref, 16384)) ];
|
["?u" | integer_to_list(erlang:phash2(Ref, 16384)) ];
|
||||||
pp({tvar, _, Name}) ->
|
pp({tvar, _, Name}) ->
|
||||||
Name;
|
Name;
|
||||||
|
pp({if_t, _, Id, Then, Else}) ->
|
||||||
|
["if(", pp([Id, Then, Else]), ")"];
|
||||||
pp({tuple_t, _, []}) ->
|
pp({tuple_t, _, []}) ->
|
||||||
"unit";
|
"unit";
|
||||||
pp({tuple_t, _, Cpts}) ->
|
pp({tuple_t, _, Cpts}) ->
|
||||||
@@ -2647,8 +2972,8 @@ pp({app_t, _, T, []}) ->
|
|||||||
pp(T);
|
pp(T);
|
||||||
pp({app_t, _, Type, Args}) ->
|
pp({app_t, _, Type, Args}) ->
|
||||||
[pp(Type), "(", pp(Args), ")"];
|
[pp(Type), "(", pp(Args), ")"];
|
||||||
pp({named_arg_t, _, Name, Type, Default}) ->
|
pp({named_arg_t, _, Name, Type, _Default}) ->
|
||||||
[pp(Name), " : ", pp(Type), " = ", pp(Default)];
|
[pp(Name), " : ", pp(Type)];
|
||||||
pp({fun_t, _, Named = {uvar, _, _}, As, B}) ->
|
pp({fun_t, _, Named = {uvar, _, _}, As, B}) ->
|
||||||
["(", pp(Named), " | ", pp(As), ") => ", pp(B)];
|
["(", pp(Named), " | ", pp(As), ") => ", pp(B)];
|
||||||
pp({fun_t, _, Named, As, B}) when is_list(Named) ->
|
pp({fun_t, _, Named, As, B}) when is_list(Named) ->
|
||||||
@@ -2733,3 +3058,7 @@ updates_key(Name, Updates) ->
|
|||||||
Updates1 = [ Upd || {Upd, false, _} <- Xs ],
|
Updates1 = [ Upd || {Upd, false, _} <- Xs ],
|
||||||
More = [ Rest || {_, true, Rest} <- Xs ],
|
More = [ Rest || {_, true, Rest} <- Xs ],
|
||||||
{More, Updates1}.
|
{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({id, _, Id}) -> Id;
|
||||||
ast_id({qid, _, 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(Rest, [{ast_id(Name), ast_typerep1(Type, Icode)}| Acc], Icode);
|
||||||
ast_args([], Acc, _Icode) -> lists:reverse(Acc).
|
ast_args([], Acc, _Icode) -> lists:reverse(Acc).
|
||||||
|
|
||||||
@@ -318,19 +318,23 @@ ast_body({app, As, Fun, Args}, Icode) ->
|
|||||||
end;
|
end;
|
||||||
ast_body({list_comp, _, Yield, []}, Icode) ->
|
ast_body({list_comp, _, Yield, []}, Icode) ->
|
||||||
#list{elems = [ast_body(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
|
#funcall
|
||||||
{ function = #var_ref{ name = ["ListInternal", "flat_map"] }
|
{ function = #var_ref{ name = ["ListInternal", "flat_map"] }
|
||||||
, args =
|
, args =
|
||||||
[ #lambda{ args=[#arg{name = ast_id(Arg), type = ast_type(ArgType, Icode)}]
|
[ #lambda{ args=[#arg{name = Arg, type = ast_type(ArgType, Icode)}]
|
||||||
, body = ast_body({list_comp, As, Yield, Rest}, Icode)
|
, body = ast_body(Body, Icode)
|
||||||
}
|
}
|
||||||
, ast_body(BindExpr, Icode)
|
, ast_body(BindExpr, Icode)
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
ast_body({list_comp, As, Yield, [{comprehension_if, AsIF, Cond}|Rest]}, 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({'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({block, As, [LV, {list_comp, As, Yield, Rest}]}, Icode);
|
||||||
ast_body({list_comp, As, Yield, [LF = {letfun, _, _, _, _, _}|Rest]}, Icode) ->
|
ast_body({list_comp, As, Yield, [LF = {letfun, _, _, _, _, _}|Rest]}, Icode) ->
|
||||||
ast_body({block, As, [LF, {list_comp, As, Yield, Rest}]}, Icode);
|
ast_body({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),
|
#switch{expr=ast_body(A, Icode),
|
||||||
cases=[{ast_body(Pat, Icode),ast_body(Body, Icode)}
|
cases=[{ast_body(Pat, Icode),ast_body(Body, Icode)}
|
||||||
|| {'case',_,Pat,Body} <- Cases]};
|
|| {'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),
|
E1 = ast_body(E, Icode),
|
||||||
Pat1 = ast_body(Pat, Icode),
|
Pat1 = ast_body(Pat, Icode),
|
||||||
Rest1 = ast_body({block, As, Rest}, Icode),
|
Rest1 = ast_body({block, As, Rest}, Icode),
|
||||||
#switch{expr = E1,
|
#switch{expr = E1,
|
||||||
cases = [{Pat1, Rest1}]};
|
cases = [{Pat1, Rest1}]};
|
||||||
ast_body({block, As, [{letfun, Ann, F, Args, _Type, Expr} | Rest]}, Icode) ->
|
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) ->
|
ast_body({block,_,[]}, _Icode) ->
|
||||||
#tuple{cpts=[]};
|
#tuple{cpts=[]};
|
||||||
ast_body({block,_,[E]}, Icode) ->
|
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", "claim"]}, _Icode) -> true;
|
||||||
is_builtin_fun({qid, _, ["AENS", "transfer"]}, _Icode) -> true;
|
is_builtin_fun({qid, _, ["AENS", "transfer"]}, _Icode) -> true;
|
||||||
is_builtin_fun({qid, _, ["AENS", "revoke"]}, _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"]}, _Icode) -> true;
|
||||||
is_builtin_fun({qid, _, ["Map", "lookup_default"]}, _Icode) -> true;
|
is_builtin_fun({qid, _, ["Map", "lookup_default"]}, _Icode) -> true;
|
||||||
is_builtin_fun({qid, _, ["Map", "member"]}, _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)],
|
[ast_body(Addr, Icode), ast_body(Name, Icode), ast_body(Sign, Icode)],
|
||||||
[word, word, sign_t()], {tuple, []});
|
[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
|
%% -- Maps
|
||||||
%% -- lookup functions
|
%% -- lookup functions
|
||||||
builtin_code(_, {qid, _, ["Map", "lookup"]}, [Key, Map], _, _, Icode) ->
|
builtin_code(_, {qid, _, ["Map", "lookup"]}, [Key, Map], _, _, Icode) ->
|
||||||
@@ -800,10 +813,10 @@ check_entrypoint_type(Ann, Name, Args, Ret) ->
|
|||||||
true -> ok
|
true -> ok
|
||||||
end end,
|
end end,
|
||||||
[ CheckFirstOrder(T, {invalid_entrypoint, higher_order, Ann1, Name, {argument, X, T}})
|
[ 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}}),
|
CheckFirstOrder(Ret, {invalid_entrypoint, higher_order, Ann, Name, {result, Ret}}),
|
||||||
[ CheckMonomorphic(T, {invalid_entrypoint, polymorphic, Ann1, Name, {argument, X, T}})
|
[ 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}}).
|
CheckMonomorphic(Ret, {invalid_entrypoint, polymorphic, Ann, Name, {result, Ret}}).
|
||||||
|
|
||||||
check_oracle_type(Ann, Type = ?oracle_t(QType, RType)) ->
|
check_oracle_type(Ann, Type = ?oracle_t(QType, RType)) ->
|
||||||
@@ -921,11 +934,16 @@ ast_typerep1({variant_t, Cons}, Icode) ->
|
|||||||
{variant, [ begin
|
{variant, [ begin
|
||||||
{constr_t, _, _, Args} = Con,
|
{constr_t, _, _, Args} = Con,
|
||||||
[ ast_typerep1(Arg, Icode) || Arg <- Args ]
|
[ 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) ->
|
ttl_t(Icode) ->
|
||||||
ast_typerep({qid, [], ["Chain", "ttl"]}, Icode).
|
ast_typerep({qid, [], ["Chain", "ttl"]}, Icode).
|
||||||
|
|
||||||
|
%% pointee_t(Icode) ->
|
||||||
|
%% ast_typerep({qid, [], ["AENS", "pointee"]}, Icode).
|
||||||
|
|
||||||
sign_t() -> bytes_t(64).
|
sign_t() -> bytes_t(64).
|
||||||
bytes_t(Len) when Len =< 32 -> word;
|
bytes_t(Len) when Len =< 32 -> word;
|
||||||
bytes_t(Len) -> {tuple, lists:duplicate((31 + Len) div 32, word)}.
|
bytes_t(Len) -> {tuple, lists:duplicate((31 + Len) div 32, word)}.
|
||||||
|
|||||||
+154
-43
@@ -23,6 +23,7 @@
|
|||||||
, decode_calldata/4
|
, decode_calldata/4
|
||||||
, parse/2
|
, parse/2
|
||||||
, add_include_path/2
|
, add_include_path/2
|
||||||
|
, validate_byte_code/3
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-include_lib("aebytecode/include/aeb_opcodes.hrl").
|
-include_lib("aebytecode/include/aeb_opcodes.hrl").
|
||||||
@@ -37,10 +38,13 @@
|
|||||||
| pp_assembler
|
| pp_assembler
|
||||||
| pp_bytecode
|
| pp_bytecode
|
||||||
| no_code
|
| no_code
|
||||||
|
| keep_included
|
||||||
|
| debug_mode
|
||||||
| {backend, aevm | fate}
|
| {backend, aevm | fate}
|
||||||
| {include, {file_system, [string()]} |
|
| {include, {file_system, [string()]} |
|
||||||
{explicit_files, #{string() => binary()}}}
|
{explicit_files, #{string() => binary()}}}
|
||||||
| {src_file, string()}.
|
| {src_file, string()}
|
||||||
|
| {aci, aeso_aci:aci_type()}.
|
||||||
-type options() :: [option()].
|
-type options() :: [option()].
|
||||||
|
|
||||||
-export_type([ option/0
|
-export_type([ option/0
|
||||||
@@ -114,7 +118,8 @@ from_string(Backend, ContractString, Options) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
from_string1(aevm, ContractString, Options) ->
|
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),
|
TypeInfo = extract_type_info(Icode),
|
||||||
Assembler = assemble(Icode, Options),
|
Assembler = assemble(Icode, Options),
|
||||||
pp_assembler(aevm, Assembler, Options),
|
pp_assembler(aevm, Assembler, Options),
|
||||||
@@ -122,47 +127,63 @@ from_string1(aevm, ContractString, Options) ->
|
|||||||
ByteCode = << << B:8 >> || B <- ByteCodeList >>,
|
ByteCode = << << B:8 >> || B <- ByteCodeList >>,
|
||||||
pp_bytecode(ByteCode, Options),
|
pp_bytecode(ByteCode, Options),
|
||||||
{ok, Version} = version(),
|
{ok, Version} = version(),
|
||||||
{ok, #{byte_code => ByteCode,
|
Res = #{byte_code => ByteCode,
|
||||||
compiler_version => Version,
|
compiler_version => Version,
|
||||||
contract_source => ContractString,
|
contract_source => ContractString,
|
||||||
type_info => TypeInfo,
|
type_info => TypeInfo,
|
||||||
abi_version => aeb_aevm_abi:abi_version(),
|
abi_version => aeb_aevm_abi:abi_version(),
|
||||||
payable => maps:get(payable, Icode)
|
payable => maps:get(payable, Icode)
|
||||||
}};
|
},
|
||||||
|
{ok, maybe_generate_aci(Res, FoldedTypedAst, Options)};
|
||||||
from_string1(fate, ContractString, 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),
|
FateCode = aeso_fcode_to_fate:compile(FCode, Options),
|
||||||
pp_assembler(fate, FateCode, Options),
|
pp_assembler(fate, FateCode, Options),
|
||||||
ByteCode = aeb_fate_code:serialize(FateCode, []),
|
ByteCode = aeb_fate_code:serialize(FateCode, []),
|
||||||
{ok, Version} = version(),
|
{ok, Version} = version(),
|
||||||
{ok, #{byte_code => ByteCode,
|
Res = #{byte_code => ByteCode,
|
||||||
compiler_version => Version,
|
compiler_version => Version,
|
||||||
contract_source => ContractString,
|
contract_source => ContractString,
|
||||||
type_info => [],
|
type_info => [],
|
||||||
fate_code => FateCode,
|
fate_code => FateCode,
|
||||||
abi_version => aeb_fate_abi:abi_version(),
|
abi_version => aeb_fate_abi:abi_version(),
|
||||||
payable => maps:get(payable, FCode)
|
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().
|
-spec string_to_code(string(), options()) -> map().
|
||||||
string_to_code(ContractString, Options) ->
|
string_to_code(ContractString, Options) ->
|
||||||
Ast = parse(ContractString, Options),
|
Ast = parse(ContractString, Options),
|
||||||
pp_sophia_code(Ast, Options),
|
pp_sophia_code(Ast, Options),
|
||||||
pp_ast(Ast, Options),
|
pp_ast(Ast, Options),
|
||||||
{TypeEnv, TypedAst} = aeso_ast_infer_types:infer(Ast, [return_env | Options]),
|
{TypeEnv, FoldedTypedAst, UnfoldedTypedAst} = aeso_ast_infer_types:infer(Ast, [return_env | Options]),
|
||||||
pp_typed_ast(TypedAst, Options),
|
pp_typed_ast(UnfoldedTypedAst, Options),
|
||||||
case proplists:get_value(backend, Options, aevm) of
|
case proplists:get_value(backend, Options, aevm) of
|
||||||
aevm ->
|
aevm ->
|
||||||
Icode = ast_to_icode(TypedAst, Options),
|
Icode = ast_to_icode(UnfoldedTypedAst, Options),
|
||||||
pp_icode(Icode, Options),
|
pp_icode(Icode, Options),
|
||||||
#{ icode => Icode,
|
#{ icode => Icode
|
||||||
typed_ast => TypedAst,
|
, unfolded_typed_ast => UnfoldedTypedAst
|
||||||
type_env => TypeEnv};
|
, folded_typed_ast => FoldedTypedAst
|
||||||
|
, type_env => TypeEnv
|
||||||
|
, ast => Ast };
|
||||||
fate ->
|
fate ->
|
||||||
Fcode = aeso_ast_to_fcode:ast_to_fcode(TypedAst, Options),
|
Fcode = aeso_ast_to_fcode:ast_to_fcode(UnfoldedTypedAst, Options),
|
||||||
#{ fcode => Fcode,
|
#{ fcode => Fcode
|
||||||
typed_ast => TypedAst,
|
, unfolded_typed_ast => UnfoldedTypedAst
|
||||||
type_env => TypeEnv}
|
, folded_typed_ast => FoldedTypedAst
|
||||||
|
, type_env => TypeEnv
|
||||||
|
, ast => Ast }
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-define(CALL_NAME, "__call").
|
-define(CALL_NAME, "__call").
|
||||||
@@ -197,9 +218,9 @@ check_call1(ContractString0, FunName, Args, Options) ->
|
|||||||
case proplists:get_value(backend, Options, aevm) of
|
case proplists:get_value(backend, Options, aevm) of
|
||||||
aevm ->
|
aevm ->
|
||||||
%% First check the contract without the __call function
|
%% First check the contract without the __call function
|
||||||
#{} = string_to_code(ContractString0, Options),
|
#{ast := Ast} = string_to_code(ContractString0, Options),
|
||||||
ContractString = insert_call_function(ContractString0, ?CALL_NAME, FunName, Args, Options),
|
ContractString = insert_call_function(Ast, ContractString0, ?CALL_NAME, FunName, Args),
|
||||||
#{typed_ast := TypedAst,
|
#{unfolded_typed_ast := TypedAst,
|
||||||
icode := Icode} = string_to_code(ContractString, Options),
|
icode := Icode} = string_to_code(ContractString, Options),
|
||||||
{ok, {FunName, {fun_t, _, _, ArgTypes, RetType}}} = get_call_type(TypedAst),
|
{ok, {FunName, {fun_t, _, _, ArgTypes, RetType}}} = get_call_type(TypedAst),
|
||||||
ArgVMTypes = [ aeso_ast_to_icode:ast_typerep(T, Icode) || T <- ArgTypes ],
|
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};
|
{ok, FunName, {ArgVMTypes, RetVMType1}, ArgTerms};
|
||||||
fate ->
|
fate ->
|
||||||
%% First check the contract without the __call function
|
%% 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, []),
|
FateCode = aeso_fcode_to_fate:compile(OrgFcode, []),
|
||||||
%% collect all hashes and compute the first name without hash collision to
|
%% collect all hashes and compute the first name without hash collision to
|
||||||
SymbolHashes = maps:keys(aeb_fate_code:symbols(FateCode)),
|
SymbolHashes = maps:keys(aeb_fate_code:symbols(FateCode)),
|
||||||
CallName = first_none_match(?CALL_NAME, SymbolHashes,
|
CallName = first_none_match(?CALL_NAME, SymbolHashes,
|
||||||
lists:seq($1, $9) ++ lists:seq($A, $Z) ++ lists:seq($a, $z)),
|
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),
|
#{fcode := Fcode} = string_to_code(ContractString, Options),
|
||||||
CallArgs = arguments_of_body(CallName, FunName, Fcode),
|
CallArgs = arguments_of_body(CallName, FunName, Fcode),
|
||||||
{ok, FunName, CallArgs}
|
{ok, FunName, CallArgs}
|
||||||
@@ -251,9 +273,8 @@ first_none_match(CallName, Hashes, [Char|Chars]) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
%% Add the __call function to a contract.
|
%% Add the __call function to a contract.
|
||||||
-spec insert_call_function(string(), string(), string(), [string()], options()) -> string().
|
-spec insert_call_function(aeso_syntax:ast(), string(), string(), string(), [string()]) -> string().
|
||||||
insert_call_function(Code, Call, FunName, Args, Options) ->
|
insert_call_function(Ast, Code, Call, FunName, Args) ->
|
||||||
Ast = parse(Code, Options),
|
|
||||||
Ind = last_contract_indent(Ast),
|
Ind = last_contract_indent(Ast),
|
||||||
lists:flatten(
|
lists:flatten(
|
||||||
[ Code,
|
[ Code,
|
||||||
@@ -309,7 +330,7 @@ to_sophia_value(ContractString, FunName, ok, Data, Options0) ->
|
|||||||
Options = [no_code | Options0],
|
Options = [no_code | Options0],
|
||||||
try
|
try
|
||||||
Code = string_to_code(ContractString, Options),
|
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),
|
{ok, _, Type0} = get_decode_type(FunName, TypedAst),
|
||||||
Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]),
|
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
|
try
|
||||||
{ok, aeso_vm_decode:from_fate(Type, aeb_fate_encoding:deserialize(Data))}
|
{ok, aeso_vm_decode:from_fate(Type, aeb_fate_encoding:deserialize(Data))}
|
||||||
catch throw:cannot_translate_to_sophia ->
|
catch throw:cannot_translate_to_sophia ->
|
||||||
Type1 = prettypr:format(aeso_pretty:type(Type)),
|
Type1 = prettypr:format(aeso_pretty:type(Type0)),
|
||||||
Msg = io_lib:format("Cannot translate FATE value ~p\n of Sophia type ~s\n",
|
Msg = io_lib:format("Cannot translate FATE value ~p\n of Sophia type ~s\n",
|
||||||
[aeb_fate_encoding:deserialize(Data), Type1]),
|
[aeb_fate_encoding:deserialize(Data), Type1]),
|
||||||
{error, [aeso_errors:new(data_error, Msg)]};
|
{error, [aeso_errors:new(data_error, Msg)]};
|
||||||
_:_ ->
|
_:_ ->
|
||||||
Type1 = prettypr:format(aeso_pretty:type(Type)),
|
Type1 = prettypr:format(aeso_pretty:type(Type0)),
|
||||||
Msg = io_lib:format("Failed to decode binary as type ~s\n", [Type1]),
|
Msg = io_lib:format("Failed to decode binary as type ~s\n", [Type1]),
|
||||||
{error, [aeso_errors:new(data_error, Msg)]}
|
{error, [aeso_errors:new(data_error, Msg)]}
|
||||||
end
|
end
|
||||||
@@ -385,11 +406,11 @@ decode_calldata(ContractString, FunName, Calldata, Options0) ->
|
|||||||
Options = [no_code | Options0],
|
Options = [no_code | Options0],
|
||||||
try
|
try
|
||||||
Code = string_to_code(ContractString, Options),
|
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),
|
{ok, Args, _} = get_decode_type(FunName, TypedAst),
|
||||||
DropArg = fun({arg, _, _, T}) -> T; (T) -> T end,
|
GetType = fun({typed, _, _, T}) -> T; (T) -> T end,
|
||||||
ArgTypes = lists:map(DropArg, Args),
|
ArgTypes = lists:map(GetType, Args),
|
||||||
Type0 = {tuple_t, [], ArgTypes},
|
Type0 = {tuple_t, [], ArgTypes},
|
||||||
%% user defined data types such as variants needed to match against
|
%% 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]),
|
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})
|
_ -> throw({todo, M})
|
||||||
end;
|
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, _) ->
|
icode_to_term(typerep, _) ->
|
||||||
throw({todo, typerep});
|
throw({todo, typerep});
|
||||||
icode_to_term(T, V) ->
|
icode_to_term(T, V) ->
|
||||||
@@ -558,10 +587,92 @@ pp(Code, Options, Option, PPFun) ->
|
|||||||
ok
|
ok
|
||||||
end.
|
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) ->
|
sophia_type_to_typerep(String) ->
|
||||||
{ok, Ast} = aeso_parser:type(String),
|
Ast = aeso_parser:run_parser(aeso_parser:type(), String),
|
||||||
try aeso_ast_to_icode:ast_typerep(Ast) of
|
try aeso_ast_to_icode:ast_typerep(Ast) of
|
||||||
Type -> {ok, Type}
|
Type -> {ok, Type}
|
||||||
catch _:_ -> {error, bad_type}
|
catch _:_ -> {error, bad_type}
|
||||||
|
|||||||
+437
-299
File diff suppressed because it is too large
Load Diff
+24
-20
@@ -73,28 +73,33 @@ new(Options) ->
|
|||||||
|
|
||||||
builtin_types() ->
|
builtin_types() ->
|
||||||
Word = fun([]) -> word end,
|
Word = fun([]) -> word end,
|
||||||
#{ "bool" => Word
|
#{ "bool" => Word
|
||||||
, "int" => Word
|
, "int" => Word
|
||||||
, "char" => Word
|
, "char" => Word
|
||||||
, "bits" => Word
|
, "bits" => Word
|
||||||
, "string" => fun([]) -> string end
|
, "string" => fun([]) -> string end
|
||||||
, "address" => Word
|
, "address" => Word
|
||||||
, "hash" => Word
|
, "hash" => Word
|
||||||
, "unit" => fun([]) -> {tuple, []} end
|
, "unit" => fun([]) -> {tuple, []} end
|
||||||
, "signature" => fun([]) -> {tuple, [word, word]} end
|
, "signature" => fun([]) -> {tuple, [word, word]} end
|
||||||
, "oracle" => fun([_, _]) -> word end
|
, "oracle" => fun([_, _]) -> word end
|
||||||
, "oracle_query" => fun([_, _]) -> word end
|
, "oracle_query" => fun([_, _]) -> word end
|
||||||
, "list" => fun([A]) -> {list, A} end
|
, "list" => fun([A]) -> {list, A} end
|
||||||
, "option" => fun([A]) -> {variant, [[], [A]]} end
|
, "option" => fun([A]) -> {variant, [[], [A]]} end
|
||||||
, "map" => fun([K, V]) -> map_typerep(K, V) end
|
, "map" => fun([K, V]) -> map_typerep(K, V) end
|
||||||
, ["Chain", "ttl"] => fun([]) -> {variant, [[word], [word]]} end
|
, ["Chain", "ttl"] => fun([]) -> {variant, [[word], [word]]} end
|
||||||
|
, ["AENS", "pointee"] => fun([]) -> {variant, [[word], [word], [word]]} end
|
||||||
}.
|
}.
|
||||||
|
|
||||||
builtin_constructors() ->
|
builtin_constructors() ->
|
||||||
#{ ["RelativeTTL"] => 0
|
#{ ["RelativeTTL"] => 0
|
||||||
, ["FixedTTL"] => 1
|
, ["FixedTTL"] => 1
|
||||||
, ["None"] => 0
|
, ["None"] => 0
|
||||||
, ["Some"] => 1 }.
|
, ["Some"] => 1
|
||||||
|
, ["AccountPointee"] => 0
|
||||||
|
, ["OraclePointee"] => 1
|
||||||
|
, ["ContractPointee"] => 2
|
||||||
|
}.
|
||||||
|
|
||||||
map_typerep(K, V) ->
|
map_typerep(K, V) ->
|
||||||
{map, K, V}.
|
{map, K, V}.
|
||||||
@@ -146,4 +151,3 @@ get_constructor_tag(Name, #{constructors := Constructors}) ->
|
|||||||
undefined -> error({undefined_constructor, Name});
|
undefined -> error({undefined_constructor, Name});
|
||||||
Tag -> Tag
|
Tag -> Tag
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|||||||
+94
-44
@@ -3,20 +3,33 @@
|
|||||||
%%% Description :
|
%%% Description :
|
||||||
%%% Created : 1 Mar 2018 by Ulf Norell
|
%%% Created : 1 Mar 2018 by Ulf Norell
|
||||||
-module(aeso_parser).
|
-module(aeso_parser).
|
||||||
|
-compile({no_auto_import,[map_get/2]}).
|
||||||
|
|
||||||
-export([string/1,
|
-export([string/1,
|
||||||
string/2,
|
string/2,
|
||||||
string/3,
|
string/3,
|
||||||
|
auto_imports/1,
|
||||||
hash_include/2,
|
hash_include/2,
|
||||||
type/1]).
|
decl/0,
|
||||||
|
type/0,
|
||||||
|
body/0,
|
||||||
|
maybe_block/1,
|
||||||
|
run_parser/2,
|
||||||
|
run_parser/3]).
|
||||||
|
|
||||||
-include("aeso_parse_lib.hrl").
|
-include("aeso_parse_lib.hrl").
|
||||||
-import(aeso_parse_lib, [current_file/0, set_current_file/1]).
|
-import(aeso_parse_lib, [current_file/0, set_current_file/1]).
|
||||||
|
|
||||||
-type parse_result() :: aeso_syntax:ast() | none().
|
-type parse_result() :: aeso_syntax:ast() | {aeso_syntax:ast(), sets:set(include_hash())} | none().
|
||||||
|
|
||||||
-type include_hash() :: {string(), binary()}.
|
-type include_hash() :: {string(), binary()}.
|
||||||
|
|
||||||
|
|
||||||
|
escape_errors({ok, Ok}) ->
|
||||||
|
Ok;
|
||||||
|
escape_errors({error, Err}) ->
|
||||||
|
parse_error(Err).
|
||||||
|
|
||||||
-spec string(string()) -> parse_result().
|
-spec string(string()) -> parse_result().
|
||||||
string(String) ->
|
string(String) ->
|
||||||
string(String, sets:new(), []).
|
string(String, sets:new(), []).
|
||||||
@@ -30,27 +43,24 @@ string(String, Opts) ->
|
|||||||
|
|
||||||
-spec string(string(), sets:set(include_hash()), aeso_compiler:options()) -> parse_result().
|
-spec string(string(), sets:set(include_hash()), aeso_compiler:options()) -> parse_result().
|
||||||
string(String, Included, Opts) ->
|
string(String, Included, Opts) ->
|
||||||
case parse_and_scan(file(), String, Opts) of
|
AST = run_parser(file(), String, Opts),
|
||||||
{ok, AST} ->
|
case expand_includes(AST, Included, Opts) of
|
||||||
case expand_includes(AST, Included, Opts) of
|
{ok, AST1} -> AST1;
|
||||||
{ok, AST1} -> AST1;
|
{error, Err} -> parse_error(Err)
|
||||||
{error, Err} -> parse_error(Err)
|
|
||||||
end;
|
|
||||||
{error, Err} ->
|
|
||||||
parse_error(Err)
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
type(String) ->
|
|
||||||
case parse_and_scan(type(), String, []) of
|
run_parser(P, Inp) ->
|
||||||
{ok, AST} -> {ok, AST};
|
escape_errors(parse_and_scan(P, Inp, [])).
|
||||||
{error, Err} -> {error, [mk_error(Err)]}
|
run_parser(P, Inp, Opts) ->
|
||||||
end.
|
escape_errors(parse_and_scan(P, Inp, Opts)).
|
||||||
|
|
||||||
parse_and_scan(P, S, Opts) ->
|
parse_and_scan(P, S, Opts) ->
|
||||||
set_current_file(proplists:get_value(src_file, Opts, no_file)),
|
set_current_file(proplists:get_value(src_file, Opts, no_file)),
|
||||||
case aeso_scan:scan(S) of
|
case aeso_scan:scan(S) of
|
||||||
{ok, Tokens} -> aeso_parse_lib:parse(P, Tokens);
|
{ok, Tokens} -> aeso_parse_lib:parse(P, Tokens);
|
||||||
Error -> Error
|
{error, {{Input, Pos}, _}} ->
|
||||||
|
{error, {Pos, scan_error, Input}}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-dialyzer({nowarn_function, parse_error/1}).
|
-dialyzer({nowarn_function, parse_error/1}).
|
||||||
@@ -60,8 +70,8 @@ parse_error(Err) ->
|
|||||||
mk_p_err(Pos, Msg) ->
|
mk_p_err(Pos, Msg) ->
|
||||||
aeso_errors:new(parse_error, mk_pos(Pos), lists:flatten(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_error({Pos, scan_error, Input}) ->
|
||||||
mk_p_err(Pos, "Scan error\n");
|
mk_p_err(Pos, io_lib:format("Lexical error on input: ~s\n", [Input]));
|
||||||
mk_error({Pos, parse_error, Err}) ->
|
mk_error({Pos, parse_error, Err}) ->
|
||||||
Msg = io_lib:format("~s\n", [Err]),
|
Msg = io_lib:format("~s\n", [Err]),
|
||||||
mk_p_err(Pos, Msg);
|
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})
|
, ?RULE(keyword(datatype), id(), type_vars(), tok('='), typedef(variant), {type_def, _1, _2, _3, _5})
|
||||||
|
|
||||||
%% Function declarations
|
%% 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(), maybe_block(fundef_or_decl()), fun_block(_1, _2, _3))
|
||||||
, ?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(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() ->
|
pragma() ->
|
||||||
Op = choice([token(T) || T <- ['<', '=<', '==', '>=', '>']]),
|
Op = choice([token(T) || T <- ['<', '=<', '==', '>=', '>']]),
|
||||||
?RULE(tok('@'), id("compiler"), Op, version(), {pragma, get_ann(_1), {compiler, element(1, _3), _4}}).
|
?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]].
|
[Maj | [N || {_, {int, _, N}} <- Rest]].
|
||||||
|
|
||||||
fun_or_entry() ->
|
fun_or_entry() ->
|
||||||
choice([?RULE(keyword(function), {function, _1}),
|
choice([?RULE(keyword(function), {function, _1}),
|
||||||
?RULE(keyword(entrypoint), {entrypoint, _1})]).
|
?RULE(keyword(entrypoint), {entrypoint, _1})]).
|
||||||
|
|
||||||
modifiers() ->
|
modifiers() ->
|
||||||
@@ -163,20 +181,19 @@ letdecl() ->
|
|||||||
letdef() -> choice(valdef(), fundef()).
|
letdef() -> choice(valdef(), fundef()).
|
||||||
|
|
||||||
valdef() ->
|
valdef() ->
|
||||||
choice(
|
?RULE(pattern(), tok('='), body(), {letval, [], _1, _3}).
|
||||||
?RULE(id(), tok('='), body(), {letval, [], _1, type_wildcard(), _3}),
|
|
||||||
?RULE(id(), tok(':'), type(), tok('='), body(), {letval, [], _1, _3, _5})).
|
|
||||||
|
|
||||||
fundef() ->
|
fundef() ->
|
||||||
choice(
|
choice(
|
||||||
[ ?RULE(id(), args(), tok('='), body(), {letfun, [], _1, _2, type_wildcard(), _4})
|
[ ?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, [], _1, _2, _4, _6})
|
, ?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(
|
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})).
|
?RULE(id(), tok(':'), type(), {arg, get_ann(_1), _1, _3})).
|
||||||
|
|
||||||
%% -- Types ------------------------------------------------------------------
|
%% -- Types ------------------------------------------------------------------
|
||||||
@@ -237,7 +254,7 @@ branch() ->
|
|||||||
?RULE(pattern(), keyword('=>'), body(), {'case', _2, _1, _3}).
|
?RULE(pattern(), keyword('=>'), body(), {'case', _2, _1, _3}).
|
||||||
|
|
||||||
pattern() ->
|
pattern() ->
|
||||||
?LET_P(E, expr500(), parse_pattern(E)).
|
?LET_P(E, expr(), parse_pattern(E)).
|
||||||
|
|
||||||
%% -- Expressions ------------------------------------------------------------
|
%% -- Expressions ------------------------------------------------------------
|
||||||
|
|
||||||
@@ -247,7 +264,7 @@ expr100() ->
|
|||||||
Expr100 = ?LAZY_P(expr100()),
|
Expr100 = ?LAZY_P(expr100()),
|
||||||
Expr200 = ?LAZY_P(expr200()),
|
Expr200 = ?LAZY_P(expr200()),
|
||||||
choice(
|
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)}
|
, {'if', keyword('if'), parens(Expr100), Expr200, right(tok(else), Expr100)}
|
||||||
, ?RULE(Expr200, optional(right(tok(':'), type())),
|
, ?RULE(Expr200, optional(right(tok(':'), type())),
|
||||||
case _2 of
|
case _2 of
|
||||||
@@ -296,7 +313,7 @@ comprehension_if() ->
|
|||||||
?RULE(keyword('if'), parens(expr()), {comprehension_if, _1, _2}).
|
?RULE(keyword('if'), parens(expr()), {comprehension_if, _1, _2}).
|
||||||
|
|
||||||
comprehension_bind() ->
|
comprehension_bind() ->
|
||||||
?RULE(id(), tok('<-'), expr(), {comprehension_bind, _1, _3}).
|
?RULE(pattern(), tok('<-'), expr(), {comprehension_bind, _1, _3}).
|
||||||
|
|
||||||
arg_expr() ->
|
arg_expr() ->
|
||||||
?LAZY_P(
|
?LAZY_P(
|
||||||
@@ -348,7 +365,9 @@ record(Fs) ->
|
|||||||
bad_expr_err("Cannot use '@' in map construction", infix({lvalue, FAnn, LV}, {'@', Ann}, Id));
|
bad_expr_err("Cannot use '@' in map construction", infix({lvalue, FAnn, LV}, {'@', Ann}, Id));
|
||||||
({field, FAnn, LV, _}) ->
|
({field, FAnn, LV, _}) ->
|
||||||
bad_expr_err("Cannot use nested fields or keys in map construction", {lvalue, FAnn, LV}) end,
|
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.
|
end.
|
||||||
|
|
||||||
record_or_map(Fields) ->
|
record_or_map(Fields) ->
|
||||||
@@ -360,9 +379,7 @@ record_or_map(Fields) ->
|
|||||||
case lists:usort(lists:map(Kind, Fields)) of
|
case lists:usort(lists:map(Kind, Fields)) of
|
||||||
[proj] -> record;
|
[proj] -> record;
|
||||||
[map_get] -> map;
|
[map_get] -> map;
|
||||||
_ ->
|
_ -> record_or_map_error %% Defer error until type checking
|
||||||
[{field, Ann, _, _} | _] = Fields,
|
|
||||||
bad_expr_err("Mixed record fields and map keys in", {record, Ann, Fields})
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
field_assignment() ->
|
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).
|
prefixes(Ops, E) -> lists:foldr(fun prefix/2, E, Ops).
|
||||||
prefix(Op, E) -> set_ann(format, prefix, {app, get_ann(Op), Op, [E]}).
|
prefix(Op, E) -> set_ann(format, prefix, {app, get_ann(Op), Op, [E]}).
|
||||||
|
|
||||||
type_wildcard() ->
|
type_wildcard(Ann) ->
|
||||||
{id, [{origin, system}], "_"}.
|
{id, [{origin, system} | Ann], "_"}.
|
||||||
|
|
||||||
block_e(Stmts) ->
|
block_e(Stmts) ->
|
||||||
group_ifs(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()).
|
-spec parse_pattern(aeso_syntax:expr()) -> aeso_parse_lib:parser(aeso_syntax:pat()).
|
||||||
parse_pattern({app, Ann, Con = {'::', _}, Es}) ->
|
parse_pattern({app, Ann, Con = {'::', _}, Es}) ->
|
||||||
{app, Ann, Con, lists:map(fun parse_pattern/1, Es)};
|
{app, Ann, Con, lists:map(fun parse_pattern/1, Es)};
|
||||||
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)};
|
{app, Ann, Con, lists:map(fun parse_pattern/1, Es)};
|
||||||
parse_pattern({tuple, Ann, Es}) ->
|
parse_pattern({tuple, Ann, Es}) ->
|
||||||
{tuple, Ann, lists:map(fun parse_pattern/1, 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)};
|
{list, Ann, lists:map(fun parse_pattern/1, Es)};
|
||||||
parse_pattern({record, Ann, Fs}) ->
|
parse_pattern({record, Ann, Fs}) ->
|
||||||
{record, Ann, lists:map(fun parse_field_pattern/1, 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 = {con, _, _}) -> E;
|
||||||
|
parse_pattern(E = {qcon, _, _}) -> E;
|
||||||
parse_pattern(E = {id, _, _}) -> E;
|
parse_pattern(E = {id, _, _}) -> E;
|
||||||
parse_pattern(E = {int, _, _}) -> E;
|
parse_pattern(E = {int, _, _}) -> E;
|
||||||
parse_pattern(E = {bool, _, _}) -> E;
|
parse_pattern(E = {bool, _, _}) -> E;
|
||||||
@@ -583,8 +605,13 @@ expand_includes(AST, Included, Opts) ->
|
|||||||
|| File <- lists:usort(auto_imports(AST)) ] ++ AST,
|
|| File <- lists:usort(auto_imports(AST)) ] ++ AST,
|
||||||
expand_includes(AST1, Included, [], Opts).
|
expand_includes(AST1, Included, [], Opts).
|
||||||
|
|
||||||
expand_includes([], _Included, Acc, _Opts) ->
|
expand_includes([], Included, Acc, Opts) ->
|
||||||
{ok, lists:reverse(Acc)};
|
case lists:member(keep_included, Opts) of
|
||||||
|
false ->
|
||||||
|
{ok, lists:reverse(Acc)};
|
||||||
|
true ->
|
||||||
|
{ok, {lists:reverse(Acc), Included}}
|
||||||
|
end;
|
||||||
expand_includes([{include, Ann, {string, _SAnn, File}} | AST], Included, Acc, Opts) ->
|
expand_includes([{include, Ann, {string, _SAnn, File}} | AST], Included, Acc, Opts) ->
|
||||||
case get_include_code(File, Ann, Opts) of
|
case get_include_code(File, Ann, Opts) of
|
||||||
{ok, Code} ->
|
{ok, Code} ->
|
||||||
@@ -618,16 +645,39 @@ read_file(File, Opts) ->
|
|||||||
case maps:get(binary_to_list(File), Files, not_found) of
|
case maps:get(binary_to_list(File), Files, not_found) of
|
||||||
not_found -> {error, not_found};
|
not_found -> {error, not_found};
|
||||||
Src -> {ok, Src}
|
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
|
||||||
end.
|
end.
|
||||||
|
|
||||||
stdlib_options() ->
|
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) ->
|
get_include_code(File, Ann, Opts) ->
|
||||||
case {read_file(File, Opts), read_file(File, stdlib_options())} of
|
case {read_file(File, Opts), read_file(File, stdlib_options())} of
|
||||||
{{ok, _}, {ok,_ }} ->
|
{{ok, Bin}, {ok, _}} ->
|
||||||
fail(ann_pos(Ann), "Illegal redefinition of standard library " ++ File);
|
case filename:basename(File) == File of
|
||||||
|
true -> { error
|
||||||
|
, fail( ann_pos(Ann)
|
||||||
|
, "Illegal redefinition of standard library " ++ binary_to_list(File))};
|
||||||
|
%% If a path is provided then the stdlib takes lower priority
|
||||||
|
false -> {ok, binary_to_list(Bin)}
|
||||||
|
end;
|
||||||
{_, {ok, Bin}} ->
|
{_, {ok, Bin}} ->
|
||||||
{ok, binary_to_list(Bin)};
|
{ok, binary_to_list(Bin)};
|
||||||
{{ok, Bin}, _} ->
|
{{ok, Bin}, _} ->
|
||||||
|
|||||||
+45
-23
@@ -145,8 +145,12 @@ decl(D, Options) ->
|
|||||||
with_options(Options, fun() -> decl(D) end).
|
with_options(Options, fun() -> decl(D) end).
|
||||||
|
|
||||||
-spec decl(aeso_syntax:decl()) -> doc().
|
-spec decl(aeso_syntax:decl()) -> doc().
|
||||||
decl({contract, _, C, Ds}) ->
|
decl({contract, Attrs, C, Ds}) ->
|
||||||
block(follow(text("contract"), hsep(name(C), text("="))), decls(Ds));
|
Mod = fun({Mod, true}) when Mod == payable ->
|
||||||
|
text(atom_to_list(Mod));
|
||||||
|
(_) -> empty() end,
|
||||||
|
block(follow( hsep(lists:map(Mod, Attrs) ++ [text("contract")])
|
||||||
|
, hsep(name(C), text("="))), decls(Ds));
|
||||||
decl({namespace, _, C, Ds}) ->
|
decl({namespace, _, C, Ds}) ->
|
||||||
block(follow(text("namespace"), hsep(name(C), text("="))), decls(Ds));
|
block(follow(text("namespace"), hsep(name(C), text("="))), decls(Ds));
|
||||||
decl({pragma, _, Pragma}) -> pragma(Pragma);
|
decl({pragma, _, Pragma}) -> pragma(Pragma);
|
||||||
@@ -155,13 +159,16 @@ decl({type_def, _, T, Vars, Def}) ->
|
|||||||
Kind = element(1, Def),
|
Kind = element(1, Def),
|
||||||
equals(typedecl(Kind, T, Vars), typedef(Def));
|
equals(typedecl(Kind, T, Vars), typedef(Def));
|
||||||
decl({fun_decl, Ann, F, T}) ->
|
decl({fun_decl, Ann, F, T}) ->
|
||||||
|
Mod = fun({Mod, true}) when Mod == private; Mod == stateful; Mod == payable ->
|
||||||
|
text(atom_to_list(Mod));
|
||||||
|
(_) -> empty() end,
|
||||||
Fun = case aeso_syntax:get_ann(entrypoint, Ann, false) of
|
Fun = case aeso_syntax:get_ann(entrypoint, Ann, false) of
|
||||||
true -> text("entrypoint");
|
true -> text("entrypoint");
|
||||||
false -> text("function")
|
false -> text("function")
|
||||||
end,
|
end,
|
||||||
hsep(Fun, typed(name(F), T));
|
hsep(lists:map(Mod, Ann) ++ [Fun, typed(name(F), T)]);
|
||||||
decl(D = {letfun, Attrs, _, _, _, _}) ->
|
decl(D = {letfun, Attrs, _, _, _, _}) ->
|
||||||
Mod = fun({Mod, true}) when Mod == private; Mod == stateful ->
|
Mod = fun({Mod, true}) when Mod == private; Mod == stateful; Mod == payable ->
|
||||||
text(atom_to_list(Mod));
|
text(atom_to_list(Mod));
|
||||||
(_) -> empty() end,
|
(_) -> empty() end,
|
||||||
Fun = case aeso_syntax:get_ann(entrypoint, Attrs, false) of
|
Fun = case aeso_syntax:get_ann(entrypoint, Attrs, false) of
|
||||||
@@ -169,7 +176,11 @@ decl(D = {letfun, Attrs, _, _, _, _}) ->
|
|||||||
false -> "function"
|
false -> "function"
|
||||||
end,
|
end,
|
||||||
hsep(lists:map(Mod, Attrs) ++ [letdecl(Fun, D)]);
|
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().
|
-spec pragma(aeso_syntax:pragma()) -> doc().
|
||||||
pragma({compiler, Op, Ver}) ->
|
pragma({compiler, Op, Ver}) ->
|
||||||
@@ -193,10 +204,10 @@ name({tvar, _, Name}) -> text(Name);
|
|||||||
name({typed, _, Name, _}) -> name(Name).
|
name({typed, _, Name, _}) -> name(Name).
|
||||||
|
|
||||||
-spec letdecl(string(), aeso_syntax:letbind()) -> doc().
|
-spec letdecl(string(), aeso_syntax:letbind()) -> doc().
|
||||||
letdecl(Let, {letval, _, F, T, E}) ->
|
letdecl(Let, {letval, _, P, E}) ->
|
||||||
block_expr(0, hsep([text(Let), typed(name(F), T), text("=")]), E);
|
block_expr(0, hsep([text(Let), expr(P), text("=")]), E);
|
||||||
letdecl(Let, {letfun, _, F, Args, T, 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().
|
-spec args([aeso_syntax:arg()]) -> doc().
|
||||||
args(Args) ->
|
args(Args) ->
|
||||||
@@ -253,6 +264,8 @@ type({args_t, _, Args}) ->
|
|||||||
type({bytes_t, _, any}) -> text("bytes(_)");
|
type({bytes_t, _, any}) -> text("bytes(_)");
|
||||||
type({bytes_t, _, Len}) ->
|
type({bytes_t, _, Len}) ->
|
||||||
text(lists:concat(["bytes(", Len, ")"]));
|
text(lists:concat(["bytes(", Len, ")"]));
|
||||||
|
type({if_t, _, Id, Then, Else}) ->
|
||||||
|
beside(text("if"), args_type([Id, Then, Else]));
|
||||||
type({named_arg_t, _, Name, Type, _Default}) ->
|
type({named_arg_t, _, Name, Type, _Default}) ->
|
||||||
%% Drop the default value
|
%% Drop the default value
|
||||||
%% follow(hsep(typed(name(Name), Type), text("=")), expr(Default));
|
%% follow(hsep(typed(name(Name), Type), text("=")), expr(Default));
|
||||||
@@ -279,12 +292,9 @@ tuple_type(Factors) ->
|
|||||||
, text(")")
|
, text(")")
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-spec arg_expr(aeso_syntax:arg_expr()) -> doc().
|
-spec expr_p(integer(), aeso_syntax:arg_expr()) -> doc().
|
||||||
arg_expr({named_arg, _, Name, E}) ->
|
expr_p(P, {named_arg, _, Name, E}) ->
|
||||||
follow(hsep(expr(Name), text("=")), expr(E));
|
paren(P > 100, follow(hsep(expr(Name), text("=")), expr(E)));
|
||||||
arg_expr(E) -> expr(E).
|
|
||||||
|
|
||||||
-spec expr_p(integer(), aeso_syntax:expr()) -> doc().
|
|
||||||
expr_p(P, {lam, _, Args, E}) ->
|
expr_p(P, {lam, _, Args, E}) ->
|
||||||
paren(P > 100, follow(hsep(args(Args), text("=>")), expr_p(100, E)));
|
paren(P > 100, follow(hsep(args(Args), text("=>")), expr_p(100, E)));
|
||||||
expr_p(P, If = {'if', Ann, Cond, Then, Else}) ->
|
expr_p(P, If = {'if', Ann, Cond, Then, Else}) ->
|
||||||
@@ -305,6 +315,8 @@ expr_p(_, {tuple, _, Es}) ->
|
|||||||
tuple(lists:map(fun expr/1, Es));
|
tuple(lists:map(fun expr/1, Es));
|
||||||
expr_p(_, {list, _, Es}) ->
|
expr_p(_, {list, _, Es}) ->
|
||||||
list(lists:map(fun expr/1, 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}) ->
|
expr_p(_, {record, _, Fs}) ->
|
||||||
record(lists:map(fun field/1, Fs));
|
record(lists:map(fun field/1, Fs));
|
||||||
expr_p(_, {map, Ann, KVs}) ->
|
expr_p(_, {map, Ann, KVs}) ->
|
||||||
@@ -358,13 +370,19 @@ expr_p(_, {Type, _, Bin})
|
|||||||
Type == oracle_query_id ->
|
Type == oracle_query_id ->
|
||||||
text(binary_to_list(aeser_api_encoder:encode(Type, Bin)));
|
text(binary_to_list(aeser_api_encoder:encode(Type, Bin)));
|
||||||
expr_p(_, {string, _, <<>>}) -> text("\"\"");
|
expr_p(_, {string, _, <<>>}) -> text("\"\"");
|
||||||
expr_p(_, {string, _, S}) -> term(binary_to_list(S));
|
expr_p(_, {string, _, S}) ->
|
||||||
|
text(io_lib:format("\"~s\"", [binary_to_list(S)]));
|
||||||
expr_p(_, {char, _, C}) ->
|
expr_p(_, {char, _, C}) ->
|
||||||
case C of
|
case C of
|
||||||
$' -> text("'\\''");
|
$' -> text("'\\''");
|
||||||
$" -> text("'\"'");
|
$" -> text("'\"'");
|
||||||
_ -> S = lists:flatten(io_lib:format("~p", [[C]])),
|
_ when C < 16#80 ->
|
||||||
text("'" ++ tl(lists:droplast(S)) ++ "'")
|
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;
|
end;
|
||||||
%% -- Names
|
%% -- Names
|
||||||
expr_p(_, E = {id, _, _}) -> name(E);
|
expr_p(_, E = {id, _, _}) -> name(E);
|
||||||
@@ -387,6 +405,13 @@ stmt_p({else, Else}) ->
|
|||||||
_ -> block_expr(200, text("else"), Else)
|
_ -> block_expr(200, text("else"), Else)
|
||||||
end.
|
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()}.
|
-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 '[ ]'
|
||||||
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) ->
|
app(P, F, Args) ->
|
||||||
paren(P > 900,
|
paren(P > 900,
|
||||||
beside(expr_p(900, F),
|
beside(expr_p(900, F),
|
||||||
tuple(lists:map(fun arg_expr/1, Args)))).
|
tuple(lists:map(fun expr/1, Args)))).
|
||||||
|
|
||||||
field({field, _, LV, E}) ->
|
field({field, _, LV, E}) ->
|
||||||
follow(hsep(lvalue(LV), text("=")), expr(E));
|
follow(hsep(lvalue(LV), text("=")), expr(E));
|
||||||
@@ -450,7 +475,7 @@ elim1(Get={map_get, _, _}) -> elim(Get);
|
|||||||
elim1(Get={map_get, _, _, _}) -> elim(Get).
|
elim1(Get={map_get, _, _, _}) -> elim(Get).
|
||||||
|
|
||||||
alt({'case', _, Pat, Body}) ->
|
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_expr(_, Header, {block, _, Ss}) ->
|
||||||
block(Header, statements(Ss));
|
block(Header, statements(Ss));
|
||||||
@@ -460,7 +485,7 @@ block_expr(P, Header, E) ->
|
|||||||
statements(Stmts) ->
|
statements(Stmts) ->
|
||||||
above([ statement(S) || S <- 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(S = {letfun, _, _, _, _, _}) -> letdecl("let", S);
|
||||||
statement(E) -> expr(E).
|
statement(E) -> expr(E).
|
||||||
|
|
||||||
@@ -473,6 +498,3 @@ get_elifs(If = {'if', Ann, Cond, Then, Else}, Elifs) ->
|
|||||||
end;
|
end;
|
||||||
get_elifs(Else, Elifs) -> {lists:reverse(Elifs), {else, Else}}.
|
get_elifs(Else, Elifs) -> {lists:reverse(Elifs), {else, Else}}.
|
||||||
|
|
||||||
fmt(Fmt, Args) -> text(lists:flatten(io_lib:format(Fmt, Args))).
|
|
||||||
term(X) -> fmt("~p", [X]).
|
|
||||||
|
|
||||||
|
|||||||
+32
-26
@@ -7,7 +7,7 @@
|
|||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(aeso_scan).
|
-module(aeso_scan).
|
||||||
|
|
||||||
-export([scan/1]).
|
-export([scan/1, utf8_encode/1]).
|
||||||
|
|
||||||
-import(aeso_scan_lib, [token/1, token/2, symbol/0, skip/0,
|
-import(aeso_scan_lib, [token/1, token/2, symbol/0, skip/0,
|
||||||
override/2, push/2, pop/1]).
|
override/2, push/2, pop/1]).
|
||||||
@@ -28,7 +28,13 @@ lexer() ->
|
|||||||
QID = ["(", CON, "\\.)+", ID],
|
QID = ["(", CON, "\\.)+", ID],
|
||||||
QCON = ["(", CON, "\\.)+", CON],
|
QCON = ["(", CON, "\\.)+", CON],
|
||||||
OP = "[=!<>+\\-*/:&|?~@^]+",
|
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 = "\"([^\"\\\\]|(\\\\.))*\"",
|
STRING = "\"([^\"\\\\]|(\\\\.))*\"",
|
||||||
|
|
||||||
CommentStart = {"/\\*", push(comment, skip())},
|
CommentStart = {"/\\*", push(comment, skip())},
|
||||||
@@ -77,34 +83,34 @@ scan(String) ->
|
|||||||
%% -- Helpers ----------------------------------------------------------------
|
%% -- Helpers ----------------------------------------------------------------
|
||||||
|
|
||||||
parse_string([$" | Chars]) ->
|
parse_string([$" | Chars]) ->
|
||||||
unescape(Chars).
|
unicode:characters_to_nfc_binary(unescape(Chars)).
|
||||||
|
|
||||||
parse_char([$', $\\, Code, $']) ->
|
parse_char([$' | Chars]) ->
|
||||||
case Code of
|
case unicode:characters_to_nfc_list(unescape($', Chars, [])) of
|
||||||
$' -> $';
|
[Char] -> Char;
|
||||||
$\\ -> $\\;
|
_Bad -> {error, "Bad character literal: '" ++ Chars}
|
||||||
$b -> $\b;
|
end.
|
||||||
$e -> $\e;
|
|
||||||
$f -> $\f;
|
|
||||||
$n -> $\n;
|
|
||||||
$r -> $\r;
|
|
||||||
$t -> $\t;
|
|
||||||
$v -> $\v;
|
|
||||||
_ -> {error, "Bad control sequence: \\" ++ [Code]}
|
|
||||||
end;
|
|
||||||
parse_char([$', C, $']) -> C.
|
|
||||||
|
|
||||||
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));
|
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),
|
C = list_to_integer([D1, D2], 16),
|
||||||
unescape(Chars, [C | Acc]);
|
Utf8Cs = binary_to_list(unicode:characters_to_binary([C])),
|
||||||
unescape([$\\, Code | Chars], Acc) ->
|
unescape(Delim, Chars, [Utf8Cs | Acc]);
|
||||||
Ok = fun(C) -> unescape(Chars, [C | Acc]) end,
|
unescape(Delim, [$\\, Code | Chars], Acc) ->
|
||||||
|
Ok = fun(C) -> unescape(Delim, Chars, [C | Acc]) end,
|
||||||
case Code of
|
case Code of
|
||||||
$" -> Ok($");
|
Delim -> Ok(Delim);
|
||||||
$\\ -> Ok($\\);
|
$\\ -> Ok($\\);
|
||||||
$b -> Ok($\b);
|
$b -> Ok($\b);
|
||||||
$e -> Ok($\e);
|
$e -> Ok($\e);
|
||||||
@@ -115,8 +121,8 @@ unescape([$\\, Code | Chars], Acc) ->
|
|||||||
$v -> Ok($\v);
|
$v -> Ok($\v);
|
||||||
_ -> error("Bad control sequence: \\" ++ [Code]) %% TODO
|
_ -> error("Bad control sequence: \\" ++ [Code]) %% TODO
|
||||||
end;
|
end;
|
||||||
unescape([C | Chars], Acc) ->
|
unescape(Delim, [C | Chars], Acc) ->
|
||||||
unescape(Chars, [C | Acc]).
|
unescape(Delim, Chars, [C | Acc]).
|
||||||
|
|
||||||
strip_underscores(S) ->
|
strip_underscores(S) ->
|
||||||
lists:filter(fun(C) -> C /= $_ end, S).
|
lists:filter(fun(C) -> C /= $_ end, S).
|
||||||
|
|||||||
+19
-9
@@ -37,18 +37,26 @@
|
|||||||
-type decl() :: {contract, ann(), con(), [decl()]}
|
-type decl() :: {contract, ann(), con(), [decl()]}
|
||||||
| {namespace, ann(), con(), [decl()]}
|
| {namespace, ann(), con(), [decl()]}
|
||||||
| {pragma, ann(), pragma()}
|
| {pragma, ann(), pragma()}
|
||||||
| {type_decl, ann(), id(), [tvar()]}
|
| {type_decl, ann(), id(), [tvar()]} % Only for error msgs
|
||||||
| {type_def, ann(), id(), [tvar()], typedef()}
|
| {type_def, ann(), id(), [tvar()], typedef()}
|
||||||
| {fun_decl, ann(), id(), type()}
|
| {fun_clauses, ann(), id(), type(), [letfun() | fundecl()]}
|
||||||
| letbind().
|
| {block, ann(), [decl()]}
|
||||||
|
| fundecl()
|
||||||
|
| letfun()
|
||||||
|
| letval(). % Only for error msgs
|
||||||
|
|
||||||
-type compiler_version() :: [non_neg_integer()].
|
-type compiler_version() :: [non_neg_integer()].
|
||||||
|
|
||||||
-type pragma() :: {compiler, '==' | '<' | '>' | '=<' | '>=', compiler_version()}.
|
-type pragma() :: {compiler, '==' | '<' | '>' | '=<' | '>=', compiler_version()}.
|
||||||
|
|
||||||
|
|
||||||
|
-type letval() :: {letval, ann(), pat(), expr()}.
|
||||||
|
-type letfun() :: {letfun, ann(), id(), [pat()], type(), expr()}.
|
||||||
|
-type fundecl() :: {fun_decl, ann(), id(), type()}.
|
||||||
|
|
||||||
-type letbind()
|
-type letbind()
|
||||||
:: {letval, ann(), id(), type(), expr()}
|
:: letfun()
|
||||||
| {letfun, ann(), id(), [arg()], type(), expr()}.
|
| letval().
|
||||||
|
|
||||||
-type arg() :: {arg, ann(), id(), type()}.
|
-type arg() :: {arg, ann(), id(), type()}.
|
||||||
|
|
||||||
@@ -100,9 +108,8 @@
|
|||||||
| {list, ann(), [expr()]}
|
| {list, ann(), [expr()]}
|
||||||
| {list_comp, ann(), expr(), [comprehension_exp()]}
|
| {list_comp, ann(), expr(), [comprehension_exp()]}
|
||||||
| {typed, ann(), expr(), type()}
|
| {typed, ann(), expr(), type()}
|
||||||
| {record, ann(), [field(expr())]}
|
| {record_or_map(), ann(), [field(expr())]}
|
||||||
| {record, ann(), expr(), [field(expr())]} %% record update
|
| {record_or_map(), ann(), expr(), [field(expr())]} %% record/map update
|
||||||
| {map, ann(), expr(), [field(expr())]} %% map update
|
|
||||||
| {map, ann(), [{expr(), expr()}]}
|
| {map, ann(), [{expr(), expr()}]}
|
||||||
| {map_get, ann(), expr(), expr()}
|
| {map_get, ann(), expr(), expr()}
|
||||||
| {map_get, ann(), expr(), expr(), expr()}
|
| {map_get, ann(), expr(), expr(), expr()}
|
||||||
@@ -111,7 +118,9 @@
|
|||||||
| id() | qid() | con() | qcon()
|
| id() | qid() | con() | qcon()
|
||||||
| constant().
|
| 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()}
|
| {comprehension_if, ann(), expr()}
|
||||||
| letbind() ].
|
| letbind() ].
|
||||||
|
|
||||||
@@ -139,6 +148,7 @@
|
|||||||
-type pat() :: {app, ann(), con() | op(), [pat()]}
|
-type pat() :: {app, ann(), con() | op(), [pat()]}
|
||||||
| {tuple, ann(), [pat()]}
|
| {tuple, ann(), [pat()]}
|
||||||
| {list, ann(), [pat()]}
|
| {list, ann(), [pat()]}
|
||||||
|
| {typed, ann(), pat(), type()}
|
||||||
| {record, ann(), [field(pat())]}
|
| {record, ann(), [field(pat())]}
|
||||||
| constant()
|
| constant()
|
||||||
| con()
|
| con()
|
||||||
|
|||||||
@@ -45,11 +45,11 @@ fold(Alg = #alg{zero = Zero, plus = Plus, scoped = Scoped}, Fun, K, X) ->
|
|||||||
%% decl()
|
%% decl()
|
||||||
{contract, _, _, Ds} -> Decl(Ds);
|
{contract, _, _, Ds} -> Decl(Ds);
|
||||||
{namespace, _, _, Ds} -> Decl(Ds);
|
{namespace, _, _, Ds} -> Decl(Ds);
|
||||||
{type_decl, _, I, _} -> BindType(I);
|
|
||||||
{type_def, _, I, _, D} -> Plus(BindType(I), Decl(D));
|
{type_def, _, I, _, D} -> Plus(BindType(I), Decl(D));
|
||||||
{fun_decl, _, _, T} -> Type(T);
|
{fun_decl, _, _, T} -> Type(T);
|
||||||
{letval, _, 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])]);
|
{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()
|
%% typedef()
|
||||||
{alias_t, T} -> Type(T);
|
{alias_t, T} -> Type(T);
|
||||||
{record_t, Fs} -> Type(Fs);
|
{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})));
|
Plus(Expr(E), Scoped(BindExpr(I), Expr({list_comp, A, Y, R})));
|
||||||
{list_comp, A, Y, [{comprehension_if, _, E}|R]} ->
|
{list_comp, A, Y, [{comprehension_if, _, E}|R]} ->
|
||||||
Plus(Expr(E), Expr({list_comp, A, Y, R}));
|
Plus(Expr(E), Expr({list_comp, A, Y, R}));
|
||||||
{list_comp, A, Y, [D = {letval, _, F, _, _} | R]} ->
|
{list_comp, A, Y, [D = {letval, _, Pat, _} | R]} ->
|
||||||
Plus(Decl(D), Scoped(BindExpr(F), Expr({list_comp, A, Y, R})));
|
Plus(Decl(D), Scoped(BindExpr(Pat), Expr({list_comp, A, Y, R})));
|
||||||
{list_comp, A, Y, [D = {letfun, _, F, _, _, _} | R]} ->
|
{list_comp, A, Y, [D = {letfun, _, F, _, _, _} | R]} ->
|
||||||
Plus(Decl(D), Scoped(BindExpr(F), Expr({list_comp, A, Y, R})));
|
Plus(Decl(D), Scoped(BindExpr(F), Expr({list_comp, A, Y, R})));
|
||||||
{typed, _, E, T} -> Plus(Expr(E), Type(T));
|
{typed, _, E, T} -> Plus(Expr(E), Type(T));
|
||||||
|
|||||||
+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"}, _}, N) -> address_literal(oracle_pubkey, N);
|
||||||
from_aevm(word, {app_t, _, {id, _, "oracle_query"}, _}, N) -> address_literal(oracle_query_id, 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, {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, _, "int"}, N0) ->
|
||||||
from_aevm(word, {id, _, "bits"}, N) -> error({todo, bits, N});
|
<<N:256/signed>> = <<N0:256>>,
|
||||||
from_aevm(word, {id, _, "bool"}, N) -> {bool, [], N /= 0};
|
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 ->
|
from_aevm(word, {bytes_t, _, Len}, Val) when Len =< 32 ->
|
||||||
<<Bytes:Len/unit:8, _/binary>> = <<Val:32/unit:8>>,
|
<<Bytes:Len/unit:8, _/binary>> = <<Val:32/unit:8>>,
|
||||||
{bytes, [], <<Bytes:Len/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),
|
VmTypes = lists:nth(Tag + 1, VmCons),
|
||||||
ConType = lists:nth(Tag + 1, Cons),
|
ConType = lists:nth(Tag + 1, Cons),
|
||||||
from_aevm(VmTypes, ConType, Args);
|
from_aevm(VmTypes, ConType, Args);
|
||||||
|
from_aevm([], {constr_t, _, Con, []}, []) -> Con;
|
||||||
from_aevm(VmTypes, {constr_t, _, Con, Types}, Args)
|
from_aevm(VmTypes, {constr_t, _, Con, Types}, Args)
|
||||||
when length(VmTypes) == length(Types),
|
when length(VmTypes) == length(Types),
|
||||||
length(VmTypes) == length(Args) ->
|
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({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({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({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, _, "bits"}, ?FATE_BITS(N)) -> make_bits(N);
|
||||||
from_fate({id, _, "int"}, N) when is_integer(N) -> {int, [], 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, _, "bool"}, B) when is_boolean(B) -> {bool, [], B};
|
||||||
from_fate({id, _, "string"}, S) when is_binary(S) -> {string, [], S};
|
from_fate({id, _, "string"}, S) when is_binary(S) -> {string, [], S};
|
||||||
from_fate({app_t, _, {id, _, "list"}, [Type]}, List) when is_list(List) ->
|
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) ->
|
when length(Types) == tuple_size(Val) ->
|
||||||
{tuple, [], [from_fate(Type, X)
|
{tuple, [], [from_fate(Type, X)
|
||||||
|| {Type, X} <- lists:zip(Types, tuple_to_list(Val))]};
|
|| {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))
|
from_fate({record_t, Fields}, ?FATE_TUPLE(Val))
|
||||||
when length(Fields) == tuple_size(Val) ->
|
when length(Fields) == tuple_size(Val) ->
|
||||||
{record, [], [ {field, [], [{proj, [], FName}], from_fate(FType, X)}
|
{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);
|
from_fate(ConType, ArgList);
|
||||||
_ -> throw(cannot_translate_to_sophia)
|
_ -> throw(cannot_translate_to_sophia)
|
||||||
end;
|
end;
|
||||||
|
from_fate({constr_t, _, Con, []}, []) -> Con;
|
||||||
from_fate({constr_t, _, Con, Types}, Args)
|
from_fate({constr_t, _, Con, Types}, Args)
|
||||||
when length(Types) == length(Args) ->
|
when length(Types) == length(Args) ->
|
||||||
{app, [], Con, [ from_fate(Type, Arg)
|
{app, [], Con, [ from_fate(Type, Arg)
|
||||||
|| {Type, Arg} <- lists:zip(Types, Args) ]};
|
|| {Type, Arg} <- lists:zip(Types, Args) ]};
|
||||||
from_fate(_Type, _Data) ->
|
from_fate(_Type, _Data) ->
|
||||||
throw(cannot_translate_to_sophia).
|
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,
|
{application, aesophia,
|
||||||
[{description, "Contract Language for aeternity"},
|
[{description, "Contract Language for aeternity"},
|
||||||
{vsn, "4.0.0"},
|
{vsn, "4.3.0"},
|
||||||
{registered, []},
|
{registered, []},
|
||||||
{applications,
|
{applications,
|
||||||
[kernel,
|
[kernel,
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ encode_decode_sophia_test() ->
|
|||||||
Other -> Other
|
Other -> Other
|
||||||
end end,
|
end end,
|
||||||
ok = Check("int", "42"),
|
ok = Check("int", "42"),
|
||||||
ok = Check("int", "-42"),
|
ok = Check("int", "- 42"),
|
||||||
ok = Check("bool", "true"),
|
ok = Check("bool", "true"),
|
||||||
ok = Check("bool", "false"),
|
ok = Check("bool", "false"),
|
||||||
ok = Check("string", "\"Hello\""),
|
ok = Check("string", "\"Hello\""),
|
||||||
|
|||||||
+18
-11
@@ -11,7 +11,10 @@ test_contract(N) ->
|
|||||||
{Contract,MapACI,DecACI} = test_cases(N),
|
{Contract,MapACI,DecACI} = test_cases(N),
|
||||||
{ok,JSON} = aeso_aci:contract_interface(json, Contract),
|
{ok,JSON} = aeso_aci:contract_interface(json, Contract),
|
||||||
?assertEqual([MapACI], JSON),
|
?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) ->
|
test_cases(1) ->
|
||||||
Contract = <<"payable contract C =\n"
|
Contract = <<"payable contract C =\n"
|
||||||
@@ -80,11 +83,11 @@ test_cases(3) ->
|
|||||||
DecACI = <<"contract C =\n"
|
DecACI = <<"contract C =\n"
|
||||||
" type state = unit\n"
|
" type state = unit\n"
|
||||||
" datatype event = SingleEventDefined\n"
|
" datatype event = SingleEventDefined\n"
|
||||||
" datatype bert('a) = Bin('a)\n"
|
" datatype bert('a) = Bin('a)\n"
|
||||||
" entrypoint a : (C.bert(string)) => int\n">>,
|
" entrypoint a : (C.bert(string)) => int\n">>,
|
||||||
{Contract,MapACI,DecACI}.
|
{Contract,MapACI,DecACI}.
|
||||||
|
|
||||||
%% Rounttrip
|
%% Roundtrip
|
||||||
aci_test_() ->
|
aci_test_() ->
|
||||||
[{"Testing ACI generation for " ++ ContractName,
|
[{"Testing ACI generation for " ++ ContractName,
|
||||||
fun() -> aci_test_contract(ContractName) end}
|
fun() -> aci_test_contract(ContractName) end}
|
||||||
@@ -94,8 +97,13 @@ all_contracts() -> aeso_compiler_tests:compilable_contracts().
|
|||||||
|
|
||||||
aci_test_contract(Name) ->
|
aci_test_contract(Name) ->
|
||||||
String = aeso_test_utils:read_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, 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]),
|
io:format("JSON:\n~p\n", [JSON]),
|
||||||
{ok, ContractStub} = aeso_aci:render_aci_json(JSON),
|
{ok, ContractStub} = aeso_aci:render_aci_json(JSON),
|
||||||
@@ -106,7 +114,7 @@ aci_test_contract(Name) ->
|
|||||||
ok.
|
ok.
|
||||||
|
|
||||||
check_stub(Stub, Options) ->
|
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 ->
|
Ast ->
|
||||||
try
|
try
|
||||||
%% io:format("AST: ~120p\n", [Ast]),
|
%% io:format("AST: ~120p\n", [Ast]),
|
||||||
@@ -117,9 +125,8 @@ check_stub(Stub, Options) ->
|
|||||||
_:R ->
|
_:R ->
|
||||||
io:format("Error: ~p\n", [R]),
|
io:format("Error: ~p\n", [R]),
|
||||||
error(R)
|
error(R)
|
||||||
end;
|
end
|
||||||
{error, E} ->
|
catch throw:{error, Errs} ->
|
||||||
io:format("Error: ~p\n", [E]),
|
_ = [ io:format("~s\n", [aeso_errors:pp(E)]) || E <- Errs ],
|
||||||
error({parse_error, E})
|
error({parse_errors, Errs})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|||||||
@@ -29,11 +29,10 @@ calldata_test_() ->
|
|||||||
true -> ast_exprs(ContractString, Fun, Args, [{backend, fate}]);
|
true -> ast_exprs(ContractString, Fun, Args, [{backend, fate}]);
|
||||||
false -> undefined
|
false -> undefined
|
||||||
end,
|
end,
|
||||||
case FateExprs == undefined orelse AevmExprs == undefined of
|
ParsedExprs = parse_args(Fun, Args),
|
||||||
true -> ok;
|
[ ?assertEqual(ParsedExprs, AevmExprs) || AevmExprs /= undefined ],
|
||||||
false ->
|
[ ?assertEqual(ParsedExprs, FateExprs) || FateExprs /= undefined ],
|
||||||
?assertEqual(FateExprs, AevmExprs)
|
ok
|
||||||
end
|
|
||||||
end} || {ContractName, Fun, Args} <- compilable_contracts()].
|
end} || {ContractName, Fun, Args} <- compilable_contracts()].
|
||||||
|
|
||||||
calldata_aci_test_() ->
|
calldata_aci_test_() ->
|
||||||
@@ -53,19 +52,34 @@ calldata_aci_test_() ->
|
|||||||
true -> ast_exprs(ContractACI, Fun, Args, [{backend, fate}]);
|
true -> ast_exprs(ContractACI, Fun, Args, [{backend, fate}]);
|
||||||
false -> undefined
|
false -> undefined
|
||||||
end,
|
end,
|
||||||
case FateExprs == undefined orelse AevmExprs == undefined of
|
ParsedExprs = parse_args(Fun, Args),
|
||||||
true -> ok;
|
[ ?assertEqual(ParsedExprs, AevmExprs) || AevmExprs /= undefined ],
|
||||||
false ->
|
[ ?assertEqual(ParsedExprs, FateExprs) || FateExprs /= undefined ],
|
||||||
?assertEqual(FateExprs, AevmExprs)
|
ok
|
||||||
end
|
|
||||||
end} || {ContractName, Fun, Args} <- compilable_contracts()].
|
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) ->
|
ast_exprs(ContractString, Fun, Args, Opts) ->
|
||||||
{ok, Data} = (catch aeso_compiler:create_calldata(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)),
|
{ok, _Types, Exprs} = (catch aeso_compiler:decode_calldata(ContractString, Fun, Data, Opts)),
|
||||||
?assert(is_list(Exprs)),
|
?assert(is_list(Exprs)),
|
||||||
Exprs.
|
strip_ann(Exprs).
|
||||||
|
|
||||||
check_errors(Expect, ErrorString) ->
|
check_errors(Expect, ErrorString) ->
|
||||||
%% This removes the final single \n as well.
|
%% This removes the final single \n as well.
|
||||||
@@ -85,7 +99,9 @@ compilable_contracts() ->
|
|||||||
{"maps", "init", []},
|
{"maps", "init", []},
|
||||||
{"funargs", "menot", ["false"]},
|
{"funargs", "menot", ["false"]},
|
||||||
{"funargs", "append", ["[\"false\", \" is\", \" not\", \" true\"]"]},
|
{"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", "read", ["{label = \"question 1\", result = 4}"]},
|
||||||
{"funargs", "sjutton", ["#0011012003100011012003100011012003"]},
|
{"funargs", "sjutton", ["#0011012003100011012003100011012003"]},
|
||||||
{"funargs", "sextiosju", ["#01020304050607080910111213141516171819202122232425262728293031323334353637383940"
|
{"funargs", "sextiosju", ["#01020304050607080910111213141516171819202122232425262728293031323334353637383940"
|
||||||
@@ -97,6 +113,7 @@ compilable_contracts() ->
|
|||||||
{"funargs", "traffic_light", ["Pantone(12)"]},
|
{"funargs", "traffic_light", ["Pantone(12)"]},
|
||||||
{"funargs", "tuples", ["()"]},
|
{"funargs", "tuples", ["()"]},
|
||||||
%% TODO {"funargs", "due", ["FixedTTL(1020)"]},
|
%% TODO {"funargs", "due", ["FixedTTL(1020)"]},
|
||||||
|
{"funargs", "singleton_rec", ["{x = 1000}"]},
|
||||||
{"variant_types", "init", []},
|
{"variant_types", "init", []},
|
||||||
{"basic_auth", "init", []},
|
{"basic_auth", "init", []},
|
||||||
{"address_literals", "init", []},
|
{"address_literals", "init", []},
|
||||||
@@ -126,4 +143,4 @@ compilable_contracts() ->
|
|||||||
not_yet_compilable(fate) ->
|
not_yet_compilable(fate) ->
|
||||||
[];
|
[];
|
||||||
not_yet_compilable(aevm) ->
|
not_yet_compilable(aevm) ->
|
||||||
[].
|
["funargs", "strings"].
|
||||||
|
|||||||
+276
-87
@@ -12,6 +12,14 @@
|
|||||||
|
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-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
|
%% Very simply test compile the given contracts. Only basic checks
|
||||||
%% are made on the output, just that it is a binary which indicates
|
%% are made on the output, just that it is a binary which indicates
|
||||||
%% that the compilation worked.
|
%% that the compilation worked.
|
||||||
@@ -31,7 +39,7 @@ simple_compile_test_() ->
|
|||||||
error(ErrBin)
|
error(ErrBin)
|
||||||
end
|
end
|
||||||
end} || ContractName <- compilable_contracts(), Backend <- [aevm, fate],
|
end} || ContractName <- compilable_contracts(), Backend <- [aevm, fate],
|
||||||
not lists:member(ContractName, not_yet_compilable(Backend))] ++
|
not lists:member(ContractName, not_compilable_on(Backend))] ++
|
||||||
[ {"Test file not found error",
|
[ {"Test file not found error",
|
||||||
fun() ->
|
fun() ->
|
||||||
{error, Errors} = aeso_compiler:file("does_not_exist.aes"),
|
{error, Errors} = aeso_compiler:file("does_not_exist.aes"),
|
||||||
@@ -102,7 +110,15 @@ compile(Backend, Name) ->
|
|||||||
|
|
||||||
compile(Backend, Name, Options) ->
|
compile(Backend, Name, Options) ->
|
||||||
String = aeso_test_utils:read_contract(Name),
|
String = aeso_test_utils:read_contract(Name),
|
||||||
case aeso_compiler:from_string(String, [{src_file, Name ++ ".aes"}, {backend, Backend} | Options]) of
|
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;
|
{ok, Map} -> Map;
|
||||||
{error, ErrorString} when is_binary(ErrorString) -> ErrorString;
|
{error, ErrorString} when is_binary(ErrorString) -> ErrorString;
|
||||||
{error, Errors} -> Errors
|
{error, Errors} -> Errors
|
||||||
@@ -130,6 +146,7 @@ compilable_contracts() ->
|
|||||||
"test",
|
"test",
|
||||||
"builtin_bug",
|
"builtin_bug",
|
||||||
"builtin_map_get_bug",
|
"builtin_map_get_bug",
|
||||||
|
"lc_record_bug",
|
||||||
"nodeadcode",
|
"nodeadcode",
|
||||||
"deadcode",
|
"deadcode",
|
||||||
"variant_types",
|
"variant_types",
|
||||||
@@ -137,6 +154,7 @@ compilable_contracts() ->
|
|||||||
"events",
|
"events",
|
||||||
"include",
|
"include",
|
||||||
"basic_auth",
|
"basic_auth",
|
||||||
|
"basic_auth_tx",
|
||||||
"bitcoin_auth",
|
"bitcoin_auth",
|
||||||
"address_literals",
|
"address_literals",
|
||||||
"bytes_equality",
|
"bytes_equality",
|
||||||
@@ -145,6 +163,7 @@ compilable_contracts() ->
|
|||||||
"bytes_to_x",
|
"bytes_to_x",
|
||||||
"bytes_concat",
|
"bytes_concat",
|
||||||
"aens",
|
"aens",
|
||||||
|
"aens_update",
|
||||||
"tuple_match",
|
"tuple_match",
|
||||||
"cyclic_include",
|
"cyclic_include",
|
||||||
"stdlib_include",
|
"stdlib_include",
|
||||||
@@ -153,11 +172,27 @@ compilable_contracts() ->
|
|||||||
"list_comp",
|
"list_comp",
|
||||||
"payable",
|
"payable",
|
||||||
"unapplied_builtins",
|
"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_compilable_on(fate) -> [];
|
||||||
not_yet_compilable(aevm) -> [].
|
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
|
%% Contracts that should produce type errors
|
||||||
|
|
||||||
@@ -288,9 +323,22 @@ failing_contracts() ->
|
|||||||
"Repeated name x in pattern\n"
|
"Repeated name x in pattern\n"
|
||||||
" x :: x (at line 26, column 7)">>,
|
" x :: x (at line 26, column 7)">>,
|
||||||
<<?Pos(44, 14)
|
<<?Pos(44, 14)
|
||||||
"Repeated argument x to function repeated_arg (at line 44, column 14).">>,
|
"Repeated names x, y in pattern\n"
|
||||||
<<?Pos(44, 14)
|
" (x : int, y, x : string, y : bool) (at line 44, column 14)">>,
|
||||||
"Repeated argument y to function repeated_arg (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)
|
<<?Pos(14, 24)
|
||||||
"No record type with fields y, z (at line 14, column 24)">>,
|
"No record type with fields y, z (at line 14, column 24)">>,
|
||||||
<<?Pos(15, 26)
|
<<?Pos(15, 26)
|
||||||
@@ -346,83 +394,21 @@ failing_contracts() ->
|
|||||||
[<<?Pos(12, 42)
|
[<<?Pos(12, 42)
|
||||||
"Cannot unify int\n"
|
"Cannot unify int\n"
|
||||||
" and string\n"
|
" and string\n"
|
||||||
"when checking the record projection at line 12, column 42\n"
|
"when checking the type of the expression at line 12, column 42\n"
|
||||||
" r.foo : (gas : int, value : int) => Remote.themap\n"
|
" r.foo() : map(int, string)\n"
|
||||||
"against the expected type\n"
|
"against the expected type\n"
|
||||||
" (gas : int, value : int) => map(string, int)">>])
|
" map(string, int)">>])
|
||||||
, ?TYPE_ERROR(bad_include_and_ns,
|
, ?TYPE_ERROR(not_toplevel_include,
|
||||||
[<<?Pos(2, 11)
|
[<<?Pos(2, 11)
|
||||||
"Include of 'included.aes' at line 2, column 11\nnot allowed, include only allowed at top level.">>,
|
"Include of 'included.aes' at line 2, column 11\nnot allowed, include only allowed at top level.">>])
|
||||||
<<?Pos(3, 13)
|
, ?TYPE_ERROR(not_toplevel_namespace,
|
||||||
"Nested namespace not allowed\nNamespace 'Foo' at line 3, column 13 not defined at top level.">>])
|
[<<?Pos(2, 13)
|
||||||
|
"Nested namespaces are not allowed\nNamespace 'Foo' at line 2, column 13 not defined at top level.">>])
|
||||||
|
, ?TYPE_ERROR(not_toplevel_contract,
|
||||||
|
[<<?Pos(2, 12)
|
||||||
|
"Nested contracts are not allowed\nContract 'Con' at line 2, column 12 not defined at top level.">>])
|
||||||
, ?TYPE_ERROR(bad_address_literals,
|
, ?TYPE_ERROR(bad_address_literals,
|
||||||
[<<?Pos(32, 5)
|
[<<?Pos(11, 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)
|
|
||||||
"Cannot unify address\n"
|
"Cannot unify address\n"
|
||||||
" and oracle(int, bool)\n"
|
" and oracle(int, bool)\n"
|
||||||
"when checking the type of the expression at line 11, column 5\n"
|
"when checking the type of the expression at line 11, column 5\n"
|
||||||
@@ -443,6 +429,72 @@ failing_contracts() ->
|
|||||||
" ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt : address\n"
|
" ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt : address\n"
|
||||||
"against the expected type\n"
|
"against the expected type\n"
|
||||||
" bytes(32)">>,
|
" 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),
|
<<?Pos(34, 5),
|
||||||
"The type address is not a contract type\n"
|
"The type address is not a contract type\n"
|
||||||
"when checking that the call to\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">>
|
"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,
|
, ?TYPE_ERROR(map_as_map_key,
|
||||||
[<<?Pos(5, 25)
|
[<<?Pos(5, 47)
|
||||||
"Invalid key type\n"
|
"Invalid key type\n"
|
||||||
" map(int, int)\n"
|
" map(int, int)\n"
|
||||||
"Map keys cannot contain other maps.">>,
|
"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"
|
"Invalid key type\n"
|
||||||
" lm\n"
|
" lm\n"
|
||||||
"Map keys cannot contain other maps.">>])
|
"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"
|
"Failed to resolve byte array lengths in call to Bytes.split with argument of type\n"
|
||||||
" - 'f (at line 12, column 20)\n"
|
" - 'f (at line 12, column 20)\n"
|
||||||
"and result types\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)">>,
|
" - bytes(20) (at line 12, column 29)">>,
|
||||||
<<?Pos(16, 5)
|
<<?Pos(16, 5)
|
||||||
"Failed to resolve byte array lengths in call to Bytes.split with argument of type\n"
|
"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"
|
" - 'b (at line 18, column 20)\n"
|
||||||
"and result types\n"
|
"and result types\n"
|
||||||
" - bytes(20) (at line 18, column 25)\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,
|
, ?TYPE_ERROR(wrong_compiler_version,
|
||||||
[<<?Pos(1, 1)
|
[<<?Pos(1, 1)
|
||||||
"Cannot compile with this version of the compiler,\n"
|
"Cannot compile with this version of the compiler,\n"
|
||||||
@@ -587,6 +643,93 @@ failing_contracts() ->
|
|||||||
[<<?Pos(5, 28)
|
[<<?Pos(5, 28)
|
||||||
"Invalid call to contract entrypoint 'Foo.foo'.\n"
|
"Invalid call to contract entrypoint 'Foo.foo'.\n"
|
||||||
"It must be called as 'c.foo' for some c : Foo.">>])
|
"It must be called as 'c.foo' for some c : Foo.">>])
|
||||||
|
, ?TYPE_ERROR(toplevel_let,
|
||||||
|
[<<?Pos(2, 7)
|
||||||
|
"Toplevel \"let\" definitions are not supported\n"
|
||||||
|
"Value this_is_illegal at line 2, column 7 could be replaced by 0-argument function">>])
|
||||||
|
, ?TYPE_ERROR(empty_typedecl,
|
||||||
|
[<<?Pos(2, 8)
|
||||||
|
"Empty type declarations are not supported\n"
|
||||||
|
"Type t at line 2, column 8 lacks a definition">>])
|
||||||
|
, ?TYPE_ERROR(higher_kinded_type,
|
||||||
|
[<<?Pos(2, 35)
|
||||||
|
"Type 'm is a higher kinded type variable\n"
|
||||||
|
"(takes another type as an argument)">>])
|
||||||
|
, ?TYPE_ERROR(bad_arity,
|
||||||
|
[<<?Pos(3, 20)
|
||||||
|
"Arity for id doesn't match. Expected 1, got 0">>,
|
||||||
|
<<?Pos(3, 25)
|
||||||
|
"Cannot unify int\n"
|
||||||
|
" and id\n"
|
||||||
|
"when checking the type of the expression at line 3, column 25\n"
|
||||||
|
" 123 : int\n"
|
||||||
|
"against the expected type\n"
|
||||||
|
" id">>,
|
||||||
|
<<?Pos(4, 20)
|
||||||
|
"Arity for id doesn't match. Expected 1, got 2">>,
|
||||||
|
<<?Pos(4, 35)
|
||||||
|
"Cannot unify int\n"
|
||||||
|
" and id(int, int)\n"
|
||||||
|
"when checking the type of the expression at line 4, column 35\n"
|
||||||
|
" 123 : int\n"
|
||||||
|
"against the expected type\n"
|
||||||
|
" id(int, int)">>])
|
||||||
|
, ?TYPE_ERROR(bad_unnamed_map_update_default,
|
||||||
|
[<<?Pos(4, 17)
|
||||||
|
"Invalid map update with default">>])
|
||||||
|
, ?TYPE_ERROR(non_functional_entrypoint,
|
||||||
|
[<<?Pos(2, 14)
|
||||||
|
"f at line 2, column 14 was declared with an invalid type int.\n"
|
||||||
|
"Entrypoints and functions must have functional types">>])
|
||||||
|
, ?TYPE_ERROR(bad_records,
|
||||||
|
[<<?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).
|
-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.")
|
"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").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
|
id(X) -> X.
|
||||||
|
|
||||||
simple_contracts_test_() ->
|
simple_contracts_test_() ->
|
||||||
{foreach,
|
{foreach,
|
||||||
fun() -> ok end,
|
fun() -> ok end,
|
||||||
@@ -14,7 +16,7 @@ simple_contracts_test_() ->
|
|||||||
" function id(x) = x\n",
|
" function id(x) = x\n",
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
[{contract, _, {con, _, "Identity"},
|
[{contract, _, {con, _, "Identity"},
|
||||||
[{letfun, _, {id, _, "id"}, [{arg, _, {id, _, "x"}, {id, _, "_"}}], {id, _, "_"},
|
[{letfun, _, {id, _, "id"}, [{id, _, "x"}], {id, _, "_"},
|
||||||
{id, _, "x"}}]}], parse_string(Text)),
|
{id, _, "x"}}]}], parse_string(Text)),
|
||||||
ok
|
ok
|
||||||
end},
|
end},
|
||||||
@@ -30,7 +32,7 @@ simple_contracts_test_() ->
|
|||||||
end,
|
end,
|
||||||
Parse = fun(S) ->
|
Parse = fun(S) ->
|
||||||
try remove_line_numbers(parse_expr(S))
|
try remove_line_numbers(parse_expr(S))
|
||||||
catch _:_ -> ?assertMatch(ok, {parse_fail, S}) end
|
catch _:_ -> ?assertMatch(ok, id({parse_fail, S})) end
|
||||||
end,
|
end,
|
||||||
CheckParens = fun(Expr) ->
|
CheckParens = fun(Expr) ->
|
||||||
?assertEqual(Parse(NoPar(Expr)), Parse(Par(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,
|
LeftAssoc = fun(Op) -> CheckParens({{a, Op, b}, Op, c}) end,
|
||||||
RightAssoc = fun(Op) -> CheckParens({a, Op, {b, Op, c}}) end,
|
RightAssoc = fun(Op) -> CheckParens({a, Op, {b, Op, c}}) end,
|
||||||
NonAssoc = fun(Op) ->
|
NonAssoc = fun(Op) ->
|
||||||
OpAtom = list_to_atom(Op),
|
|
||||||
?assertThrow({error, [_]},
|
?assertThrow({error, [_]},
|
||||||
parse_expr(NoPar({a, Op, {b, Op, c}}))) end,
|
parse_expr(NoPar({a, Op, {b, Op, c}}))) end,
|
||||||
Stronger = fun(Op1, Op2) ->
|
Stronger = fun(Op1, Op2) ->
|
||||||
@@ -62,7 +63,8 @@ simple_contracts_test_() ->
|
|||||||
%% Parse tests of example contracts
|
%% Parse tests of example contracts
|
||||||
[ {lists:concat(["Parse the ", Contract, " contract."]),
|
[ {lists:concat(["Parse the ", Contract, " contract."]),
|
||||||
fun() -> roundtrip_contract(Contract) end}
|
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) ->
|
parse_contract(Name) ->
|
||||||
@@ -77,14 +79,14 @@ parse_string(Text, Opts) ->
|
|||||||
aeso_parser:string(Text, Opts).
|
aeso_parser:string(Text, Opts).
|
||||||
|
|
||||||
parse_expr(Text) ->
|
parse_expr(Text) ->
|
||||||
[{letval, _, _, _, Expr}] =
|
[{letval, _, _, Expr}] =
|
||||||
parse_string("let _ = " ++ Text),
|
parse_string("let _ = " ++ Text),
|
||||||
Expr.
|
Expr.
|
||||||
|
|
||||||
round_trip(Text) ->
|
round_trip(Text) ->
|
||||||
Contract = parse_string(Text),
|
Contract = parse_string(Text),
|
||||||
Text1 = prettypr:format(aeso_pretty:decls(strip_stdlib(Contract))),
|
Text1 = prettypr:format(aeso_pretty:decls(strip_stdlib(Contract))),
|
||||||
Contract1 = parse_string(Text1),
|
Contract1 = parse_string(aeso_scan:utf8_encode(Text1)),
|
||||||
NoSrcLoc = remove_line_numbers(Contract),
|
NoSrcLoc = remove_line_numbers(Contract),
|
||||||
NoSrcLoc1 = remove_line_numbers(Contract1),
|
NoSrcLoc1 = remove_line_numbers(Contract1),
|
||||||
?assertMatch(NoSrcLoc, diff(NoSrcLoc, NoSrcLoc1)).
|
?assertMatch(NoSrcLoc, diff(NoSrcLoc, NoSrcLoc1)).
|
||||||
|
|||||||
@@ -58,8 +58,7 @@ contract Greeter =
|
|||||||
|
|
||||||
let state = { greeting = "Hello" }
|
let state = { greeting = "Hello" }
|
||||||
|
|
||||||
let setGreeting =
|
function setGreeting(greeting: string) =
|
||||||
(greeting: string) =>
|
|
||||||
state{ greeting = greeting }
|
state{ greeting = greeting }
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+16
-1
@@ -33,7 +33,22 @@ contract AENSTest =
|
|||||||
sign : signature) : unit =
|
sign : signature) : unit =
|
||||||
AENS.claim(addr, name, salt, name_fee, signature = sign)
|
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,
|
stateful entrypoint transfer(owner : address,
|
||||||
new_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.
|
// Try to cover all syntactic constructs.
|
||||||
|
@compiler > 0
|
||||||
|
@compiler =< 10.1.1.1.1.1.2.3.4
|
||||||
|
|
||||||
contract AllSyntaxType =
|
|
||||||
type typeDecl /* bla */
|
|
||||||
type paramTypeDecl('a, 'b)
|
|
||||||
|
|
||||||
|
namespace Ns =
|
||||||
|
datatype d('a) = D | S(int) | M('a, list('a), int)
|
||||||
|
private function fff() = 123
|
||||||
|
|
||||||
|
stateful entrypoint
|
||||||
|
f (1, x) = (_) => x
|
||||||
|
|
||||||
|
payable contract AllSyntaxType =
|
||||||
/** Multi-
|
/** Multi-
|
||||||
* line
|
* line
|
||||||
* comment
|
* comment
|
||||||
*/
|
*/
|
||||||
function foo : _
|
stateful function foo : _
|
||||||
|
entrypoint bar : int => (int * 'a)
|
||||||
|
|
||||||
|
|
||||||
contract AllSyntax =
|
contract AllSyntax =
|
||||||
|
|
||||||
type typeDecl = int
|
datatype mickiewicz = Adam | Mickiewicz
|
||||||
type paramTypeDecl('a, 'b) = (('a, 'b) => 'b) => list('a) => 'b => 'b
|
record goethe('a, 'b) = {
|
||||||
|
johann : int,
|
||||||
|
wolfgang : 'a,
|
||||||
|
von : 'a * 'b * int,
|
||||||
|
goethe : unit
|
||||||
|
}
|
||||||
|
type dante = Ns.d(int)
|
||||||
|
type shakespeare('a) = goethe('a, 'a)
|
||||||
|
|
||||||
record nestedRecord = { x : int }
|
type state = shakespeare(int)
|
||||||
record recordType = { z : nestedRecord, y : int }
|
|
||||||
datatype variantType('a) = None | Some('a)
|
|
||||||
|
|
||||||
let valWithType : map(int, int) => option(int) = (m) => Map.get(m, 42)
|
entrypoint init() = {
|
||||||
let valNoType =
|
johann = 1000,
|
||||||
if(valWithType(Map.empty) == None)
|
wolfgang = -10,
|
||||||
print(42 mod 10 * 5 / 3)
|
|
||||||
|
|
||||||
function funWithType(x : int, y) : int * list(int) = (x, 0 :: [y] ++ [])
|
/* TODO: This does not compile because of bug in the parser tester.
|
||||||
function funNoType() =
|
von = (2 + 2, 0, List.sum([x | k <- [1,2,3]
|
||||||
let foo = (x, y : bool) =>
|
, let l = k + 1
|
||||||
if (! (y && x =< 0x0b || true)) [x]
|
, if(l < 10)
|
||||||
else [11..20]
|
, let f(x) = x + 100
|
||||||
let setY(r : recordType) : unit = r{ y = 5 }
|
, Adam <- [Adam, Mickiewicz]
|
||||||
let setX(r : recordType, x : int) : recordType = r { z.x = x } // nested record update
|
, let x = f(l)
|
||||||
let getY(r) = switch(r) {y = y} => y
|
])),
|
||||||
switch (funWithType(1, -2))
|
*/
|
||||||
(x, [y, z]) => bar({x = z, y = -y + - -z * (-1)})
|
von = (2 + 2, 0, List.sum([1,2,3,4])),
|
||||||
(x, y :: _) => ()
|
goethe = () }
|
||||||
|
|
||||||
let hash : address = #01ab0fff11
|
function f() =
|
||||||
let b = false
|
let kp = "nietzsche"
|
||||||
let qcon = Mod.Con
|
// let p = "Пушкин" // TODO: this also doesn't do right round_trip...
|
||||||
let str = "blabla\nfoo"
|
let k(x : bytes(8)) : bytes(8) = Bytes.to_int(#fedcba9876543210)
|
||||||
let chr = '"'
|
|
||||||
|
|
||||||
|
let f : () => address = () => ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt
|
||||||
|
if(Bits.test(Bits.all, 10))
|
||||||
|
abort("ohno")
|
||||||
|
if(true && false)
|
||||||
|
require(true, "ohyes")
|
||||||
|
elif(false || 2 == 2)
|
||||||
|
()
|
||||||
|
else
|
||||||
|
()
|
||||||
|
if(true) f(1,2)((1,2))
|
||||||
|
else switch(1::[1,2,3])
|
||||||
|
[] => 1
|
||||||
|
a::b => 123
|
||||||
|
1::2::3 => 123123
|
||||||
|
[2,3,4] => 1
|
||||||
|
_ => 13
|
||||||
|
1::[2] => 2138
|
||||||
|
put(state{johann = 1})
|
||||||
|
|
||||||
|
let m = {["foo"] = 19, /*hey wanna talk about inlined comments?*/ ["bar"] = 42}
|
||||||
|
let n = {}
|
||||||
|
m{ ["x" = 0] @ z = z + state.johann }
|
||||||
|
|
||||||
|
let sh : shakespeare(shakespeare(int)) =
|
||||||
|
{wolfgang = state}
|
||||||
|
sh{wolfgang.wolfgang = sh.wolfgang} // comment
|
||||||
|
|||||||
@@ -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 =
|
contract BytesToX =
|
||||||
|
|
||||||
entrypoint to_int(b : bytes(42)) : int = Bytes.to_int(b)
|
entrypoint to_int(b : bytes(42)) : int = Bytes.to_int(b)
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ contract ChannelOnChainContractOracle =
|
|||||||
Some(_value) =>
|
Some(_value) =>
|
||||||
"bet_already_taken"
|
"bet_already_taken"
|
||||||
|
|
||||||
|
public function expiry() =
|
||||||
|
Oracle.expiry(state.oracle)
|
||||||
|
|
||||||
public function query_fee() =
|
public function query_fee() =
|
||||||
Oracle.query_fee(state.oracle)
|
Oracle.query_fee(state.oracle)
|
||||||
|
|
||||||
|
|||||||
@@ -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})
|
stateful entrypoint set_worker(worker) = put(state{worker = worker})
|
||||||
|
|
||||||
entrypoint fac(x : int) : int =
|
entrypoint
|
||||||
if(x == 0) 1
|
fac : int => int
|
||||||
else x * state.worker.fac(x - 1)
|
fac(0) = 1
|
||||||
|
fac(x) = x * state.worker.fac(x - 1)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
|
include "String.aes"
|
||||||
contract FunctionArguments =
|
contract FunctionArguments =
|
||||||
|
|
||||||
entrypoint sum(n : int, m: int) =
|
entrypoint sum(n : int, m: int) =
|
||||||
@@ -45,3 +45,8 @@ contract FunctionArguments =
|
|||||||
|
|
||||||
entrypoint due(t : Chain.ttl) =
|
entrypoint due(t : Chain.ttl) =
|
||||||
true
|
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 }
|
| OwnerRemoved (address) // of { .removedOwner : Address }
|
||||||
| ReqChanged (int) // of { .newReq : int }
|
| ReqChanged (int) // of { .newReq : int }
|
||||||
|
|
||||||
let maxOwners : int = 250
|
function maxOwners() : int = 250
|
||||||
|
|
||||||
record state = { nRequired : int
|
record state = { nRequired : int
|
||||||
, nOwners : int
|
, nOwners : int
|
||||||
@@ -68,7 +68,7 @@ contract MultiSig =
|
|||||||
switch(check_pending(callhash()))
|
switch(check_pending(callhash()))
|
||||||
CheckFail(state') => { state = state' }
|
CheckFail(state') => { state = state' }
|
||||||
CheckOk(state') =>
|
CheckOk(state') =>
|
||||||
if(state.nOwners >= maxOwners) () /* TODO */
|
if(state.nOwners >= maxOwners()) () /* TODO */
|
||||||
else
|
else
|
||||||
let nOwners' = state'.nOwners + 1
|
let nOwners' = state'.nOwners + 1
|
||||||
{ state = state' { owners = Map.insert(nOwners', newOwner, state'.owners)
|
{ state = state' { owners = Map.insert(nOwners', newOwner, state'.owners)
|
||||||
|
|||||||
@@ -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 =
|
contract BadCon =
|
||||||
include "included.aes"
|
|
||||||
namespace Foo =
|
namespace Foo =
|
||||||
function foo() = 42
|
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) }
|
entrypoint init(ss : list(string)) = { stack = ss, size = length(ss) }
|
||||||
|
|
||||||
function length(xs) =
|
function
|
||||||
switch(xs)
|
length([]) = 0
|
||||||
[] => 0
|
length(_ :: xs) = length(xs) + 1
|
||||||
_ :: xs => length(xs) + 1
|
|
||||||
|
|
||||||
stateful entrypoint pop() : string =
|
stateful entrypoint pop() : string =
|
||||||
switch(state.stack)
|
switch(state.stack)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
include "String.aes"
|
||||||
contract Remote =
|
contract Remote =
|
||||||
record rstate = { i : int, s : string, m : map(int, int) }
|
record rstate = { i : int, s : string, m : map(int, int) }
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
include "String.aes"
|
||||||
contract Strings =
|
contract Strings =
|
||||||
entrypoint str_len(s) = String.length(s)
|
entrypoint str_len(s) = String.length(s)
|
||||||
entrypoint str_concat(s1, s2) = String.concat(s1, s2)
|
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.transfer
|
||||||
// AENS.revoke
|
// AENS.revoke
|
||||||
// Oracle.extend
|
// Oracle.extend
|
||||||
|
include "String.aes"
|
||||||
contract UnappliedBuiltins =
|
contract UnappliedBuiltins =
|
||||||
entrypoint main() = ()
|
entrypoint main() = ()
|
||||||
type o = oracle(int, int)
|
type o = oracle(int, int)
|
||||||
@@ -21,6 +22,7 @@ contract UnappliedBuiltins =
|
|||||||
function b_abort() = abort
|
function b_abort() = abort
|
||||||
function b_require() = require
|
function b_require() = require
|
||||||
function oracle_query_fee() = Oracle.query_fee
|
function oracle_query_fee() = Oracle.query_fee
|
||||||
|
function oracle_expiry() = Oracle.expiry
|
||||||
stateful function oracle_query() = Oracle.query : (o, _, _, _, _) => _
|
stateful function oracle_query() = Oracle.query : (o, _, _, _, _) => _
|
||||||
function oracle_get_question() = Oracle.get_question : (o, _) => _
|
function oracle_get_question() = Oracle.get_question : (o, _) => _
|
||||||
function oracle_get_answer() = Oracle.get_answer : (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 =
|
contract Voting =
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
|
|||||||
Reference in New Issue
Block a user