Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 411399a39a | |||
| 8289f4af9d | |||
| 3cd8c37399 | |||
| 8d7025f794 |
@@ -2,4 +2,4 @@ mkdocs==1.4.2
|
||||
mkdocs-simple-hooks==0.1.5
|
||||
mkdocs-material==9.0.9
|
||||
mike==1.1.2
|
||||
pygments==2.17.2
|
||||
pygments==2.14.0
|
||||
@@ -19,10 +19,8 @@ rebar3.crashdump
|
||||
*.erl~
|
||||
*.aes~
|
||||
aesophia
|
||||
sophia
|
||||
.qcci
|
||||
current_counterexample.eqc
|
||||
test/contracts/test.aes
|
||||
__pycache__
|
||||
.docssite/docs/*.md
|
||||
.vscode
|
||||
|
||||
+10
-86
@@ -1,88 +1,19 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
All notable changes to this project shall be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
and this project adheres to [Semantic
|
||||
Versioning](https://semver.org/spec/v2.0.0.html). One deviation from _Keep a
|
||||
Changelog_ is that "Unreleased" may suggest a specific version bump in case of
|
||||
breaking changes.
|
||||
|
||||
## [Unreleased]
|
||||
## [Unreleased] [8.x.x]
|
||||
### Added
|
||||
- Added a check for number of type variables in a type signature; it is serialized using 8 bits,
|
||||
so the upper limit is 256.
|
||||
### Changed
|
||||
- `pp_assembler` option to `pp_fate` as it is more specific.
|
||||
### Removed
|
||||
|
||||
## [8.0.1]
|
||||
### Changed
|
||||
- Upgrade aebytecode to v3.4.1 to fix C warnings
|
||||
|
||||
## [8.0.0]
|
||||
### Added
|
||||
- Bitwise operations for integers: `band`, `bor`, `bxor`, `bnot`, `<<` and `>>`.
|
||||
- `Int.mulmod` - combined builtin operation for multiplication and modulus.
|
||||
- `Crypto.poseidon` - a ZK/SNARK-friendly hash function (over the BLS12-381 scalar field).
|
||||
- `Address.to_bytes` - convert an address to its binary representation (for hashing, etc.).
|
||||
- Raw data pointers added to AENS. In short we have introduced a new namespace
|
||||
`AENSv2`; they contain types similar to the old `AENS`; `AENS.name` and
|
||||
`AENS.pointee`, where the latter now has a constructor `DataPt(bytes())`. All
|
||||
AENS actions have been moved to `AENSv2`, and `AENSv2.lookup` and
|
||||
`AENSv2.update` consume and produce the new types. The old `AENS` namespace
|
||||
only contains the old datatypes, that can be used to interface existing
|
||||
contracts. Standard library `AENSCompat` is added to convert between old and
|
||||
new pointers.
|
||||
- Introduce arbitrary sized binary arrays (type `bytes()`); adding `Bytes.split_any`,
|
||||
`Bytes.to_fixed_size`, `Bytes.to_any_size`, `Bytes.size`, `String.to_bytes`,
|
||||
and `Int.to_bytes`; and adjust `Bytes.concat` to allow both fixed and arbitrary
|
||||
sized byte arrays.
|
||||
- `Chain.network_id` - a function to get hold of the Chain's network id.
|
||||
- Allowing `Bytes.to_any_size` in calldata creation, to enable creation of arguments
|
||||
with arbitray size.
|
||||
- Signature literals `sg_...` - they have type `signature` (which is an alias for `bytes(64)`).
|
||||
- Support for OTP-27 - no changes in behavior.
|
||||
### Changed
|
||||
- `Crypto.verify_sig` is changed to have `msg : bytes()`. I.e. the
|
||||
signed data can be of any length (used to be limited to `bytes(32)`/`hash`).
|
||||
- System aliases are handled explicitly when converting to a Sophia value, this is only
|
||||
observable for `signature` where a value of type `signature` is now represented as a
|
||||
(new) signature literal.
|
||||
- Allow self-qualification, i.e. referencing `X.foo` when in namespace `X`.
|
||||
### Removed
|
||||
- `Bitwise.aes` standard library is removed - the builtin operations are superior.
|
||||
|
||||
## [7.4.1]
|
||||
### Changed
|
||||
- Improve how includes with relative paths are resolved during parsing/compilation. Relative
|
||||
include paths are now always relative to the file containing the `include` statement.
|
||||
### Fixed
|
||||
- Disable unused type warnings for types used inside of records.
|
||||
|
||||
## [7.4.0]
|
||||
### Changed
|
||||
- Names of lifted lambdas now consist of parent function's name and their
|
||||
position in the source code.
|
||||
### Fixed
|
||||
- Lifted lambdas get their names assigned deterministically.
|
||||
|
||||
## [7.3.0]
|
||||
### Fixed
|
||||
- Fixed a bug with polymorphism that allowed functions with the same name but different type to be considered as implementations for their corresponding interface function.
|
||||
- Fixed a bug in the byte code optimization that incorrectly reordered dependent instructions.
|
||||
|
||||
## [7.2.1]
|
||||
### Fixed
|
||||
- Fixed bugs with the newly added debugging symbols
|
||||
|
||||
## [7.2.0]
|
||||
### Added
|
||||
- Toplevel compile-time constants
|
||||
```
|
||||
namespace N =
|
||||
let nc = 1
|
||||
contract C =
|
||||
let cc = 2
|
||||
```
|
||||
- API functions for encoding/decoding Sophia values to/from FATE.
|
||||
### Removed
|
||||
- Remove the mapping from variables to FATE registers from the compilation output.
|
||||
- `pp_sophia_code` option as it was a duplicate of `pp_ast`.
|
||||
- `aeso_ast` module as it was unused.
|
||||
### Fixed
|
||||
- Warning about unused include when there is no include.
|
||||
|
||||
@@ -456,14 +387,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Simplify calldata creation - instead of passing a compiled contract, simply
|
||||
pass a (stubbed) contract string.
|
||||
|
||||
[Unreleased]: https://github.com/aeternity/aesophia/compare/v8.0.1...HEAD
|
||||
[8.0.1]: https://github.com/aeternity/aesophia/compare/v8.0.0...v8.0.1
|
||||
[8.0.0]: https://github.com/aeternity/aesophia/compare/v7.4.1...v8.0.0
|
||||
[7.4.1]: https://github.com/aeternity/aesophia/compare/v7.4.0...v7.4.1
|
||||
[7.4.0]: https://github.com/aeternity/aesophia/compare/v7.3.0...v7.4.0
|
||||
[7.3.0]: https://github.com/aeternity/aesophia/compare/v7.2.1...v7.3.0
|
||||
[7.2.1]: https://github.com/aeternity/aesophia/compare/v7.2.0...v7.2.1
|
||||
[7.2.0]: https://github.com/aeternity/aesophia/compare/v7.1.0...v7.2.0
|
||||
[Unreleased]: https://github.com/aeternity/aesophia/compare/v7.1.0...HEAD
|
||||
[7.1.0]: https://github.com/aeternity/aesophia/compare/v7.0.1...v7.1.0
|
||||
[7.0.1]: https://github.com/aeternity/aesophia/compare/v7.0.0...v7.0.1
|
||||
[7.0.0]: https://github.com/aeternity/aesophia/compare/v6.1.0...v7.0.0
|
||||
|
||||
@@ -53,6 +53,8 @@ The **pp_** options all print to standard output the following:
|
||||
|
||||
The option `include_child_contract_symbols` includes the symbols of child contracts functions in the generated fate code. It is turned off by default to avoid making contracts bigger on chain.
|
||||
|
||||
The option `debug_info` includes information related to debugging in the compiler output. Currently this option only includes the mapping from variables to registers.
|
||||
|
||||
#### Options to control which compiler optimizations should run:
|
||||
|
||||
By default all optimizations are turned on, to disable an optimization, it should be
|
||||
|
||||
+17
-109
@@ -84,7 +84,7 @@ the return value of the call.
|
||||
|
||||
```sophia
|
||||
contract interface VotingType =
|
||||
entrypoint vote : string => unit
|
||||
entrypoint : vote : string => unit
|
||||
|
||||
contract Voter =
|
||||
entrypoint tryVote(v : VotingType, alt : string) =
|
||||
@@ -204,7 +204,7 @@ When a `contract` or a `contract interface` implements another `contract interfa
|
||||
|
||||
#### Subtyping and variance
|
||||
|
||||
Subtyping in Sophia follows common rules that take type variance into account. As described by [Wikipedia](https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)),
|
||||
Subtyping in Sophia follows common rules that take type variance into account. As described by [Wikipedia](https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)),
|
||||
|
||||
>Variance refers to how subtyping between more complex types relates to subtyping between their components.
|
||||
|
||||
@@ -224,11 +224,11 @@ A good example of where it matters can be pictured by subtyping of function type
|
||||
```sophia
|
||||
contract interface Animal =
|
||||
entrypoint age : () => int
|
||||
|
||||
|
||||
contract Dog : Animal =
|
||||
entrypoint age() = // ...
|
||||
entrypoint woof() = "woof"
|
||||
|
||||
|
||||
contract Cat : Animal =
|
||||
entrypoint age() = // ...
|
||||
entrypoint meow() = "meow"
|
||||
@@ -295,11 +295,6 @@ of `A`.
|
||||
- When a user-defined type `t('a)` is invariant in `'a`, then `t(A)` can never be
|
||||
a subtype of `t(B)`.
|
||||
|
||||
#### Type variable limitation
|
||||
|
||||
Because of how FATE represents types as values there is a fixed upper limit (256)
|
||||
of type variables that can be used in a single type signature.
|
||||
|
||||
## Mutable state
|
||||
|
||||
Sophia does not have arbitrary mutable state, but only a limited form of state
|
||||
@@ -498,24 +493,6 @@ the file, except that error messages will refer to the original source
|
||||
locations. The language will try to include each file at most one time automatically,
|
||||
so even cyclic includes should be working without any special tinkering.
|
||||
|
||||
### Include files using relative paths
|
||||
|
||||
When including code from another file using the `include` statement, the path
|
||||
is relative to _the file that includes it_. Consider the following file tree:
|
||||
```
|
||||
c1.aes
|
||||
c3.aes
|
||||
dir1/c2.aes
|
||||
dir1/c3.aes
|
||||
```
|
||||
|
||||
If `c1.aes` contains `include "c3.aes"` it will include the top level `c3.aes`,
|
||||
while if `c2.aes` contained the same line it would as expected include
|
||||
`dir1/c3.aes`.
|
||||
|
||||
Note: Prior to v7.5.0, it would consider the include path relative to _the main
|
||||
contract file_ (or any explicitly set include path).
|
||||
|
||||
## Standard library
|
||||
|
||||
Sophia offers [standard library](sophia_stdlib.md) which exposes some
|
||||
@@ -563,7 +540,6 @@ Sophia has the following types:
|
||||
## Literals
|
||||
| Type | Constant/Literal example(s) |
|
||||
| ---------- | ------------------------------- |
|
||||
| unit | () |
|
||||
| int | `-1`, `2425`, `4598275923475723498573485768` |
|
||||
| address | `ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt` |
|
||||
| bool | `true`, `false` |
|
||||
@@ -578,7 +554,7 @@ Sophia has the following types:
|
||||
| state | `state{ owner = Call.origin, magic_key = #a298105f }` |
|
||||
| event | `EventX(0, "Hello")` |
|
||||
| hash | `#000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f` |
|
||||
| signature | `sg_MhibzTP1wWzGCTjtPFr1TiPqRJrrJqw7auvEuF5i3FdoALWqXLBDY6xxRRNUSPHK3EQTnTzF12EyspkxrSMxVHKsZeSMj`, `#000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f` |
|
||||
| signature | `#000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f` |
|
||||
| Chain.ttl | `FixedTTL(1050)`, `RelativeTTL(50)` |
|
||||
| oracle('a, 'b) | `ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5` |
|
||||
| oracle_query('a, 'b) | `oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY` |
|
||||
@@ -597,32 +573,6 @@ contract C =
|
||||
|
||||
A hole expression found in the example above will generate the error `` Found a hole of type `(int) => int` ``. This says that the compiler expects a function from `int` to `int` in place of the `???` placeholder.
|
||||
|
||||
## Constants
|
||||
|
||||
Constants in Sophia are contract-level bindings that can be used in either contracts or namespaces. The value of a constant can be a literal, another constant, or arithmetic operations applied to other constants. Lists, tuples, maps, and records can also be used to define a constant as long as their elements are also constants.
|
||||
|
||||
The following visibility rules apply to constants:
|
||||
* Constants defined inside a contract are private in that contract. Thus, cannot be accessed through instances of their defining contract.
|
||||
* Constants defined inside a namespace are public. Thus, can be used in other contracts or namespaces.
|
||||
* Constants cannot be defined inside a contract interface.
|
||||
|
||||
When a constant is shadowed, it can be accessed using its qualified name:
|
||||
|
||||
```
|
||||
contract C =
|
||||
let c = 1
|
||||
entrypoint f() =
|
||||
let c = 2
|
||||
c + C.c // the result is 3
|
||||
```
|
||||
|
||||
The name of the constant must be an id; therefore, no pattern matching is allowed when defining a constant:
|
||||
|
||||
```
|
||||
contract C
|
||||
let x::y::_ = [1,2,3] // this will result in an error
|
||||
```
|
||||
|
||||
## Arithmetic
|
||||
|
||||
Sophia integers (`int`) are represented by arbitrary-sized signed words and support the following
|
||||
@@ -634,28 +584,14 @@ arithmetic operations:
|
||||
- remainder (`x mod y`), satisfying `y * (x / y) + x mod y == x` for non-zero `y`
|
||||
- exponentiation (`x ^ y`)
|
||||
|
||||
All operations are *safe* with respect to overflow and underflow.
|
||||
All operations are *safe* with respect to overflow and underflow.
|
||||
The division and modulo operations throw an arithmetic error if the
|
||||
right-hand operand is zero.
|
||||
|
||||
Sophia arbitrary-sized integers (FATE) also supports the following bitwise operations:
|
||||
- bitwise and (`x band y`)
|
||||
- bitwise or (`x bor y`)
|
||||
- bitwise xor (`x bxor y`)
|
||||
- bitwise not (`bnot x`)
|
||||
- arithmetic bitshift left (`x << n`)
|
||||
- arithmetic bitshift right (`x >> n`)
|
||||
|
||||
Note: Arithmetic bitshift treats the number as a signed integer (in 2s
|
||||
complement), and "retains" the topmost bit. I.e. shifting in zeros if the
|
||||
topmost bit was 0, and ones if it was one.
|
||||
|
||||
## Bit fields
|
||||
|
||||
Originally Sophia integers did not support bit arithmetic. Instead we used a
|
||||
separate type `bits` (see the standard library
|
||||
[documentation](sophia_stdlib.md#bits)) - it is still provided as an
|
||||
alternative to bit arithmetic.
|
||||
Sophia integers do not support bit arithmetic. Instead there is a separate
|
||||
type `bits`. See the standard library [documentation](sophia_stdlib.md#bits).
|
||||
|
||||
A bit field can be of arbitrary size (but it is still represented by the
|
||||
corresponding integer, so setting very high bits can be expensive).
|
||||
@@ -960,18 +896,16 @@ functions are provided.
|
||||
|
||||
## AENS interface
|
||||
|
||||
Contracts can interact with the [æternity naming
|
||||
system](https://github.com/aeternity/protocol/blob/master/AENS.md). For this
|
||||
purpose the [AENS](sophia_stdlib.md#aens) and later the
|
||||
[AENSv2](sophia_stdlib.md#aensv2) library was exposed.
|
||||
Contracts can interact with the
|
||||
[æternity naming system](https://github.com/aeternity/protocol/blob/master/AENS.md).
|
||||
For this purpose the [AENS](sophia_stdlib.md#aens) library was exposed.
|
||||
|
||||
### Example
|
||||
|
||||
In this example we assume that the name `name` already exists, and is owned by
|
||||
an account with address `addr`. In order to allow a contract `ct` to handle
|
||||
`name` the account holder needs to create a [delegation
|
||||
signature](#delegation-signature) `sig` from the name owner address `addr`, the
|
||||
name hash and the contract address.
|
||||
`name` the account holder needs to create a
|
||||
[signature](#delegation-signature) `sig` of `addr | name.hash | ct.address`.
|
||||
|
||||
Armed with this information we can for example write a function that extends
|
||||
the name if it expires within 1000 blocks:
|
||||
@@ -1113,34 +1047,8 @@ however is in the gas consumption — while `abort` returns unused gas, a call t
|
||||
|
||||
## Delegation signature
|
||||
|
||||
Some chain operations (`Oracle.<operation>` and `AENSv2.<operation>`) have an
|
||||
Some chain operations (`Oracle.<operation>` and `AENS.<operation>`) have an
|
||||
optional delegation signature. This is typically used when a user/accounts
|
||||
would like to allow a contract to act on it's behalf.
|
||||
|
||||
### From Ceres
|
||||
|
||||
From the Ceres protocol version the delegation signatures have more structure,
|
||||
including a unique tag, `network_id` and identifiers; there are five different
|
||||
delegation signatures:
|
||||
|
||||
- AENS wildcard - the user signs: `owner account + contract`
|
||||
- `AENS_PRECLAIM` - the user signs: `owner account + contract`
|
||||
- `AENS_CLAIM, AENS_UPDATE, AENS_TRANSFER, AENS_REVOKE` - the user signs: `owner account + name hash + contract`
|
||||
- `ORACLE_REGISTER, ORACLE_EXTEND` - the user signs: `owner account + contract`
|
||||
- `ORACLE_RESPOND` - the user signs: `query id + contract`
|
||||
|
||||
See [Serialized signature
|
||||
data](https://github.com/aeternity/protocol/blob/master/contracts/fate.md#from-ceres-serialized-signature-data)
|
||||
for the exact structure used.
|
||||
|
||||
### Before ceres
|
||||
|
||||
The exact data to be signed varies for the different operations, but in all
|
||||
cases you should prepend the signature data with the `network_id` (`ae_mainnet`
|
||||
for the æternity mainnet, etc.).
|
||||
|
||||
There are four different delegation signatures:
|
||||
- `AENS_PRECLAIM` - the user signs: owner `network_id + account + contract`
|
||||
- `AENS_CLAIM, AENS_UPDATE, AENS_TRANSFER, AENS_REVOKE` - the user signs: `network_id + owner account + name hash + contract`
|
||||
- `ORACLE_REGISTER, ORACLE_EXTEND` - the user signs: `network_id + owner account + contract`
|
||||
- `ORACLE_RESPOND` - the user signs: `network_id + query id + contract`
|
||||
would like to allow a contract to act on it's behalf. The exact data to be
|
||||
signed varies for the different operations, but in all cases you should prepend
|
||||
the signature data with the `network_id` (`ae_mainnet` for the æternity mainnet, etc.).
|
||||
|
||||
+164
-253
@@ -14,7 +14,6 @@ The out-of-the-box namespaces are:
|
||||
|
||||
- [Address](#address)
|
||||
- [AENS](#aens)
|
||||
- [AENSv2](#aensv2)
|
||||
- [Auth](#auth)
|
||||
- [Bits](#bits)
|
||||
- [Bytes](#bytes)
|
||||
@@ -32,7 +31,6 @@ The following ones need to be included as regular files with `.aes` suffix, for
|
||||
include "List.aes"
|
||||
```
|
||||
|
||||
- [AENSCompat](#aenscompat)
|
||||
- [Bitwise](#bitwise)
|
||||
- [BLS12_381](#bls12_381)
|
||||
- [Func](#func)
|
||||
@@ -57,12 +55,6 @@ Address.to_str(a : address) : string
|
||||
|
||||
Base58 encoded string
|
||||
|
||||
#### to_bytes
|
||||
```
|
||||
Address.to_bytes(a : address) : bytes(32)
|
||||
```
|
||||
|
||||
The binary representation of the address.
|
||||
|
||||
#### is_contract
|
||||
```
|
||||
@@ -98,10 +90,13 @@ Cast address to contract type C (where `C` is a contract)
|
||||
|
||||
### AENS
|
||||
|
||||
The old AENS namespace, kept in the compiler to be able to interact with
|
||||
contracts from before Ceres, compiled using aesophia compiler version 7.x and
|
||||
earlier. Used in [AENSCompat](#aenscompat) when converting between old and new
|
||||
pointers.
|
||||
The following functionality is available for interacting with the æternity
|
||||
naming system (AENS).
|
||||
If `owner` is equal to `Contract.address` the signature `signature` is
|
||||
ignored, and can be left out since it is a named argument. Otherwise we need
|
||||
a signature to prove that we are allowed to do AENS operations on behalf of
|
||||
`owner`. The [signature is tied to a network id](https://github.com/aeternity/protocol/blob/iris/consensus/consensus.md#transaction-signature),
|
||||
i.e. the signature material should be prefixed by the network id.
|
||||
|
||||
#### Types
|
||||
|
||||
@@ -118,41 +113,12 @@ datatype pointee = AccountPt(address) | OraclePt(address)
|
||||
| ContractPt(address) | ChannelPt(address)
|
||||
```
|
||||
|
||||
### AENSv2
|
||||
|
||||
Note: introduced in v8.0
|
||||
|
||||
The following functionality is available for interacting with the æternity
|
||||
naming system (AENS). If `owner` is equal to `Contract.address` the signature
|
||||
`signature` is ignored, and can be left out since it is a named argument.
|
||||
Otherwise we need a signature to prove that we are allowed to do AENS
|
||||
operations on behalf of `owner`. The [signature is tied to a network
|
||||
id](https://github.com/aeternity/protocol/blob/iris/consensus/consensus.md#transaction-signature),
|
||||
i.e. the signature material should be prefixed by the network id.
|
||||
|
||||
#### Types
|
||||
|
||||
##### name
|
||||
```
|
||||
datatype name = Name(address, Chain.ttl, map(string, AENSv2.pointee))
|
||||
```
|
||||
|
||||
|
||||
##### pointee
|
||||
|
||||
```
|
||||
datatype pointee = AccountPt(address) | OraclePt(address)
|
||||
| ContractPt(address) | ChannelPt(address) | DataPt(bytes())
|
||||
```
|
||||
|
||||
Note: on-chain there is a maximum length enforced for `DataPt`, it is 1024 bytes.
|
||||
Sophia itself does _not_ check for this.
|
||||
|
||||
#### Functions
|
||||
|
||||
##### resolve
|
||||
```
|
||||
AENSv2.resolve(name : string, key : string) : option('a)
|
||||
AENS.resolve(name : string, key : string) : option('a)
|
||||
```
|
||||
|
||||
Name resolution. Here `name` should be a registered name and `key` one of the attributes
|
||||
@@ -163,107 +129,74 @@ type checked against this type at run time.
|
||||
|
||||
##### lookup
|
||||
```
|
||||
AENSv2.lookup(name : string) : option(AENSv2.name)
|
||||
AENS.lookup(name : string) : option(AENS.name)
|
||||
```
|
||||
|
||||
If `name` is an active name `AENSv2.lookup` returns a name object.
|
||||
If `name` is an active name `AENS.lookup` returns a name object.
|
||||
The three arguments to `Name` are `owner`, `expiry` and a map of the
|
||||
`pointees` for the name. Note: the expiry of the name is always a fixed TTL.
|
||||
For example:
|
||||
```
|
||||
let Some(AENSv2.Name(owner, FixedTTL(expiry), ptrs)) = AENSv2.lookup("example.chain")
|
||||
let Some(Name(owner, FixedTTL(expiry), ptrs)) = AENS.lookup("example.chain")
|
||||
```
|
||||
|
||||
Note: Changed to produce `AENSv2.name` in v8.0 (Ceres protocol upgrade).
|
||||
|
||||
##### preclaim
|
||||
```
|
||||
AENSv2.preclaim(owner : address, commitment_hash : hash, <signature : signature>) : unit
|
||||
AENS.preclaim(owner : address, commitment_hash : hash, <signature : signature>) : unit
|
||||
```
|
||||
|
||||
The [signature](./sophia_features.md#delegation-signature) should be a
|
||||
serialized structure containing `network id`, `owner address`, and
|
||||
`Contract.address`.
|
||||
|
||||
From Ceres (i.e. FATE VM version 3) the
|
||||
[signature](./sophia_features.md#delegation-signature) can also be generic
|
||||
(allowing _all_, existing and future, names to be delegated with one
|
||||
signature), i.e. containing `network id`, `owner address`, `Contract.address`.
|
||||
The [signature](./sophia_features.md#delegation-signature) should be over
|
||||
`network id` + `owner address` + `Contract.address` (concatenated as byte arrays).
|
||||
|
||||
|
||||
##### claim
|
||||
```
|
||||
AENSv2.claim(owner : address, name : string, salt : int, name_fee : int, <signature : signature>) : unit
|
||||
AENS.claim(owner : address, name : string, salt : int, name_fee : int, <signature : signature>) : unit
|
||||
```
|
||||
|
||||
The [signature](./sophia_features.md#delegation-signature) should be a
|
||||
serialized structure containing `network id`, `owner address`, and
|
||||
`Contract.address`. Using the private key of `owner address` for signing.
|
||||
|
||||
From Ceres (i.e. FATE VM version 3) the
|
||||
[signature](./sophia_features.md#delegation-signature) can also be generic
|
||||
(allowing _all_, existing and future, names to be delegated with one
|
||||
signature), i.e. containing `network id`, `owner address`, `name_hash`, and
|
||||
`Contract.address`.
|
||||
The [signature](./sophia_features.md#delegation-signature) should be over
|
||||
`network id` + `owner address` + `name_hash` + `Contract.address`
|
||||
(concatenated as byte arrays)
|
||||
using the private key of the `owner` account for signing.
|
||||
|
||||
|
||||
##### transfer
|
||||
```
|
||||
AENSv2.transfer(owner : address, new_owner : address, name : string, <signature : signature>) : unit
|
||||
AENS.transfer(owner : address, new_owner : address, name : string, <signature : signature>) : unit
|
||||
```
|
||||
|
||||
Transfers name to the new owner.
|
||||
|
||||
The [signature](./sophia_features.md#delegation-signature) should be a
|
||||
serialized structure containing `network id`, `owner address`, and
|
||||
`Contract.address`. Using the private key of `owner address` for signing.
|
||||
|
||||
From Ceres (i.e. FATE VM version 3) the
|
||||
[signature](./sophia_features.md#delegation-signature) can also be generic
|
||||
(allowing _all_, existing and future, names to be delegated with one
|
||||
signature), i.e. containing `network id`, `owner address`, `name_hash`, and
|
||||
`Contract.address`.
|
||||
The [signature](./sophia_features.md#delegation-signature) should be over
|
||||
`network id` + `owner address` + `name_hash` + `Contract.address`
|
||||
(concatenated as byte arrays)
|
||||
using the private key of the `owner` account for signing.
|
||||
|
||||
|
||||
##### revoke
|
||||
```
|
||||
AENSv2.revoke(owner : address, name : string, <signature : signature>) : unit
|
||||
AENS.revoke(owner : address, name : string, <signature : signature>) : unit
|
||||
```
|
||||
|
||||
Revokes the name to extend the ownership time.
|
||||
|
||||
The [signature](./sophia_features.md#delegation-signature) should be a
|
||||
serialized structure containing `network id`, `owner address`, and
|
||||
`Contract.address`. Using the private key of `owner address` for signing.
|
||||
|
||||
From Ceres (i.e. FATE VM version 3) the
|
||||
[signature](./sophia_features.md#delegation-signature) can also be generic
|
||||
(allowing _all_, existing and future, names to be delegated with one
|
||||
signature), i.e. containing `network id`, `owner address`, `name_hash`, and
|
||||
`Contract.address`.
|
||||
The [signature](./sophia_features.md#delegation-signature) should be over
|
||||
`network id` + `owner address` + `name_hash` + `Contract.address`
|
||||
(concatenated as byte arrays)
|
||||
using the private key of the `owner` account for signing.
|
||||
|
||||
|
||||
##### update
|
||||
```
|
||||
AENSv2.update(owner : address, name : string, expiry : option(Chain.ttl), client_ttl : option(int),
|
||||
new_ptrs : option(map(string, AENSv2.pointee)), <signature : signature>) : unit
|
||||
AENS.update(owner : address, name : string, expiry : option(Chain.ttl), client_ttl : option(int),
|
||||
new_ptrs : map(string, AENS.pointee), <signature : signature>) : unit
|
||||
```
|
||||
|
||||
Updates the name. If the optional parameters are set to `None` that parameter
|
||||
will not be updated, for example if `None` is passed as `expiry` the expiry
|
||||
block of the name is not changed.
|
||||
|
||||
Note: Changed to consume `AENSv2.pointee` in v8.0 (Ceres protocol upgrade).
|
||||
|
||||
The [signature](./sophia_features.md#delegation-signature) should be a
|
||||
serialized structure containing `network id`, `owner address`, and
|
||||
`Contract.address`. Using the private key of `owner address` for signing.
|
||||
|
||||
From Ceres (i.e. FATE VM version 3) the
|
||||
[signature](./sophia_features.md#delegation-signature) can also be generic
|
||||
(allowing _all_, existing and future, names to be delegated with one
|
||||
signature), i.e. containing `network id`, `owner address`, `name_hash`, and
|
||||
`Contract.address`.
|
||||
|
||||
### Auth
|
||||
|
||||
@@ -303,10 +236,7 @@ namespace Chain =
|
||||
Auth.tx_hash : option(hash)
|
||||
```
|
||||
|
||||
Gets the transaction hash during authentication. Note: `Auth.tx_hash`
|
||||
computation differs between protocol versions (changed in Ceres!), see
|
||||
[aeserialisation](https://github.com/aeternity/protocol/blob/master/serializations.md)
|
||||
specification for details.
|
||||
Gets the transaction hash during authentication.
|
||||
|
||||
|
||||
### Bits
|
||||
@@ -385,7 +315,7 @@ Each bit is true if and only if it was 1 in `a` and 0 in `b`
|
||||
|
||||
### Bytes
|
||||
|
||||
#### to\_int
|
||||
#### to_int
|
||||
```
|
||||
Bytes.to_int(b : bytes(n)) : int
|
||||
```
|
||||
@@ -393,7 +323,7 @@ Bytes.to_int(b : bytes(n)) : int
|
||||
Interprets the byte array as a big endian integer
|
||||
|
||||
|
||||
#### to\_str
|
||||
#### to_str
|
||||
```
|
||||
Bytes.to_str(b : bytes(n)) : string
|
||||
```
|
||||
@@ -406,8 +336,7 @@ Returns the hexadecimal representation of the byte array
|
||||
Bytes.concat : (a : bytes(m), b : bytes(n)) => bytes(m + n)
|
||||
```
|
||||
|
||||
Concatenates two byte arrays. If `m` and `n` are known at compile time, the
|
||||
result can be used as a fixed size byte array, otherwise it has type `bytes()`.
|
||||
Concatenates two byte arrays
|
||||
|
||||
|
||||
#### split
|
||||
@@ -417,38 +346,6 @@ Bytes.split(a : bytes(m + n)) : bytes(m) * bytes(n)
|
||||
|
||||
Splits a byte array at given index
|
||||
|
||||
#### split\_any
|
||||
```
|
||||
Bytes.split_any(a : bytes(), at : int) : option(bytes() * bytes(n))
|
||||
```
|
||||
|
||||
Splits an arbitrary size byte array at index `at`. If `at` is positive split
|
||||
from the beginning of the array, if `at` is negative, split `abs(at)` from the
|
||||
_end_ of the array. If the array is shorter than `abs(at)` then `None` is
|
||||
returned.
|
||||
|
||||
#### to\_fixed\_size
|
||||
```
|
||||
Bytes.to_fixed_size(a : bytes()) : option(bytes(n))
|
||||
```
|
||||
|
||||
Converts an arbitrary size byte array to a fix size byte array. If `a` is
|
||||
not `n` bytes, `None` is returned.
|
||||
|
||||
#### to\_any\_size
|
||||
```
|
||||
Bytes.to_any_size(a : bytes(n)) : bytes()
|
||||
```
|
||||
|
||||
Converts a fixed size byte array to an arbitrary size byte array. This is a
|
||||
no-op at run-time, and only used during type checking.
|
||||
|
||||
#### size
|
||||
```
|
||||
Bytes.size(a : bytes()) : int
|
||||
```
|
||||
|
||||
Computes the lenght/size of a byte array.
|
||||
|
||||
### Call
|
||||
|
||||
@@ -484,12 +381,6 @@ Call.gas_price : int
|
||||
|
||||
The gas price of the current call.
|
||||
|
||||
#### mulmod
|
||||
```
|
||||
Int.mulmod : (a : int, b : int, q : int) : int
|
||||
```
|
||||
|
||||
Combined multiplication and modulus, returns `(a * b) mod q`.
|
||||
|
||||
#### fee
|
||||
```
|
||||
@@ -578,6 +469,39 @@ Chain.block_height : int"
|
||||
|
||||
The height of the current block (i.e. the block in which the current call will be included).
|
||||
|
||||
|
||||
##### coinbase
|
||||
```
|
||||
Chain.coinbase : address
|
||||
```
|
||||
|
||||
The address of the account that mined the current block.
|
||||
|
||||
|
||||
##### timestamp
|
||||
```
|
||||
Chain.timestamp : int
|
||||
```
|
||||
|
||||
The timestamp of the current block (unix time, milliseconds).
|
||||
|
||||
|
||||
##### difficulty
|
||||
```
|
||||
Chain.difficulty : int
|
||||
```
|
||||
|
||||
The difficulty of the current block.
|
||||
|
||||
|
||||
##### gas
|
||||
```
|
||||
Chain.gas_limit : int
|
||||
```
|
||||
|
||||
The gas limit of the current block.
|
||||
|
||||
|
||||
##### bytecode_hash
|
||||
```
|
||||
Chain.bytecode_hash : 'c => option(hash)
|
||||
@@ -614,6 +538,7 @@ charging the calling contract. Note that this won't be visible in `Call.value`
|
||||
in the `init` call of the new contract. It will be included in
|
||||
`Contract.balance`, however.
|
||||
|
||||
|
||||
The type `'c` must be instantiated with a contract.
|
||||
|
||||
|
||||
@@ -640,7 +565,6 @@ main contract Market =
|
||||
The typechecker must be certain about the created contract's type, so it is
|
||||
worth writing it explicitly as shown in the example.
|
||||
|
||||
|
||||
##### clone
|
||||
```
|
||||
Chain.clone : ( ref : 'c, gas : int, value : int, protected : bool, ...
|
||||
@@ -679,8 +603,8 @@ Example usage:
|
||||
```
|
||||
payable contract interface Auction =
|
||||
entrypoint init : (int, string) => void
|
||||
stateful payable entrypoint buy : (int) => unit
|
||||
stateful entrypoint sell : (int) => unit
|
||||
stateful payable entrypoint buy : (int) => ()
|
||||
stateful entrypoint sell : (int) => ()
|
||||
|
||||
main contract Market =
|
||||
type state = list(Auction)
|
||||
@@ -699,71 +623,11 @@ implementation of the `init` function does not actually return `state`, but
|
||||
calls `put` instead. Moreover, FATE prevents even handcrafted calls to `init`.
|
||||
|
||||
|
||||
##### coinbase
|
||||
```
|
||||
Chain.coinbase : address
|
||||
```
|
||||
|
||||
The address of the account that mined the current block.
|
||||
|
||||
|
||||
##### difficulty
|
||||
```
|
||||
Chain.difficulty : int
|
||||
```
|
||||
|
||||
The difficulty of the current block.
|
||||
|
||||
|
||||
##### event
|
||||
```
|
||||
Chain.event(e : event) : unit
|
||||
```
|
||||
|
||||
Emits the event. To use this function one needs to define the `event` type as a
|
||||
`datatype` in the contract.
|
||||
|
||||
|
||||
##### gas\_limit
|
||||
```
|
||||
Chain.gas_limit : int
|
||||
```
|
||||
|
||||
The gas limit of the current block.
|
||||
|
||||
|
||||
##### network\_id
|
||||
```
|
||||
Chain.network\_id : string
|
||||
```
|
||||
|
||||
The network id of the chain.
|
||||
|
||||
|
||||
#### poseidon
|
||||
```
|
||||
Crypto.poseidon(x1 : int, x2 : int) : int
|
||||
```
|
||||
|
||||
Hash two integers (in the scalar field of BLS12-381) to another integer (in the scalar
|
||||
field of BLS12-281). This is a ZK/SNARK-friendly hash function.
|
||||
|
||||
|
||||
##### spend
|
||||
```
|
||||
Chain.spend(to : address, amount : int) : unit
|
||||
```
|
||||
|
||||
Spend `amount` tokens to `to`. Will fail (and abort the contract) if contract
|
||||
doesn't have `amount` tokens to transfer, or, if `to` is not `payable`.
|
||||
|
||||
|
||||
##### timestamp
|
||||
```
|
||||
Chain.timestamp : int
|
||||
```
|
||||
|
||||
The timestamp of the current block (unix time, milliseconds).
|
||||
Emits the event. To use this function one needs to define the `event` type as a `datatype` in the contract.
|
||||
|
||||
|
||||
### Char
|
||||
@@ -841,14 +705,11 @@ Hash any object to blake2b
|
||||
|
||||
#### verify_sig
|
||||
```
|
||||
Crypto.verify_sig(msg : bytes(), pubkey : address, sig : signature) : bool
|
||||
Crypto.verify_sig(msg : hash, pubkey : address, sig : signature) : bool
|
||||
```
|
||||
|
||||
Checks if the signature of `msg` was made using private key corresponding to
|
||||
the `pubkey`.
|
||||
|
||||
Note: before v8 of the compiler, `msg` had type `hash` (i.e. `bytes(32)`).
|
||||
|
||||
the `pubkey`
|
||||
|
||||
#### ecverify_secp256k1
|
||||
```
|
||||
@@ -881,21 +742,12 @@ Verifies a standard 64-byte ECDSA signature (`R || S`).
|
||||
|
||||
### Int
|
||||
|
||||
#### to\_str
|
||||
#### to_str
|
||||
```
|
||||
Int.to_str(n : int) : string
|
||||
Int.to_str : int => string
|
||||
```
|
||||
|
||||
Casts the integer to a string (in decimal representation).
|
||||
|
||||
#### to\_bytes
|
||||
```
|
||||
Int.to_bytes(n : int, size : int) : bytes()
|
||||
```
|
||||
|
||||
Casts the integer to a byte array with `size` bytes (big endian, truncating if
|
||||
necessary not preserving signedness). I.e. if you try to squeeze `-129` into a
|
||||
single byte that will be indistinguishable from `127`.
|
||||
Casts integer to string using decimal representation
|
||||
|
||||
|
||||
### Map
|
||||
@@ -954,11 +806,11 @@ Oracle.register(<signature : bytes(64)>, acct : address, qfee : int, ttl : Chain
|
||||
Registers new oracle answering questions of type `'a` with answers of type `'b`.
|
||||
|
||||
* The `acct` is the address of the oracle to register (can be the same as the contract).
|
||||
* The [signature](./sophia_features.md#delegation-signature) should be a
|
||||
serialized structure containing `network id`, `account address`, and
|
||||
`contract address`. Using the private key of `account address` for signing.
|
||||
Proving you have the private key of the oracle to be. If the address is the same
|
||||
as the contract `sign` is ignored and can be left out entirely.
|
||||
* `signature` is a signature proving that the contract is allowed to register the account -
|
||||
the `network id` + `account address` + `contract address` (concatenated as byte arrays) is
|
||||
[signed](./sophia_features.md#delegation-signature) with the
|
||||
private key of the account, proving you have the private key of the oracle to be. If the
|
||||
address is the same as the contract `sign` is ignored and can be left out entirely.
|
||||
* The `qfee` is the minimum query fee to be paid by a user when asking a question of the oracle.
|
||||
* The `ttl` is the Time To Live for the oracle in key blocks, either relative to the current
|
||||
key block height (`RelativeTTL(delta)`) or a fixed key block height (`FixedTTL(height)`).
|
||||
@@ -972,7 +824,7 @@ Examples:
|
||||
```
|
||||
|
||||
|
||||
#### get\_question
|
||||
#### get_question
|
||||
```
|
||||
Oracle.get_question(o : oracle('a, 'b), q : oracle_query('a, 'b)) : 'a
|
||||
```
|
||||
@@ -985,11 +837,12 @@ Checks what was the question of query `q` on oracle `o`
|
||||
Oracle.respond(<signature : bytes(64)>, o : oracle('a, 'b), q : oracle_query('a, 'b), 'b) : unit
|
||||
```
|
||||
|
||||
Responds to the question `q` on `o`. Unless the contract address is the same
|
||||
as the oracle address the `signature` (which is an optional, named argument)
|
||||
Responds to the question `q` on `o`.
|
||||
Unless the contract address is the same as the oracle address the `signature`
|
||||
(which is an optional, named argument)
|
||||
needs to be provided. Proving that we have the private key of the oracle by
|
||||
[signing](./sophia_features.md#delegation-signature) should be a serialized
|
||||
structure containing `network id`, `oracle query id`, and `contract address`.
|
||||
[signing](./sophia_features.md#delegation-signature)
|
||||
the `network id` + `oracle query id` + `contract address`
|
||||
|
||||
|
||||
#### extend
|
||||
@@ -1002,7 +855,7 @@ Extends TTL of an oracle.
|
||||
* `o` is the oracle being extended
|
||||
* `ttl` must be `RelativeTTL`. The time to live of `o` will be extended by this value.
|
||||
|
||||
#### query\_fee
|
||||
#### query_fee
|
||||
```
|
||||
Oracle.query_fee(o : oracle('a, 'b)) : int
|
||||
```
|
||||
@@ -1023,7 +876,7 @@ Asks the oracle a question.
|
||||
The call fails if the oracle could expire before an answer.
|
||||
|
||||
|
||||
#### get\_answer
|
||||
#### get_answer
|
||||
```
|
||||
Oracle.get_answer(o : oracle('a, 'b), q : oracle_query('a, 'b)) : option('b)
|
||||
```
|
||||
@@ -1061,21 +914,88 @@ It returns `true` iff the oracle query exist and has the expected type.
|
||||
These need to be explicitly included (with `.aes` suffix)
|
||||
|
||||
|
||||
### AENSCompat
|
||||
### Bitwise
|
||||
|
||||
#### pointee\_to\_V2
|
||||
Bitwise operations on arbitrary precision integers.
|
||||
|
||||
#### bsr
|
||||
```
|
||||
AENSCompat.pointee_to_V2(p : AENS.pointee) : AENSv2.pointee
|
||||
Bitwise.bsr(n : int, x : int) : int
|
||||
```
|
||||
|
||||
Translate old pointee format to new, this is always possible.
|
||||
Logical bit shift `x` right `n` positions.
|
||||
|
||||
#### pointee\_from\_V2
|
||||
|
||||
#### bsl
|
||||
```
|
||||
AENSCompat.pointee_from_V2(p2 : AENSv2.pointee) : option(AENS.pointee)
|
||||
Bitwise.bsl(n : int, x : int) : int
|
||||
```
|
||||
|
||||
Translate new pointee format to old, `DataPt` can't be translated, so `None` is returned in this case.
|
||||
Logical bit shift `x` left `n` positions.
|
||||
|
||||
|
||||
#### bsli
|
||||
```
|
||||
Bitwise.bsli(n : int, x : int, lim : int) : int
|
||||
```
|
||||
|
||||
Logical bit shift `x` left `n` positions, limit to `lim` bits.
|
||||
|
||||
|
||||
#### band
|
||||
```
|
||||
Bitwise.band(x : int, y : int) : int
|
||||
```
|
||||
|
||||
Bitwise `and` of `x` and `y`.
|
||||
|
||||
|
||||
#### bor
|
||||
```
|
||||
Bitwise.bor(x : int, y : int) : int
|
||||
```
|
||||
|
||||
Bitwise `or` of `x` and `y`.
|
||||
|
||||
|
||||
#### bxor
|
||||
```
|
||||
Bitwise.bxor(x : int, y : int) : int
|
||||
```
|
||||
|
||||
Bitwise `xor` of `x` and `y`.
|
||||
|
||||
|
||||
#### bnot
|
||||
```
|
||||
Bitwise.bnot(x : int) : int
|
||||
```
|
||||
|
||||
Bitwise `not` of `x`. Defined and implemented as `bnot(x) = bxor(x, -1)`.
|
||||
|
||||
|
||||
#### uband
|
||||
```
|
||||
Bitwise.uband(x : int, y : int) : int
|
||||
```
|
||||
|
||||
Bitwise `and` of _non-negative_ numbers `x` and `y`.
|
||||
|
||||
|
||||
#### ubor
|
||||
```
|
||||
Bitwise.ubor(x : int, y : int) : int
|
||||
```
|
||||
|
||||
Bitwise `or` of _non-negative_ `x` and `y`.
|
||||
|
||||
|
||||
#### ubxor
|
||||
```
|
||||
Bitwise.ubxor(x : int, y : int) : int
|
||||
```
|
||||
|
||||
Bitwise `xor` of _non-negative_ `x` and `y`.
|
||||
|
||||
|
||||
### BLS12\_381
|
||||
@@ -2471,15 +2391,6 @@ to_int(s : string) : option(int)
|
||||
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.
|
||||
|
||||
#### to\_bytes
|
||||
```
|
||||
to_bytes(s : string) : bytes()
|
||||
```
|
||||
|
||||
Converts string into byte array. String is UTF-8 encoded. I.e.
|
||||
`String.length(s)` is not guaranteed to be equal to
|
||||
`Bytes.size(String.to_bytes(s))`.
|
||||
|
||||
#### sha3
|
||||
```
|
||||
sha3(s : string) : hash
|
||||
|
||||
+7
-15
@@ -30,7 +30,6 @@ interface main using as for hiding
|
||||
- `ContractAddress` base58-encoded 32 byte contract address with `ct_` prefix
|
||||
- `OracleAddress` base58-encoded 32 byte oracle address with `ok_` prefix
|
||||
- `OracleQueryId` base58-encoded 32 byte oracle query id with `oq_` prefix
|
||||
- `Signature` base58-encoded 64 byte cryptographic signature with `sg_` prefix
|
||||
|
||||
Valid string escape codes are
|
||||
|
||||
@@ -105,7 +104,6 @@ Implement ::= ':' Sep1(Con, ',')
|
||||
Decl ::= 'type' Id ['(' TVar* ')'] '=' TypeAlias
|
||||
| 'record' Id ['(' TVar* ')'] '=' RecordType
|
||||
| 'datatype' Id ['(' TVar* ')'] '=' DataType
|
||||
| 'let' Id [':' Type] '=' Expr
|
||||
| (EModifier* 'entrypoint' | FModifier* 'function') Block(FunDecl)
|
||||
| Using
|
||||
|
||||
@@ -240,7 +238,6 @@ Expr ::= '(' LamArgs ')' '=>' Block(Stmt) // Anonymous function (x) => x +
|
||||
| Int | Bytes | String | Char // Literals 123, 0xff, #00abc123, "foo", '%'
|
||||
| AccountAddress | ContractAddress // Chain identifiers
|
||||
| OracleAddress | OracleQueryId // Chain identifiers
|
||||
| Signature // Signature
|
||||
| '???' // Hole expression 1 + ???
|
||||
|
||||
Generator ::= Pattern '<-' Expr // Generator
|
||||
@@ -258,8 +255,8 @@ Path ::= Id // Record field
|
||||
|
||||
BinOp ::= '||' | '&&' | '<' | '>' | '=<' | '>=' | '==' | '!='
|
||||
| '::' | '++' | '+' | '-' | '*' | '/' | 'mod' | '^'
|
||||
| 'band' | 'bor' | 'bxor' | '<<' | '>>' | '|>'
|
||||
UnOp ::= '-' | '!' | 'bnot'
|
||||
| '|>'
|
||||
UnOp ::= '-' | '!'
|
||||
```
|
||||
|
||||
## Operators types
|
||||
@@ -267,11 +264,10 @@ UnOp ::= '-' | '!' | 'bnot'
|
||||
| Operators | Type
|
||||
| --- | ---
|
||||
| `-` `+` `*` `/` `mod` `^` | arithmetic operators
|
||||
| `!` `&&` `\|\|` | logical operators
|
||||
| `band` `bor` `bxor` `bnot` `<<` `>>` | bitwise operators
|
||||
| `!` `&&` `||` | logical operators
|
||||
| `==` `!=` `<` `>` `=<` `>=` | comparison operators
|
||||
| `::` `++` | list operators
|
||||
| `\|>` | functional operators
|
||||
| `|>` | functional operators
|
||||
|
||||
## Operator precedence
|
||||
|
||||
@@ -279,17 +275,13 @@ In order of highest to lowest precedence.
|
||||
|
||||
| Operators | Associativity
|
||||
| --- | ---
|
||||
| `!` `bnot`| right
|
||||
| `!` | right
|
||||
| `^` | left
|
||||
| `*` `/` `mod` | left
|
||||
| `-` (unary) | right
|
||||
| `+` `-` | left
|
||||
| `<<` `>>` | left
|
||||
| `::` `++` | right
|
||||
| `<` `>` `=<` `>=` `==` `!=` | none
|
||||
| `band` | left
|
||||
| `bxor` | left
|
||||
| `bor` | left
|
||||
| `&&` | right
|
||||
| `\|\|` | right
|
||||
| `\|>` | left
|
||||
| `||` | right
|
||||
| `|>` | left
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
namespace AENSCompat =
|
||||
// Translate old format to new format - always possible
|
||||
function pointee_to_V2(p : AENS.pointee) : AENSv2.pointee =
|
||||
switch(p)
|
||||
AENS.AccountPt(a) => AENSv2.AccountPt(a)
|
||||
AENS.OraclePt(a) => AENSv2.OraclePt(a)
|
||||
AENS.ContractPt(a) => AENSv2.ContractPt(a)
|
||||
AENS.ChannelPt(a) => AENSv2.ChannelPt(a)
|
||||
|
||||
// Translate new format to old format - option type!
|
||||
function pointee_from_V2(p2 : AENSv2.pointee) : option(AENS.pointee) =
|
||||
switch(p2)
|
||||
AENSv2.AccountPt(a) => Some(AENS.AccountPt(a))
|
||||
AENSv2.OraclePt(a) => Some(AENS.OraclePt(a))
|
||||
AENSv2.ContractPt(a) => Some(AENS.ContractPt(a))
|
||||
AENSv2.ChannelPt(a) => Some(AENS.ChannelPt(a))
|
||||
AENSv2.DataPt(_) => None
|
||||
@@ -0,0 +1,126 @@
|
||||
@compiler >= 4.3
|
||||
|
||||
namespace Bitwise =
|
||||
|
||||
// bit shift 'x' right 'n' postions
|
||||
function bsr(n : int, x : int) : int =
|
||||
let step = 2^n
|
||||
let res = x / step
|
||||
if (x >= 0 || x mod step == 0)
|
||||
res
|
||||
else
|
||||
res - 1
|
||||
|
||||
// bit shift 'x' left 'n' positions
|
||||
function bsl(n : int, x : int) : int =
|
||||
x * 2^n
|
||||
|
||||
// bit shift 'x' left 'n' positions, limit at 'lim' bits
|
||||
function bsli(n : int, x : int, lim : int) : int =
|
||||
(x * 2^n) mod (2^lim)
|
||||
|
||||
// bitwise 'and' for arbitrary precision integers
|
||||
function band(a : int, b : int) : int =
|
||||
if (a >= 0 && b >= 0)
|
||||
uband_(a, b)
|
||||
elif (b >= 0)
|
||||
ubnand_(b, -1 - a)
|
||||
elif (a >= 0)
|
||||
ubnand_(a, -1 - b)
|
||||
else
|
||||
-1 - ubor_(-1 - a, -1 - b)
|
||||
|
||||
// bitwise 'or' for arbitrary precision integers
|
||||
function
|
||||
bor : (int, int) => int
|
||||
bor(0, b) = b
|
||||
bor(a, 0) = a
|
||||
bor(a : int, b : int) : int =
|
||||
if (a >= 0 && b >= 0)
|
||||
ubor_(a, b)
|
||||
elif (b >= 0)
|
||||
-1 - ubnand_(-1 - a, b)
|
||||
elif (a >= 0)
|
||||
-1 - ubnand_(-1 - b, a)
|
||||
else
|
||||
-1 - uband_(-1 - a, -1 - b)
|
||||
|
||||
// bitwise 'xor' for arbitrary precision integers
|
||||
function
|
||||
bxor : (int, int) => int
|
||||
bxor(0, b) = b
|
||||
bxor(a, 0) = a
|
||||
bxor(a, b) =
|
||||
if (a >= 0 && b >= 0)
|
||||
ubxor_(a, b)
|
||||
elif (b >= 0)
|
||||
-1 - ubxor_(-1 - a, b)
|
||||
elif (a >= 0)
|
||||
-1 - ubxor_(a, -1 - b)
|
||||
else
|
||||
ubxor_(-1 - a, -1 - b)
|
||||
|
||||
// bitwise 'not' for arbitrary precision integers
|
||||
function bnot(a : int) = bxor(a, -1)
|
||||
|
||||
// Bitwise 'and' for non-negative integers
|
||||
function uband(a : int, b : int) : int =
|
||||
require(a >= 0 && b >= 0, "uband is only defined for non-negative integers")
|
||||
switch((a, b))
|
||||
(0, _) => 0
|
||||
(_, 0) => 0
|
||||
_ => uband__(a, b, 1, 0)
|
||||
|
||||
private function uband_(a, b) = uband__(a, b, 1, 0)
|
||||
|
||||
private function
|
||||
uband__(0, b, val, acc) = acc
|
||||
uband__(a, 0, val, acc) = acc
|
||||
uband__(a, b, val, acc) =
|
||||
switch (a mod 2 + b mod 2)
|
||||
2 => uband__(a / 2, b / 2, val * 2, acc + val)
|
||||
_ => uband__(a / 2, b / 2, val * 2, acc)
|
||||
|
||||
// Bitwise 'or' for non-negative integers
|
||||
function ubor(a, b) =
|
||||
require(a >= 0 && b >= 0, "ubor is only defined for non-negative integers")
|
||||
switch((a, b))
|
||||
(0, _) => b
|
||||
(_, 0) => a
|
||||
_ => ubor__(a, b, 1, 0)
|
||||
|
||||
private function ubor_(a, b) = ubor__(a, b, 1, 0)
|
||||
|
||||
private function
|
||||
ubor__(0, 0, val, acc) = acc
|
||||
ubor__(a, b, val, acc) =
|
||||
switch (a mod 2 + b mod 2)
|
||||
0 => ubor__(a / 2, b / 2, val * 2, acc)
|
||||
_ => ubor__(a / 2, b / 2, val * 2, acc + val)
|
||||
|
||||
//Bitwise 'xor' for non-negative integers
|
||||
function
|
||||
ubxor : (int, int) => int
|
||||
ubxor(0, b) = b
|
||||
ubxor(a, 0) = a
|
||||
ubxor(a, b) =
|
||||
require(a >= 0 && b >= 0, "ubxor is only defined for non-negative integers")
|
||||
ubxor__(a, b, 1, 0)
|
||||
|
||||
private function ubxor_(a, b) = ubxor__(a, b, 1, 0)
|
||||
|
||||
private function
|
||||
ubxor__(0, 0, val, acc) = acc
|
||||
ubxor__(a, b, val, acc) =
|
||||
switch(a mod 2 + b mod 2)
|
||||
1 => ubxor__(a / 2, b / 2, val * 2, acc + val)
|
||||
_ => ubxor__(a / 2, b / 2, val * 2, acc)
|
||||
|
||||
private function ubnand_(a, b) = ubnand__(a, b, 1, 0)
|
||||
|
||||
private function
|
||||
ubnand__(0, b, val, acc) = acc
|
||||
ubnand__(a, b, val, acc) =
|
||||
switch((a mod 2, b mod 2))
|
||||
(1, 0) => ubnand__(a / 2, b / 2, val * 2, acc + val)
|
||||
_ => ubnand__(a / 2, b / 2, val * 2, acc)
|
||||
@@ -282,9 +282,9 @@ namespace List =
|
||||
private function
|
||||
asc : (('a, 'a) => bool, 'a, list('a), list('a)) => list(list('a))
|
||||
asc(lt, x, acc, h::t) =
|
||||
if(lt(h, x)) reverse(x::acc) :: monotonic_subs(lt, h::t)
|
||||
if(lt(h, x)) List.reverse(x::acc) :: monotonic_subs(lt, h::t)
|
||||
else asc(lt, h, x::acc, t)
|
||||
asc(_, x, acc, []) = [reverse(x::acc)]
|
||||
asc(_, x, acc, []) = [List.reverse(x::acc)]
|
||||
|
||||
/** Merges list of sorted lists
|
||||
*/
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
include "List.aes"
|
||||
namespace String =
|
||||
// Gives a bytes() representation of the string
|
||||
function to_bytes(s : string) : bytes() = StringInternal.to_bytes(s)
|
||||
|
||||
// Computes the SHA3/Keccak hash of the string
|
||||
function sha3(s : string) : hash = StringInternal.sha3(s)
|
||||
// Computes the SHA256 hash of the string.
|
||||
|
||||
+4
-4
@@ -2,8 +2,8 @@
|
||||
|
||||
{erl_opts, [debug_info]}.
|
||||
|
||||
{deps, [ {gmbytecode, {git, "https://git.qpq.swiss/QPQ-AG/gmbytecode.git",
|
||||
{ref, "691b9742bf7e4aa6a4e8e209397916be5abe1763"}}}
|
||||
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {tag, "v3.2.0"}}}
|
||||
, {getopt, "1.0.1"}
|
||||
, {eblake2, "1.0.0"}
|
||||
, {jsx, {git, "https://github.com/talentdeficit/jsx.git", {tag, "2.8.0"}}}
|
||||
]}.
|
||||
@@ -14,8 +14,8 @@
|
||||
{base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]}
|
||||
]}.
|
||||
|
||||
{relx, [{release, {sophia, "8.0.1"},
|
||||
[sophia, gmbytecode]},
|
||||
{relx, [{release, {aesophia, "7.1.0"},
|
||||
[aesophia, aebytecode, getopt]},
|
||||
|
||||
{dev_mode, true},
|
||||
{include_erts, false},
|
||||
|
||||
+14
-14
@@ -1,22 +1,22 @@
|
||||
{"1.2.0",
|
||||
[{<<"base58">>,
|
||||
{git,"https://git.qpq.swiss/QPQ-AG/erl-base58.git",
|
||||
{ref,"e6aa62eeae3d4388311401f06e4b939bf4e94b9c"}},
|
||||
[{<<"aebytecode">>,
|
||||
{git,"https://github.com/aeternity/aebytecode.git",
|
||||
{ref,"2a0a397afad6b45da52572170f718194018bf33c"}},
|
||||
0},
|
||||
{<<"aeserialization">>,
|
||||
{git,"https://github.com/aeternity/aeserialization.git",
|
||||
{ref,"eb68fe331bd476910394966b7f5ede7a74d37e35"}},
|
||||
1},
|
||||
{<<"base58">>,
|
||||
{git,"https://github.com/aeternity/erl-base58.git",
|
||||
{ref,"60a335668a60328a29f9731b67c4a0e9e3d50ab6"}},
|
||||
2},
|
||||
{<<"eblake2">>,{pkg,<<"eblake2">>,<<"1.0.0">>},0},
|
||||
{<<"enacl">>,
|
||||
{git,"https://git.qpq.swiss/QPQ-AG/enacl.git",
|
||||
{ref,"4eb7ec70084ba7c87b1af8797c4c4e90c84f95a2"}},
|
||||
{git,"https://github.com/aeternity/enacl.git",
|
||||
{ref,"793ddb502f7fe081302e1c42227dca70b09f8e17"}},
|
||||
2},
|
||||
{<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},1},
|
||||
{<<"gmbytecode">>,
|
||||
{git,"https://git.qpq.swiss/QPQ-AG/gmbytecode.git",
|
||||
{ref,"691b9742bf7e4aa6a4e8e209397916be5abe1763"}},
|
||||
0},
|
||||
{<<"gmserialization">>,
|
||||
{git,"https://git.qpq.swiss/QPQ-AG/gmserialization.git",
|
||||
{ref,"ac64e01b0f675c1a34c70a827062f381920742db"}},
|
||||
1},
|
||||
{<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},0},
|
||||
{<<"jsx">>,
|
||||
{git,"https://github.com/talentdeficit/jsx.git",
|
||||
{ref,"3074d4865b3385a050badf7828ad31490d860df5"}},
|
||||
|
||||
+1
-4
@@ -198,8 +198,7 @@ encode_expr({bytes, _, B}) ->
|
||||
<<N:Digits/unit:8>> = B,
|
||||
list_to_binary(lists:flatten(io_lib:format("#~*.16.0b", [Digits*2, N])));
|
||||
encode_expr({Lit, _, L}) when Lit == oracle_pubkey; Lit == oracle_query_id;
|
||||
Lit == contract_pubkey; Lit == account_pubkey;
|
||||
Lit == signature ->
|
||||
Lit == contract_pubkey; Lit == account_pubkey ->
|
||||
aeser_api_encoder:encode(Lit, L);
|
||||
encode_expr({app, _, {'-', _}, [{int, _, N}]}) ->
|
||||
encode_expr({int, [], -N});
|
||||
@@ -283,8 +282,6 @@ decode_type(#{list := [Et]}) ->
|
||||
decode_type(#{map := Ets}) ->
|
||||
Ts = decode_types(Ets),
|
||||
["map",$(,lists:join(",", Ts),$)];
|
||||
decode_type(#{bytes := any}) ->
|
||||
["bytes()"];
|
||||
decode_type(#{bytes := Len}) ->
|
||||
["bytes(", integer_to_list(Len), ")"];
|
||||
decode_type(#{variant := Ets}) ->
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
-module(aeso_ast).
|
||||
|
||||
-export([int/2,
|
||||
line/1,
|
||||
pp/1,
|
||||
pp_typed/1,
|
||||
symbol/2,
|
||||
symbol_name/1
|
||||
]).
|
||||
|
||||
|
||||
symbol(Line, Chars) -> {symbol, Line, Chars}.
|
||||
int(Line, Int) -> {'Int', Line, Int}.
|
||||
|
||||
line({symbol, Line, _}) -> Line.
|
||||
|
||||
symbol_name({symbol, _, Name}) -> Name.
|
||||
|
||||
pp(Ast) ->
|
||||
String = prettypr:format(aeso_pretty:decls(Ast, [])),
|
||||
io:format("Ast:\n~s\n", [String]).
|
||||
|
||||
pp_typed(TypedAst) ->
|
||||
%% io:format("Typed tree:\n~p\n",[TypedAst]),
|
||||
String = prettypr:format(aeso_pretty:decls(TypedAst, [show_generated])),
|
||||
io:format("Type ast:\n~s\n",[String]).
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
-module(aeso_ast_code_analysis).
|
||||
|
||||
|
||||
all_warnings() ->
|
||||
[ warn_unused_includes
|
||||
, warn_unused_stateful
|
||||
, warn_unused_variables
|
||||
, warn_unused_typedefs
|
||||
, warn_unused_return_value
|
||||
, warn_unused_functions
|
||||
, warn_shadowing
|
||||
, warn_division_by_zero
|
||||
, warn_negative_spend ].
|
||||
|
||||
|
||||
when_warning(Warn, Do) ->
|
||||
case lists:member(Warn, all_warnings()) of
|
||||
false ->
|
||||
create_type_errors(),
|
||||
type_error({unknown_warning, Warn}),
|
||||
destroy_and_report_type_errors(global_env());
|
||||
true ->
|
||||
case ets_tab_exists(warnings) of
|
||||
true ->
|
||||
IsEnabled = get_option(Warn, false),
|
||||
IsAll = get_option(warn_all, false) andalso lists:member(Warn, all_warnings()),
|
||||
if
|
||||
IsEnabled orelse IsAll -> Do();
|
||||
true -> ok
|
||||
end;
|
||||
false ->
|
||||
ok
|
||||
end
|
||||
end.
|
||||
|
||||
%% Warnings (Unused includes)
|
||||
|
||||
potential_unused_include(Ann, SrcFile) ->
|
||||
IsIncluded = aeso_syntax:get_ann(include_type, Ann, none) =/= none,
|
||||
case IsIncluded of
|
||||
false -> ok;
|
||||
true ->
|
||||
case aeso_syntax:get_ann(file, Ann, no_file) of
|
||||
no_file -> ok;
|
||||
File -> ets_insert(warnings, {unused_include, File, SrcFile})
|
||||
end
|
||||
end.
|
||||
|
||||
used_include(Ann) ->
|
||||
case aeso_syntax:get_ann(file, Ann, no_file) of
|
||||
no_file -> ok;
|
||||
File -> ets_match_delete(warnings, {unused_include, File, '_'})
|
||||
end.
|
||||
|
||||
%% Warnings (Unused stateful)
|
||||
|
||||
potential_unused_stateful(Ann, Fun) ->
|
||||
case aeso_syntax:get_ann(stateful, Ann, false) of
|
||||
false -> ok;
|
||||
true -> ets_insert(warnings, {unused_stateful, Ann, Fun})
|
||||
end.
|
||||
|
||||
used_stateful(Fun) ->
|
||||
ets_match_delete(warnings, {unused_stateful, '_', Fun}).
|
||||
|
||||
%% Warnings (Unused type defs)
|
||||
|
||||
potential_unused_typedefs(Namespace, TypeDefs) ->
|
||||
lists:map(fun({type_def, Ann, Id, Args, _}) ->
|
||||
ets_insert(warnings, {unused_typedef, Ann, Namespace ++ qname(Id), length(Args)}) end, TypeDefs).
|
||||
|
||||
used_typedef(TypeAliasId, Arity) ->
|
||||
ets_match_delete(warnings, {unused_typedef, '_', qname(TypeAliasId), Arity}).
|
||||
|
||||
%% Warnings (Unused variables)
|
||||
|
||||
potential_unused_variables(Namespace, Fun, Vars0) ->
|
||||
Vars = [ Var || Var = {id, _, VarName} <- Vars0, VarName /= "_" ],
|
||||
lists:map(fun({id, Ann, VarName}) ->
|
||||
ets_insert(warnings, {unused_variable, Ann, Namespace, Fun, VarName}) end, Vars).
|
||||
|
||||
used_variable(Namespace, Fun, [VarName]) ->
|
||||
ets_match_delete(warnings, {unused_variable, '_', Namespace, Fun, VarName});
|
||||
used_variable(_, _, _) -> ok.
|
||||
|
||||
%% Warnings (Unused return value)
|
||||
|
||||
potential_unused_return_value({typed, Ann, {app, _, {typed, _, _, {fun_t, _, _, _, {id, _, Type}}}, _}, _}) when Type /= "unit" ->
|
||||
ets_insert(warnings, {unused_return_value, Ann});
|
||||
potential_unused_return_value(_) -> ok.
|
||||
|
||||
%% Warnings (Unused functions)
|
||||
|
||||
create_unused_functions() ->
|
||||
ets_new(function_calls, [bag]),
|
||||
ets_new(all_functions, [set]).
|
||||
|
||||
register_function_call(Caller, Callee) ->
|
||||
ets_insert(function_calls, {Caller, Callee}).
|
||||
|
||||
potential_unused_function(#env{ what = namespace }, Ann, FunQName, FunId) ->
|
||||
ets_insert(all_functions, {Ann, FunQName, FunId, not aeso_syntax:get_ann(private, Ann, false)});
|
||||
potential_unused_function(_Env, Ann, FunQName, FunId) ->
|
||||
ets_insert(all_functions, {Ann, FunQName, FunId, aeso_syntax:get_ann(entrypoint, Ann, false)}).
|
||||
|
||||
remove_used_funs(All) ->
|
||||
{Used, Unused} = lists:partition(fun({_, _, _, IsUsed}) -> IsUsed end, All),
|
||||
CallsByUsed = lists:flatmap(fun({_, F, _, _}) -> ets_lookup(function_calls, F) end, Used),
|
||||
CalledFuns = sets:from_list(lists:map(fun({_, Callee}) -> Callee end, CallsByUsed)),
|
||||
MarkUsedFun = fun(Fun, Acc) ->
|
||||
case lists:keyfind(Fun, 2, Acc) of
|
||||
false -> Acc;
|
||||
T -> lists:keyreplace(Fun, 2, Acc, setelement(4, T, true))
|
||||
end
|
||||
end,
|
||||
NewUnused = sets:fold(MarkUsedFun, Unused, CalledFuns),
|
||||
case lists:keyfind(true, 4, NewUnused) of
|
||||
false -> NewUnused;
|
||||
_ -> remove_used_funs(NewUnused)
|
||||
end.
|
||||
|
||||
destroy_and_report_unused_functions() ->
|
||||
AllFuns = ets_tab2list(all_functions),
|
||||
lists:map(fun({Ann, _, FunId, _}) -> ets_insert(warnings, {unused_function, Ann, name(FunId)}) end,
|
||||
remove_used_funs(AllFuns)),
|
||||
ets_delete(all_functions),
|
||||
ets_delete(function_calls).
|
||||
|
||||
%% Warnings (Shadowing)
|
||||
|
||||
warn_potential_shadowing(_, "_", _) -> ok;
|
||||
warn_potential_shadowing(Ann, Name, Vars) ->
|
||||
case proplists:get_value(Name, Vars, false) of
|
||||
false -> ok;
|
||||
{AnnOld, _} -> ets_insert(warnings, {shadowing, Ann, Name, AnnOld})
|
||||
end.
|
||||
|
||||
%% Warnings (Division by zero)
|
||||
|
||||
warn_potential_division_by_zero(Ann, Op, Args) ->
|
||||
case {Op, Args} of
|
||||
{{'/', _}, [_, {int, _, 0}]} -> ets_insert(warnings, {division_by_zero, Ann});
|
||||
_ -> ok
|
||||
end.
|
||||
|
||||
%% Warnings (Negative spends)
|
||||
|
||||
warn_potential_negative_spend(Ann, Fun, Args) ->
|
||||
case {Fun, Args} of
|
||||
{ {typed, _, {qid, _, ["Chain", "spend"]}, _}
|
||||
, [_, {typed, _, {app, _, {'-', _}, [{typed, _, {int, _, X}, _}]}, _}]} when X > 0 ->
|
||||
ets_insert(warnings, {negative_spend, Ann});
|
||||
_ -> ok
|
||||
end.
|
||||
File diff suppressed because it is too large
Load Diff
+546
-809
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,721 @@
|
||||
-module(aeso_ast_types_errors).
|
||||
|
||||
|
||||
cannot_unify(A, B, Cxt, When) ->
|
||||
type_error({cannot_unify, A, B, Cxt, When}).
|
||||
|
||||
|
||||
type_error(Err) ->
|
||||
ets_insert(type_errors, Err).
|
||||
|
||||
|
||||
mk_t_err(Pos, Msg) ->
|
||||
aeso_errors:new(type_error, Pos, lists:flatten(Msg)).
|
||||
mk_t_err(Pos, Msg, Ctxt) ->
|
||||
aeso_errors:new(type_error, Pos, lists:flatten(Msg), lists:flatten(Ctxt)).
|
||||
|
||||
mk_t_err_from_warn(Warn) ->
|
||||
aeso_warnings:warn_to_err(type_error, Warn).
|
||||
|
||||
mk_error({no_decls, File}) ->
|
||||
Pos = aeso_errors:pos(File, 0, 0),
|
||||
mk_t_err(Pos, "Empty contract");
|
||||
mk_error({mismatched_decl_in_funblock, Name, Decl}) ->
|
||||
Msg = io_lib:format("Mismatch in the function block. Expected implementation/type declaration of ~s function", [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 "
|
||||
"(takes another type as an argument)", [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"
|
||||
, [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",
|
||||
mk_t_err(pos(Upd), Msg);
|
||||
mk_error({fundecl_must_have_funtype, _Ann, Id, Type}) ->
|
||||
Msg = io_lib:format("`~s` was declared with an invalid type `~s`. "
|
||||
"Entrypoints and functions must have functional types"
|
||||
, [pp(Id), pp(instantiate(Type))]),
|
||||
mk_t_err(pos(Id), Msg);
|
||||
mk_error({cannot_unify, A, B, Cxt, When}) ->
|
||||
VarianceContext = case Cxt of
|
||||
none -> "";
|
||||
_ -> io_lib:format(" in a ~p context", [Cxt])
|
||||
end,
|
||||
Msg = io_lib:format("Cannot unify `~s` and `~s`" ++ VarianceContext,
|
||||
[pp(instantiate(A)), pp(instantiate(B))]),
|
||||
{Pos, Ctxt} = pp_when(When),
|
||||
mk_t_err(Pos, Msg, Ctxt);
|
||||
mk_error({hole_found, Ann, Type}) ->
|
||||
Msg = io_lib:format("Found a hole of type `~s`", [pp(instantiate(Type))]),
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({unbound_variable, Id}) ->
|
||||
Msg = io_lib:format("Unbound variable `~s`", [pp(Id)]),
|
||||
case Id of
|
||||
{qid, _, ["Chain", "event"]} ->
|
||||
Cxt = "Did you forget to define the event type?",
|
||||
mk_t_err(pos(Id), Msg, Cxt);
|
||||
_ -> mk_t_err(pos(Id), Msg)
|
||||
end;
|
||||
mk_error({undefined_field, Id}) ->
|
||||
Msg = io_lib:format("Unbound field ~s", [pp(Id)]),
|
||||
mk_t_err(pos(Id), Msg);
|
||||
mk_error({not_a_record_type, Type, Why}) ->
|
||||
Msg = io_lib:format("Not a record type: `~s`", [pp_type(Type)]),
|
||||
{Pos, Ctxt} = pp_why_record(Why),
|
||||
mk_t_err(Pos, Msg, Ctxt);
|
||||
mk_error({not_a_contract_type, Type, Cxt}) ->
|
||||
Msg =
|
||||
case Type of
|
||||
{tvar, _, _} ->
|
||||
"Unresolved contract type";
|
||||
_ ->
|
||||
io_lib:format("The type `~s` is not a contract type", [pp_type(Type)])
|
||||
end,
|
||||
{Pos, Cxt1} =
|
||||
case Cxt of
|
||||
{var_args, Ann, Fun} ->
|
||||
{pos(Ann),
|
||||
io_lib:format("when calling variadic function `~s`", [pp_expr(Fun)])};
|
||||
{contract_literal, Lit} ->
|
||||
{pos(Lit),
|
||||
io_lib:format("when checking that the contract literal `~s` has the type `~s`",
|
||||
[pp_expr(Lit), pp_type(Type)])};
|
||||
{address_to_contract, Ann} ->
|
||||
{pos(Ann),
|
||||
io_lib:format("when checking that the call to `Address.to_contract` has the type `~s`",
|
||||
[pp_type(Type)])}
|
||||
end,
|
||||
mk_t_err(Pos, Msg, Cxt1);
|
||||
mk_error({non_linear_pattern, Pattern, Nonlinear}) ->
|
||||
Msg = io_lib:format("Repeated name~s ~s in the pattern `~s`",
|
||||
[plural("", "s", Nonlinear),
|
||||
string:join(lists:map(fun(F) -> "`" ++ F ++ "`" end, Nonlinear), ", "),
|
||||
pp_expr(Pattern)]),
|
||||
mk_t_err(pos(Pattern), Msg);
|
||||
mk_error({ambiguous_record, Fields = [{_, First} | _], Candidates}) ->
|
||||
Msg = io_lib:format("Ambiguous record type with field~s ~s could be one of~s",
|
||||
[plural("", "s", Fields),
|
||||
string:join([ "`" ++ pp(F) ++ "`" || {_, F} <- Fields ], ", "),
|
||||
[ ["\n - ", "`" ++ pp(C) ++ "`", " (at ", pp_loc(C), ")"] || C <- Candidates ]]),
|
||||
mk_t_err(pos(First), Msg);
|
||||
mk_error({missing_field, Field, Rec}) ->
|
||||
Msg = io_lib:format("Record type `~s` does not have field `~s`",
|
||||
[pp(Rec), pp(Field)]),
|
||||
mk_t_err(pos(Field), Msg);
|
||||
mk_error({missing_fields, Ann, RecType, Fields}) ->
|
||||
Msg = io_lib:format("The field~s ~s ~s missing when constructing an element of type `~s`",
|
||||
[plural("", "s", Fields),
|
||||
string:join(lists:map(fun(F) -> "`" ++ F ++ "`" end, Fields), ", "),
|
||||
plural("is", "are", Fields), pp(RecType)]),
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({no_records_with_all_fields, Fields = [{_, First} | _]}) ->
|
||||
Msg = io_lib:format("No record type with field~s ~s",
|
||||
[plural("", "s", Fields),
|
||||
string:join([ "`" ++ pp(F) ++ "`" || {_, F} <- Fields ], ", ")]),
|
||||
mk_t_err(pos(First), Msg);
|
||||
mk_error({recursive_types_not_implemented, Types}) ->
|
||||
S = plural(" is", "s are mutually", Types),
|
||||
Msg = io_lib:format("The following type~s recursive, which is not yet supported:~s",
|
||||
[S, [io_lib:format("\n - `~s` (at ~s)", [pp(T), pp_loc(T)]) || T <- Types]]),
|
||||
mk_t_err(pos(hd(Types)), Msg);
|
||||
mk_error({event_must_be_variant_type, Where}) ->
|
||||
Msg = io_lib:format("The event type must be a variant type", []),
|
||||
mk_t_err(pos(Where), Msg);
|
||||
mk_error({indexed_type_must_be_word, Type, Type}) ->
|
||||
Msg = io_lib:format("The indexed type `~s` is not a word type",
|
||||
[pp_type(Type)]),
|
||||
mk_t_err(pos(Type), Msg);
|
||||
mk_error({indexed_type_must_be_word, Type, Type1}) ->
|
||||
Msg = io_lib:format("The indexed type `~s` equals `~s` which is not a word type",
|
||||
[pp_type(Type), pp_type(Type1)]),
|
||||
mk_t_err(pos(Type), Msg);
|
||||
mk_error({event_0_to_3_indexed_values, Constr}) ->
|
||||
Msg = io_lib:format("The event constructor `~s` has too many indexed values (max 3)",
|
||||
[name(Constr)]),
|
||||
mk_t_err(pos(Constr), Msg);
|
||||
mk_error({event_0_to_1_string_values, Constr}) ->
|
||||
Msg = io_lib:format("The event constructor `~s` has too many non-indexed values (max 1)",
|
||||
[name(Constr)]),
|
||||
mk_t_err(pos(Constr), Msg);
|
||||
mk_error({repeated_constructor, Cs}) ->
|
||||
Msg = io_lib:format("Variant types must have distinct constructor names~s",
|
||||
[[ io_lib:format("\n`~s` (at ~s)", [pp_typed(" - ", C, T), pp_loc(C)]) || {C, T} <- Cs ]]),
|
||||
mk_t_err(pos(element(1, hd(Cs))), Msg);
|
||||
mk_error({bad_named_argument, [], Name}) ->
|
||||
Msg = io_lib:format("Named argument ~s supplied to function expecting no named arguments.",
|
||||
[pp(Name)]),
|
||||
mk_t_err(pos(Name), Msg);
|
||||
mk_error({bad_named_argument, Args, Name}) ->
|
||||
Msg = io_lib:format("Named argument `~s` is not one of the expected named arguments~s",
|
||||
[pp(Name),
|
||||
[ io_lib:format("\n - `~s`", [pp_typed("", Arg, Type)])
|
||||
|| {named_arg_t, _, Arg, Type, _} <- Args ]]),
|
||||
mk_t_err(pos(Name), Msg);
|
||||
mk_error({unsolved_named_argument_constraint, #named_argument_constraint{name = Name, type = Type}}) ->
|
||||
Msg = io_lib:format("Named argument ~s supplied to function with unknown named arguments.",
|
||||
[pp_typed("", Name, Type)]),
|
||||
mk_t_err(pos(Name), Msg);
|
||||
mk_error({reserved_entrypoint, Name, Def}) ->
|
||||
Msg = io_lib:format("The name '~s' is reserved and cannot be used for a "
|
||||
"top-level contract function.", [Name]),
|
||||
mk_t_err(pos(Def), Msg);
|
||||
mk_error({duplicate_definition, Name, Locs}) ->
|
||||
Msg = io_lib:format("Duplicate definitions of `~s` at~s",
|
||||
[Name, [ ["\n - ", pp_loc(L)] || L <- Locs ]]),
|
||||
mk_t_err(pos(lists:last(Locs)), Msg);
|
||||
mk_error({duplicate_scope, Kind, Name, OtherKind, L}) ->
|
||||
Msg = io_lib:format("The ~p `~s` has the same name as a ~p at ~s",
|
||||
[Kind, pp(Name), OtherKind, pp_loc(L)]),
|
||||
mk_t_err(pos(Name), Msg);
|
||||
mk_error({include, _, {string, Pos, Name}}) ->
|
||||
Msg = io_lib:format("Include of `~s` is not allowed, include only allowed at top level.",
|
||||
[binary_to_list(Name)]),
|
||||
mk_t_err(pos(Pos), Msg);
|
||||
mk_error({namespace, _Pos, {con, Pos, Name}, _Def}) ->
|
||||
Msg = io_lib:format("Nested namespaces are not allowed. Namespace `~s` is not defined at top level.",
|
||||
[Name]),
|
||||
mk_t_err(pos(Pos), Msg);
|
||||
mk_error({Contract, _Pos, {con, Pos, Name}, _Impls, _Def}) when ?IS_CONTRACT_HEAD(Contract) ->
|
||||
Msg = io_lib:format("Nested contracts are not allowed. Contract `~s` is not defined at top level.",
|
||||
[Name]),
|
||||
mk_t_err(pos(Pos), Msg);
|
||||
mk_error({type_decl, _, {id, Pos, Name}, _}) ->
|
||||
Msg = io_lib:format("Empty type declarations are not supported. Type `~s` lacks a definition",
|
||||
[Name]),
|
||||
mk_t_err(pos(Pos), Msg);
|
||||
mk_error({letval, _Pos, {id, Pos, Name}, _Def}) ->
|
||||
Msg = io_lib:format("Toplevel \"let\" definitions are not supported. Value `~s` could be replaced by 0-argument function.",
|
||||
[Name]),
|
||||
mk_t_err(pos(Pos), Msg);
|
||||
mk_error({stateful_not_allowed, Id, Fun}) ->
|
||||
Msg = io_lib:format("Cannot reference stateful function `~s` in the definition of non-stateful function `~s`.",
|
||||
[pp(Id), pp(Fun)]),
|
||||
mk_t_err(pos(Id), Msg);
|
||||
mk_error({stateful_not_allowed_in_guards, Id}) ->
|
||||
Msg = io_lib:format("Cannot reference stateful function `~s` in a pattern guard.",
|
||||
[pp(Id)]),
|
||||
mk_t_err(pos(Id), Msg);
|
||||
mk_error({value_arg_not_allowed, Value, Fun}) ->
|
||||
Msg = io_lib:format("Cannot pass non-zero value argument `~s` in the definition of non-stateful function `~s`.",
|
||||
[pp_expr(Value), pp(Fun)]),
|
||||
mk_t_err(pos(Value), Msg);
|
||||
mk_error({init_depends_on_state, Which, [_Init | Chain]}) ->
|
||||
WhichCalls = fun("put") -> ""; ("state") -> ""; (_) -> ", which calls" end,
|
||||
Msg = io_lib:format("The `init` function should return the initial state as its result and cannot ~s the state, but it calls~s",
|
||||
[if Which == put -> "write"; true -> "read" end,
|
||||
[ io_lib:format("\n - `~s` (at ~s)~s", [Fun, pp_loc(Ann), WhichCalls(Fun)])
|
||||
|| {[_, Fun], Ann} <- Chain]]),
|
||||
mk_t_err(pos(element(2, hd(Chain))), Msg);
|
||||
mk_error({missing_body_for_let, Ann}) ->
|
||||
Msg = io_lib:format("Let binding must be followed by an expression.", []),
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({public_modifier_in_contract, Decl}) ->
|
||||
Decl1 = mk_entrypoint(Decl),
|
||||
Msg = io_lib:format("Use `entrypoint` instead of `function` for public function `~s`: `~s`",
|
||||
[pp_expr(element(3, Decl)),
|
||||
prettypr:format(aeso_pretty:decl(Decl1))]),
|
||||
mk_t_err(pos(Decl), Msg);
|
||||
mk_error({init_must_be_an_entrypoint, Decl}) ->
|
||||
Decl1 = mk_entrypoint(Decl),
|
||||
Msg = io_lib:format("The init function must be an entrypoint: ~s",
|
||||
[prettypr:format(prettypr:nest(2, aeso_pretty:decl(Decl1)))]),
|
||||
mk_t_err(pos(Decl), Msg);
|
||||
mk_error({init_must_not_be_payable, Decl}) ->
|
||||
Msg = io_lib:format("The init function cannot be payable. "
|
||||
"You don't need the 'payable' annotation to be able to attach "
|
||||
"funds to the create contract transaction.",
|
||||
[]),
|
||||
mk_t_err(pos(Decl), Msg);
|
||||
mk_error({proto_must_be_entrypoint, Decl}) ->
|
||||
Decl1 = mk_entrypoint(Decl),
|
||||
Msg = io_lib:format("Use `entrypoint` for declaration of `~s`: `~s`",
|
||||
[pp_expr(element(3, Decl)),
|
||||
prettypr:format(aeso_pretty:decl(Decl1))]),
|
||||
mk_t_err(pos(Decl), Msg);
|
||||
mk_error({proto_in_namespace, Decl}) ->
|
||||
Msg = io_lib:format("Namespaces cannot contain function prototypes.", []),
|
||||
mk_t_err(pos(Decl), Msg);
|
||||
mk_error({entrypoint_in_namespace, Decl}) ->
|
||||
Msg = io_lib:format("Namespaces cannot contain entrypoints. Use `function` instead.", []),
|
||||
mk_t_err(pos(Decl), Msg);
|
||||
mk_error({private_entrypoint, Decl}) ->
|
||||
Msg = io_lib:format("The entrypoint `~s` cannot be private. Use `function` instead.",
|
||||
[pp_expr(element(3, Decl))]),
|
||||
mk_t_err(pos(Decl), Msg);
|
||||
mk_error({private_and_public, Decl}) ->
|
||||
Msg = io_lib:format("The function `~s` cannot be both public and private.",
|
||||
[pp_expr(element(3, Decl))]),
|
||||
mk_t_err(pos(Decl), Msg);
|
||||
mk_error({contract_has_no_entrypoints, Con}) ->
|
||||
Msg = io_lib:format("The contract `~s` has no entrypoints. Since Sophia version 3.2, public "
|
||||
"contract functions must be declared with the `entrypoint` keyword instead of "
|
||||
"`function`.", [pp_expr(Con)]),
|
||||
mk_t_err(pos(Con), Msg);
|
||||
mk_error({definition_in_contract_interface, Ann, {id, _, Id}}) ->
|
||||
Msg = "Contract interfaces cannot contain defined functions or entrypoints.",
|
||||
Cxt = io_lib:format("Fix: replace the definition of `~s` by a type signature.", [Id]),
|
||||
mk_t_err(pos(Ann), Msg, Cxt);
|
||||
mk_error({unbound_type, Type}) ->
|
||||
Msg = io_lib:format("Unbound type ~s.", [pp_type(Type)]),
|
||||
mk_t_err(pos(Type), Msg);
|
||||
mk_error({new_tuple_syntax, Ann, Ts}) ->
|
||||
Msg = io_lib:format("Invalid type `~s`. The syntax of tuple types changed in Sophia version 4.0. Did you mean `~s`",
|
||||
[pp_type({args_t, Ann, Ts}), pp_type({tuple_t, Ann, Ts})]),
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({map_in_map_key, Ann, KeyType}) ->
|
||||
Msg = io_lib:format("Invalid key type `~s`", [pp_type(KeyType)]),
|
||||
Cxt = "Map keys cannot contain other maps.",
|
||||
mk_t_err(pos(Ann), Msg, Cxt);
|
||||
mk_error({cannot_call_init_function, Ann}) ->
|
||||
Msg = "The 'init' function is called exclusively by the create contract transaction "
|
||||
"and cannot be called from the contract code.",
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({contract_treated_as_namespace, Ann, [Con, Fun] = QName}) ->
|
||||
Msg = io_lib:format("Invalid call to contract entrypoint `~s`.", [string:join(QName, ".")]),
|
||||
Cxt = io_lib:format("It must be called as `c.~s` for some `c : ~s`.", [Fun, Con]),
|
||||
mk_t_err(pos(Ann), Msg, Cxt);
|
||||
mk_error({bad_top_level_decl, Decl}) ->
|
||||
What = case element(1, Decl) of
|
||||
letval -> "function or entrypoint";
|
||||
_ -> "contract or namespace"
|
||||
end,
|
||||
Id = element(3, Decl),
|
||||
Msg = io_lib:format("The definition of '~s' must appear inside a ~s.",
|
||||
[pp_expr(Id), What]),
|
||||
mk_t_err(pos(Decl), Msg);
|
||||
mk_error({unknown_byte_length, Type}) ->
|
||||
Msg = io_lib:format("Cannot resolve length of byte array.", []),
|
||||
mk_t_err(pos(Type), Msg);
|
||||
mk_error({unsolved_bytes_constraint, Ann, concat, A, B, C}) ->
|
||||
Msg = io_lib:format("Failed to resolve byte array lengths in call to Bytes.concat with arguments of type\n"
|
||||
"~s (at ~s)\n~s (at ~s)\nand result type\n~s (at ~s)",
|
||||
[pp_type(" - ", A), pp_loc(A), pp_type(" - ", B),
|
||||
pp_loc(B), pp_type(" - ", C), pp_loc(C)]),
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({unsolved_bytes_constraint, Ann, split, A, B, C}) ->
|
||||
Msg = io_lib:format("Failed to resolve byte array lengths in call to Bytes.split with argument of type\n"
|
||||
"~s (at ~s)\nand result types\n~s (at ~s)\n~s (at ~s)",
|
||||
[ pp_type(" - ", C), pp_loc(C), pp_type(" - ", A), pp_loc(A),
|
||||
pp_type(" - ", B), pp_loc(B)]),
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({failed_to_get_compiler_version, Err}) ->
|
||||
Msg = io_lib:format("Failed to get compiler version. Error: ~p", [Err]),
|
||||
mk_t_err(pos(0, 0), Msg);
|
||||
mk_error({compiler_version_mismatch, Ann, Version, Op, Bound}) ->
|
||||
PrintV = fun(V) -> string:join([integer_to_list(N) || N <- V], ".") end,
|
||||
Msg = io_lib:format("Cannot compile with this version of the compiler, "
|
||||
"because it does not satisfy the constraint"
|
||||
" ~s ~s ~s", [PrintV(Version), Op, PrintV(Bound)]),
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({empty_record_or_map_update, Expr}) ->
|
||||
Msg = io_lib:format("Empty record/map update `~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 `~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 `~s`. "
|
||||
"It must be either `true` or `false`.",
|
||||
[Name, pp_expr(instantiate(Arg))]),
|
||||
mk_t_err(pos(Arg), Msg);
|
||||
mk_error({conflicting_updates_for_field, Upd, Key}) ->
|
||||
Msg = io_lib:format("Conflicting updates for field '~s'", [Key]),
|
||||
mk_t_err(pos(Upd), Msg);
|
||||
mk_error({ambiguous_main_contract, Ann}) ->
|
||||
Msg = "Could not deduce the main contract. You can point it out manually with the `main` keyword.",
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({main_contract_undefined, Ann}) ->
|
||||
Msg = "No contract defined.",
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({multiple_main_contracts, Ann}) ->
|
||||
Msg = "Only one main contract can be defined.",
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({unify_varargs, When}) ->
|
||||
Msg = "Cannot infer types for variable argument list.",
|
||||
{Pos, Ctxt} = pp_when(When),
|
||||
mk_t_err(Pos, Msg, Ctxt);
|
||||
mk_error({clone_no_contract, Ann}) ->
|
||||
Msg = "Chain.clone requires `ref` named argument of contract type.",
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({contract_lacks_definition, Type, When}) ->
|
||||
Msg = io_lib:format(
|
||||
"~s is not implemented.",
|
||||
[pp_type(Type)]
|
||||
),
|
||||
{Pos, Ctxt} = pp_when(When),
|
||||
mk_t_err(Pos, Msg, Ctxt);
|
||||
mk_error({ambiguous_name, Name, QIds}) ->
|
||||
Msg = io_lib:format("Ambiguous name `~s` could be one of~s",
|
||||
[pp(Name),
|
||||
[io_lib:format("\n - `~s` (at ~s)", [pp(QId), pp_loc(QId)]) || QId <- QIds]]),
|
||||
mk_t_err(pos(Name), Msg);
|
||||
mk_error({using_undefined_namespace, Ann, Namespace}) ->
|
||||
Msg = io_lib:format("Cannot use undefined namespace ~s", [Namespace]),
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({using_undefined_namespace_parts, Ann, Namespace, Parts}) ->
|
||||
PartsStr = lists:concat(lists:join(", ", Parts)),
|
||||
Msg = io_lib:format("The namespace ~s does not define the following names: ~s", [Namespace, PartsStr]),
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({unknown_warning, Warning}) ->
|
||||
Msg = io_lib:format("Trying to report unknown warning: ~p", [Warning]),
|
||||
mk_t_err(pos(0, 0), Msg);
|
||||
mk_error({empty_record_definition, Ann, Name}) ->
|
||||
Msg = io_lib:format("Empty record definitions are not allowed. Cannot define the record `~s`", [Name]),
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({unimplemented_interface_function, ConId, InterfaceName, FunName}) ->
|
||||
Msg = io_lib:format("Unimplemented entrypoint `~s` from the interface `~s` in the contract `~s`", [FunName, InterfaceName, pp(ConId)]),
|
||||
mk_t_err(pos(ConId), Msg);
|
||||
mk_error({referencing_undefined_interface, InterfaceId}) ->
|
||||
Msg = io_lib:format("Trying to implement or extend an undefined interface `~s`", [pp(InterfaceId)]),
|
||||
mk_t_err(pos(InterfaceId), Msg);
|
||||
mk_error({missing_definition, Id}) ->
|
||||
Msg = io_lib:format("Missing definition of function `~s`", [name(Id)]),
|
||||
mk_t_err(pos(Id), Msg);
|
||||
mk_error({parameterized_state, Ann}) ->
|
||||
Msg = "The state type cannot be parameterized",
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({parameterized_event, Ann}) ->
|
||||
Msg = "The event type cannot be parameterized",
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({missing_init_function, Con}) ->
|
||||
Msg = io_lib:format("Missing `init` function for the contract `~s`.", [name(Con)]),
|
||||
Cxt = "The `init` function can only be omitted if the state type is `unit`",
|
||||
mk_t_err(pos(Con), Msg, Cxt);
|
||||
mk_error({higher_order_entrypoint, Ann, {id, _, Name}, Thing}) ->
|
||||
What = "higher-order (contains function types)",
|
||||
ThingS = case Thing of
|
||||
{argument, X, T} -> io_lib:format("argument\n~s`\n", [pp_typed(" `", X, T)]);
|
||||
{result, T} -> io_lib:format("return type\n~s`\n", [pp_type(" `", T)])
|
||||
end,
|
||||
Bad = case Thing of
|
||||
{argument, _, _} -> io_lib:format("has a ~s type", [What]);
|
||||
{result, _} -> io_lib:format("is ~s", [What])
|
||||
end,
|
||||
Msg = io_lib:format("The ~sof entrypoint `~s` ~s",
|
||||
[ThingS, Name, Bad]),
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({invalid_aens_resolve_type, Ann, T}) ->
|
||||
Msg = io_lib:format("Invalid return type of `AENS.resolve`:\n"
|
||||
"~s`\n"
|
||||
"It must be a `string` or a pubkey type (`address`, `oracle`, etc)",
|
||||
[pp_type(" `", T)]),
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({invalid_oracle_type, Why, What, Ann, Type}) ->
|
||||
WhyS = case Why of higher_order -> "higher-order (contain function types)";
|
||||
polymorphic -> "polymorphic (contain type variables)" end,
|
||||
Msg = io_lib:format("Invalid oracle type\n~s`", [pp_type(" `", Type)]),
|
||||
Cxt = io_lib:format("The ~s type must not be ~s", [What, WhyS]),
|
||||
mk_t_err(pos(Ann), Msg, Cxt);
|
||||
mk_error({interface_implementation_conflict, Contract, I1, I2, Fun}) ->
|
||||
Msg = io_lib:format("Both interfaces `~s` and `~s` implemented by "
|
||||
"the contract `~s` have a function called `~s`",
|
||||
[name(I1), name(I2), name(Contract), name(Fun)]),
|
||||
mk_t_err(pos(Contract), Msg);
|
||||
mk_error({function_should_be_entrypoint, Impl, Base, Interface}) ->
|
||||
Msg = io_lib:format("`~s` must be declared as an entrypoint instead of a function "
|
||||
"in order to implement the entrypoint `~s` from the interface `~s`",
|
||||
[name(Impl), name(Base), name(Interface)]),
|
||||
mk_t_err(pos(Impl), Msg);
|
||||
mk_error({entrypoint_cannot_be_stateful, Impl, Base, Interface}) ->
|
||||
Msg = io_lib:format("`~s` cannot be stateful because the entrypoint `~s` in the "
|
||||
"interface `~s` is not stateful",
|
||||
[name(Impl), name(Base), name(Interface)]),
|
||||
mk_t_err(pos(Impl), Msg);
|
||||
mk_error({entrypoint_must_be_payable, Impl, Base, Interface}) ->
|
||||
Msg = io_lib:format("`~s` must be payable because the entrypoint `~s` in the "
|
||||
"interface `~s` is payable",
|
||||
[name(Impl), name(Base), name(Interface)]),
|
||||
mk_t_err(pos(Impl), Msg);
|
||||
mk_error({unpreserved_payablity, Kind, ContractCon, InterfaceCon}) ->
|
||||
KindStr = case Kind of
|
||||
contract -> "contract";
|
||||
contract_interface -> "interface"
|
||||
end,
|
||||
Msg = io_lib:format("Non-payable ~s `~s` cannot implement payable interface `~s`",
|
||||
[KindStr, name(ContractCon), name(InterfaceCon)]),
|
||||
mk_t_err(pos(ContractCon), Msg);
|
||||
mk_error(Err) ->
|
||||
Msg = io_lib:format("Unknown error: ~p", [Err]),
|
||||
mk_t_err(pos(0, 0), Msg).
|
||||
|
||||
mk_warning({unused_include, FileName, SrcFile}) ->
|
||||
Msg = io_lib:format("The file `~s` is included but not used.", [FileName]),
|
||||
aeso_warnings:new(aeso_errors:pos(SrcFile, 0, 0), Msg);
|
||||
mk_warning({unused_stateful, Ann, FunName}) ->
|
||||
Msg = io_lib:format("The function `~s` is unnecessarily marked as stateful.", [name(FunName)]),
|
||||
aeso_warnings:new(pos(Ann), Msg);
|
||||
mk_warning({unused_variable, Ann, _Namespace, _Fun, VarName}) ->
|
||||
Msg = io_lib:format("The variable `~s` is defined but never used.", [VarName]),
|
||||
aeso_warnings:new(pos(Ann), Msg);
|
||||
mk_warning({unused_typedef, Ann, QName, _Arity}) ->
|
||||
Msg = io_lib:format("The type `~s` is defined but never used.", [lists:last(QName)]),
|
||||
aeso_warnings:new(pos(Ann), Msg);
|
||||
mk_warning({unused_return_value, Ann}) ->
|
||||
Msg = io_lib:format("Unused return value.", []),
|
||||
aeso_warnings:new(pos(Ann), Msg);
|
||||
mk_warning({unused_function, Ann, FunName}) ->
|
||||
Msg = io_lib:format("The function `~s` is defined but never used.", [FunName]),
|
||||
aeso_warnings:new(pos(Ann), Msg);
|
||||
mk_warning({shadowing, Ann, VarName, AnnOld}) ->
|
||||
Msg = io_lib:format("The definition of `~s` shadows an older definition at ~s.", [VarName, pp_loc(AnnOld)]),
|
||||
aeso_warnings:new(pos(Ann), Msg);
|
||||
mk_warning({division_by_zero, Ann}) ->
|
||||
Msg = io_lib:format("Division by zero.", []),
|
||||
aeso_warnings:new(pos(Ann), Msg);
|
||||
mk_warning({negative_spend, Ann}) ->
|
||||
Msg = io_lib:format("Negative spend.", []),
|
||||
aeso_warnings:new(pos(Ann), Msg);
|
||||
mk_warning(Warn) ->
|
||||
Msg = io_lib:format("Unknown warning: ~p", [Warn]),
|
||||
aeso_warnings:new(Msg).
|
||||
|
||||
mk_entrypoint(Decl) ->
|
||||
Ann = [entrypoint | lists:keydelete(public, 1,
|
||||
lists:keydelete(private, 1,
|
||||
aeso_syntax:get_ann(Decl))) -- [public, private]],
|
||||
aeso_syntax:set_ann(Ann, Decl).
|
||||
|
||||
pp_when({todo, What}) -> {pos(0, 0), io_lib:format("[TODO] ~p", [What])};
|
||||
pp_when({at, Ann}) -> {pos(Ann), io_lib:format("at ~s", [pp_loc(Ann)])};
|
||||
pp_when({check_typesig, Name, Inferred, Given}) ->
|
||||
{pos(Given),
|
||||
io_lib:format("when checking the definition of `~s`\n"
|
||||
" inferred type: `~s`\n"
|
||||
" given type: `~s`",
|
||||
[Name, pp(instantiate(Inferred)), pp(instantiate(Given))])};
|
||||
pp_when({infer_app, Fun, NamedArgs, Args, Inferred0, ArgTypes0}) ->
|
||||
Inferred = instantiate(Inferred0),
|
||||
ArgTypes = instantiate(ArgTypes0),
|
||||
{pos(Fun),
|
||||
io_lib:format("when checking the application of\n"
|
||||
" `~s`\n"
|
||||
"to arguments~s",
|
||||
[pp_typed("", Fun, Inferred),
|
||||
[ ["\n ", "`" ++ pp_expr(NamedArg) ++ "`"] || NamedArg <- NamedArgs ] ++
|
||||
[ ["\n ", "`" ++ pp_typed("", Arg, ArgT) ++ "`"]
|
||||
|| {Arg, ArgT} <- lists:zip(Args, ArgTypes) ] ])};
|
||||
pp_when({field_constraint, FieldType0, InferredType0, Fld}) ->
|
||||
FieldType = instantiate(FieldType0),
|
||||
InferredType = instantiate(InferredType0),
|
||||
{pos(Fld),
|
||||
case Fld of
|
||||
{var_args, _Ann, _Fun} ->
|
||||
io_lib:format("when checking contract construction of type\n~s (at ~s)\nagainst the expected type\n~s\n",
|
||||
[pp_type(" ", FieldType),
|
||||
pp_loc(Fld),
|
||||
pp_type(" ", InferredType)
|
||||
]);
|
||||
{field, _Ann, LV, Id, E} ->
|
||||
io_lib:format("when checking the assignment of the field `~s` to the old value `~s` and the new value `~s`",
|
||||
[pp_typed("", {lvalue, [], LV}, FieldType),
|
||||
pp(Id),
|
||||
pp_typed("", E, InferredType)]);
|
||||
{field, _Ann, LV, E} ->
|
||||
io_lib:format("when checking the assignment of the field `~s` to the value `~s`",
|
||||
[pp_typed("", {lvalue, [], LV}, FieldType),
|
||||
pp_typed("", E, InferredType)]);
|
||||
{proj, _Ann, _Rec, _Fld} ->
|
||||
io_lib:format("when checking the record projection `~s` against the expected type `~s`",
|
||||
[pp_typed(" ", Fld, FieldType),
|
||||
pp_type(" ", InferredType)])
|
||||
end};
|
||||
pp_when({record_constraint, RecType0, InferredType0, Fld}) ->
|
||||
RecType = instantiate(RecType0),
|
||||
InferredType = instantiate(InferredType0),
|
||||
{Pos, WhyRec} = pp_why_record(Fld),
|
||||
case Fld of
|
||||
{var_args, _Ann, _Fun} ->
|
||||
{Pos,
|
||||
io_lib:format("when checking that contract construction of type\n~s\n~s\n"
|
||||
"matches the expected type\n~s",
|
||||
[pp_type(" ", RecType), WhyRec, pp_type(" ", InferredType)]
|
||||
)
|
||||
};
|
||||
{field, _Ann, _LV, _Id, _E} ->
|
||||
{Pos,
|
||||
io_lib:format("when checking that the record type\n~s\n~s\n"
|
||||
"matches the expected type\n~s",
|
||||
[pp_type(" ", RecType), WhyRec, pp_type(" ", InferredType)])};
|
||||
{field, _Ann, _LV, _E} ->
|
||||
{Pos,
|
||||
io_lib:format("when checking that the record type\n~s\n~s\n"
|
||||
"matches the expected type\n~s",
|
||||
[pp_type(" ", RecType), WhyRec, pp_type(" ", InferredType)])};
|
||||
{proj, _Ann, Rec, _FldName} ->
|
||||
{pos(Rec),
|
||||
io_lib:format("when checking that the expression\n~s (at ~s)\nhas type\n~s\n~s",
|
||||
[pp_typed(" ", Rec, InferredType), pp_loc(Rec),
|
||||
pp_type(" ", RecType), WhyRec])}
|
||||
end;
|
||||
pp_when({if_branches, Then, ThenType0, Else, ElseType0}) ->
|
||||
{ThenType, ElseType} = instantiate({ThenType0, ElseType0}),
|
||||
Branches = [ {Then, ThenType} | [ {B, ElseType} || B <- if_branches(Else) ] ],
|
||||
{pos(element(1, hd(Branches))),
|
||||
io_lib:format("when comparing the types of the if-branches\n"
|
||||
"~s", [ [ io_lib:format("~s (at ~s)\n", [pp_typed(" - ", B, BType), pp_loc(B)])
|
||||
|| {B, BType} <- Branches ] ])};
|
||||
pp_when({case_pat, Pat, PatType0, ExprType0}) ->
|
||||
{PatType, ExprType} = instantiate({PatType0, ExprType0}),
|
||||
{pos(Pat),
|
||||
io_lib:format("when checking the type of the pattern `~s` against the expected type `~s`",
|
||||
[pp_typed("", Pat, PatType),
|
||||
pp_type(ExprType)])};
|
||||
pp_when({check_expr, Expr, Inferred0, Expected0}) ->
|
||||
{Inferred, Expected} = instantiate({Inferred0, Expected0}),
|
||||
{pos(Expr),
|
||||
io_lib:format("when checking the type of the expression `~s` against the expected type `~s`",
|
||||
[pp_typed("", Expr, Inferred), pp_type(Expected)])};
|
||||
pp_when({checking_init_type, Ann}) ->
|
||||
{pos(Ann),
|
||||
io_lib:format("when checking that `init` returns a value of type `state`", [])};
|
||||
pp_when({list_comp, BindExpr, Inferred0, Expected0}) ->
|
||||
{Inferred, Expected} = instantiate({Inferred0, Expected0}),
|
||||
{pos(BindExpr),
|
||||
io_lib:format("when checking rvalue of list comprehension binding `~s` against type `~s`",
|
||||
[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 `~s` against inferred type `~s`",
|
||||
[pp_typed("", Arg, Type), pp_type(C#named_argument_constraint.type)]),
|
||||
{pos(Arg), Err};
|
||||
pp_when({checking_init_args, Ann, Con0, ArgTypes0}) ->
|
||||
Con = instantiate(Con0),
|
||||
ArgTypes = instantiate(ArgTypes0),
|
||||
{pos(Ann),
|
||||
io_lib:format("when checking arguments of `~s`'s init entrypoint to match\n(~s)",
|
||||
[pp_type(Con), string:join([pp_type(A) || A <- ArgTypes], ", ")])
|
||||
};
|
||||
pp_when({return_contract, App, Con0}) ->
|
||||
Con = instantiate(Con0),
|
||||
{pos(App)
|
||||
, io_lib:format("when checking that expression returns contract of type `~s`", [pp_type(Con)])
|
||||
};
|
||||
pp_when({arg_name, Id1, Id2, When}) ->
|
||||
{Pos, Ctx} = pp_when(When),
|
||||
{Pos
|
||||
, io_lib:format("when unifying names of named arguments: `~s` and `~s`\n~s", [pp_expr(Id1), pp_expr(Id2), Ctx])
|
||||
};
|
||||
pp_when({var_args, Ann, Fun}) ->
|
||||
{pos(Ann)
|
||||
, io_lib:format("when resolving arguments of variadic function `~s`", [pp_expr(Fun)])
|
||||
};
|
||||
pp_when(unknown) -> {pos(0,0), ""}.
|
||||
|
||||
-spec pp_why_record(why_record()) -> {pos(), iolist()}.
|
||||
pp_why_record({var_args, Ann, Fun}) ->
|
||||
{pos(Ann),
|
||||
io_lib:format("arising from resolution of variadic function `~s`",
|
||||
[pp_expr(Fun)])};
|
||||
pp_why_record(Fld = {field, _Ann, LV, _E}) ->
|
||||
{pos(Fld),
|
||||
io_lib:format("arising from an assignment of the field `~s`",
|
||||
[pp_expr({lvalue, [], LV})])};
|
||||
pp_why_record(Fld = {field, _Ann, LV, _Alias, _E}) ->
|
||||
{pos(Fld),
|
||||
io_lib:format("arising from an assignment of the field `~s`",
|
||||
[pp_expr({lvalue, [], LV})])};
|
||||
pp_why_record({proj, _Ann, Rec, FldName}) ->
|
||||
{pos(Rec),
|
||||
io_lib:format("arising from the projection of the field `~s`",
|
||||
[pp(FldName)])}.
|
||||
|
||||
|
||||
if_branches(If = {'if', Ann, _, Then, Else}) ->
|
||||
case proplists:get_value(format, Ann) of
|
||||
elif -> [Then | if_branches(Else)];
|
||||
_ -> [If]
|
||||
end;
|
||||
if_branches(E) -> [E].
|
||||
|
||||
pp_typed(Label, E, T = {type_sig, _, _, _, _, _}) -> pp_typed(Label, E, typesig_to_fun_t(T));
|
||||
pp_typed(Label, {typed, _, Expr, _}, Type) ->
|
||||
pp_typed(Label, Expr, Type);
|
||||
pp_typed(Label, Expr, Type) ->
|
||||
pp_expr(Label, {typed, [], Expr, Type}).
|
||||
|
||||
pp_expr(Expr) ->
|
||||
pp_expr("", Expr).
|
||||
pp_expr(Label, Expr) ->
|
||||
prettypr:format(prettypr:beside(prettypr:text(Label), aeso_pretty:expr(Expr, [show_generated])), 80, 80).
|
||||
|
||||
pp_type(Type) ->
|
||||
pp_type("", Type).
|
||||
pp_type(Label, Type) ->
|
||||
prettypr:format(prettypr:beside(prettypr:text(Label), aeso_pretty:type(Type, [show_generated])), 80, 80).
|
||||
|
||||
src_file(T) -> aeso_syntax:get_ann(file, T, no_file).
|
||||
include_type(T) -> aeso_syntax:get_ann(include_type, T, none).
|
||||
line_number(T) -> aeso_syntax:get_ann(line, T, 0).
|
||||
column_number(T) -> aeso_syntax:get_ann(col, T, 0).
|
||||
|
||||
pos(T) -> aeso_errors:pos(src_file(T), line_number(T), column_number(T)).
|
||||
pos(L, C) -> aeso_errors:pos(L, C).
|
||||
|
||||
loc(T) ->
|
||||
{src_file(T), include_type(T), line_number(T), column_number(T)}.
|
||||
|
||||
pp_loc(T) ->
|
||||
{File, IncludeType, Line, Col} = loc(T),
|
||||
case {Line, Col} of
|
||||
{0, 0} -> "(builtin location)";
|
||||
_ -> case IncludeType of
|
||||
none -> io_lib:format("line ~p, column ~p", [Line, Col]);
|
||||
_ -> io_lib:format("line ~p, column ~p in ~s", [Line, Col, File])
|
||||
end
|
||||
end.
|
||||
|
||||
plural(No, _Yes, [_]) -> No;
|
||||
plural(_No, Yes, _) -> Yes.
|
||||
|
||||
pp(T = {type_sig, _, _, _, _, _}) ->
|
||||
pp(typesig_to_fun_t(T));
|
||||
pp([]) ->
|
||||
"";
|
||||
pp([T]) ->
|
||||
pp(T);
|
||||
pp([T|Ts]) ->
|
||||
[pp(T), ", "|pp(Ts)];
|
||||
pp({id, _, Name}) ->
|
||||
Name;
|
||||
pp({qid, _, Name}) ->
|
||||
string:join(Name, ".");
|
||||
pp({con, _, Name}) ->
|
||||
Name;
|
||||
pp({qcon, _, Name}) ->
|
||||
string:join(Name, ".");
|
||||
pp({uvar, _, Ref}) ->
|
||||
%% Show some unique representation
|
||||
["?u" | integer_to_list(erlang:phash2(Ref, 16384)) ];
|
||||
pp({tvar, _, Name}) ->
|
||||
Name;
|
||||
pp({if_t, _, Id, Then, Else}) ->
|
||||
["if(", pp([Id, Then, Else]), ")"];
|
||||
pp({tuple_t, _, []}) ->
|
||||
"unit";
|
||||
pp({tuple_t, _, Cpts}) ->
|
||||
["(", string:join(lists:map(fun pp/1, Cpts), " * "), ")"];
|
||||
pp({bytes_t, _, any}) -> "bytes(_)";
|
||||
pp({bytes_t, _, Len}) ->
|
||||
["bytes(", integer_to_list(Len), ")"];
|
||||
pp({app_t, _, T, []}) ->
|
||||
pp(T);
|
||||
pp({app_t, _, Type, Args}) ->
|
||||
[pp(Type), "(", pp(Args), ")"];
|
||||
pp({named_arg_t, _, Name, Type, _Default}) ->
|
||||
[pp(Name), " : ", pp(Type)];
|
||||
pp({fun_t, _, Named = {uvar, _, _}, As, B}) ->
|
||||
["(", pp(Named), " | ", pp(As), ") => ", pp(B)];
|
||||
pp({fun_t, _, Named, As, B}) when is_list(Named) ->
|
||||
["(", pp(Named ++ As), ") => ", pp(B)];
|
||||
pp(Other) ->
|
||||
io_lib:format("~p", [Other]).
|
||||
|
||||
|
||||
plural(No, _Yes, [_]) -> No;
|
||||
plural(_No, Yes, _) -> Yes.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,344 @@
|
||||
-module(aeso_ast_types_solve).
|
||||
|
||||
|
||||
|
||||
-spec solve_constraints(env()) -> ok.
|
||||
solve_constraints(Env) ->
|
||||
%% First look for record fields that appear in only one type definition
|
||||
IsAmbiguous =
|
||||
fun(#field_constraint{
|
||||
record_t = RecordType,
|
||||
field = Field={id, _Attrs, FieldName},
|
||||
field_t = FieldType,
|
||||
kind = Kind,
|
||||
context = When }) ->
|
||||
Arity = fun_arity(dereference_deep(FieldType)),
|
||||
FieldInfos = case Arity of
|
||||
none -> lookup_record_field(Env, FieldName, Kind);
|
||||
_ -> lookup_record_field_arity(Env, FieldName, Arity, Kind)
|
||||
end,
|
||||
case FieldInfos of
|
||||
[] ->
|
||||
type_error({undefined_field, Field}),
|
||||
false;
|
||||
[#field_info{field_t = FldType, record_t = RecType}] ->
|
||||
create_freshen_tvars(),
|
||||
FreshFldType = freshen(FldType),
|
||||
FreshRecType = freshen(RecType),
|
||||
destroy_freshen_tvars(),
|
||||
unify(Env, FreshFldType, FieldType, {field_constraint, FreshFldType, FieldType, When}),
|
||||
unify(Env, FreshRecType, RecordType, {record_constraint, FreshRecType, RecordType, When}),
|
||||
false;
|
||||
_ ->
|
||||
%% ambiguity--need cleverer strategy
|
||||
true
|
||||
end;
|
||||
(_) -> true
|
||||
end,
|
||||
AmbiguousConstraints = lists:filter(IsAmbiguous, get_constraints()),
|
||||
|
||||
% The two passes on AmbiguousConstraints are needed
|
||||
solve_ambiguous_constraints(Env, AmbiguousConstraints ++ AmbiguousConstraints).
|
||||
|
||||
-spec solve_ambiguous_constraints(env(), [constraint()]) -> ok.
|
||||
solve_ambiguous_constraints(Env, Constraints) ->
|
||||
Unknown = solve_known_record_types(Env, Constraints),
|
||||
if Unknown == [] -> ok;
|
||||
length(Unknown) < length(Constraints) ->
|
||||
%% progress! Keep trying.
|
||||
solve_ambiguous_constraints(Env, Unknown);
|
||||
true ->
|
||||
case solve_unknown_record_types(Env, Unknown) of
|
||||
true -> %% Progress!
|
||||
solve_ambiguous_constraints(Env, Unknown);
|
||||
_ -> ok %% No progress. Report errors later.
|
||||
end
|
||||
end.
|
||||
|
||||
solve_then_destroy_and_report_unsolved_constraints(Env) ->
|
||||
solve_constraints(Env),
|
||||
destroy_and_report_unsolved_constraints(Env).
|
||||
|
||||
destroy_and_report_unsolved_constraints(Env) ->
|
||||
{FieldCs, OtherCs} =
|
||||
lists:partition(fun(#field_constraint{}) -> true; (_) -> false end,
|
||||
get_constraints()),
|
||||
{CreateCs, OtherCs1} =
|
||||
lists:partition(fun(#record_create_constraint{}) -> true; (_) -> false end,
|
||||
OtherCs),
|
||||
{ContractCs, OtherCs2} =
|
||||
lists:partition(fun(#is_contract_constraint{}) -> true; (_) -> false end, OtherCs1),
|
||||
{NamedArgCs, OtherCs3} =
|
||||
lists:partition(fun(#dependent_type_constraint{}) -> true;
|
||||
(#named_argument_constraint{}) -> true;
|
||||
(_) -> false
|
||||
end, OtherCs2),
|
||||
{BytesCs, OtherCs4} =
|
||||
lists:partition(fun({is_bytes, _}) -> true;
|
||||
({add_bytes, _, _, _, _, _}) -> true;
|
||||
(_) -> false
|
||||
end, OtherCs3),
|
||||
{AensResolveCs, OtherCs5} =
|
||||
lists:partition(fun({aens_resolve_type, _}) -> true;
|
||||
(_) -> false
|
||||
end, OtherCs4),
|
||||
{OracleTypeCs, []} =
|
||||
lists:partition(fun({oracle_type, _, _}) -> true;
|
||||
(_) -> false
|
||||
end, OtherCs5),
|
||||
|
||||
Unsolved = [ S || S <- [ solve_constraint(Env, dereference_deep(C)) || C <- NamedArgCs ],
|
||||
S == unsolved ],
|
||||
[ type_error({unsolved_named_argument_constraint, C}) || C <- Unsolved ],
|
||||
|
||||
Unknown = solve_known_record_types(Env, FieldCs),
|
||||
if Unknown == [] -> ok;
|
||||
true ->
|
||||
case solve_unknown_record_types(Env, Unknown) of
|
||||
true -> ok;
|
||||
Errors -> [ type_error(Err) || Err <- Errors ]
|
||||
end
|
||||
end,
|
||||
|
||||
check_record_create_constraints(Env, CreateCs),
|
||||
check_is_contract_constraints(Env, ContractCs),
|
||||
check_bytes_constraints(Env, BytesCs),
|
||||
check_aens_resolve_constraints(Env, AensResolveCs),
|
||||
check_oracle_type_constraints(Env, OracleTypeCs),
|
||||
|
||||
destroy_constraints().
|
||||
|
||||
%% If false, a type error has been emitted, so it's safe to drop the constraint.
|
||||
-spec check_named_argument_constraint(env(), named_argument_constraint()) -> true | false | unsolved.
|
||||
check_named_argument_constraint(_Env, #named_argument_constraint{ args = {uvar, _, _} }) ->
|
||||
unsolved;
|
||||
check_named_argument_constraint(Env,
|
||||
C = #named_argument_constraint{ args = Args,
|
||||
name = Id = {id, _, Name},
|
||||
type = Type }) ->
|
||||
case [ T || {named_arg_t, _, {id, _, Name1}, T, _} <- Args, Name1 == Name ] of
|
||||
[] ->
|
||||
type_error({bad_named_argument, Args, Id}),
|
||||
false;
|
||||
[T] -> unify(Env, T, Type, {check_named_arg_constraint, C}), true
|
||||
end;
|
||||
check_named_argument_constraint(Env,
|
||||
#dependent_type_constraint{ named_args_t = NamedArgsT0,
|
||||
named_args = NamedArgs,
|
||||
general_type = GenType,
|
||||
specialized_type = SpecType,
|
||||
context = {check_return, App} }) ->
|
||||
NamedArgsT = dereference(NamedArgsT0),
|
||||
case dereference(NamedArgsT0) of
|
||||
[_ | _] = NamedArgsT ->
|
||||
GetVal = fun(Name, Default) ->
|
||||
hd([ Val || {named_arg, _, {id, _, N}, Val} <- NamedArgs, N == Name] ++
|
||||
[ Default ])
|
||||
end,
|
||||
ArgEnv = maps:from_list([ {Name, GetVal(Name, Default)}
|
||||
|| {named_arg_t, _, {id, _, Name}, _, Default} <- NamedArgsT ]),
|
||||
GenType1 = specialize_dependent_type(ArgEnv, GenType),
|
||||
unify(Env, GenType1, SpecType, {check_expr, App, GenType1, SpecType}),
|
||||
true;
|
||||
_ -> unify(Env, GenType, SpecType, {check_expr, App, GenType, SpecType}), true
|
||||
end.
|
||||
|
||||
solve_constraint(_Env, #field_constraint{record_t = {uvar, _, _}}) ->
|
||||
not_solved;
|
||||
solve_constraint(Env, C = #field_constraint{record_t = RecType,
|
||||
field = FieldName,
|
||||
field_t = FieldType,
|
||||
context = When}) ->
|
||||
RecId = record_type_name(RecType),
|
||||
Attrs = aeso_syntax:get_ann(RecId),
|
||||
case lookup_type(Env, RecId) of
|
||||
{_, {_Ann, {Formals, {What, Fields}}}} when What =:= record_t; What =:= contract_t ->
|
||||
FieldTypes = [{Name, Type} || {field_t, _, {id, _, Name}, Type} <- Fields],
|
||||
{id, _, FieldString} = FieldName,
|
||||
case proplists:get_value(FieldString, FieldTypes) of
|
||||
undefined ->
|
||||
type_error({missing_field, FieldName, RecId}),
|
||||
not_solved;
|
||||
FldType ->
|
||||
create_freshen_tvars(),
|
||||
FreshFldType = freshen(FldType),
|
||||
FreshRecType = freshen(app_t(Attrs, RecId, Formals)),
|
||||
destroy_freshen_tvars(),
|
||||
unify(Env, FreshFldType, FieldType, {field_constraint, FreshFldType, FieldType, When}),
|
||||
unify(Env, FreshRecType, RecType, {record_constraint, FreshRecType, RecType, When}),
|
||||
C
|
||||
end;
|
||||
_ ->
|
||||
type_error({not_a_record_type, instantiate(RecType), When}),
|
||||
not_solved
|
||||
end;
|
||||
solve_constraint(Env, C = #dependent_type_constraint{}) ->
|
||||
check_named_argument_constraint(Env, C);
|
||||
solve_constraint(Env, C = #named_argument_constraint{}) ->
|
||||
check_named_argument_constraint(Env, C);
|
||||
solve_constraint(_Env, {is_bytes, _}) -> ok;
|
||||
solve_constraint(Env, {add_bytes, Ann, _, A0, B0, C0}) ->
|
||||
A = unfold_types_in_type(Env, dereference(A0)),
|
||||
B = unfold_types_in_type(Env, dereference(B0)),
|
||||
C = unfold_types_in_type(Env, dereference(C0)),
|
||||
case {A, B, C} of
|
||||
{{bytes_t, _, M}, {bytes_t, _, N}, _} -> unify(Env, {bytes_t, Ann, M + N}, C, {at, Ann});
|
||||
{{bytes_t, _, M}, _, {bytes_t, _, R}} when R >= M -> unify(Env, {bytes_t, Ann, R - M}, B, {at, Ann});
|
||||
{_, {bytes_t, _, N}, {bytes_t, _, R}} when R >= N -> unify(Env, {bytes_t, Ann, R - N}, A, {at, Ann});
|
||||
_ -> ok
|
||||
end;
|
||||
solve_constraint(_, _) -> ok.
|
||||
|
||||
check_bytes_constraints(Env, Constraints) ->
|
||||
InAddConstraint = [ T || {add_bytes, _, _, A, B, C} <- Constraints,
|
||||
T <- [A, B, C],
|
||||
element(1, T) /= bytes_t ],
|
||||
%% Skip is_bytes constraints for types that occur in add_bytes constraints
|
||||
%% (no need to generate error messages for both is_bytes and add_bytes).
|
||||
Skip = fun({is_bytes, T}) -> lists:member(T, InAddConstraint);
|
||||
(_) -> false end,
|
||||
[ check_bytes_constraint(Env, C) || C <- Constraints, not Skip(C) ].
|
||||
|
||||
check_bytes_constraint(Env, {is_bytes, Type}) ->
|
||||
Type1 = unfold_types_in_type(Env, instantiate(Type)),
|
||||
case Type1 of
|
||||
{bytes_t, _, _} -> ok;
|
||||
_ ->
|
||||
type_error({unknown_byte_length, Type})
|
||||
end;
|
||||
check_bytes_constraint(Env, {add_bytes, Ann, Fun, A0, B0, C0}) ->
|
||||
A = unfold_types_in_type(Env, instantiate(A0)),
|
||||
B = unfold_types_in_type(Env, instantiate(B0)),
|
||||
C = unfold_types_in_type(Env, instantiate(C0)),
|
||||
case {A, B, C} of
|
||||
{{bytes_t, _, _M}, {bytes_t, _, _N}, {bytes_t, _, _R}} ->
|
||||
ok; %% If all are solved we checked M + N == R in solve_constraint.
|
||||
_ -> type_error({unsolved_bytes_constraint, Ann, Fun, A, B, C})
|
||||
end.
|
||||
|
||||
check_aens_resolve_constraints(_Env, []) ->
|
||||
ok;
|
||||
check_aens_resolve_constraints(Env, [{aens_resolve_type, Type} | Rest]) ->
|
||||
Type1 = unfold_types_in_type(Env, instantiate(Type)),
|
||||
{app_t, _, {id, _, "option"}, [Type2]} = Type1,
|
||||
case Type2 of
|
||||
{id, _, "string"} -> ok;
|
||||
{id, _, "address"} -> ok;
|
||||
{con, _, _} -> ok;
|
||||
{app_t, _, {id, _, "oracle"}, [_, _]} -> ok;
|
||||
{app_t, _, {id, _, "oracle_query"}, [_, _]} -> ok;
|
||||
_ -> type_error({invalid_aens_resolve_type, aeso_syntax:get_ann(Type), Type2})
|
||||
end,
|
||||
check_aens_resolve_constraints(Env, Rest).
|
||||
|
||||
check_oracle_type_constraints(_Env, []) ->
|
||||
ok;
|
||||
check_oracle_type_constraints(Env, [{oracle_type, Ann, OType} | Rest]) ->
|
||||
Type = unfold_types_in_type(Env, instantiate(OType)),
|
||||
{app_t, _, {id, _, "oracle"}, [QType, RType]} = Type,
|
||||
ensure_monomorphic(QType, {invalid_oracle_type, polymorphic, query, Ann, Type}),
|
||||
ensure_monomorphic(RType, {invalid_oracle_type, polymorphic, response, Ann, Type}),
|
||||
ensure_first_order(QType, {invalid_oracle_type, higher_order, query, Ann, Type}),
|
||||
ensure_first_order(RType, {invalid_oracle_type, higher_order, response, Ann, Type}),
|
||||
check_oracle_type_constraints(Env, Rest).
|
||||
|
||||
%% -- Field constraints --
|
||||
|
||||
check_record_create_constraints(_, []) -> ok;
|
||||
check_record_create_constraints(Env, [C | Cs]) ->
|
||||
#record_create_constraint{
|
||||
record_t = Type,
|
||||
fields = Fields,
|
||||
context = When } = C,
|
||||
Type1 = unfold_types_in_type(Env, instantiate(Type)),
|
||||
try lookup_type(Env, record_type_name(Type1)) of
|
||||
{_QId, {_Ann, {_Args, {record_t, RecFields}}}} ->
|
||||
ActualNames = [ Fld || {field_t, _, {id, _, Fld}, _} <- RecFields ],
|
||||
GivenNames = [ Fld || {id, _, Fld} <- Fields ],
|
||||
case ActualNames -- GivenNames of %% We know already that we don't have too many fields
|
||||
[] -> ok;
|
||||
Missing -> type_error({missing_fields, When, Type1, Missing})
|
||||
end;
|
||||
_ -> %% We can get here if there are other type errors.
|
||||
ok
|
||||
catch _:_ -> %% Might be unsolved, we get a different error in that case
|
||||
ok
|
||||
end,
|
||||
check_record_create_constraints(Env, Cs).
|
||||
|
||||
check_is_contract_constraints(_Env, []) -> ok;
|
||||
check_is_contract_constraints(Env, [C | Cs]) ->
|
||||
#is_contract_constraint{ contract_t = Type, context = Cxt, force_def = ForceDef } = C,
|
||||
Type1 = unfold_types_in_type(Env, instantiate(Type)),
|
||||
TypeName = record_type_name(Type1),
|
||||
case lookup_type(Env, TypeName) of
|
||||
{_, {_Ann, {[], {contract_t, _}}}} ->
|
||||
case not ForceDef orelse is_contract_defined(TypeName) of
|
||||
true -> ok;
|
||||
false -> type_error({contract_lacks_definition, Type1, Cxt})
|
||||
end;
|
||||
_ -> type_error({not_a_contract_type, Type1, Cxt})
|
||||
end,
|
||||
check_is_contract_constraints(Env, Cs).
|
||||
|
||||
-spec solve_unknown_record_types(env(), [field_constraint()]) -> true | [tuple()].
|
||||
solve_unknown_record_types(Env, Unknown) ->
|
||||
UVars = lists:usort([UVar || #field_constraint{record_t = UVar = {uvar, _, _}} <- Unknown]),
|
||||
Solutions = [solve_for_uvar(Env, UVar, [{Kind, When, Field}
|
||||
|| #field_constraint{record_t = U, field = Field, kind = Kind, context = When} <- Unknown,
|
||||
U == UVar])
|
||||
|| UVar <- UVars],
|
||||
case lists:member(true, Solutions) of
|
||||
true -> true;
|
||||
false -> Solutions
|
||||
end.
|
||||
|
||||
%% This will solve all kinds of constraints but will only return the
|
||||
%% unsolved field constraints
|
||||
-spec solve_known_record_types(env(), [constraint()]) -> [field_constraint()].
|
||||
solve_known_record_types(Env, Constraints) ->
|
||||
DerefConstraints = lists:map(fun(C = #field_constraint{record_t = RecordType}) ->
|
||||
C#field_constraint{record_t = dereference(RecordType)};
|
||||
(C) -> dereference_deep(C)
|
||||
end, Constraints),
|
||||
SolvedConstraints = lists:map(fun(C) -> solve_constraint(Env, dereference_deep(C)) end, DerefConstraints),
|
||||
Unsolved = DerefConstraints--SolvedConstraints,
|
||||
lists:filter(fun(#field_constraint{}) -> true; (_) -> false end, Unsolved).
|
||||
|
||||
solve_for_uvar(Env, UVar = {uvar, Attrs, _}, Fields0) ->
|
||||
Fields = [{Kind, Fld} || {Kind, _, Fld} <- Fields0],
|
||||
[{_, When, _} | _] = Fields0, %% Get the location from the first field
|
||||
%% If we have 'create' constraints they must be complete.
|
||||
Covering = lists:usort([ Name || {create, {id, _, Name}} <- Fields ]),
|
||||
%% Does this set of fields uniquely identify a record type?
|
||||
FieldNames = [ Name || {_Kind, {id, _, Name}} <- Fields ],
|
||||
UniqueFields = lists:usort(FieldNames),
|
||||
Candidates = [RecType || #field_info{record_t = RecType} <- lookup_record_field(Env, hd(FieldNames))],
|
||||
TypesAndFields = [case lookup_type(Env, record_type_name(RecType)) of
|
||||
{_, {_, {_, {record_t, RecFields}}}} ->
|
||||
{RecType, [Field || {field_t, _, {id, _, Field}, _} <- RecFields]};
|
||||
{_, {_, {_, {contract_t, ConFields}}}} ->
|
||||
%% TODO: is this right?
|
||||
{RecType, [Field || {field_t, _, {id, _, Field}, _} <- ConFields]};
|
||||
false -> %% impossible?
|
||||
error({no_definition_for, record_type_name(RecType), in, Env})
|
||||
end
|
||||
|| RecType <- Candidates],
|
||||
PartialSolutions =
|
||||
lists:sort([{RecType, if Covering == [] -> []; true -> RecFields -- Covering end}
|
||||
|| {RecType, RecFields} <- TypesAndFields,
|
||||
UniqueFields -- RecFields == []]),
|
||||
Solutions = [RecName || {RecName, []} <- PartialSolutions],
|
||||
|
||||
|
||||
apply_typesig_constraint(_Ann, none, _FunT) -> ok;
|
||||
apply_typesig_constraint(Ann, address_to_contract, {fun_t, _, [], [_], Type}) ->
|
||||
add_constraint([#is_contract_constraint{ contract_t = Type,
|
||||
context = {address_to_contract, Ann}}]);
|
||||
apply_typesig_constraint(Ann, bytes_concat, {fun_t, _, [], [A, B], C}) ->
|
||||
add_constraint({add_bytes, Ann, concat, A, B, C});
|
||||
apply_typesig_constraint(Ann, bytes_split, {fun_t, _, [], [C], {tuple_t, _, [A, B]}}) ->
|
||||
add_constraint({add_bytes, Ann, split, A, B, C});
|
||||
apply_typesig_constraint(Ann, bytecode_hash, {fun_t, _, _, [Con], _}) ->
|
||||
add_constraint([#is_contract_constraint{ contract_t = Con,
|
||||
context = {bytecode_hash, Ann} }]).
|
||||
+75
-129
@@ -12,8 +12,6 @@
|
||||
, file/2
|
||||
, from_string/2
|
||||
, check_call/4
|
||||
, decode_value/4
|
||||
, encode_value/4
|
||||
, create_calldata/3
|
||||
, create_calldata/4
|
||||
, version/0
|
||||
@@ -27,22 +25,20 @@
|
||||
, validate_byte_code/3
|
||||
]).
|
||||
|
||||
-include_lib("gmbytecode/include/gmb_opcodes.hrl").
|
||||
-include_lib("aebytecode/include/aeb_opcodes.hrl").
|
||||
-include("aeso_utils.hrl").
|
||||
|
||||
|
||||
-type option() :: pp_sophia_code
|
||||
| pp_ast
|
||||
-type option() :: pp_ast
|
||||
| pp_types
|
||||
| pp_typed_ast
|
||||
| pp_assembler
|
||||
| pp_fate
|
||||
| no_code
|
||||
| keep_included
|
||||
| debug_mode
|
||||
| {include, {file_system, [string()]} |
|
||||
{explicit_files, #{string() => binary()}}}
|
||||
| {src_file, string()}
|
||||
| {src_dir, string()}
|
||||
| {aci, aeso_aci:aci_type()}.
|
||||
-type options() :: [option()].
|
||||
|
||||
@@ -52,15 +48,15 @@
|
||||
|
||||
-spec version() -> {ok, binary()} | {error, term()}.
|
||||
version() ->
|
||||
case lists:keyfind(sophia, 1, application:loaded_applications()) of
|
||||
case lists:keyfind(aesophia, 1, application:loaded_applications()) of
|
||||
false ->
|
||||
case application:load(sophia) of
|
||||
case application:load(aesophia) of
|
||||
ok ->
|
||||
case application:get_key(sophia, vsn) of
|
||||
case application:get_key(aesophia, vsn) of
|
||||
{ok, VsnString} ->
|
||||
{ok, list_to_binary(VsnString)};
|
||||
undefined ->
|
||||
{error, failed_to_load_sophia}
|
||||
{error, failed_to_load_aesophia}
|
||||
end;
|
||||
Err = {error, _} ->
|
||||
Err
|
||||
@@ -88,9 +84,7 @@ file(Filename) ->
|
||||
file(File, Options0) ->
|
||||
Options = add_include_path(File, Options0),
|
||||
case read_contract(File) of
|
||||
{ok, Bin} ->
|
||||
SrcDir = aeso_utils:canonical_dir(filename:dirname(File)),
|
||||
from_string(Bin, [{src_file, File}, {src_dir, SrcDir} | Options]);
|
||||
{ok, Bin} -> from_string(Bin, [{src_file, File} | Options]);
|
||||
{error, Error} ->
|
||||
Msg = lists:flatten([File,": ",file:format_error(Error)]),
|
||||
{error, [aeso_errors:new(file_error, Msg)]}
|
||||
@@ -102,7 +96,7 @@ add_include_path(File, Options) ->
|
||||
false ->
|
||||
Dir = filename:dirname(File),
|
||||
{ok, Cwd} = file:get_cwd(),
|
||||
[{include, {file_system, [Cwd, aeso_utils:canonical_dir(Dir)]}} | Options]
|
||||
[{include, {file_system, [Cwd, Dir]}} | Options]
|
||||
end.
|
||||
|
||||
-spec from_string(binary() | string(), options()) -> {ok, map()} | {error, [aeso_errors:error()]}.
|
||||
@@ -122,20 +116,26 @@ from_string1(ContractString, Options) ->
|
||||
, warnings := Warnings } = string_to_code(ContractString, Options),
|
||||
#{ child_con_env := ChildContracts } = FCodeEnv,
|
||||
SavedFreshNames = maps:get(saved_fresh_names, FCodeEnv, #{}),
|
||||
FateCode = aeso_fcode_to_fate:compile(ChildContracts, FCode, SavedFreshNames, Options),
|
||||
pp_assembler(FateCode, Options),
|
||||
ByteCode = gmb_fate_code:serialize(FateCode, []),
|
||||
{FateCode, VarsRegs} = aeso_fcode_to_fate:compile(ChildContracts, FCode, SavedFreshNames, Options),
|
||||
pp_fate(FateCode, Options),
|
||||
ByteCode = aeb_fate_code:serialize(FateCode, []),
|
||||
{ok, Version} = version(),
|
||||
Res = #{byte_code => ByteCode,
|
||||
compiler_version => Version,
|
||||
contract_source => ContractString,
|
||||
type_info => [],
|
||||
fate_code => FateCode,
|
||||
abi_version => gmb_fate_abi:abi_version(),
|
||||
abi_version => aeb_fate_abi:abi_version(),
|
||||
payable => maps:get(payable, FCode),
|
||||
warnings => Warnings
|
||||
},
|
||||
{ok, maybe_generate_aci(Res, FoldedTypedAst, Options)}.
|
||||
ResDbg = Res#{variables_registers => VarsRegs},
|
||||
FinalRes =
|
||||
case proplists:get_value(debug_info, Options, false) of
|
||||
true -> ResDbg;
|
||||
false -> Res
|
||||
end,
|
||||
{ok, maybe_generate_aci(FinalRes, FoldedTypedAst, Options)}.
|
||||
|
||||
maybe_generate_aci(Result, FoldedTypedAst, Options) ->
|
||||
case proplists:get_value(aci, Options) of
|
||||
@@ -149,7 +149,6 @@ maybe_generate_aci(Result, FoldedTypedAst, Options) ->
|
||||
-spec string_to_code(string(), options()) -> map().
|
||||
string_to_code(ContractString, Options) ->
|
||||
Ast = parse(ContractString, Options),
|
||||
pp_sophia_code(Ast, Options),
|
||||
pp_ast(Ast, Options),
|
||||
{TypeEnv, FoldedTypedAst, UnfoldedTypedAst, Warnings} = aeso_ast_infer_types:infer(Ast, [return_env | Options]),
|
||||
pp_typed_ast(UnfoldedTypedAst, Options),
|
||||
@@ -187,63 +186,35 @@ check_call(Source, FunName, Args, Options) ->
|
||||
check_call1(Source, FunName, Args, Options).
|
||||
|
||||
check_call1(ContractString0, FunName, Args, Options) ->
|
||||
case add_extra_call(ContractString0, {call, FunName, Args}, Options) of
|
||||
{ok, CallName, Code} ->
|
||||
{def, _, _, FcodeArgs} = get_call_body(CallName, Code),
|
||||
{ok, FunName, [ aeso_fcode_to_fate:term_to_fate(A) || A <- FcodeArgs ]};
|
||||
Err = {error, _} ->
|
||||
Err
|
||||
end.
|
||||
|
||||
add_extra_call(Contract0, Call, Options) ->
|
||||
try
|
||||
%% First check the contract without the __call function
|
||||
#{fcode := OrgFcode
|
||||
, fcode_env := #{child_con_env := ChildContracts}
|
||||
, ast := Ast} = string_to_code(Contract0, Options),
|
||||
FateCode = aeso_fcode_to_fate:compile(ChildContracts, OrgFcode, #{}, []),
|
||||
, ast := Ast} = string_to_code(ContractString0, Options),
|
||||
{FateCode, _} = aeso_fcode_to_fate:compile(ChildContracts, OrgFcode, #{}, []),
|
||||
%% collect all hashes and compute the first name without hash collision to
|
||||
SymbolHashes = maps:keys(gmb_fate_code:symbols(FateCode)),
|
||||
SymbolHashes = maps:keys(aeb_fate_code:symbols(FateCode)),
|
||||
CallName = first_none_match(?CALL_NAME, SymbolHashes,
|
||||
lists:seq($1, $9) ++ lists:seq($A, $Z) ++ lists:seq($a, $z)),
|
||||
Contract = insert_call_function(Ast, Contract0, CallName, Call),
|
||||
{ok, CallName, string_to_code(Contract, Options)}
|
||||
ContractString = insert_call_function(Ast, ContractString0, CallName, FunName, Args),
|
||||
#{fcode := Fcode} = string_to_code(ContractString, Options),
|
||||
CallArgs = arguments_of_body(CallName, FunName, Fcode),
|
||||
|
||||
{ok, FunName, CallArgs}
|
||||
catch
|
||||
throw:{error, Errors} -> {error, Errors}
|
||||
end.
|
||||
|
||||
get_call_body(CallName, #{fcode := Fcode}) ->
|
||||
arguments_of_body(CallName, _FunName, Fcode) ->
|
||||
#{body := Body} = maps:get({entrypoint, list_to_binary(CallName)}, maps:get(functions, Fcode)),
|
||||
Body.
|
||||
|
||||
encode_value(Contract0, Type, Value, Options) ->
|
||||
case add_extra_call(Contract0, {value, Type, Value}, Options) of
|
||||
{ok, CallName, Code} ->
|
||||
Body = get_call_body(CallName, Code),
|
||||
{ok, gmb_fate_encoding:serialize(aeso_fcode_to_fate:term_to_fate(Body))};
|
||||
Err = {error, _} ->
|
||||
Err
|
||||
end.
|
||||
|
||||
decode_value(Contract0, Type, FateValue, Options) ->
|
||||
case add_extra_call(Contract0, {type, Type}, Options) of
|
||||
{ok, CallName, Code} ->
|
||||
#{ folded_typed_ast := TypedAst
|
||||
, type_env := TypeEnv} = Code,
|
||||
{ok, _, Type0} = get_decode_type(CallName, TypedAst),
|
||||
Type1 = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0,
|
||||
[ unfold_record_types
|
||||
, unfold_variant_types
|
||||
, not_unfold_system_alias_types ]),
|
||||
fate_data_to_sophia_value(Type0, Type1, FateValue);
|
||||
Err = {error, _} ->
|
||||
Err
|
||||
end.
|
||||
{def, _FName, Args} = Body,
|
||||
%% FName is either {entrypoint, list_to_binary(FunName)} or 'init'
|
||||
[ aeso_fcode_to_fate:term_to_fate(A) || A <- Args ].
|
||||
|
||||
first_none_match(_CallName, _Hashes, []) ->
|
||||
error(unable_to_find_unique_call_name);
|
||||
first_none_match(CallName, Hashes, [Char|Chars]) ->
|
||||
case not lists:member(gmb_fate_code:symbol_identifier(list_to_binary(CallName)), Hashes) of
|
||||
case not lists:member(aeb_fate_code:symbol_identifier(list_to_binary(CallName)), Hashes) of
|
||||
true ->
|
||||
CallName;
|
||||
false ->
|
||||
@@ -251,31 +222,14 @@ first_none_match(CallName, Hashes, [Char|Chars]) ->
|
||||
end.
|
||||
|
||||
%% Add the __call function to a contract.
|
||||
-spec insert_call_function(aeso_syntax:ast(), string(), string(),
|
||||
{call, string(), [string()]} | {value, string(), string()} | {type, string()}) -> string().
|
||||
insert_call_function(Ast, Code, Call, {call, FunName, Args}) ->
|
||||
-spec insert_call_function(aeso_syntax:ast(), string(), string(), string(), [string()]) -> string().
|
||||
insert_call_function(Ast, Code, Call, FunName, Args) ->
|
||||
Ind = last_contract_indent(Ast),
|
||||
lists:flatten(
|
||||
[ Code,
|
||||
"\n\n",
|
||||
lists:duplicate(Ind, " "),
|
||||
"stateful entrypoint ", Call, "() = ", FunName, "(", string:join(Args, ","), ")\n"
|
||||
]);
|
||||
insert_call_function(Ast, Code, Call, {value, Type, Value}) ->
|
||||
Ind = last_contract_indent(Ast),
|
||||
lists:flatten(
|
||||
[ Code,
|
||||
"\n\n",
|
||||
lists:duplicate(Ind, " "),
|
||||
"entrypoint ", Call, "() : ", Type, " = ", Value, "\n"
|
||||
]);
|
||||
insert_call_function(Ast, Code, Call, {type, Type}) ->
|
||||
Ind = last_contract_indent(Ast),
|
||||
lists:flatten(
|
||||
[ Code,
|
||||
"\n\n",
|
||||
lists:duplicate(Ind, " "),
|
||||
"entrypoint ", Call, "(val : ", Type, ") : ", Type, " = val\n"
|
||||
]).
|
||||
|
||||
-spec insert_init_function(string(), options()) -> string().
|
||||
@@ -303,7 +257,7 @@ to_sophia_value(ContractString, Fun, ResType, Data) ->
|
||||
to_sophia_value(_, _, error, Err, _Options) ->
|
||||
{ok, {app, [], {id, [], "error"}, [{string, [], Err}]}};
|
||||
to_sophia_value(_, _, revert, Data, _Options) ->
|
||||
try aeso_vm_decode:from_fate({id, [], "string"}, gmb_fate_encoding:deserialize(Data)) of
|
||||
try aeso_vm_decode:from_fate({id, [], "string"}, aeb_fate_encoding:deserialize(Data)) of
|
||||
Err ->
|
||||
{ok, {app, [], {id, [], "abort"}, [Err]}}
|
||||
catch _:_ ->
|
||||
@@ -314,32 +268,26 @@ to_sophia_value(ContractString, FunName, ok, Data, Options0) ->
|
||||
Options = [no_code | Options0],
|
||||
try
|
||||
Code = string_to_code(ContractString, Options),
|
||||
#{ folded_typed_ast := TypedAst, type_env := TypeEnv} = Code,
|
||||
#{ unfolded_typed_ast := TypedAst, type_env := TypeEnv} = Code,
|
||||
{ok, _, Type0} = get_decode_type(FunName, TypedAst),
|
||||
Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0,
|
||||
[ unfold_record_types
|
||||
, unfold_variant_types
|
||||
, not_unfold_system_alias_types]),
|
||||
Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]),
|
||||
|
||||
fate_data_to_sophia_value(Type0, Type, Data)
|
||||
try
|
||||
{ok, aeso_vm_decode:from_fate(Type, aeb_fate_encoding:deserialize(Data))}
|
||||
catch throw:cannot_translate_to_sophia ->
|
||||
Type1 = prettypr:format(aeso_pretty:type(Type0)),
|
||||
Msg = io_lib:format("Cannot translate FATE value ~p\n of Sophia type ~s",
|
||||
[aeb_fate_encoding:deserialize(Data), Type1]),
|
||||
{error, [aeso_errors:new(data_error, Msg)]};
|
||||
_:_ ->
|
||||
Type1 = prettypr:format(aeso_pretty:type(Type0)),
|
||||
Msg = io_lib:format("Failed to decode binary as type ~s", [Type1]),
|
||||
{error, [aeso_errors:new(data_error, Msg)]}
|
||||
end
|
||||
catch
|
||||
throw:{error, Errors} -> {error, Errors}
|
||||
end.
|
||||
|
||||
fate_data_to_sophia_value(Type, UnfoldedType, FateData) ->
|
||||
try
|
||||
{ok, aeso_vm_decode:from_fate(UnfoldedType, gmb_fate_encoding:deserialize(FateData))}
|
||||
catch throw:cannot_translate_to_sophia ->
|
||||
Type1 = prettypr:format(aeso_pretty:type(Type)),
|
||||
Msg = io_lib:format("Cannot translate FATE value ~p\n of Sophia type ~s",
|
||||
[gmb_fate_encoding:deserialize(FateData), Type1]),
|
||||
{error, [aeso_errors:new(data_error, Msg)]};
|
||||
_:_ ->
|
||||
Type1 = prettypr:format(aeso_pretty:type(Type)),
|
||||
Msg = io_lib:format("Failed to decode binary as type ~s", [Type1]),
|
||||
{error, [aeso_errors:new(data_error, Msg)]}
|
||||
end.
|
||||
|
||||
-spec create_calldata(string(), string(), [string()]) ->
|
||||
{ok, binary()} | {error, [aeso_errors:error()]}.
|
||||
create_calldata(Code, Fun, Args) ->
|
||||
@@ -350,7 +298,7 @@ create_calldata(Code, Fun, Args, Options0) ->
|
||||
Options = [no_code | Options0],
|
||||
case check_call(Code, Fun, Args, Options) of
|
||||
{ok, FunName, FateArgs} ->
|
||||
gmb_fate_abi:create_calldata(FunName, FateArgs);
|
||||
aeb_fate_abi:create_calldata(FunName, FateArgs);
|
||||
{error, _} = Err -> Err
|
||||
end.
|
||||
|
||||
@@ -366,18 +314,15 @@ decode_calldata(ContractString, FunName, Calldata, Options0) ->
|
||||
Options = [no_code | Options0],
|
||||
try
|
||||
Code = string_to_code(ContractString, Options),
|
||||
#{ folded_typed_ast := TypedAst, type_env := TypeEnv} = Code,
|
||||
#{ unfolded_typed_ast := TypedAst, type_env := TypeEnv} = Code,
|
||||
|
||||
{ok, Args, _} = get_decode_type(FunName, TypedAst),
|
||||
GetType = fun({typed, _, _, T}) -> T; (T) -> T end,
|
||||
ArgTypes = lists:map(GetType, Args),
|
||||
Type0 = {tuple_t, [], ArgTypes},
|
||||
%% user defined data types such as variants needed to match against
|
||||
Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0,
|
||||
[ unfold_record_types
|
||||
, unfold_variant_types
|
||||
, not_unfold_system_alias_types]),
|
||||
case gmb_fate_abi:decode_calldata(FunName, Calldata) of
|
||||
Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]),
|
||||
case aeb_fate_abi:decode_calldata(FunName, Calldata) of
|
||||
{ok, FateArgs} ->
|
||||
try
|
||||
{tuple_t, [], ArgTypes1} = Type,
|
||||
@@ -418,21 +363,22 @@ get_decode_type(FunName, [_ | Contracts]) ->
|
||||
%% The __decode should be in the final contract
|
||||
get_decode_type(FunName, Contracts).
|
||||
|
||||
pp_sophia_code(C, Opts)-> pp(C, Opts, pp_sophia_code, fun(Code) ->
|
||||
io:format("~s\n", [prettypr:format(aeso_pretty:decls(Code))])
|
||||
end).
|
||||
pp_ast(C, Opts) -> pp(C, Opts, pp_ast, fun aeso_ast:pp/1).
|
||||
pp_typed_ast(C, Opts)-> pp(C, Opts, pp_typed_ast, fun aeso_ast:pp_typed/1).
|
||||
pp_ast(C, Opts) ->
|
||||
[ io:format("AST:\n~s\n",
|
||||
[prettypr:format(aeso_pretty:decls(Ast, []))])
|
||||
|| true <- proplists:get_value(pp_ast, Opts)
|
||||
].
|
||||
|
||||
pp_assembler(C, Opts) -> pp(C, Opts, pp_assembler, fun(Asm) -> io:format("~s", [gmb_fate_asm:pp(Asm)]) end).
|
||||
pp_typed_ast(C, Opts) ->
|
||||
[ io:format("Typed AST:\n~s\n",
|
||||
[prettypr:format(aeso_pretty:decls(Ast, [show_generated]))])
|
||||
|| true <- proplists:get_value(pp_typed_ast, Opts)
|
||||
].
|
||||
|
||||
pp(Code, Options, Option, PPFun) ->
|
||||
case proplists:lookup(Option, Options) of
|
||||
{Option1, true} when Option1 =:= Option ->
|
||||
PPFun(Code);
|
||||
none ->
|
||||
ok
|
||||
end.
|
||||
pp_fate(C, Opts) ->
|
||||
[ io:format("FATE:\n~s\n", [aeb_fate_asm:pp(Asm)])
|
||||
|| true <- proplists:get_value(pp_fate, Opts)
|
||||
].
|
||||
|
||||
%% -- Byte code validation ---------------------------------------------------
|
||||
|
||||
@@ -442,14 +388,14 @@ pp(Code, Options, Option, PPFun) ->
|
||||
validate_byte_code(#{ byte_code := ByteCode, payable := Payable }, Source, Options) ->
|
||||
Fail = fun(Err) -> {error, [aeso_errors:new(data_error, Err)]} end,
|
||||
try
|
||||
FCode1 = ?protect(deserialize, gmb_fate_code:strip_init_function(gmb_fate_code:deserialize(ByteCode))),
|
||||
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(Source, Options),
|
||||
FCode = gmb_fate_code:deserialize(SrcByteCode),
|
||||
{gmb_fate_code:strip_init_function(FCode), SrcPayable}
|
||||
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 ->
|
||||
@@ -465,10 +411,10 @@ validate_byte_code(#{ byte_code := ByteCode, payable := Payable }, Source, Optio
|
||||
end.
|
||||
|
||||
compare_fate_code(FCode1, FCode2) ->
|
||||
Funs1 = gmb_fate_code:functions(FCode1),
|
||||
Funs2 = gmb_fate_code:functions(FCode2),
|
||||
Syms1 = gmb_fate_code:symbols(FCode1),
|
||||
Syms2 = gmb_fate_code:symbols(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
|
||||
|
||||
+310
-430
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,7 @@
|
||||
many/1, many1/1, sep/2, sep1/2,
|
||||
infixl/2, infixr/2]).
|
||||
|
||||
-export([current_file/0, set_current_file/1, current_dir/0, set_current_dir/1,
|
||||
-export([current_file/0, set_current_file/1,
|
||||
current_include_type/0, set_current_include_type/1]).
|
||||
|
||||
%% -- Types ------------------------------------------------------------------
|
||||
@@ -480,13 +480,6 @@ current_file() ->
|
||||
set_current_file(File) ->
|
||||
put('$current_file', File).
|
||||
|
||||
%% Current source directory
|
||||
current_dir() ->
|
||||
get('$current_dir').
|
||||
|
||||
set_current_dir(File) ->
|
||||
put('$current_dir', File).
|
||||
|
||||
add_current_file({L, C}) -> {current_file(), L, C};
|
||||
add_current_file(Pos) -> Pos.
|
||||
|
||||
|
||||
+32
-173
@@ -8,23 +8,17 @@
|
||||
-export([string/1,
|
||||
string/2,
|
||||
string/3,
|
||||
auto_imports/1,
|
||||
hash_include/2,
|
||||
decl/0,
|
||||
type/0,
|
||||
body/0,
|
||||
maybe_block/1,
|
||||
run_parser/2,
|
||||
run_parser/3]).
|
||||
run_parser/2]).
|
||||
|
||||
-include("aeso_parse_lib.hrl").
|
||||
-import(aeso_parse_lib, [current_file/0, set_current_file/1,
|
||||
current_dir/0, set_current_dir/1,
|
||||
current_include_type/0, set_current_include_type/1]).
|
||||
|
||||
-type parse_result() :: aeso_syntax:ast() | {aeso_syntax:ast(), sets:set(include_hash())} | none().
|
||||
|
||||
-type include_hash() :: {string(), binary()}.
|
||||
-type parse_result() :: aeso_syntax:ast() | none().
|
||||
|
||||
|
||||
escape_errors({ok, Ok}) ->
|
||||
@@ -32,34 +26,22 @@ escape_errors({ok, Ok}) ->
|
||||
escape_errors({error, Err}) ->
|
||||
parse_error(Err).
|
||||
|
||||
-spec string(string()) -> parse_result().
|
||||
string(String) ->
|
||||
string(String, sets:new(), []).
|
||||
-spec module(string()) -> parse_result().
|
||||
module(String) ->
|
||||
module(String, []).
|
||||
|
||||
-spec string(string(), aeso_compiler:options()) -> parse_result().
|
||||
string(String, Opts) ->
|
||||
case lists:keyfind(src_file, 1, Opts) of
|
||||
{src_file, File} -> string(String, sets:add_element(File, sets:new()), Opts);
|
||||
false -> string(String, sets:new(), Opts)
|
||||
end.
|
||||
|
||||
-spec string(string(), sets:set(include_hash()), aeso_compiler:options()) -> parse_result().
|
||||
string(String, Included, Opts) ->
|
||||
-spec module(string(), aeso_compiler:options()) -> parse_result().
|
||||
module(String, Opts) ->
|
||||
AST = run_parser(file(), String, Opts),
|
||||
case expand_includes(AST, Included, Opts) of
|
||||
{ok, AST1} -> AST1;
|
||||
{error, Err} -> parse_error(Err)
|
||||
end.
|
||||
|
||||
add_auto_imports(AST).
|
||||
|
||||
run_parser(P, Inp) ->
|
||||
escape_errors(parse_and_scan(P, Inp, [])).
|
||||
escape_errors(scan_and_parse(P, Inp, [])).
|
||||
run_parser(P, Inp, Opts) ->
|
||||
escape_errors(parse_and_scan(P, Inp, Opts)).
|
||||
escape_errors(scan_and_parse(P, Inp, Opts)).
|
||||
|
||||
parse_and_scan(P, S, Opts) ->
|
||||
scan_and_parse(P, S, Opts) ->
|
||||
set_current_file(proplists:get_value(src_file, Opts, no_file)),
|
||||
set_current_dir(proplists:get_value(src_dir, Opts, no_file)),
|
||||
set_current_include_type(proplists:get_value(include_type, Opts, none)),
|
||||
case aeso_scan:scan(S) of
|
||||
{ok, Tokens} -> aeso_parse_lib:parse(P, Tokens);
|
||||
@@ -266,11 +248,10 @@ type300() ->
|
||||
type400() ->
|
||||
choice(
|
||||
[?RULE(typeAtom(), optional(type_args()),
|
||||
any_bytes(
|
||||
case _2 of
|
||||
none -> _1;
|
||||
{ok, Args} -> {app_t, get_ann(_1), _1, Args}
|
||||
end)),
|
||||
case _2 of
|
||||
none -> _1;
|
||||
{ok, Args} -> {app_t, get_ann(_1), _1, Args}
|
||||
end),
|
||||
?RULE(id("bytes"), parens(token(int)),
|
||||
{bytes_t, get_ann(_1), element(3, _2)})
|
||||
]).
|
||||
@@ -302,7 +283,7 @@ stmt() ->
|
||||
, {switch, keyword(switch), parens(expr()), maybe_block(branch())}
|
||||
, {'if', keyword('if'), parens(expr()), body()}
|
||||
, {elif, keyword(elif), parens(expr()), body()}
|
||||
, {'else', keyword('else'), body()}
|
||||
, {else, keyword(else), body()}
|
||||
])).
|
||||
|
||||
branch() ->
|
||||
@@ -326,7 +307,7 @@ expr100() ->
|
||||
Expr150 = ?LAZY_P(expr150()),
|
||||
choice(
|
||||
[ ?RULE(lam_args(), keyword('=>'), body(), {lam, _2, _1, _3}) %% TODO: better location
|
||||
, {'if', keyword('if'), parens(Expr100), Expr150, right(tok('else'), Expr100)}
|
||||
, {'if', keyword('if'), parens(Expr100), Expr150, right(tok(else), Expr100)}
|
||||
, ?RULE(Expr150, optional(right(tok(':'), type())),
|
||||
case _2 of
|
||||
none -> _1;
|
||||
@@ -336,19 +317,14 @@ expr100() ->
|
||||
|
||||
expr150() -> infixl(expr200(), binop('|>')).
|
||||
expr200() -> infixr(expr300(), binop('||')).
|
||||
expr300() -> infixr(expr325(), binop('&&')).
|
||||
expr325() -> infixl(expr350(), binop('bor')).
|
||||
expr350() -> infixl(expr375(), binop('bxor')).
|
||||
expr375() -> infixl(expr400(), binop('band')).
|
||||
expr300() -> infixr(expr400(), binop('&&')).
|
||||
expr400() -> infix(expr500(), binop(['<', '>', '=<', '>=', '==', '!='])).
|
||||
expr500() -> infixr(expr550(), binop(['::', '++'])).
|
||||
expr550() -> infixl(expr600(), binop(['<<', '>>'])).
|
||||
expr500() -> infixr(expr600(), binop(['::', '++'])).
|
||||
expr600() -> infixl(expr650(), binop(['+', '-'])).
|
||||
expr650() -> ?RULE(many(token('-')), expr700(), prefixes(_1, _2)).
|
||||
expr700() -> infixl(expr750(), binop(['*', '/', mod])).
|
||||
expr750() -> infixl(expr800(), binop(['^'])).
|
||||
expr800() -> ?RULE(many(token('!')), expr850(), prefixes(_1, _2)).
|
||||
expr850() -> ?RULE(many(token('bnot')), expr900(), prefixes(_1, _2)).
|
||||
expr800() -> ?RULE(many(token('!')), expr900(), prefixes(_1, _2)).
|
||||
expr900() -> ?RULE(exprAtom(), many(elim()), elim(_1, _2)).
|
||||
|
||||
exprAtom() ->
|
||||
@@ -525,7 +501,7 @@ id_or_addr() ->
|
||||
?RULE(id(), parse_addr_literal(_1)).
|
||||
|
||||
parse_addr_literal(Id = {id, Ann, Name}) ->
|
||||
case lists:member(lists:sublist(Name, 3), ["ak_", "ok_", "oq_", "ct_", "sg_"]) of
|
||||
case lists:member(lists:sublist(Name, 3), ["ak_", "ok_", "oq_", "ct_"]) of
|
||||
false -> Id;
|
||||
true ->
|
||||
try aeser_api_encoder:decode(list_to_binary(Name)) of
|
||||
@@ -564,11 +540,13 @@ bracket_list(P) -> brackets(comma_sep(P)).
|
||||
-spec pos_ann(ann_line(), ann_col()) -> ann().
|
||||
pos_ann(Line, Col) ->
|
||||
[ {file, current_file()}
|
||||
, {dir, current_dir()}
|
||||
, {include_type, current_include_type()}
|
||||
, {line, Line}
|
||||
, {col, Col} ].
|
||||
|
||||
top_ann() ->
|
||||
pos_ann(0, 0).
|
||||
|
||||
ann_pos(Ann) ->
|
||||
{proplists:get_value(file, Ann),
|
||||
proplists:get_value(line, Ann),
|
||||
@@ -612,7 +590,7 @@ group_ifs([], Acc) ->
|
||||
group_ifs([{'if', Ann, Cond, Then} | Stmts], Acc) ->
|
||||
{Elses, Rest} = else_branches(Stmts, []),
|
||||
group_ifs(Rest, [build_if(Ann, Cond, Then, Elses) | Acc]);
|
||||
group_ifs([{'else', Ann, _} | _], _) ->
|
||||
group_ifs([{else, Ann, _} | _], _) ->
|
||||
fail({Ann, "No matching 'if' for 'else'"});
|
||||
group_ifs([{elif, Ann, _, _} | _], _) ->
|
||||
fail({Ann, "No matching 'if' for 'elif'"});
|
||||
@@ -622,14 +600,14 @@ group_ifs([Stmt | Stmts], Acc) ->
|
||||
build_if(Ann, Cond, Then, [{elif, Ann1, Cond1, Then1} | Elses]) ->
|
||||
{'if', Ann, Cond, Then,
|
||||
set_ann(format, elif, build_if(Ann1, Cond1, Then1, Elses))};
|
||||
build_if(Ann, Cond, Then, [{'else', _Ann, Else}]) ->
|
||||
build_if(Ann, Cond, Then, [{else, _Ann, Else}]) ->
|
||||
{'if', Ann, Cond, Then, Else};
|
||||
build_if(Ann, Cond, Then, []) ->
|
||||
{'if', Ann, Cond, Then, {tuple, [{origin, system}], []}}.
|
||||
|
||||
else_branches([Elif = {elif, _, _, _} | Stmts], Acc) ->
|
||||
else_branches(Stmts, [Elif | Acc]);
|
||||
else_branches([Else = {'else', _, _} | Stmts], Acc) ->
|
||||
else_branches([Else = {else, _, _} | Stmts], Acc) ->
|
||||
{lists:reverse([Else | Acc]), Stmts};
|
||||
else_branches(Stmts, Acc) ->
|
||||
{lists:reverse(Acc), Stmts}.
|
||||
@@ -690,134 +668,15 @@ bad_expr_err(Reason, E) ->
|
||||
|
||||
%% -- Helper functions -------------------------------------------------------
|
||||
|
||||
expand_includes(AST, Included, Opts) ->
|
||||
add_auto_imports(AST) ->
|
||||
Ann = [{origin, system}],
|
||||
AST1 = [ {include, Ann, {string, Ann, File}}
|
||||
|| File <- lists:usort(auto_imports(AST)) ] ++ AST,
|
||||
expand_includes(AST1, Included, [], Opts).
|
||||
[ {using, Ann, {con, Ann, Import}}
|
||||
|| Import <- lists:usort(auto_imports(AST)) ] ++ AST.
|
||||
|
||||
expand_includes([], Included, Acc, Opts) ->
|
||||
case lists:member(keep_included, Opts) of
|
||||
false ->
|
||||
{ok, lists:reverse(Acc)};
|
||||
true ->
|
||||
{ok, {lists:reverse(Acc), Included}}
|
||||
end;
|
||||
expand_includes([{include, Ann, {string, _SAnn, File}} | AST], Included, Acc, Opts) ->
|
||||
case get_include_code(File, Ann, Opts) of
|
||||
{ok, AbsDir, Code} ->
|
||||
Hashed = hash_include(File, Code),
|
||||
case sets:is_element(Hashed, Included) of
|
||||
false ->
|
||||
SrcFile = proplists:get_value(src_file, Opts, no_file),
|
||||
IncludeType = case proplists:get_value(file, Ann) of
|
||||
SrcFile -> direct;
|
||||
_ -> indirect
|
||||
end,
|
||||
Opts1 = lists:keystore(src_file, 1, Opts, {src_file, File}),
|
||||
Opts2 = lists:keystore(src_dir, 1, Opts1, {src_dir, AbsDir}),
|
||||
Opts3 = lists:keystore(include_type, 1, Opts2, {include_type, IncludeType}),
|
||||
Included1 = sets:add_element(Hashed, Included),
|
||||
case parse_and_scan(file(), Code, Opts3) of
|
||||
{ok, AST1} ->
|
||||
expand_includes(AST1 ++ AST, Included1, Acc, Opts);
|
||||
Err = {error, _} ->
|
||||
Err
|
||||
end;
|
||||
true ->
|
||||
expand_includes(AST, Included, Acc, Opts)
|
||||
end;
|
||||
Err = {error, _} ->
|
||||
Err
|
||||
end;
|
||||
expand_includes([E | AST], Included, Acc, Opts) ->
|
||||
expand_includes(AST, Included, [E | Acc], Opts).
|
||||
|
||||
read_file(File, Opts) ->
|
||||
case proplists:get_value(include, Opts, {explicit_files, #{}}) of
|
||||
{file_system, Paths} ->
|
||||
lists:foldr(fun(Path, {error, _}) -> read_file_(Path, File);
|
||||
(_Path, OK) -> OK end, {error, not_found}, Paths);
|
||||
{explicit_files, Files} ->
|
||||
case maps:get(binary_to_list(File), Files, not_found) of
|
||||
not_found -> {error, not_found};
|
||||
Src -> {ok, File, 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([sophia, priv, stdlib, File])),
|
||||
case zip:extract(Archive, [{file_list, [FileName]}, memory]) of
|
||||
{ok, [{_, Src}]} -> {ok, escript, Src};
|
||||
_ -> {error, not_found}
|
||||
end
|
||||
catch _:_ ->
|
||||
{error, not_found}
|
||||
end
|
||||
end.
|
||||
|
||||
read_file_(Path, File) ->
|
||||
AbsFile = filename:join(Path, File),
|
||||
case file:read_file(AbsFile) of
|
||||
{ok, Bin} -> {ok, aeso_utils:canonical_dir(filename:dirname(AbsFile)), Bin};
|
||||
Err -> Err
|
||||
end.
|
||||
|
||||
stdlib_options() ->
|
||||
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) ->
|
||||
%% Temporarily extend include paths with the directory of the current file
|
||||
Opts1 = include_current_file_dir(Opts, Ann),
|
||||
case {read_file(File, Opts1), read_file(File, stdlib_options())} of
|
||||
{{ok, Dir, Bin}, {ok, _}} ->
|
||||
case filename:basename(File) == File of
|
||||
true -> { error
|
||||
, fail( ann_pos(Ann)
|
||||
, "Illegal redefinition of standard library " ++ binary_to_list(File))};
|
||||
%% If a path is provided then the stdlib takes lower priority
|
||||
false -> {ok, Dir, binary_to_list(Bin)}
|
||||
end;
|
||||
{_, {ok, _, Bin}} ->
|
||||
{ok, stdlib, binary_to_list(Bin)};
|
||||
{{ok, Dir, Bin}, _} ->
|
||||
{ok, Dir, binary_to_list(Bin)};
|
||||
{_, _} ->
|
||||
{error, {ann_pos(Ann), include_error, File}}
|
||||
end.
|
||||
|
||||
include_current_file_dir(Opts, Ann) ->
|
||||
case {proplists:get_value(dir, Ann, undefined),
|
||||
proplists:get_value(include, Opts, undefined)} of
|
||||
{undefined, _} -> Opts;
|
||||
{CurrDir, {file_system, Paths}} ->
|
||||
case lists:member(CurrDir, Paths) of
|
||||
false -> [{include, {file_system, [CurrDir | Paths]}} | Opts];
|
||||
true -> Opts
|
||||
end;
|
||||
{_, _} -> Opts
|
||||
end.
|
||||
|
||||
-spec hash_include(string() | binary(), string()) -> include_hash().
|
||||
hash_include(File, Code) when is_binary(File) ->
|
||||
hash_include(binary_to_list(File), Code);
|
||||
hash_include(File, Code) when is_list(File) ->
|
||||
{filename:basename(File), crypto:hash(sha256, Code)}.
|
||||
|
||||
auto_imports({comprehension_bind, _, _}) -> [<<"ListInternal.aes">>];
|
||||
auto_imports({'..', _}) -> [<<"ListInternal.aes">>];
|
||||
auto_imports({comprehension_bind, _, _}) -> ["ListInternal"];
|
||||
auto_imports({'..', _}) -> ["ListInternal"];
|
||||
auto_imports(L) when is_list(L) ->
|
||||
lists:flatmap(fun auto_imports/1, L);
|
||||
auto_imports(T) when is_tuple(T) ->
|
||||
auto_imports(tuple_to_list(T));
|
||||
auto_imports(_) -> [].
|
||||
|
||||
any_bytes({id, Ann, "bytes"}) -> {bytes_t, Ann, any};
|
||||
any_bytes({app_t, _, {id, Ann, "bytes"}, []}) -> {bytes_t, Ann, any};
|
||||
any_bytes(Type) -> Type.
|
||||
|
||||
+8
-18
@@ -275,9 +275,7 @@ type({tuple_t, _, Args}) ->
|
||||
tuple_type(Args);
|
||||
type({args_t, _, Args}) ->
|
||||
args_type(Args);
|
||||
type({bytes_t, _, any}) -> text("bytes()");
|
||||
type({bytes_t, _, '_'}) -> text("bytes(_)");
|
||||
type({bytes_t, _, fixed}) -> text("bytes(_)");
|
||||
type({bytes_t, _, any}) -> text("bytes(_)");
|
||||
type({bytes_t, _, Len}) ->
|
||||
text(lists:concat(["bytes(", Len, ")"]));
|
||||
type({if_t, _, Id, Then, Else}) ->
|
||||
@@ -387,8 +385,7 @@ expr_p(_, {Type, _, Bin})
|
||||
when Type == account_pubkey;
|
||||
Type == contract_pubkey;
|
||||
Type == oracle_pubkey;
|
||||
Type == oracle_query_id;
|
||||
Type == signature ->
|
||||
Type == oracle_query_id ->
|
||||
text(binary_to_list(aeser_api_encoder:encode(Type, Bin)));
|
||||
expr_p(_, {string, _, <<>>}) -> text("\"\"");
|
||||
expr_p(_, {string, _, S}) ->
|
||||
@@ -419,7 +416,7 @@ stmt_p({'if', _, Cond, Then}) ->
|
||||
block_expr(200, beside(text("if"), paren(expr(Cond))), Then);
|
||||
stmt_p({elif, _, Cond, Then}) ->
|
||||
block_expr(200, beside(text("elif"), paren(expr(Cond))), Then);
|
||||
stmt_p({'else', Else}) ->
|
||||
stmt_p({else, Else}) ->
|
||||
HideGenerated = not show_generated(),
|
||||
case aeso_syntax:get_ann(origin, Else) of
|
||||
system when HideGenerated -> empty();
|
||||
@@ -439,20 +436,15 @@ bin_prec('=') -> { 0, 0, 0}; %% Always printed inside '[ ]'
|
||||
bin_prec('@') -> { 0, 0, 0}; %% Only in error messages
|
||||
bin_prec('|>') -> {150, 150, 200};
|
||||
bin_prec('||') -> {200, 300, 200};
|
||||
bin_prec('&&') -> {300, 325, 300};
|
||||
bin_prec('bor') -> {325, 350, 325};
|
||||
bin_prec('bxor') -> {350, 375, 350};
|
||||
bin_prec('band') -> {375, 400, 375};
|
||||
bin_prec('&&') -> {300, 400, 300};
|
||||
bin_prec('<') -> {400, 500, 500};
|
||||
bin_prec('>') -> {400, 500, 500};
|
||||
bin_prec('=<') -> {400, 500, 500};
|
||||
bin_prec('>=') -> {400, 500, 500};
|
||||
bin_prec('==') -> {400, 500, 500};
|
||||
bin_prec('!=') -> {400, 500, 500};
|
||||
bin_prec('++') -> {500, 550, 500};
|
||||
bin_prec('::') -> {500, 550, 500};
|
||||
bin_prec('<<') -> {550, 600, 550};
|
||||
bin_prec('>>') -> {550, 600, 550};
|
||||
bin_prec('++') -> {500, 600, 500};
|
||||
bin_prec('::') -> {500, 600, 500};
|
||||
bin_prec('+') -> {600, 600, 650};
|
||||
bin_prec('-') -> {600, 600, 650};
|
||||
bin_prec('*') -> {700, 700, 750};
|
||||
@@ -462,8 +454,7 @@ bin_prec('^') -> {750, 750, 800}.
|
||||
|
||||
-spec un_prec(aeso_syntax:un_op()) -> {integer(), integer()}.
|
||||
un_prec('-') -> {650, 650};
|
||||
un_prec('!') -> {800, 800};
|
||||
un_prec('bnot') -> {850, 850}.
|
||||
un_prec('!') -> {800, 800}.
|
||||
|
||||
equals(Ann, A, B) ->
|
||||
{app, [{format, infix} | Ann], {'=', Ann}, [A, B]}.
|
||||
@@ -534,5 +525,4 @@ get_elifs(If = {'if', Ann, Cond, Then, Else}, Elifs) ->
|
||||
elif -> get_elifs(Else, [{elif, Ann, Cond, Then} | Elifs]);
|
||||
_ -> {lists:reverse(Elifs), If}
|
||||
end;
|
||||
get_elifs(Else, Elifs) -> {lists:reverse(Elifs), {'else', Else}}.
|
||||
|
||||
get_elifs(Else, Elifs) -> {lists:reverse(Elifs), {else, Else}}.
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ lexer() ->
|
||||
|
||||
Keywords = ["contract", "include", "let", "switch", "type", "record", "datatype", "if", "elif", "else", "function",
|
||||
"stateful", "payable", "true", "false", "mod", "public", "entrypoint", "private", "indexed", "namespace",
|
||||
"interface", "main", "using", "as", "for", "hiding", "band", "bor", "bxor", "bnot"
|
||||
"interface", "main", "using", "as", "for", "hiding"
|
||||
],
|
||||
KW = string:join(Keywords, "|"),
|
||||
|
||||
|
||||
+1
-1
@@ -13,5 +13,5 @@
|
||||
-export([stdlib_include_path/0]).
|
||||
|
||||
stdlib_include_path() ->
|
||||
filename:join([code:priv_dir(sophia), "stdlib"]).
|
||||
filename:join([code:priv_dir(aesophia), "stdlib"]).
|
||||
|
||||
|
||||
+32
-22
@@ -10,23 +10,23 @@
|
||||
|
||||
-export([get_ann/1, get_ann/2, get_ann/3, set_ann/2, qualify/2]).
|
||||
|
||||
-export_type([ann_file/0, ann_line/0, ann_col/0, ann_origin/0, ann_format/0, ann/0]).
|
||||
-export_type([ann_line/0, ann_col/0, ann_origin/0, ann_format/0, ann/0]).
|
||||
-export_type([name/0, id/0, con/0, qid/0, qcon/0, tvar/0, op/0]).
|
||||
-export_type([bin_op/0, un_op/0]).
|
||||
-export_type([decl/0, letbind/0, typedef/0, pragma/0, fundecl/0]).
|
||||
-export_type([top_decl/0, decl/0, letbind/0, typedef/0, pragma/0, fundecl/0]).
|
||||
-export_type([arg/0, field_t/0, constructor_t/0, named_arg_t/0]).
|
||||
-export_type([type/0, constant/0, expr/0, arg_expr/0, field/1, stmt/0, alt/0, lvalue/0, elim/0, pat/0]).
|
||||
-export_type([ast/0]).
|
||||
|
||||
-type ast() :: [decl()].
|
||||
-type ast() :: [top_decl()].
|
||||
|
||||
-type ann_line() :: integer().
|
||||
-type ann_col() :: integer().
|
||||
-type ann_origin() :: system | user.
|
||||
-type ann_format() :: '?:' | hex | infix | prefix | elif.
|
||||
-type ann_file() :: string() | no_file.
|
||||
|
||||
-type ann() :: [ {file, ann_file()} | {line, ann_line()} | {col, ann_col()} | {format, ann_format()} | {origin, ann_origin()}
|
||||
-type ann() :: [ {line, ann_line()} | {col, ann_col()} | {file, ann_file()}
|
||||
| {format, ann_format()} | {origin, ann_origin()}
|
||||
| stateful | private | payable | main | interface | entrypoint].
|
||||
|
||||
-type name() :: string().
|
||||
@@ -39,20 +39,31 @@
|
||||
-type namespace_alias() :: none | con().
|
||||
-type namespace_parts() :: none | {for, [id()]} | {hiding, [id()]}.
|
||||
|
||||
-type decl() :: {contract_main, ann(), con(), [con()], [decl()]}
|
||||
| {contract_child, ann(), con(), [con()], [decl()]}
|
||||
| {contract_interface, ann(), con(), [con()], [decl()]}
|
||||
| {namespace, ann(), con(), [decl()]}
|
||||
| {include, ann(), {string, ann(), string()}}
|
||||
| {pragma, ann(), pragma()}
|
||||
| {type_decl, ann(), id(), [tvar()]} % Only for error msgs
|
||||
| {type_def, ann(), id(), [tvar()], typedef()}
|
||||
| {fun_clauses, ann(), id(), type(), [letfun() | fundecl()]}
|
||||
| {block, ann(), [decl()]}
|
||||
| {using, ann(), con(), namespace_alias(), namespace_parts()}
|
||||
| fundecl()
|
||||
| letfun()
|
||||
| letval(). % Only for error msgs
|
||||
% Can't be toplevel
|
||||
-type scoped_decl()
|
||||
:: {contract_decl, ann(), con()}
|
||||
| {namespace_decl, ann(), con()}
|
||||
| {type_decl, ann(), id(), [tvar()]} % Only for error msgs
|
||||
| {type_def, ann(), id(), [tvar()], typedef()}
|
||||
| {fun_clauses, ann(), id(), type(), [letfun() | fundecl()]}
|
||||
| {block, ann(), [scoped_decl()]}
|
||||
| fundecl()
|
||||
| letfun()
|
||||
| letval() % Only for error msgs
|
||||
| decl().
|
||||
|
||||
% Toplevel, can be nested
|
||||
-type decl()
|
||||
:: {contract_main, ann(), con(), [con()], [scoped_decl()]}
|
||||
| {contract_child, ann(), con(), [con()], [scoped_decl()]}
|
||||
| {contract_interface, ann(), con(), [con()], [scoped_decl()]}
|
||||
| {namespace, ann(), con(), [scoped_decl()]}
|
||||
| {using, ann(), con(), namespace_alias(), namespace_parts()}.
|
||||
|
||||
% Toplevel only
|
||||
-type top_decl()
|
||||
:: {pragma, ann(), pragma()}
|
||||
| decl().
|
||||
|
||||
-type compiler_version() :: [non_neg_integer()].
|
||||
|
||||
@@ -100,7 +111,6 @@
|
||||
| {contract_pubkey, ann(), binary()}
|
||||
| {oracle_pubkey, ann(), binary()}
|
||||
| {oracle_query_id, ann(), binary()}
|
||||
| {signature, ann(), binary()}
|
||||
| {string, ann(), binary()}
|
||||
| {char, ann(), integer()}.
|
||||
|
||||
@@ -108,8 +118,8 @@
|
||||
|
||||
-type bin_op() :: '+' | '-' | '*' | '/' | mod | '^'
|
||||
| '++' | '::' | '<' | '>' | '=<' | '>=' | '==' | '!='
|
||||
| '||' | '&&' | '..' | 'band' | 'bor' | 'bxor' | '>>' | '<<' | '|>'.
|
||||
-type un_op() :: '-' | '!' | 'bnot'.
|
||||
| '||' | '&&' | '..' | '|>'.
|
||||
-type un_op() :: '-' | '!'.
|
||||
|
||||
-type expr()
|
||||
:: {lam, ann(), [arg()], expr()}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeso_syntax_utils).
|
||||
|
||||
-export([used_ids/1, used_ids/2, used_types/2, used/1]).
|
||||
-export([used_ids/1, used_types/2, used/1]).
|
||||
|
||||
-record(alg, {zero, plus, scoped}).
|
||||
|
||||
@@ -31,13 +31,11 @@
|
||||
| aeso_syntax:field(aeso_syntax:expr())
|
||||
| aeso_syntax:stmt().
|
||||
fold(Alg = #alg{zero = Zero, plus = Plus, scoped = Scoped}, Fun, K, X) ->
|
||||
ExprKind = if K == bind_expr -> bind_expr; true -> expr end,
|
||||
TypeKind = if K == bind_type -> bind_type; true -> type end,
|
||||
Sum = fun(Xs) -> lists:foldl(Plus, Zero, Xs) end,
|
||||
Same = fun(A) -> fold(Alg, Fun, K, A) end,
|
||||
Decl = fun(D) -> fold(Alg, Fun, decl, D) end,
|
||||
Type = fun(T) -> fold(Alg, Fun, TypeKind, T) end,
|
||||
Expr = fun(E) -> fold(Alg, Fun, ExprKind, E) end,
|
||||
Type = fun(T) -> fold(Alg, Fun, type, T) end,
|
||||
Expr = fun(E) -> fold(Alg, Fun, expr, E) end,
|
||||
BindExpr = fun(P) -> fold(Alg, Fun, bind_expr, P) end,
|
||||
BindType = fun(T) -> fold(Alg, Fun, bind_type, T) end,
|
||||
Top = Fun(K, X),
|
||||
@@ -110,16 +108,8 @@ fold(Alg = #alg{zero = Zero, plus = Plus, scoped = Scoped}, Fun, K, X) ->
|
||||
|
||||
%% Name dependencies
|
||||
|
||||
%% Used ids, top level
|
||||
used_ids(E) ->
|
||||
used_ids([], E).
|
||||
|
||||
%% Used ids, top level or in (current) namespace
|
||||
used_ids(Ns, E) ->
|
||||
[ lists:last(Xs) || {{term, Xs}, _} <- used(E), in_ns(Xs, Ns) ].
|
||||
|
||||
in_ns([_], _) -> true;
|
||||
in_ns(Xs, Ns) -> lists:droplast(Xs) == Ns.
|
||||
[ X || {{term, [X]}, _} <- used(E) ].
|
||||
|
||||
used_types([Top] = _CurrentNS, T) ->
|
||||
F = fun({{type, [X]}, _}) -> [X];
|
||||
@@ -165,3 +155,4 @@ used(D) ->
|
||||
(_, _) -> #{}
|
||||
end, decl, D)),
|
||||
lists:filter(NotBound, Xs).
|
||||
|
||||
|
||||
+1
-13
@@ -6,22 +6,10 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeso_utils).
|
||||
|
||||
-export([scc/1, canonical_dir/1]).
|
||||
-export([scc/1]).
|
||||
|
||||
-export_type([graph/1]).
|
||||
|
||||
%% -- Simplistic canonical directory
|
||||
%% Note: no attempts to be 100% complete
|
||||
|
||||
canonical_dir(Dir) ->
|
||||
{ok, Cwd} = file:get_cwd(),
|
||||
AbsName = filename:absname(Dir),
|
||||
RelAbsName = filename:join(tl(filename:split(AbsName))),
|
||||
case filelib:safe_relative_path(RelAbsName, Cwd) of
|
||||
unsafe -> AbsName;
|
||||
Simplified -> filename:absname(Simplified, "")
|
||||
end.
|
||||
|
||||
%% -- Topological sort
|
||||
|
||||
-type graph(Node) :: #{Node => [Node]}. %% List of incoming edges (dependencies).
|
||||
|
||||
+3
-25
@@ -8,17 +8,13 @@
|
||||
|
||||
-export([ from_fate/2 ]).
|
||||
|
||||
-include_lib("gmbytecode/include/gmb_fate_data.hrl").
|
||||
-include_lib("aebytecode/include/aeb_fate_data.hrl").
|
||||
|
||||
-spec from_fate(aeso_syntax:type(), gmb_fate_data:fate_type()) -> aeso_syntax:expr().
|
||||
-spec from_fate(aeso_syntax:type(), aeb_fate_data:fate_type()) -> aeso_syntax:expr().
|
||||
from_fate({id, _, "address"}, ?FATE_ADDRESS(Bin)) -> {account_pubkey, [], Bin};
|
||||
from_fate({id, _, "signature"}, ?FATE_BYTES(Bin)) -> {signature, [], Bin};
|
||||
from_fate({id, _, "hash"}, ?FATE_BYTES(Bin)) -> {bytes, [], Bin};
|
||||
from_fate({id, _, "unit"}, ?FATE_UNIT) -> {tuple, [], []};
|
||||
from_fate({app_t, _, {id, _, "oracle"}, _}, ?FATE_ORACLE(Bin)) -> {oracle_pubkey, [], 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({bytes_t, _, any}, ?FATE_BYTES(Bin)) -> make_any_bytes(Bin);
|
||||
from_fate({bytes_t, _, N}, ?FATE_BYTES(Bin)) when byte_size(Bin) == N -> {bytes, [], Bin};
|
||||
from_fate({id, _, "bits"}, ?FATE_BITS(N)) -> make_bits(N);
|
||||
from_fate({id, _, "int"}, N) when is_integer(N) ->
|
||||
@@ -82,7 +78,6 @@ from_fate_builtin(QType, Val) ->
|
||||
Hsh = {bytes_t, [], 32},
|
||||
I32 = {bytes_t, [], 32},
|
||||
I48 = {bytes_t, [], 48},
|
||||
Bts = {bytes_t, [], any},
|
||||
Qid = fun(Name) -> {qid, [], Name} end,
|
||||
Map = fun(KT, VT) -> {app_t, [], {id, [], "map"}, [KT, VT]} end,
|
||||
ChainTxArities = [3, 0, 0, 0, 0, 0, 1, 1, 1, 2, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 0],
|
||||
@@ -93,7 +88,7 @@ from_fate_builtin(QType, Val) ->
|
||||
|
||||
{["AENS", "name"], {variant, [3], 0, {Addr, TTL, Ptrs}}} ->
|
||||
App(["AENS","Name"], [Chk(Adr, Addr), Chk(Qid(["Chain", "ttl"]), TTL),
|
||||
Chk(Map(Str, Qid(["AENS", "pointee"])), Ptrs)]);
|
||||
Chk(Map(Str, Qid(["AENS", "pointee"])), Ptrs)]);
|
||||
|
||||
{["AENS", "pointee"], {variant, [1, 1, 1, 1], 0, {Addr}}} ->
|
||||
App(["AENS","AccountPt"], [Chk(Adr, Addr)]);
|
||||
@@ -104,21 +99,6 @@ from_fate_builtin(QType, Val) ->
|
||||
{["AENS", "pointee"], {variant, [1, 1, 1, 1], 3, {Addr}}} ->
|
||||
App(["AENS","ChannelPt"], [Chk(Adr, Addr)]);
|
||||
|
||||
{["AENSv2", "name"], {variant, [3], 0, {Addr, TTL, Ptrs}}} ->
|
||||
App(["AENSv2","Name"], [Chk(Adr, Addr), Chk(Qid(["Chain", "ttl"]), TTL),
|
||||
Chk(Map(Str, Qid(["AENSv2", "pointee"])), Ptrs)]);
|
||||
|
||||
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 0, {Value}}} ->
|
||||
App(["AENSv2","AccountPt"], [Chk(Adr, Value)]);
|
||||
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 1, {Value}}} ->
|
||||
App(["AENSv2","OraclePt"], [Chk(Adr, Value)]);
|
||||
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 2, {Value}}} ->
|
||||
App(["AENSv2","ContractPt"], [Chk(Adr, Value)]);
|
||||
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 3, {Value}}} ->
|
||||
App(["AENSv2","ChannelPt"], [Chk(Adr, Value)]);
|
||||
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 4, {Value}}} ->
|
||||
App(["AENSv2","DataPt"], [Chk(Bts, Value)]);
|
||||
|
||||
{["Chain", "ga_meta_tx"], {variant, [2], 0, {Addr, X}}} ->
|
||||
App(["Chain","GAMetaTx"], [Chk(Adr, Addr), Chk(Int, X)]);
|
||||
|
||||
@@ -190,5 +170,3 @@ make_bits(Set, Zero, I, N) when 0 == N rem 2 ->
|
||||
make_bits(Set, Zero, I, N) ->
|
||||
{app, [], Set, [make_bits(Set, Zero, I + 1, N div 2), {int, [], I}]}.
|
||||
|
||||
make_any_bytes(Bin) ->
|
||||
{app, [], {qid, [], ["Bytes", "to_any_size"]}, [{bytes, [], Bin}]}.
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
{application, sophia,
|
||||
{application, aesophia,
|
||||
[{description, "Compiler for Aeternity Sophia language"},
|
||||
{vsn, "8.0.1"},
|
||||
{vsn, "7.1.0"},
|
||||
{registered, []},
|
||||
{applications,
|
||||
[kernel,
|
||||
stdlib,
|
||||
jsx,
|
||||
syntax_tools,
|
||||
gmbytecode,
|
||||
getopt,
|
||||
aebytecode,
|
||||
eblake2
|
||||
]},
|
||||
{env,[]},
|
||||
@@ -65,9 +65,9 @@ to_sophia_value_mcl_bls12_381_test() ->
|
||||
|
||||
Opts = [{backend, fate}],
|
||||
|
||||
CallValue32 = gmb_fate_encoding:serialize({bytes, <<20:256>>}),
|
||||
CallValue48 = gmb_fate_encoding:serialize({bytes, <<55:384>>}),
|
||||
CallValueTp = gmb_fate_encoding:serialize({tuple, {{bytes, <<15:256>>}, {bytes, <<160:256>>}, {bytes, <<1234:256>>}}}),
|
||||
CallValue32 = aeb_fate_encoding:serialize({bytes, <<20:256>>}),
|
||||
CallValue48 = aeb_fate_encoding:serialize({bytes, <<55:384>>}),
|
||||
CallValueTp = aeb_fate_encoding:serialize({tuple, {{bytes, <<15:256>>}, {bytes, <<160:256>>}, {bytes, <<1234:256>>}}}),
|
||||
|
||||
{ok, _} = aeso_compiler:to_sophia_value(Code, "test_bls12_381_fp", ok, CallValue32, Opts),
|
||||
{error, _} = aeso_compiler:to_sophia_value(Code, "test_bls12_381_fp", ok, CallValue48, Opts),
|
||||
@@ -227,7 +227,7 @@ encode_decode_calldata_(Code, FunName, Args) ->
|
||||
"init" ->
|
||||
[];
|
||||
_ ->
|
||||
{ok, FateArgs} = gmb_fate_abi:decode_calldata(FunName, Calldata),
|
||||
{ok, FateArgs} = aeb_fate_abi:decode_calldata(FunName, Calldata),
|
||||
FateArgs
|
||||
end.
|
||||
|
||||
@@ -236,7 +236,7 @@ encode_decode(D) ->
|
||||
D.
|
||||
|
||||
encode(D) ->
|
||||
gmb_fate_encoding:serialize(D).
|
||||
aeb_fate_encoding:serialize(D).
|
||||
|
||||
decode(B) ->
|
||||
gmb_fate_encoding:deserialize(B).
|
||||
aeb_fate_encoding:deserialize(B).
|
||||
|
||||
@@ -85,7 +85,6 @@ compilable_contracts() ->
|
||||
{"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", "any_bytes", ["Bytes.to_any_size(#0011AA)"]},
|
||||
{"funargs", "sjutton", ["#0011012003100011012003100011012003"]},
|
||||
{"funargs", "sextiosju", ["#01020304050607080910111213141516171819202122232425262728293031323334353637383940"
|
||||
"414243444546474849505152535455565758596061626364656667"]},
|
||||
@@ -117,7 +116,6 @@ compilable_contracts() ->
|
||||
{"funargs", "chain_base_tx", ["Chain.NameRevokeTx(#ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)"]},
|
||||
{"funargs", "chain_base_tx", ["Chain.NameTransferTx(ak_2dATVcZ9KJU5a8hdsVtTv21pYiGWiPbmVcU1Pz72FFqpk9pSRR, #ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)"]},
|
||||
{"funargs", "chain_base_tx", ["Chain.GAAttachTx"]},
|
||||
{"funargs", "sig", ["sg_MhibzTP1wWzGCTjtPFr1TiPqRJrrJqw7auvEuF5i3FdoALWqXLBDY6xxRRNUSPHK3EQTnTzF12EyspkxrSMxVHKsZeSMj"]},
|
||||
{"variant_types", "init", []},
|
||||
{"basic_auth", "init", []},
|
||||
{"address_literals", "init", []},
|
||||
|
||||
+18
-133
@@ -28,7 +28,7 @@ simple_compile_test_() ->
|
||||
fun() ->
|
||||
case compile(ContractName) of
|
||||
#{fate_code := Code} ->
|
||||
Code1 = gmb_fate_code:deserialize(gmb_fate_code:serialize(Code)),
|
||||
Code1 = aeb_fate_code:deserialize(aeb_fate_code:serialize(Code)),
|
||||
?assertMatch({X, X}, {Code1, Code});
|
||||
Error -> io:format("\n\n~p\n\n", [Error]), print_and_throw(Error)
|
||||
end
|
||||
@@ -70,7 +70,6 @@ simple_compile_test_() ->
|
||||
fun() ->
|
||||
#{ warnings := Warnings } = compile("warnings", [warn_all]),
|
||||
#{ warnings := [] } = compile("warning_unused_include_no_include", [warn_all]),
|
||||
#{ warnings := [] } = compile("warning_used_record_typedef", [warn_all]),
|
||||
check_warnings(warnings(), Warnings)
|
||||
end} ] ++
|
||||
[].
|
||||
@@ -84,7 +83,7 @@ stdlib_test_() ->
|
||||
Options = [{src_file, File}],
|
||||
case aeso_compiler:from_string(String, Options) of
|
||||
{ok, #{fate_code := Code}} ->
|
||||
Code1 = gmb_fate_code:deserialize(gmb_fate_code:serialize(Code)),
|
||||
Code1 = aeb_fate_code:deserialize(aeb_fate_code:serialize(Code)),
|
||||
?assertMatch({X, X}, {Code1, Code});
|
||||
{error, Error} -> io:format("\n\n~p\n\n", [Error]), print_and_throw(Error)
|
||||
end
|
||||
@@ -162,7 +161,6 @@ compilable_contracts() ->
|
||||
"state_handling",
|
||||
"events",
|
||||
"include",
|
||||
"relative_include",
|
||||
"basic_auth",
|
||||
"basic_auth_tx",
|
||||
"bitcoin_auth",
|
||||
@@ -172,7 +170,6 @@ compilable_contracts() ->
|
||||
"namespace_bug",
|
||||
"bytes_to_x",
|
||||
"bytes_concat",
|
||||
"bytes_misc",
|
||||
"aens",
|
||||
"aens_update",
|
||||
"tuple_match",
|
||||
@@ -225,8 +222,6 @@ compilable_contracts() ->
|
||||
"unapplied_contract_call",
|
||||
"unapplied_named_arg_builtin",
|
||||
"resolve_field_constraint_by_arity",
|
||||
"toplevel_constants",
|
||||
"ceres",
|
||||
"test" % Custom general-purpose test file. Keep it last on the list.
|
||||
].
|
||||
|
||||
@@ -291,11 +286,7 @@ warnings() ->
|
||||
<<?PosW(48, 5)
|
||||
"Unused return value.">>,
|
||||
<<?PosW(60, 5)
|
||||
"The function `dec` is defined but never used.">>,
|
||||
<<?PosW(73, 9)
|
||||
"The definition of `const` shadows an older definition at line 70, column 3.">>,
|
||||
<<?PosW(84, 7)
|
||||
"The constant `c` is defined but never used.">>
|
||||
"The function `dec` is defined but never used.">>
|
||||
]).
|
||||
|
||||
failing_contracts() ->
|
||||
@@ -451,10 +442,6 @@ failing_contracts() ->
|
||||
[<<?Pos(12, 42)
|
||||
"Cannot unify `int` and `string`\n"
|
||||
"when checking the type of the expression `r.foo() : map(int, string)` "
|
||||
"against the expected type `map(string, int)`">>,
|
||||
<<?Pos(12, 42)
|
||||
"Cannot unify `string` and `int`\n"
|
||||
"when checking the type of the expression `r.foo() : map(int, string)` "
|
||||
"against the expected type `map(string, int)`">>])
|
||||
, ?TYPE_ERROR(not_toplevel_include,
|
||||
[<<?Pos(2, 11)
|
||||
@@ -612,21 +599,6 @@ failing_contracts() ->
|
||||
[<<?Pos(3, 5)
|
||||
"Unbound variable `Chain.event`\n"
|
||||
"Did you forget to define the event type?">>])
|
||||
, ?TYPE_ERROR(bad_bytes_to_x,
|
||||
[<<?Pos(3, 35)
|
||||
"Cannot resolve length of byte array in\n"
|
||||
" the result of a call to Bytes.to_fixed_size">>,
|
||||
<<?Pos(4, 36)
|
||||
"Cannot unify `bytes()` and `bytes(4)`\nwhen checking the application of\n"
|
||||
" `Bytes.to_fixed_size : (bytes()) => option('a)`\n"
|
||||
"to arguments\n"
|
||||
" `b : bytes(4)`">>,
|
||||
<<?Pos(4, 36)
|
||||
"Cannot resolve length of byte array in\n"
|
||||
" the result of a call to Bytes.to_fixed_size">>,
|
||||
<<?Pos(5, 35)
|
||||
"Cannot resolve length of byte array in\n"
|
||||
" the first argument of a call to Bytes.to_any_size">>])
|
||||
, ?TYPE_ERROR(bad_bytes_concat,
|
||||
[<<?Pos(12, 40)
|
||||
"Failed to resolve byte array lengths in call to Bytes.concat with arguments of type\n"
|
||||
@@ -651,8 +623,7 @@ failing_contracts() ->
|
||||
"and result type\n"
|
||||
" - 'c (at line 16, column 39)">>,
|
||||
<<?Pos(19, 25)
|
||||
"Cannot resolve type of byte array in\n"
|
||||
" the first argument of a call to Bytes.to_str">>])
|
||||
"Cannot resolve length of byte array.">>])
|
||||
, ?TYPE_ERROR(bad_bytes_split,
|
||||
[<<?Pos(13, 5)
|
||||
"Failed to resolve byte array lengths in call to Bytes.split with argument of type\n"
|
||||
@@ -687,6 +658,10 @@ failing_contracts() ->
|
||||
[<<?Pos(5, 28)
|
||||
"Invalid call to contract entrypoint `Foo.foo`.\n"
|
||||
"It must be called as `c.foo` for some `c : Foo`.">>])
|
||||
, ?TYPE_ERROR(toplevel_let,
|
||||
[<<?Pos(2, 7)
|
||||
"Toplevel \"let\" definitions are not supported. "
|
||||
"Value `this_is_illegal` could be replaced by 0-argument function.">>])
|
||||
, ?TYPE_ERROR(empty_typedecl,
|
||||
[<<?Pos(2, 8)
|
||||
"Empty type declarations are not supported. "
|
||||
@@ -884,21 +859,17 @@ failing_contracts() ->
|
||||
<<?Pos(48, 5)
|
||||
"Unused return value.">>,
|
||||
<<?Pos(60, 5)
|
||||
"The function `dec` is defined but never used.">>,
|
||||
<<?Pos(73, 9)
|
||||
"The definition of `const` shadows an older definition at line 70, column 3.">>,
|
||||
<<?Pos(84, 7)
|
||||
"The constant `c` is defined but never used.">>
|
||||
"The function `dec` is defined but never used.">>
|
||||
])
|
||||
, ?TYPE_ERROR(polymorphism_contract_interface_recursive,
|
||||
[<<?Pos(1,24)
|
||||
"Trying to implement or extend an undefined interface `Z`">>
|
||||
])
|
||||
, ?TYPE_ERROR(polymorphism_contract_interface_same_name_different_type,
|
||||
[<<?Pos(5,5)
|
||||
"Cannot unify `char` and `int`\n"
|
||||
"when implementing the entrypoint `f` from the interface `I1`">>
|
||||
])
|
||||
[<<?Pos(9,5)
|
||||
"Duplicate definitions of `f` at\n"
|
||||
" - line 8, column 5\n"
|
||||
" - line 9, column 5">>])
|
||||
, ?TYPE_ERROR(polymorphism_contract_missing_implementation,
|
||||
[<<?Pos(4,20)
|
||||
"Unimplemented entrypoint `f` from the interface `I1` in the contract `I2`">>
|
||||
@@ -952,9 +923,6 @@ failing_contracts() ->
|
||||
<<?Pos(67,36)
|
||||
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
|
||||
"when checking the application of\n `DT_INV : ((Cat) => Cat) => dt_inv(Cat)`\nto arguments\n `f_c_to_a : (Cat) => Animal`">>,
|
||||
<<?Pos(67,36)
|
||||
"Cannot unify `Cat` and `Animal` in a invariant context\n"
|
||||
"when checking the type of the expression `DT_INV(f_c_to_a) : dt_inv(Cat)` against the expected type `dt_inv(Animal)`">>,
|
||||
<<?Pos(68,36)
|
||||
"Cannot unify `Cat` and `Animal` in a invariant context\n"
|
||||
"when checking the type of the expression `DT_INV(f_c_to_c) : dt_inv(Cat)` against the expected type `dt_inv(Animal)`">>,
|
||||
@@ -1003,9 +971,6 @@ failing_contracts() ->
|
||||
<<?Pos(116,59)
|
||||
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
|
||||
"when checking the type of the expression `DT_A_CONTRA_B_CONTRA(f_c_to_c_to_u) : dt_a_contra_b_contra(Cat, Cat)` against the expected type `dt_a_contra_b_contra(Animal, Animal)`">>,
|
||||
<<?Pos(116,59)
|
||||
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
|
||||
"when checking the type of the expression `DT_A_CONTRA_B_CONTRA(f_c_to_c_to_u) : dt_a_contra_b_contra(Cat, Cat)` against the expected type `dt_a_contra_b_contra(Animal, Animal)`">>,
|
||||
<<?Pos(119,59)
|
||||
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
|
||||
"when checking the type of the expression `DT_A_CONTRA_B_CONTRA(f_c_to_a_to_u) : dt_a_contra_b_contra(Cat, Animal)` against the expected type `dt_a_contra_b_contra(Animal, Cat)`">>,
|
||||
@@ -1049,9 +1014,6 @@ failing_contracts() ->
|
||||
<<?Pos(19,13)
|
||||
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
|
||||
"when checking the type of the pattern `o07 : oracle(Animal, Cat)` against the expected type `oracle(Cat, Animal)`">>,
|
||||
<<?Pos(19,13)
|
||||
"Cannot unify `Animal` and `Cat` in a covariant context\n"
|
||||
"when checking the type of the pattern `o07 : oracle(Animal, Cat)` against the expected type `oracle(Cat, Animal)`">>,
|
||||
<<?Pos(20,13)
|
||||
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
|
||||
"when checking the type of the pattern `o08 : oracle(Animal, Cat)` against the expected type `oracle(Cat, Cat)`">>,
|
||||
@@ -1076,9 +1038,6 @@ failing_contracts() ->
|
||||
<<?Pos(42,13)
|
||||
"Cannot unify `Animal` and `Cat` in a covariant context\n"
|
||||
"when checking the type of the pattern `q13 : oracle_query(Cat, Cat)` against the expected type `oracle_query(Animal, Animal)`">>,
|
||||
<<?Pos(42,13)
|
||||
"Cannot unify `Animal` and `Cat` in a covariant context\n"
|
||||
"when checking the type of the pattern `q13 : oracle_query(Cat, Cat)` against the expected type `oracle_query(Animal, Animal)`">>,
|
||||
<<?Pos(43,13)
|
||||
"Cannot unify `Animal` and `Cat` in a covariant context\n"
|
||||
"when checking the type of the pattern `q14 : oracle_query(Cat, Cat)` against the expected type `oracle_query(Animal, Cat)`">>,
|
||||
@@ -1140,19 +1099,19 @@ failing_contracts() ->
|
||||
])
|
||||
, ?TYPE_ERROR(polymorphic_aens_resolve,
|
||||
[<<?Pos(4,5)
|
||||
"Invalid return type of `AENSv2.resolve`:\n"
|
||||
"Invalid return type of `AENS.resolve`:\n"
|
||||
" `'a`\n"
|
||||
"It must be a `string` or a pubkey type (`address`, `oracle`, etc)">>
|
||||
])
|
||||
, ?TYPE_ERROR(bad_aens_resolve,
|
||||
[<<?Pos(6,5)
|
||||
"Invalid return type of `AENSv2.resolve`:\n"
|
||||
"Invalid return type of `AENS.resolve`:\n"
|
||||
" `list(int)`\n"
|
||||
"It must be a `string` or a pubkey type (`address`, `oracle`, etc)">>
|
||||
])
|
||||
, ?TYPE_ERROR(bad_aens_resolve_using,
|
||||
[<<?Pos(7,5)
|
||||
"Invalid return type of `AENSv2.resolve`:\n"
|
||||
"Invalid return type of `AENS.resolve`:\n"
|
||||
" `list(int)`\n"
|
||||
"It must be a `string` or a pubkey type (`address`, `oracle`, etc)">>
|
||||
])
|
||||
@@ -1234,78 +1193,6 @@ failing_contracts() ->
|
||||
<<?Pos(13,20)
|
||||
"Found a hole of type `'a`">>
|
||||
])
|
||||
, ?TYPE_ERROR(toplevel_constants_contract_as_namespace,
|
||||
[<<?Pos(5,13)
|
||||
"Invalid use of the contract constant `G.const`.\n"
|
||||
"Toplevel contract constants can only be used in the contracts where they are defined.">>,
|
||||
<<?Pos(10,11)
|
||||
"Record type `G` does not have field `const`">>,
|
||||
<<?Pos(10,11)
|
||||
"Unbound field const">>,
|
||||
<<?Pos(11,11)
|
||||
"Record type `G` does not have field `const`">>,
|
||||
<<?Pos(11,11)
|
||||
"Unbound field const">>
|
||||
])
|
||||
, ?TYPE_ERROR(toplevel_constants_cycles,
|
||||
[<<?Pos(2,21)
|
||||
"Unbound variable `selfcycle`">>,
|
||||
<<?Pos(4,5)
|
||||
"Mutual recursion detected between the constants\n"
|
||||
" - `cycle1` at line 4, column 5\n"
|
||||
" - `cycle2` at line 5, column 5\n"
|
||||
" - `cycle3` at line 6, column 5">>
|
||||
])
|
||||
, ?TYPE_ERROR(toplevel_constants_in_interface,
|
||||
[<<?Pos(2,10)
|
||||
"The name of the compile-time constant cannot have pattern matching">>,
|
||||
<<?Pos(3,5)
|
||||
"Cannot define toplevel constants inside a contract interface">>,
|
||||
<<?Pos(4,5)
|
||||
"Cannot define toplevel constants inside a contract interface">>
|
||||
])
|
||||
, ?TYPE_ERROR(toplevel_constants_invalid_expr,
|
||||
[<<?Pos(10,9)
|
||||
"Invalid expression in the definition of the constant `c01`\n"
|
||||
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>,
|
||||
<<?Pos(11,9)
|
||||
"Invalid expression in the definition of the constant `c02`\n"
|
||||
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>,
|
||||
<<?Pos(12,9)
|
||||
"Invalid expression in the definition of the constant `c03`\n"
|
||||
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>,
|
||||
<<?Pos(13,9)
|
||||
"Invalid expression in the definition of the constant `c04`\n"
|
||||
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>,
|
||||
<<?Pos(14,9)
|
||||
"Invalid expression in the definition of the constant `c05`\n"
|
||||
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>,
|
||||
<<?Pos(17,9)
|
||||
"Invalid expression in the definition of the constant `c07`\n"
|
||||
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>,
|
||||
<<?Pos(18,9)
|
||||
"Invalid expression in the definition of the constant `c08`\n"
|
||||
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>,
|
||||
<<?Pos(19,9)
|
||||
"Invalid expression in the definition of the constant `c09`\n"
|
||||
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>,
|
||||
<<?Pos(20,9)
|
||||
"Invalid expression in the definition of the constant `c10`\n"
|
||||
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>,
|
||||
<<?Pos(21,9)
|
||||
"Invalid expression in the definition of the constant `c11`\n"
|
||||
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>
|
||||
])
|
||||
, ?TYPE_ERROR(toplevel_constants_invalid_id,
|
||||
[<<?Pos(2,9)
|
||||
"The name of the compile-time constant cannot have pattern matching">>,
|
||||
<<?Pos(3,9)
|
||||
"The name of the compile-time constant cannot have pattern matching">>
|
||||
])
|
||||
, ?TYPE_ERROR(too_many_tvars,
|
||||
[<<?Pos(2,3)
|
||||
"Too many type variables (max 256) in definition of `too_many`">>
|
||||
])
|
||||
].
|
||||
|
||||
validation_test_() ->
|
||||
@@ -1345,7 +1232,7 @@ validation_fails() ->
|
||||
validate(Contract1, Contract2) ->
|
||||
case compile(Contract1) of
|
||||
ByteCode = #{ fate_code := FCode } ->
|
||||
FCode1 = gmb_fate_code:serialize(gmb_fate_code:strip_init_function(FCode)),
|
||||
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,
|
||||
@@ -1353,9 +1240,7 @@ validate(Contract1, Contract2) ->
|
||||
true -> [debug_mode];
|
||||
false -> []
|
||||
end ++
|
||||
[ {src_file, lists:concat([Contract2, ".aes"])}
|
||||
, {include, {file_system, [aeso_test_utils:contract_path()]}}
|
||||
]);
|
||||
[{include, {file_system, [aeso_test_utils:contract_path()]}}]);
|
||||
Error -> print_and_throw(Error)
|
||||
end.
|
||||
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
-module(aeso_encode_decode_tests).
|
||||
|
||||
-compile([export_all, nowarn_export_all]).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-define(EMPTY, "contract C =\n entrypoint f() = true").
|
||||
|
||||
encode_decode_test_() ->
|
||||
[ {lists:flatten(io_lib:format("Testing encode-decode roundtrip for ~p : ~p", [Value, {EType, DType}])),
|
||||
fun() ->
|
||||
{ok, SerRes} = aeso_compiler:encode_value(?EMPTY, EType, Value, []),
|
||||
{ok, Expr} = aeso_compiler:decode_value(?EMPTY, DType, SerRes, []),
|
||||
Value2 = prettypr:format(aeso_pretty:expr(Expr)),
|
||||
?assertEqual(Value, Value2)
|
||||
end} || {Value, EType, DType} <- test_data() ].
|
||||
|
||||
test_data() ->
|
||||
lists:map(fun({V, T}) -> {V, T, T};
|
||||
({V, T1, T2}) -> {V, T1, T2} end, data()).
|
||||
|
||||
data() ->
|
||||
[ {"42", "int"}
|
||||
, {"- 42", "int"}
|
||||
, {"true", "bool"}
|
||||
, {"ak_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ", "address"}
|
||||
, {"ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ", "C"}
|
||||
, {"Some(42)", "option(int)"}
|
||||
, {"None", "option(int)"}
|
||||
, {"(true, 42)", "bool * int"}
|
||||
, {"{[1] = true, [3] = false}", "map(int, bool)"}
|
||||
, {"()", "unit"}
|
||||
, {"#000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f", "hash"}
|
||||
, {"#000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f", "bytes(32)"}
|
||||
, {"sg_MhibzTP1wWzGCTjtPFr1TiPqRJrrJqw7auvEuF5i3FdoALWqXLBDY6xxRRNUSPHK3EQTnTzF12EyspkxrSMxVHKsZeSMj", "signature"}
|
||||
, {"sg_MhibzTP1wWzGCTjtPFr1TiPqRJrrJqw7auvEuF5i3FdoALWqXLBDY6xxRRNUSPHK3EQTnTzF12EyspkxrSMxVHKsZeSMj", "bytes(64)", "signature"}
|
||||
, {"#0102030405060708090a0b0c0d0e0f101718192021222324252627282930313233343536373839401a1b1c1d1e1f202122232425262728293031323334353637", "bytes(64)"}
|
||||
, {"#0102030405060708090a0b0c0d0e0f101718192021222324252627282930313233343536373839401a1b1c1d1e1f202122232425262728293031323334353637", "signature", "bytes(64)"}
|
||||
].
|
||||
|
||||
@@ -53,7 +53,7 @@ simple_contracts_test_() ->
|
||||
%% associativity
|
||||
[ RightAssoc(Op) || Op <- ["||", "&&", "::", "++"] ],
|
||||
[ NonAssoc(Op) || Op <- ["==", "!=", "<", ">", "=<", ">="] ],
|
||||
[ LeftAssoc(Op) || Op <- ["+", "-", "*", "/", "mod", "band", "bor", "bxor", "<<", ">>"] ],
|
||||
[ LeftAssoc(Op) || Op <- ["+", "-", "*", "/", "mod"] ],
|
||||
|
||||
%% precedence
|
||||
[ Stronger(Op2, Op1) || [T1 , T2 | _] <- tails(Tiers), Op1 <- T1, Op2 <- T2 ],
|
||||
|
||||
@@ -39,8 +39,7 @@ all_tokens() ->
|
||||
%% Symbols
|
||||
lists:map(Lit, [',', '.', ';', '|', ':', '(', ')', '[', ']', '{', '}']) ++
|
||||
%% Operators
|
||||
lists:map(Lit, ['=', '==', '!=', '>', '<', '>=', '=<', '-', '+', '++', '*', '/', mod,
|
||||
':', '::', '->', '=>', '||', '&&', '!', 'band', 'bor', 'bxor', 'bnot' ,'<<', '>>']) ++
|
||||
lists:map(Lit, ['=', '==', '!=', '>', '<', '>=', '=<', '-', '+', '++', '*', '/', mod, ':', '::', '->', '=>', '||', '&&', '!']) ++
|
||||
%% Keywords
|
||||
lists:map(Lit, [contract, type, 'let', switch]) ++
|
||||
%% Comment token (not an actual token), just for tests
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
-export([read_contract/1, contract_path/0]).
|
||||
|
||||
contract_path() ->
|
||||
filename:join(code:lib_dir(sophia, test), "contracts").
|
||||
filename:join(code:lib_dir(aesophia, test), "contracts").
|
||||
|
||||
%% Read a contract file from the test/contracts directory.
|
||||
-spec read_contract(string() | atom()) -> string().
|
||||
|
||||
+17
-17
@@ -6,77 +6,77 @@ main contract AENSTest =
|
||||
// Name resolution
|
||||
|
||||
stateful entrypoint resolve_word(name : string, key : string) : option(address) =
|
||||
AENSv2.resolve(name, key)
|
||||
AENS.resolve(name, key)
|
||||
|
||||
stateful entrypoint resolve_string(name : string, key : string) : option(string) =
|
||||
AENSv2.resolve(name, key)
|
||||
AENS.resolve(name, key)
|
||||
|
||||
stateful entrypoint resolve_contract(name : string, key : string) : option(C) =
|
||||
AENSv2.resolve(name, key)
|
||||
AENS.resolve(name, key)
|
||||
|
||||
stateful entrypoint resolve_oracle(name : string, key : string) : option(oracle(int, int)) =
|
||||
AENSv2.resolve(name, key)
|
||||
AENS.resolve(name, key)
|
||||
|
||||
stateful entrypoint resolve_oracle_query(name : string, key : string) : option(oracle_query(int, int)) =
|
||||
AENSv2.resolve(name, key)
|
||||
AENS.resolve(name, key)
|
||||
|
||||
// Transactions
|
||||
|
||||
stateful entrypoint preclaim(addr : address, // Claim on behalf of this account (can be Contract.address)
|
||||
chash : hash) : unit = // Commitment hash
|
||||
AENSv2.preclaim(addr, chash)
|
||||
AENS.preclaim(addr, chash)
|
||||
|
||||
stateful entrypoint signedPreclaim(addr : address, // Claim on behalf of this account (can be Contract.address)
|
||||
chash : hash, // Commitment hash
|
||||
sign : signature) : unit = // Signed by addr (if not Contract.address)
|
||||
AENSv2.preclaim(addr, chash, signature = sign)
|
||||
AENS.preclaim(addr, chash, signature = sign)
|
||||
|
||||
stateful entrypoint claim(addr : address,
|
||||
name : string,
|
||||
salt : int,
|
||||
name_fee : int) : unit =
|
||||
AENSv2.claim(addr, name, salt, name_fee)
|
||||
AENS.claim(addr, name, salt, name_fee)
|
||||
|
||||
stateful entrypoint signedClaim(addr : address,
|
||||
name : string,
|
||||
salt : int,
|
||||
name_fee : int,
|
||||
sign : signature) : unit =
|
||||
AENSv2.claim(addr, name, salt, name_fee, signature = sign)
|
||||
AENS.claim(addr, name, salt, name_fee, signature = sign)
|
||||
|
||||
|
||||
stateful entrypoint update(owner : address,
|
||||
name : string,
|
||||
ttl : option(Chain.ttl),
|
||||
client_ttl : option(int),
|
||||
pointers : option(map(string, AENSv2.pointee))) : unit =
|
||||
AENSv2.update(owner, name, ttl, client_ttl, pointers)
|
||||
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, AENSv2.pointee)),
|
||||
pointers : option(map(string, AENS.pointee)),
|
||||
sign : signature) : unit =
|
||||
AENSv2.update(owner, name, ttl, client_ttl, pointers, signature = sign)
|
||||
AENS.update(owner, name, ttl, client_ttl, pointers, signature = sign)
|
||||
|
||||
|
||||
stateful entrypoint transfer(owner : address,
|
||||
new_owner : address,
|
||||
name : string) : unit =
|
||||
AENSv2.transfer(owner, new_owner, name)
|
||||
AENS.transfer(owner, new_owner, name)
|
||||
|
||||
stateful entrypoint signedTransfer(owner : address,
|
||||
new_owner : address,
|
||||
name : string,
|
||||
sign : signature) : unit =
|
||||
AENSv2.transfer(owner, new_owner, name, signature = sign)
|
||||
AENS.transfer(owner, new_owner, name, signature = sign)
|
||||
|
||||
stateful entrypoint revoke(owner : address,
|
||||
name : string) : unit =
|
||||
AENSv2.revoke(owner, name)
|
||||
AENS.revoke(owner, name)
|
||||
|
||||
stateful entrypoint signedRevoke(owner : address,
|
||||
name : string,
|
||||
sign : signature) : unit =
|
||||
AENSv2.revoke(owner, name, signature = sign)
|
||||
AENS.revoke(owner, name, signature = sign)
|
||||
|
||||
@@ -1,32 +1,17 @@
|
||||
include "Option.aes"
|
||||
include "String.aes"
|
||||
include "AENSCompat.aes"
|
||||
contract interface OldAENSContract =
|
||||
entrypoint set : (string, string, AENS.pointee) => unit
|
||||
entrypoint lookup : (string, string) => AENS.pointee
|
||||
|
||||
main contract AENSUpdate =
|
||||
stateful entrypoint update_name(owner : address, name : string, b : bytes(2)) =
|
||||
let p1 : AENSv2.pointee = AENSv2.AccountPt(Call.caller)
|
||||
let p2 : AENSv2.pointee = AENSv2.OraclePt(Call.caller)
|
||||
let p3 : AENSv2.pointee = AENSv2.ContractPt(Call.caller)
|
||||
let p4 : AENSv2.pointee = AENSv2.ChannelPt(Call.caller)
|
||||
let p5 : AENSv2.pointee = AENSv2.DataPt(String.to_bytes("any something will do"))
|
||||
let p6 : AENSv2.pointee = AENSv2.DataPt(Int.to_bytes(1345, 4))
|
||||
AENSv2.update(owner, name, None, None,
|
||||
Some({ ["account_pubkey"] = p1, ["oracle_pubkey"] = p2,
|
||||
["contract_pubkey"] = p3, ["misc"] = p4, ["data"] = p5, ["data2"] = p6 }))
|
||||
|
||||
stateful entrypoint old_interaction(c : OldAENSContract, owner : address, name : string) =
|
||||
let p : AENS.pointee = c.lookup(name, "key1")
|
||||
AENSv2.update(owner, name, None, None, Some({ ["key1"] = AENSCompat.pointee_to_V2(p) }))
|
||||
switch(AENSv2.lookup(name))
|
||||
Some(AENSv2.Name(_, _, pt_map)) =>
|
||||
c.set(name, "key2", Option.force(AENSCompat.pointee_from_V2(pt_map["key1"])))
|
||||
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(AENSv2.lookup(name))
|
||||
Some(AENSv2.Name(_, FixedTTL(ttl), _)) => ttl
|
||||
switch(AENS.lookup(name))
|
||||
Some(AENS.Name(_, FixedTTL(ttl), _)) => ttl
|
||||
|
||||
entrypoint expiry(o : oracle(int, int)) : int =
|
||||
Oracle.expiry(o)
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
namespace Ns =
|
||||
datatype d('a) = D | S(int) | M('a, list('a), int)
|
||||
private function fff() = 123
|
||||
let const = 1
|
||||
|
||||
stateful entrypoint
|
||||
f (1, x) = (_) => x
|
||||
@@ -34,8 +33,6 @@ contract AllSyntax =
|
||||
|
||||
type state = shakespeare(int)
|
||||
|
||||
let cc = "str"
|
||||
|
||||
entrypoint init() = {
|
||||
johann = 1000,
|
||||
wolfgang = -10,
|
||||
|
||||
@@ -3,7 +3,7 @@ contract BadAENSresolve =
|
||||
type t('a) = option(list('a))
|
||||
|
||||
function fail() : t(int) =
|
||||
AENSv2.resolve("foo.aet", "whatever")
|
||||
AENS.resolve("foo.aet", "whatever")
|
||||
|
||||
entrypoint main_fun() = ()
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
contract BadAENSresolve =
|
||||
using AENSv2
|
||||
using AENS
|
||||
|
||||
type t('a) = option(list('a))
|
||||
|
||||
function fail() : t(int) =
|
||||
resolve("foo.aet", "whatever")
|
||||
|
||||
entrypoint main_fun() = ()
|
||||
entrypoint main_fun() = ()
|
||||
@@ -1,5 +0,0 @@
|
||||
// include "String.aes"
|
||||
contract BytesToX =
|
||||
entrypoint fail1(b : bytes()) = Bytes.to_fixed_size(b)
|
||||
entrypoint fail2(b : bytes(4)) = Bytes.to_fixed_size(b)
|
||||
entrypoint fail3(b : bytes()) = Bytes.to_any_size(b)
|
||||
@@ -1,27 +0,0 @@
|
||||
include "String.aes"
|
||||
contract BytesMisc =
|
||||
entrypoint sizeFixed(b : bytes(4)) : int = Bytes.size(b)
|
||||
entrypoint sizeAny(b : bytes()) : int = Bytes.size(b)
|
||||
entrypoint int_to_bytes(i : int) : bytes() = Int.to_bytes(i, 16)
|
||||
|
||||
entrypoint test(b3 : bytes(3), b7 : bytes(7), bX : bytes, i : int, s : string) =
|
||||
let bi = Int.to_bytes(i, 8)
|
||||
let bs = String.to_bytes(s)
|
||||
|
||||
let b10 = Bytes.concat(b3, b7)
|
||||
|
||||
let (b4, b6 : bytes(6)) = Bytes.split(b10)
|
||||
|
||||
let Some((b8, b2)) = Bytes.split_any(bX, 8)
|
||||
|
||||
let bX7 = Bytes.concat(bX, b7)
|
||||
|
||||
let Some((b5, bX2)) = Bytes.split_any(bX7, 5)
|
||||
|
||||
let Some((b7b, b0)) = Bytes.split_any(bX, Bytes.size(b7))
|
||||
|
||||
let Some(b5b : bytes(5)) = Bytes.to_fixed_size(b5)
|
||||
|
||||
let (b1 : bytes(1), _) = Bytes.split(b5b)
|
||||
|
||||
[bi, bs, b0, Bytes.to_any_size(b1), b2, Bytes.to_any_size(b4), Bytes.to_any_size(b6), b7b, b8, bX2]
|
||||
@@ -6,5 +6,3 @@ contract BytesToX =
|
||||
String.concat(Bytes.to_str(b), Bytes.to_str(#ffff))
|
||||
entrypoint to_str_big(b : bytes(65)) : string =
|
||||
Bytes.to_str(b)
|
||||
entrypoint to_fixed(b : bytes()) : option(bytes(4)) = Bytes.to_fixed_size(b)
|
||||
entrypoint to_any(b : bytes(4)) = Bytes.to_any_size(b)
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
contract C =
|
||||
entrypoint test() =
|
||||
let a : int = 23
|
||||
let b : int = 52
|
||||
let c = a bor b
|
||||
let d = c bxor b
|
||||
let e = d band b
|
||||
let f = bnot a
|
||||
let g = f << 2
|
||||
let h = g >> 2
|
||||
let i = Int.mulmod(a, b, h)
|
||||
let j = Crypto.poseidon(i, a)
|
||||
let k : bytes(32) = Address.to_bytes(Call.origin)
|
||||
let l = sg_MhibzTP1wWzGCTjtPFr1TiPqRJrrJqw7auvEuF5i3FdoALWqXLBDY6xxRRNUSPHK3EQTnTzF12EyspkxrSMxVHKsZeSMj
|
||||
(a bor b band c bxor a << bnot b >> a, k, l)
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
contract ChainTest =
|
||||
|
||||
record state = { last_bf : address
|
||||
, nw_id : string }
|
||||
record state = { last_bf : address }
|
||||
|
||||
function init() : state =
|
||||
{last_bf = Contract.address}
|
||||
@@ -12,6 +11,3 @@ contract ChainTest =
|
||||
|
||||
function save_coinbase() =
|
||||
put(state{last_bf = Chain.coinbase})
|
||||
|
||||
function save_network_id() =
|
||||
put(state{nw_id = Chain.network_id})
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
include "../dir2/baz.aes"
|
||||
namespace D =
|
||||
function g() = E.h()
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
namespace E =
|
||||
function h() = 42
|
||||
|
||||
@@ -20,8 +20,6 @@ contract FunctionArguments =
|
||||
entrypoint read(a : answer(int)) =
|
||||
a.result
|
||||
|
||||
entrypoint any_bytes(b : bytes()) = b
|
||||
|
||||
entrypoint sjutton(b : bytes(17)) =
|
||||
b
|
||||
|
||||
@@ -59,5 +57,3 @@ contract FunctionArguments =
|
||||
entrypoint chain_ga_meta_tx(tx : Chain.ga_meta_tx) = true
|
||||
entrypoint chain_paying_for_tx(tx : Chain.paying_for_tx) = true
|
||||
entrypoint chain_base_tx(tx : Chain.base_tx) = true
|
||||
|
||||
entrypoint sig(sg : signature) = true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
contract PolymorphicAENSresolve =
|
||||
|
||||
function fail() : option('a) =
|
||||
AENSv2.resolve("foo.aet", "whatever")
|
||||
AENS.resolve("foo.aet", "whatever")
|
||||
|
||||
entrypoint main_fun() = ()
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
include "./dir1/bar.aes"
|
||||
contract C =
|
||||
entrypoint f() = D.g()
|
||||
@@ -1,60 +0,0 @@
|
||||
contract C =
|
||||
entrypoint too_many(
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _)) = 0
|
||||
|
||||
entrypoint not_too_many(
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _)) = 0
|
||||
@@ -1,64 +0,0 @@
|
||||
namespace N0 =
|
||||
let nsconst = 1
|
||||
|
||||
namespace N =
|
||||
let nsconst = N0.nsconst
|
||||
|
||||
contract C =
|
||||
datatype event = EventX(int, string)
|
||||
|
||||
record account = { name : string,
|
||||
balance : int }
|
||||
|
||||
let c01 = 2425
|
||||
let c02 = -5
|
||||
let c03 = ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt
|
||||
let c04 = true
|
||||
let c05 = Bits.none
|
||||
let c06 = #fedcba9876543210
|
||||
let c07 = "str"
|
||||
let c08 = [1, 2, 3]
|
||||
let c09 = [(true, 24), (false, 19), (false, -42)]
|
||||
let c10 = (42, "Foo", true)
|
||||
let c11 = { name = "str", balance = 100000000 }
|
||||
let c12 = {["foo"] = 19, ["bar"] = 42}
|
||||
let c13 = Some(42)
|
||||
let c14 = 11 : int
|
||||
let c15 = EventX(0, "Hello")
|
||||
let c16 = #000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
|
||||
let c17 = #000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
|
||||
let c18 = RelativeTTL(50)
|
||||
let c19 = ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5
|
||||
let c20 = oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY
|
||||
let c21 = ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ : C
|
||||
let c22 = N.nsconst
|
||||
let c23 = c01
|
||||
let c24 = c11.name
|
||||
let c25 : int = 1
|
||||
|
||||
entrypoint f01() = c01
|
||||
entrypoint f02() = c02
|
||||
entrypoint f03() = c03
|
||||
entrypoint f04() = c04
|
||||
entrypoint f05() = c05
|
||||
entrypoint f06() = c06
|
||||
entrypoint f07() = c07
|
||||
entrypoint f08() = c08
|
||||
entrypoint f09() = c09
|
||||
entrypoint f10() = c10
|
||||
entrypoint f11() = c11
|
||||
entrypoint f12() = c12
|
||||
entrypoint f13() = c13
|
||||
entrypoint f14() = c14
|
||||
entrypoint f15() = c15
|
||||
entrypoint f16() = c16
|
||||
entrypoint f17() = c17
|
||||
entrypoint f18() = c18
|
||||
entrypoint f19() = c19
|
||||
entrypoint f20() = c20
|
||||
entrypoint f21() = c21
|
||||
entrypoint f22() = c22
|
||||
entrypoint f23() = c23
|
||||
entrypoint f24() = c24
|
||||
entrypoint f25() = c25
|
||||
entrypoint fqual() = C.c01
|
||||
@@ -1,11 +0,0 @@
|
||||
contract G =
|
||||
let const = 1
|
||||
|
||||
main contract C =
|
||||
let c = G.const
|
||||
|
||||
stateful entrypoint f() =
|
||||
let g = Chain.create() : G
|
||||
|
||||
g.const
|
||||
g.const()
|
||||
@@ -1,6 +0,0 @@
|
||||
contract C =
|
||||
let selfcycle = selfcycle
|
||||
|
||||
let cycle1 = cycle2
|
||||
let cycle2 = cycle3
|
||||
let cycle3 = cycle1
|
||||
@@ -1,7 +0,0 @@
|
||||
contract interface I =
|
||||
let (x::y::_) = [1,2,3]
|
||||
let c = 10
|
||||
let d = 10
|
||||
|
||||
contract C =
|
||||
entrypoint init() = ()
|
||||
@@ -1,21 +0,0 @@
|
||||
main contract C =
|
||||
record account = { name : string,
|
||||
balance : int }
|
||||
|
||||
let one = 1
|
||||
let opt = Some(5)
|
||||
let acc = { name = "str", balance = 100000 }
|
||||
let mpp = {["foo"] = 19, ["bar"] = 42}
|
||||
|
||||
let c01 = [x | x <- [1,2,3,4,5]]
|
||||
let c02 = [x + k | x <- [1,2,3,4,5], let k = x*x]
|
||||
let c03 = [x + y | x <- [1,2,3,4,5], let k = x*x, if (k > 5), y <- [k, k+1, k+2]]
|
||||
let c04 = if (one > 2) 3 else 4
|
||||
let c05 = switch (opt)
|
||||
Some(x) => x
|
||||
None => 2
|
||||
let c07 = acc{ balance = one }
|
||||
let c08 = mpp["foo"]
|
||||
let c09 = mpp["non" = 10]
|
||||
let c10 = mpp{["foo"] = 20}
|
||||
let c11 = (x) => x + 1
|
||||
@@ -1,3 +0,0 @@
|
||||
contract C =
|
||||
let x::_ = [1,2,3,4]
|
||||
let y::(p = z::_) = [1,2,3,4]
|
||||
@@ -0,0 +1,3 @@
|
||||
contract C =
|
||||
let this_is_illegal = 2/0
|
||||
entrypoint this_is_legal() = 2/0
|
||||
@@ -2,10 +2,10 @@
|
||||
// Named argument builtins are:
|
||||
// Oracle.register
|
||||
// Oracle.respond
|
||||
// AENSv2.preclaim
|
||||
// AENSv2.claim
|
||||
// AENSv2.transfer
|
||||
// AENSv2.revoke
|
||||
// AENS.preclaim
|
||||
// AENS.claim
|
||||
// AENS.transfer
|
||||
// AENS.revoke
|
||||
// Oracle.extend
|
||||
include "String.aes"
|
||||
contract UnappliedBuiltins =
|
||||
@@ -28,7 +28,7 @@ contract UnappliedBuiltins =
|
||||
function oracle_get_answer() = Oracle.get_answer : (o, _) => _
|
||||
function oracle_check() = Oracle.check : o => _
|
||||
function oracle_check_query() = Oracle.check_query : (o, _) => _
|
||||
function aens_resolve() = AENSv2.resolve : (_, _) => option(string)
|
||||
function aens_resolve() = AENS.resolve : (_, _) => option(string)
|
||||
function map_lookup() = Map.lookup : (_, m) => _
|
||||
function map_lookup_default() = Map.lookup_default : (_, m, _) => _
|
||||
function map_member() = Map.member : (_, m) => _
|
||||
@@ -36,7 +36,7 @@ contract UnappliedBuiltins =
|
||||
function map_delete() = Map.delete : (_, m) => _
|
||||
function map_from_list() = Map.from_list : _ => m
|
||||
function map_to_list() = Map.to_list : m => _
|
||||
function crypto_verify_sig() = Crypto.verify_sig : (bytes(), _, _) => _
|
||||
function crypto_verify_sig() = Crypto.verify_sig
|
||||
function crypto_verify_sig_secp256k1() = Crypto.verify_sig_secp256k1
|
||||
function crypto_ecverify_secp256k1() = Crypto.ecverify_secp256k1
|
||||
function crypto_ecrecover_secp256k1() = Crypto.ecrecover_secp256k1
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
contract Test =
|
||||
type option_int = option(int)
|
||||
record option_point = {x: int, y: option_int}
|
||||
|
||||
entrypoint test_option_record(a: option_point) = a
|
||||
@@ -65,24 +65,3 @@ contract Remote =
|
||||
contract C =
|
||||
payable stateful entrypoint
|
||||
call_missing_con() : int = (ct_1111111111111111111111111111112JF6Dz72 : Remote).id(value = 1, 0)
|
||||
|
||||
namespace ShadowingConst =
|
||||
let const = 1
|
||||
|
||||
function f() =
|
||||
let const = 2
|
||||
const
|
||||
|
||||
namespace UnusedConstNamespace =
|
||||
// No warnings should be shown even though const is not used
|
||||
let const = 1
|
||||
|
||||
contract UnusedConstContract =
|
||||
// Only `c` should show a warning because it is never used in the contract
|
||||
let a = 1
|
||||
let b = 2
|
||||
let c = 3
|
||||
|
||||
entrypoint f() =
|
||||
// Both normal access and qualified access should prevent the unused const warning
|
||||
a + UnusedConstContract.b
|
||||
|
||||
Reference in New Issue
Block a user