Compare commits
81 Commits
ghallak/229
...
zomp
| Author | SHA1 | Date | |
|---|---|---|---|
| 3771b6a5f2 | |||
| ec316d2a0d | |||
| 6c6bf38d8c | |||
| 9f90396023 | |||
| 927cd42592 | |||
| 16308a7840 | |||
| 46a307432f | |||
| 83dcc6dbc4 | |||
| ffdd4ecf26 | |||
| 51bae61736 | |||
| 31301911a2 | |||
| de426a210b | |||
| 944ed49f0b | |||
| 1387e814f8 | |||
| 44d6982d66 | |||
| aa532046d3 | |||
| fbaab570f2 | |||
| 1a80f3faa0 | |||
| 745eeda858 | |||
| 78b758c337 | |||
| 108cb1f948 | |||
| 2c8dcf8032 | |||
| c51d0a5e21 | |||
| e44174b71c | |||
| dcea538e11 | |||
| f60f9122ba | |||
| 5c3b42aff1 | |||
| dbeb792ca5 | |||
| f75455bb85 | |||
| cdbd430f23 | |||
| 03d6dd6ca2 | |||
| 33229c3513 | |||
| 002e55d529 | |||
| 9b518150c3 | |||
| 67948513d5 | |||
| 08fa372c24 | |||
| 3b0ca28c8e | |||
| 86d7b36ba7 | |||
| 43c8328615 | |||
| c15d411660 | |||
| b902226c26 | |||
| c1e8195fd8 | |||
| d5ff9d4a2f | |||
| c395849684 | |||
| 7bac15949c | |||
| f11697da1a | |||
| fc2731fbbb | |||
| cc76f93dd7 | |||
| 7b6eba5319 | |||
| 99bb3fe1fb | |||
| add82fe5ee | |||
| 311bf49505 | |||
| 0e3bcba07d | |||
| 699d1f7ab8 | |||
| 1a40a93157 | |||
| c078119bc4 | |||
| 31fd8fe24f | |||
| 9ad8e26e88 | |||
| 5adeb6c93e | |||
| 256df25af4 | |||
| 83abfae32b | |||
| 4ca90feea0 | |||
| 09638daa90 | |||
| d59023a9f4 | |||
| 34b52739fd | |||
| 1c83287d45 | |||
| da92ddbd5d | |||
| c1c169273c | |||
| ad4c341a4a | |||
| f964fa89a1 | |||
| 8d8d9c6b83 | |||
| c98ea25e8b | |||
| 4dbc9858fb | |||
| 51f9eaa934 | |||
| 0ebcf006e2 | |||
| 381a7c98cd | |||
| 4bec4e5107 | |||
| 4dd247b159 | |||
| d926c4a7e3 | |||
| 7b8957b46a | |||
| e46226a693 |
@@ -3,7 +3,7 @@ version: 2.1
|
|||||||
executors:
|
executors:
|
||||||
aebuilder:
|
aebuilder:
|
||||||
docker:
|
docker:
|
||||||
- image: aeternity/builder:xenial-otp21
|
- image: aeternity/builder:bionic-otp24
|
||||||
user: builder
|
user: builder
|
||||||
working_directory: ~/aesophia
|
working_directory: ~/aesophia
|
||||||
|
|
||||||
|
|||||||
@@ -13,10 +13,6 @@ jobs:
|
|||||||
- uses: actions/setup-python@v2
|
- uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
python-version: 3.8
|
python-version: 3.8
|
||||||
- uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: ~/.cache/pip3
|
|
||||||
key: ${{ runner.os }}-pip-${{ hashFiles('.github/workflows/requirements.txt') }}
|
|
||||||
- run: pip3 install -r .github/workflows/requirements.txt -U
|
- run: pip3 install -r .github/workflows/requirements.txt -U
|
||||||
- run: git config --global user.email "github-action@users.noreply.github.com"
|
- run: git config --global user.email "github-action@users.noreply.github.com"
|
||||||
- run: git config --global user.name "GitHub Action"
|
- run: git config --global user.name "GitHub Action"
|
||||||
|
|||||||
@@ -13,10 +13,6 @@ jobs:
|
|||||||
- uses: actions/setup-python@v2
|
- uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
python-version: 3.8
|
python-version: 3.8
|
||||||
- uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: ~/.cache/pip3
|
|
||||||
key: ${{ runner.os }}-pip-${{ hashFiles('.github/workflows/requirements.txt') }}
|
|
||||||
- run: pip3 install -r .github/workflows/requirements.txt -U
|
- run: pip3 install -r .github/workflows/requirements.txt -U
|
||||||
- run: git config --global user.email "github-action@users.noreply.github.com"
|
- run: git config --global user.email "github-action@users.noreply.github.com"
|
||||||
- run: git config --global user.name "GitHub Action"
|
- run: git config --global user.name "GitHub Action"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
mkdocs==1.2.4
|
mkdocs==1.4.2
|
||||||
mkdocs-simple-hooks==0.1.3
|
mkdocs-simple-hooks==0.1.5
|
||||||
mkdocs-material==7.1.9
|
mkdocs-material==9.0.9
|
||||||
mike==1.0.1
|
mike==1.1.2
|
||||||
pygments==2.11.2
|
pygments==2.17.2
|
||||||
|
|||||||
@@ -24,3 +24,4 @@ current_counterexample.eqc
|
|||||||
test/contracts/test.aes
|
test/contracts/test.aes
|
||||||
__pycache__
|
__pycache__
|
||||||
.docssite/docs/*.md
|
.docssite/docs/*.md
|
||||||
|
.vscode
|
||||||
|
|||||||
+123
-2
@@ -6,7 +6,117 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
### Added
|
### Added
|
||||||
- Compiler warnings for the follwing: shadowing, negative spends, division by zero, unused functions, unused includes, unused stateful annotations, unused variables, unused parameters, unused user-defined type, dead return value.
|
- 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
|
||||||
|
### 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.
|
||||||
|
### Fixed
|
||||||
|
- Warning about unused include when there is no include.
|
||||||
|
|
||||||
|
## [7.1.0]
|
||||||
|
### Added
|
||||||
|
- Options to enable/disable certain optimizations.
|
||||||
|
- The ability to call a different instance of the current contract
|
||||||
|
```
|
||||||
|
contract Main =
|
||||||
|
entrypoint spend(x : int) : int = x
|
||||||
|
entrypoint f(c : Main) : int = c.spend(10)
|
||||||
|
```
|
||||||
|
- Return a mapping from variables to FATE registers in the compilation output.
|
||||||
|
- Hole expression.
|
||||||
|
### Changed
|
||||||
|
- Type definitions serialised to ACI as `typedefs` field instead of `type_defs` to increase compatibility.
|
||||||
|
- Check contracts and entrypoints modifiers when implementing interfaces.
|
||||||
|
- Contracts can no longer be used as namespaces.
|
||||||
|
- Do not show unused stateful warning for functions that call other contracts with a non-zero value argument.
|
||||||
|
### Fixed
|
||||||
|
- Typechecker crashes if Chain.create or Chain.clone are used without arguments.
|
||||||
|
|
||||||
|
## [7.0.1]
|
||||||
|
### Added
|
||||||
|
- Add CONTRIBUTING.md file.
|
||||||
|
### Changed
|
||||||
|
- Update Sophia syntax docs to include missing information about existing syntax.
|
||||||
|
### Fixed
|
||||||
|
- [404](https://github.com/aeternity/aesophia/issues/404) Contract polymorphism crashes on non-obvious child contract typing.
|
||||||
|
|
||||||
|
## [7.0.0]
|
||||||
|
### Added
|
||||||
|
- Added support for `EXIT` opcode via `exit : (string) => 'a` function (behaves same as `ABORT`, but consumes all gas).
|
||||||
|
- Compiler warnings for the following: shadowing, negative spends, division by zero, unused functions, unused includes, unused stateful annotations, unused variables, unused parameters, unused user-defined type, dead return value.
|
||||||
- The pipe operator |>
|
- The pipe operator |>
|
||||||
```
|
```
|
||||||
[1, 2, 3] |> List.first |> Option.is_some // Option.is_some(List.first([1, 2, 3]))
|
[1, 2, 3] |> List.first |> Option.is_some // Option.is_some(List.first([1, 2, 3]))
|
||||||
@@ -16,6 +126,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
function sum(l : list(int)) : int = foldl((+), 0, l)
|
function sum(l : list(int)) : int = foldl((+), 0, l)
|
||||||
function logical_and(x, y) = (&&)(x, y)
|
function logical_and(x, y) = (&&)(x, y)
|
||||||
```
|
```
|
||||||
|
- Contract interfaces polymorphism
|
||||||
### Changed
|
### Changed
|
||||||
- Error messages have been restructured (less newlines) to provide more unified errors. Also `pp_oneline/1` has been added.
|
- Error messages have been restructured (less newlines) to provide more unified errors. Also `pp_oneline/1` has been added.
|
||||||
- Ban empty record definitions (e.g. `record r = {}` would give an error).
|
- Ban empty record definitions (e.g. `record r = {}` would give an error).
|
||||||
@@ -345,7 +456,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Simplify calldata creation - instead of passing a compiled contract, simply
|
- Simplify calldata creation - instead of passing a compiled contract, simply
|
||||||
pass a (stubbed) contract string.
|
pass a (stubbed) contract string.
|
||||||
|
|
||||||
[Unreleased]: https://github.com/aeternity/aesophia/compare/v6.1.0...HEAD
|
[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
|
||||||
|
[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
|
||||||
[6.1.0]: https://github.com/aeternity/aesophia/compare/v6.0.2...v6.1.0
|
[6.1.0]: https://github.com/aeternity/aesophia/compare/v6.0.2...v6.1.0
|
||||||
[6.0.2]: https://github.com/aeternity/aesophia/compare/v6.0.1...v6.0.2
|
[6.0.2]: https://github.com/aeternity/aesophia/compare/v6.0.1...v6.0.2
|
||||||
[6.0.1]: https://github.com/aeternity/aesophia/compare/v6.0.0...v6.0.1
|
[6.0.1]: https://github.com/aeternity/aesophia/compare/v6.0.0...v6.0.1
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# Contributing to Sophia
|
||||||
|
|
||||||
|
## Checklist For Creating New Pull Requests
|
||||||
|
|
||||||
|
The following points should be considered before creating a new PR to the Sophia compiler.
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
- The [Changelog](CHANGELOG.md) file should be updated for all PRs.
|
||||||
|
- If a PR introduces a new feature that is relevant to the users of the language, the [Sophia Features Documentation](docs/sophia_features.md) should be updated to describe the new feature.
|
||||||
|
- If a PR introduces new syntax (e.g. changes in [aeso_syntax.erl](src/aeso_syntax.erl), [aeso_scan.erl](src/aeso_scan.erl), or [aeso_parser.erl](src/aeso_parser.erl)), the [Sophia Syntax Documentation](docs/sophia_syntax.md) should be updated to include the new syntax.
|
||||||
|
- If a PR introduces a new library, the public interface of the new library should be fully documented in the [Sophia Standard Library Documentation](docs/sophia_stdlib.md).
|
||||||
|
- If a PR introduces a new compiler option, the new option should be documented in the file
|
||||||
|
[aeso_compiler.md](docs/aeso_compiler.md).
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
|
||||||
|
- If a PR introduces new syntax (e.g. changes in [aeso_syntax.erl](src/aeso_syntax.erl), [aeso_scan.erl](src/aeso_scan.erl), or [aeso_parser.erl](src/aeso_parser.erl)), the contract [all_syntax.aes](test/contracts/all_syntax.aes) should be updated to include the new syntax.
|
||||||
|
- If a PR fixes a bug, the code that replicates the bug should be added as a new passing test contract.
|
||||||
|
- If a PR introduces a new feature, add tests for both successful and failing usage of that feature. In order to run the entire compilation pipeline and to avoid erroring during intermediate steps, failing tests should not be mixed with the successful ones.
|
||||||
|
|
||||||
|
### Source Code
|
||||||
|
|
||||||
|
- If a PR introduces new syntax (e.g. changes in [aeso_syntax.erl](src/aeso_syntax.erl), [aeso_scan.erl](src/aeso_scan.erl), or [aeso_parser.erl](src/aeso_parser.erl)), the following code should be updated to handle the new syntax:
|
||||||
|
- The function `aeso_syntax_utils:fold/4` in the file [aeso_syntax_utils.erl](src/aeso_syntax_utils.erl).
|
||||||
|
- Any related pretty printing function in the file [aeso_pretty.erl](src/aeso_pretty.erl), depending on the type of the newly added syntax.
|
||||||
|
|
||||||
|
## Checklist For Creating a Release
|
||||||
|
|
||||||
|
- Update the version in the file [aesophia.app.src](src/aesophia.app.src).
|
||||||
|
- Update the version in the file [rebar.config](rebar.config).
|
||||||
|
- In the [Changelog](CHANGELOG.md):
|
||||||
|
- Update the `Unreleased` changes to be under the new version.
|
||||||
|
- Update the version at the bottom of the file.
|
||||||
|
- Commit and the changes and create a new PR (check the commit of [v6.1.0](https://github.com/aeternity/aesophia/commit/5ad5270e381f6e810d7b8b5cdc168d283e7a90bb) for reference).
|
||||||
|
- Create a release after merging the new PR to `master` branch.
|
||||||
|
- After releasing `aesophia`, refer to each of the following repositories and create new releases as well, using the new `aesophia` release:
|
||||||
|
- [aesophia_cli](https://github.com/aeternity/aesophia_cli)
|
||||||
|
- [aesophia_http](https://github.com/aeternity/aesophia_http)
|
||||||
|
- [aerepl](https://github.com/aeternity/aerepl)
|
||||||
@@ -14,6 +14,7 @@ The compiler is currently being used three places
|
|||||||
* [Features](docs/sophia_features.md)
|
* [Features](docs/sophia_features.md)
|
||||||
* [Standard library](docs/sophia_stdlib.md)
|
* [Standard library](docs/sophia_stdlib.md)
|
||||||
* [Contract examples](docs/sophia_examples.md)
|
* [Contract examples](docs/sophia_examples.md)
|
||||||
|
* [Contributing](CONTRIBUTING.md)
|
||||||
|
|
||||||
Additionally you can check out the [contracts section](https://github.com/aeternity/protocol/blob/master/contracts/contracts.md) of the æternity blockchain specification.
|
Additionally you can check out the [contracts section](https://github.com/aeternity/protocol/blob/master/contracts/contracts.md) of the æternity blockchain specification.
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -67,7 +67,7 @@ generates the following JSON structure representing the contract interface:
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"type_defs": [
|
"typedefs": [
|
||||||
{
|
{
|
||||||
"name": "answers",
|
"name": "answers",
|
||||||
"typedef": {
|
"typedef": {
|
||||||
@@ -138,7 +138,7 @@ be included inside another contract.
|
|||||||
state =>
|
state =>
|
||||||
#{record =>
|
#{record =>
|
||||||
[#{name => <<"a">>,type => <<"Answers.answers">>}]},
|
[#{name => <<"a">>,type => <<"Answers.answers">>}]},
|
||||||
type_defs =>
|
typedefs =>
|
||||||
[#{name => <<"answers">>,
|
[#{name => <<"answers">>,
|
||||||
typedef => #{<<"map">> => [<<"string">>,<<"int">>]},
|
typedef => #{<<"map">> => [<<"string">>,<<"int">>]},
|
||||||
vars => []}]}}]}
|
vars => []}]}}]}
|
||||||
|
|||||||
@@ -51,6 +51,34 @@ The **pp_** options all print to standard output the following:
|
|||||||
|
|
||||||
`pp_assembler` - print the generated assembler code
|
`pp_assembler` - print the generated assembler code
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
#### Options to control which compiler optimizations should run:
|
||||||
|
|
||||||
|
By default all optimizations are turned on, to disable an optimization, it should be
|
||||||
|
explicitly set to false and passed as a compiler option.
|
||||||
|
|
||||||
|
List of optimizations:
|
||||||
|
|
||||||
|
- optimize_inliner
|
||||||
|
- optimize_inline_local_functions
|
||||||
|
- optimize_bind_subexpressions
|
||||||
|
- optimize_let_floating
|
||||||
|
- optimize_simplifier
|
||||||
|
- optimize_drop_unused_lets
|
||||||
|
- optimize_push_consume
|
||||||
|
- optimize_one_shot_var
|
||||||
|
- optimize_write_to_dead_var
|
||||||
|
- optimize_inline_switch_target
|
||||||
|
- optimize_swap_push
|
||||||
|
- optimize_swap_pop
|
||||||
|
- optimize_swap_write
|
||||||
|
- optimize_constant_propagation
|
||||||
|
- optimize_prune_impossible_branches
|
||||||
|
- optimize_single_successful_branch
|
||||||
|
- optimize_inline_store
|
||||||
|
- optimize_float_switch_bod
|
||||||
|
|
||||||
#### check_call(ContractString, Options) -> CheckRet
|
#### check_call(ContractString, Options) -> CheckRet
|
||||||
|
|
||||||
Types
|
Types
|
||||||
|
|||||||
+288
-13
@@ -84,7 +84,7 @@ the return value of the call.
|
|||||||
|
|
||||||
```sophia
|
```sophia
|
||||||
contract interface VotingType =
|
contract interface VotingType =
|
||||||
entrypoint : vote : string => unit
|
entrypoint vote : string => unit
|
||||||
|
|
||||||
contract Voter =
|
contract Voter =
|
||||||
entrypoint tryVote(v : VotingType, alt : string) =
|
entrypoint tryVote(v : VotingType, alt : string) =
|
||||||
@@ -134,6 +134,171 @@ main contract IntHolderFactory =
|
|||||||
In case of a presence of child contracts (`IntHolder` in this case), the main
|
In case of a presence of child contracts (`IntHolder` in this case), the main
|
||||||
contract must be pointed out with the `main` keyword as shown in the example.
|
contract must be pointed out with the `main` keyword as shown in the example.
|
||||||
|
|
||||||
|
### Contract interfaces and polymorphism
|
||||||
|
|
||||||
|
Contracts can implement one or multiple interfaces, the contract has to define
|
||||||
|
every entrypoint from the implemented interface and the entrypoints in both
|
||||||
|
the contract and implemented interface should have compatible types.
|
||||||
|
|
||||||
|
```
|
||||||
|
contract interface Animal =
|
||||||
|
entrypoint sound : () => string
|
||||||
|
|
||||||
|
contract Cat : Animal =
|
||||||
|
entrypoint sound() = "Cat sound"
|
||||||
|
```
|
||||||
|
|
||||||
|
Contract interfaces can extend other interfaces. An extended interface has to
|
||||||
|
declare all entrypoints from every parent interface. All the declarations in the extended
|
||||||
|
interface must have types compatible with the declarations from the parent
|
||||||
|
interface.
|
||||||
|
|
||||||
|
```
|
||||||
|
contract interface II =
|
||||||
|
entrypoint f : () => unit
|
||||||
|
|
||||||
|
contract interface I : II =
|
||||||
|
entrypoint f : () => unit
|
||||||
|
entrypoint g : () => unit
|
||||||
|
|
||||||
|
contract C : I =
|
||||||
|
entrypoint f() = ()
|
||||||
|
entrypoint g() = ()
|
||||||
|
```
|
||||||
|
|
||||||
|
It is only possible to implement (or extend) an interface that has been already
|
||||||
|
defined earlier in the file (or in an included file). Therefore recursive
|
||||||
|
interface implementation is not allowed in Sophia.
|
||||||
|
|
||||||
|
```
|
||||||
|
// The following code would show an error
|
||||||
|
|
||||||
|
contract interface X : Z =
|
||||||
|
entrypoint x : () => int
|
||||||
|
|
||||||
|
contract interface Y : X =
|
||||||
|
entrypoint x : () => int
|
||||||
|
entrypoint y : () => int
|
||||||
|
|
||||||
|
contract interface Z : Y =
|
||||||
|
entrypoint x : () => int
|
||||||
|
entrypoint y : () => int
|
||||||
|
entrypoint z : () => int
|
||||||
|
|
||||||
|
contract C : Z =
|
||||||
|
entrypoint x() = 1
|
||||||
|
entrypoint y() = 1
|
||||||
|
entrypoint z() = 1
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Adding or removing modifiers
|
||||||
|
|
||||||
|
When a `contract` or a `contract interface` implements another `contract interface`, the `payable` and `stateful` modifiers can be kept or changed, both in the contract and in the entrypoints, according to the following rules:
|
||||||
|
|
||||||
|
1. A `payable` contract or interface can implement a `payable` interface or a non-`payable` interface.
|
||||||
|
2. A non-`payable` contract or interface can only implement a non-`payable` interface, and cannot implement a `payable` interface.
|
||||||
|
3. A `payable` entrypoint can implement a `payable` entrypoint or a non-`payable` entrypoint.
|
||||||
|
4. A non-`payable` entrypoint can only implement a non-`payable` entrypoint, and cannot implement a `payable` entrypoint.
|
||||||
|
5. A non-`stateful` entrypoint can implement a `stateful` entrypoint or a non-`stateful` entrypoint.
|
||||||
|
6. A `stateful` entrypoint can only implement a `stateful` entrypoint, and cannot implement a non-`stateful` entrypoint.
|
||||||
|
|
||||||
|
#### 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)),
|
||||||
|
|
||||||
|
>Variance refers to how subtyping between more complex types relates to subtyping between their components.
|
||||||
|
|
||||||
|
This concept plays an important role in complex types such as tuples, `datatype`s and functions. Depending on the context, it can apply to positions in the structure of a type, or type parameters of generic types. There are four kinds of variances:
|
||||||
|
|
||||||
|
- covariant
|
||||||
|
- contravariant
|
||||||
|
- invariant
|
||||||
|
- bivariant
|
||||||
|
|
||||||
|
A type is said to be on a "covariant" position when it describes output or a result of some computation. Analogously, position is "contravariant" when it is an input, or a parameter. Intuitively, when a part of the type is produced by values of it, it is covariant. When it is consumed, it is contravariant. When a type appears to be simultaneously input and output, it is described as invariant. If a type is neither of those (that is, it's unused) it's bivariant. Furthermore, whenever a complex type appears on a contravariant position, all its covariant components become contravariant and vice versa.
|
||||||
|
|
||||||
|
Variance influences how subtyping is applied. Types on covariant positions are subtyped normally, while contravariant the opposite way. Invariant types have to be exactly the same in order for subtyping to work. Bivariant types are always compatible.
|
||||||
|
|
||||||
|
A good example of where it matters can be pictured by subtyping of function types. Let us assume there is a contract interface `Animal` and two contracts that implement it: `Dog` and `Cat`.
|
||||||
|
|
||||||
|
```sophia
|
||||||
|
contract interface Animal =
|
||||||
|
entrypoint age : () => int
|
||||||
|
|
||||||
|
contract Dog : Animal =
|
||||||
|
entrypoint age() = // ...
|
||||||
|
entrypoint woof() = "woof"
|
||||||
|
|
||||||
|
contract Cat : Animal =
|
||||||
|
entrypoint age() = // ...
|
||||||
|
entrypoint meow() = "meow"
|
||||||
|
```
|
||||||
|
|
||||||
|
The assumption of this exercise is that cats do not bark (because `Cat` does not define the `woof` entrypoint). If subtyping rules were applied naively, that is if we let `Dog => Dog` be a subtype of `Animal => Animal`, the following code would break:
|
||||||
|
|
||||||
|
```sophia
|
||||||
|
let f : (Dog) => string = d => d.woof()
|
||||||
|
let g : (Animal) => string = f
|
||||||
|
let c : Cat = Chain.create()
|
||||||
|
g(c) // Cat barking!
|
||||||
|
```
|
||||||
|
|
||||||
|
That is because arguments of functions are contravariant, as opposed to return the type which is covariant. Because of that, the assignment of `f` to `g` is invalid - while `Dog` is a subtype of `Animal`, `Dog => string` is **not** a subtype of `Animal => string`. However, `Animal => string` **is** a subtype of `Dog => string`. More than that, `(Dog => Animal) => Dog` is a subtype of `(Animal => Dog) => Animal`.
|
||||||
|
|
||||||
|
This has consequences on how user-defined generic types work. A type variable gains its variance from its role in the type definition as shown in the example:
|
||||||
|
|
||||||
|
```sophia
|
||||||
|
datatype co('a) = Co('a) // co is covariant on 'a
|
||||||
|
datatype ct('a) = Ct('a => unit) // ct is contravariant on 'a
|
||||||
|
datatype in('a) = In('a => 'a) // in is invariant on 'a
|
||||||
|
datatype bi('a) = Bi // bi is bivariant on 'a
|
||||||
|
```
|
||||||
|
|
||||||
|
The following facts apply here:
|
||||||
|
|
||||||
|
- `co('a)` is a subtype of `co('b)` when `'a` is a subtype of `'b`
|
||||||
|
- `ct('a)` is a subtype of `ct('b)` when `'b` is a subtype of `'a`
|
||||||
|
- `in('a)` is a subtype of `in('b)` when `'a` is equal to `'b`
|
||||||
|
- `bi('a)` is a subtype of `bi('b)` always
|
||||||
|
|
||||||
|
That altogether induce the following rules of subtyping in Sophia:
|
||||||
|
|
||||||
|
- A function type `(Args1) => Ret1` is a subtype of `(Args2) => Ret2` when `Ret1`
|
||||||
|
is a subtype of `Ret2` and each argument type from `Args2` is a subtype of its
|
||||||
|
counterpart in `Args1`.
|
||||||
|
|
||||||
|
- A list type `list(A)` is a subtype of `list(B)` if `A` is a subtype of `B`.
|
||||||
|
|
||||||
|
- An option type `option(A)` is a subtype of `option(B)` if `A` is a subtype of `B`.
|
||||||
|
|
||||||
|
- A map type `map(A1, A2)` is a subtype of `map(B1, B2)` if `A1` is a subtype
|
||||||
|
of `B1`, and `A2` is a subtype of `B2`.
|
||||||
|
|
||||||
|
- An oracle type `oracle(A1, A2)` is a subtype of `oracle(B1, B2)` if `B1` is
|
||||||
|
a subtype of `A1`, and `A2` is a subtype of `B2`.
|
||||||
|
|
||||||
|
- An oracle_query type `oracle_query(A1, A2)` is a subtype of `oracle_query(B1, B2)`
|
||||||
|
if `A1` is a subtype of `B1`, and `A2` is a subtype of `B2`.
|
||||||
|
|
||||||
|
- A user-defined datatype `t(Args1)` is a subtype of `t(Args2)`
|
||||||
|
|
||||||
|
- When a user-defined type `t('a)` is covariant in `'a`, then `t(A)` is a
|
||||||
|
subtype of `t(B)` when `A` is a subtype of `B`.
|
||||||
|
|
||||||
|
- When a user-defined type `t('a)` is contravariant in `'a`, then `t(A)` is a
|
||||||
|
subtype of `t(B)` when `B` is a subtype of `A`.
|
||||||
|
|
||||||
|
- When a user-defined type `t('a)` is binvariant in `'a`, then `t(A)` is a
|
||||||
|
subtype of `t(B)` when either `A` is a subtype of `B` or when `B` is a subtype
|
||||||
|
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
|
## Mutable state
|
||||||
|
|
||||||
@@ -333,6 +498,24 @@ 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,
|
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.
|
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
|
## Standard library
|
||||||
|
|
||||||
Sophia offers [standard library](sophia_stdlib.md) which exposes some
|
Sophia offers [standard library](sophia_stdlib.md) which exposes some
|
||||||
@@ -380,6 +563,7 @@ Sophia has the following types:
|
|||||||
## Literals
|
## Literals
|
||||||
| Type | Constant/Literal example(s) |
|
| Type | Constant/Literal example(s) |
|
||||||
| ---------- | ------------------------------- |
|
| ---------- | ------------------------------- |
|
||||||
|
| unit | () |
|
||||||
| int | `-1`, `2425`, `4598275923475723498573485768` |
|
| int | `-1`, `2425`, `4598275923475723498573485768` |
|
||||||
| address | `ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt` |
|
| address | `ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt` |
|
||||||
| bool | `true`, `false` |
|
| bool | `true`, `false` |
|
||||||
@@ -394,12 +578,51 @@ Sophia has the following types:
|
|||||||
| state | `state{ owner = Call.origin, magic_key = #a298105f }` |
|
| state | `state{ owner = Call.origin, magic_key = #a298105f }` |
|
||||||
| event | `EventX(0, "Hello")` |
|
| event | `EventX(0, "Hello")` |
|
||||||
| hash | `#000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f` |
|
| hash | `#000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f` |
|
||||||
| signature | `#000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f` |
|
| signature | `sg_MhibzTP1wWzGCTjtPFr1TiPqRJrrJqw7auvEuF5i3FdoALWqXLBDY6xxRRNUSPHK3EQTnTzF12EyspkxrSMxVHKsZeSMj`, `#000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f` |
|
||||||
| Chain.ttl | `FixedTTL(1050)`, `RelativeTTL(50)` |
|
| Chain.ttl | `FixedTTL(1050)`, `RelativeTTL(50)` |
|
||||||
| oracle('a, 'b) | `ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5` |
|
| oracle('a, 'b) | `ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5` |
|
||||||
| oracle_query('a, 'b) | `oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY` |
|
| oracle_query('a, 'b) | `oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY` |
|
||||||
| contract | `ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ` |
|
| contract | `ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ` |
|
||||||
|
|
||||||
|
## Hole expression
|
||||||
|
|
||||||
|
Hole expressions, written as `???`, are expressions that are used as a placeholder. During compilation, the compiler will generate a type error indication the type of the hole expression.
|
||||||
|
|
||||||
|
```
|
||||||
|
include "List.aes"
|
||||||
|
contract C =
|
||||||
|
entrypoint f() =
|
||||||
|
List.sum(List.map(???, [1,2,3]))
|
||||||
|
```
|
||||||
|
|
||||||
|
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
|
## Arithmetic
|
||||||
|
|
||||||
Sophia integers (`int`) are represented by arbitrary-sized signed words and support the following
|
Sophia integers (`int`) are represented by arbitrary-sized signed words and support the following
|
||||||
@@ -415,10 +638,24 @@ All operations are *safe* with respect to overflow and underflow.
|
|||||||
The division and modulo operations throw an arithmetic error if the
|
The division and modulo operations throw an arithmetic error if the
|
||||||
right-hand operand is zero.
|
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
|
## Bit fields
|
||||||
|
|
||||||
Sophia integers do not support bit arithmetic. Instead there is a separate
|
Originally Sophia integers did not support bit arithmetic. Instead we used a
|
||||||
type `bits`. See the standard library [documentation](sophia_stdlib.md#bits).
|
separate type `bits` (see the standard library
|
||||||
|
[documentation](sophia_stdlib.md#bits)) - it is still provided as an
|
||||||
|
alternative to bit arithmetic.
|
||||||
|
|
||||||
A bit field can be of arbitrary size (but it is still represented by the
|
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).
|
corresponding integer, so setting very high bits can be expensive).
|
||||||
@@ -723,16 +960,18 @@ functions are provided.
|
|||||||
|
|
||||||
## AENS interface
|
## AENS interface
|
||||||
|
|
||||||
Contracts can interact with the
|
Contracts can interact with the [æternity naming
|
||||||
[æternity naming system](https://github.com/aeternity/protocol/blob/master/AENS.md).
|
system](https://github.com/aeternity/protocol/blob/master/AENS.md). For this
|
||||||
For this purpose the [AENS](sophia_stdlib.md#aens) library was exposed.
|
purpose the [AENS](sophia_stdlib.md#aens) and later the
|
||||||
|
[AENSv2](sophia_stdlib.md#aensv2) library was exposed.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
In this example we assume that the name `name` already exists, and is owned by
|
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
|
an account with address `addr`. In order to allow a contract `ct` to handle
|
||||||
`name` the account holder needs to create a
|
`name` the account holder needs to create a [delegation
|
||||||
[signature](#delegation-signature) `sig` of `addr | name.hash | ct.address`.
|
signature](#delegation-signature) `sig` from the name owner address `addr`, the
|
||||||
|
name hash and the contract address.
|
||||||
|
|
||||||
Armed with this information we can for example write a function that extends
|
Armed with this information we can for example write a function that extends
|
||||||
the name if it expires within 1000 blocks:
|
the name if it expires within 1000 blocks:
|
||||||
@@ -862,10 +1101,46 @@ function require(b : bool, err : string) =
|
|||||||
if(!b) abort(err)
|
if(!b) abort(err)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Aside from that, there is an almost equivalent function `exit`
|
||||||
|
|
||||||
|
```sophia
|
||||||
|
exit(reason : string) : 'a
|
||||||
|
```
|
||||||
|
|
||||||
|
Just like `abort`, it breaks the execution with the given reason. The difference
|
||||||
|
however is in the gas consumption — while `abort` returns unused gas, a call to
|
||||||
|
`exit` burns it all.
|
||||||
|
|
||||||
## Delegation signature
|
## Delegation signature
|
||||||
|
|
||||||
Some chain operations (`Oracle.<operation>` and `AENS.<operation>`) have an
|
Some chain operations (`Oracle.<operation>` and `AENSv2.<operation>`) have an
|
||||||
optional delegation signature. This is typically used when a user/accounts
|
optional delegation signature. This is typically used when a user/accounts
|
||||||
would like to allow a contract to act on it's behalf. The exact data to be
|
would like to allow a contract to act on it's behalf.
|
||||||
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.).
|
### 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`
|
||||||
|
|||||||
+255
-166
@@ -14,6 +14,7 @@ The out-of-the-box namespaces are:
|
|||||||
|
|
||||||
- [Address](#address)
|
- [Address](#address)
|
||||||
- [AENS](#aens)
|
- [AENS](#aens)
|
||||||
|
- [AENSv2](#aensv2)
|
||||||
- [Auth](#auth)
|
- [Auth](#auth)
|
||||||
- [Bits](#bits)
|
- [Bits](#bits)
|
||||||
- [Bytes](#bytes)
|
- [Bytes](#bytes)
|
||||||
@@ -31,6 +32,7 @@ The following ones need to be included as regular files with `.aes` suffix, for
|
|||||||
include "List.aes"
|
include "List.aes"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- [AENSCompat](#aenscompat)
|
||||||
- [Bitwise](#bitwise)
|
- [Bitwise](#bitwise)
|
||||||
- [BLS12_381](#bls12_381)
|
- [BLS12_381](#bls12_381)
|
||||||
- [Func](#func)
|
- [Func](#func)
|
||||||
@@ -55,6 +57,12 @@ Address.to_str(a : address) : string
|
|||||||
|
|
||||||
Base58 encoded string
|
Base58 encoded string
|
||||||
|
|
||||||
|
#### to_bytes
|
||||||
|
```
|
||||||
|
Address.to_bytes(a : address) : bytes(32)
|
||||||
|
```
|
||||||
|
|
||||||
|
The binary representation of the address.
|
||||||
|
|
||||||
#### is_contract
|
#### is_contract
|
||||||
```
|
```
|
||||||
@@ -90,13 +98,10 @@ Cast address to contract type C (where `C` is a contract)
|
|||||||
|
|
||||||
### AENS
|
### AENS
|
||||||
|
|
||||||
The following functionality is available for interacting with the æternity
|
The old AENS namespace, kept in the compiler to be able to interact with
|
||||||
naming system (AENS).
|
contracts from before Ceres, compiled using aesophia compiler version 7.x and
|
||||||
If `owner` is equal to `Contract.address` the signature `signature` is
|
earlier. Used in [AENSCompat](#aenscompat) when converting between old and new
|
||||||
ignored, and can be left out since it is a named argument. Otherwise we need
|
pointers.
|
||||||
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
|
#### Types
|
||||||
|
|
||||||
@@ -113,12 +118,41 @@ datatype pointee = AccountPt(address) | OraclePt(address)
|
|||||||
| ContractPt(address) | ChannelPt(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
|
#### Functions
|
||||||
|
|
||||||
##### resolve
|
##### resolve
|
||||||
```
|
```
|
||||||
AENS.resolve(name : string, key : string) : option('a)
|
AENSv2.resolve(name : string, key : string) : option('a)
|
||||||
```
|
```
|
||||||
|
|
||||||
Name resolution. Here `name` should be a registered name and `key` one of the attributes
|
Name resolution. Here `name` should be a registered name and `key` one of the attributes
|
||||||
@@ -129,74 +163,107 @@ type checked against this type at run time.
|
|||||||
|
|
||||||
##### lookup
|
##### lookup
|
||||||
```
|
```
|
||||||
AENS.lookup(name : string) : option(AENS.name)
|
AENSv2.lookup(name : string) : option(AENSv2.name)
|
||||||
```
|
```
|
||||||
|
|
||||||
If `name` is an active name `AENS.lookup` returns a name object.
|
If `name` is an active name `AENSv2.lookup` returns a name object.
|
||||||
The three arguments to `Name` are `owner`, `expiry` and a map of the
|
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.
|
`pointees` for the name. Note: the expiry of the name is always a fixed TTL.
|
||||||
For example:
|
For example:
|
||||||
```
|
```
|
||||||
let Some(Name(owner, FixedTTL(expiry), ptrs)) = AENS.lookup("example.chain")
|
let Some(AENSv2.Name(owner, FixedTTL(expiry), ptrs)) = AENSv2.lookup("example.chain")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note: Changed to produce `AENSv2.name` in v8.0 (Ceres protocol upgrade).
|
||||||
|
|
||||||
##### preclaim
|
##### preclaim
|
||||||
```
|
```
|
||||||
AENS.preclaim(owner : address, commitment_hash : hash, <signature : signature>) : unit
|
AENSv2.preclaim(owner : address, commitment_hash : hash, <signature : signature>) : unit
|
||||||
```
|
```
|
||||||
|
|
||||||
The [signature](./sophia_features.md#delegation-signature) should be over
|
The [signature](./sophia_features.md#delegation-signature) should be a
|
||||||
`network id` + `owner address` + `Contract.address` (concatenated as byte arrays).
|
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`.
|
||||||
|
|
||||||
|
|
||||||
##### claim
|
##### claim
|
||||||
```
|
```
|
||||||
AENS.claim(owner : address, name : string, salt : int, name_fee : int, <signature : signature>) : unit
|
AENSv2.claim(owner : address, name : string, salt : int, name_fee : int, <signature : signature>) : unit
|
||||||
```
|
```
|
||||||
|
|
||||||
The [signature](./sophia_features.md#delegation-signature) should be over
|
The [signature](./sophia_features.md#delegation-signature) should be a
|
||||||
`network id` + `owner address` + `name_hash` + `Contract.address`
|
serialized structure containing `network id`, `owner address`, and
|
||||||
(concatenated as byte arrays)
|
`Contract.address`. Using the private key of `owner address` for signing.
|
||||||
using the private key of the `owner` account 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`.
|
||||||
|
|
||||||
|
|
||||||
##### transfer
|
##### transfer
|
||||||
```
|
```
|
||||||
AENS.transfer(owner : address, new_owner : address, name : string, <signature : signature>) : unit
|
AENSv2.transfer(owner : address, new_owner : address, name : string, <signature : signature>) : unit
|
||||||
```
|
```
|
||||||
|
|
||||||
Transfers name to the new owner.
|
Transfers name to the new owner.
|
||||||
|
|
||||||
The [signature](./sophia_features.md#delegation-signature) should be over
|
The [signature](./sophia_features.md#delegation-signature) should be a
|
||||||
`network id` + `owner address` + `name_hash` + `Contract.address`
|
serialized structure containing `network id`, `owner address`, and
|
||||||
(concatenated as byte arrays)
|
`Contract.address`. Using the private key of `owner address` for signing.
|
||||||
using the private key of the `owner` account 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`.
|
||||||
|
|
||||||
|
|
||||||
##### revoke
|
##### revoke
|
||||||
```
|
```
|
||||||
AENS.revoke(owner : address, name : string, <signature : signature>) : unit
|
AENSv2.revoke(owner : address, name : string, <signature : signature>) : unit
|
||||||
```
|
```
|
||||||
|
|
||||||
Revokes the name to extend the ownership time.
|
Revokes the name to extend the ownership time.
|
||||||
|
|
||||||
The [signature](./sophia_features.md#delegation-signature) should be over
|
The [signature](./sophia_features.md#delegation-signature) should be a
|
||||||
`network id` + `owner address` + `name_hash` + `Contract.address`
|
serialized structure containing `network id`, `owner address`, and
|
||||||
(concatenated as byte arrays)
|
`Contract.address`. Using the private key of `owner address` for signing.
|
||||||
using the private key of the `owner` account 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`.
|
||||||
|
|
||||||
|
|
||||||
##### update
|
##### update
|
||||||
```
|
```
|
||||||
AENS.update(owner : address, name : string, expiry : option(Chain.ttl), client_ttl : option(int),
|
AENSv2.update(owner : address, name : string, expiry : option(Chain.ttl), client_ttl : option(int),
|
||||||
new_ptrs : map(string, AENS.pointee), <signature : signature>) : unit
|
new_ptrs : option(map(string, AENSv2.pointee)), <signature : signature>) : unit
|
||||||
```
|
```
|
||||||
|
|
||||||
Updates the name. If the optional parameters are set to `None` that parameter
|
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
|
will not be updated, for example if `None` is passed as `expiry` the expiry
|
||||||
block of the name is not changed.
|
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
|
### Auth
|
||||||
|
|
||||||
@@ -236,7 +303,10 @@ namespace Chain =
|
|||||||
Auth.tx_hash : option(hash)
|
Auth.tx_hash : option(hash)
|
||||||
```
|
```
|
||||||
|
|
||||||
Gets the transaction hash during authentication.
|
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.
|
||||||
|
|
||||||
|
|
||||||
### Bits
|
### Bits
|
||||||
@@ -315,7 +385,7 @@ Each bit is true if and only if it was 1 in `a` and 0 in `b`
|
|||||||
|
|
||||||
### Bytes
|
### Bytes
|
||||||
|
|
||||||
#### to_int
|
#### to\_int
|
||||||
```
|
```
|
||||||
Bytes.to_int(b : bytes(n)) : int
|
Bytes.to_int(b : bytes(n)) : int
|
||||||
```
|
```
|
||||||
@@ -323,7 +393,7 @@ Bytes.to_int(b : bytes(n)) : int
|
|||||||
Interprets the byte array as a big endian integer
|
Interprets the byte array as a big endian integer
|
||||||
|
|
||||||
|
|
||||||
#### to_str
|
#### to\_str
|
||||||
```
|
```
|
||||||
Bytes.to_str(b : bytes(n)) : string
|
Bytes.to_str(b : bytes(n)) : string
|
||||||
```
|
```
|
||||||
@@ -336,7 +406,8 @@ Returns the hexadecimal representation of the byte array
|
|||||||
Bytes.concat : (a : bytes(m), b : bytes(n)) => bytes(m + n)
|
Bytes.concat : (a : bytes(m), b : bytes(n)) => bytes(m + n)
|
||||||
```
|
```
|
||||||
|
|
||||||
Concatenates two byte arrays
|
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()`.
|
||||||
|
|
||||||
|
|
||||||
#### split
|
#### split
|
||||||
@@ -346,6 +417,38 @@ Bytes.split(a : bytes(m + n)) : bytes(m) * bytes(n)
|
|||||||
|
|
||||||
Splits a byte array at given index
|
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
|
### Call
|
||||||
|
|
||||||
@@ -381,6 +484,12 @@ Call.gas_price : int
|
|||||||
|
|
||||||
The gas price of the current call.
|
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
|
#### fee
|
||||||
```
|
```
|
||||||
@@ -469,39 +578,6 @@ Chain.block_height : int"
|
|||||||
|
|
||||||
The height of the current block (i.e. the block in which the current call will be included).
|
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.
|
|
||||||
|
|
||||||
|
|
||||||
##### difficulty
|
|
||||||
```
|
|
||||||
Chain.difficulty : int
|
|
||||||
```
|
|
||||||
|
|
||||||
The difficulty of the current block.
|
|
||||||
|
|
||||||
|
|
||||||
##### gas
|
|
||||||
```
|
|
||||||
Chain.gas_limit : int
|
|
||||||
```
|
|
||||||
|
|
||||||
The gas limit of the current block.
|
|
||||||
|
|
||||||
|
|
||||||
##### bytecode_hash
|
##### bytecode_hash
|
||||||
```
|
```
|
||||||
Chain.bytecode_hash : 'c => option(hash)
|
Chain.bytecode_hash : 'c => option(hash)
|
||||||
@@ -538,7 +614,6 @@ 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
|
in the `init` call of the new contract. It will be included in
|
||||||
`Contract.balance`, however.
|
`Contract.balance`, however.
|
||||||
|
|
||||||
|
|
||||||
The type `'c` must be instantiated with a contract.
|
The type `'c` must be instantiated with a contract.
|
||||||
|
|
||||||
|
|
||||||
@@ -565,6 +640,7 @@ main contract Market =
|
|||||||
The typechecker must be certain about the created contract's type, so it is
|
The typechecker must be certain about the created contract's type, so it is
|
||||||
worth writing it explicitly as shown in the example.
|
worth writing it explicitly as shown in the example.
|
||||||
|
|
||||||
|
|
||||||
##### clone
|
##### clone
|
||||||
```
|
```
|
||||||
Chain.clone : ( ref : 'c, gas : int, value : int, protected : bool, ...
|
Chain.clone : ( ref : 'c, gas : int, value : int, protected : bool, ...
|
||||||
@@ -603,8 +679,8 @@ Example usage:
|
|||||||
```
|
```
|
||||||
payable contract interface Auction =
|
payable contract interface Auction =
|
||||||
entrypoint init : (int, string) => void
|
entrypoint init : (int, string) => void
|
||||||
stateful payable entrypoint buy : (int) => ()
|
stateful payable entrypoint buy : (int) => unit
|
||||||
stateful entrypoint sell : (int) => ()
|
stateful entrypoint sell : (int) => unit
|
||||||
|
|
||||||
main contract Market =
|
main contract Market =
|
||||||
type state = list(Auction)
|
type state = list(Auction)
|
||||||
@@ -623,11 +699,71 @@ implementation of the `init` function does not actually return `state`, but
|
|||||||
calls `put` instead. Moreover, FATE prevents even handcrafted calls to `init`.
|
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
|
##### event
|
||||||
```
|
```
|
||||||
Chain.event(e : event) : unit
|
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.
|
|
||||||
|
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).
|
||||||
|
|
||||||
|
|
||||||
### Char
|
### Char
|
||||||
@@ -705,11 +841,14 @@ Hash any object to blake2b
|
|||||||
|
|
||||||
#### verify_sig
|
#### verify_sig
|
||||||
```
|
```
|
||||||
Crypto.verify_sig(msg : hash, pubkey : address, sig : signature) : bool
|
Crypto.verify_sig(msg : bytes(), pubkey : address, sig : signature) : bool
|
||||||
```
|
```
|
||||||
|
|
||||||
Checks if the signature of `msg` was made using private key corresponding to
|
Checks if the signature of `msg` was made using private key corresponding to
|
||||||
the `pubkey`
|
the `pubkey`.
|
||||||
|
|
||||||
|
Note: before v8 of the compiler, `msg` had type `hash` (i.e. `bytes(32)`).
|
||||||
|
|
||||||
|
|
||||||
#### ecverify_secp256k1
|
#### ecverify_secp256k1
|
||||||
```
|
```
|
||||||
@@ -742,12 +881,21 @@ Verifies a standard 64-byte ECDSA signature (`R || S`).
|
|||||||
|
|
||||||
### Int
|
### Int
|
||||||
|
|
||||||
#### to_str
|
#### to\_str
|
||||||
```
|
```
|
||||||
Int.to_str : int => string
|
Int.to_str(n : int) : string
|
||||||
```
|
```
|
||||||
|
|
||||||
Casts integer to string using decimal representation
|
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`.
|
||||||
|
|
||||||
|
|
||||||
### Map
|
### Map
|
||||||
@@ -806,14 +954,14 @@ Oracle.register(<signature : bytes(64)>, acct : address, qfee : int, ttl : Chain
|
|||||||
Registers new oracle answering questions of type `'a` with answers of type `'b`.
|
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 `acct` is the address of the oracle to register (can be the same as the contract).
|
||||||
* `signature` is a signature proving that the contract is allowed to register the account -
|
* The [signature](./sophia_features.md#delegation-signature) should be a
|
||||||
the `network id` + `account address` + `contract address` (concatenated as byte arrays) is
|
serialized structure containing `network id`, `account address`, and
|
||||||
[signed](./sophia_features.md#delegation-signature) with the
|
`contract address`. Using the private key of `account address` for signing.
|
||||||
private key of the account, proving you have the private key of the oracle to be. If the
|
Proving you have the private key of the oracle to be. If the address is the same
|
||||||
address is the same as the contract `sign` is ignored and can be left out entirely.
|
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 `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, either relative to the current
|
* The `ttl` is the Time To Live for the oracle in key blocks, either relative to the current
|
||||||
height (`RelativeTTL(delta)`) or a fixed height (`FixedTTL(height)`).
|
key block height (`RelativeTTL(delta)`) or a fixed key block height (`FixedTTL(height)`).
|
||||||
* The type `'a` is the type of the question to ask.
|
* The type `'a` is the type of the question to ask.
|
||||||
* The type `'b` is the type of the oracle answers.
|
* The type `'b` is the type of the oracle answers.
|
||||||
|
|
||||||
@@ -824,7 +972,7 @@ Examples:
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
#### get_question
|
#### get\_question
|
||||||
```
|
```
|
||||||
Oracle.get_question(o : oracle('a, 'b), q : oracle_query('a, 'b)) : 'a
|
Oracle.get_question(o : oracle('a, 'b), q : oracle_query('a, 'b)) : 'a
|
||||||
```
|
```
|
||||||
@@ -837,12 +985,11 @@ 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
|
Oracle.respond(<signature : bytes(64)>, o : oracle('a, 'b), q : oracle_query('a, 'b), 'b) : unit
|
||||||
```
|
```
|
||||||
|
|
||||||
Responds to the question `q` on `o`.
|
Responds to the question `q` on `o`. Unless the contract address is the same
|
||||||
Unless the contract address is the same as the oracle address the `signature`
|
as the oracle address the `signature` (which is an optional, named argument)
|
||||||
(which is an optional, named argument)
|
|
||||||
needs to be provided. Proving that we have the private key of the oracle by
|
needs to be provided. Proving that we have the private key of the oracle by
|
||||||
[signing](./sophia_features.md#delegation-signature)
|
[signing](./sophia_features.md#delegation-signature) should be a serialized
|
||||||
the `network id` + `oracle query id` + `contract address`
|
structure containing `network id`, `oracle query id`, and `contract address`.
|
||||||
|
|
||||||
|
|
||||||
#### extend
|
#### extend
|
||||||
@@ -855,7 +1002,7 @@ Extends TTL of an oracle.
|
|||||||
* `o` is the oracle being extended
|
* `o` is the oracle being extended
|
||||||
* `ttl` must be `RelativeTTL`. The time to live of `o` will be extended by this value.
|
* `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
|
Oracle.query_fee(o : oracle('a, 'b)) : int
|
||||||
```
|
```
|
||||||
@@ -876,7 +1023,7 @@ Asks the oracle a question.
|
|||||||
The call fails if the oracle could expire before an answer.
|
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)
|
Oracle.get_answer(o : oracle('a, 'b), q : oracle_query('a, 'b)) : option('b)
|
||||||
```
|
```
|
||||||
@@ -914,88 +1061,21 @@ It returns `true` iff the oracle query exist and has the expected type.
|
|||||||
These need to be explicitly included (with `.aes` suffix)
|
These need to be explicitly included (with `.aes` suffix)
|
||||||
|
|
||||||
|
|
||||||
### Bitwise
|
### AENSCompat
|
||||||
|
|
||||||
Bitwise operations on arbitrary precision integers.
|
#### pointee\_to\_V2
|
||||||
|
|
||||||
#### bsr
|
|
||||||
```
|
```
|
||||||
Bitwise.bsr(n : int, x : int) : int
|
AENSCompat.pointee_to_V2(p : AENS.pointee) : AENSv2.pointee
|
||||||
```
|
```
|
||||||
|
|
||||||
Logical bit shift `x` right `n` positions.
|
Translate old pointee format to new, this is always possible.
|
||||||
|
|
||||||
|
#### pointee\_from\_V2
|
||||||
#### bsl
|
|
||||||
```
|
```
|
||||||
Bitwise.bsl(n : int, x : int) : int
|
AENSCompat.pointee_from_V2(p2 : AENSv2.pointee) : option(AENS.pointee)
|
||||||
```
|
```
|
||||||
|
|
||||||
Logical bit shift `x` left `n` positions.
|
Translate new pointee format to old, `DataPt` can't be translated, so `None` is returned in this case.
|
||||||
|
|
||||||
|
|
||||||
#### 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
|
### BLS12\_381
|
||||||
@@ -2391,6 +2471,15 @@ to_int(s : string) : option(int)
|
|||||||
Converts a decimal ("123", "-253") or a hexadecimal ("0xa2f", "-0xBBB") string into
|
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.
|
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
|
||||||
```
|
```
|
||||||
sha3(s : string) : hash
|
sha3(s : string) : hash
|
||||||
|
|||||||
+38
-10
@@ -10,8 +10,9 @@ and `*/` and can be nested.
|
|||||||
### Keywords
|
### Keywords
|
||||||
|
|
||||||
```
|
```
|
||||||
contract elif else entrypoint false function if import include let mod namespace
|
contract include let switch type record datatype if elif else function
|
||||||
private payable stateful switch true type record datatype main interface
|
stateful payable true false mod public entrypoint private indexed namespace
|
||||||
|
interface main using as for hiding
|
||||||
```
|
```
|
||||||
|
|
||||||
### Tokens
|
### Tokens
|
||||||
@@ -29,6 +30,7 @@ private payable stateful switch true type record datatype main interface
|
|||||||
- `ContractAddress` base58-encoded 32 byte contract address with `ct_` prefix
|
- `ContractAddress` base58-encoded 32 byte contract address with `ct_` prefix
|
||||||
- `OracleAddress` base58-encoded 32 byte oracle address with `ok_` prefix
|
- `OracleAddress` base58-encoded 32 byte oracle address with `ok_` prefix
|
||||||
- `OracleQueryId` base58-encoded 32 byte oracle query id with `oq_` 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
|
Valid string escape codes are
|
||||||
|
|
||||||
@@ -91,18 +93,31 @@ A Sophia file consists of a sequence of *declarations* in a layout block.
|
|||||||
```c
|
```c
|
||||||
File ::= Block(TopDecl)
|
File ::= Block(TopDecl)
|
||||||
|
|
||||||
TopDecl ::= ['payable'] 'contract' Con '=' Block(Decl)
|
TopDecl ::= ['payable'] ['main'] 'contract' Con [Implement] '=' Block(Decl)
|
||||||
| 'namespace' Con '=' Block(Decl)
|
| 'contract' 'interface' Con [Implement] '=' Block(Decl)
|
||||||
| '@compiler' PragmaOp Version
|
| 'namespace' Con '=' Block(Decl)
|
||||||
| 'include' String
|
| '@compiler' PragmaOp Version
|
||||||
|
| 'include' String
|
||||||
|
| Using
|
||||||
|
|
||||||
|
Implement ::= ':' Sep1(Con, ',')
|
||||||
|
|
||||||
Decl ::= 'type' Id ['(' TVar* ')'] '=' TypeAlias
|
Decl ::= 'type' Id ['(' TVar* ')'] '=' TypeAlias
|
||||||
| 'record' Id ['(' TVar* ')'] '=' RecordType
|
| 'record' Id ['(' TVar* ')'] '=' RecordType
|
||||||
| 'datatype' Id ['(' TVar* ')'] '=' DataType
|
| 'datatype' Id ['(' TVar* ')'] '=' DataType
|
||||||
|
| 'let' Id [':' Type] '=' Expr
|
||||||
| (EModifier* 'entrypoint' | FModifier* 'function') Block(FunDecl)
|
| (EModifier* 'entrypoint' | FModifier* 'function') Block(FunDecl)
|
||||||
|
| Using
|
||||||
|
|
||||||
FunDecl ::= Id ':' Type // Type signature
|
FunDecl ::= Id ':' Type // Type signature
|
||||||
| Id Args [':' Type] '=' Block(Stmt) // Definition
|
| Id Args [':' Type] '=' Block(Stmt) // Definition
|
||||||
|
| Id Args [':' Type] Block(GuardedDef) // Guarded definitions
|
||||||
|
|
||||||
|
GuardedDef ::= '|' Sep1(Expr, ',') '=' Block(Stmt)
|
||||||
|
|
||||||
|
Using ::= 'using' Con ['as' Con] [UsingParts]
|
||||||
|
UsingParts ::= 'for' '[' Sep1(Id, ',') ']'
|
||||||
|
| 'hiding' '[' Sep1(Id, ',') ']'
|
||||||
|
|
||||||
PragmaOp ::= '<' | '=<' | '==' | '>=' | '>'
|
PragmaOp ::= '<' | '=<' | '==' | '>=' | '>'
|
||||||
Version ::= Sep1(Int, '.')
|
Version ::= Sep1(Int, '.')
|
||||||
@@ -172,12 +187,17 @@ Stmt ::= 'switch' '(' Expr ')' Block(Case)
|
|||||||
| 'elif' '(' Expr ')' Block(Stmt)
|
| 'elif' '(' Expr ')' Block(Stmt)
|
||||||
| 'else' Block(Stmt)
|
| 'else' Block(Stmt)
|
||||||
| 'let' LetDef
|
| 'let' LetDef
|
||||||
|
| Using
|
||||||
| Expr
|
| Expr
|
||||||
|
|
||||||
LetDef ::= Id Args [':' Type] '=' Block(Stmt) // Function definition
|
LetDef ::= Id Args [':' Type] '=' Block(Stmt) // Function definition
|
||||||
| Pattern '=' Block(Stmt) // Value definition
|
| Pattern '=' Block(Stmt) // Value definition
|
||||||
|
|
||||||
Case ::= Pattern '=>' Block(Stmt)
|
Case ::= Pattern '=>' Block(Stmt)
|
||||||
|
| Pattern Block(GuardedCase)
|
||||||
|
|
||||||
|
GuardedCase ::= '|' Sep1(Expr, ',') '=>' Block(Stmt)
|
||||||
|
|
||||||
Pattern ::= Expr
|
Pattern ::= Expr
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -215,10 +235,13 @@ Expr ::= '(' LamArgs ')' '=>' Block(Stmt) // Anonymous function (x) => x +
|
|||||||
| '[' Expr '..' Expr ']' // List range [1..n]
|
| '[' Expr '..' Expr ']' // List range [1..n]
|
||||||
| '{' Sep(FieldUpdate, ',') '}' // Record or map value {x = 0, y = 1}, {[key] = val}
|
| '{' Sep(FieldUpdate, ',') '}' // Record or map value {x = 0, y = 1}, {[key] = val}
|
||||||
| '(' Expr ')' // Parens (1 + 2) * 3
|
| '(' Expr ')' // Parens (1 + 2) * 3
|
||||||
|
| '(' Expr '=' Expr ')' // Assign pattern (y = x::_)
|
||||||
| Id | Con | QId | QCon // Identifiers x, None, Map.member, AELib.Token
|
| Id | Con | QId | QCon // Identifiers x, None, Map.member, AELib.Token
|
||||||
| Int | Bytes | String | Char // Literals 123, 0xff, #00abc123, "foo", '%'
|
| Int | Bytes | String | Char // Literals 123, 0xff, #00abc123, "foo", '%'
|
||||||
| AccountAddress | ContractAddress // Chain identifiers
|
| AccountAddress | ContractAddress // Chain identifiers
|
||||||
| OracleAddress | OracleQueryId // Chain identifiers
|
| OracleAddress | OracleQueryId // Chain identifiers
|
||||||
|
| Signature // Signature
|
||||||
|
| '???' // Hole expression 1 + ???
|
||||||
|
|
||||||
Generator ::= Pattern '<-' Expr // Generator
|
Generator ::= Pattern '<-' Expr // Generator
|
||||||
| 'if' '(' Expr ')' // Guard
|
| 'if' '(' Expr ')' // Guard
|
||||||
@@ -235,8 +258,8 @@ Path ::= Id // Record field
|
|||||||
|
|
||||||
BinOp ::= '||' | '&&' | '<' | '>' | '=<' | '>=' | '==' | '!='
|
BinOp ::= '||' | '&&' | '<' | '>' | '=<' | '>=' | '==' | '!='
|
||||||
| '::' | '++' | '+' | '-' | '*' | '/' | 'mod' | '^'
|
| '::' | '++' | '+' | '-' | '*' | '/' | 'mod' | '^'
|
||||||
| '|>'
|
| 'band' | 'bor' | 'bxor' | '<<' | '>>' | '|>'
|
||||||
UnOp ::= '-' | '!'
|
UnOp ::= '-' | '!' | 'bnot'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Operators types
|
## Operators types
|
||||||
@@ -245,23 +268,28 @@ UnOp ::= '-' | '!'
|
|||||||
| --- | ---
|
| --- | ---
|
||||||
| `-` `+` `*` `/` `mod` `^` | arithmetic operators
|
| `-` `+` `*` `/` `mod` `^` | arithmetic operators
|
||||||
| `!` `&&` `\|\|` | logical operators
|
| `!` `&&` `\|\|` | logical operators
|
||||||
|
| `band` `bor` `bxor` `bnot` `<<` `>>` | bitwise operators
|
||||||
| `==` `!=` `<` `>` `=<` `>=` | comparison operators
|
| `==` `!=` `<` `>` `=<` `>=` | comparison operators
|
||||||
| `::` `++` | list operators
|
| `::` `++` | list operators
|
||||||
| `\|>` | functional operators
|
| `\|>` | functional operators
|
||||||
|
|
||||||
## Operator precendences
|
## Operator precedence
|
||||||
|
|
||||||
In order of highest to lowest precedence.
|
In order of highest to lowest precedence.
|
||||||
|
|
||||||
| Operators | Associativity
|
| Operators | Associativity
|
||||||
| --- | ---
|
| --- | ---
|
||||||
| `!` | right
|
| `!` `bnot`| right
|
||||||
| `^` | left
|
| `^` | left
|
||||||
| `*` `/` `mod` | left
|
| `*` `/` `mod` | left
|
||||||
| `-` (unary) | right
|
| `-` (unary) | right
|
||||||
| `+` `-` | left
|
| `+` `-` | left
|
||||||
|
| `<<` `>>` | left
|
||||||
| `::` `++` | right
|
| `::` `++` | right
|
||||||
| `<` `>` `=<` `>=` `==` `!=` | none
|
| `<` `>` `=<` `>=` `==` `!=` | none
|
||||||
|
| `band` | left
|
||||||
|
| `bxor` | left
|
||||||
|
| `bor` | left
|
||||||
| `&&` | right
|
| `&&` | right
|
||||||
| `\|\|` | right
|
| `\|\|` | right
|
||||||
| `\|>` | left
|
| `\|>` | left
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
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
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
@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
|
private function
|
||||||
asc : (('a, 'a) => bool, 'a, list('a), list('a)) => list(list('a))
|
asc : (('a, 'a) => bool, 'a, list('a), list('a)) => list(list('a))
|
||||||
asc(lt, x, acc, h::t) =
|
asc(lt, x, acc, h::t) =
|
||||||
if(lt(h, x)) List.reverse(x::acc) :: monotonic_subs(lt, h::t)
|
if(lt(h, x)) reverse(x::acc) :: monotonic_subs(lt, h::t)
|
||||||
else asc(lt, h, x::acc, t)
|
else asc(lt, h, x::acc, t)
|
||||||
asc(_, x, acc, []) = [List.reverse(x::acc)]
|
asc(_, x, acc, []) = [reverse(x::acc)]
|
||||||
|
|
||||||
/** Merges list of sorted lists
|
/** Merges list of sorted lists
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
include "List.aes"
|
include "List.aes"
|
||||||
namespace String =
|
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
|
// Computes the SHA3/Keccak hash of the string
|
||||||
function sha3(s : string) : hash = StringInternal.sha3(s)
|
function sha3(s : string) : hash = StringInternal.sha3(s)
|
||||||
// Computes the SHA256 hash of the string.
|
// Computes the SHA256 hash of the string.
|
||||||
|
|||||||
+4
-6
@@ -2,11 +2,9 @@
|
|||||||
|
|
||||||
{erl_opts, [debug_info]}.
|
{erl_opts, [debug_info]}.
|
||||||
|
|
||||||
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"0699f35"}}}
|
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {tag, "v3.4.1"}}}
|
||||||
, {getopt, "1.0.1"}
|
|
||||||
, {eblake2, "1.0.0"}
|
, {eblake2, "1.0.0"}
|
||||||
, {jsx, {git, "https://github.com/talentdeficit/jsx.git",
|
, {jsx, {git, "https://github.com/talentdeficit/jsx.git", {tag, "2.8.0"}}}
|
||||||
{tag, "2.8.0"}}}
|
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
{dialyzer, [
|
{dialyzer, [
|
||||||
@@ -15,8 +13,8 @@
|
|||||||
{base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]}
|
{base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]}
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
{relx, [{release, {aesophia, "6.1.0"},
|
{relx, [{release, {aesophia, "8.0.1"},
|
||||||
[aesophia, aebytecode, getopt]},
|
[aesophia, aebytecode]},
|
||||||
|
|
||||||
{dev_mode, true},
|
{dev_mode, true},
|
||||||
{include_erts, false},
|
{include_erts, false},
|
||||||
|
|||||||
+9
-6
@@ -1,11 +1,11 @@
|
|||||||
{"1.1.0",
|
{"1.2.0",
|
||||||
[{<<"aebytecode">>,
|
[{<<"aebytecode">>,
|
||||||
{git,"https://github.com/aeternity/aebytecode.git",
|
{git,"https://github.com/aeternity/aebytecode.git",
|
||||||
{ref,"0699f35b0398bac6cd4468da654d608375bd853d"}},
|
{ref,"6bd6f82c70d800950ea1a2c70c364a4181ff5291"}},
|
||||||
0},
|
0},
|
||||||
{<<"aeserialization">>,
|
{<<"aeserialization">>,
|
||||||
{git,"https://github.com/aeternity/aeserialization.git",
|
{git,"https://github.com/aeternity/aeserialization.git",
|
||||||
{ref,"47aaa8f5434b365c50a35bfd1490340b19241991"}},
|
{ref,"b26e6d105424748ba1c27917267b7cff07f37802"}},
|
||||||
1},
|
1},
|
||||||
{<<"base58">>,
|
{<<"base58">>,
|
||||||
{git,"https://github.com/aeternity/erl-base58.git",
|
{git,"https://github.com/aeternity/erl-base58.git",
|
||||||
@@ -14,9 +14,9 @@
|
|||||||
{<<"eblake2">>,{pkg,<<"eblake2">>,<<"1.0.0">>},0},
|
{<<"eblake2">>,{pkg,<<"eblake2">>,<<"1.0.0">>},0},
|
||||||
{<<"enacl">>,
|
{<<"enacl">>,
|
||||||
{git,"https://github.com/aeternity/enacl.git",
|
{git,"https://github.com/aeternity/enacl.git",
|
||||||
{ref,"26180f42c0b3a450905d2efd8bc7fd5fd9cece75"}},
|
{ref,"4eb7ec70084ba7c87b1af8797c4c4e90c84f95a2"}},
|
||||||
2},
|
2},
|
||||||
{<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},0},
|
{<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},1},
|
||||||
{<<"jsx">>,
|
{<<"jsx">>,
|
||||||
{git,"https://github.com/talentdeficit/jsx.git",
|
{git,"https://github.com/talentdeficit/jsx.git",
|
||||||
{ref,"3074d4865b3385a050badf7828ad31490d860df5"}},
|
{ref,"3074d4865b3385a050badf7828ad31490d860df5"}},
|
||||||
@@ -24,5 +24,8 @@
|
|||||||
[
|
[
|
||||||
{pkg_hash,[
|
{pkg_hash,[
|
||||||
{<<"eblake2">>, <<"EC8AD20E438AAB3F2E8D5D118C366A0754219195F8A0F536587440F8F9BCF2EF">>},
|
{<<"eblake2">>, <<"EC8AD20E438AAB3F2E8D5D118C366A0754219195F8A0F536587440F8F9BCF2EF">>},
|
||||||
{<<"getopt">>, <<"C73A9FA687B217F2FF79F68A3B637711BB1936E712B521D8CE466B29CBF7808A">>}]}
|
{<<"getopt">>, <<"C73A9FA687B217F2FF79F68A3B637711BB1936E712B521D8CE466B29CBF7808A">>}]},
|
||||||
|
{pkg_hash_ext,[
|
||||||
|
{<<"eblake2">>, <<"3C4D300A91845B25D501929A26AC2E6F7157480846FAB2347A4C11AE52E08A99">>},
|
||||||
|
{<<"getopt">>, <<"53E1AB83B9CEB65C9672D3E7A35B8092E9BDC9B3EE80721471A161C10C59959C">>}]}
|
||||||
].
|
].
|
||||||
|
|||||||
+45
-14
@@ -8,6 +8,7 @@
|
|||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
|
|
||||||
-module(aeso_aci).
|
-module(aeso_aci).
|
||||||
|
-vsn("8.0.1").
|
||||||
|
|
||||||
-export([ file/2
|
-export([ file/2
|
||||||
, file/3
|
, file/3
|
||||||
@@ -83,7 +84,7 @@ from_typed_ast(Type, TypedAst) ->
|
|||||||
string -> do_render_aci_json(JArray)
|
string -> do_render_aci_json(JArray)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
encode_contract(Contract = {Head, _, {con, _, Name}, _}) when ?IS_CONTRACT_HEAD(Head) ->
|
encode_contract(Contract = {Head, _, {con, _, Name}, _, _}) when ?IS_CONTRACT_HEAD(Head) ->
|
||||||
C0 = #{name => encode_name(Name)},
|
C0 = #{name => encode_name(Name)},
|
||||||
|
|
||||||
Tdefs0 = [ encode_typedef(T) || T <- sort_decls(contract_types(Contract)) ],
|
Tdefs0 = [ encode_typedef(T) || T <- sort_decls(contract_types(Contract)) ],
|
||||||
@@ -91,7 +92,7 @@ encode_contract(Contract = {Head, _, {con, _, Name}, _}) when ?IS_CONTRACT_HEAD(
|
|||||||
{Es, Tdefs1} = lists:partition(FilterT(<<"event">>), Tdefs0),
|
{Es, Tdefs1} = lists:partition(FilterT(<<"event">>), Tdefs0),
|
||||||
{Ss, Tdefs} = lists:partition(FilterT(<<"state">>), Tdefs1),
|
{Ss, Tdefs} = lists:partition(FilterT(<<"state">>), Tdefs1),
|
||||||
|
|
||||||
C1 = C0#{type_defs => Tdefs},
|
C1 = C0#{typedefs => Tdefs},
|
||||||
|
|
||||||
C2 = case Es of
|
C2 = case Es of
|
||||||
[] -> C1;
|
[] -> C1;
|
||||||
@@ -111,7 +112,7 @@ encode_contract(Contract = {Head, _, {con, _, Name}, _}) when ?IS_CONTRACT_HEAD(
|
|||||||
encode_contract(Namespace = {namespace, _, {con, _, Name}, _}) ->
|
encode_contract(Namespace = {namespace, _, {con, _, Name}, _}) ->
|
||||||
Tdefs = [ encode_typedef(T) || T <- sort_decls(contract_types(Namespace)) ],
|
Tdefs = [ encode_typedef(T) || T <- sort_decls(contract_types(Namespace)) ],
|
||||||
#{namespace => #{name => encode_name(Name),
|
#{namespace => #{name => encode_name(Name),
|
||||||
type_defs => Tdefs}}.
|
typedefs => Tdefs}}.
|
||||||
|
|
||||||
%% Encode a function definition. Currently we are only interested in
|
%% Encode a function definition. Currently we are only interested in
|
||||||
%% the interface and type.
|
%% the interface and type.
|
||||||
@@ -198,7 +199,8 @@ encode_expr({bytes, _, B}) ->
|
|||||||
<<N:Digits/unit:8>> = B,
|
<<N:Digits/unit:8>> = B,
|
||||||
list_to_binary(lists:flatten(io_lib:format("#~*.16.0b", [Digits*2, N])));
|
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;
|
encode_expr({Lit, _, L}) when Lit == oracle_pubkey; Lit == oracle_query_id;
|
||||||
Lit == contract_pubkey; Lit == account_pubkey ->
|
Lit == contract_pubkey; Lit == account_pubkey;
|
||||||
|
Lit == signature ->
|
||||||
aeser_api_encoder:encode(Lit, L);
|
aeser_api_encoder:encode(Lit, L);
|
||||||
encode_expr({app, _, {'-', _}, [{int, _, N}]}) ->
|
encode_expr({app, _, {'-', _}, [{int, _, N}]}) ->
|
||||||
encode_expr({int, [], -N});
|
encode_expr({int, [], -N});
|
||||||
@@ -221,20 +223,45 @@ do_render_aci_json(Json) ->
|
|||||||
case Json of
|
case Json of
|
||||||
JArray when is_list(JArray) -> JArray;
|
JArray when is_list(JArray) -> JArray;
|
||||||
JObject when is_map(JObject) -> [JObject];
|
JObject when is_map(JObject) -> [JObject];
|
||||||
JText when is_binary(JText) ->
|
JText when is_binary(JText) -> decode(JText)
|
||||||
case jsx:decode(Json, [{labels, atom}, return_maps]) of
|
|
||||||
JArray when is_list(JArray) -> JArray;
|
|
||||||
JObject when is_map(JObject) -> [JObject];
|
|
||||||
_ -> error(bad_aci_json)
|
|
||||||
end
|
|
||||||
end,
|
end,
|
||||||
DecodedContracts = [ decode_contract(C) || C <- Contracts ],
|
DecodedContracts = [ decode_contract(C) || C <- Contracts ],
|
||||||
{ok, list_to_binary(string:join(DecodedContracts, "\n"))}.
|
{ok, list_to_binary(string:join(DecodedContracts, "\n"))}.
|
||||||
|
|
||||||
|
|
||||||
|
decode(JSON) ->
|
||||||
|
case code:is_loaded(jsx) of
|
||||||
|
{file, _} ->
|
||||||
|
case jsx:decode(JSON, [{labels, atom}, return_maps]) of
|
||||||
|
JArray when is_list(JArray) -> JArray;
|
||||||
|
JObject when is_map(JObject) -> [JObject];
|
||||||
|
_ -> error(bad_aci_json)
|
||||||
|
end;
|
||||||
|
false ->
|
||||||
|
case zj:binary_decode(JSON) of
|
||||||
|
{ok, Decoded} when is_list(Decoded) -> atomize(Decoded);
|
||||||
|
{ok, Decoded} when is_map(Decoded) -> [atomize(Decoded)];
|
||||||
|
_ -> error(bad_aci_json)
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
atomize(T) when is_map(T) ->
|
||||||
|
maps:fold(fun atomize/3, #{}, T);
|
||||||
|
atomize(T) when is_list(T) ->
|
||||||
|
lists:map(fun atomize/1, T);
|
||||||
|
atomize(T) ->
|
||||||
|
T.
|
||||||
|
|
||||||
|
atomize(K, V, M) when is_binary(K) ->
|
||||||
|
maps:put(binary_to_atom(K), atomize(V), M);
|
||||||
|
atomize(K, V, M) ->
|
||||||
|
maps:put(K, V, M).
|
||||||
|
|
||||||
|
|
||||||
decode_contract(#{contract := #{name := Name,
|
decode_contract(#{contract := #{name := Name,
|
||||||
kind := Kind,
|
kind := Kind,
|
||||||
payable := Payable,
|
payable := Payable,
|
||||||
type_defs := Ts0,
|
typedefs := Ts0,
|
||||||
functions := Fs} = C}) ->
|
functions := Fs} = C}) ->
|
||||||
MkTDef = fun(N, T) -> #{name => N, vars => [], typedef => T} end,
|
MkTDef = fun(N, T) -> #{name => N, vars => [], typedef => T} end,
|
||||||
Ts = [ MkTDef(<<"state">>, maps:get(state, C)) || maps:is_key(state, C) ] ++
|
Ts = [ MkTDef(<<"state">>, maps:get(state, C)) || maps:is_key(state, C) ] ++
|
||||||
@@ -246,7 +273,7 @@ decode_contract(#{contract := #{name := Name,
|
|||||||
end,
|
end,
|
||||||
io_lib:format("~s", [Name])," =\n",
|
io_lib:format("~s", [Name])," =\n",
|
||||||
decode_tdefs(Ts), decode_funcs(Fs)];
|
decode_tdefs(Ts), decode_funcs(Fs)];
|
||||||
decode_contract(#{namespace := #{name := Name, type_defs := Ts}}) when Ts /= [] ->
|
decode_contract(#{namespace := #{name := Name, typedefs := Ts}}) when Ts /= [] ->
|
||||||
["namespace ", io_lib:format("~s", [Name])," =\n",
|
["namespace ", io_lib:format("~s", [Name])," =\n",
|
||||||
decode_tdefs(Ts)];
|
decode_tdefs(Ts)];
|
||||||
decode_contract(_) -> [].
|
decode_contract(_) -> [].
|
||||||
@@ -282,6 +309,8 @@ decode_type(#{list := [Et]}) ->
|
|||||||
decode_type(#{map := Ets}) ->
|
decode_type(#{map := Ets}) ->
|
||||||
Ts = decode_types(Ets),
|
Ts = decode_types(Ets),
|
||||||
["map",$(,lists:join(",", Ts),$)];
|
["map",$(,lists:join(",", Ts),$)];
|
||||||
|
decode_type(#{bytes := any}) ->
|
||||||
|
["bytes()"];
|
||||||
decode_type(#{bytes := Len}) ->
|
decode_type(#{bytes := Len}) ->
|
||||||
["bytes(", integer_to_list(Len), ")"];
|
["bytes(", integer_to_list(Len), ")"];
|
||||||
decode_type(#{variant := Ets}) ->
|
decode_type(#{variant := Ets}) ->
|
||||||
@@ -341,10 +370,12 @@ stateful(false) -> "".
|
|||||||
|
|
||||||
%% #contract{Ann, Con, [Declarations]}.
|
%% #contract{Ann, Con, [Declarations]}.
|
||||||
|
|
||||||
contract_funcs({C, _, _, Decls}) when ?IS_CONTRACT_HEAD(C); C == namespace ->
|
contract_funcs({C, _, _, _, Decls}) when ?IS_CONTRACT_HEAD(C) ->
|
||||||
[ D || D <- Decls, is_fun(D)].
|
[ D || D <- Decls, is_fun(D)].
|
||||||
|
|
||||||
contract_types({C, _, _, Decls}) when ?IS_CONTRACT_HEAD(C); C == namespace ->
|
contract_types({namespace, _, _, Decls}) ->
|
||||||
|
[ D || D <- Decls, is_type(D) ];
|
||||||
|
contract_types({C, _, _, _, Decls}) when ?IS_CONTRACT_HEAD(C) ->
|
||||||
[ D || D <- Decls, is_type(D) ].
|
[ D || D <- Decls, is_type(D) ].
|
||||||
|
|
||||||
is_fun({letfun, _, _, _, _, _}) -> true;
|
is_fun({letfun, _, _, _, _, _}) -> true;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
-module(aeso_ast).
|
-module(aeso_ast).
|
||||||
|
-vsn("8.0.1").
|
||||||
|
|
||||||
-export([int/2,
|
-export([int/2,
|
||||||
line/1,
|
line/1,
|
||||||
|
|||||||
+1115
-319
File diff suppressed because it is too large
Load Diff
+883
-657
File diff suppressed because it is too large
Load Diff
@@ -1,94 +0,0 @@
|
|||||||
%%%-------------------------------------------------------------------
|
|
||||||
%%% @author Ulf Norell
|
|
||||||
%%% @copyright (C) 2019, Aeternity Anstalt
|
|
||||||
%%% @doc
|
|
||||||
%%% Formatting of code generation errors.
|
|
||||||
%%% @end
|
|
||||||
%%%
|
|
||||||
%%%-------------------------------------------------------------------
|
|
||||||
-module(aeso_code_errors).
|
|
||||||
|
|
||||||
-export([format/1, pos/1]).
|
|
||||||
|
|
||||||
format({last_declaration_must_be_main_contract, Decl = {Kind, _, {con, _, C}, _}}) ->
|
|
||||||
Msg = io_lib:format("Expected a main contract as the last declaration instead of the ~p '~s'",
|
|
||||||
[Kind, C]),
|
|
||||||
mk_err(pos(Decl), Msg);
|
|
||||||
format({missing_init_function, Con}) ->
|
|
||||||
Msg = io_lib:format("Missing init function for the contract '~s'.", [pp_expr(Con)]),
|
|
||||||
Cxt = "The 'init' function can only be omitted if the state type is 'unit'.",
|
|
||||||
mk_err(pos(Con), Msg, Cxt);
|
|
||||||
format({missing_definition, Id}) ->
|
|
||||||
Msg = io_lib:format("Missing definition of function '~s'.", [pp_expr(Id)]),
|
|
||||||
mk_err(pos(Id), Msg);
|
|
||||||
format({parameterized_state, Decl}) ->
|
|
||||||
Msg = "The state type cannot be parameterized.",
|
|
||||||
mk_err(pos(Decl), Msg);
|
|
||||||
format({parameterized_event, Decl}) ->
|
|
||||||
Msg = "The event type cannot be parameterized.",
|
|
||||||
mk_err(pos(Decl), Msg);
|
|
||||||
format({invalid_entrypoint, Why, Ann, {id, _, Name}, Thing}) ->
|
|
||||||
What = case Why of higher_order -> "higher-order (contains function types)";
|
|
||||||
polymorphic -> "polymorphic (contains type variables)" end,
|
|
||||||
ThingS = case Thing of
|
|
||||||
{argument, X, T} -> io_lib:format("argument\n~s\n", [pp_typed(X, T)]);
|
|
||||||
{result, T} -> io_lib:format("return type\n~s\n", [pp_type(2, T)])
|
|
||||||
end,
|
|
||||||
Bad = case Thing of
|
|
||||||
{argument, _, _} -> io_lib:format("has a ~s type", [What]);
|
|
||||||
{result, _} -> io_lib:format("is ~s", [What])
|
|
||||||
end,
|
|
||||||
Msg = io_lib:format("The ~sof entrypoint '~s' ~s.",
|
|
||||||
[ThingS, Name, Bad]),
|
|
||||||
case Why of
|
|
||||||
higher_order -> mk_err(pos(Ann), Msg)
|
|
||||||
end;
|
|
||||||
format({invalid_aens_resolve_type, Ann, T}) ->
|
|
||||||
Msg = io_lib:format("Invalid return type of AENS.resolve:\n"
|
|
||||||
"~s\n"
|
|
||||||
"It must be a string or a pubkey type (address, oracle, etc).",
|
|
||||||
[pp_type(2, T)]),
|
|
||||||
mk_err(pos(Ann), Msg);
|
|
||||||
format({invalid_oracle_type, Why, What, Ann, Type}) ->
|
|
||||||
WhyS = case Why of higher_order -> "higher-order (contain function types)";
|
|
||||||
polymorphic -> "polymorphic (contain type variables)" end,
|
|
||||||
Msg = io_lib:format("Invalid oracle type\n~s", [pp_type(2, Type)]),
|
|
||||||
Cxt = io_lib:format("The ~s type must not be ~s.", [What, WhyS]),
|
|
||||||
mk_err(pos(Ann), Msg, Cxt);
|
|
||||||
format({var_args_not_set, Expr}) ->
|
|
||||||
mk_err( pos(Expr), "Could not deduce type of variable arguments list"
|
|
||||||
, "When compiling " ++ pp_expr(Expr)
|
|
||||||
);
|
|
||||||
format({found_void, Ann}) ->
|
|
||||||
mk_err(pos(Ann), "Found a void-typed value.", "`void` is a restricted, uninhabited type. Did you mean `unit`?");
|
|
||||||
|
|
||||||
format(Err) ->
|
|
||||||
mk_err(aeso_errors:pos(0, 0), io_lib:format("Unknown error: ~p\n", [Err])).
|
|
||||||
|
|
||||||
pos(Ann) ->
|
|
||||||
File = aeso_syntax:get_ann(file, Ann, no_file),
|
|
||||||
Line = aeso_syntax:get_ann(line, Ann, 0),
|
|
||||||
Col = aeso_syntax:get_ann(col, Ann, 0),
|
|
||||||
aeso_errors:pos(File, Line, Col).
|
|
||||||
|
|
||||||
pp_typed(E, T) ->
|
|
||||||
prettypr:format(prettypr:nest(2,
|
|
||||||
lists:foldr(fun prettypr:beside/2, prettypr:empty(),
|
|
||||||
[aeso_pretty:expr(E), prettypr:text(" : "),
|
|
||||||
aeso_pretty:type(T)]))).
|
|
||||||
|
|
||||||
pp_expr(E) ->
|
|
||||||
pp_expr(0, E).
|
|
||||||
|
|
||||||
pp_expr(N, E) ->
|
|
||||||
prettypr:format(prettypr:nest(N, aeso_pretty:expr(E))).
|
|
||||||
|
|
||||||
pp_type(N, T) ->
|
|
||||||
prettypr:format(prettypr:nest(N, aeso_pretty:type(T))).
|
|
||||||
|
|
||||||
mk_err(Pos, Msg) ->
|
|
||||||
aeso_errors:new(code_error, Pos, lists:flatten(Msg)).
|
|
||||||
|
|
||||||
mk_err(Pos, Msg, Cxt) ->
|
|
||||||
aeso_errors:new(code_error, Pos, lists:flatten(Msg), lists:flatten(Cxt)).
|
|
||||||
|
|
||||||
+100
-38
@@ -7,11 +7,14 @@
|
|||||||
%%% Created : 12 Dec 2017
|
%%% Created : 12 Dec 2017
|
||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(aeso_compiler).
|
-module(aeso_compiler).
|
||||||
|
-vsn("8.0.1").
|
||||||
|
|
||||||
-export([ file/1
|
-export([ file/1
|
||||||
, file/2
|
, file/2
|
||||||
, from_string/2
|
, from_string/2
|
||||||
, check_call/4
|
, check_call/4
|
||||||
|
, decode_value/4
|
||||||
|
, encode_value/4
|
||||||
, create_calldata/3
|
, create_calldata/3
|
||||||
, create_calldata/4
|
, create_calldata/4
|
||||||
, version/0
|
, version/0
|
||||||
@@ -25,7 +28,7 @@
|
|||||||
, validate_byte_code/3
|
, validate_byte_code/3
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-include_lib("aebytecode/include/aeb_opcodes.hrl").
|
-include("$aebytecode_include/aeb_opcodes.hrl").
|
||||||
-include("aeso_utils.hrl").
|
-include("aeso_utils.hrl").
|
||||||
|
|
||||||
|
|
||||||
@@ -40,6 +43,7 @@
|
|||||||
| {include, {file_system, [string()]} |
|
| {include, {file_system, [string()]} |
|
||||||
{explicit_files, #{string() => binary()}}}
|
{explicit_files, #{string() => binary()}}}
|
||||||
| {src_file, string()}
|
| {src_file, string()}
|
||||||
|
| {src_dir, string()}
|
||||||
| {aci, aeso_aci:aci_type()}.
|
| {aci, aeso_aci:aci_type()}.
|
||||||
-type options() :: [option()].
|
-type options() :: [option()].
|
||||||
|
|
||||||
@@ -85,7 +89,9 @@ file(Filename) ->
|
|||||||
file(File, Options0) ->
|
file(File, Options0) ->
|
||||||
Options = add_include_path(File, Options0),
|
Options = add_include_path(File, Options0),
|
||||||
case read_contract(File) of
|
case read_contract(File) of
|
||||||
{ok, Bin} -> from_string(Bin, [{src_file, File} | Options]);
|
{ok, Bin} ->
|
||||||
|
SrcDir = aeso_utils:canonical_dir(filename:dirname(File)),
|
||||||
|
from_string(Bin, [{src_file, File}, {src_dir, SrcDir} | Options]);
|
||||||
{error, Error} ->
|
{error, Error} ->
|
||||||
Msg = lists:flatten([File,": ",file:format_error(Error)]),
|
Msg = lists:flatten([File,": ",file:format_error(Error)]),
|
||||||
{error, [aeso_errors:new(file_error, Msg)]}
|
{error, [aeso_errors:new(file_error, Msg)]}
|
||||||
@@ -97,7 +103,7 @@ add_include_path(File, Options) ->
|
|||||||
false ->
|
false ->
|
||||||
Dir = filename:dirname(File),
|
Dir = filename:dirname(File),
|
||||||
{ok, Cwd} = file:get_cwd(),
|
{ok, Cwd} = file:get_cwd(),
|
||||||
[{include, {file_system, [Cwd, Dir]}} | Options]
|
[{include, {file_system, [Cwd, aeso_utils:canonical_dir(Dir)]}} | Options]
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec from_string(binary() | string(), options()) -> {ok, map()} | {error, [aeso_errors:error()]}.
|
-spec from_string(binary() | string(), options()) -> {ok, map()} | {error, [aeso_errors:error()]}.
|
||||||
@@ -112,10 +118,12 @@ from_string(ContractString, Options) ->
|
|||||||
|
|
||||||
from_string1(ContractString, Options) ->
|
from_string1(ContractString, Options) ->
|
||||||
#{ fcode := FCode
|
#{ fcode := FCode
|
||||||
, fcode_env := #{child_con_env := ChildContracts}
|
, fcode_env := FCodeEnv
|
||||||
, folded_typed_ast := FoldedTypedAst
|
, folded_typed_ast := FoldedTypedAst
|
||||||
, warnings := Warnings } = string_to_code(ContractString, Options),
|
, warnings := Warnings } = string_to_code(ContractString, Options),
|
||||||
FateCode = aeso_fcode_to_fate:compile(ChildContracts, FCode, 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),
|
pp_assembler(FateCode, Options),
|
||||||
ByteCode = aeb_fate_code:serialize(FateCode, []),
|
ByteCode = aeb_fate_code:serialize(FateCode, []),
|
||||||
{ok, Version} = version(),
|
{ok, Version} = version(),
|
||||||
@@ -180,30 +188,58 @@ check_call(Source, FunName, Args, Options) ->
|
|||||||
check_call1(Source, FunName, Args, Options).
|
check_call1(Source, FunName, Args, Options).
|
||||||
|
|
||||||
check_call1(ContractString0, 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
|
try
|
||||||
%% First check the contract without the __call function
|
%% First check the contract without the __call function
|
||||||
#{fcode := OrgFcode
|
#{fcode := OrgFcode
|
||||||
, fcode_env := #{child_con_env := ChildContracts}
|
, fcode_env := #{child_con_env := ChildContracts}
|
||||||
, ast := Ast} = string_to_code(ContractString0, Options),
|
, ast := Ast} = string_to_code(Contract0, Options),
|
||||||
FateCode = aeso_fcode_to_fate:compile(ChildContracts, OrgFcode, []),
|
FateCode = aeso_fcode_to_fate:compile(ChildContracts, OrgFcode, #{}, []),
|
||||||
%% collect all hashes and compute the first name without hash collision to
|
%% collect all hashes and compute the first name without hash collision to
|
||||||
SymbolHashes = maps:keys(aeb_fate_code:symbols(FateCode)),
|
SymbolHashes = maps:keys(aeb_fate_code:symbols(FateCode)),
|
||||||
CallName = first_none_match(?CALL_NAME, SymbolHashes,
|
CallName = first_none_match(?CALL_NAME, SymbolHashes,
|
||||||
lists:seq($1, $9) ++ lists:seq($A, $Z) ++ lists:seq($a, $z)),
|
lists:seq($1, $9) ++ lists:seq($A, $Z) ++ lists:seq($a, $z)),
|
||||||
ContractString = insert_call_function(Ast, ContractString0, CallName, FunName, Args),
|
Contract = insert_call_function(Ast, Contract0, CallName, Call),
|
||||||
#{fcode := Fcode} = string_to_code(ContractString, Options),
|
{ok, CallName, string_to_code(Contract, Options)}
|
||||||
CallArgs = arguments_of_body(CallName, FunName, Fcode),
|
|
||||||
|
|
||||||
{ok, FunName, CallArgs}
|
|
||||||
catch
|
catch
|
||||||
throw:{error, Errors} -> {error, Errors}
|
throw:{error, Errors} -> {error, Errors}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
arguments_of_body(CallName, _FunName, Fcode) ->
|
get_call_body(CallName, #{fcode := Fcode}) ->
|
||||||
#{body := Body} = maps:get({entrypoint, list_to_binary(CallName)}, maps:get(functions, Fcode)),
|
#{body := Body} = maps:get({entrypoint, list_to_binary(CallName)}, maps:get(functions, Fcode)),
|
||||||
{def, _FName, Args} = Body,
|
Body.
|
||||||
%% FName is either {entrypoint, list_to_binary(FunName)} or 'init'
|
|
||||||
[ aeso_fcode_to_fate:term_to_fate(A) || A <- Args ].
|
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, aeb_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.
|
||||||
|
|
||||||
first_none_match(_CallName, _Hashes, []) ->
|
first_none_match(_CallName, _Hashes, []) ->
|
||||||
error(unable_to_find_unique_call_name);
|
error(unable_to_find_unique_call_name);
|
||||||
@@ -216,14 +252,31 @@ first_none_match(CallName, Hashes, [Char|Chars]) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
%% Add the __call function to a contract.
|
%% Add the __call function to a contract.
|
||||||
-spec insert_call_function(aeso_syntax:ast(), string(), string(), string(), [string()]) -> string().
|
-spec insert_call_function(aeso_syntax:ast(), string(), string(),
|
||||||
insert_call_function(Ast, Code, Call, FunName, Args) ->
|
{call, string(), [string()]} | {value, string(), string()} | {type, string()}) -> string().
|
||||||
|
insert_call_function(Ast, Code, Call, {call, FunName, Args}) ->
|
||||||
Ind = last_contract_indent(Ast),
|
Ind = last_contract_indent(Ast),
|
||||||
lists:flatten(
|
lists:flatten(
|
||||||
[ Code,
|
[ Code,
|
||||||
"\n\n",
|
"\n\n",
|
||||||
lists:duplicate(Ind, " "),
|
lists:duplicate(Ind, " "),
|
||||||
"stateful entrypoint ", Call, "() = ", FunName, "(", string:join(Args, ","), ")\n"
|
"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().
|
-spec insert_init_function(string(), options()) -> string().
|
||||||
@@ -238,8 +291,8 @@ insert_init_function(Code, Options) ->
|
|||||||
|
|
||||||
last_contract_indent(Decls) ->
|
last_contract_indent(Decls) ->
|
||||||
case lists:last(Decls) of
|
case lists:last(Decls) of
|
||||||
{_, _, _, [Decl | _]} -> aeso_syntax:get_ann(col, Decl, 1) - 1;
|
{_, _, _, _, [Decl | _]} -> aeso_syntax:get_ann(col, Decl, 1) - 1;
|
||||||
_ -> 0
|
_ -> 0
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec to_sophia_value(string(), string(), ok | error | revert, binary()) ->
|
-spec to_sophia_value(string(), string(), ok | error | revert, binary()) ->
|
||||||
@@ -262,26 +315,32 @@ to_sophia_value(ContractString, FunName, ok, Data, Options0) ->
|
|||||||
Options = [no_code | Options0],
|
Options = [no_code | Options0],
|
||||||
try
|
try
|
||||||
Code = string_to_code(ContractString, Options),
|
Code = string_to_code(ContractString, Options),
|
||||||
#{ unfolded_typed_ast := TypedAst, type_env := TypeEnv} = Code,
|
#{ folded_typed_ast := TypedAst, type_env := TypeEnv} = Code,
|
||||||
{ok, _, Type0} = get_decode_type(FunName, TypedAst),
|
{ok, _, Type0} = get_decode_type(FunName, TypedAst),
|
||||||
Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]),
|
Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0,
|
||||||
|
[ unfold_record_types
|
||||||
|
, unfold_variant_types
|
||||||
|
, not_unfold_system_alias_types]),
|
||||||
|
|
||||||
try
|
fate_data_to_sophia_value(Type0, Type, Data)
|
||||||
{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
|
catch
|
||||||
throw:{error, Errors} -> {error, Errors}
|
throw:{error, Errors} -> {error, Errors}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
fate_data_to_sophia_value(Type, UnfoldedType, FateData) ->
|
||||||
|
try
|
||||||
|
{ok, aeso_vm_decode:from_fate(UnfoldedType, aeb_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",
|
||||||
|
[aeb_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()]) ->
|
-spec create_calldata(string(), string(), [string()]) ->
|
||||||
{ok, binary()} | {error, [aeso_errors:error()]}.
|
{ok, binary()} | {error, [aeso_errors:error()]}.
|
||||||
create_calldata(Code, Fun, Args) ->
|
create_calldata(Code, Fun, Args) ->
|
||||||
@@ -308,14 +367,17 @@ decode_calldata(ContractString, FunName, Calldata, Options0) ->
|
|||||||
Options = [no_code | Options0],
|
Options = [no_code | Options0],
|
||||||
try
|
try
|
||||||
Code = string_to_code(ContractString, Options),
|
Code = string_to_code(ContractString, Options),
|
||||||
#{ unfolded_typed_ast := TypedAst, type_env := TypeEnv} = Code,
|
#{ folded_typed_ast := TypedAst, type_env := TypeEnv} = Code,
|
||||||
|
|
||||||
{ok, Args, _} = get_decode_type(FunName, TypedAst),
|
{ok, Args, _} = get_decode_type(FunName, TypedAst),
|
||||||
GetType = fun({typed, _, _, T}) -> T; (T) -> T end,
|
GetType = fun({typed, _, _, T}) -> T; (T) -> T end,
|
||||||
ArgTypes = lists:map(GetType, Args),
|
ArgTypes = lists:map(GetType, Args),
|
||||||
Type0 = {tuple_t, [], ArgTypes},
|
Type0 = {tuple_t, [], ArgTypes},
|
||||||
%% user defined data types such as variants needed to match against
|
%% user defined data types such as variants needed to match against
|
||||||
Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]),
|
Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0,
|
||||||
|
[ unfold_record_types
|
||||||
|
, unfold_variant_types
|
||||||
|
, not_unfold_system_alias_types]),
|
||||||
case aeb_fate_abi:decode_calldata(FunName, Calldata) of
|
case aeb_fate_abi:decode_calldata(FunName, Calldata) of
|
||||||
{ok, FateArgs} ->
|
{ok, FateArgs} ->
|
||||||
try
|
try
|
||||||
@@ -338,7 +400,7 @@ decode_calldata(ContractString, FunName, Calldata, Options0) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
-dialyzer({nowarn_function, get_decode_type/2}).
|
-dialyzer({nowarn_function, get_decode_type/2}).
|
||||||
get_decode_type(FunName, [{Contract, Ann, _, Defs}]) when ?IS_CONTRACT_HEAD(Contract) ->
|
get_decode_type(FunName, [{Contract, Ann, _, _, Defs}]) when ?IS_CONTRACT_HEAD(Contract) ->
|
||||||
GetType = fun({letfun, _, {id, _, Name}, Args, Ret, _}) when Name == FunName -> [{Args, Ret}];
|
GetType = fun({letfun, _, {id, _, Name}, Args, Ret, _}) when Name == FunName -> [{Args, Ret}];
|
||||||
({fun_decl, _, {id, _, Name}, {fun_t, _, _, Args, Ret}}) when Name == FunName -> [{Args, Ret}];
|
({fun_decl, _, {id, _, Name}, {fun_t, _, _, Args, Ret}}) when Name == FunName -> [{Args, Ret}];
|
||||||
(_) -> [] end,
|
(_) -> [] end,
|
||||||
@@ -349,7 +411,7 @@ get_decode_type(FunName, [{Contract, Ann, _, Defs}]) when ?IS_CONTRACT_HEAD(Cont
|
|||||||
"init" -> {ok, [], {tuple_t, [], []}};
|
"init" -> {ok, [], {tuple_t, [], []}};
|
||||||
_ ->
|
_ ->
|
||||||
Msg = io_lib:format("Function '~s' is missing in contract", [FunName]),
|
Msg = io_lib:format("Function '~s' is missing in contract", [FunName]),
|
||||||
Pos = aeso_code_errors:pos(Ann),
|
Pos = aeso_errors:pos(Ann),
|
||||||
aeso_errors:throw(aeso_errors:new(data_error, Pos, Msg))
|
aeso_errors:throw(aeso_errors:new(data_error, Pos, Msg))
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
|
|
||||||
-module(aeso_errors).
|
-module(aeso_errors).
|
||||||
|
-vsn("8.0.1").
|
||||||
|
|
||||||
-type src_file() :: no_file | iolist().
|
-type src_file() :: no_file | iolist().
|
||||||
|
|
||||||
@@ -34,6 +35,7 @@
|
|||||||
, new/2
|
, new/2
|
||||||
, new/3
|
, new/3
|
||||||
, new/4
|
, new/4
|
||||||
|
, pos/1
|
||||||
, pos/2
|
, pos/2
|
||||||
, pos/3
|
, pos/3
|
||||||
, pp/1
|
, pp/1
|
||||||
@@ -53,6 +55,12 @@ new(Type, Pos, Msg) ->
|
|||||||
new(Type, Pos, Msg, Ctxt) ->
|
new(Type, Pos, Msg, Ctxt) ->
|
||||||
#err{ type = Type, pos = Pos, message = Msg, context = Ctxt }.
|
#err{ type = Type, pos = Pos, message = Msg, context = Ctxt }.
|
||||||
|
|
||||||
|
pos(Ann) ->
|
||||||
|
File = aeso_syntax:get_ann(file, Ann, no_file),
|
||||||
|
Line = aeso_syntax:get_ann(line, Ann, 0),
|
||||||
|
Col = aeso_syntax:get_ann(col, Ann, 0),
|
||||||
|
pos(File, Line, Col).
|
||||||
|
|
||||||
pos(Line, Col) ->
|
pos(Line, Col) ->
|
||||||
#pos{ line = Line, col = Col }.
|
#pos{ line = Line, col = Col }.
|
||||||
|
|
||||||
|
|||||||
+334
-160
@@ -8,8 +8,9 @@
|
|||||||
%%%
|
%%%
|
||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(aeso_fcode_to_fate).
|
-module(aeso_fcode_to_fate).
|
||||||
|
-vsn("8.0.1").
|
||||||
|
|
||||||
-export([compile/2, compile/3, term_to_fate/1, term_to_fate/2]).
|
-export([compile/3, compile/4, term_to_fate/1, term_to_fate/2]).
|
||||||
|
|
||||||
-ifdef(TEST).
|
-ifdef(TEST).
|
||||||
-export([optimize_fun/4, to_basic_blocks/1]).
|
-export([optimize_fun/4, to_basic_blocks/1]).
|
||||||
@@ -45,7 +46,15 @@
|
|||||||
-define(s(N), {store, N}).
|
-define(s(N), {store, N}).
|
||||||
-define(void, {var, 9999}).
|
-define(void, {var, 9999}).
|
||||||
|
|
||||||
-record(env, { contract, vars = [], locals = [], current_function, tailpos = true, child_contracts = #{}, options = []}).
|
-record(env, { contract,
|
||||||
|
vars = [],
|
||||||
|
locals = [],
|
||||||
|
current_function,
|
||||||
|
tailpos = true,
|
||||||
|
child_contracts = #{},
|
||||||
|
saved_fresh_names = #{},
|
||||||
|
options = [],
|
||||||
|
debug_info = false }).
|
||||||
|
|
||||||
%% -- Debugging --------------------------------------------------------------
|
%% -- Debugging --------------------------------------------------------------
|
||||||
|
|
||||||
@@ -64,21 +73,26 @@ debug(Tag, Options, Fun) ->
|
|||||||
|
|
||||||
-dialyzer({nowarn_function, [code_error/1]}).
|
-dialyzer({nowarn_function, [code_error/1]}).
|
||||||
code_error(Err) ->
|
code_error(Err) ->
|
||||||
aeso_errors:throw(aeso_code_errors:format(Err)).
|
Pos = aeso_errors:pos(0, 0),
|
||||||
|
Msg = lists:flatten(io_lib:format("Unknown error: ~p\n", [Err])),
|
||||||
|
aeso_errors:throw(aeso_errors:new(code_error, Pos, Msg)).
|
||||||
|
|
||||||
%% -- Main -------------------------------------------------------------------
|
%% -- Main -------------------------------------------------------------------
|
||||||
|
|
||||||
%% @doc Main entry point.
|
%% @doc Main entry point.
|
||||||
compile(FCode, Options) ->
|
compile(FCode, SavedFreshNames, Options) ->
|
||||||
compile(#{}, FCode, Options).
|
compile(#{}, FCode, SavedFreshNames, Options).
|
||||||
compile(ChildContracts, FCode, Options) ->
|
compile(ChildContracts, FCode, SavedFreshNames, Options) ->
|
||||||
#{ contract_name := ContractName,
|
#{ contract_name := ContractName,
|
||||||
functions := Functions } = FCode,
|
functions := Functions } = FCode,
|
||||||
SFuns = functions_to_scode(ChildContracts, ContractName, Functions, Options),
|
SFuns = functions_to_scode(ChildContracts, ContractName, Functions, SavedFreshNames, Options),
|
||||||
SFuns1 = optimize_scode(SFuns, Options),
|
SFuns1 = optimize_scode(SFuns, Options),
|
||||||
FateCode = to_basic_blocks(SFuns1),
|
FateCode = to_basic_blocks(SFuns1),
|
||||||
?debug(compile, Options, "~s\n", [aeb_fate_asm:pp(FateCode)]),
|
?debug(compile, Options, "~s\n", [aeb_fate_asm:pp(FateCode)]),
|
||||||
FateCode.
|
case proplists:get_value(include_child_contract_symbols, Options, false) of
|
||||||
|
false -> FateCode;
|
||||||
|
true -> add_child_symbols(ChildContracts, FateCode)
|
||||||
|
end.
|
||||||
|
|
||||||
make_function_id(X) ->
|
make_function_id(X) ->
|
||||||
aeb_fate_code:symbol_identifier(make_function_name(X)).
|
aeb_fate_code:symbol_identifier(make_function_name(X)).
|
||||||
@@ -87,21 +101,31 @@ make_function_name(event) -> <<"Chain.event">>;
|
|||||||
make_function_name({entrypoint, Name}) -> Name;
|
make_function_name({entrypoint, Name}) -> Name;
|
||||||
make_function_name({local_fun, Xs}) -> list_to_binary("." ++ string:join(Xs, ".")).
|
make_function_name({local_fun, Xs}) -> list_to_binary("." ++ string:join(Xs, ".")).
|
||||||
|
|
||||||
functions_to_scode(ChildContracts, ContractName, Functions, Options) ->
|
add_child_symbols(ChildContracts, FateCode) ->
|
||||||
|
Funs = lists:flatten([ maps:keys(ChildFuns) || {_, #{functions := ChildFuns}} <- maps:to_list(ChildContracts) ]),
|
||||||
|
Symbols = maps:from_list([ {make_function_id(FName), make_function_name(FName)} || FName <- Funs ]),
|
||||||
|
aeb_fate_code:update_symbols(FateCode, Symbols).
|
||||||
|
|
||||||
|
functions_to_scode(ChildContracts, ContractName, Functions, SavedFreshNames, Options) ->
|
||||||
FunNames = maps:keys(Functions),
|
FunNames = maps:keys(Functions),
|
||||||
maps:from_list(
|
maps:from_list(
|
||||||
[ {make_function_name(Name), function_to_scode(ChildContracts, ContractName, FunNames, Name, Attrs, Args, Body, Type, Options)}
|
[ {make_function_name(Name), function_to_scode(ChildContracts, ContractName, FunNames, Name, Attrs, Args, Body, Type, SavedFreshNames, Options)}
|
||||||
|| {Name, #{args := Args,
|
|| {Name, #{args := Args,
|
||||||
body := Body,
|
body := Body,
|
||||||
attrs := Attrs,
|
attrs := Attrs,
|
||||||
return := Type}} <- maps:to_list(Functions)]).
|
return := Type}} <- maps:to_list(Functions)]).
|
||||||
|
|
||||||
function_to_scode(ChildContracts, ContractName, Functions, Name, Attrs0, Args, Body, ResType, Options) ->
|
function_to_scode(ChildContracts, ContractName, Functions, Name, Attrs0, Args, Body, ResType, SavedFreshNames, Options) ->
|
||||||
{ArgTypes, ResType1} = typesig_to_scode(Args, ResType),
|
{ArgTypes, ResType1} = typesig_to_scode(Args, ResType),
|
||||||
Attrs = Attrs0 -- [stateful], %% Only track private and payable from here.
|
Attrs = [ A || A <- Attrs0, A == private orelse A == payable ],
|
||||||
Env = init_env(ChildContracts, ContractName, Functions, Name, Args, Options),
|
Env = init_env(ChildContracts, ContractName, Functions, Name, Args, SavedFreshNames, Options),
|
||||||
|
ArgsNames = [ X || {X, _} <- lists:reverse(Env#env.vars) ],
|
||||||
|
|
||||||
|
%% DBG_LOC is added before the function body to make it possible to break
|
||||||
|
%% at the function signature
|
||||||
SCode = to_scode(Env, Body),
|
SCode = to_scode(Env, Body),
|
||||||
{Attrs, {ArgTypes, ResType1}, SCode}.
|
DbgSCode = dbg_contract(Env) ++ dbg_loc(Env, Attrs0) ++ dbg_scoped_vars(Env, ArgsNames, SCode),
|
||||||
|
{Attrs, {ArgTypes, ResType1}, DbgSCode}.
|
||||||
|
|
||||||
-define(tvars, '$tvars').
|
-define(tvars, '$tvars').
|
||||||
|
|
||||||
@@ -147,14 +171,16 @@ types_to_scode(Ts) -> lists:map(fun type_to_scode/1, Ts).
|
|||||||
|
|
||||||
%% -- Environment functions --
|
%% -- Environment functions --
|
||||||
|
|
||||||
init_env(ChildContracts, ContractName, FunNames, Name, Args, Options) ->
|
init_env(ChildContracts, ContractName, FunNames, Name, Args, SavedFreshNames, Options) ->
|
||||||
#env{ vars = [ {X, {arg, I}} || {I, {X, _}} <- with_ixs(Args) ],
|
#env{ vars = [ {X, {arg, I}} || {I, {X, _}} <- with_ixs(Args) ],
|
||||||
contract = ContractName,
|
contract = ContractName,
|
||||||
child_contracts = ChildContracts,
|
child_contracts = ChildContracts,
|
||||||
locals = FunNames,
|
locals = FunNames,
|
||||||
current_function = Name,
|
current_function = Name,
|
||||||
options = Options,
|
options = Options,
|
||||||
tailpos = true }.
|
tailpos = true,
|
||||||
|
saved_fresh_names = SavedFreshNames,
|
||||||
|
debug_info = proplists:get_value(debug_info, Options, false) }.
|
||||||
|
|
||||||
next_var(#env{ vars = Vars }) ->
|
next_var(#env{ vars = Vars }) ->
|
||||||
1 + lists:max([-1 | [J || {_, {var, J}} <- Vars]]).
|
1 + lists:max([-1 | [J || {_, {var, J}} <- Vars]]).
|
||||||
@@ -183,9 +209,10 @@ serialize_contract_code(Env, C) ->
|
|||||||
end,
|
end,
|
||||||
case maps:get(C, Cache, none) of
|
case maps:get(C, Cache, none) of
|
||||||
none ->
|
none ->
|
||||||
Options = Env#env.options,
|
Options = Env#env.options,
|
||||||
FCode = maps:get(C, Env#env.child_contracts),
|
SavedFreshNames = Env#env.saved_fresh_names,
|
||||||
FateCode = compile(Env#env.child_contracts, FCode, Options),
|
FCode = maps:get(C, Env#env.child_contracts),
|
||||||
|
FateCode = compile(Env#env.child_contracts, FCode, SavedFreshNames, Options),
|
||||||
ByteCode = aeb_fate_code:serialize(FateCode, []),
|
ByteCode = aeb_fate_code:serialize(FateCode, []),
|
||||||
{ok, Version} = aeso_compiler:version(),
|
{ok, Version} = aeso_compiler:version(),
|
||||||
OriginalSourceCode = proplists:get_value(original_src, Options, ""),
|
OriginalSourceCode = proplists:get_value(original_src, Options, ""),
|
||||||
@@ -209,6 +236,7 @@ lit_to_fate(Env, L) ->
|
|||||||
{bytes, B} -> aeb_fate_data:make_bytes(B);
|
{bytes, B} -> aeb_fate_data:make_bytes(B);
|
||||||
{bool, B} -> aeb_fate_data:make_boolean(B);
|
{bool, B} -> aeb_fate_data:make_boolean(B);
|
||||||
{account_pubkey, K} -> aeb_fate_data:make_address(K);
|
{account_pubkey, K} -> aeb_fate_data:make_address(K);
|
||||||
|
{signature, S} -> aeb_fate_data:make_bytes(S);
|
||||||
{contract_pubkey, K} -> aeb_fate_data:make_contract(K);
|
{contract_pubkey, K} -> aeb_fate_data:make_contract(K);
|
||||||
{oracle_pubkey, K} -> aeb_fate_data:make_oracle(K);
|
{oracle_pubkey, K} -> aeb_fate_data:make_oracle(K);
|
||||||
{oracle_query_id, H} -> aeb_fate_data:make_oracle_query(H);
|
{oracle_query_id, H} -> aeb_fate_data:make_oracle_query(H);
|
||||||
@@ -219,97 +247,106 @@ lit_to_fate(Env, L) ->
|
|||||||
term_to_fate(E) -> term_to_fate(#env{}, #{}, E).
|
term_to_fate(E) -> term_to_fate(#env{}, #{}, E).
|
||||||
term_to_fate(GlobEnv, E) -> term_to_fate(GlobEnv, #{}, E).
|
term_to_fate(GlobEnv, E) -> term_to_fate(GlobEnv, #{}, E).
|
||||||
|
|
||||||
term_to_fate(GlobEnv, _Env, {lit, L}) ->
|
term_to_fate(GlobEnv, _Env, {lit, _, L}) ->
|
||||||
lit_to_fate(GlobEnv, L);
|
lit_to_fate(GlobEnv, L);
|
||||||
%% negative literals are parsed as 0 - N
|
%% negative literals are parsed as 0 - N
|
||||||
term_to_fate(_GlobEnv, _Env, {op, '-', [{lit, {int, 0}}, {lit, {int, N}}]}) ->
|
term_to_fate(_GlobEnv, _Env, {op, _, '-', [{lit, _, {int, 0}}, {lit, _, {int, N}}]}) ->
|
||||||
aeb_fate_data:make_integer(-N);
|
aeb_fate_data:make_integer(-N);
|
||||||
term_to_fate(_GlobEnv, _Env, nil) ->
|
term_to_fate(_GlobEnv, _Env, {nil, _}) ->
|
||||||
aeb_fate_data:make_list([]);
|
aeb_fate_data:make_list([]);
|
||||||
term_to_fate(GlobEnv, Env, {op, '::', [Hd, Tl]}) ->
|
term_to_fate(GlobEnv, Env, {op, _, '::', [Hd, Tl]}) ->
|
||||||
%% The Tl will translate into a list, because FATE lists are just lists
|
%% The Tl will translate into a list, because FATE lists are just lists
|
||||||
[term_to_fate(GlobEnv, Env, Hd) | term_to_fate(GlobEnv, Env, Tl)];
|
[term_to_fate(GlobEnv, Env, Hd) | term_to_fate(GlobEnv, Env, Tl)];
|
||||||
term_to_fate(GlobEnv, Env, {tuple, As}) ->
|
term_to_fate(GlobEnv, Env, {tuple, _, As}) ->
|
||||||
aeb_fate_data:make_tuple(list_to_tuple([ term_to_fate(GlobEnv, Env, A) || A<-As]));
|
aeb_fate_data:make_tuple(list_to_tuple([ term_to_fate(GlobEnv, Env, A) || A<-As]));
|
||||||
term_to_fate(GlobEnv, Env, {con, Ar, I, As}) ->
|
term_to_fate(GlobEnv, Env, {con, _, Ar, I, As}) ->
|
||||||
FateAs = [ term_to_fate(GlobEnv, Env, A) || A <- As ],
|
FateAs = [ term_to_fate(GlobEnv, Env, A) || A <- As ],
|
||||||
aeb_fate_data:make_variant(Ar, I, list_to_tuple(FateAs));
|
aeb_fate_data:make_variant(Ar, I, list_to_tuple(FateAs));
|
||||||
term_to_fate(_GlobEnv, _Env, {builtin, bits_all, []}) ->
|
term_to_fate(_GlobEnv, _Env, {builtin, _, bits_all, []}) ->
|
||||||
aeb_fate_data:make_bits(-1);
|
aeb_fate_data:make_bits(-1);
|
||||||
term_to_fate(_GlobEnv, _Env, {builtin, bits_none, []}) ->
|
term_to_fate(_GlobEnv, _Env, {builtin, _, bits_none, []}) ->
|
||||||
aeb_fate_data:make_bits(0);
|
aeb_fate_data:make_bits(0);
|
||||||
term_to_fate(GlobEnv, _Env, {op, bits_set, [B, I]}) ->
|
term_to_fate(GlobEnv, _Env, {op, _, bits_set, [B, I]}) ->
|
||||||
{bits, N} = term_to_fate(GlobEnv, B),
|
{bits, N} = term_to_fate(GlobEnv, B),
|
||||||
J = term_to_fate(GlobEnv, I),
|
J = term_to_fate(GlobEnv, I),
|
||||||
{bits, N bor (1 bsl J)};
|
{bits, N bor (1 bsl J)};
|
||||||
term_to_fate(GlobEnv, _Env, {op, bits_clear, [B, I]}) ->
|
term_to_fate(GlobEnv, _Env, {op, _, bits_clear, [B, I]}) ->
|
||||||
{bits, N} = term_to_fate(GlobEnv, B),
|
{bits, N} = term_to_fate(GlobEnv, B),
|
||||||
J = term_to_fate(GlobEnv, I),
|
J = term_to_fate(GlobEnv, I),
|
||||||
{bits, N band bnot (1 bsl J)};
|
{bits, N band bnot (1 bsl J)};
|
||||||
term_to_fate(GlobEnv, Env, {'let', X, E, Body}) ->
|
term_to_fate(GlobEnv, Env, {'let', _, X, E, Body}) ->
|
||||||
Env1 = Env#{ X => term_to_fate(GlobEnv, Env, E) },
|
Env1 = Env#{ X => term_to_fate(GlobEnv, Env, E) },
|
||||||
term_to_fate(GlobEnv, Env1, Body);
|
term_to_fate(GlobEnv, Env1, Body);
|
||||||
term_to_fate(_GlobEnv, Env, {var, X}) ->
|
term_to_fate(_GlobEnv, Env, {var, _, X}) ->
|
||||||
case maps:get(X, Env, undefined) of
|
case maps:get(X, Env, undefined) of
|
||||||
undefined -> throw(not_a_fate_value);
|
undefined -> throw(not_a_fate_value);
|
||||||
V -> V
|
V -> V
|
||||||
end;
|
end;
|
||||||
term_to_fate(_GlobEnv, _Env, {builtin, map_empty, []}) ->
|
term_to_fate(_GlobEnv, _Env, {builtin, _, map_empty, []}) ->
|
||||||
aeb_fate_data:make_map(#{});
|
aeb_fate_data:make_map(#{});
|
||||||
term_to_fate(GlobEnv, Env, {op, map_set, [M, K, V]}) ->
|
term_to_fate(GlobEnv, Env, {op, _, map_set, [M, K, V]}) ->
|
||||||
Map = term_to_fate(GlobEnv, Env, M),
|
Map = term_to_fate(GlobEnv, Env, M),
|
||||||
Map#{term_to_fate(GlobEnv, Env, K) => term_to_fate(GlobEnv, Env, V)};
|
Map#{term_to_fate(GlobEnv, Env, K) => term_to_fate(GlobEnv, Env, V)};
|
||||||
|
term_to_fate(GlobEnv, Env, {builtin, _, bytes_to_any_size, [Bs]}) ->
|
||||||
|
term_to_fate(GlobEnv, Env, Bs);
|
||||||
term_to_fate(_GlobEnv, _Env, _) ->
|
term_to_fate(_GlobEnv, _Env, _) ->
|
||||||
throw(not_a_fate_value).
|
throw(not_a_fate_value).
|
||||||
|
|
||||||
to_scode(Env, T) ->
|
to_scode(Env, T) ->
|
||||||
try term_to_fate(Env, T) of
|
try term_to_fate(Env, T) of
|
||||||
V -> [push(?i(V))]
|
V ->
|
||||||
|
FAnn = element(2, T),
|
||||||
|
[dbg_loc(Env, FAnn), push(?i(V))]
|
||||||
catch throw:not_a_fate_value ->
|
catch throw:not_a_fate_value ->
|
||||||
to_scode1(Env, T)
|
to_scode1(Env, T)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
to_scode1(Env, {lit, L}) ->
|
to_scode1(Env, {lit, Ann, L}) ->
|
||||||
[push(?i(lit_to_fate(Env, L)))];
|
[ dbg_loc(Env, Ann), push(?i(lit_to_fate(Env, L))) ];
|
||||||
|
|
||||||
to_scode1(_Env, nil) ->
|
to_scode1(Env, {nil, Ann}) ->
|
||||||
[aeb_fate_ops:nil(?a)];
|
[ dbg_loc(Env, Ann), aeb_fate_ops:nil(?a) ];
|
||||||
|
|
||||||
to_scode1(Env, {var, X}) ->
|
to_scode1(Env, {var, Ann, X}) ->
|
||||||
[push(lookup_var(Env, X))];
|
[ dbg_loc(Env, Ann), push(lookup_var(Env, X)) ];
|
||||||
|
|
||||||
to_scode1(Env, {con, Ar, I, As}) ->
|
to_scode1(Env, {con, Ann, Ar, I, As}) ->
|
||||||
N = length(As),
|
N = length(As),
|
||||||
[[to_scode(notail(Env), A) || A <- As],
|
[ dbg_loc(Env, Ann),
|
||||||
aeb_fate_ops:variant(?a, ?i(Ar), ?i(I), ?i(N))];
|
[to_scode(notail(Env), A) || A <- As],
|
||||||
|
aeb_fate_ops:variant(?a, ?i(Ar), ?i(I), ?i(N)) ];
|
||||||
|
|
||||||
to_scode1(Env, {tuple, As}) ->
|
to_scode1(Env, {tuple, Ann, As}) ->
|
||||||
N = length(As),
|
N = length(As),
|
||||||
[[ to_scode(notail(Env), A) || A <- As ],
|
[ dbg_loc(Env, Ann),
|
||||||
tuple(N)];
|
[ to_scode(notail(Env), A) || A <- As ],
|
||||||
|
tuple(N) ];
|
||||||
|
|
||||||
to_scode1(Env, {proj, E, I}) ->
|
to_scode1(Env, {proj, Ann, E, I}) ->
|
||||||
[to_scode(notail(Env), E),
|
[ dbg_loc(Env, Ann),
|
||||||
aeb_fate_ops:element_op(?a, ?i(I), ?a)];
|
to_scode(notail(Env), E),
|
||||||
|
aeb_fate_ops:element_op(?a, ?i(I), ?a) ];
|
||||||
|
|
||||||
to_scode1(Env, {set_proj, R, I, E}) ->
|
to_scode1(Env, {set_proj, Ann, R, I, E}) ->
|
||||||
[to_scode(notail(Env), E),
|
[ dbg_loc(Env, Ann),
|
||||||
to_scode(notail(Env), R),
|
to_scode(notail(Env), E),
|
||||||
aeb_fate_ops:setelement(?a, ?i(I), ?a, ?a)];
|
to_scode(notail(Env), R),
|
||||||
|
aeb_fate_ops:setelement(?a, ?i(I), ?a, ?a) ];
|
||||||
|
|
||||||
to_scode1(Env, {op, Op, Args}) ->
|
to_scode1(Env, {op, Ann, Op, Args}) ->
|
||||||
call_to_scode(Env, op_to_scode(Op), Args);
|
[ dbg_loc(Env, Ann) | call_to_scode(Env, op_to_scode(Op), Args) ];
|
||||||
|
|
||||||
to_scode1(Env, {'let', X, {var, Y}, Body}) ->
|
to_scode1(Env, {'let', Ann, X, {var, _, Y}, Body}) ->
|
||||||
Env1 = bind_var(X, lookup_var(Env, Y), Env),
|
Env1 = bind_var(X, lookup_var(Env, Y), Env),
|
||||||
to_scode(Env1, Body);
|
[ dbg_loc(Env, Ann) | dbg_scoped_vars(Env1, [X], to_scode(Env1, Body)) ];
|
||||||
to_scode1(Env, {'let', X, Expr, Body}) ->
|
to_scode1(Env, {'let', Ann, X, Expr, Body}) ->
|
||||||
{I, Env1} = bind_local(X, Env),
|
{I, Env1} = bind_local(X, Env),
|
||||||
[ to_scode(notail(Env), Expr),
|
SCode = [ to_scode(notail(Env), Expr),
|
||||||
aeb_fate_ops:store({var, I}, {stack, 0}),
|
aeb_fate_ops:store({var, I}, {stack, 0}),
|
||||||
to_scode(Env1, Body) ];
|
to_scode(Env1, Body) ],
|
||||||
|
[ dbg_loc(Env, Ann) | dbg_scoped_vars(Env1, [X], SCode) ];
|
||||||
|
|
||||||
to_scode1(Env = #env{ current_function = Fun, tailpos = true }, {def, Fun, Args}) ->
|
to_scode1(Env = #env{ current_function = Fun, tailpos = true, debug_info = false }, {def, Ann, Fun, Args}) ->
|
||||||
%% Tail-call to current function, f(e0..en). Compile to
|
%% Tail-call to current function, f(e0..en). Compile to
|
||||||
%% [ let xi = ei ]
|
%% [ let xi = ei ]
|
||||||
%% [ STORE argi xi ]
|
%% [ STORE argi xi ]
|
||||||
@@ -322,61 +359,62 @@ to_scode1(Env = #env{ current_function = Fun, tailpos = true }, {def, Fun, Args}
|
|||||||
aeb_fate_ops:store({var, I}, ?a)],
|
aeb_fate_ops:store({var, I}, ?a)],
|
||||||
{[I | Is], Acc1, Env2}
|
{[I | Is], Acc1, Env2}
|
||||||
end, {[], [], Env}, Args),
|
end, {[], [], Env}, Args),
|
||||||
[ Code,
|
[ dbg_loc(Env, Ann),
|
||||||
|
Code,
|
||||||
[ aeb_fate_ops:store({arg, I}, {var, J})
|
[ aeb_fate_ops:store({arg, I}, {var, J})
|
||||||
|| {I, J} <- lists:zip(lists:seq(0, length(Vars) - 1),
|
|| {I, J} <- lists:zip(lists:seq(0, length(Vars) - 1),
|
||||||
lists:reverse(Vars)) ],
|
lists:reverse(Vars)) ],
|
||||||
loop ];
|
loop ];
|
||||||
to_scode1(Env, {def, Fun, Args}) ->
|
to_scode1(Env, {def, Ann, Fun, Args}) ->
|
||||||
FName = make_function_id(Fun),
|
FName = make_function_id(Fun),
|
||||||
Lbl = aeb_fate_data:make_string(FName),
|
Lbl = aeb_fate_data:make_string(FName),
|
||||||
call_to_scode(Env, local_call(Env, ?i(Lbl)), Args);
|
[ dbg_loc(Env, Ann) | call_to_scode(Env, local_call(Env, ?i(Lbl)), Args) ];
|
||||||
to_scode1(Env, {funcall, Fun, Args}) ->
|
to_scode1(Env, {funcall, Ann, Fun, Args}) ->
|
||||||
call_to_scode(Env, [to_scode(Env, Fun), local_call(Env, ?a)], Args);
|
[ dbg_loc(Env, Ann) | call_to_scode(Env, [to_scode(Env, Fun), local_call(Env, ?a)], Args) ];
|
||||||
|
|
||||||
to_scode1(Env, {builtin, B, Args}) ->
|
to_scode1(Env, {builtin, Ann, B, Args}) ->
|
||||||
builtin_to_scode(Env, B, Args);
|
[ dbg_loc(Env, Ann) | builtin_to_scode(Env, B, Args) ];
|
||||||
|
|
||||||
to_scode1(Env, {remote, ArgsT, RetT, Ct, Fun, [Gas, Value, Protected | Args]}) ->
|
to_scode1(Env, {remote, Ann, ArgsT, RetT, Ct, Fun, [Gas, Value, Protected | Args]}) ->
|
||||||
Lbl = make_function_id(Fun),
|
Lbl = make_function_id(Fun),
|
||||||
{ArgTypes, RetType0} = typesig_to_scode([{"_", T} || T <- ArgsT], RetT),
|
{ArgTypes, RetType0} = typesig_to_scode([{"_", T} || T <- ArgsT], RetT),
|
||||||
ArgType = ?i(aeb_fate_data:make_typerep({tuple, ArgTypes})),
|
ArgType = ?i(aeb_fate_data:make_typerep({tuple, ArgTypes})),
|
||||||
RetType = ?i(aeb_fate_data:make_typerep(RetType0)),
|
RetType = ?i(aeb_fate_data:make_typerep(RetType0)),
|
||||||
case Protected of
|
SCode = case Protected of
|
||||||
{lit, {bool, false}} ->
|
{lit, _, {bool, false}} ->
|
||||||
case Gas of
|
case Gas of
|
||||||
{builtin, call_gas_left, _} ->
|
{builtin, _, call_gas_left, _} ->
|
||||||
Call = aeb_fate_ops:call_r(?a, Lbl, ArgType, RetType, ?a),
|
Call = aeb_fate_ops:call_r(?a, Lbl, ArgType, RetType, ?a),
|
||||||
call_to_scode(Env, Call, [Ct, Value | Args]);
|
call_to_scode(Env, Call, [Ct, Value | Args]);
|
||||||
_ ->
|
_ ->
|
||||||
Call = aeb_fate_ops:call_gr(?a, Lbl, ArgType, RetType, ?a, ?a),
|
Call = aeb_fate_ops:call_gr(?a, Lbl, ArgType, RetType, ?a, ?a),
|
||||||
call_to_scode(Env, Call, [Ct, Value, Gas | Args])
|
call_to_scode(Env, Call, [Ct, Value, Gas | Args])
|
||||||
end;
|
end;
|
||||||
{lit, {bool, true}} ->
|
{lit, _, {bool, true}} ->
|
||||||
Call = aeb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?i(true)),
|
Call = aeb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?i(true)),
|
||||||
call_to_scode(Env, Call, [Ct, Value, Gas | Args]);
|
call_to_scode(Env, Call, [Ct, Value, Gas | Args]);
|
||||||
_ ->
|
_ ->
|
||||||
Call = aeb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?a),
|
Call = aeb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?a),
|
||||||
call_to_scode(Env, Call, [Ct, Value, Gas, Protected | Args])
|
call_to_scode(Env, Call, [Ct, Value, Gas, Protected | Args])
|
||||||
end;
|
end,
|
||||||
|
[ dbg_loc(Env, Ann) | SCode ];
|
||||||
|
|
||||||
to_scode1(_Env, {get_state, Reg}) ->
|
to_scode1(Env, {get_state, Ann, Reg}) ->
|
||||||
[push(?s(Reg))];
|
[ dbg_loc(Env, Ann), push(?s(Reg)) ];
|
||||||
to_scode1(Env, {set_state, Reg, Val}) ->
|
to_scode1(Env, {set_state, Ann, Reg, Val}) ->
|
||||||
call_to_scode(Env, [{'STORE', ?s(Reg), ?a},
|
[ dbg_loc(Env, Ann) | call_to_scode(Env, [{'STORE', ?s(Reg), ?a}, tuple(0)], [Val]) ];
|
||||||
tuple(0)], [Val]);
|
|
||||||
|
|
||||||
to_scode1(Env, {closure, Fun, FVs}) ->
|
to_scode1(Env, {closure, Ann, Fun, FVs}) ->
|
||||||
to_scode(Env, {tuple, [{lit, {string, make_function_id(Fun)}}, FVs]});
|
[ to_scode(Env, {tuple, Ann, [{lit, Ann, {string, make_function_id(Fun)}}, FVs]}) ];
|
||||||
|
|
||||||
to_scode1(Env, {switch, Case}) ->
|
to_scode1(Env, {switch, Ann, Case}) ->
|
||||||
split_to_scode(Env, Case).
|
[ dbg_loc(Env, Ann) | split_to_scode(Env, Case) ].
|
||||||
|
|
||||||
local_call( Env, Fun) when Env#env.tailpos -> aeb_fate_ops:call_t(Fun);
|
local_call( Env = #env{debug_info = false}, Fun) when Env#env.tailpos -> aeb_fate_ops:call_t(Fun);
|
||||||
local_call(_Env, Fun) -> aeb_fate_ops:call(Fun).
|
local_call(_Env, Fun) -> aeb_fate_ops:call(Fun).
|
||||||
|
|
||||||
split_to_scode(Env, {nosplit, Expr}) ->
|
split_to_scode(Env, {nosplit, Renames, Expr}) ->
|
||||||
[switch_body, to_scode(Env, Expr)];
|
[switch_body, dbg_scoped_vars(Env, Renames, to_scode(Env, Expr))];
|
||||||
split_to_scode(Env, {split, {tuple, _}, X, Alts}) ->
|
split_to_scode(Env, {split, {tuple, _}, X, Alts}) ->
|
||||||
{Def, Alts1} = catchall_to_scode(Env, X, Alts),
|
{Def, Alts1} = catchall_to_scode(Env, X, Alts),
|
||||||
Arg = lookup_var(Env, X),
|
Arg = lookup_var(Env, X),
|
||||||
@@ -505,8 +543,18 @@ builtin_to_scode(Env, bytes_concat, [_, _] = Args) ->
|
|||||||
call_to_scode(Env, aeb_fate_ops:bytes_concat(?a, ?a, ?a), Args);
|
call_to_scode(Env, aeb_fate_ops:bytes_concat(?a, ?a, ?a), Args);
|
||||||
builtin_to_scode(Env, bytes_split, [_, _] = Args) ->
|
builtin_to_scode(Env, bytes_split, [_, _] = Args) ->
|
||||||
call_to_scode(Env, aeb_fate_ops:bytes_split(?a, ?a, ?a), Args);
|
call_to_scode(Env, aeb_fate_ops:bytes_split(?a, ?a, ?a), Args);
|
||||||
|
builtin_to_scode(Env, bytes_split_any, [_, _] = Args) ->
|
||||||
|
call_to_scode(Env, aeb_fate_ops:bytes_split_any(?a, ?a, ?a), Args);
|
||||||
|
builtin_to_scode(Env, bytes_to_fixed_size, [_, _] = Args) ->
|
||||||
|
call_to_scode(Env, aeb_fate_ops:bytes_to_fixed_size(?a, ?a, ?a), Args);
|
||||||
|
builtin_to_scode(Env, bytes_to_any_size, [A]) ->
|
||||||
|
[to_scode(Env, A)]; %% no_op!
|
||||||
|
builtin_to_scode(Env, bytes_size, [_] = Args) ->
|
||||||
|
call_to_scode(Env, aeb_fate_ops:bytes_size(?a, ?a), Args);
|
||||||
builtin_to_scode(Env, abort, [_] = Args) ->
|
builtin_to_scode(Env, abort, [_] = Args) ->
|
||||||
call_to_scode(Env, aeb_fate_ops:abort(?a), Args);
|
call_to_scode(Env, aeb_fate_ops:abort(?a), Args);
|
||||||
|
builtin_to_scode(Env, exit, [_] = Args) ->
|
||||||
|
call_to_scode(Env, aeb_fate_ops:exit(?a), Args);
|
||||||
builtin_to_scode(Env, chain_spend, [_, _] = Args) ->
|
builtin_to_scode(Env, chain_spend, [_, _] = Args) ->
|
||||||
call_to_scode(Env, [aeb_fate_ops:spend(?a, ?a),
|
call_to_scode(Env, [aeb_fate_ops:spend(?a, ?a),
|
||||||
tuple(0)], Args);
|
tuple(0)], Args);
|
||||||
@@ -524,6 +572,8 @@ builtin_to_scode(_Env, chain_difficulty, []) ->
|
|||||||
[aeb_fate_ops:difficulty(?a)];
|
[aeb_fate_ops:difficulty(?a)];
|
||||||
builtin_to_scode(_Env, chain_gas_limit, []) ->
|
builtin_to_scode(_Env, chain_gas_limit, []) ->
|
||||||
[aeb_fate_ops:gaslimit(?a)];
|
[aeb_fate_ops:gaslimit(?a)];
|
||||||
|
builtin_to_scode(_Env, chain_network_id, []) ->
|
||||||
|
[aeb_fate_ops:network_id(?a)];
|
||||||
builtin_to_scode(_Env, contract_balance, []) ->
|
builtin_to_scode(_Env, contract_balance, []) ->
|
||||||
[aeb_fate_ops:balance(?a)];
|
[aeb_fate_ops:balance(?a)];
|
||||||
builtin_to_scode(_Env, contract_address, []) ->
|
builtin_to_scode(_Env, contract_address, []) ->
|
||||||
@@ -598,7 +648,7 @@ builtin_to_scode(Env, chain_bytecode_hash, [_Addr] = Args) ->
|
|||||||
builtin_to_scode(Env, chain_clone,
|
builtin_to_scode(Env, chain_clone,
|
||||||
[InitArgsT, GasCap, Value, Prot, Contract | InitArgs]) ->
|
[InitArgsT, GasCap, Value, Prot, Contract | InitArgs]) ->
|
||||||
case GasCap of
|
case GasCap of
|
||||||
{builtin, call_gas_left, _} ->
|
{builtin, _, call_gas_left, _} ->
|
||||||
call_to_scode(Env, aeb_fate_ops:clone(?a, ?a, ?a, ?a),
|
call_to_scode(Env, aeb_fate_ops:clone(?a, ?a, ?a, ?a),
|
||||||
[Contract, InitArgsT, Value, Prot | InitArgs]
|
[Contract, InitArgsT, Value, Prot | InitArgs]
|
||||||
);
|
);
|
||||||
@@ -631,6 +681,12 @@ op_to_scode('>=') -> aeb_fate_ops:egt(?a, ?a, ?a);
|
|||||||
op_to_scode('==') -> aeb_fate_ops:eq(?a, ?a, ?a);
|
op_to_scode('==') -> aeb_fate_ops:eq(?a, ?a, ?a);
|
||||||
op_to_scode('!=') -> aeb_fate_ops:neq(?a, ?a, ?a);
|
op_to_scode('!=') -> aeb_fate_ops:neq(?a, ?a, ?a);
|
||||||
op_to_scode('!') -> aeb_fate_ops:not_op(?a, ?a);
|
op_to_scode('!') -> aeb_fate_ops:not_op(?a, ?a);
|
||||||
|
op_to_scode('bnot') -> aeb_fate_ops:bin_not(?a, ?a);
|
||||||
|
op_to_scode('band') -> aeb_fate_ops:bin_and(?a, ?a, ?a);
|
||||||
|
op_to_scode('bor') -> aeb_fate_ops:bin_or(?a, ?a, ?a);
|
||||||
|
op_to_scode('bxor') -> aeb_fate_ops:bin_xor(?a, ?a, ?a);
|
||||||
|
op_to_scode('<<') -> aeb_fate_ops:bin_sl(?a, ?a, ?a);
|
||||||
|
op_to_scode('>>') -> aeb_fate_ops:bin_sr(?a, ?a, ?a);
|
||||||
op_to_scode(map_get) -> aeb_fate_ops:map_lookup(?a, ?a, ?a);
|
op_to_scode(map_get) -> aeb_fate_ops:map_lookup(?a, ?a, ?a);
|
||||||
op_to_scode(map_get_d) -> aeb_fate_ops:map_lookup(?a, ?a, ?a, ?a);
|
op_to_scode(map_get_d) -> aeb_fate_ops:map_lookup(?a, ?a, ?a, ?a);
|
||||||
op_to_scode(map_set) -> aeb_fate_ops:map_update(?a, ?a, ?a, ?a);
|
op_to_scode(map_set) -> aeb_fate_ops:map_update(?a, ?a, ?a, ?a);
|
||||||
@@ -641,6 +697,7 @@ op_to_scode(map_member) -> aeb_fate_ops:map_member(?a, ?a, ?a);
|
|||||||
op_to_scode(map_size) -> aeb_fate_ops:map_size_(?a, ?a);
|
op_to_scode(map_size) -> aeb_fate_ops:map_size_(?a, ?a);
|
||||||
op_to_scode(stringinternal_length) -> aeb_fate_ops:str_length(?a, ?a);
|
op_to_scode(stringinternal_length) -> aeb_fate_ops:str_length(?a, ?a);
|
||||||
op_to_scode(stringinternal_concat) -> aeb_fate_ops:str_join(?a, ?a, ?a);
|
op_to_scode(stringinternal_concat) -> aeb_fate_ops:str_join(?a, ?a, ?a);
|
||||||
|
op_to_scode(stringinternal_to_bytes) -> aeb_fate_ops:str_to_bytes(?a, ?a);
|
||||||
op_to_scode(stringinternal_to_list) -> aeb_fate_ops:str_to_list(?a, ?a);
|
op_to_scode(stringinternal_to_list) -> aeb_fate_ops:str_to_list(?a, ?a);
|
||||||
op_to_scode(stringinternal_from_list) -> aeb_fate_ops:str_from_list(?a, ?a);
|
op_to_scode(stringinternal_from_list) -> aeb_fate_ops:str_from_list(?a, ?a);
|
||||||
op_to_scode(stringinternal_to_lower) -> aeb_fate_ops:str_to_lower(?a, ?a);
|
op_to_scode(stringinternal_to_lower) -> aeb_fate_ops:str_to_lower(?a, ?a);
|
||||||
@@ -655,7 +712,10 @@ op_to_scode(bits_intersection) -> aeb_fate_ops:bits_and(?a, ?a, ?a);
|
|||||||
op_to_scode(bits_union) -> aeb_fate_ops:bits_or(?a, ?a, ?a);
|
op_to_scode(bits_union) -> aeb_fate_ops:bits_or(?a, ?a, ?a);
|
||||||
op_to_scode(bits_difference) -> aeb_fate_ops:bits_diff(?a, ?a, ?a);
|
op_to_scode(bits_difference) -> aeb_fate_ops:bits_diff(?a, ?a, ?a);
|
||||||
op_to_scode(address_to_str) -> aeb_fate_ops:addr_to_str(?a, ?a);
|
op_to_scode(address_to_str) -> aeb_fate_ops:addr_to_str(?a, ?a);
|
||||||
|
op_to_scode(address_to_bytes) -> aeb_fate_ops:addr_to_bytes(?a, ?a);
|
||||||
op_to_scode(int_to_str) -> aeb_fate_ops:int_to_str(?a, ?a);
|
op_to_scode(int_to_str) -> aeb_fate_ops:int_to_str(?a, ?a);
|
||||||
|
op_to_scode(int_to_bytes) -> aeb_fate_ops:int_to_bytes(?a, ?a, ?a);
|
||||||
|
op_to_scode(int_mulmod) -> aeb_fate_ops:mulmod(?a, ?a, ?a, ?a);
|
||||||
op_to_scode(contract_to_address) -> aeb_fate_ops:contract_to_address(?a, ?a);
|
op_to_scode(contract_to_address) -> aeb_fate_ops:contract_to_address(?a, ?a);
|
||||||
op_to_scode(address_to_contract) -> aeb_fate_ops:address_to_contract(?a, ?a);
|
op_to_scode(address_to_contract) -> aeb_fate_ops:address_to_contract(?a, ?a);
|
||||||
op_to_scode(crypto_verify_sig) -> aeb_fate_ops:verify_sig(?a, ?a, ?a, ?a);
|
op_to_scode(crypto_verify_sig) -> aeb_fate_ops:verify_sig(?a, ?a, ?a, ?a);
|
||||||
@@ -665,6 +725,7 @@ op_to_scode(crypto_ecrecover_secp256k1) -> aeb_fate_ops:ecrecover_secp256k1(?a,
|
|||||||
op_to_scode(crypto_sha3) -> aeb_fate_ops:sha3(?a, ?a);
|
op_to_scode(crypto_sha3) -> aeb_fate_ops:sha3(?a, ?a);
|
||||||
op_to_scode(crypto_sha256) -> aeb_fate_ops:sha256(?a, ?a);
|
op_to_scode(crypto_sha256) -> aeb_fate_ops:sha256(?a, ?a);
|
||||||
op_to_scode(crypto_blake2b) -> aeb_fate_ops:blake2b(?a, ?a);
|
op_to_scode(crypto_blake2b) -> aeb_fate_ops:blake2b(?a, ?a);
|
||||||
|
op_to_scode(crypto_poseidon) -> aeb_fate_ops:poseidon(?a, ?a, ?a);
|
||||||
op_to_scode(stringinternal_sha3) -> aeb_fate_ops:sha3(?a, ?a);
|
op_to_scode(stringinternal_sha3) -> aeb_fate_ops:sha3(?a, ?a);
|
||||||
op_to_scode(stringinternal_sha256) -> aeb_fate_ops:sha256(?a, ?a);
|
op_to_scode(stringinternal_sha256) -> aeb_fate_ops:sha256(?a, ?a);
|
||||||
op_to_scode(stringinternal_blake2b) -> aeb_fate_ops:blake2b(?a, ?a);
|
op_to_scode(stringinternal_blake2b) -> aeb_fate_ops:blake2b(?a, ?a);
|
||||||
@@ -700,12 +761,83 @@ push(A) -> {'STORE', ?a, A}.
|
|||||||
tuple(0) -> push(?i({tuple, {}}));
|
tuple(0) -> push(?i({tuple, {}}));
|
||||||
tuple(N) -> aeb_fate_ops:tuple(?a, N).
|
tuple(N) -> aeb_fate_ops:tuple(?a, N).
|
||||||
|
|
||||||
|
%% -- Debug info functions --
|
||||||
|
|
||||||
|
dbg_contract(#env{debug_info = false}) ->
|
||||||
|
[];
|
||||||
|
dbg_contract(#env{contract = Contract}) ->
|
||||||
|
[{'DBG_CONTRACT', {immediate, Contract}}].
|
||||||
|
|
||||||
|
dbg_loc(#env{debug_info = false}, _) ->
|
||||||
|
[];
|
||||||
|
dbg_loc(_Env, Ann) ->
|
||||||
|
File = case proplists:get_value(file, Ann, no_file) of
|
||||||
|
no_file -> "";
|
||||||
|
F -> F
|
||||||
|
end,
|
||||||
|
Line = proplists:get_value(line, Ann, undefined),
|
||||||
|
case Line of
|
||||||
|
undefined -> [];
|
||||||
|
_ -> [{'DBG_LOC', {immediate, File}, {immediate, Line}}]
|
||||||
|
end.
|
||||||
|
|
||||||
|
dbg_scoped_vars(#env{debug_info = false}, _, SCode) ->
|
||||||
|
SCode;
|
||||||
|
dbg_scoped_vars(_Env, [], SCode) ->
|
||||||
|
SCode;
|
||||||
|
dbg_scoped_vars(Env, [{SavedVarName, Var} | Rest], SCode) ->
|
||||||
|
dbg_scoped_vars(Env, Rest, dbg_scoped_var(Env, SavedVarName, Var, SCode));
|
||||||
|
dbg_scoped_vars(Env = #env{saved_fresh_names = SavedFreshNames}, [Var | Rest], SCode) ->
|
||||||
|
SavedVarName = maps:get(Var, SavedFreshNames, Var),
|
||||||
|
dbg_scoped_vars(Env, Rest, dbg_scoped_var(Env, SavedVarName, Var, SCode)).
|
||||||
|
|
||||||
|
dbg_scoped_var(Env, SavedVarName, Var, SCode) ->
|
||||||
|
case SavedVarName == "_" orelse is_fresh_name(SavedVarName) of
|
||||||
|
true ->
|
||||||
|
SCode;
|
||||||
|
false ->
|
||||||
|
Register = lookup_var(Env, Var),
|
||||||
|
Def = [{'DBG_DEF', {immediate, SavedVarName}, Register}],
|
||||||
|
Undef = [{'DBG_UNDEF', {immediate, SavedVarName}, Register}],
|
||||||
|
Def ++ dbg_undef(Undef, SCode)
|
||||||
|
end.
|
||||||
|
|
||||||
|
is_fresh_name([$% | _]) ->
|
||||||
|
true;
|
||||||
|
is_fresh_name(_) ->
|
||||||
|
false.
|
||||||
|
|
||||||
|
dbg_undef(_Undef, missing) ->
|
||||||
|
missing;
|
||||||
|
dbg_undef(Undef, loop) ->
|
||||||
|
[Undef, loop];
|
||||||
|
dbg_undef(Undef, switch_body) ->
|
||||||
|
[switch_body, Undef];
|
||||||
|
dbg_undef(Undef, {switch, Arg, Type, Alts, Catch}) ->
|
||||||
|
NewAlts = [ dbg_undef(Undef, Alt) || Alt <- Alts ],
|
||||||
|
NewCatch = dbg_undef(Undef, Catch),
|
||||||
|
NewSwitch = {switch, Arg, Type, NewAlts, NewCatch},
|
||||||
|
NewSwitch;
|
||||||
|
dbg_undef(Undef, SCode) when is_list(SCode) ->
|
||||||
|
lists:droplast(SCode) ++ [dbg_undef(Undef, lists:last(SCode))];
|
||||||
|
dbg_undef(Undef, SCode) when is_tuple(SCode); is_atom(SCode) ->
|
||||||
|
[Mnemonic | _] =
|
||||||
|
case is_tuple(SCode) of
|
||||||
|
true -> tuple_to_list(SCode);
|
||||||
|
false -> [SCode]
|
||||||
|
end,
|
||||||
|
Op = aeb_fate_opcodes:m_to_op(Mnemonic),
|
||||||
|
case aeb_fate_opcodes:end_bb(Op) of
|
||||||
|
true -> [Undef, SCode];
|
||||||
|
false -> [SCode, Undef]
|
||||||
|
end.
|
||||||
|
|
||||||
%% -- Phase II ---------------------------------------------------------------
|
%% -- Phase II ---------------------------------------------------------------
|
||||||
%% Optimize
|
%% Optimize
|
||||||
|
|
||||||
optimize_scode(Funs, Options) ->
|
optimize_scode(Funs, Options) ->
|
||||||
maps:map(fun(Name, Def) -> optimize_fun(Funs, Name, Def, Options) end,
|
maps:map(fun(Name, Def) -> optimize_fun(Funs, Name, Def, Options) end,
|
||||||
Funs).
|
Funs).
|
||||||
|
|
||||||
flatten(missing) -> missing;
|
flatten(missing) -> missing;
|
||||||
flatten(Code) -> lists:map(fun flatten_s/1, lists:flatten(Code)).
|
flatten(Code) -> lists:map(fun flatten_s/1, lists:flatten(Code)).
|
||||||
@@ -835,6 +967,10 @@ attributes(I) ->
|
|||||||
loop -> Impure(pc, []);
|
loop -> Impure(pc, []);
|
||||||
switch_body -> Pure(none, []);
|
switch_body -> Pure(none, []);
|
||||||
'RETURN' -> Impure(pc, []);
|
'RETURN' -> Impure(pc, []);
|
||||||
|
{'DBG_LOC', _, _} -> Impure(none, []);
|
||||||
|
{'DBG_DEF', _, _} -> Impure(none, []);
|
||||||
|
{'DBG_UNDEF', _, _} -> Impure(none, []);
|
||||||
|
{'DBG_CONTRACT', _} -> Impure(none, []);
|
||||||
{'RETURNR', A} -> Impure(pc, A);
|
{'RETURNR', A} -> Impure(pc, A);
|
||||||
{'CALL', A} -> Impure(?a, [A]);
|
{'CALL', A} -> Impure(?a, [A]);
|
||||||
{'CALL_R', A, _, B, C, D} -> Impure(?a, [A, B, C, D]);
|
{'CALL_R', A, _, B, C, D} -> Impure(?a, [A, B, C, D]);
|
||||||
@@ -862,6 +998,13 @@ attributes(I) ->
|
|||||||
{'DIV', A, B, C} -> Pure(A, [B, C]);
|
{'DIV', A, B, C} -> Pure(A, [B, C]);
|
||||||
{'MOD', A, B, C} -> Pure(A, [B, C]);
|
{'MOD', A, B, C} -> Pure(A, [B, C]);
|
||||||
{'POW', A, B, C} -> Pure(A, [B, C]);
|
{'POW', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'MULMOD', A, B, C, D} -> Pure(A, [B, C, D]);
|
||||||
|
{'BAND', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BOR', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BXOR', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BNOT', A, B} -> Pure(A, [B]);
|
||||||
|
{'BSL', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BSR', A, B, C} -> Pure(A, [B, C]);
|
||||||
{'LT', A, B, C} -> Pure(A, [B, C]);
|
{'LT', A, B, C} -> Pure(A, [B, C]);
|
||||||
{'GT', A, B, C} -> Pure(A, [B, C]);
|
{'GT', A, B, C} -> Pure(A, [B, C]);
|
||||||
{'EQ', A, B, C} -> Pure(A, [B, C]);
|
{'EQ', A, B, C} -> Pure(A, [B, C]);
|
||||||
@@ -892,9 +1035,11 @@ attributes(I) ->
|
|||||||
{'APPEND', A, B, C} -> Pure(A, [B, C]);
|
{'APPEND', A, B, C} -> Pure(A, [B, C]);
|
||||||
{'STR_JOIN', A, B, C} -> Pure(A, [B, C]);
|
{'STR_JOIN', A, B, C} -> Pure(A, [B, C]);
|
||||||
{'INT_TO_STR', A, B} -> Pure(A, B);
|
{'INT_TO_STR', A, B} -> Pure(A, B);
|
||||||
|
{'INT_TO_BYTES', A, B, C} -> Pure(A, [B, C]);
|
||||||
{'ADDR_TO_STR', A, B} -> Pure(A, B);
|
{'ADDR_TO_STR', A, B} -> Pure(A, B);
|
||||||
{'STR_REVERSE', A, B} -> Pure(A, B);
|
{'STR_REVERSE', A, B} -> Pure(A, B);
|
||||||
{'STR_LENGTH', A, B} -> Pure(A, B);
|
{'STR_LENGTH', A, B} -> Pure(A, B);
|
||||||
|
{'STR_TO_BYTES', A, B} -> Pure(A, B);
|
||||||
{'INT_TO_ADDR', A, B} -> Pure(A, B);
|
{'INT_TO_ADDR', A, B} -> Pure(A, B);
|
||||||
{'VARIANT', A, B, C, D} -> Pure(A, [?a, B, C, D]);
|
{'VARIANT', A, B, C, D} -> Pure(A, [?a, B, C, D]);
|
||||||
{'VARIANT_TEST', A, B, C} -> Pure(A, [B, C]);
|
{'VARIANT_TEST', A, B, C} -> Pure(A, [B, C]);
|
||||||
@@ -914,18 +1059,23 @@ attributes(I) ->
|
|||||||
{'SHA3', A, B} -> Pure(A, [B]);
|
{'SHA3', A, B} -> Pure(A, [B]);
|
||||||
{'SHA256', A, B} -> Pure(A, [B]);
|
{'SHA256', A, B} -> Pure(A, [B]);
|
||||||
{'BLAKE2B', A, B} -> Pure(A, [B]);
|
{'BLAKE2B', A, B} -> Pure(A, [B]);
|
||||||
|
{'POSEIDON', A, B, C} -> Pure(A, [B, C]);
|
||||||
{'VERIFY_SIG', A, B, C, D} -> Pure(A, [B, C, D]);
|
{'VERIFY_SIG', A, B, C, D} -> Pure(A, [B, C, D]);
|
||||||
{'VERIFY_SIG_SECP256K1', A, B, C, D} -> Pure(A, [B, C, D]);
|
{'VERIFY_SIG_SECP256K1', A, B, C, D} -> Pure(A, [B, C, D]);
|
||||||
{'ECVERIFY_SECP256K1', A, B, C, D} -> Pure(A, [B, C, D]);
|
{'ECVERIFY_SECP256K1', A, B, C, D} -> Pure(A, [B, C, D]);
|
||||||
{'ECRECOVER_SECP256K1', A, B, C} -> Pure(A, [B, C]);
|
{'ECRECOVER_SECP256K1', A, B, C} -> Pure(A, [B, C]);
|
||||||
{'CONTRACT_TO_ADDRESS', A, B} -> Pure(A, [B]);
|
{'CONTRACT_TO_ADDRESS', A, B} -> Pure(A, [B]);
|
||||||
{'ADDRESS_TO_CONTRACT', A, B} -> Pure(A, [B]);
|
{'ADDRESS_TO_CONTRACT', A, B} -> Pure(A, [B]);
|
||||||
|
{'ADDRESS_TO_BYTES', A, B} -> Pure(A, [B]);
|
||||||
{'AUTH_TX_HASH', A} -> Pure(A, []);
|
{'AUTH_TX_HASH', A} -> Pure(A, []);
|
||||||
{'AUTH_TX', A} -> Pure(A, []);
|
{'AUTH_TX', A} -> Pure(A, []);
|
||||||
{'BYTES_TO_INT', A, B} -> Pure(A, [B]);
|
{'BYTES_TO_INT', A, B} -> Pure(A, [B]);
|
||||||
{'BYTES_TO_STR', A, B} -> Pure(A, [B]);
|
{'BYTES_TO_STR', A, B} -> Pure(A, [B]);
|
||||||
{'BYTES_CONCAT', A, B, C} -> Pure(A, [B, C]);
|
{'BYTES_CONCAT', A, B, C} -> Pure(A, [B, C]);
|
||||||
{'BYTES_SPLIT', A, B, C} -> Pure(A, [B, C]);
|
{'BYTES_SPLIT', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BYTES_SPLIT_ANY', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BYTES_SIZE', A, B} -> Pure(A, B);
|
||||||
|
{'BYTES_TO_FIXED_SIZE', A, B, C} -> Pure(A, [B, C]);
|
||||||
{'ORACLE_CHECK', A, B, C, D} -> Pure(A, [B, C, D]);
|
{'ORACLE_CHECK', A, B, C, D} -> Pure(A, [B, C, D]);
|
||||||
{'ORACLE_CHECK_QUERY', A, B, C, D, E} -> Pure(A, [B, C, D, E]);
|
{'ORACLE_CHECK_QUERY', A, B, C, D, E} -> Pure(A, [B, C, D, E]);
|
||||||
{'IS_ORACLE', A, B} -> Pure(A, [B]);
|
{'IS_ORACLE', A, B} -> Pure(A, [B]);
|
||||||
@@ -946,6 +1096,7 @@ attributes(I) ->
|
|||||||
{'MICROBLOCK', A} -> Pure(A, []);
|
{'MICROBLOCK', A} -> Pure(A, []);
|
||||||
{'DIFFICULTY', A} -> Pure(A, []);
|
{'DIFFICULTY', A} -> Pure(A, []);
|
||||||
{'GASLIMIT', A} -> Pure(A, []);
|
{'GASLIMIT', A} -> Pure(A, []);
|
||||||
|
{'NETWORK_ID', A} -> Pure(A, []);
|
||||||
{'GAS', A} -> Pure(A, []);
|
{'GAS', A} -> Pure(A, []);
|
||||||
{'LOG0', A} -> Impure(none, [A]);
|
{'LOG0', A} -> Impure(none, [A]);
|
||||||
{'LOG1', A, B} -> Impure(none, [A, B]);
|
{'LOG1', A, B} -> Impure(none, [A, B]);
|
||||||
@@ -1030,11 +1181,16 @@ independent({i, _, I}, {i, _, J}) ->
|
|||||||
StackI = lists:member(?a, [WI | RI]),
|
StackI = lists:member(?a, [WI | RI]),
|
||||||
StackJ = lists:member(?a, [WJ | RJ]),
|
StackJ = lists:member(?a, [WJ | RJ]),
|
||||||
|
|
||||||
if WI == pc; WJ == pc -> false; %% no jumps
|
ReadStoreI = [] /= [ x || {store, _} <- RI ],
|
||||||
not (PureI or PureJ) -> false; %% at least one is pure
|
ReadStoreJ = [] /= [ x || {store, _} <- RJ ],
|
||||||
StackI and StackJ -> false; %% cannot both use the stack
|
|
||||||
WI == WJ -> false; %% cannot write to the same register
|
if WI == pc; WJ == pc -> false; %% no jumps
|
||||||
true ->
|
not (PureI or PureJ) -> false; %% at least one is pure
|
||||||
|
StackI and StackJ -> false; %% cannot both use the stack
|
||||||
|
WI == WJ -> false; %% cannot write to the same register
|
||||||
|
ReadStoreI and not PureJ -> false; %% can't read store/state if other is impure
|
||||||
|
ReadStoreJ and not PureI -> false; %% can't read store/state if other is impure
|
||||||
|
true ->
|
||||||
%% and cannot write to each other's inputs
|
%% and cannot write to each other's inputs
|
||||||
not lists:member(WI, RJ) andalso
|
not lists:member(WI, RJ) andalso
|
||||||
not lists:member(WJ, RI)
|
not lists:member(WJ, RI)
|
||||||
@@ -1088,7 +1244,8 @@ simpl_top(I, Code, Options) ->
|
|||||||
simpl_top(0, I, Code, _Options) ->
|
simpl_top(0, I, Code, _Options) ->
|
||||||
code_error({optimizer_out_of_fuel, I, Code});
|
code_error({optimizer_out_of_fuel, I, Code});
|
||||||
simpl_top(Fuel, I, Code, Options) ->
|
simpl_top(Fuel, I, Code, Options) ->
|
||||||
apply_rules(Fuel, rules(), I, Code, Options).
|
Rules = [R || R = {Rule, _} <- rules(), proplists:get_value(Rule, Options, true)],
|
||||||
|
apply_rules(Fuel, Rules, I, Code, Options).
|
||||||
|
|
||||||
apply_rules(Fuel, Rules, I, Code, Options) ->
|
apply_rules(Fuel, Rules, I, Code, Options) ->
|
||||||
Cons = fun(X, Xs) -> simpl_top(Fuel - 1, X, Xs, Options) end,
|
Cons = fun(X, Xs) -> simpl_top(Fuel - 1, X, Xs, Options) end,
|
||||||
@@ -1115,29 +1272,29 @@ apply_rules_once([{RName, Rule} | Rules], I, Code) ->
|
|||||||
-define(RULE(Name), {Name, fun Name/2}).
|
-define(RULE(Name), {Name, fun Name/2}).
|
||||||
|
|
||||||
merge_rules() ->
|
merge_rules() ->
|
||||||
[?RULE(r_push_consume),
|
[?RULE(optimize_push_consume),
|
||||||
?RULE(r_one_shot_var),
|
?RULE(optimize_one_shot_var),
|
||||||
?RULE(r_write_to_dead_var),
|
?RULE(optimize_write_to_dead_var),
|
||||||
?RULE(r_inline_switch_target)
|
?RULE(optimize_inline_switch_target)
|
||||||
].
|
].
|
||||||
|
|
||||||
rules() ->
|
rules() ->
|
||||||
merge_rules() ++
|
merge_rules() ++
|
||||||
[?RULE(r_swap_push),
|
[?RULE(optimize_swap_push),
|
||||||
?RULE(r_swap_pop),
|
?RULE(optimize_swap_pop),
|
||||||
?RULE(r_swap_write),
|
?RULE(optimize_swap_write),
|
||||||
?RULE(r_constant_propagation),
|
?RULE(optimize_constant_propagation),
|
||||||
?RULE(r_prune_impossible_branches),
|
?RULE(optimize_prune_impossible_branches),
|
||||||
?RULE(r_single_successful_branch),
|
?RULE(optimize_single_successful_branch),
|
||||||
?RULE(r_inline_store),
|
?RULE(optimize_inline_store),
|
||||||
?RULE(r_float_switch_body)
|
?RULE(optimize_float_switch_body)
|
||||||
].
|
].
|
||||||
|
|
||||||
%% Removing pushes that are immediately consumed.
|
%% Removing pushes that are immediately consumed.
|
||||||
r_push_consume({i, Ann1, {'STORE', ?a, A}}, Code) ->
|
optimize_push_consume({i, Ann1, {'STORE', ?a, A}}, Code) ->
|
||||||
inline_push(Ann1, A, 0, Code, []);
|
inline_push(Ann1, A, 0, Code, []);
|
||||||
%% Writing directly to memory instead of going through the accumulator.
|
%% Writing directly to memory instead of going through the accumulator.
|
||||||
r_push_consume({i, Ann1, I}, [{i, Ann2, {'STORE', R, ?a}} | Code]) ->
|
optimize_push_consume({i, Ann1, I}, [{i, Ann2, {'STORE', R, ?a}} | Code]) ->
|
||||||
IsPush =
|
IsPush =
|
||||||
case op_view(I) of
|
case op_view(I) of
|
||||||
{_, ?a, _} -> true;
|
{_, ?a, _} -> true;
|
||||||
@@ -1149,7 +1306,7 @@ r_push_consume({i, Ann1, I}, [{i, Ann2, {'STORE', R, ?a}} | Code]) ->
|
|||||||
end,
|
end,
|
||||||
if IsPush -> {[{i, merge_ann(Ann1, Ann2), setelement(2, I, R)}], Code};
|
if IsPush -> {[{i, merge_ann(Ann1, Ann2), setelement(2, I, R)}], Code};
|
||||||
true -> false end;
|
true -> false end;
|
||||||
r_push_consume(_, _) -> false.
|
optimize_push_consume(_, _) -> false.
|
||||||
|
|
||||||
inline_push(Ann, Arg, Stack, [{i, _, switch_body} = AI | Code], Acc) ->
|
inline_push(Ann, Arg, Stack, [{i, _, switch_body} = AI | Code], Acc) ->
|
||||||
{AI1, {i, Ann1, _}} = swap_instrs({i, Ann, {'STORE', ?a, Arg}}, AI),
|
{AI1, {i, Ann1, _}} = swap_instrs({i, Ann, {'STORE', ?a, Arg}}, AI),
|
||||||
@@ -1182,7 +1339,7 @@ split_stack_arg(N, [A | As], Acc) ->
|
|||||||
split_stack_arg(N1, As, [A | Acc]).
|
split_stack_arg(N1, As, [A | Acc]).
|
||||||
|
|
||||||
%% Move PUSHes past non-stack instructions.
|
%% Move PUSHes past non-stack instructions.
|
||||||
r_swap_push(Push = {i, _, PushI}, [I | Code]) ->
|
optimize_swap_push(Push = {i, _, PushI}, [I | Code]) ->
|
||||||
case op_view(PushI) of
|
case op_view(PushI) of
|
||||||
{_, ?a, _} ->
|
{_, ?a, _} ->
|
||||||
case independent(Push, I) of
|
case independent(Push, I) of
|
||||||
@@ -1193,10 +1350,10 @@ r_swap_push(Push = {i, _, PushI}, [I | Code]) ->
|
|||||||
end;
|
end;
|
||||||
_ -> false
|
_ -> false
|
||||||
end;
|
end;
|
||||||
r_swap_push(_, _) -> false.
|
optimize_swap_push(_, _) -> false.
|
||||||
|
|
||||||
%% Move non-stack instruction past POPs.
|
%% Move non-stack instruction past POPs.
|
||||||
r_swap_pop(IA = {i, _, I}, [JA = {i, _, J} | Code]) ->
|
optimize_swap_pop(IA = {i, _, I}, [JA = {i, _, J} | Code]) ->
|
||||||
case independent(IA, JA) of
|
case independent(IA, JA) of
|
||||||
true ->
|
true ->
|
||||||
case {op_view(I), op_view(J)} of
|
case {op_view(I), op_view(J)} of
|
||||||
@@ -1204,7 +1361,7 @@ r_swap_pop(IA = {i, _, I}, [JA = {i, _, J} | Code]) ->
|
|||||||
{_, false} -> false;
|
{_, false} -> false;
|
||||||
{{_, IR, IAs}, {_, RJ, JAs}} ->
|
{{_, IR, IAs}, {_, RJ, JAs}} ->
|
||||||
NonStackI = not lists:member(?a, [IR | IAs]),
|
NonStackI = not lists:member(?a, [IR | IAs]),
|
||||||
%% RJ /= ?a to not conflict with r_swap_push
|
%% RJ /= ?a to not conflict with optimize_swap_push
|
||||||
PopJ = RJ /= ?a andalso lists:member(?a, JAs),
|
PopJ = RJ /= ?a andalso lists:member(?a, JAs),
|
||||||
case NonStackI andalso PopJ of
|
case NonStackI andalso PopJ of
|
||||||
false -> false;
|
false -> false;
|
||||||
@@ -1215,22 +1372,22 @@ r_swap_pop(IA = {i, _, I}, [JA = {i, _, J} | Code]) ->
|
|||||||
end;
|
end;
|
||||||
false -> false
|
false -> false
|
||||||
end;
|
end;
|
||||||
r_swap_pop(_, _) -> false.
|
optimize_swap_pop(_, _) -> false.
|
||||||
|
|
||||||
%% Match up writes to variables with instructions further down.
|
%% Match up writes to variables with instructions further down.
|
||||||
r_swap_write(I = {i, _, _}, [J | Code]) ->
|
optimize_swap_write(I = {i, _, _}, [J | Code]) ->
|
||||||
case {var_writes(I), independent(I, J)} of
|
case {var_writes(I), independent(I, J)} of
|
||||||
{[_], true} ->
|
{[_], true} ->
|
||||||
{J1, I1} = swap_instrs(I, J),
|
{J1, I1} = swap_instrs(I, J),
|
||||||
r_swap_write([J1], I1, Code);
|
optimize_swap_write([J1], I1, Code);
|
||||||
_ -> false
|
_ -> false
|
||||||
end;
|
end;
|
||||||
r_swap_write(_, _) -> false.
|
optimize_swap_write(_, _) -> false.
|
||||||
|
|
||||||
r_swap_write(Pre, I, [{i, _, switch_body} = J | Code]) ->
|
optimize_swap_write(Pre, I, [{i, _, switch_body} = J | Code]) ->
|
||||||
{J1, I1} = swap_instrs(I, J),
|
{J1, I1} = swap_instrs(I, J),
|
||||||
r_swap_write([J1 | Pre], I1, Code);
|
optimize_swap_write([J1 | Pre], I1, Code);
|
||||||
r_swap_write(Pre, I, Code0 = [J | Code]) ->
|
optimize_swap_write(Pre, I, Code0 = [J | Code]) ->
|
||||||
case apply_rules_once(merge_rules(), I, Code0) of
|
case apply_rules_once(merge_rules(), I, Code0) of
|
||||||
{_Rule, New, Rest} ->
|
{_Rule, New, Rest} ->
|
||||||
{lists:reverse(Pre) ++ New, Rest};
|
{lists:reverse(Pre) ++ New, Rest};
|
||||||
@@ -1239,27 +1396,27 @@ r_swap_write(Pre, I, Code0 = [J | Code]) ->
|
|||||||
false -> false;
|
false -> false;
|
||||||
true ->
|
true ->
|
||||||
{J1, I1} = swap_instrs(I, J),
|
{J1, I1} = swap_instrs(I, J),
|
||||||
r_swap_write([J1 | Pre], I1, Code)
|
optimize_swap_write([J1 | Pre], I1, Code)
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
r_swap_write(_, _, _) -> false.
|
optimize_swap_write(_, _, _) -> false.
|
||||||
|
|
||||||
%% Precompute instructions with known values
|
%% Precompute instructions with known values
|
||||||
r_constant_propagation(Cons = {i, Ann1, {'CONS', R, X, Xs}}, [{i, Ann, {'IS_NIL', S, R}} | Code]) ->
|
optimize_constant_propagation(Cons = {i, Ann1, {'CONS', R, X, Xs}}, [{i, Ann, {'IS_NIL', S, R}} | Code]) ->
|
||||||
Store = {i, Ann, {'STORE', S, ?i(false)}},
|
Store = {i, Ann, {'STORE', S, ?i(false)}},
|
||||||
Cons1 = case R of
|
Cons1 = case R of
|
||||||
?a -> {i, Ann1, {'CONS', ?void, X, Xs}};
|
?a -> {i, Ann1, {'CONS', ?void, X, Xs}};
|
||||||
_ -> Cons
|
_ -> Cons
|
||||||
end,
|
end,
|
||||||
{[Cons1, Store], Code};
|
{[Cons1, Store], Code};
|
||||||
r_constant_propagation(Nil = {i, Ann1, {'NIL', R}}, [{i, Ann, {'IS_NIL', S, R}} | Code]) ->
|
optimize_constant_propagation(Nil = {i, Ann1, {'NIL', R}}, [{i, Ann, {'IS_NIL', S, R}} | Code]) ->
|
||||||
Store = {i, Ann, {'STORE', S, ?i(true)}},
|
Store = {i, Ann, {'STORE', S, ?i(true)}},
|
||||||
Nil1 = case R of
|
Nil1 = case R of
|
||||||
?a -> {i, Ann1, {'NIL', ?void}};
|
?a -> {i, Ann1, {'NIL', ?void}};
|
||||||
_ -> Nil
|
_ -> Nil
|
||||||
end,
|
end,
|
||||||
{[Nil1, Store], Code};
|
{[Nil1, Store], Code};
|
||||||
r_constant_propagation({i, Ann, I}, Code) ->
|
optimize_constant_propagation({i, Ann, I}, Code) ->
|
||||||
case op_view(I) of
|
case op_view(I) of
|
||||||
false -> false;
|
false -> false;
|
||||||
{Op, R, As} ->
|
{Op, R, As} ->
|
||||||
@@ -1273,7 +1430,7 @@ r_constant_propagation({i, Ann, I}, Code) ->
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
r_constant_propagation(_, _) -> false.
|
optimize_constant_propagation(_, _) -> false.
|
||||||
|
|
||||||
eval_op('ADD', [X, Y]) when is_integer(X), is_integer(Y) -> X + Y;
|
eval_op('ADD', [X, Y]) when is_integer(X), is_integer(Y) -> X + Y;
|
||||||
eval_op('SUB', [X, Y]) when is_integer(X), is_integer(Y) -> X - Y;
|
eval_op('SUB', [X, Y]) when is_integer(X), is_integer(Y) -> X - Y;
|
||||||
@@ -1292,12 +1449,12 @@ eval_op('NOT', [false]) -> true;
|
|||||||
eval_op(_, _) -> no_eval. %% TODO: bits?
|
eval_op(_, _) -> no_eval. %% TODO: bits?
|
||||||
|
|
||||||
%% Prune impossible branches from switches
|
%% Prune impossible branches from switches
|
||||||
r_prune_impossible_branches({switch, ?i(V), Type, Alts, missing}, Code) ->
|
optimize_prune_impossible_branches({switch, ?i(V), Type, Alts, missing}, Code) ->
|
||||||
case pick_branch(Type, V, Alts) of
|
case pick_branch(Type, V, Alts) of
|
||||||
false -> false;
|
false -> false;
|
||||||
Alt -> {Alt, Code}
|
Alt -> {Alt, Code}
|
||||||
end;
|
end;
|
||||||
r_prune_impossible_branches({switch, ?i(V), boolean, [False, True] = Alts, Def}, Code) when V == true; V == false ->
|
optimize_prune_impossible_branches({switch, ?i(V), boolean, [False, True] = Alts, Def}, Code) when V == true; V == false ->
|
||||||
Alts1 = [if V -> missing; true -> False end,
|
Alts1 = [if V -> missing; true -> False end,
|
||||||
if V -> True; true -> missing end],
|
if V -> True; true -> missing end],
|
||||||
case Alts == Alts1 of
|
case Alts == Alts1 of
|
||||||
@@ -1308,7 +1465,7 @@ r_prune_impossible_branches({switch, ?i(V), boolean, [False, True] = Alts, Def},
|
|||||||
_ -> {[{switch, ?i(V), boolean, Alts1, Def}], Code}
|
_ -> {[{switch, ?i(V), boolean, Alts1, Def}], Code}
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
r_prune_impossible_branches(Variant = {i, _, {'VARIANT', R, ?i(_), ?i(Tag), ?i(_)}},
|
optimize_prune_impossible_branches(Variant = {i, _, {'VARIANT', R, ?i(_), ?i(Tag), ?i(_)}},
|
||||||
[{switch, R, Type = {variant, _}, Alts, missing} | Code]) when is_integer(Tag) ->
|
[{switch, R, Type = {variant, _}, Alts, missing} | Code]) when is_integer(Tag) ->
|
||||||
case {R, lists:nth(Tag + 1, Alts)} of
|
case {R, lists:nth(Tag + 1, Alts)} of
|
||||||
{_, missing} ->
|
{_, missing} ->
|
||||||
@@ -1324,7 +1481,7 @@ r_prune_impossible_branches(Variant = {i, _, {'VARIANT', R, ?i(_), ?i(Tag), ?i(_
|
|||||||
false -> {Alt, Code}
|
false -> {Alt, Code}
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
r_prune_impossible_branches(_, _) -> false.
|
optimize_prune_impossible_branches(_, _) -> false.
|
||||||
|
|
||||||
pick_branch(boolean, V, [False, True]) when V == true; V == false ->
|
pick_branch(boolean, V, [False, True]) when V == true; V == false ->
|
||||||
Alt = if V -> True; true -> False end,
|
Alt = if V -> True; true -> False end,
|
||||||
@@ -1337,7 +1494,7 @@ pick_branch(_Type, _V, _Alts) ->
|
|||||||
|
|
||||||
%% If there's a single branch that doesn't abort we can push the code for that
|
%% If there's a single branch that doesn't abort we can push the code for that
|
||||||
%% out of the switch.
|
%% out of the switch.
|
||||||
r_single_successful_branch({switch, R, Type, Alts, Def}, Code) ->
|
optimize_single_successful_branch({switch, R, Type, Alts, Def}, Code) ->
|
||||||
case push_code_out_of_switch([Def | Alts]) of
|
case push_code_out_of_switch([Def | Alts]) of
|
||||||
{_, none} -> false;
|
{_, none} -> false;
|
||||||
{_, many} -> false;
|
{_, many} -> false;
|
||||||
@@ -1345,7 +1502,7 @@ r_single_successful_branch({switch, R, Type, Alts, Def}, Code) ->
|
|||||||
{[Def1 | Alts1], PushedOut} ->
|
{[Def1 | Alts1], PushedOut} ->
|
||||||
{[{switch, R, Type, Alts1, Def1} | PushedOut], Code}
|
{[{switch, R, Type, Alts1, Def1} | PushedOut], Code}
|
||||||
end;
|
end;
|
||||||
r_single_successful_branch(_, _) -> false.
|
optimize_single_successful_branch(_, _) -> false.
|
||||||
|
|
||||||
push_code_out_of_switch([]) -> {[], none};
|
push_code_out_of_switch([]) -> {[], none};
|
||||||
push_code_out_of_switch([Alt | Alts]) ->
|
push_code_out_of_switch([Alt | Alts]) ->
|
||||||
@@ -1381,7 +1538,7 @@ does_abort({switch, _, _, Alts, Def}) ->
|
|||||||
does_abort(_) -> false.
|
does_abort(_) -> false.
|
||||||
|
|
||||||
%% STORE R A, SWITCH R --> SWITCH A
|
%% STORE R A, SWITCH R --> SWITCH A
|
||||||
r_inline_switch_target({i, Ann, {'STORE', R, A}}, [{switch, R, Type, Alts, Def} | Code]) ->
|
optimize_inline_switch_target({i, Ann, {'STORE', R, A}}, [{switch, R, Type, Alts, Def} | Code]) ->
|
||||||
Ann1 =
|
Ann1 =
|
||||||
case is_reg(A) of
|
case is_reg(A) of
|
||||||
true -> Ann#{ live_out := ordsets:add_element(A, maps:get(live_out, Ann)) };
|
true -> Ann#{ live_out := ordsets:add_element(A, maps:get(live_out, Ann)) };
|
||||||
@@ -1400,18 +1557,18 @@ r_inline_switch_target({i, Ann, {'STORE', R, A}}, [{switch, R, Type, Alts, Def}
|
|||||||
end;
|
end;
|
||||||
_ -> false %% impossible
|
_ -> false %% impossible
|
||||||
end;
|
end;
|
||||||
r_inline_switch_target(_, _) -> false.
|
optimize_inline_switch_target(_, _) -> false.
|
||||||
|
|
||||||
%% Float switch-body to closest switch
|
%% Float switch-body to closest switch
|
||||||
r_float_switch_body(I = {i, _, _}, [J = {i, _, switch_body} | Code]) ->
|
optimize_float_switch_body(I = {i, _, _}, [J = {i, _, switch_body} | Code]) ->
|
||||||
{J1, I1} = swap_instrs(I, J),
|
{J1, I1} = swap_instrs(I, J),
|
||||||
{[], [J1, I1 | Code]};
|
{[], [J1, I1 | Code]};
|
||||||
r_float_switch_body(_, _) -> false.
|
optimize_float_switch_body(_, _) -> false.
|
||||||
|
|
||||||
%% Inline stores
|
%% Inline stores
|
||||||
r_inline_store({i, _, {'STORE', R, R}}, Code) ->
|
optimize_inline_store({i, _, {'STORE', R, R}}, Code) ->
|
||||||
{[], Code};
|
{[], Code};
|
||||||
r_inline_store(I = {i, _, {'STORE', R = {var, _}, A}}, Code) ->
|
optimize_inline_store(I = {i, _, {'STORE', R = {var, _}, A}}, Code) ->
|
||||||
%% Not when A is var unless updating the annotations properly.
|
%% Not when A is var unless updating the annotations properly.
|
||||||
Inline = case A of
|
Inline = case A of
|
||||||
{arg, _} -> true;
|
{arg, _} -> true;
|
||||||
@@ -1419,13 +1576,13 @@ r_inline_store(I = {i, _, {'STORE', R = {var, _}, A}}, Code) ->
|
|||||||
{store, _} -> true;
|
{store, _} -> true;
|
||||||
_ -> false
|
_ -> false
|
||||||
end,
|
end,
|
||||||
if Inline -> r_inline_store([I], false, R, A, Code);
|
if Inline -> optimize_inline_store([I], false, R, A, Code);
|
||||||
true -> false end;
|
true -> false end;
|
||||||
r_inline_store(_, _) -> false.
|
optimize_inline_store(_, _) -> false.
|
||||||
|
|
||||||
r_inline_store(Acc, Progress, R, A, [I = {i, _, switch_body} | Code]) ->
|
optimize_inline_store(Acc, Progress, R, A, [I = {i, _, switch_body} | Code]) ->
|
||||||
r_inline_store([I | Acc], Progress, R, A, Code);
|
optimize_inline_store([I | Acc], Progress, R, A, Code);
|
||||||
r_inline_store(Acc, Progress, R, A, [{i, Ann, I} | Code]) ->
|
optimize_inline_store(Acc, Progress, R, A, [{i, Ann, I} | Code]) ->
|
||||||
#{ write := W } = attributes(I),
|
#{ write := W } = attributes(I),
|
||||||
Inl = fun(X) when X == R -> A; (X) -> X end,
|
Inl = fun(X) when X == R -> A; (X) -> X end,
|
||||||
case live_in(R, Ann) of
|
case live_in(R, Ann) of
|
||||||
@@ -1445,14 +1602,14 @@ r_inline_store(Acc, Progress, R, A, [{i, Ann, I} | Code]) ->
|
|||||||
case lists:member(W, [R, A]) of
|
case lists:member(W, [R, A]) of
|
||||||
true when Progress1 -> {lists:reverse(Acc1), Code};
|
true when Progress1 -> {lists:reverse(Acc1), Code};
|
||||||
true -> false;
|
true -> false;
|
||||||
false -> r_inline_store(Acc1, Progress1, R, A, Code)
|
false -> optimize_inline_store(Acc1, Progress1, R, A, Code)
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
r_inline_store(Acc, true, _, _, Code) -> {lists:reverse(Acc), Code};
|
optimize_inline_store(Acc, true, _, _, Code) -> {lists:reverse(Acc), Code};
|
||||||
r_inline_store(_, false, _, _, _) -> false.
|
optimize_inline_store(_, false, _, _, _) -> false.
|
||||||
|
|
||||||
%% Shortcut write followed by final read
|
%% Shortcut write followed by final read
|
||||||
r_one_shot_var({i, Ann1, I}, [{i, Ann2, J} | Code]) ->
|
optimize_one_shot_var({i, Ann1, I}, [{i, Ann2, J} | Code]) ->
|
||||||
case op_view(I) of
|
case op_view(I) of
|
||||||
{Op, R = {var, _}, As} ->
|
{Op, R = {var, _}, As} ->
|
||||||
Copy = case J of
|
Copy = case J of
|
||||||
@@ -1466,11 +1623,11 @@ r_one_shot_var({i, Ann1, I}, [{i, Ann2, J} | Code]) ->
|
|||||||
end;
|
end;
|
||||||
_ -> false
|
_ -> false
|
||||||
end;
|
end;
|
||||||
r_one_shot_var(_, _) -> false.
|
optimize_one_shot_var(_, _) -> false.
|
||||||
|
|
||||||
%% Remove writes to dead variables
|
%% Remove writes to dead variables
|
||||||
r_write_to_dead_var({i, _, {'STORE', ?void, ?a}}, _) -> false; %% Avoid looping
|
optimize_write_to_dead_var({i, _, {'STORE', ?void, ?a}}, _) -> false; %% Avoid looping
|
||||||
r_write_to_dead_var({i, Ann, I}, Code) ->
|
optimize_write_to_dead_var({i, Ann, I}, Code) ->
|
||||||
#{ pure := Pure } = attributes(I),
|
#{ pure := Pure } = attributes(I),
|
||||||
case op_view(I) of
|
case op_view(I) of
|
||||||
{_Op, R, As} when R /= ?a, Pure ->
|
{_Op, R, As} when R /= ?a, Pure ->
|
||||||
@@ -1483,9 +1640,10 @@ r_write_to_dead_var({i, Ann, I}, Code) ->
|
|||||||
end;
|
end;
|
||||||
_ -> false
|
_ -> false
|
||||||
end;
|
end;
|
||||||
r_write_to_dead_var(_, _) -> false.
|
optimize_write_to_dead_var(_, _) -> false.
|
||||||
|
|
||||||
op_view({'ABORT', R}) -> {'ABORT', none, [R]};
|
op_view({'ABORT', R}) -> {'ABORT', none, [R]};
|
||||||
|
op_view({'EXIT', R}) -> {'EXIT', none, [R]};
|
||||||
op_view(T) when is_tuple(T) ->
|
op_view(T) when is_tuple(T) ->
|
||||||
[Op, R | As] = tuple_to_list(T),
|
[Op, R | As] = tuple_to_list(T),
|
||||||
CheckReads = fun(Rs, X) -> case [] == Rs -- [dst, src] of true -> X; false -> false end end,
|
CheckReads = fun(Rs, X) -> case [] == Rs -- [dst, src] of true -> X; false -> false end end,
|
||||||
@@ -1552,7 +1710,23 @@ bb(_Name, Code) ->
|
|||||||
Blocks = lists:flatmap(fun split_calls/1, Blocks1),
|
Blocks = lists:flatmap(fun split_calls/1, Blocks1),
|
||||||
Labels = maps:from_list([ {Ref, I} || {I, {Ref, _}} <- with_ixs(Blocks) ]),
|
Labels = maps:from_list([ {Ref, I} || {I, {Ref, _}} <- with_ixs(Blocks) ]),
|
||||||
BBs = [ set_labels(Labels, B) || B <- Blocks ],
|
BBs = [ set_labels(Labels, B) || B <- Blocks ],
|
||||||
maps:from_list(BBs).
|
maps:from_list(dbg_loc_filter(BBs)).
|
||||||
|
|
||||||
|
%% Filter DBG_LOC instructions to keep one instruction per line
|
||||||
|
dbg_loc_filter(BBs) ->
|
||||||
|
dbg_loc_filter(BBs, [], [], sets:new()).
|
||||||
|
|
||||||
|
dbg_loc_filter([], _, AllBlocks, _) ->
|
||||||
|
lists:reverse(AllBlocks);
|
||||||
|
dbg_loc_filter([{I, []} | Rest], AllOps, AllBlocks, DbgLocs) ->
|
||||||
|
dbg_loc_filter(Rest, [], [{I, lists:reverse(AllOps)} | AllBlocks], DbgLocs);
|
||||||
|
dbg_loc_filter([{I, [Op = {'DBG_LOC', _, _} | Ops]} | Rest], AllOps, AllBlocks, DbgLocs) ->
|
||||||
|
case sets:is_element(Op, DbgLocs) of
|
||||||
|
true -> dbg_loc_filter([{I, Ops} | Rest], AllOps, AllBlocks, DbgLocs);
|
||||||
|
false -> dbg_loc_filter([{I, Ops} | Rest], [Op | AllOps], AllBlocks, sets:add_element(Op, DbgLocs))
|
||||||
|
end;
|
||||||
|
dbg_loc_filter([{I, [Op | Ops]} | Rest], AllOps, AllBlocks, DbgLocs) ->
|
||||||
|
dbg_loc_filter([{I, Ops} | Rest], [Op | AllOps], AllBlocks, DbgLocs).
|
||||||
|
|
||||||
%% -- Break up scode into basic blocks --
|
%% -- Break up scode into basic blocks --
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
%%% @end
|
%%% @end
|
||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(aeso_parse_lib).
|
-module(aeso_parse_lib).
|
||||||
|
-vsn("8.0.1").
|
||||||
|
|
||||||
-export([parse/2,
|
-export([parse/2,
|
||||||
return/1, fail/0, fail/1, fail/2, map/2, bind/2,
|
return/1, fail/0, fail/1, fail/2, map/2, bind/2,
|
||||||
@@ -15,7 +16,7 @@
|
|||||||
many/1, many1/1, sep/2, sep1/2,
|
many/1, many1/1, sep/2, sep1/2,
|
||||||
infixl/2, infixr/2]).
|
infixl/2, infixr/2]).
|
||||||
|
|
||||||
-export([current_file/0, set_current_file/1,
|
-export([current_file/0, set_current_file/1, current_dir/0, set_current_dir/1,
|
||||||
current_include_type/0, set_current_include_type/1]).
|
current_include_type/0, set_current_include_type/1]).
|
||||||
|
|
||||||
%% -- Types ------------------------------------------------------------------
|
%% -- Types ------------------------------------------------------------------
|
||||||
@@ -480,6 +481,13 @@ current_file() ->
|
|||||||
set_current_file(File) ->
|
set_current_file(File) ->
|
||||||
put('$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({L, C}) -> {current_file(), L, C};
|
||||||
add_current_file(Pos) -> Pos.
|
add_current_file(Pos) -> Pos.
|
||||||
|
|
||||||
|
|||||||
+84
-34
@@ -3,6 +3,7 @@
|
|||||||
%%% Description :
|
%%% Description :
|
||||||
%%% Created : 1 Mar 2018 by Ulf Norell
|
%%% Created : 1 Mar 2018 by Ulf Norell
|
||||||
-module(aeso_parser).
|
-module(aeso_parser).
|
||||||
|
-vsn("8.0.1").
|
||||||
-compile({no_auto_import,[map_get/2]}).
|
-compile({no_auto_import,[map_get/2]}).
|
||||||
|
|
||||||
-export([string/1,
|
-export([string/1,
|
||||||
@@ -19,6 +20,7 @@
|
|||||||
|
|
||||||
-include("aeso_parse_lib.hrl").
|
-include("aeso_parse_lib.hrl").
|
||||||
-import(aeso_parse_lib, [current_file/0, set_current_file/1,
|
-import(aeso_parse_lib, [current_file/0, set_current_file/1,
|
||||||
|
current_dir/0, set_current_dir/1,
|
||||||
current_include_type/0, set_current_include_type/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 parse_result() :: aeso_syntax:ast() | {aeso_syntax:ast(), sets:set(include_hash())} | none().
|
||||||
@@ -58,6 +60,7 @@ run_parser(P, Inp, Opts) ->
|
|||||||
|
|
||||||
parse_and_scan(P, S, Opts) ->
|
parse_and_scan(P, S, Opts) ->
|
||||||
set_current_file(proplists:get_value(src_file, Opts, no_file)),
|
set_current_file(proplists:get_value(src_file, Opts, no_file)),
|
||||||
|
set_current_dir(proplists:get_value(src_dir, Opts, no_file)),
|
||||||
set_current_include_type(proplists:get_value(include_type, Opts, none)),
|
set_current_include_type(proplists:get_value(include_type, Opts, none)),
|
||||||
case aeso_scan:scan(S) of
|
case aeso_scan:scan(S) of
|
||||||
{ok, Tokens} -> aeso_parse_lib:parse(P, Tokens);
|
{ok, Tokens} -> aeso_parse_lib:parse(P, Tokens);
|
||||||
@@ -96,17 +99,29 @@ decl() ->
|
|||||||
choice(
|
choice(
|
||||||
%% Contract declaration
|
%% Contract declaration
|
||||||
[ ?RULE(token(main), keyword(contract),
|
[ ?RULE(token(main), keyword(contract),
|
||||||
con(), tok('='), maybe_block(decl()), {contract_main, _2, _3, _5})
|
con(), tok('='), maybe_block(decl()), {contract_main, _2, _3, [], _5})
|
||||||
|
, ?RULE(token(main), keyword(contract),
|
||||||
|
con(), tok(':'), comma_sep(con()), tok('='), maybe_block(decl()), {contract_main, _2, _3, _5, _7})
|
||||||
, ?RULE(keyword(contract),
|
, ?RULE(keyword(contract),
|
||||||
con(), tok('='), maybe_block(decl()), {contract_child, _1, _2, _4})
|
con(), tok('='), maybe_block(decl()), {contract_child, _1, _2, [], _4})
|
||||||
|
, ?RULE(keyword(contract),
|
||||||
|
con(), tok(':'), comma_sep(con()), tok('='), maybe_block(decl()), {contract_child, _1, _2, _4, _6})
|
||||||
, ?RULE(keyword(contract), token(interface),
|
, ?RULE(keyword(contract), token(interface),
|
||||||
con(), tok('='), maybe_block(decl()), {contract_interface, _1, _3, _5})
|
con(), tok('='), maybe_block(decl()), {contract_interface, _1, _3, [], _5})
|
||||||
|
, ?RULE(keyword(contract), token(interface),
|
||||||
|
con(), tok(':'), comma_sep(con()), tok('='), maybe_block(decl()), {contract_interface, _1, _3, _5, _7})
|
||||||
, ?RULE(token(payable), token(main), keyword(contract),
|
, ?RULE(token(payable), token(main), keyword(contract),
|
||||||
con(), tok('='), maybe_block(decl()), add_modifiers([_1], {contract_main, _3, _4, _6}))
|
con(), tok('='), maybe_block(decl()), add_modifiers([_1], {contract_main, _3, _4, [], _6}))
|
||||||
|
, ?RULE(token(payable), token(main), keyword(contract),
|
||||||
|
con(), tok(':'), comma_sep(con()), tok('='), maybe_block(decl()), add_modifiers([_1], {contract_main, _3, _4, _6, _8}))
|
||||||
, ?RULE(token(payable), keyword(contract),
|
, ?RULE(token(payable), keyword(contract),
|
||||||
con(), tok('='), maybe_block(decl()), add_modifiers([_1], {contract_child, _2, _3, _5}))
|
con(), tok('='), maybe_block(decl()), add_modifiers([_1], {contract_child, _2, _3, [], _5}))
|
||||||
|
, ?RULE(token(payable), keyword(contract),
|
||||||
|
con(), tok(':'), comma_sep(con()), tok('='), maybe_block(decl()), add_modifiers([_1], {contract_child, _2, _3, _5, _7}))
|
||||||
, ?RULE(token(payable), keyword(contract), token(interface),
|
, ?RULE(token(payable), keyword(contract), token(interface),
|
||||||
con(), tok('='), maybe_block(decl()), add_modifiers([_1], {contract_interface, _2, _4, _6}))
|
con(), tok('='), maybe_block(decl()), add_modifiers([_1], {contract_interface, _2, _4, [], _6}))
|
||||||
|
, ?RULE(token(payable), keyword(contract), token(interface),
|
||||||
|
con(), tok(':'), comma_sep(con()), tok('='), maybe_block(decl()), add_modifiers([_1], {contract_interface, _2, _4, _6, _8}))
|
||||||
|
|
||||||
|
|
||||||
, ?RULE(keyword(namespace), con(), tok('='), maybe_block(decl()), {namespace, _1, _2, _4})
|
, ?RULE(keyword(namespace), con(), tok('='), maybe_block(decl()), {namespace, _1, _2, _4})
|
||||||
@@ -252,10 +267,11 @@ type300() ->
|
|||||||
type400() ->
|
type400() ->
|
||||||
choice(
|
choice(
|
||||||
[?RULE(typeAtom(), optional(type_args()),
|
[?RULE(typeAtom(), optional(type_args()),
|
||||||
case _2 of
|
any_bytes(
|
||||||
none -> _1;
|
case _2 of
|
||||||
{ok, Args} -> {app_t, get_ann(_1), _1, Args}
|
none -> _1;
|
||||||
end),
|
{ok, Args} -> {app_t, get_ann(_1), _1, Args}
|
||||||
|
end)),
|
||||||
?RULE(id("bytes"), parens(token(int)),
|
?RULE(id("bytes"), parens(token(int)),
|
||||||
{bytes_t, get_ann(_1), element(3, _2)})
|
{bytes_t, get_ann(_1), element(3, _2)})
|
||||||
]).
|
]).
|
||||||
@@ -287,7 +303,7 @@ stmt() ->
|
|||||||
, {switch, keyword(switch), parens(expr()), maybe_block(branch())}
|
, {switch, keyword(switch), parens(expr()), maybe_block(branch())}
|
||||||
, {'if', keyword('if'), parens(expr()), body()}
|
, {'if', keyword('if'), parens(expr()), body()}
|
||||||
, {elif, keyword(elif), parens(expr()), body()}
|
, {elif, keyword(elif), parens(expr()), body()}
|
||||||
, {else, keyword(else), body()}
|
, {'else', keyword('else'), body()}
|
||||||
])).
|
])).
|
||||||
|
|
||||||
branch() ->
|
branch() ->
|
||||||
@@ -311,7 +327,7 @@ expr100() ->
|
|||||||
Expr150 = ?LAZY_P(expr150()),
|
Expr150 = ?LAZY_P(expr150()),
|
||||||
choice(
|
choice(
|
||||||
[ ?RULE(lam_args(), keyword('=>'), body(), {lam, _2, _1, _3}) %% TODO: better location
|
[ ?RULE(lam_args(), keyword('=>'), body(), {lam, _2, _1, _3}) %% TODO: better location
|
||||||
, {'if', keyword('if'), parens(Expr100), Expr150, right(tok(else), Expr100)}
|
, {'if', keyword('if'), parens(Expr100), Expr150, right(tok('else'), Expr100)}
|
||||||
, ?RULE(Expr150, optional(right(tok(':'), type())),
|
, ?RULE(Expr150, optional(right(tok(':'), type())),
|
||||||
case _2 of
|
case _2 of
|
||||||
none -> _1;
|
none -> _1;
|
||||||
@@ -321,14 +337,19 @@ expr100() ->
|
|||||||
|
|
||||||
expr150() -> infixl(expr200(), binop('|>')).
|
expr150() -> infixl(expr200(), binop('|>')).
|
||||||
expr200() -> infixr(expr300(), binop('||')).
|
expr200() -> infixr(expr300(), binop('||')).
|
||||||
expr300() -> infixr(expr400(), binop('&&')).
|
expr300() -> infixr(expr325(), binop('&&')).
|
||||||
|
expr325() -> infixl(expr350(), binop('bor')).
|
||||||
|
expr350() -> infixl(expr375(), binop('bxor')).
|
||||||
|
expr375() -> infixl(expr400(), binop('band')).
|
||||||
expr400() -> infix(expr500(), binop(['<', '>', '=<', '>=', '==', '!='])).
|
expr400() -> infix(expr500(), binop(['<', '>', '=<', '>=', '==', '!='])).
|
||||||
expr500() -> infixr(expr600(), binop(['::', '++'])).
|
expr500() -> infixr(expr550(), binop(['::', '++'])).
|
||||||
|
expr550() -> infixl(expr600(), binop(['<<', '>>'])).
|
||||||
expr600() -> infixl(expr650(), binop(['+', '-'])).
|
expr600() -> infixl(expr650(), binop(['+', '-'])).
|
||||||
expr650() -> ?RULE(many(token('-')), expr700(), prefixes(_1, _2)).
|
expr650() -> ?RULE(many(token('-')), expr700(), prefixes(_1, _2)).
|
||||||
expr700() -> infixl(expr750(), binop(['*', '/', mod])).
|
expr700() -> infixl(expr750(), binop(['*', '/', mod])).
|
||||||
expr750() -> infixl(expr800(), binop(['^'])).
|
expr750() -> infixl(expr800(), binop(['^'])).
|
||||||
expr800() -> ?RULE(many(token('!')), expr900(), prefixes(_1, _2)).
|
expr800() -> ?RULE(many(token('!')), expr850(), prefixes(_1, _2)).
|
||||||
|
expr850() -> ?RULE(many(token('bnot')), expr900(), prefixes(_1, _2)).
|
||||||
expr900() -> ?RULE(exprAtom(), many(elim()), elim(_1, _2)).
|
expr900() -> ?RULE(exprAtom(), many(elim()), elim(_1, _2)).
|
||||||
|
|
||||||
exprAtom() ->
|
exprAtom() ->
|
||||||
@@ -347,9 +368,12 @@ exprAtom() ->
|
|||||||
, ?RULE(tok('['), Expr, binop('..'), Expr, tok(']'), _3(_2, _4))
|
, ?RULE(tok('['), Expr, binop('..'), Expr, tok(']'), _3(_2, _4))
|
||||||
, ?RULE(keyword('('), comma_sep(Expr), tok(')'), tuple_e(_1, _2))
|
, ?RULE(keyword('('), comma_sep(Expr), tok(')'), tuple_e(_1, _2))
|
||||||
, letpat()
|
, letpat()
|
||||||
|
, hole()
|
||||||
])
|
])
|
||||||
end).
|
end).
|
||||||
|
|
||||||
|
hole() -> ?RULE(token('???'), {id, get_ann(_1), "???"}).
|
||||||
|
|
||||||
comprehension_exp() ->
|
comprehension_exp() ->
|
||||||
?LAZY_P(choice(
|
?LAZY_P(choice(
|
||||||
[ comprehension_bind()
|
[ comprehension_bind()
|
||||||
@@ -502,7 +526,7 @@ id_or_addr() ->
|
|||||||
?RULE(id(), parse_addr_literal(_1)).
|
?RULE(id(), parse_addr_literal(_1)).
|
||||||
|
|
||||||
parse_addr_literal(Id = {id, Ann, Name}) ->
|
parse_addr_literal(Id = {id, Ann, Name}) ->
|
||||||
case lists:member(lists:sublist(Name, 3), ["ak_", "ok_", "oq_", "ct_"]) of
|
case lists:member(lists:sublist(Name, 3), ["ak_", "ok_", "oq_", "ct_", "sg_"]) of
|
||||||
false -> Id;
|
false -> Id;
|
||||||
true ->
|
true ->
|
||||||
try aeser_api_encoder:decode(list_to_binary(Name)) of
|
try aeser_api_encoder:decode(list_to_binary(Name)) of
|
||||||
@@ -541,6 +565,7 @@ bracket_list(P) -> brackets(comma_sep(P)).
|
|||||||
-spec pos_ann(ann_line(), ann_col()) -> ann().
|
-spec pos_ann(ann_line(), ann_col()) -> ann().
|
||||||
pos_ann(Line, Col) ->
|
pos_ann(Line, Col) ->
|
||||||
[ {file, current_file()}
|
[ {file, current_file()}
|
||||||
|
, {dir, current_dir()}
|
||||||
, {include_type, current_include_type()}
|
, {include_type, current_include_type()}
|
||||||
, {line, Line}
|
, {line, Line}
|
||||||
, {col, Col} ].
|
, {col, Col} ].
|
||||||
@@ -588,7 +613,7 @@ group_ifs([], Acc) ->
|
|||||||
group_ifs([{'if', Ann, Cond, Then} | Stmts], Acc) ->
|
group_ifs([{'if', Ann, Cond, Then} | Stmts], Acc) ->
|
||||||
{Elses, Rest} = else_branches(Stmts, []),
|
{Elses, Rest} = else_branches(Stmts, []),
|
||||||
group_ifs(Rest, [build_if(Ann, Cond, Then, Elses) | Acc]);
|
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'"});
|
fail({Ann, "No matching 'if' for 'else'"});
|
||||||
group_ifs([{elif, Ann, _, _} | _], _) ->
|
group_ifs([{elif, Ann, _, _} | _], _) ->
|
||||||
fail({Ann, "No matching 'if' for 'elif'"});
|
fail({Ann, "No matching 'if' for 'elif'"});
|
||||||
@@ -598,14 +623,14 @@ group_ifs([Stmt | Stmts], Acc) ->
|
|||||||
build_if(Ann, Cond, Then, [{elif, Ann1, Cond1, Then1} | Elses]) ->
|
build_if(Ann, Cond, Then, [{elif, Ann1, Cond1, Then1} | Elses]) ->
|
||||||
{'if', Ann, Cond, Then,
|
{'if', Ann, Cond, Then,
|
||||||
set_ann(format, elif, build_if(Ann1, Cond1, Then1, Elses))};
|
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};
|
{'if', Ann, Cond, Then, Else};
|
||||||
build_if(Ann, Cond, Then, []) ->
|
build_if(Ann, Cond, Then, []) ->
|
||||||
{'if', Ann, Cond, Then, {tuple, [{origin, system}], []}}.
|
{'if', Ann, Cond, Then, {tuple, [{origin, system}], []}}.
|
||||||
|
|
||||||
else_branches([Elif = {elif, _, _, _} | Stmts], Acc) ->
|
else_branches([Elif = {elif, _, _, _} | Stmts], Acc) ->
|
||||||
else_branches(Stmts, [Elif | Acc]);
|
else_branches(Stmts, [Elif | Acc]);
|
||||||
else_branches([Else = {else, _, _} | Stmts], Acc) ->
|
else_branches([Else = {'else', _, _} | Stmts], Acc) ->
|
||||||
{lists:reverse([Else | Acc]), Stmts};
|
{lists:reverse([Else | Acc]), Stmts};
|
||||||
else_branches(Stmts, Acc) ->
|
else_branches(Stmts, Acc) ->
|
||||||
{lists:reverse(Acc), Stmts}.
|
{lists:reverse(Acc), Stmts}.
|
||||||
@@ -681,7 +706,7 @@ expand_includes([], Included, Acc, Opts) ->
|
|||||||
end;
|
end;
|
||||||
expand_includes([{include, Ann, {string, _SAnn, File}} | AST], Included, Acc, Opts) ->
|
expand_includes([{include, Ann, {string, _SAnn, File}} | AST], Included, Acc, Opts) ->
|
||||||
case get_include_code(File, Ann, Opts) of
|
case get_include_code(File, Ann, Opts) of
|
||||||
{ok, Code} ->
|
{ok, AbsDir, Code} ->
|
||||||
Hashed = hash_include(File, Code),
|
Hashed = hash_include(File, Code),
|
||||||
case sets:is_element(Hashed, Included) of
|
case sets:is_element(Hashed, Included) of
|
||||||
false ->
|
false ->
|
||||||
@@ -691,9 +716,10 @@ expand_includes([{include, Ann, {string, _SAnn, File}} | AST], Included, Acc, Op
|
|||||||
_ -> indirect
|
_ -> indirect
|
||||||
end,
|
end,
|
||||||
Opts1 = lists:keystore(src_file, 1, Opts, {src_file, File}),
|
Opts1 = lists:keystore(src_file, 1, Opts, {src_file, File}),
|
||||||
Opts2 = lists:keystore(include_type, 1, Opts1, {include_type, IncludeType}),
|
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),
|
Included1 = sets:add_element(Hashed, Included),
|
||||||
case parse_and_scan(file(), Code, Opts2) of
|
case parse_and_scan(file(), Code, Opts3) of
|
||||||
{ok, AST1} ->
|
{ok, AST1} ->
|
||||||
expand_includes(AST1 ++ AST, Included1, Acc, Opts);
|
expand_includes(AST1 ++ AST, Included1, Acc, Opts);
|
||||||
Err = {error, _} ->
|
Err = {error, _} ->
|
||||||
@@ -711,13 +737,12 @@ expand_includes([E | AST], Included, Acc, Opts) ->
|
|||||||
read_file(File, Opts) ->
|
read_file(File, Opts) ->
|
||||||
case proplists:get_value(include, Opts, {explicit_files, #{}}) of
|
case proplists:get_value(include, Opts, {explicit_files, #{}}) of
|
||||||
{file_system, Paths} ->
|
{file_system, Paths} ->
|
||||||
CandidateNames = [ filename:join(Dir, File) || Dir <- Paths ],
|
lists:foldr(fun(Path, {error, _}) -> read_file_(Path, File);
|
||||||
lists:foldr(fun(F, {error, _}) -> file:read_file(F);
|
(_Path, OK) -> OK end, {error, not_found}, Paths);
|
||||||
(_F, OK) -> OK end, {error, not_found}, CandidateNames);
|
|
||||||
{explicit_files, Files} ->
|
{explicit_files, Files} ->
|
||||||
case maps:get(binary_to_list(File), Files, not_found) of
|
case maps:get(binary_to_list(File), Files, not_found) of
|
||||||
not_found -> {error, not_found};
|
not_found -> {error, not_found};
|
||||||
Src -> {ok, Src}
|
Src -> {ok, File, Src}
|
||||||
end;
|
end;
|
||||||
escript ->
|
escript ->
|
||||||
try
|
try
|
||||||
@@ -726,7 +751,7 @@ read_file(File, Opts) ->
|
|||||||
Archive = proplists:get_value(archive, Sections),
|
Archive = proplists:get_value(archive, Sections),
|
||||||
FileName = binary_to_list(filename:join([aesophia, priv, stdlib, File])),
|
FileName = binary_to_list(filename:join([aesophia, priv, stdlib, File])),
|
||||||
case zip:extract(Archive, [{file_list, [FileName]}, memory]) of
|
case zip:extract(Archive, [{file_list, [FileName]}, memory]) of
|
||||||
{ok, [{_, Src}]} -> {ok, Src};
|
{ok, [{_, Src}]} -> {ok, escript, Src};
|
||||||
_ -> {error, not_found}
|
_ -> {error, not_found}
|
||||||
end
|
end
|
||||||
catch _:_ ->
|
catch _:_ ->
|
||||||
@@ -734,6 +759,13 @@ read_file(File, Opts) ->
|
|||||||
end
|
end
|
||||||
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() ->
|
stdlib_options() ->
|
||||||
StdLibDir = aeso_stdlib:stdlib_include_path(),
|
StdLibDir = aeso_stdlib:stdlib_include_path(),
|
||||||
case filelib:is_dir(StdLibDir) of
|
case filelib:is_dir(StdLibDir) of
|
||||||
@@ -742,23 +774,37 @@ stdlib_options() ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
get_include_code(File, Ann, Opts) ->
|
get_include_code(File, Ann, Opts) ->
|
||||||
case {read_file(File, Opts), read_file(File, stdlib_options())} of
|
%% Temporarily extend include paths with the directory of the current file
|
||||||
{{ok, Bin}, {ok, _}} ->
|
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
|
case filename:basename(File) == File of
|
||||||
true -> { error
|
true -> { error
|
||||||
, fail( ann_pos(Ann)
|
, fail( ann_pos(Ann)
|
||||||
, "Illegal redefinition of standard library " ++ binary_to_list(File))};
|
, "Illegal redefinition of standard library " ++ binary_to_list(File))};
|
||||||
%% If a path is provided then the stdlib takes lower priority
|
%% If a path is provided then the stdlib takes lower priority
|
||||||
false -> {ok, binary_to_list(Bin)}
|
false -> {ok, Dir, binary_to_list(Bin)}
|
||||||
end;
|
end;
|
||||||
{_, {ok, Bin}} ->
|
{_, {ok, _, Bin}} ->
|
||||||
{ok, binary_to_list(Bin)};
|
{ok, stdlib, binary_to_list(Bin)};
|
||||||
{{ok, Bin}, _} ->
|
{{ok, Dir, Bin}, _} ->
|
||||||
{ok, binary_to_list(Bin)};
|
{ok, Dir, binary_to_list(Bin)};
|
||||||
{_, _} ->
|
{_, _} ->
|
||||||
{error, {ann_pos(Ann), include_error, File}}
|
{error, {ann_pos(Ann), include_error, File}}
|
||||||
end.
|
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().
|
-spec hash_include(string() | binary(), string()) -> include_hash().
|
||||||
hash_include(File, Code) when is_binary(File) ->
|
hash_include(File, Code) when is_binary(File) ->
|
||||||
hash_include(binary_to_list(File), Code);
|
hash_include(binary_to_list(File), Code);
|
||||||
@@ -772,3 +818,7 @@ auto_imports(L) when is_list(L) ->
|
|||||||
auto_imports(T) when is_tuple(T) ->
|
auto_imports(T) when is_tuple(T) ->
|
||||||
auto_imports(tuple_to_list(T));
|
auto_imports(tuple_to_list(T));
|
||||||
auto_imports(_) -> [].
|
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.
|
||||||
|
|||||||
+30
-11
@@ -6,6 +6,7 @@
|
|||||||
%%% @end
|
%%% @end
|
||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(aeso_pretty).
|
-module(aeso_pretty).
|
||||||
|
-vsn("8.0.1").
|
||||||
|
|
||||||
-import(prettypr, [text/1, sep/1, above/2, beside/2, nest/2, empty/0]).
|
-import(prettypr, [text/1, sep/1, above/2, beside/2, nest/2, empty/0]).
|
||||||
|
|
||||||
@@ -151,12 +152,16 @@ decl(D, Options) ->
|
|||||||
with_options(Options, fun() -> decl(D) end).
|
with_options(Options, fun() -> decl(D) end).
|
||||||
|
|
||||||
-spec decl(aeso_syntax:decl()) -> doc().
|
-spec decl(aeso_syntax:decl()) -> doc().
|
||||||
decl({Con, Attrs, C, Ds}) when ?IS_CONTRACT_HEAD(Con) ->
|
decl({Con, Attrs, C, Is, Ds}) when ?IS_CONTRACT_HEAD(Con) ->
|
||||||
Mod = fun({Mod, true}) when Mod == payable ->
|
Mod = fun({Mod, true}) when Mod == payable ->
|
||||||
text(atom_to_list(Mod));
|
text(atom_to_list(Mod));
|
||||||
(_) -> empty() end,
|
(_) -> empty() end,
|
||||||
|
ImplsList = case Is of
|
||||||
|
[] -> [empty()];
|
||||||
|
_ -> [text(":"), par(punctuate(text(","), lists:map(fun name/1, Is)), 0)]
|
||||||
|
end,
|
||||||
block(follow( hsep(lists:map(Mod, Attrs) ++ [contract_head(Con)])
|
block(follow( hsep(lists:map(Mod, Attrs) ++ [contract_head(Con)])
|
||||||
, hsep(name(C), text("="))), decls(Ds));
|
, hsep([name(C)] ++ ImplsList ++ [text("=")])), decls(Ds));
|
||||||
decl({namespace, _, C, Ds}) ->
|
decl({namespace, _, C, Ds}) ->
|
||||||
block(follow(text("namespace"), hsep(name(C), text("="))), decls(Ds));
|
block(follow(text("namespace"), hsep(name(C), text("="))), decls(Ds));
|
||||||
decl({pragma, _, Pragma}) -> pragma(Pragma);
|
decl({pragma, _, Pragma}) -> pragma(Pragma);
|
||||||
@@ -257,6 +262,8 @@ type(Type, Options) ->
|
|||||||
with_options(Options, fun() -> type(Type) end).
|
with_options(Options, fun() -> type(Type) end).
|
||||||
|
|
||||||
-spec type(aeso_syntax:type()) -> doc().
|
-spec type(aeso_syntax:type()) -> doc().
|
||||||
|
type(F = {fun_t, _, _, var_args, _}) ->
|
||||||
|
type(setelement(4, F, [var_args]));
|
||||||
type({fun_t, _, Named, Args, Ret}) ->
|
type({fun_t, _, Named, Args, Ret}) ->
|
||||||
follow(hsep(args_type(Named ++ Args), text("=>")), type(Ret));
|
follow(hsep(args_type(Named ++ Args), text("=>")), type(Ret));
|
||||||
type({type_sig, _, Named, Args, Ret}) ->
|
type({type_sig, _, Named, Args, Ret}) ->
|
||||||
@@ -269,7 +276,9 @@ type({tuple_t, _, Args}) ->
|
|||||||
tuple_type(Args);
|
tuple_type(Args);
|
||||||
type({args_t, _, Args}) ->
|
type({args_t, _, Args}) ->
|
||||||
args_type(Args);
|
args_type(Args);
|
||||||
type({bytes_t, _, any}) -> text("bytes(_)");
|
type({bytes_t, _, any}) -> text("bytes()");
|
||||||
|
type({bytes_t, _, '_'}) -> text("bytes(_)");
|
||||||
|
type({bytes_t, _, fixed}) -> text("bytes(_)");
|
||||||
type({bytes_t, _, Len}) ->
|
type({bytes_t, _, Len}) ->
|
||||||
text(lists:concat(["bytes(", Len, ")"]));
|
text(lists:concat(["bytes(", Len, ")"]));
|
||||||
type({if_t, _, Id, Then, Else}) ->
|
type({if_t, _, Id, Then, Else}) ->
|
||||||
@@ -284,7 +293,9 @@ type(T = {id, _, _}) -> name(T);
|
|||||||
type(T = {qid, _, _}) -> name(T);
|
type(T = {qid, _, _}) -> name(T);
|
||||||
type(T = {con, _, _}) -> name(T);
|
type(T = {con, _, _}) -> name(T);
|
||||||
type(T = {qcon, _, _}) -> name(T);
|
type(T = {qcon, _, _}) -> name(T);
|
||||||
type(T = {tvar, _, _}) -> name(T).
|
type(T = {tvar, _, _}) -> name(T);
|
||||||
|
|
||||||
|
type(var_args) -> text("var_args").
|
||||||
|
|
||||||
-spec args_type([aeso_syntax:type()]) -> doc().
|
-spec args_type([aeso_syntax:type()]) -> doc().
|
||||||
args_type(Args) ->
|
args_type(Args) ->
|
||||||
@@ -377,7 +388,8 @@ expr_p(_, {Type, _, Bin})
|
|||||||
when Type == account_pubkey;
|
when Type == account_pubkey;
|
||||||
Type == contract_pubkey;
|
Type == contract_pubkey;
|
||||||
Type == oracle_pubkey;
|
Type == oracle_pubkey;
|
||||||
Type == oracle_query_id ->
|
Type == oracle_query_id;
|
||||||
|
Type == signature ->
|
||||||
text(binary_to_list(aeser_api_encoder:encode(Type, Bin)));
|
text(binary_to_list(aeser_api_encoder:encode(Type, Bin)));
|
||||||
expr_p(_, {string, _, <<>>}) -> text("\"\"");
|
expr_p(_, {string, _, <<>>}) -> text("\"\"");
|
||||||
expr_p(_, {string, _, S}) ->
|
expr_p(_, {string, _, S}) ->
|
||||||
@@ -408,7 +420,7 @@ stmt_p({'if', _, Cond, Then}) ->
|
|||||||
block_expr(200, beside(text("if"), paren(expr(Cond))), Then);
|
block_expr(200, beside(text("if"), paren(expr(Cond))), Then);
|
||||||
stmt_p({elif, _, Cond, Then}) ->
|
stmt_p({elif, _, Cond, Then}) ->
|
||||||
block_expr(200, beside(text("elif"), paren(expr(Cond))), Then);
|
block_expr(200, beside(text("elif"), paren(expr(Cond))), Then);
|
||||||
stmt_p({else, Else}) ->
|
stmt_p({'else', Else}) ->
|
||||||
HideGenerated = not show_generated(),
|
HideGenerated = not show_generated(),
|
||||||
case aeso_syntax:get_ann(origin, Else) of
|
case aeso_syntax:get_ann(origin, Else) of
|
||||||
system when HideGenerated -> empty();
|
system when HideGenerated -> empty();
|
||||||
@@ -426,16 +438,22 @@ lc_bind(Let) ->
|
|||||||
bin_prec('..') -> { 0, 0, 0}; %% Always printed inside '[ ]'
|
bin_prec('..') -> { 0, 0, 0}; %% Always printed inside '[ ]'
|
||||||
bin_prec('=') -> { 0, 0, 0}; %% Always printed inside '[ ]'
|
bin_prec('=') -> { 0, 0, 0}; %% Always printed inside '[ ]'
|
||||||
bin_prec('@') -> { 0, 0, 0}; %% Only in error messages
|
bin_prec('@') -> { 0, 0, 0}; %% Only in error messages
|
||||||
|
bin_prec('|>') -> {150, 150, 200};
|
||||||
bin_prec('||') -> {200, 300, 200};
|
bin_prec('||') -> {200, 300, 200};
|
||||||
bin_prec('&&') -> {300, 400, 300};
|
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('<') -> {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('>=') -> {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, 600, 500};
|
bin_prec('++') -> {500, 550, 500};
|
||||||
bin_prec('::') -> {500, 600, 500};
|
bin_prec('::') -> {500, 550, 500};
|
||||||
|
bin_prec('<<') -> {550, 600, 550};
|
||||||
|
bin_prec('>>') -> {550, 600, 550};
|
||||||
bin_prec('+') -> {600, 600, 650};
|
bin_prec('+') -> {600, 600, 650};
|
||||||
bin_prec('-') -> {600, 600, 650};
|
bin_prec('-') -> {600, 600, 650};
|
||||||
bin_prec('*') -> {700, 700, 750};
|
bin_prec('*') -> {700, 700, 750};
|
||||||
@@ -445,7 +463,8 @@ bin_prec('^') -> {750, 750, 800}.
|
|||||||
|
|
||||||
-spec un_prec(aeso_syntax:un_op()) -> {integer(), integer()}.
|
-spec un_prec(aeso_syntax:un_op()) -> {integer(), integer()}.
|
||||||
un_prec('-') -> {650, 650};
|
un_prec('-') -> {650, 650};
|
||||||
un_prec('!') -> {800, 800}.
|
un_prec('!') -> {800, 800};
|
||||||
|
un_prec('bnot') -> {850, 850}.
|
||||||
|
|
||||||
equals(Ann, A, B) ->
|
equals(Ann, A, B) ->
|
||||||
{app, [{format, infix} | Ann], {'=', Ann}, [A, B]}.
|
{app, [{format, infix} | Ann], {'=', Ann}, [A, B]}.
|
||||||
@@ -516,5 +535,5 @@ get_elifs(If = {'if', Ann, Cond, Then, Else}, Elifs) ->
|
|||||||
elif -> get_elifs(Else, [{elif, Ann, Cond, Then} | Elifs]);
|
elif -> get_elifs(Else, [{elif, Ann, Cond, Then} | Elifs]);
|
||||||
_ -> {lists:reverse(Elifs), If}
|
_ -> {lists:reverse(Elifs), If}
|
||||||
end;
|
end;
|
||||||
get_elifs(Else, Elifs) -> {lists:reverse(Elifs), {else, Else}}.
|
get_elifs(Else, Elifs) -> {lists:reverse(Elifs), {'else', Else}}.
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -6,6 +6,7 @@
|
|||||||
%%% @end
|
%%% @end
|
||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(aeso_scan).
|
-module(aeso_scan).
|
||||||
|
-vsn("8.0.1").
|
||||||
|
|
||||||
-export([scan/1, utf8_encode/1]).
|
-export([scan/1, utf8_encode/1]).
|
||||||
|
|
||||||
@@ -45,7 +46,7 @@ lexer() ->
|
|||||||
|
|
||||||
Keywords = ["contract", "include", "let", "switch", "type", "record", "datatype", "if", "elif", "else", "function",
|
Keywords = ["contract", "include", "let", "switch", "type", "record", "datatype", "if", "elif", "else", "function",
|
||||||
"stateful", "payable", "true", "false", "mod", "public", "entrypoint", "private", "indexed", "namespace",
|
"stateful", "payable", "true", "false", "mod", "public", "entrypoint", "private", "indexed", "namespace",
|
||||||
"interface", "main", "using", "as", "for", "hiding"
|
"interface", "main", "using", "as", "for", "hiding", "band", "bor", "bxor", "bnot"
|
||||||
],
|
],
|
||||||
KW = string:join(Keywords, "|"),
|
KW = string:join(Keywords, "|"),
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
%%% @end
|
%%% @end
|
||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(aeso_scan_lib).
|
-module(aeso_scan_lib).
|
||||||
|
-vsn("8.0.1").
|
||||||
|
|
||||||
-export([compile/1, string/3,
|
-export([compile/1, string/3,
|
||||||
token/1, token/2, symbol/0, skip/0,
|
token/1, token/2, symbol/0, skip/0,
|
||||||
|
|||||||
+3
-2
@@ -9,9 +9,10 @@
|
|||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
|
|
||||||
-module(aeso_stdlib).
|
-module(aeso_stdlib).
|
||||||
|
-vsn("8.0.1").
|
||||||
|
|
||||||
-export([stdlib_include_path/0]).
|
-export([stdlib_include_path/0]).
|
||||||
|
|
||||||
stdlib_include_path() ->
|
stdlib_include_path() ->
|
||||||
filename:join([code:priv_dir(aesophia), "stdlib"]).
|
{file, Path} = code:is_loaded(?MODULE),
|
||||||
|
filename:join(filename:dirname(filename:dirname(Path)), "priv/stdlib").
|
||||||
|
|||||||
+13
-9
@@ -7,13 +7,14 @@
|
|||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
|
|
||||||
-module(aeso_syntax).
|
-module(aeso_syntax).
|
||||||
|
-vsn("8.0.1").
|
||||||
|
|
||||||
-export([get_ann/1, get_ann/2, get_ann/3, set_ann/2, qualify/2]).
|
-export([get_ann/1, get_ann/2, get_ann/3, set_ann/2, qualify/2]).
|
||||||
|
|
||||||
-export_type([ann_line/0, ann_col/0, ann_origin/0, ann_format/0, ann/0]).
|
-export_type([ann_file/0, 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([name/0, id/0, con/0, qid/0, qcon/0, tvar/0, op/0]).
|
||||||
-export_type([bin_op/0, un_op/0]).
|
-export_type([bin_op/0, un_op/0]).
|
||||||
-export_type([decl/0, letbind/0, typedef/0, pragma/0]).
|
-export_type([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([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([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]).
|
-export_type([ast/0]).
|
||||||
@@ -24,9 +25,10 @@
|
|||||||
-type ann_col() :: integer().
|
-type ann_col() :: integer().
|
||||||
-type ann_origin() :: system | user.
|
-type ann_origin() :: system | user.
|
||||||
-type ann_format() :: '?:' | hex | infix | prefix | elif.
|
-type ann_format() :: '?:' | hex | infix | prefix | elif.
|
||||||
|
-type ann_file() :: string() | no_file.
|
||||||
|
|
||||||
-type ann() :: [ {line, ann_line()} | {col, ann_col()} | {format, ann_format()} | {origin, ann_origin()}
|
-type ann() :: [ {file, ann_file()} | {line, ann_line()} | {col, ann_col()} | {format, ann_format()} | {origin, ann_origin()}
|
||||||
| stateful | private | payable | main | interface].
|
| stateful | private | payable | main | interface | entrypoint].
|
||||||
|
|
||||||
-type name() :: string().
|
-type name() :: string().
|
||||||
-type id() :: {id, ann(), name()}.
|
-type id() :: {id, ann(), name()}.
|
||||||
@@ -38,10 +40,11 @@
|
|||||||
-type namespace_alias() :: none | con().
|
-type namespace_alias() :: none | con().
|
||||||
-type namespace_parts() :: none | {for, [id()]} | {hiding, [id()]}.
|
-type namespace_parts() :: none | {for, [id()]} | {hiding, [id()]}.
|
||||||
|
|
||||||
-type decl() :: {contract_main, ann(), con(), [decl()]}
|
-type decl() :: {contract_main, ann(), con(), [con()], [decl()]}
|
||||||
| {contract_child, ann(), con(), [decl()]}
|
| {contract_child, ann(), con(), [con()], [decl()]}
|
||||||
| {contract_interface, ann(), con(), [decl()]}
|
| {contract_interface, ann(), con(), [con()], [decl()]}
|
||||||
| {namespace, ann(), con(), [decl()]}
|
| {namespace, ann(), con(), [decl()]}
|
||||||
|
| {include, ann(), {string, ann(), string()}}
|
||||||
| {pragma, ann(), pragma()}
|
| {pragma, ann(), pragma()}
|
||||||
| {type_decl, ann(), id(), [tvar()]} % Only for error msgs
|
| {type_decl, ann(), id(), [tvar()]} % Only for error msgs
|
||||||
| {type_def, ann(), id(), [tvar()], typedef()}
|
| {type_def, ann(), id(), [tvar()], typedef()}
|
||||||
@@ -98,6 +101,7 @@
|
|||||||
| {contract_pubkey, ann(), binary()}
|
| {contract_pubkey, ann(), binary()}
|
||||||
| {oracle_pubkey, ann(), binary()}
|
| {oracle_pubkey, ann(), binary()}
|
||||||
| {oracle_query_id, ann(), binary()}
|
| {oracle_query_id, ann(), binary()}
|
||||||
|
| {signature, ann(), binary()}
|
||||||
| {string, ann(), binary()}
|
| {string, ann(), binary()}
|
||||||
| {char, ann(), integer()}.
|
| {char, ann(), integer()}.
|
||||||
|
|
||||||
@@ -105,8 +109,8 @@
|
|||||||
|
|
||||||
-type bin_op() :: '+' | '-' | '*' | '/' | mod | '^'
|
-type bin_op() :: '+' | '-' | '*' | '/' | mod | '^'
|
||||||
| '++' | '::' | '<' | '>' | '=<' | '>=' | '==' | '!='
|
| '++' | '::' | '<' | '>' | '=<' | '>=' | '==' | '!='
|
||||||
| '||' | '&&' | '..'.
|
| '||' | '&&' | '..' | 'band' | 'bor' | 'bxor' | '>>' | '<<' | '|>'.
|
||||||
-type un_op() :: '-' | '!'.
|
-type un_op() :: '-' | '!' | 'bnot'.
|
||||||
|
|
||||||
-type expr()
|
-type expr()
|
||||||
:: {lam, ann(), [arg()], expr()}
|
:: {lam, ann(), [arg()], expr()}
|
||||||
|
|||||||
@@ -5,8 +5,9 @@
|
|||||||
%%% @end
|
%%% @end
|
||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(aeso_syntax_utils).
|
-module(aeso_syntax_utils).
|
||||||
|
-vsn("8.0.1").
|
||||||
|
|
||||||
-export([used_ids/1, used_types/2, used/1]).
|
-export([used_ids/1, used_ids/2, used_types/2, used/1]).
|
||||||
|
|
||||||
-record(alg, {zero, plus, scoped}).
|
-record(alg, {zero, plus, scoped}).
|
||||||
|
|
||||||
@@ -31,11 +32,13 @@
|
|||||||
| aeso_syntax:field(aeso_syntax:expr())
|
| aeso_syntax:field(aeso_syntax:expr())
|
||||||
| aeso_syntax:stmt().
|
| aeso_syntax:stmt().
|
||||||
fold(Alg = #alg{zero = Zero, plus = Plus, scoped = Scoped}, Fun, K, X) ->
|
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,
|
Sum = fun(Xs) -> lists:foldl(Plus, Zero, Xs) end,
|
||||||
Same = fun(A) -> fold(Alg, Fun, K, A) end,
|
Same = fun(A) -> fold(Alg, Fun, K, A) end,
|
||||||
Decl = fun(D) -> fold(Alg, Fun, decl, D) end,
|
Decl = fun(D) -> fold(Alg, Fun, decl, D) end,
|
||||||
Type = fun(T) -> fold(Alg, Fun, type, T) end,
|
Type = fun(T) -> fold(Alg, Fun, TypeKind, T) end,
|
||||||
Expr = fun(E) -> fold(Alg, Fun, expr, E) end,
|
Expr = fun(E) -> fold(Alg, Fun, ExprKind, E) end,
|
||||||
BindExpr = fun(P) -> fold(Alg, Fun, bind_expr, P) end,
|
BindExpr = fun(P) -> fold(Alg, Fun, bind_expr, P) end,
|
||||||
BindType = fun(T) -> fold(Alg, Fun, bind_type, T) end,
|
BindType = fun(T) -> fold(Alg, Fun, bind_type, T) end,
|
||||||
Top = Fun(K, X),
|
Top = Fun(K, X),
|
||||||
@@ -108,8 +111,16 @@ fold(Alg = #alg{zero = Zero, plus = Plus, scoped = Scoped}, Fun, K, X) ->
|
|||||||
|
|
||||||
%% Name dependencies
|
%% Name dependencies
|
||||||
|
|
||||||
|
%% Used ids, top level
|
||||||
used_ids(E) ->
|
used_ids(E) ->
|
||||||
[ X || {{term, [X]}, _} <- used(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.
|
||||||
|
|
||||||
used_types([Top] = _CurrentNS, T) ->
|
used_types([Top] = _CurrentNS, T) ->
|
||||||
F = fun({{type, [X]}, _}) -> [X];
|
F = fun({{type, [X]}, _}) -> [X];
|
||||||
@@ -155,4 +166,3 @@ used(D) ->
|
|||||||
(_, _) -> #{}
|
(_, _) -> #{}
|
||||||
end, decl, D)),
|
end, decl, D)),
|
||||||
lists:filter(NotBound, Xs).
|
lists:filter(NotBound, Xs).
|
||||||
|
|
||||||
|
|||||||
+14
-1
@@ -5,11 +5,24 @@
|
|||||||
%%% @end
|
%%% @end
|
||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(aeso_utils).
|
-module(aeso_utils).
|
||||||
|
-vsn("8.0.1").
|
||||||
|
|
||||||
-export([scc/1]).
|
-export([scc/1, canonical_dir/1]).
|
||||||
|
|
||||||
-export_type([graph/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
|
%% -- Topological sort
|
||||||
|
|
||||||
-type graph(Node) :: #{Node => [Node]}. %% List of incoming edges (dependencies).
|
-type graph(Node) :: #{Node => [Node]}. %% List of incoming edges (dependencies).
|
||||||
|
|||||||
+25
-2
@@ -5,16 +5,21 @@
|
|||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
|
|
||||||
-module(aeso_vm_decode).
|
-module(aeso_vm_decode).
|
||||||
|
-vsn("8.0.1").
|
||||||
|
|
||||||
-export([ from_fate/2 ]).
|
-export([ from_fate/2 ]).
|
||||||
|
|
||||||
-include_lib("aebytecode/include/aeb_fate_data.hrl").
|
-include("$aebytecode_include/aeb_fate_data.hrl").
|
||||||
|
|
||||||
-spec from_fate(aeso_syntax:type(), aeb_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, _, "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"}, _}, ?FATE_ORACLE(Bin)) -> {oracle_pubkey, [], Bin};
|
||||||
from_fate({app_t, _, {id, _, "oracle_query"}, _}, ?FATE_ORACLE_Q(Bin)) -> {oracle_query_id, [], Bin};
|
from_fate({app_t, _, {id, _, "oracle_query"}, _}, ?FATE_ORACLE_Q(Bin)) -> {oracle_query_id, [], Bin};
|
||||||
from_fate({con, _, _Name}, ?FATE_CONTRACT(Bin)) -> {contract_pubkey, [], Bin};
|
from_fate({con, _, _Name}, ?FATE_CONTRACT(Bin)) -> {contract_pubkey, [], Bin};
|
||||||
|
from_fate({bytes_t, _, 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({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, _, "bits"}, ?FATE_BITS(N)) -> make_bits(N);
|
||||||
from_fate({id, _, "int"}, N) when is_integer(N) ->
|
from_fate({id, _, "int"}, N) when is_integer(N) ->
|
||||||
@@ -78,6 +83,7 @@ from_fate_builtin(QType, Val) ->
|
|||||||
Hsh = {bytes_t, [], 32},
|
Hsh = {bytes_t, [], 32},
|
||||||
I32 = {bytes_t, [], 32},
|
I32 = {bytes_t, [], 32},
|
||||||
I48 = {bytes_t, [], 48},
|
I48 = {bytes_t, [], 48},
|
||||||
|
Bts = {bytes_t, [], any},
|
||||||
Qid = fun(Name) -> {qid, [], Name} end,
|
Qid = fun(Name) -> {qid, [], Name} end,
|
||||||
Map = fun(KT, VT) -> {app_t, [], {id, [], "map"}, [KT, VT]} 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],
|
ChainTxArities = [3, 0, 0, 0, 0, 0, 1, 1, 1, 2, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 0],
|
||||||
@@ -88,7 +94,7 @@ from_fate_builtin(QType, Val) ->
|
|||||||
|
|
||||||
{["AENS", "name"], {variant, [3], 0, {Addr, TTL, Ptrs}}} ->
|
{["AENS", "name"], {variant, [3], 0, {Addr, TTL, Ptrs}}} ->
|
||||||
App(["AENS","Name"], [Chk(Adr, Addr), Chk(Qid(["Chain", "ttl"]), TTL),
|
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}}} ->
|
{["AENS", "pointee"], {variant, [1, 1, 1, 1], 0, {Addr}}} ->
|
||||||
App(["AENS","AccountPt"], [Chk(Adr, Addr)]);
|
App(["AENS","AccountPt"], [Chk(Adr, Addr)]);
|
||||||
@@ -99,6 +105,21 @@ from_fate_builtin(QType, Val) ->
|
|||||||
{["AENS", "pointee"], {variant, [1, 1, 1, 1], 3, {Addr}}} ->
|
{["AENS", "pointee"], {variant, [1, 1, 1, 1], 3, {Addr}}} ->
|
||||||
App(["AENS","ChannelPt"], [Chk(Adr, 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}}} ->
|
{["Chain", "ga_meta_tx"], {variant, [2], 0, {Addr, X}}} ->
|
||||||
App(["Chain","GAMetaTx"], [Chk(Adr, Addr), Chk(Int, X)]);
|
App(["Chain","GAMetaTx"], [Chk(Adr, Addr), Chk(Int, X)]);
|
||||||
|
|
||||||
@@ -170,3 +191,5 @@ make_bits(Set, Zero, I, N) when 0 == N rem 2 ->
|
|||||||
make_bits(Set, Zero, I, N) ->
|
make_bits(Set, Zero, I, N) ->
|
||||||
{app, [], Set, [make_bits(Set, Zero, I + 1, N div 2), {int, [], I}]}.
|
{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,4 +1,5 @@
|
|||||||
-module(aeso_warnings).
|
-module(aeso_warnings).
|
||||||
|
-vsn("8.0.1").
|
||||||
|
|
||||||
-record(warn, { pos :: aeso_errors:pos()
|
-record(warn, { pos :: aeso_errors:pos()
|
||||||
, message :: iolist()
|
, message :: iolist()
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
{application, aesophia,
|
{application, aesophia,
|
||||||
[{description, "Compiler for Aeternity Sophia language"},
|
[{description, "Compiler for Aeternity Sophia language"},
|
||||||
{vsn, "6.1.0"},
|
{vsn, "8.0.1"},
|
||||||
{registered, []},
|
{registered, []},
|
||||||
{applications,
|
{applications,
|
||||||
[kernel,
|
[kernel,
|
||||||
stdlib,
|
stdlib,
|
||||||
jsx,
|
jsx,
|
||||||
syntax_tools,
|
syntax_tools,
|
||||||
getopt,
|
|
||||||
aebytecode,
|
aebytecode,
|
||||||
eblake2
|
eblake2
|
||||||
]},
|
]},
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ test_cases(1) ->
|
|||||||
" payable stateful entrypoint a(i : int) = i+1\n">>,
|
" payable stateful entrypoint a(i : int) = i+1\n">>,
|
||||||
MapACI = #{contract =>
|
MapACI = #{contract =>
|
||||||
#{name => <<"C">>,
|
#{name => <<"C">>,
|
||||||
type_defs => [],
|
typedefs => [],
|
||||||
payable => true,
|
payable => true,
|
||||||
kind => contract_main,
|
kind => contract_main,
|
||||||
functions =>
|
functions =>
|
||||||
@@ -43,7 +43,7 @@ test_cases(2) ->
|
|||||||
MapACI = #{contract =>
|
MapACI = #{contract =>
|
||||||
#{name => <<"C">>, payable => false,
|
#{name => <<"C">>, payable => false,
|
||||||
kind => contract_main,
|
kind => contract_main,
|
||||||
type_defs =>
|
typedefs =>
|
||||||
[#{name => <<"allan">>,
|
[#{name => <<"allan">>,
|
||||||
typedef => <<"int">>,
|
typedef => <<"int">>,
|
||||||
vars => []}],
|
vars => []}],
|
||||||
@@ -76,7 +76,7 @@ test_cases(3) ->
|
|||||||
name => <<"C">>, payable => false, kind => contract_main,
|
name => <<"C">>, payable => false, kind => contract_main,
|
||||||
event => #{variant => [#{<<"SingleEventDefined">> => []}]},
|
event => #{variant => [#{<<"SingleEventDefined">> => []}]},
|
||||||
state => <<"unit">>,
|
state => <<"unit">>,
|
||||||
type_defs =>
|
typedefs =>
|
||||||
[#{name => <<"bert">>,
|
[#{name => <<"bert">>,
|
||||||
typedef =>
|
typedef =>
|
||||||
#{variant =>
|
#{variant =>
|
||||||
@@ -127,7 +127,7 @@ check_stub(Stub, Options) ->
|
|||||||
Ast ->
|
Ast ->
|
||||||
try
|
try
|
||||||
%% io:format("AST: ~120p\n", [Ast]),
|
%% io:format("AST: ~120p\n", [Ast]),
|
||||||
aeso_ast_infer_types:infer(Ast, [])
|
aeso_ast_infer_types:infer(Ast, [no_code])
|
||||||
catch throw:{type_errors, TE} ->
|
catch throw:{type_errors, TE} ->
|
||||||
io:format("Type error:\n~s\n", [TE]),
|
io:format("Type error:\n~s\n", [TE]),
|
||||||
error(TE);
|
error(TE);
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ calldata_aci_test_() ->
|
|||||||
[ {"Testing " ++ ContractName ++ " contract calling " ++ Fun,
|
[ {"Testing " ++ ContractName ++ " contract calling " ++ Fun,
|
||||||
fun() ->
|
fun() ->
|
||||||
ContractString = aeso_test_utils:read_contract(ContractName),
|
ContractString = aeso_test_utils:read_contract(ContractName),
|
||||||
{ok, ContractACIBin} = aeso_aci:contract_interface(string, ContractString),
|
{ok, ContractACIBin} = aeso_aci:contract_interface(string, ContractString, [no_code]),
|
||||||
ContractACI = binary_to_list(ContractACIBin),
|
ContractACI = binary_to_list(ContractACIBin),
|
||||||
io:format("ACI:\n~s\n", [ContractACIBin]),
|
io:format("ACI:\n~s\n", [ContractACIBin]),
|
||||||
FateExprs = ast_exprs(ContractACI, Fun, Args),
|
FateExprs = ast_exprs(ContractACI, Fun, Args),
|
||||||
@@ -39,7 +39,7 @@ calldata_aci_test_() ->
|
|||||||
end} || {ContractName, Fun, Args} <- compilable_contracts()].
|
end} || {ContractName, Fun, Args} <- compilable_contracts()].
|
||||||
|
|
||||||
parse_args(Fun, Args) ->
|
parse_args(Fun, Args) ->
|
||||||
[{contract_main, _, _, [{letfun, _, _, _, _, [{guarded, _, [], {app, _, _, AST}}]}]}] =
|
[{contract_main, _, _, _, [{letfun, _, _, _, _, [{guarded, _, [], {app, _, _, AST}}]}]}] =
|
||||||
aeso_parser:string("main contract Temp = function foo() = " ++ Fun ++ "(" ++ string:join(Args, ", ") ++ ")"),
|
aeso_parser:string("main contract Temp = function foo() = " ++ Fun ++ "(" ++ string:join(Args, ", ") ++ ")"),
|
||||||
strip_ann(AST).
|
strip_ann(AST).
|
||||||
|
|
||||||
@@ -85,6 +85,7 @@ compilable_contracts() ->
|
|||||||
{"funargs", "bitsum", ["Bits.clear(Bits.clear(Bits.all, 4), 2)"]}, %% Order matters for test
|
{"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", "bitsum", ["Bits.set(Bits.set(Bits.none, 4), 2)"]},
|
||||||
{"funargs", "read", ["{label = \"question 1\", result = 4}"]},
|
{"funargs", "read", ["{label = \"question 1\", result = 4}"]},
|
||||||
|
{"funargs", "any_bytes", ["Bytes.to_any_size(#0011AA)"]},
|
||||||
{"funargs", "sjutton", ["#0011012003100011012003100011012003"]},
|
{"funargs", "sjutton", ["#0011012003100011012003100011012003"]},
|
||||||
{"funargs", "sextiosju", ["#01020304050607080910111213141516171819202122232425262728293031323334353637383940"
|
{"funargs", "sextiosju", ["#01020304050607080910111213141516171819202122232425262728293031323334353637383940"
|
||||||
"414243444546474849505152535455565758596061626364656667"]},
|
"414243444546474849505152535455565758596061626364656667"]},
|
||||||
@@ -116,6 +117,7 @@ compilable_contracts() ->
|
|||||||
{"funargs", "chain_base_tx", ["Chain.NameRevokeTx(#ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)"]},
|
{"funargs", "chain_base_tx", ["Chain.NameRevokeTx(#ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)"]},
|
||||||
{"funargs", "chain_base_tx", ["Chain.NameTransferTx(ak_2dATVcZ9KJU5a8hdsVtTv21pYiGWiPbmVcU1Pz72FFqpk9pSRR, #ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)"]},
|
{"funargs", "chain_base_tx", ["Chain.NameTransferTx(ak_2dATVcZ9KJU5a8hdsVtTv21pYiGWiPbmVcU1Pz72FFqpk9pSRR, #ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)"]},
|
||||||
{"funargs", "chain_base_tx", ["Chain.GAAttachTx"]},
|
{"funargs", "chain_base_tx", ["Chain.GAAttachTx"]},
|
||||||
|
{"funargs", "sig", ["sg_MhibzTP1wWzGCTjtPFr1TiPqRJrrJqw7auvEuF5i3FdoALWqXLBDY6xxRRNUSPHK3EQTnTzF12EyspkxrSMxVHKsZeSMj"]},
|
||||||
{"variant_types", "init", []},
|
{"variant_types", "init", []},
|
||||||
{"basic_auth", "init", []},
|
{"basic_auth", "init", []},
|
||||||
{"address_literals", "init", []},
|
{"address_literals", "init", []},
|
||||||
|
|||||||
+508
-88
@@ -45,12 +45,6 @@ simple_compile_test_() ->
|
|||||||
check_errors(ExpectedErrors, Errors)
|
check_errors(ExpectedErrors, Errors)
|
||||||
end} ||
|
end} ||
|
||||||
{ContractName, ExpectedErrors} <- failing_contracts() ] ++
|
{ContractName, ExpectedErrors} <- failing_contracts() ] ++
|
||||||
[ {"Testing code generation error messages of " ++ ContractName,
|
|
||||||
fun() ->
|
|
||||||
Errors = compile(ContractName),
|
|
||||||
check_errors([ExpectedError], Errors)
|
|
||||||
end} ||
|
|
||||||
{ContractName, ExpectedError} <- failing_code_gen_contracts()] ++
|
|
||||||
[ {"Testing include with explicit files",
|
[ {"Testing include with explicit files",
|
||||||
fun() ->
|
fun() ->
|
||||||
FileSystem = maps:from_list(
|
FileSystem = maps:from_list(
|
||||||
@@ -75,6 +69,8 @@ simple_compile_test_() ->
|
|||||||
[ {"Testing warning messages",
|
[ {"Testing warning messages",
|
||||||
fun() ->
|
fun() ->
|
||||||
#{ warnings := Warnings } = compile("warnings", [warn_all]),
|
#{ 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)
|
check_warnings(warnings(), Warnings)
|
||||||
end} ] ++
|
end} ] ++
|
||||||
[].
|
[].
|
||||||
@@ -166,6 +162,7 @@ compilable_contracts() ->
|
|||||||
"state_handling",
|
"state_handling",
|
||||||
"events",
|
"events",
|
||||||
"include",
|
"include",
|
||||||
|
"relative_include",
|
||||||
"basic_auth",
|
"basic_auth",
|
||||||
"basic_auth_tx",
|
"basic_auth_tx",
|
||||||
"bitcoin_auth",
|
"bitcoin_auth",
|
||||||
@@ -175,6 +172,7 @@ compilable_contracts() ->
|
|||||||
"namespace_bug",
|
"namespace_bug",
|
||||||
"bytes_to_x",
|
"bytes_to_x",
|
||||||
"bytes_concat",
|
"bytes_concat",
|
||||||
|
"bytes_misc",
|
||||||
"aens",
|
"aens",
|
||||||
"aens_update",
|
"aens_update",
|
||||||
"tuple_match",
|
"tuple_match",
|
||||||
@@ -202,6 +200,33 @@ compilable_contracts() ->
|
|||||||
"assign_patterns",
|
"assign_patterns",
|
||||||
"patterns_guards",
|
"patterns_guards",
|
||||||
"pipe_operator",
|
"pipe_operator",
|
||||||
|
"polymorphism_contract_implements_interface",
|
||||||
|
"polymorphism_contract_multi_interface",
|
||||||
|
"polymorphism_contract_interface_extends_interface",
|
||||||
|
"polymorphism_contract_interface_extensions",
|
||||||
|
"polymorphism_contract_interface_same_decl_multi_interface",
|
||||||
|
"polymorphism_contract_interface_same_name_same_type",
|
||||||
|
"polymorphism_variance_switching_chain_create",
|
||||||
|
"polymorphism_variance_switching_void_supertype",
|
||||||
|
"polymorphism_variance_switching_unify_with_interface_decls",
|
||||||
|
"polymorphism_preserve_or_add_payable_contract",
|
||||||
|
"polymorphism_preserve_or_add_payable_entrypoint",
|
||||||
|
"polymorphism_preserve_or_remove_stateful_entrypoint",
|
||||||
|
"missing_init_fun_state_unit",
|
||||||
|
"complex_compare_leq",
|
||||||
|
"complex_compare",
|
||||||
|
"higher_order_compare",
|
||||||
|
"higher_order_map_keys",
|
||||||
|
"higher_order_state",
|
||||||
|
"polymorphic_compare",
|
||||||
|
"polymorphic_entrypoint",
|
||||||
|
"polymorphic_entrypoint_return",
|
||||||
|
"polymorphic_map_keys",
|
||||||
|
"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.
|
"test" % Custom general-purpose test file. Keep it last on the list.
|
||||||
].
|
].
|
||||||
|
|
||||||
@@ -266,7 +291,11 @@ warnings() ->
|
|||||||
<<?PosW(48, 5)
|
<<?PosW(48, 5)
|
||||||
"Unused return value.">>,
|
"Unused return value.">>,
|
||||||
<<?PosW(60, 5)
|
<<?PosW(60, 5)
|
||||||
"The function `dec` is defined but never used.">>
|
"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.">>
|
||||||
]).
|
]).
|
||||||
|
|
||||||
failing_contracts() ->
|
failing_contracts() ->
|
||||||
@@ -283,34 +312,26 @@ failing_contracts() ->
|
|||||||
|
|
||||||
%% Type errors
|
%% Type errors
|
||||||
, ?TYPE_ERROR(name_clash,
|
, ?TYPE_ERROR(name_clash,
|
||||||
[<<?Pos(14, 3)
|
[<<?Pos(4, 3)
|
||||||
|
"Duplicate definitions of `double_def` at\n"
|
||||||
|
" - line 3, column 3\n"
|
||||||
|
" - line 4, column 3">>,
|
||||||
|
<<?Pos(7, 3)
|
||||||
"Duplicate definitions of `abort` at\n"
|
"Duplicate definitions of `abort` at\n"
|
||||||
" - (builtin location)\n"
|
" - (builtin location)\n"
|
||||||
" - line 14, column 3">>,
|
" - line 7, column 3">>,
|
||||||
<<?Pos(15, 3)
|
<<?Pos(8, 3)
|
||||||
"Duplicate definitions of `require` at\n"
|
"Duplicate definitions of `require` at\n"
|
||||||
" - (builtin location)\n"
|
" - (builtin location)\n"
|
||||||
" - line 15, column 3">>,
|
|
||||||
<<?Pos(11, 3)
|
|
||||||
"Duplicate definitions of `double_def` at\n"
|
|
||||||
" - line 10, column 3\n"
|
|
||||||
" - line 11, column 3">>,
|
|
||||||
<<?Pos(5, 3)
|
|
||||||
"Duplicate definitions of `double_proto` at\n"
|
|
||||||
" - line 4, column 3\n"
|
|
||||||
" - line 5, column 3">>,
|
|
||||||
<<?Pos(8, 3)
|
|
||||||
"Duplicate definitions of `proto_and_def` at\n"
|
|
||||||
" - line 7, column 3\n"
|
|
||||||
" - line 8, column 3">>,
|
" - line 8, column 3">>,
|
||||||
<<?Pos(16, 3)
|
<<?Pos(9, 3)
|
||||||
"Duplicate definitions of `put` at\n"
|
"Duplicate definitions of `put` at\n"
|
||||||
" - (builtin location)\n"
|
" - (builtin location)\n"
|
||||||
" - line 16, column 3">>,
|
" - line 9, column 3">>,
|
||||||
<<?Pos(17, 3)
|
<<?Pos(10, 3)
|
||||||
"Duplicate definitions of `state` at\n"
|
"Duplicate definitions of `state` at\n"
|
||||||
" - (builtin location)\n"
|
" - (builtin location)\n"
|
||||||
" - line 17, column 3">>])
|
" - line 10, column 3">>])
|
||||||
, ?TYPE_ERROR(type_errors,
|
, ?TYPE_ERROR(type_errors,
|
||||||
[<<?Pos(17, 23)
|
[<<?Pos(17, 23)
|
||||||
"Unbound variable `zz`">>,
|
"Unbound variable `zz`">>,
|
||||||
@@ -430,6 +451,10 @@ failing_contracts() ->
|
|||||||
[<<?Pos(12, 42)
|
[<<?Pos(12, 42)
|
||||||
"Cannot unify `int` and `string`\n"
|
"Cannot unify `int` and `string`\n"
|
||||||
"when checking the type of the expression `r.foo() : map(int, string)` "
|
"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)`">>])
|
"against the expected type `map(string, int)`">>])
|
||||||
, ?TYPE_ERROR(not_toplevel_include,
|
, ?TYPE_ERROR(not_toplevel_include,
|
||||||
[<<?Pos(2, 11)
|
[<<?Pos(2, 11)
|
||||||
@@ -564,7 +589,7 @@ failing_contracts() ->
|
|||||||
])
|
])
|
||||||
, ?TYPE_ERROR(list_comp_bad_shadow,
|
, ?TYPE_ERROR(list_comp_bad_shadow,
|
||||||
[<<?Pos(2, 53)
|
[<<?Pos(2, 53)
|
||||||
"Cannot unify `int` and `string`\n"
|
"Cannot unify `string` and `int`\n"
|
||||||
"when checking the type of the pattern `x : int` against the expected type `string`">>
|
"when checking the type of the pattern `x : int` against the expected type `string`">>
|
||||||
])
|
])
|
||||||
, ?TYPE_ERROR(map_as_map_key,
|
, ?TYPE_ERROR(map_as_map_key,
|
||||||
@@ -587,6 +612,21 @@ failing_contracts() ->
|
|||||||
[<<?Pos(3, 5)
|
[<<?Pos(3, 5)
|
||||||
"Unbound variable `Chain.event`\n"
|
"Unbound variable `Chain.event`\n"
|
||||||
"Did you forget to define the event type?">>])
|
"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,
|
, ?TYPE_ERROR(bad_bytes_concat,
|
||||||
[<<?Pos(12, 40)
|
[<<?Pos(12, 40)
|
||||||
"Failed to resolve byte array lengths in call to Bytes.concat with arguments of type\n"
|
"Failed to resolve byte array lengths in call to Bytes.concat with arguments of type\n"
|
||||||
@@ -611,7 +651,8 @@ failing_contracts() ->
|
|||||||
"and result type\n"
|
"and result type\n"
|
||||||
" - 'c (at line 16, column 39)">>,
|
" - 'c (at line 16, column 39)">>,
|
||||||
<<?Pos(19, 25)
|
<<?Pos(19, 25)
|
||||||
"Cannot resolve length of byte array.">>])
|
"Cannot resolve type of byte array in\n"
|
||||||
|
" the first argument of a call to Bytes.to_str">>])
|
||||||
, ?TYPE_ERROR(bad_bytes_split,
|
, ?TYPE_ERROR(bad_bytes_split,
|
||||||
[<<?Pos(13, 5)
|
[<<?Pos(13, 5)
|
||||||
"Failed to resolve byte array lengths in call to Bytes.split with argument of type\n"
|
"Failed to resolve byte array lengths in call to Bytes.split with argument of type\n"
|
||||||
@@ -646,10 +687,6 @@ failing_contracts() ->
|
|||||||
[<<?Pos(5, 28)
|
[<<?Pos(5, 28)
|
||||||
"Invalid call to contract entrypoint `Foo.foo`.\n"
|
"Invalid call to contract entrypoint `Foo.foo`.\n"
|
||||||
"It must be called as `c.foo` for some `c : Foo`.">>])
|
"It must be called as `c.foo` for some `c : Foo`.">>])
|
||||||
, ?TYPE_ERROR(toplevel_let,
|
|
||||||
[<<?Pos(2, 7)
|
|
||||||
"Toplevel \"let\" definitions are not supported. "
|
|
||||||
"Value `this_is_illegal` could be replaced by 0-argument function.">>])
|
|
||||||
, ?TYPE_ERROR(empty_typedecl,
|
, ?TYPE_ERROR(empty_typedecl,
|
||||||
[<<?Pos(2, 8)
|
[<<?Pos(2, 8)
|
||||||
"Empty type declarations are not supported. "
|
"Empty type declarations are not supported. "
|
||||||
@@ -726,10 +763,22 @@ failing_contracts() ->
|
|||||||
"Conflicting updates for field 'foo'">>])
|
"Conflicting updates for field 'foo'">>])
|
||||||
, ?TYPE_ERROR(factories_type_errors,
|
, ?TYPE_ERROR(factories_type_errors,
|
||||||
[<<?Pos(10,18)
|
[<<?Pos(10,18)
|
||||||
"Chain.clone requires `ref` named argument of contract type.">>,
|
"Chain.clone requires `ref` named argument of contract type.">>,
|
||||||
<<?Pos(11,18)
|
<<?Pos(11,18)
|
||||||
"Cannot unify `(gas : int, value : int, protected : bool) => if(protected, option(void), void)` and `(gas : int, value : int, protected : bool, int, bool) => 'b`\n"
|
"Cannot unify `(gas : int, value : int, protected : bool) => if(protected, option(void), void)` and `(gas : int, value : int, protected : bool, int, bool) => if(protected, option(void), void)`\n"
|
||||||
"when checking contract construction of type\n (gas : int, value : int, protected : bool) =>\n if(protected, option(void), void) (at line 11, column 18)\nagainst the expected type\n (gas : int, value : int, protected : bool, int, bool) => 'b">>,
|
"when checking contract construction of type\n"
|
||||||
|
" (gas : int, value : int, protected : bool) =>\n"
|
||||||
|
" if(protected, option(void), void) (at line 11, column 18)\n"
|
||||||
|
"against the expected type\n"
|
||||||
|
" (gas : int, value : int, protected : bool, int, bool) =>\n"
|
||||||
|
" if(protected, option(void), void)">>,
|
||||||
|
<<?Pos(11,18)
|
||||||
|
"Cannot unify `Bakoom` and `Kaboom`\n"
|
||||||
|
"when checking that contract construction of type\n"
|
||||||
|
" Bakoom\n"
|
||||||
|
"arising from resolution of variadic function `Chain.clone`\n"
|
||||||
|
"matches the expected type\n"
|
||||||
|
" Kaboom">>,
|
||||||
<<?Pos(12,37)
|
<<?Pos(12,37)
|
||||||
"Cannot unify `int` and `bool`\n"
|
"Cannot unify `int` and `bool`\n"
|
||||||
"when checking named argument `gas : int` against inferred type `bool`">>,
|
"when checking named argument `gas : int` against inferred type `bool`">>,
|
||||||
@@ -835,59 +884,428 @@ failing_contracts() ->
|
|||||||
<<?Pos(48, 5)
|
<<?Pos(48, 5)
|
||||||
"Unused return value.">>,
|
"Unused return value.">>,
|
||||||
<<?Pos(60, 5)
|
<<?Pos(60, 5)
|
||||||
"The function `dec` is defined but never used.">>
|
"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.">>
|
||||||
|
])
|
||||||
|
, ?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`">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(polymorphism_contract_missing_implementation,
|
||||||
|
[<<?Pos(4,20)
|
||||||
|
"Unimplemented entrypoint `f` from the interface `I1` in the contract `I2`">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(polymorphism_contract_same_decl_multi_interface,
|
||||||
|
[<<?Pos(7,10)
|
||||||
|
"Both interfaces `I` and `J` implemented by the contract `C` have a function called `f`">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(polymorphism_contract_undefined_interface,
|
||||||
|
[<<?Pos(1,14)
|
||||||
|
"Trying to implement or extend an undefined interface `I`">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(polymorphism_contract_same_name_different_type_multi_interface,
|
||||||
|
[<<?Pos(7,10)
|
||||||
|
"Both interfaces `I` and `J` implemented by the contract `C` have a function called `f`">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(polymorphism_contract_interface_undefined_interface,
|
||||||
|
[<<?Pos(1,24)
|
||||||
|
"Trying to implement or extend an undefined interface `H`">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(polymorphism_variance_switching,
|
||||||
|
[<<?Pos(36,49)
|
||||||
|
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
|
||||||
|
"when checking the application of\n"
|
||||||
|
" `g2 : (Cat) => Cat`\n"
|
||||||
|
"to arguments\n"
|
||||||
|
" `x : Animal`">>,
|
||||||
|
<<?Pos(39,43)
|
||||||
|
"Cannot unify `Animal` and `Cat` in a covariant context\n"
|
||||||
|
"when checking the type of the expression `g3(x) : Animal` against the expected type `Cat`">>,
|
||||||
|
<<?Pos(48,55)
|
||||||
|
"Cannot unify `Animal` and `Cat` in a covariant context\n"
|
||||||
|
"when checking the application of\n"
|
||||||
|
" `g5 : ((Animal) => Animal) => Cat`\n"
|
||||||
|
"to arguments\n"
|
||||||
|
" `x : (Cat) => Cat`">>,
|
||||||
|
<<?Pos(52,44)
|
||||||
|
"Cannot unify `Animal` and `Cat` in a covariant context\n"
|
||||||
|
"when checking the type of the expression `f6() : option(Animal)` against the expected type `option(Cat)`">>,
|
||||||
|
<<?Pos(73,43)
|
||||||
|
"Cannot unify `Animal` and `Cat` in a covariant context\n"
|
||||||
|
"when checking the type of the expression `some_animal : Animal` against the expected type `Cat`">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(polymorphism_variance_switching_custom_types,
|
||||||
|
[<<?Pos(56,39)
|
||||||
|
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
|
||||||
|
"when checking the type of the expression `DT_CONTRA(f_c_to_u) : dt_contra(Cat)` against the expected type `dt_contra(Animal)`">>,
|
||||||
|
<<?Pos(62,35)
|
||||||
|
"Cannot unify `Animal` and `Cat` in a covariant context\n"
|
||||||
|
"when checking the type of the expression `DT_CO(f_u_to_a) : dt_co(Animal)` against the expected type `dt_co(Cat)`">>,
|
||||||
|
<<?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)`">>,
|
||||||
|
<<?Pos(69,36)
|
||||||
|
"Cannot unify `Animal` and `Cat` in a invariant context\n"
|
||||||
|
"when checking the type of the expression `DT_INV(f_a_to_a) : dt_inv(Animal)` against the expected type `dt_inv(Cat)`">>,
|
||||||
|
<<?Pos(70,36)
|
||||||
|
"Cannot unify `Animal` and `Cat` in a invariant context\n"
|
||||||
|
"when checking the type of the expression `DT_INV(f_a_to_c) : dt_inv(Animal)` against the expected type `dt_inv(Cat)`">>,
|
||||||
|
<<?Pos(71,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(80,40)
|
||||||
|
"Cannot unify `Cat` and `Animal` in a invariant context\n"
|
||||||
|
"when checking the type of the expression `DT_INV_SEP_A(f_c_to_u) : dt_inv_sep(Cat)` against the expected type `dt_inv_sep(Animal)`">>,
|
||||||
|
<<?Pos(82,40)
|
||||||
|
"Cannot unify `Cat` and `Animal` in a invariant context\n"
|
||||||
|
"when checking the type of the expression `DT_INV_SEP_B(f_u_to_c) : dt_inv_sep(Cat)` against the expected type `dt_inv_sep(Animal)`">>,
|
||||||
|
<<?Pos(83,40)
|
||||||
|
"Cannot unify `Animal` and `Cat` in a invariant context\n"
|
||||||
|
"when checking the type of the expression `DT_INV_SEP_A(f_a_to_u) : dt_inv_sep(Animal)` against the expected type `dt_inv_sep(Cat)`">>,
|
||||||
|
<<?Pos(85,40)
|
||||||
|
"Cannot unify `Animal` and `Cat` in a invariant context\n"
|
||||||
|
"when checking the type of the expression `DT_INV_SEP_B(f_u_to_a) : dt_inv_sep(Animal)` against the expected type `dt_inv_sep(Cat)`">>,
|
||||||
|
<<?Pos(90,42)
|
||||||
|
"Cannot unify `Animal` and `Cat` in a covariant context\n"
|
||||||
|
"when checking the type of the expression `DT_CO_NEST_A(f_dt_contra_a_to_u) : dt_co_nest_a(Animal)` against the expected type `dt_co_nest_a(Cat)`">>,
|
||||||
|
<<?Pos(94,46)
|
||||||
|
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
|
||||||
|
"when checking the type of the expression `DT_CONTRA_NEST_A(f_dt_co_c_to_u) : dt_contra_nest_a(Cat)` against the expected type `dt_contra_nest_a(Animal)`">>,
|
||||||
|
<<?Pos(99,46)
|
||||||
|
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
|
||||||
|
"when checking the type of the expression `DT_CONTRA_NEST_B(f_u_to_dt_contra_c) : dt_contra_nest_b(Cat)` against the expected type `dt_contra_nest_b(Animal)`">>,
|
||||||
|
<<?Pos(105,42)
|
||||||
|
"Cannot unify `Animal` and `Cat` in a covariant context\n"
|
||||||
|
"when checking the type of the expression `DT_CO_NEST_B(f_u_to_dt_co_a) : dt_co_nest_b(Animal)` against the expected type `dt_co_nest_b(Cat)`">>,
|
||||||
|
<<?Pos(110,13)
|
||||||
|
"Cannot unify `Animal` and `Cat` in a covariant context\n"
|
||||||
|
"when checking the type of the pattern `vj3 : dt_co_twice(Cat)` against the expected type `dt_co_twice(Animal)`">>,
|
||||||
|
<<?Pos(114,59)
|
||||||
|
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
|
||||||
|
"when checking the type of the expression `DT_A_CONTRA_B_CONTRA(f_a_to_c_to_u) : dt_a_contra_b_contra(Animal, Cat)` against the expected type `dt_a_contra_b_contra(Animal, Animal)`">>,
|
||||||
|
<<?Pos(115,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, 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(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)`">>,
|
||||||
|
<<?Pos(120,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, Cat)`">>,
|
||||||
|
<<?Pos(122,59)
|
||||||
|
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
|
||||||
|
"when checking the type of the expression `DT_A_CONTRA_B_CONTRA(f_a_to_c_to_u) : dt_a_contra_b_contra(Animal, Cat)` against the expected type `dt_a_contra_b_contra(Cat, Animal)`">>,
|
||||||
|
<<?Pos(124,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(Cat, Animal)`">>,
|
||||||
|
<<?Pos(131,13)
|
||||||
|
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
|
||||||
|
"when checking the type of the pattern `vl2 : dt_contra_twice(Animal)` against the expected type `dt_contra_twice(Cat)`">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(polymorphism_variance_switching_records,
|
||||||
|
[<<?Pos(27,13)
|
||||||
|
"Cannot unify `Animal` and `Cat` in a covariant context\n"
|
||||||
|
"when checking the type of the pattern `r03 : rec_co(Cat)` against the expected type `Main.rec_co(Animal)`">>,
|
||||||
|
<<?Pos(33,13)
|
||||||
|
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
|
||||||
|
"when checking the type of the pattern `r06 : rec_contra(Animal)` against the expected type `Main.rec_contra(Cat)`">>,
|
||||||
|
<<?Pos(40,13)
|
||||||
|
"Cannot unify `Cat` and `Animal` in a invariant context\n"
|
||||||
|
"when checking the type of the pattern `r10 : rec_inv(Animal)` against the expected type `Main.rec_inv(Cat)`">>,
|
||||||
|
<<?Pos(41,13)
|
||||||
|
"Cannot unify `Animal` and `Cat` in a invariant context\n"
|
||||||
|
"when checking the type of the pattern `r11 : rec_inv(Cat)` against the expected type `Main.rec_inv(Animal)`">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(polymorphism_variance_switching_oracles,
|
||||||
|
[<<?Pos(15,13)
|
||||||
|
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
|
||||||
|
"when checking the type of the pattern `o03 : oracle(Animal, Animal)` against the expected type `oracle(Cat, Animal)`">>,
|
||||||
|
<<?Pos(16,13)
|
||||||
|
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
|
||||||
|
"when checking the type of the pattern `o04 : oracle(Animal, Animal)` against the expected type `oracle(Cat, Cat)`">>,
|
||||||
|
<<?Pos(17,13)
|
||||||
|
"Cannot unify `Animal` and `Cat` in a covariant context\n"
|
||||||
|
"when checking the type of the pattern `o05 : oracle(Animal, Cat)` against the expected type `oracle(Animal, Animal)`">>,
|
||||||
|
<<?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)`">>,
|
||||||
|
<<?Pos(25,13)
|
||||||
|
"Cannot unify `Animal` and `Cat` in a covariant context\n"
|
||||||
|
"when checking the type of the pattern `o13 : oracle(Cat, Cat)` against the expected type `oracle(Animal, Animal)`">>,
|
||||||
|
<<?Pos(27,13)
|
||||||
|
"Cannot unify `Animal` and `Cat` in a covariant context\n"
|
||||||
|
"when checking the type of the pattern `o15 : oracle(Cat, Cat)` against the expected type `oracle(Cat, Animal)`">>,
|
||||||
|
<<?Pos(34,13)
|
||||||
|
"Cannot unify `Animal` and `Cat` in a covariant context\n"
|
||||||
|
"when checking the type of the pattern `q05 : oracle_query(Animal, Cat)` against the expected type `oracle_query(Animal, Animal)`">>,
|
||||||
|
<<?Pos(36,13)
|
||||||
|
"Cannot unify `Animal` and `Cat` in a covariant context\n"
|
||||||
|
"when checking the type of the pattern `q07 : oracle_query(Animal, Cat)` against the expected type `oracle_query(Cat, Animal)`">>,
|
||||||
|
<<?Pos(38,13)
|
||||||
|
"Cannot unify `Animal` and `Cat` in a covariant context\n"
|
||||||
|
"when checking the type of the pattern `q09 : oracle_query(Cat, Animal)` against the expected type `oracle_query(Animal, Animal)`">>,
|
||||||
|
<<?Pos(39,13)
|
||||||
|
"Cannot unify `Animal` and `Cat` in a covariant context\n"
|
||||||
|
"when checking the type of the pattern `q10 : oracle_query(Cat, Animal)` against the expected type `oracle_query(Animal, Cat)`">>,
|
||||||
|
<<?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)`">>,
|
||||||
|
<<?Pos(44,13)
|
||||||
|
"Cannot unify `Animal` and `Cat` in a covariant context\n"
|
||||||
|
"when checking the type of the pattern `q15 : oracle_query(Cat, Cat)` against the expected type `oracle_query(Cat, Animal)`">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(polymorphism_variance_switching_chain_create_fail,
|
||||||
|
[<<?Pos(9,22)
|
||||||
|
"I is not implemented.\n"
|
||||||
|
"when resolving arguments of variadic function `Chain.create`">>,
|
||||||
|
<<?Pos(10,13)
|
||||||
|
"Cannot unify `I` and `C` in a covariant context\n"
|
||||||
|
"when checking the type of the pattern `c2 : C` against the expected type `I`">>,
|
||||||
|
<<?Pos(10,22)
|
||||||
|
"I is not implemented.\n"
|
||||||
|
"when resolving arguments of variadic function `Chain.create`">>,
|
||||||
|
<<?Pos(11,22)
|
||||||
|
"I is not implemented.\n"
|
||||||
|
"when resolving arguments of variadic function `Chain.create`">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(missing_definition,
|
||||||
|
[<<?Pos(2,14)
|
||||||
|
"Missing definition of function `foo`">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(child_with_decls,
|
||||||
|
[<<?Pos(2,14)
|
||||||
|
"Missing definition of function `f`">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(parameterised_state,
|
||||||
|
[<<?Pos(3,8)
|
||||||
|
"The state type cannot be parameterized">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(parameterised_event,
|
||||||
|
[<<?Pos(3,12)
|
||||||
|
"The event type cannot be parameterized">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(missing_init_fun_alias_to_type,
|
||||||
|
[<<?Pos(1,10)
|
||||||
|
"Missing `init` function for the contract `AliasToType`.\n"
|
||||||
|
"The `init` function can only be omitted if the state type is `unit`">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(missing_init_fun_alias_to_alias_to_type,
|
||||||
|
[<<?Pos(1,10)
|
||||||
|
"Missing `init` function for the contract `AliasToAliasToType`.\n"
|
||||||
|
"The `init` function can only be omitted if the state type is `unit`">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(higher_order_entrypoint,
|
||||||
|
[<<?Pos(2,20)
|
||||||
|
"The argument\n"
|
||||||
|
" `f : (int) => int`\n"
|
||||||
|
"of entrypoint `apply` has a higher-order (contains function types) type">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(higher_order_entrypoint_return,
|
||||||
|
[<<?Pos(2,3)
|
||||||
|
"The return type\n"
|
||||||
|
" `(int) => int`\n"
|
||||||
|
"of entrypoint `add` is higher-order (contains function types)">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(polymorphic_aens_resolve,
|
||||||
|
[<<?Pos(4,5)
|
||||||
|
"Invalid return type of `AENSv2.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"
|
||||||
|
" `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"
|
||||||
|
" `list(int)`\n"
|
||||||
|
"It must be a `string` or a pubkey type (`address`, `oracle`, etc)">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(polymorphic_query_type,
|
||||||
|
[<<?Pos(3,5)
|
||||||
|
"Invalid oracle type\n"
|
||||||
|
" `oracle('a, 'b)`\n"
|
||||||
|
"The query type must not be polymorphic (contain type variables)">>,
|
||||||
|
<<?Pos(3,5)
|
||||||
|
"Invalid oracle type\n"
|
||||||
|
" `oracle('a, 'b)`\n"
|
||||||
|
"The response type must not be polymorphic (contain type variables)">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(polymorphic_response_type,
|
||||||
|
[<<?Pos(3,5)
|
||||||
|
"Invalid oracle type\n"
|
||||||
|
" `oracle(string, 'r)`\n"
|
||||||
|
"The response type must not be polymorphic (contain type variables)">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(higher_order_query_type,
|
||||||
|
[<<?Pos(3,5)
|
||||||
|
"Invalid oracle type\n"
|
||||||
|
" `oracle((int) => int, string)`\n"
|
||||||
|
"The query type must not be higher-order (contain function types)">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(higher_order_response_type,
|
||||||
|
[<<?Pos(3,5)
|
||||||
|
"Invalid oracle type\n"
|
||||||
|
" `oracle(string, (int) => int)`\n"
|
||||||
|
"The response type must not be higher-order (contain function types)">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(var_args_unify_let,
|
||||||
|
[<<?Pos(3,9)
|
||||||
|
"Cannot infer types for variable argument list.\n"
|
||||||
|
"when checking the type of the pattern `x : 'a` against the expected type `(gas : int, value : int, protected : bool, ref : 'b, var_args) => 'b`">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(var_args_unify_fun_call,
|
||||||
|
[<<?Pos(6,5)
|
||||||
|
"Cannot infer types for variable argument list.\n"
|
||||||
|
"when checking the application of\n"
|
||||||
|
" `g : (() => 'b) => 'b`\n"
|
||||||
|
"to arguments\n"
|
||||||
|
" `Chain.create : (value : int, var_args) => 'c`">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(polymorphism_add_stateful_entrypoint,
|
||||||
|
[<<?Pos(5,25)
|
||||||
|
"`f` cannot be stateful because the entrypoint `f` in the interface `I` is not stateful">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(polymorphism_change_entrypoint_to_function,
|
||||||
|
[<<?Pos(6,14)
|
||||||
|
"`f` must be declared as an entrypoint instead of a function in order to implement the entrypoint `f` from the interface `I`">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(polymorphism_non_payable_contract_implement_payable,
|
||||||
|
[<<?Pos(4,10)
|
||||||
|
"Non-payable contract `C` cannot implement payable interface `I`">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(polymorphism_non_payable_interface_implement_payable,
|
||||||
|
[<<?Pos(4,20)
|
||||||
|
"Non-payable interface `H` cannot implement payable interface `I`">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(polymorphism_remove_payable_entrypoint,
|
||||||
|
[<<?Pos(5,16)
|
||||||
|
"`f` must be payable because the entrypoint `f` in the interface `I` is payable">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(calling_child_contract_entrypoint,
|
||||||
|
[<<?Pos(5,20)
|
||||||
|
"Invalid call to contract entrypoint `F.g`.\n"
|
||||||
|
"It must be called as `c.g` for some `c : F`.">>])
|
||||||
|
, ?TYPE_ERROR(using_contract_as_namespace,
|
||||||
|
[<<?Pos(5,3)
|
||||||
|
"Cannot use undefined namespace F">>])
|
||||||
|
, ?TYPE_ERROR(hole_expression,
|
||||||
|
[<<?Pos(5,13)
|
||||||
|
"Found a hole of type `bool`">>,
|
||||||
|
<<?Pos(6,17)
|
||||||
|
"Found a hole of type `string`">>,
|
||||||
|
<<?Pos(9,37)
|
||||||
|
"Found a hole of type `(int) => int`">>,
|
||||||
|
<<?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`">>
|
||||||
])
|
])
|
||||||
].
|
|
||||||
|
|
||||||
-define(Path(File), "code_errors/" ??File).
|
|
||||||
-define(Msg(File, Line, Col, Err), <<?Pos("Code generation", ?Path(File), Line, Col) Err>>).
|
|
||||||
|
|
||||||
-define(FATE_ERR(File, Line, Col, Err), {?Path(File), ?Msg(File, Line, Col, Err)}).
|
|
||||||
|
|
||||||
failing_code_gen_contracts() ->
|
|
||||||
[ ?FATE_ERR(missing_definition, 2, 14,
|
|
||||||
"Missing definition of function 'foo'.")
|
|
||||||
, ?FATE_ERR(higher_order_entrypoint, 2, 20,
|
|
||||||
"The argument\n"
|
|
||||||
" f : (int) => int\n"
|
|
||||||
"of entrypoint 'apply' has a higher-order (contains function types) type.")
|
|
||||||
, ?FATE_ERR(higher_order_entrypoint_return, 2, 3,
|
|
||||||
"The return type\n"
|
|
||||||
" (int) => int\n"
|
|
||||||
"of entrypoint 'add' is higher-order (contains function types).")
|
|
||||||
, ?FATE_ERR(missing_init_function, 1, 10,
|
|
||||||
"Missing init function for the contract 'MissingInitFunction'.\n"
|
|
||||||
"The 'init' function can only be omitted if the state type is 'unit'.")
|
|
||||||
, ?FATE_ERR(parameterised_state, 3, 8,
|
|
||||||
"The state type cannot be parameterized.")
|
|
||||||
, ?FATE_ERR(parameterised_event, 3, 12,
|
|
||||||
"The event type cannot be parameterized.")
|
|
||||||
, ?FATE_ERR(polymorphic_aens_resolve, 4, 5,
|
|
||||||
"Invalid return type of AENS.resolve:\n"
|
|
||||||
" 'a\n"
|
|
||||||
"It must be a string or a pubkey type (address, oracle, etc).")
|
|
||||||
, ?FATE_ERR(bad_aens_resolve, 6, 5,
|
|
||||||
"Invalid return type of AENS.resolve:\n"
|
|
||||||
" list(int)\n"
|
|
||||||
"It must be a string or a pubkey type (address, oracle, etc).")
|
|
||||||
, ?FATE_ERR(polymorphic_query_type, 3, 5,
|
|
||||||
"Invalid oracle type\n"
|
|
||||||
" oracle('a, 'b)\n"
|
|
||||||
"The query type must not be polymorphic (contain type variables).")
|
|
||||||
, ?FATE_ERR(polymorphic_response_type, 3, 5,
|
|
||||||
"Invalid oracle type\n"
|
|
||||||
" oracle(string, 'r)\n"
|
|
||||||
"The response type must not be polymorphic (contain type variables).")
|
|
||||||
, ?FATE_ERR(higher_order_query_type, 3, 5,
|
|
||||||
"Invalid oracle type\n"
|
|
||||||
" oracle((int) => int, string)\n"
|
|
||||||
"The query type must not be higher-order (contain function types).")
|
|
||||||
, ?FATE_ERR(higher_order_response_type, 3, 5,
|
|
||||||
"Invalid oracle type\n"
|
|
||||||
" oracle(string, (int) => int)\n"
|
|
||||||
"The response type must not be higher-order (contain function types).")
|
|
||||||
, ?FATE_ERR(child_with_decls, 2, 14,
|
|
||||||
"Missing definition of function 'f'.")
|
|
||||||
].
|
].
|
||||||
|
|
||||||
validation_test_() ->
|
validation_test_() ->
|
||||||
@@ -935,7 +1353,9 @@ validate(Contract1, Contract2) ->
|
|||||||
true -> [debug_mode];
|
true -> [debug_mode];
|
||||||
false -> []
|
false -> []
|
||||||
end ++
|
end ++
|
||||||
[{include, {file_system, [aeso_test_utils:contract_path()]}}]);
|
[ {src_file, lists:concat([Contract2, ".aes"])}
|
||||||
|
, {include, {file_system, [aeso_test_utils:contract_path()]}}
|
||||||
|
]);
|
||||||
Error -> print_and_throw(Error)
|
Error -> print_and_throw(Error)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
-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)"}
|
||||||
|
].
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ simple_contracts_test_() ->
|
|||||||
Text = "main contract Identity =\n"
|
Text = "main contract Identity =\n"
|
||||||
" function id(x) = x\n",
|
" function id(x) = x\n",
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
[{contract_main, _, {con, _, "Identity"},
|
[{contract_main, _, {con, _, "Identity"}, _,
|
||||||
[{letfun, _, {id, _, "id"}, [{id, _, "x"}], {id, _, "_"},
|
[{letfun, _, {id, _, "id"}, [{id, _, "x"}], {id, _, "_"},
|
||||||
[{guarded, _, [], {id, _, "x"}}]}]}], parse_string(Text)),
|
[{guarded, _, [], {id, _, "x"}}]}]}], parse_string(Text)),
|
||||||
ok
|
ok
|
||||||
@@ -53,7 +53,7 @@ simple_contracts_test_() ->
|
|||||||
%% associativity
|
%% associativity
|
||||||
[ RightAssoc(Op) || Op <- ["||", "&&", "::", "++"] ],
|
[ RightAssoc(Op) || Op <- ["||", "&&", "::", "++"] ],
|
||||||
[ NonAssoc(Op) || Op <- ["==", "!=", "<", ">", "=<", ">="] ],
|
[ NonAssoc(Op) || Op <- ["==", "!=", "<", ">", "=<", ">="] ],
|
||||||
[ LeftAssoc(Op) || Op <- ["+", "-", "*", "/", "mod"] ],
|
[ LeftAssoc(Op) || Op <- ["+", "-", "*", "/", "mod", "band", "bor", "bxor", "<<", ">>"] ],
|
||||||
|
|
||||||
%% precedence
|
%% precedence
|
||||||
[ Stronger(Op2, Op1) || [T1 , T2 | _] <- tails(Tiers), Op1 <- T1, Op2 <- T2 ],
|
[ Stronger(Op2, Op1) || [T1 , T2 | _] <- tails(Tiers), Op1 <- T1, Op2 <- T2 ],
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ all_tokens() ->
|
|||||||
%% Symbols
|
%% Symbols
|
||||||
lists:map(Lit, [',', '.', ';', '|', ':', '(', ')', '[', ']', '{', '}']) ++
|
lists:map(Lit, [',', '.', ';', '|', ':', '(', ')', '[', ']', '{', '}']) ++
|
||||||
%% Operators
|
%% Operators
|
||||||
lists:map(Lit, ['=', '==', '!=', '>', '<', '>=', '=<', '-', '+', '++', '*', '/', mod, ':', '::', '->', '=>', '||', '&&', '!']) ++
|
lists:map(Lit, ['=', '==', '!=', '>', '<', '>=', '=<', '-', '+', '++', '*', '/', mod,
|
||||||
|
':', '::', '->', '=>', '||', '&&', '!', 'band', 'bor', 'bxor', 'bnot' ,'<<', '>>']) ++
|
||||||
%% Keywords
|
%% Keywords
|
||||||
lists:map(Lit, [contract, type, 'let', switch]) ++
|
lists:map(Lit, [contract, type, 'let', switch]) ++
|
||||||
%% Comment token (not an actual token), just for tests
|
%% Comment token (not an actual token), just for tests
|
||||||
|
|||||||
+27
-16
@@ -1,71 +1,82 @@
|
|||||||
|
contract C = entrypoint init() = ()
|
||||||
|
|
||||||
// AENS tests
|
// AENS tests
|
||||||
contract AENSTest =
|
main contract AENSTest =
|
||||||
|
|
||||||
// Name resolution
|
// Name resolution
|
||||||
|
|
||||||
stateful entrypoint resolve_word(name : string, key : string) : option(address) =
|
stateful entrypoint resolve_word(name : string, key : string) : option(address) =
|
||||||
AENS.resolve(name, key)
|
AENSv2.resolve(name, key)
|
||||||
|
|
||||||
stateful entrypoint resolve_string(name : string, key : string) : option(string) =
|
stateful entrypoint resolve_string(name : string, key : string) : option(string) =
|
||||||
AENS.resolve(name, key)
|
AENSv2.resolve(name, key)
|
||||||
|
|
||||||
|
stateful entrypoint resolve_contract(name : string, key : string) : option(C) =
|
||||||
|
AENSv2.resolve(name, key)
|
||||||
|
|
||||||
|
stateful entrypoint resolve_oracle(name : string, key : string) : option(oracle(int, int)) =
|
||||||
|
AENSv2.resolve(name, key)
|
||||||
|
|
||||||
|
stateful entrypoint resolve_oracle_query(name : string, key : string) : option(oracle_query(int, int)) =
|
||||||
|
AENSv2.resolve(name, key)
|
||||||
|
|
||||||
// Transactions
|
// Transactions
|
||||||
|
|
||||||
stateful entrypoint preclaim(addr : address, // Claim on behalf of this account (can be Contract.address)
|
stateful entrypoint preclaim(addr : address, // Claim on behalf of this account (can be Contract.address)
|
||||||
chash : hash) : unit = // Commitment hash
|
chash : hash) : unit = // Commitment hash
|
||||||
AENS.preclaim(addr, chash)
|
AENSv2.preclaim(addr, chash)
|
||||||
|
|
||||||
stateful entrypoint signedPreclaim(addr : address, // Claim on behalf of this account (can be Contract.address)
|
stateful entrypoint signedPreclaim(addr : address, // Claim on behalf of this account (can be Contract.address)
|
||||||
chash : hash, // Commitment hash
|
chash : hash, // Commitment hash
|
||||||
sign : signature) : unit = // Signed by addr (if not Contract.address)
|
sign : signature) : unit = // Signed by addr (if not Contract.address)
|
||||||
AENS.preclaim(addr, chash, signature = sign)
|
AENSv2.preclaim(addr, chash, signature = sign)
|
||||||
|
|
||||||
stateful entrypoint claim(addr : address,
|
stateful entrypoint claim(addr : address,
|
||||||
name : string,
|
name : string,
|
||||||
salt : int,
|
salt : int,
|
||||||
name_fee : int) : unit =
|
name_fee : int) : unit =
|
||||||
AENS.claim(addr, name, salt, name_fee)
|
AENSv2.claim(addr, name, salt, name_fee)
|
||||||
|
|
||||||
stateful entrypoint signedClaim(addr : address,
|
stateful entrypoint signedClaim(addr : address,
|
||||||
name : string,
|
name : string,
|
||||||
salt : int,
|
salt : int,
|
||||||
name_fee : int,
|
name_fee : int,
|
||||||
sign : signature) : unit =
|
sign : signature) : unit =
|
||||||
AENS.claim(addr, name, salt, name_fee, signature = sign)
|
AENSv2.claim(addr, name, salt, name_fee, signature = sign)
|
||||||
|
|
||||||
|
|
||||||
stateful entrypoint update(owner : address,
|
stateful entrypoint update(owner : address,
|
||||||
name : string,
|
name : string,
|
||||||
ttl : option(Chain.ttl),
|
ttl : option(Chain.ttl),
|
||||||
client_ttl : option(int),
|
client_ttl : option(int),
|
||||||
pointers : option(map(string, AENS.pointee))) : unit =
|
pointers : option(map(string, AENSv2.pointee))) : unit =
|
||||||
AENS.update(owner, name, ttl, client_ttl, pointers)
|
AENSv2.update(owner, name, ttl, client_ttl, pointers)
|
||||||
|
|
||||||
stateful entrypoint signedUpdate(owner : address,
|
stateful entrypoint signedUpdate(owner : address,
|
||||||
name : string,
|
name : string,
|
||||||
ttl : option(Chain.ttl),
|
ttl : option(Chain.ttl),
|
||||||
client_ttl : option(int),
|
client_ttl : option(int),
|
||||||
pointers : option(map(string, AENS.pointee)),
|
pointers : option(map(string, AENSv2.pointee)),
|
||||||
sign : signature) : unit =
|
sign : signature) : unit =
|
||||||
AENS.update(owner, name, ttl, client_ttl, pointers, signature = sign)
|
AENSv2.update(owner, name, ttl, client_ttl, pointers, signature = sign)
|
||||||
|
|
||||||
|
|
||||||
stateful entrypoint transfer(owner : address,
|
stateful entrypoint transfer(owner : address,
|
||||||
new_owner : address,
|
new_owner : address,
|
||||||
name : string) : unit =
|
name : string) : unit =
|
||||||
AENS.transfer(owner, new_owner, name)
|
AENSv2.transfer(owner, new_owner, name)
|
||||||
|
|
||||||
stateful entrypoint signedTransfer(owner : address,
|
stateful entrypoint signedTransfer(owner : address,
|
||||||
new_owner : address,
|
new_owner : address,
|
||||||
name : string,
|
name : string,
|
||||||
sign : signature) : unit =
|
sign : signature) : unit =
|
||||||
AENS.transfer(owner, new_owner, name, signature = sign)
|
AENSv2.transfer(owner, new_owner, name, signature = sign)
|
||||||
|
|
||||||
stateful entrypoint revoke(owner : address,
|
stateful entrypoint revoke(owner : address,
|
||||||
name : string) : unit =
|
name : string) : unit =
|
||||||
AENS.revoke(owner, name)
|
AENSv2.revoke(owner, name)
|
||||||
|
|
||||||
stateful entrypoint signedRevoke(owner : address,
|
stateful entrypoint signedRevoke(owner : address,
|
||||||
name : string,
|
name : string,
|
||||||
sign : signature) : unit =
|
sign : signature) : unit =
|
||||||
AENS.revoke(owner, name, signature = sign)
|
AENSv2.revoke(owner, name, signature = sign)
|
||||||
|
|||||||
@@ -1,17 +1,32 @@
|
|||||||
contract AENSUpdate =
|
include "Option.aes"
|
||||||
stateful entrypoint update_name(owner : address, name : string) =
|
include "String.aes"
|
||||||
let p1 : AENS.pointee = AENS.AccountPt(Call.caller)
|
include "AENSCompat.aes"
|
||||||
let p2 : AENS.pointee = AENS.OraclePt(Call.caller)
|
contract interface OldAENSContract =
|
||||||
let p3 : AENS.pointee = AENS.ContractPt(Call.caller)
|
entrypoint set : (string, string, AENS.pointee) => unit
|
||||||
let p4 : AENS.pointee = AENS.ChannelPt(Call.caller)
|
entrypoint lookup : (string, string) => AENS.pointee
|
||||||
AENS.update(owner, name, None, None,
|
|
||||||
Some({ ["account_pubkey"] = p1, ["oracle_pubkey"] = p2,
|
main contract AENSUpdate =
|
||||||
["contract_pubkey"] = p3, ["misc"] = p4 }))
|
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"])))
|
||||||
|
|
||||||
entrypoint get_ttl(name : string) =
|
entrypoint get_ttl(name : string) =
|
||||||
switch(AENS.lookup(name))
|
switch(AENSv2.lookup(name))
|
||||||
Some(AENS.Name(_, FixedTTL(ttl), _)) => ttl
|
Some(AENSv2.Name(_, FixedTTL(ttl), _)) => ttl
|
||||||
|
|
||||||
entrypoint expiry(o : oracle(int, int)) : int =
|
entrypoint expiry(o : oracle(int, int)) : int =
|
||||||
Oracle.expiry(o)
|
Oracle.expiry(o)
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
namespace Ns =
|
namespace Ns =
|
||||||
datatype d('a) = D | S(int) | M('a, list('a), int)
|
datatype d('a) = D | S(int) | M('a, list('a), int)
|
||||||
private function fff() = 123
|
private function fff() = 123
|
||||||
|
let const = 1
|
||||||
|
|
||||||
stateful entrypoint
|
stateful entrypoint
|
||||||
f (1, x) = (_) => x
|
f (1, x) = (_) => x
|
||||||
@@ -33,6 +34,8 @@ contract AllSyntax =
|
|||||||
|
|
||||||
type state = shakespeare(int)
|
type state = shakespeare(int)
|
||||||
|
|
||||||
|
let cc = "str"
|
||||||
|
|
||||||
entrypoint init() = {
|
entrypoint init() = {
|
||||||
johann = 1000,
|
johann = 1000,
|
||||||
wolfgang = -10,
|
wolfgang = -10,
|
||||||
@@ -80,3 +83,4 @@ contract AllSyntax =
|
|||||||
let sh : shakespeare(shakespeare(int)) =
|
let sh : shakespeare(shakespeare(int)) =
|
||||||
{wolfgang = state}
|
{wolfgang = state}
|
||||||
sh{wolfgang.wolfgang = sh.wolfgang} // comment
|
sh{wolfgang.wolfgang = sh.wolfgang} // comment
|
||||||
|
exit("hope you had fun reading this")
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ contract BadAENSresolve =
|
|||||||
type t('a) = option(list('a))
|
type t('a) = option(list('a))
|
||||||
|
|
||||||
function fail() : t(int) =
|
function fail() : t(int) =
|
||||||
AENS.resolve("foo.aet", "whatever")
|
AENSv2.resolve("foo.aet", "whatever")
|
||||||
|
|
||||||
entrypoint main_fun() = ()
|
entrypoint main_fun() = ()
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
contract BadAENSresolve =
|
||||||
|
using AENSv2
|
||||||
|
|
||||||
|
type t('a) = option(list('a))
|
||||||
|
|
||||||
|
function fail() : t(int) =
|
||||||
|
resolve("foo.aet", "whatever")
|
||||||
|
|
||||||
|
entrypoint main_fun() = ()
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// 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)
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
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,3 +6,5 @@ contract BytesToX =
|
|||||||
String.concat(Bytes.to_str(b), Bytes.to_str(#ffff))
|
String.concat(Bytes.to_str(b), Bytes.to_str(#ffff))
|
||||||
entrypoint to_str_big(b : bytes(65)) : string =
|
entrypoint to_str_big(b : bytes(65)) : string =
|
||||||
Bytes.to_str(b)
|
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)
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
contract F =
|
||||||
|
entrypoint g() = 1
|
||||||
|
|
||||||
|
main contract C =
|
||||||
|
entrypoint f() = F.g()
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
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,7 +2,8 @@
|
|||||||
|
|
||||||
contract ChainTest =
|
contract ChainTest =
|
||||||
|
|
||||||
record state = { last_bf : address }
|
record state = { last_bf : address
|
||||||
|
, nw_id : string }
|
||||||
|
|
||||||
function init() : state =
|
function init() : state =
|
||||||
{last_bf = Contract.address}
|
{last_bf = Contract.address}
|
||||||
@@ -11,3 +12,6 @@ contract ChainTest =
|
|||||||
|
|
||||||
function save_coinbase() =
|
function save_coinbase() =
|
||||||
put(state{last_bf = Chain.coinbase})
|
put(state{last_bf = Chain.coinbase})
|
||||||
|
|
||||||
|
function save_network_id() =
|
||||||
|
put(state{nw_id = Chain.network_id})
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
contract MissingInitFunction =
|
|
||||||
type state = int * int
|
|
||||||
|
|
||||||
@@ -1,16 +1,7 @@
|
|||||||
|
|
||||||
contract interface Remote =
|
|
||||||
entrypoint up_to : (int) => list(int)
|
|
||||||
entrypoint sum : (list(int)) => int
|
|
||||||
entrypoint some_string : () => string
|
|
||||||
entrypoint pair : (int, string) => int * string
|
|
||||||
entrypoint squares : (int) => list(int * int)
|
|
||||||
entrypoint filter_some : (list(option(int))) => list(int)
|
|
||||||
entrypoint all_some : (list(option(int))) => option(list(int))
|
|
||||||
|
|
||||||
contract ComplexTypes =
|
contract ComplexTypes =
|
||||||
|
|
||||||
record state = { worker : Remote }
|
record state = { worker : ComplexTypes }
|
||||||
|
|
||||||
entrypoint init(worker) = {worker = worker}
|
entrypoint init(worker) = {worker = worker}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
include "../dir2/baz.aes"
|
||||||
|
namespace D =
|
||||||
|
function g() = E.h()
|
||||||
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
namespace E =
|
||||||
|
function h() = 42
|
||||||
|
|
||||||
@@ -1,14 +1,9 @@
|
|||||||
|
|
||||||
// Testing primitives for accessing the block chain environment
|
// Testing primitives for accessing the block chain environment
|
||||||
contract interface Interface =
|
|
||||||
entrypoint contract_address : () => address
|
|
||||||
entrypoint call_origin : () => address
|
|
||||||
entrypoint call_caller : () => address
|
|
||||||
entrypoint call_value : () => int
|
|
||||||
|
|
||||||
contract Environment =
|
contract Environment =
|
||||||
|
|
||||||
record state = {remote : Interface}
|
record state = {remote : Environment}
|
||||||
|
|
||||||
entrypoint init(remote) = {remote = remote}
|
entrypoint init(remote) = {remote = remote}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ contract FunctionArguments =
|
|||||||
entrypoint read(a : answer(int)) =
|
entrypoint read(a : answer(int)) =
|
||||||
a.result
|
a.result
|
||||||
|
|
||||||
|
entrypoint any_bytes(b : bytes()) = b
|
||||||
|
|
||||||
entrypoint sjutton(b : bytes(17)) =
|
entrypoint sjutton(b : bytes(17)) =
|
||||||
b
|
b
|
||||||
|
|
||||||
@@ -57,3 +59,5 @@ contract FunctionArguments =
|
|||||||
entrypoint chain_ga_meta_tx(tx : Chain.ga_meta_tx) = true
|
entrypoint chain_ga_meta_tx(tx : Chain.ga_meta_tx) = true
|
||||||
entrypoint chain_paying_for_tx(tx : Chain.paying_for_tx) = true
|
entrypoint chain_paying_for_tx(tx : Chain.paying_for_tx) = true
|
||||||
entrypoint chain_base_tx(tx : Chain.base_tx) = true
|
entrypoint chain_base_tx(tx : Chain.base_tx) = true
|
||||||
|
|
||||||
|
entrypoint sig(sg : signature) = true
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
include "List.aes"
|
||||||
|
|
||||||
|
contract C =
|
||||||
|
entrypoint f() =
|
||||||
|
let ??? = true
|
||||||
|
let v = ???
|
||||||
|
let q = v == "str"
|
||||||
|
let xs = [1, 2, 3, 4]
|
||||||
|
switch (List.first(List.map(???, xs)))
|
||||||
|
Some(x) => x + 1
|
||||||
|
None => 0
|
||||||
|
|
||||||
|
function g() = ???
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
contract AliasToAliasToType =
|
||||||
|
type alias = int * int
|
||||||
|
type state = alias
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
contract AliasToType =
|
||||||
|
type state = int * int
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
contract AliasToAliasToUnit =
|
||||||
|
type alias = unit
|
||||||
|
type state = alias
|
||||||
|
|
||||||
|
contract AliasToUnit =
|
||||||
|
type state = unit
|
||||||
|
|
||||||
|
main contract ImplicitState =
|
||||||
|
type sometype = int
|
||||||
@@ -1,12 +1,5 @@
|
|||||||
|
|
||||||
contract NameClash =
|
contract NameClash =
|
||||||
|
|
||||||
entrypoint double_proto : () => int
|
|
||||||
entrypoint double_proto : () => int
|
|
||||||
|
|
||||||
entrypoint proto_and_def : int => int
|
|
||||||
entrypoint proto_and_def(n) = n + 1
|
|
||||||
|
|
||||||
entrypoint double_def(x) = x
|
entrypoint double_def(x) = x
|
||||||
entrypoint double_def(y) = 0
|
entrypoint double_def(y) = 0
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
contract PolymorphicAENSresolve =
|
contract PolymorphicAENSresolve =
|
||||||
|
|
||||||
function fail() : option('a) =
|
function fail() : option('a) =
|
||||||
AENS.resolve("foo.aet", "whatever")
|
AENSv2.resolve("foo.aet", "whatever")
|
||||||
|
|
||||||
entrypoint main_fun() = ()
|
entrypoint main_fun() = ()
|
||||||
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
contract interface I =
|
||||||
|
entrypoint f : () => int
|
||||||
|
|
||||||
|
contract C : I =
|
||||||
|
stateful entrypoint f() = 1
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
contract interface I =
|
||||||
|
entrypoint f : () => int
|
||||||
|
|
||||||
|
contract C : I =
|
||||||
|
entrypoint init() = ()
|
||||||
|
function f() = 1
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
contract interface Strokable =
|
||||||
|
entrypoint stroke : () => string
|
||||||
|
|
||||||
|
contract Cat : Strokable =
|
||||||
|
entrypoint stroke() = "Cat stroke"
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
contract interface II =
|
||||||
|
entrypoint f : () => unit
|
||||||
|
|
||||||
|
contract interface I : II =
|
||||||
|
entrypoint f : () => unit
|
||||||
|
entrypoint g : () => unit
|
||||||
|
|
||||||
|
contract C : I =
|
||||||
|
entrypoint f() = ()
|
||||||
|
entrypoint g() = ()
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
contract interface I0 =
|
||||||
|
entrypoint f : () => int
|
||||||
|
|
||||||
|
contract interface I1 : I0 =
|
||||||
|
entrypoint f : () => int
|
||||||
|
entrypoint something_else : () => int
|
||||||
|
|
||||||
|
main contract C =
|
||||||
|
entrypoint f(x : I1) = x.f() // Here we should know that x has f
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
contract interface X : Z =
|
||||||
|
entrypoint x : () => int
|
||||||
|
|
||||||
|
contract interface Y : X =
|
||||||
|
entrypoint y : () => int
|
||||||
|
|
||||||
|
contract interface Z : Y =
|
||||||
|
entrypoint z : () => int
|
||||||
|
|
||||||
|
contract C : Z =
|
||||||
|
entrypoint x() = 1
|
||||||
|
entrypoint y() = 1
|
||||||
|
entrypoint z() = 1
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
contract interface I =
|
||||||
|
entrypoint f : () => int
|
||||||
|
|
||||||
|
contract interface II : I =
|
||||||
|
entrypoint f : () => int
|
||||||
|
|
||||||
|
contract C : II =
|
||||||
|
entrypoint f() = 1
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
contract interface I1 =
|
||||||
|
entrypoint f : () => int
|
||||||
|
|
||||||
|
contract interface I2 : I1 =
|
||||||
|
entrypoint f : () => char
|
||||||
|
|
||||||
|
contract C : I2 =
|
||||||
|
entrypoint f() = 1
|
||||||
|
entrypoint f() = 'c'
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
contract interface I1 =
|
||||||
|
entrypoint f : () => int
|
||||||
|
|
||||||
|
contract interface I2 : I1 =
|
||||||
|
entrypoint f : () => int
|
||||||
|
|
||||||
|
contract C : I2 =
|
||||||
|
entrypoint f() = 1
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user