Compare commits
65 Commits
Author | SHA1 | Date | |
---|---|---|---|
dbab49936d | |||
![]() |
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 | ||
![]() |
7b6eba5319 | ||
![]() |
99bb3fe1fb | ||
![]() |
311bf49505 | ||
![]() |
0e3bcba07d | ||
![]() |
699d1f7ab8 | ||
![]() |
1a40a93157 | ||
![]() |
c078119bc4 | ||
![]() |
31fd8fe24f | ||
![]() |
9ad8e26e88 | ||
![]() |
5adeb6c93e | ||
![]() |
256df25af4 | ||
![]() |
83abfae32b | ||
![]() |
4ca90feea0 | ||
![]() |
09638daa90 | ||
![]() |
d59023a9f4 | ||
![]() |
34b52739fd | ||
![]() |
1c83287d45 | ||
![]() |
da92ddbd5d | ||
![]() |
c1c169273c | ||
![]() |
ad4c341a4a | ||
![]() |
f964fa89a1 | ||
![]() |
8d8d9c6b83 | ||
![]() |
c98ea25e8b |
@ -1,53 +0,0 @@
|
||||
version: 2.1
|
||||
|
||||
executors:
|
||||
aebuilder:
|
||||
docker:
|
||||
- image: aeternity/builder:bionic-otp24
|
||||
user: builder
|
||||
working_directory: ~/aesophia
|
||||
|
||||
jobs:
|
||||
verify_rebar_lock:
|
||||
executor: aebuilder
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Ensure lock file is up-to-date
|
||||
command: |
|
||||
./rebar3 upgrade
|
||||
git diff --quiet -- rebar.lock || (echo "rebar.lock is not up-to-date" && exit 1)
|
||||
build:
|
||||
executor: aebuilder
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- dialyzer-cache-v2-{{ .Branch }}-{{ .Revision }}
|
||||
- dialyzer-cache-v2-{{ .Branch }}-
|
||||
- dialyzer-cache-v2-
|
||||
- run:
|
||||
name: Build
|
||||
command: ./rebar3 compile
|
||||
- run:
|
||||
name: Static Analysis
|
||||
command: ./rebar3 dialyzer
|
||||
- run:
|
||||
name: Eunit
|
||||
command: ./rebar3 eunit
|
||||
- run:
|
||||
name: Common Tests
|
||||
command: ./rebar3 ct
|
||||
- save_cache:
|
||||
key: dialyzer-cache-v2-{{ .Branch }}-{{ .Revision }}
|
||||
paths:
|
||||
- _build/default/rebar3_20.3.8_plt
|
||||
- store_artifacts:
|
||||
path: _build/test/logs
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
build_test:
|
||||
jobs:
|
||||
- build
|
||||
- verify_rebar_lock
|
14
.gitea/workflows/test.yaml
Normal file
14
.gitea/workflows/test.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
name: Sophia Tests
|
||||
run-name: ${{ gitea.actor }} testing Sophia
|
||||
on: [push, workflow_dispatch]
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: linux_amd64
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v4
|
||||
- name: test
|
||||
run: |
|
||||
. /home/act_runner/.erts/27.2.1/activate
|
||||
./rebar3 eunit
|
21
.github/workflows/docs-develop.yml
vendored
21
.github/workflows/docs-develop.yml
vendored
@ -1,21 +0,0 @@
|
||||
name: Publish development docs
|
||||
on:
|
||||
push:
|
||||
branches: ['master']
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.8
|
||||
- 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.name "GitHub Action"
|
||||
- run: |
|
||||
cd .docssite
|
||||
mike deploy --push master
|
22
.github/workflows/docs-release.yml
vendored
22
.github/workflows/docs-release.yml
vendored
@ -1,22 +0,0 @@
|
||||
name: Publish release docs
|
||||
on:
|
||||
release:
|
||||
types: [released]
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.8
|
||||
- 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.name "GitHub Action"
|
||||
- run: echo "RELEASE_VERSION=${GITHUB_REF:10}" >> $GITHUB_ENV
|
||||
- run: |
|
||||
cd .docssite
|
||||
mike deploy --push --update-aliases $RELEASE_VERSION latest
|
5
.github/workflows/requirements.txt
vendored
5
.github/workflows/requirements.txt
vendored
@ -1,5 +0,0 @@
|
||||
mkdocs==1.2.4
|
||||
mkdocs-simple-hooks==0.1.5
|
||||
mkdocs-material==7.3.6
|
||||
mike==1.1.2
|
||||
pygments==2.12.0
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -18,9 +18,10 @@ _build
|
||||
rebar3.crashdump
|
||||
*.erl~
|
||||
*.aes~
|
||||
aesophia
|
||||
sophia
|
||||
.qcci
|
||||
current_counterexample.eqc
|
||||
test/contracts/test.aes
|
||||
__pycache__
|
||||
.docssite/docs/*.md
|
||||
.vscode
|
||||
|
121
CHANGELOG.md
121
CHANGELOG.md
@ -6,9 +6,119 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- Added a check for number of type variables in a type signature; it is serialized using 8 bits,
|
||||
so the upper limit is 256.
|
||||
### Changed
|
||||
### Removed
|
||||
|
||||
## [9.0.0]
|
||||
### Changed
|
||||
- stdlib dir discovery now works by finding a relative path from the loaded Sophia installation
|
||||
###Removed
|
||||
- Oracles
|
||||
|
||||
## [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).
|
||||
@ -352,7 +462,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Simplify calldata creation - instead of passing a compiled contract, simply
|
||||
pass a (stubbed) contract string.
|
||||
|
||||
[Unreleased]: https://github.com/aeternity/aesophia/compare/v7.0.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.0.2]: https://github.com/aeternity/aesophia/compare/v6.0.1...v6.0.2
|
||||
|
40
CONTRIBUTING.md
Normal file
40
CONTRIBUTING.md
Normal file
@ -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)
|
1
Emakefile
Normal file
1
Emakefile
Normal file
@ -0,0 +1 @@
|
||||
{"src/*", [debug_info, {i, "include/"}, {outdir, "ebin/"}]}.
|
1
LICENSE
1
LICENSE
@ -1,5 +1,6 @@
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2025, QPQ AG
|
||||
Copyright (c) 2017, æternity developers
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
|
17
README.md
17
README.md
@ -1,11 +1,11 @@
|
||||
# aesophia
|
||||
# The Sophia smart contract language
|
||||
|
||||
This is the __sophia__ compiler for the æternity system which compiles contracts written in __sophia__ to [FATE](https://github.com/aeternity/protocol/blob/master/contracts/fate.md) instructions.
|
||||
This is the __sophia__ compiler which compiles contracts written in __sophia__ to [FATE](https://git.qpq.swiss/QPQ-AG/protocol/src/branch/master/contracts/fate.md) instructions.
|
||||
|
||||
The compiler is currently being used three places
|
||||
- [The command line compiler](https://github.com/aeternity/aesophia_cli)
|
||||
- [The HTTP compiler](https://github.com/aeternity/aesophia_http)
|
||||
- In [æternity node](https://github.com/aeternity/aeternity) tests
|
||||
- [The command line compiler](https://git.qpq.swiss/QPQ-AG/sophia_cli)
|
||||
- [Desktop wallet](https://git.qpq.swiss/QPQ-AG/GajuDesk)
|
||||
- In the [Gajumaru core node](https://git.qpq.swiss/QPQ-AG/gajumaru) tests
|
||||
|
||||
## Documentation
|
||||
|
||||
@ -14,8 +14,9 @@ The compiler is currently being used three places
|
||||
* [Features](docs/sophia_features.md)
|
||||
* [Standard library](docs/sophia_stdlib.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://git.qpq.swiss/QPQ-AG/protocol/src/branch/master/contracts) of the Gajumaru blockchain specification.
|
||||
|
||||
## Versioning
|
||||
|
||||
@ -30,5 +31,5 @@ Versioning should follow the [semantic versioning](https://semver.org/spec/v2.0.
|
||||
|
||||
The basic modules for interfacing the compiler:
|
||||
|
||||
* [aeso_compiler: the Sophia compiler](docs/aeso_compiler.md)
|
||||
* [aeso_aci: the ACI interface](docs/aeso_aci.md)
|
||||
* [so_compiler: the Sophia compiler](docs/so_compiler.md)
|
||||
* [so_aci: the ACI interface](docs/so_aci.md)
|
||||
|
@ -1,12 +1,9 @@
|
||||
# Introduction
|
||||
Sophia is a functional language designed for smart contract development. It is strongly typed and has
|
||||
restricted mutable state.
|
||||
Sophia is a functional language designed for smart contract development.
|
||||
It is strongly typed and has restricted mutable state.
|
||||
|
||||
Sophia is customized for smart contracts, which can be published
|
||||
to a blockchain. Thus some features of conventional
|
||||
languages, such as floating point arithmetic, are not present in Sophia, and
|
||||
some [æternity blockchain](https://aeternity.com) specific primitives, constructions and types have been added.
|
||||
Sophia is customized for smart contracts, which can be published to a blockchain.
|
||||
Thus some features of conventional languages (such as floating point arithmetic) are not present in Sophia,
|
||||
and some blockchain specific primitives, constructions and types have been added.
|
||||
|
||||
!!! Note
|
||||
- For rapid prototyping of smart contracts check out [AEstudio](https://studio.aepps.com/)!
|
||||
- For playing around and diving deeper into the language itself check out the [REPL](https://repl.aeternity.io/)!
|
||||
The file extension used for Sophia source files is ".aes", reflecting Sophia's Aeternity heritage.
|
||||
|
@ -1,8 +1,8 @@
|
||||
# aeso_aci
|
||||
# so_aci
|
||||
|
||||
### Module
|
||||
|
||||
### aeso_aci
|
||||
### so_aci
|
||||
|
||||
The ACI interface encoder and decoder.
|
||||
|
||||
@ -67,7 +67,7 @@ generates the following JSON structure representing the contract interface:
|
||||
}
|
||||
]
|
||||
},
|
||||
"type_defs": [
|
||||
"typedefs": [
|
||||
{
|
||||
"name": "answers",
|
||||
"typedef": {
|
||||
@ -123,7 +123,7 @@ be included inside another contract.
|
||||
``` erlang
|
||||
1> {ok,Contract} = file:read_file("aci_test.aes").
|
||||
{ok,<<"contract Answers =\n record state = { a : answers }\n type answers() = map(string, int)\n\n stateful function"...>>}
|
||||
2> {ok,JsonACI} = aeso_aci:contract_interface(json, Contract).
|
||||
2> {ok,JsonACI} = so_aci:contract_interface(json, Contract).
|
||||
{ok,[#{contract =>
|
||||
#{functions =>
|
||||
[#{arguments => [],name => <<"init">>,
|
||||
@ -138,13 +138,13 @@ be included inside another contract.
|
||||
state =>
|
||||
#{record =>
|
||||
[#{name => <<"a">>,type => <<"Answers.answers">>}]},
|
||||
type_defs =>
|
||||
typedefs =>
|
||||
[#{name => <<"answers">>,
|
||||
typedef => #{<<"map">> => [<<"string">>,<<"int">>]},
|
||||
vars => []}]}}]}
|
||||
3> file:write_file("aci_test.aci", jsx:encode(JsonACI)).
|
||||
ok
|
||||
4> {ok,InterfaceStub} = aeso_aci:render_aci_json(JsonACI).
|
||||
4> {ok,InterfaceStub} = so_aci:render_aci_json(JsonACI).
|
||||
{ok,<<"contract Answers =\n record state = {a : Answers.answers}\n type answers = map(string, int)\n function init "...>>}
|
||||
5> file:write_file("aci_test.include", InterfaceStub).
|
||||
ok
|
@ -1,8 +1,8 @@
|
||||
# aeso_compiler
|
||||
# so_compiler
|
||||
|
||||
### Module
|
||||
|
||||
### aeso_compiler
|
||||
### so_compiler
|
||||
|
||||
The Sophia compiler
|
||||
|
||||
@ -51,6 +51,34 @@ The **pp_** options all print to standard output the following:
|
||||
|
||||
`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
|
||||
|
||||
Types
|
@ -65,9 +65,3 @@ contract FundMe =
|
||||
amount = state.contributions[to]})
|
||||
put(state{ contributions @ c = Map.delete(to, c) })
|
||||
```
|
||||
|
||||
## Repositories
|
||||
This is a list with repositories that include smart contracts written in Sophia:
|
||||
|
||||
- [aepp-sophia-examples](https://github.com/aeternity/aepp-sophia-examples)
|
||||
- A repository that contains lots of different examples. The functionality of these examples is - to some extent - also covered by tests written in JavaScript.
|
@ -84,7 +84,7 @@ the return value of the call.
|
||||
|
||||
```sophia
|
||||
contract interface VotingType =
|
||||
entrypoint : vote : string => unit
|
||||
entrypoint vote : string => unit
|
||||
|
||||
contract Voter =
|
||||
entrypoint tryVote(v : VotingType, alt : string) =
|
||||
@ -191,9 +191,20 @@ contract interface X : Z =
|
||||
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)),
|
||||
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.
|
||||
|
||||
@ -213,11 +224,11 @@ A good example of where it matters can be pictured by subtyping of function type
|
||||
```sophia
|
||||
contract interface Animal =
|
||||
entrypoint age : () => int
|
||||
|
||||
|
||||
contract Dog : Animal =
|
||||
entrypoint age() = // ...
|
||||
entrypoint woof() = "woof"
|
||||
|
||||
|
||||
contract Cat : Animal =
|
||||
entrypoint age() = // ...
|
||||
entrypoint meow() = "meow"
|
||||
@ -245,10 +256,10 @@ 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
|
||||
- `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:
|
||||
|
||||
@ -263,12 +274,6 @@ counterpart in `Args1`.
|
||||
- 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
|
||||
@ -284,6 +289,11 @@ of `A`.
|
||||
- When a user-defined type `t('a)` is invariant in `'a`, then `t(A)` can never be
|
||||
a subtype of `t(B)`.
|
||||
|
||||
#### Type variable limitation
|
||||
|
||||
Because of how FATE represents types as values there is a fixed upper limit (256)
|
||||
of type variables that can be used in a single type signature.
|
||||
|
||||
## Mutable state
|
||||
|
||||
Sophia does not have arbitrary mutable state, but only a limited form of state
|
||||
@ -324,10 +334,6 @@ Without the `stateful` annotation the compiler does not allow the call to
|
||||
* Use a stateful primitive function. These are
|
||||
- `put`
|
||||
- `Chain.spend`
|
||||
- `Oracle.register`
|
||||
- `Oracle.query`
|
||||
- `Oracle.respond`
|
||||
- `Oracle.extend`
|
||||
- `AENS.preclaim`
|
||||
- `AENS.claim`
|
||||
- `AENS.transfer`
|
||||
@ -482,6 +488,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,
|
||||
so even cyclic includes should be working without any special tinkering.
|
||||
|
||||
### Include files using relative paths
|
||||
|
||||
When including code from another file using the `include` statement, the path
|
||||
is relative to _the file that includes it_. Consider the following file tree:
|
||||
```
|
||||
c1.aes
|
||||
c3.aes
|
||||
dir1/c2.aes
|
||||
dir1/c3.aes
|
||||
```
|
||||
|
||||
If `c1.aes` contains `include "c3.aes"` it will include the top level `c3.aes`,
|
||||
while if `c2.aes` contained the same line it would as expected include
|
||||
`dir1/c3.aes`.
|
||||
|
||||
Note: Prior to v7.5.0, it would consider the include path relative to _the main
|
||||
contract file_ (or any explicitly set include path).
|
||||
|
||||
## Standard library
|
||||
|
||||
Sophia offers [standard library](sophia_stdlib.md) which exposes some
|
||||
@ -522,13 +546,12 @@ Sophia has the following types:
|
||||
| hash | A 32-byte hash - equivalent to `bytes(32)` | |
|
||||
| signature | A signature - equivalent to `bytes(64)` | |
|
||||
| Chain.ttl | Time-to-live (fixed height or relative to current block) | ```FixedTTL(1050)``` ```RelativeTTL(50)``` |
|
||||
| oracle('a, 'b) | And oracle answering questions of type 'a with answers of type 'b | ```Oracle.register(acct, qfee, ttl)``` |
|
||||
| oracle_query('a, 'b) | A specific oracle query | ```Oracle.query(o, q, qfee, qttl, rttl)``` |
|
||||
| contract | A user defined, typed, contract address | ```function call_remote(r : RemoteContract) = r.fun()``` |
|
||||
|
||||
## Literals
|
||||
| Type | Constant/Literal example(s) |
|
||||
| ---------- | ------------------------------- |
|
||||
| unit | () |
|
||||
| int | `-1`, `2425`, `4598275923475723498573485768` |
|
||||
| address | `ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt` |
|
||||
| bool | `true`, `false` |
|
||||
@ -543,12 +566,49 @@ Sophia has the following types:
|
||||
| state | `state{ owner = Call.origin, magic_key = #a298105f }` |
|
||||
| event | `EventX(0, "Hello")` |
|
||||
| hash | `#000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f` |
|
||||
| signature | `#000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f` |
|
||||
| signature | `sg_MhibzTP1wWzGCTjtPFr1TiPqRJrrJqw7auvEuF5i3FdoALWqXLBDY6xxRRNUSPHK3EQTnTzF12EyspkxrSMxVHKsZeSMj`, `#000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f` |
|
||||
| Chain.ttl | `FixedTTL(1050)`, `RelativeTTL(50)` |
|
||||
| oracle('a, 'b) | `ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5` |
|
||||
| oracle_query('a, 'b) | `oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY` |
|
||||
| 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
|
||||
|
||||
Sophia integers (`int`) are represented by arbitrary-sized signed words and support the following
|
||||
@ -560,14 +620,28 @@ arithmetic operations:
|
||||
- remainder (`x mod y`), satisfying `y * (x / y) + x mod y == x` for non-zero `y`
|
||||
- exponentiation (`x ^ y`)
|
||||
|
||||
All operations are *safe* with respect to overflow and underflow.
|
||||
All operations are *safe* with respect to overflow and underflow.
|
||||
The division and modulo operations throw an arithmetic error if the
|
||||
right-hand operand is zero.
|
||||
|
||||
Sophia arbitrary-sized integers (FATE) also supports the following bitwise operations:
|
||||
- bitwise and (`x band y`)
|
||||
- bitwise or (`x bor y`)
|
||||
- bitwise xor (`x bxor y`)
|
||||
- bitwise not (`bnot x`)
|
||||
- arithmetic bitshift left (`x << n`)
|
||||
- arithmetic bitshift right (`x >> n`)
|
||||
|
||||
Note: Arithmetic bitshift treats the number as a signed integer (in 2s
|
||||
complement), and "retains" the topmost bit. I.e. shifting in zeros if the
|
||||
topmost bit was 0, and ones if it was one.
|
||||
|
||||
## Bit fields
|
||||
|
||||
Sophia integers do not support bit arithmetic. Instead there is a separate
|
||||
type `bits`. See the standard library [documentation](sophia_stdlib.md#bits).
|
||||
Originally Sophia integers did not support bit arithmetic. Instead we used a
|
||||
separate type `bits` (see the standard library
|
||||
[documentation](sophia_stdlib.md#bits)) - it is still provided as an
|
||||
alternative to bit arithmetic.
|
||||
|
||||
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).
|
||||
@ -803,85 +877,19 @@ wrapping a transaction.) The transaction and the transaction hash is available i
|
||||
`Auth.tx` and `Auth.tx_hash` respectively, they are *only* available during authentication if invoked by a
|
||||
normal contract call they return `None`.
|
||||
|
||||
## Oracle interface
|
||||
You can attach an oracle to the current contract and you can interact with oracles
|
||||
through the Oracle interface.
|
||||
|
||||
For a full description of how Oracle works see
|
||||
[Oracles](https://github.com/aeternity/protocol/blob/master/oracles/oracles.md#oracles).
|
||||
For a functionality documentation refer to the [standard library](sophia_stdlib.md#oracle).
|
||||
|
||||
### Example
|
||||
|
||||
Example for an oracle answering questions of type `string` with answers of type `int`:
|
||||
```sophia
|
||||
contract Oracles =
|
||||
|
||||
stateful entrypoint registerOracle(acct : address,
|
||||
sign : signature, // Signed network id + oracle address + contract address
|
||||
qfee : int,
|
||||
ttl : Chain.ttl) : oracle(string, int) =
|
||||
Oracle.register(acct, signature = sign, qfee, ttl)
|
||||
|
||||
entrypoint queryFee(o : oracle(string, int)) : int =
|
||||
Oracle.query_fee(o)
|
||||
|
||||
payable stateful entrypoint createQuery(o : oracle_query(string, int),
|
||||
q : string,
|
||||
qfee : int,
|
||||
qttl : Chain.ttl,
|
||||
rttl : int) : oracle_query(string, int) =
|
||||
require(qfee =< Call.value, "insufficient value for qfee")
|
||||
Oracle.query(o, q, qfee, qttl, RelativeTTL(rttl))
|
||||
|
||||
stateful entrypoint extendOracle(o : oracle(string, int),
|
||||
ttl : Chain.ttl) : unit =
|
||||
Oracle.extend(o, ttl)
|
||||
|
||||
stateful entrypoint signExtendOracle(o : oracle(string, int),
|
||||
sign : signature, // Signed network id + oracle address + contract address
|
||||
ttl : Chain.ttl) : unit =
|
||||
Oracle.extend(o, signature = sign, ttl)
|
||||
|
||||
stateful entrypoint respond(o : oracle(string, int),
|
||||
q : oracle_query(string, int),
|
||||
sign : signature, // Signed network id + oracle query id + contract address
|
||||
r : int) =
|
||||
Oracle.respond(o, q, signature = sign, r)
|
||||
|
||||
entrypoint getQuestion(o : oracle(string, int),
|
||||
q : oracle_query(string, int)) : string =
|
||||
Oracle.get_question(o, q)
|
||||
|
||||
entrypoint hasAnswer(o : oracle(string, int),
|
||||
q : oracle_query(string, int)) =
|
||||
switch(Oracle.get_answer(o, q))
|
||||
None => false
|
||||
Some(_) => true
|
||||
|
||||
entrypoint getAnswer(o : oracle(string, int),
|
||||
q : oracle_query(string, int)) : option(int) =
|
||||
Oracle.get_answer(o, q)
|
||||
```
|
||||
|
||||
### Sanity checks
|
||||
|
||||
When an Oracle literal is passed to a contract, no deep checks are performed.
|
||||
For extra safety [Oracle.check](sophia_stdlib.md#check) and [Oracle.check_query](sophia_stdlib.md#check_query)
|
||||
functions are provided.
|
||||
|
||||
## AENS interface
|
||||
|
||||
Contracts can interact with the
|
||||
[æternity naming system](https://github.com/aeternity/protocol/blob/master/AENS.md).
|
||||
For this purpose the [AENS](sophia_stdlib.md#aens) library was exposed.
|
||||
Contracts can interact with the [æternity naming system](https://git.qpq.swiss/QPQ-AG/protocol/src/branch/master/AENS.md). For this
|
||||
purpose the [AENS](sophia_stdlib.md#aens) and later the
|
||||
[AENSv2](sophia_stdlib.md#aensv2) library was exposed.
|
||||
|
||||
### Example
|
||||
|
||||
In this example we assume that the name `name` already exists, and is owned by
|
||||
an account with address `addr`. In order to allow a contract `ct` to handle
|
||||
`name` the account holder needs to create a
|
||||
[signature](#delegation-signature) `sig` of `addr | name.hash | ct.address`.
|
||||
`name` the account holder needs to create a [delegation
|
||||
signature](#delegation-signature) `sig` from the name owner address `addr`, the
|
||||
name hash and the contract address.
|
||||
|
||||
Armed with this information we can for example write a function that extends
|
||||
the name if it expires within 1000 blocks:
|
||||
@ -919,10 +927,9 @@ automatically removed so they will not appear in the pointers map.
|
||||
|
||||
## Events
|
||||
|
||||
Sophia contracts log structured messages to an event log in the resulting
|
||||
blockchain transaction. The event log is quite similar to [Events in
|
||||
Solidity](https://solidity.readthedocs.io/en/v0.4.24/contracts.html#events).
|
||||
Events are further discussed in the [protocol](https://github.com/aeternity/protocol/blob/master/contracts/events.md).
|
||||
Sophia contracts log structured messages to an event log in the resulting blockchain transaction.
|
||||
The event log is quite similar to [Events in Solidity](https://solidity.readthedocs.io/en/v0.4.24/contracts.html#events).
|
||||
Events are further discussed in the [protocol](https://git.qpq.swiss/QPQ-AG/protocol/src/branch/master/contracts/events.md).
|
||||
|
||||
|
||||
To use events a contract must declare a datatype `event`, and events are then
|
||||
@ -942,8 +949,6 @@ field is indexed if it fits in a 32-byte word, i.e.
|
||||
- `int`
|
||||
- `bits`
|
||||
- `address`
|
||||
- `oracle(_, _)`
|
||||
- `oracle_query(_, _)`
|
||||
- contract types
|
||||
- `bytes(n)` for `n` ≤ 32, in particular `hash`
|
||||
|
||||
@ -1023,8 +1028,15 @@ however is in the gas consumption — while `abort` returns unused gas, a call t
|
||||
|
||||
## Delegation signature
|
||||
|
||||
Some chain operations (`Oracle.<operation>` and `AENS.<operation>`) have an
|
||||
Some chain operations (`AENSv2.<operation>`) have an
|
||||
optional delegation signature. This is typically used when a user/accounts
|
||||
would like to allow a contract to act on it's behalf. 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.).
|
||||
would like to allow a contract to act on it's behalf.
|
||||
|
||||
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`
|
||||
|
||||
See [Serialized signature data](https://git.qpq.swiss/QPQ-AG/protocol/src/branch/master/serializations.md)
|
||||
for the exact structure used.
|
||||
|
@ -14,6 +14,7 @@ The out-of-the-box namespaces are:
|
||||
|
||||
- [Address](#address)
|
||||
- [AENS](#aens)
|
||||
- [AENSv2](#aensv2)
|
||||
- [Auth](#auth)
|
||||
- [Bits](#bits)
|
||||
- [Bytes](#bytes)
|
||||
@ -24,13 +25,13 @@ The out-of-the-box namespaces are:
|
||||
- [Crypto](#crypto)
|
||||
- [Int](#int)
|
||||
- [Map](#map)
|
||||
- [Oracle](#oracle)
|
||||
|
||||
The following ones need to be included as regular files with `.aes` suffix, for example
|
||||
```
|
||||
include "List.aes"
|
||||
```
|
||||
|
||||
- [AENSCompat](#aenscompat)
|
||||
- [Bitwise](#bitwise)
|
||||
- [BLS12_381](#bls12_381)
|
||||
- [Func](#func)
|
||||
@ -55,6 +56,12 @@ Address.to_str(a : address) : string
|
||||
|
||||
Base58 encoded string
|
||||
|
||||
#### to_bytes
|
||||
```
|
||||
Address.to_bytes(a : address) : bytes(32)
|
||||
```
|
||||
|
||||
The binary representation of the address.
|
||||
|
||||
#### is_contract
|
||||
```
|
||||
@ -64,14 +71,6 @@ Address.is_contract(a : address) : bool
|
||||
Is the address a contract
|
||||
|
||||
|
||||
#### is_oracle
|
||||
```
|
||||
Address.is_oracle(a : address) : bool
|
||||
```
|
||||
|
||||
Is the address a registered oracle
|
||||
|
||||
|
||||
#### is_payable
|
||||
```
|
||||
Address.is_payable(a : address) : bool
|
||||
@ -90,13 +89,10 @@ Cast address to contract type C (where `C` is a contract)
|
||||
|
||||
### AENS
|
||||
|
||||
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.
|
||||
The old AENS namespace, kept in the compiler to be able to interact with
|
||||
contracts from before Ceres, compiled using sophia compiler version 7.x and
|
||||
earlier. Used in [AENSCompat](#aenscompat) when converting between old and new
|
||||
pointers.
|
||||
|
||||
#### Types
|
||||
|
||||
@ -109,16 +105,48 @@ datatype name = Name(address, Chain.ttl, map(string, AENS.pointee))
|
||||
##### pointee
|
||||
|
||||
```
|
||||
datatype pointee = AccountPt(address) | OraclePt(address)
|
||||
| ContractPt(address) | ChannelPt(address)
|
||||
datatype pointee = AccountPt(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://git.qpq.swiss/QPQ-AG/protocol/src/branch/master/consensus/README.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)
|
||||
| ContractPt(address)
|
||||
| ChannelPt(address)
|
||||
| DataPt(bytes())
|
||||
```
|
||||
|
||||
Note: on-chain there is a maximum length enforced for `DataPt`, it is 1024 bytes.
|
||||
Sophia itself does _not_ check for this.
|
||||
|
||||
#### Functions
|
||||
|
||||
##### resolve
|
||||
```
|
||||
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
|
||||
@ -129,74 +157,106 @@ type checked against this type at run time.
|
||||
|
||||
##### 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
|
||||
`pointees` for the name. Note: the expiry of the name is always a fixed TTL.
|
||||
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
|
||||
```
|
||||
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
|
||||
`network id` + `owner address` + `Contract.address` (concatenated as byte arrays).
|
||||
The [signature](./sophia_features.md#delegation-signature) should be a
|
||||
serialized structure containing `network id`, `owner address`, and
|
||||
`Contract.address`.
|
||||
|
||||
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
|
||||
```
|
||||
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
|
||||
`network id` + `owner address` + `name_hash` + `Contract.address`
|
||||
(concatenated as byte arrays)
|
||||
using the private key of the `owner` account for signing.
|
||||
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`.
|
||||
|
||||
|
||||
##### 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.
|
||||
|
||||
The [signature](./sophia_features.md#delegation-signature) should be over
|
||||
`network id` + `owner address` + `name_hash` + `Contract.address`
|
||||
(concatenated as byte arrays)
|
||||
using the private key of the `owner` account for signing.
|
||||
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`.
|
||||
|
||||
|
||||
##### 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.
|
||||
|
||||
The [signature](./sophia_features.md#delegation-signature) should be over
|
||||
`network id` + `owner address` + `name_hash` + `Contract.address`
|
||||
(concatenated as byte arrays)
|
||||
using the private key of the `owner` account for signing.
|
||||
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`.
|
||||
|
||||
|
||||
##### update
|
||||
```
|
||||
AENS.update(owner : address, name : string, expiry : option(Chain.ttl), client_ttl : option(int),
|
||||
new_ptrs : map(string, AENS.pointee), <signature : signature>) : unit
|
||||
AENSv2.update(owner : address, name : string, expiry : option(Chain.ttl), client_ttl : option(int),
|
||||
new_ptrs : option(map(string, AENSv2.pointee)), <signature : signature>) : unit
|
||||
```
|
||||
|
||||
Updates the name. If the optional parameters are set to `None` that parameter
|
||||
will not be updated, for example if `None` is passed as `expiry` the expiry
|
||||
block of the name is not changed.
|
||||
|
||||
Note: Changed to consume `AENSv2.pointee` in v8.0 (Ceres protocol upgrade).
|
||||
|
||||
The [signature](./sophia_features.md#delegation-signature) should be a
|
||||
serialized structure containing `network id`, `owner address`, and
|
||||
`Contract.address`. Using the private key of `owner address` for signing.
|
||||
|
||||
From Ceres (i.e. FATE VM version 3) the
|
||||
[signature](./sophia_features.md#delegation-signature) can also be generic
|
||||
(allowing _all_, existing and future, names to be delegated with one
|
||||
signature), i.e. containing `network id`, `owner address`, `name_hash`, and
|
||||
`Contract.address`.
|
||||
|
||||
### Auth
|
||||
|
||||
@ -220,7 +280,6 @@ namespace Chain =
|
||||
datatype ga_meta_tx = GAMetaTx(address, int)
|
||||
datatype paying_for_tx = PayingForTx(address, int)
|
||||
datatype base_tx = SpendTx(address, int, string)
|
||||
| OracleRegisterTx | OracleQueryTx | OracleResponseTx | OracleExtendTx
|
||||
| NamePreclaimTx | NameClaimTx(hash) | NameUpdateTx(string)
|
||||
| NameRevokeTx(hash) | NameTransferTx(address, string)
|
||||
| ChannelCreateTx(address) | ChannelDepositTx(address, int) | ChannelWithdrawTx(address, int) |
|
||||
@ -236,7 +295,10 @@ namespace Chain =
|
||||
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://git.qpq.swiss/QPQ-AG/protocol/src/branch/master/serializations.md)
|
||||
specification for details.
|
||||
|
||||
|
||||
### Bits
|
||||
@ -315,7 +377,7 @@ Each bit is true if and only if it was 1 in `a` and 0 in `b`
|
||||
|
||||
### Bytes
|
||||
|
||||
#### to_int
|
||||
#### to\_int
|
||||
```
|
||||
Bytes.to_int(b : bytes(n)) : int
|
||||
```
|
||||
@ -323,7 +385,7 @@ Bytes.to_int(b : bytes(n)) : int
|
||||
Interprets the byte array as a big endian integer
|
||||
|
||||
|
||||
#### to_str
|
||||
#### to\_str
|
||||
```
|
||||
Bytes.to_str(b : bytes(n)) : string
|
||||
```
|
||||
@ -336,7 +398,8 @@ Returns the hexadecimal representation of the byte array
|
||||
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
|
||||
@ -346,6 +409,38 @@ Bytes.split(a : bytes(m + n)) : bytes(m) * bytes(n)
|
||||
|
||||
Splits a byte array at given index
|
||||
|
||||
#### split\_any
|
||||
```
|
||||
Bytes.split_any(a : bytes(), at : int) : option(bytes() * bytes(n))
|
||||
```
|
||||
|
||||
Splits an arbitrary size byte array at index `at`. If `at` is positive split
|
||||
from the beginning of the array, if `at` is negative, split `abs(at)` from the
|
||||
_end_ of the array. If the array is shorter than `abs(at)` then `None` is
|
||||
returned.
|
||||
|
||||
#### to\_fixed\_size
|
||||
```
|
||||
Bytes.to_fixed_size(a : bytes()) : option(bytes(n))
|
||||
```
|
||||
|
||||
Converts an arbitrary size byte array to a fix size byte array. If `a` is
|
||||
not `n` bytes, `None` is returned.
|
||||
|
||||
#### to\_any\_size
|
||||
```
|
||||
Bytes.to_any_size(a : bytes(n)) : bytes()
|
||||
```
|
||||
|
||||
Converts a fixed size byte array to an arbitrary size byte array. This is a
|
||||
no-op at run-time, and only used during type checking.
|
||||
|
||||
#### size
|
||||
```
|
||||
Bytes.size(a : bytes()) : int
|
||||
```
|
||||
|
||||
Computes the lenght/size of a byte array.
|
||||
|
||||
### Call
|
||||
|
||||
@ -381,6 +476,12 @@ Call.gas_price : int
|
||||
|
||||
The gas price of the current call.
|
||||
|
||||
#### mulmod
|
||||
```
|
||||
Int.mulmod : (a : int, b : int, q : int) : int
|
||||
```
|
||||
|
||||
Combined multiplication and modulus, returns `(a * b) mod q`.
|
||||
|
||||
#### fee
|
||||
```
|
||||
@ -428,7 +529,6 @@ datatype paying_for_tx = PayingForTx(address, int)
|
||||
##### base_tx
|
||||
```
|
||||
datatype base_tx = SpendTx(address, int, string)
|
||||
| OracleRegisterTx | OracleQueryTx | OracleResponseTx | OracleExtendTx
|
||||
| NamePreclaimTx | NameClaimTx(hash) | NameUpdateTx(string)
|
||||
| NameRevokeTx(hash) | NameTransferTx(address, string)
|
||||
| ChannelCreateTx(address) | ChannelDepositTx(address, int) | ChannelWithdrawTx(address, int) |
|
||||
@ -469,39 +569,6 @@ Chain.block_height : int"
|
||||
|
||||
The height of the current block (i.e. the block in which the current call will be included).
|
||||
|
||||
|
||||
##### coinbase
|
||||
```
|
||||
Chain.coinbase : address
|
||||
```
|
||||
|
||||
The address of the account that mined the current block.
|
||||
|
||||
|
||||
##### timestamp
|
||||
```
|
||||
Chain.timestamp : int
|
||||
```
|
||||
|
||||
The timestamp of the current block.
|
||||
|
||||
|
||||
##### difficulty
|
||||
```
|
||||
Chain.difficulty : int
|
||||
```
|
||||
|
||||
The difficulty of the current block.
|
||||
|
||||
|
||||
##### gas
|
||||
```
|
||||
Chain.gas_limit : int
|
||||
```
|
||||
|
||||
The gas limit of the current block.
|
||||
|
||||
|
||||
##### bytecode_hash
|
||||
```
|
||||
Chain.bytecode_hash : 'c => option(hash)
|
||||
@ -538,7 +605,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
|
||||
`Contract.balance`, however.
|
||||
|
||||
|
||||
The type `'c` must be instantiated with a contract.
|
||||
|
||||
|
||||
@ -565,6 +631,7 @@ main contract Market =
|
||||
The typechecker must be certain about the created contract's type, so it is
|
||||
worth writing it explicitly as shown in the example.
|
||||
|
||||
|
||||
##### clone
|
||||
```
|
||||
Chain.clone : ( ref : 'c, gas : int, value : int, protected : bool, ...
|
||||
@ -603,8 +670,8 @@ Example usage:
|
||||
```
|
||||
payable contract interface Auction =
|
||||
entrypoint init : (int, string) => void
|
||||
stateful payable entrypoint buy : (int) => ()
|
||||
stateful entrypoint sell : (int) => ()
|
||||
stateful payable entrypoint buy : (int) => unit
|
||||
stateful entrypoint sell : (int) => unit
|
||||
|
||||
main contract Market =
|
||||
type state = list(Auction)
|
||||
@ -623,11 +690,71 @@ implementation of the `init` function does not actually return `state`, but
|
||||
calls `put` instead. Moreover, FATE prevents even handcrafted calls to `init`.
|
||||
|
||||
|
||||
##### coinbase
|
||||
```
|
||||
Chain.coinbase : address
|
||||
```
|
||||
|
||||
The address of the account that mined the current block.
|
||||
|
||||
|
||||
##### difficulty
|
||||
```
|
||||
Chain.difficulty : int
|
||||
```
|
||||
|
||||
The difficulty of the current block.
|
||||
|
||||
|
||||
##### event
|
||||
```
|
||||
Chain.event(e : event) : unit
|
||||
```
|
||||
Emits the event. To use this function one needs to define the `event` type as a `datatype` in the contract.
|
||||
|
||||
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
|
||||
@ -705,11 +832,14 @@ Hash any object to blake2b
|
||||
|
||||
#### 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
|
||||
the `pubkey`
|
||||
the `pubkey`.
|
||||
|
||||
Note: before v8 of the compiler, `msg` had type `hash` (i.e. `bytes(32)`).
|
||||
|
||||
|
||||
#### ecverify_secp256k1
|
||||
```
|
||||
@ -742,12 +872,21 @@ Verifies a standard 64-byte ECDSA signature (`R || S`).
|
||||
|
||||
### 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
|
||||
@ -796,206 +935,26 @@ Returns a list containing pairs of keys and their respective elements.
|
||||
Turns a list of pairs of form `(key, value)` into a map
|
||||
|
||||
|
||||
### Oracle
|
||||
|
||||
#### register
|
||||
```
|
||||
Oracle.register(<signature : bytes(64)>, acct : address, qfee : int, ttl : Chain.ttl) : oracle('a, '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).
|
||||
* `signature` is a signature proving that the contract is allowed to register the account -
|
||||
the `network id` + `account address` + `contract address` (concatenated as byte arrays) is
|
||||
[signed](./sophia_features.md#delegation-signature) with the
|
||||
private key of the account, proving you have the private key of the oracle to be. If the
|
||||
address is the same as the contract `sign` is ignored and can be left out entirely.
|
||||
* The `qfee` is the minimum query fee to be paid by a user when asking a question of the oracle.
|
||||
* The `ttl` is the Time To Live for the oracle in key blocks, either relative to the current
|
||||
key block height (`RelativeTTL(delta)`) or a fixed key block height (`FixedTTL(height)`).
|
||||
* The type `'a` is the type of the question to ask.
|
||||
* The type `'b` is the type of the oracle answers.
|
||||
|
||||
Examples:
|
||||
```
|
||||
Oracle.register(addr0, 25, RelativeTTL(400))
|
||||
Oracle.register(addr1, 25, RelativeTTL(500), signature = sign1)
|
||||
```
|
||||
|
||||
|
||||
#### get_question
|
||||
```
|
||||
Oracle.get_question(o : oracle('a, 'b), q : oracle_query('a, 'b)) : 'a
|
||||
```
|
||||
|
||||
Checks what was the question of query `q` on oracle `o`
|
||||
|
||||
|
||||
#### respond
|
||||
```
|
||||
Oracle.respond(<signature : bytes(64)>, o : oracle('a, 'b), q : oracle_query('a, 'b), 'b) : unit
|
||||
```
|
||||
|
||||
Responds to the question `q` on `o`.
|
||||
Unless the contract address is the same as the oracle address the `signature`
|
||||
(which is an optional, named argument)
|
||||
needs to be provided. Proving that we have the private key of the oracle by
|
||||
[signing](./sophia_features.md#delegation-signature)
|
||||
the `network id` + `oracle query id` + `contract address`
|
||||
|
||||
|
||||
#### extend
|
||||
```
|
||||
Oracle.extend(<signature : bytes(64)>, o : oracle('a, 'b), ttl : Chain.ttl) : unit
|
||||
```
|
||||
|
||||
Extends TTL of an oracle.
|
||||
* `singature` is a named argument and thus optional. Must be the same as for `Oracle.register`
|
||||
* `o` is the oracle being extended
|
||||
* `ttl` must be `RelativeTTL`. The time to live of `o` will be extended by this value.
|
||||
|
||||
#### query_fee
|
||||
```
|
||||
Oracle.query_fee(o : oracle('a, 'b)) : int
|
||||
```
|
||||
|
||||
Returns the query fee of the oracle
|
||||
|
||||
|
||||
#### query
|
||||
```
|
||||
Oracle.query(o : oracle('a, 'b), q : 'a, qfee : int, qttl : Chain.ttl, rttl : Chain.ttl) : oracle_query('a, 'b)
|
||||
```
|
||||
|
||||
Asks the oracle a question.
|
||||
* The `qfee` is the query fee debited to the contract account (`Contract.address`).
|
||||
* The `qttl` controls the last height at which the oracle can submit a response
|
||||
and can be either fixed or relative.
|
||||
* The `rttl` must be relative and controls how long an answer is kept on the chain.
|
||||
The call fails if the oracle could expire before an answer.
|
||||
|
||||
|
||||
#### get_answer
|
||||
```
|
||||
Oracle.get_answer(o : oracle('a, 'b), q : oracle_query('a, 'b)) : option('b)
|
||||
```
|
||||
|
||||
Checks what is the optional query answer
|
||||
|
||||
|
||||
#### expiry
|
||||
|
||||
```
|
||||
Oracle.expiry(o : oracle('a, 'b)) : int
|
||||
```
|
||||
|
||||
Ask the oracle when it expires. The result is the block height at which it will happen.
|
||||
|
||||
|
||||
#### check
|
||||
```
|
||||
Oracle.check(o : oracle('a, 'b)) : bool
|
||||
```
|
||||
|
||||
Returns `true` iff the oracle `o` exists and has correct type
|
||||
|
||||
|
||||
#### check_query
|
||||
```
|
||||
Oracle.check_query(o : oracle('a, 'b), q : oracle_query('a, 'b)) : bool
|
||||
```
|
||||
|
||||
It returns `true` iff the oracle query exist and has the expected type.
|
||||
|
||||
|
||||
## Includable namespaces
|
||||
|
||||
These need to be explicitly included (with `.aes` suffix)
|
||||
|
||||
|
||||
### Bitwise
|
||||
### AENSCompat
|
||||
|
||||
Bitwise operations on arbitrary precision integers.
|
||||
|
||||
#### bsr
|
||||
#### pointee\_to\_V2
|
||||
```
|
||||
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.
|
||||
|
||||
|
||||
#### bsl
|
||||
#### pointee\_from\_V2
|
||||
```
|
||||
Bitwise.bsl(n : int, x : int) : int
|
||||
AENSCompat.pointee_from_V2(p2 : AENSv2.pointee) : option(AENS.pointee)
|
||||
```
|
||||
|
||||
Logical bit shift `x` left `n` positions.
|
||||
|
||||
|
||||
#### bsli
|
||||
```
|
||||
Bitwise.bsli(n : int, x : int, lim : int) : int
|
||||
```
|
||||
|
||||
Logical bit shift `x` left `n` positions, limit to `lim` bits.
|
||||
|
||||
|
||||
#### band
|
||||
```
|
||||
Bitwise.band(x : int, y : int) : int
|
||||
```
|
||||
|
||||
Bitwise `and` of `x` and `y`.
|
||||
|
||||
|
||||
#### bor
|
||||
```
|
||||
Bitwise.bor(x : int, y : int) : int
|
||||
```
|
||||
|
||||
Bitwise `or` of `x` and `y`.
|
||||
|
||||
|
||||
#### bxor
|
||||
```
|
||||
Bitwise.bxor(x : int, y : int) : int
|
||||
```
|
||||
|
||||
Bitwise `xor` of `x` and `y`.
|
||||
|
||||
|
||||
#### bnot
|
||||
```
|
||||
Bitwise.bnot(x : int) : int
|
||||
```
|
||||
|
||||
Bitwise `not` of `x`. Defined and implemented as `bnot(x) = bxor(x, -1)`.
|
||||
|
||||
|
||||
#### uband
|
||||
```
|
||||
Bitwise.uband(x : int, y : int) : int
|
||||
```
|
||||
|
||||
Bitwise `and` of _non-negative_ numbers `x` and `y`.
|
||||
|
||||
|
||||
#### ubor
|
||||
```
|
||||
Bitwise.ubor(x : int, y : int) : int
|
||||
```
|
||||
|
||||
Bitwise `or` of _non-negative_ `x` and `y`.
|
||||
|
||||
|
||||
#### ubxor
|
||||
```
|
||||
Bitwise.ubxor(x : int, y : int) : int
|
||||
```
|
||||
|
||||
Bitwise `xor` of _non-negative_ `x` and `y`.
|
||||
Translate new pointee format to old, `DataPt` can't be translated, so `None` is returned in this case.
|
||||
|
||||
|
||||
### BLS12\_381
|
||||
@ -1584,7 +1543,7 @@ point where the error would exceed the `loss` value.
|
||||
|
||||
For debugging. If it ever returns false in a code that doesn't call `frac` constructors or
|
||||
accept arbitrary `frac`s from the surface you should report it as a
|
||||
[bug](https://github.com/aeternity/aesophia/issues/new)
|
||||
[bug](https://git.qpq.swiss/QPQ-AG/sophia/issues/new)
|
||||
|
||||
If you expect getting calls with malformed `frac`s in your contract, you should use
|
||||
this function to verify the input.
|
||||
@ -2391,6 +2350,15 @@ to_int(s : string) : option(int)
|
||||
Converts a decimal ("123", "-253") or a hexadecimal ("0xa2f", "-0xBBB") string into
|
||||
an integer. If the string doesn't contain a valid number `None` is returned.
|
||||
|
||||
#### to\_bytes
|
||||
```
|
||||
to_bytes(s : string) : bytes()
|
||||
```
|
||||
|
||||
Converts string into byte array. String is UTF-8 encoded. I.e.
|
||||
`String.length(s)` is not guaranteed to be equal to
|
||||
`Bytes.size(String.to_bytes(s))`.
|
||||
|
||||
#### sha3
|
||||
```
|
||||
sha3(s : string) : hash
|
||||
|
@ -10,8 +10,9 @@ and `*/` and can be nested.
|
||||
### Keywords
|
||||
|
||||
```
|
||||
contract elif else entrypoint false function if import include let mod namespace
|
||||
private payable stateful switch true type record datatype main interface
|
||||
contract include let switch type record datatype if elif else function
|
||||
stateful payable true false mod public entrypoint private indexed namespace
|
||||
interface main using as for hiding
|
||||
```
|
||||
|
||||
### Tokens
|
||||
@ -27,8 +28,7 @@ private payable stateful switch true type record datatype main interface
|
||||
- `Char` character literal enclosed in `'` with escape character `\`
|
||||
- `AccountAddress` base58-encoded 32 byte account pubkey with `ak_` prefix
|
||||
- `ContractAddress` base58-encoded 32 byte contract address with `ct_` prefix
|
||||
- `OracleAddress` base58-encoded 32 byte oracle address with `ok_` prefix
|
||||
- `OracleQueryId` base58-encoded 32 byte oracle query id with `oq_` prefix
|
||||
- `Signature` base58-encoded 64 byte cryptographic signature with `sg_` prefix
|
||||
|
||||
Valid string escape codes are
|
||||
|
||||
@ -44,7 +44,7 @@ Valid string escape codes are
|
||||
| `\xHexDigits` | *HexDigits* | |
|
||||
|
||||
|
||||
See the [identifier encoding scheme](https://github.com/aeternity/protocol/blob/master/node/api/api_encoding.md) for the
|
||||
See the [identifier encoding scheme](https://git.qpq.swiss/QPQ-AG/protocol/src/branch/master/node/api/api_encoding.md) for the
|
||||
details on the base58 literals.
|
||||
|
||||
## Layout blocks
|
||||
@ -91,18 +91,31 @@ A Sophia file consists of a sequence of *declarations* in a layout block.
|
||||
```c
|
||||
File ::= Block(TopDecl)
|
||||
|
||||
TopDecl ::= ['payable'] 'contract' Con '=' Block(Decl)
|
||||
| 'namespace' Con '=' Block(Decl)
|
||||
| '@compiler' PragmaOp Version
|
||||
| 'include' String
|
||||
TopDecl ::= ['payable'] ['main'] 'contract' Con [Implement] '=' Block(Decl)
|
||||
| 'contract' 'interface' Con [Implement] '=' Block(Decl)
|
||||
| 'namespace' Con '=' Block(Decl)
|
||||
| '@compiler' PragmaOp Version
|
||||
| 'include' String
|
||||
| Using
|
||||
|
||||
Implement ::= ':' Sep1(Con, ',')
|
||||
|
||||
Decl ::= 'type' Id ['(' TVar* ')'] '=' TypeAlias
|
||||
| 'record' Id ['(' TVar* ')'] '=' RecordType
|
||||
| 'datatype' Id ['(' TVar* ')'] '=' DataType
|
||||
| 'let' Id [':' Type] '=' Expr
|
||||
| (EModifier* 'entrypoint' | FModifier* 'function') Block(FunDecl)
|
||||
| Using
|
||||
|
||||
FunDecl ::= Id ':' Type // Type signature
|
||||
| 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 ::= '<' | '=<' | '==' | '>=' | '>'
|
||||
Version ::= Sep1(Int, '.')
|
||||
@ -172,12 +185,17 @@ Stmt ::= 'switch' '(' Expr ')' Block(Case)
|
||||
| 'elif' '(' Expr ')' Block(Stmt)
|
||||
| 'else' Block(Stmt)
|
||||
| 'let' LetDef
|
||||
| Using
|
||||
| Expr
|
||||
|
||||
LetDef ::= Id Args [':' Type] '=' Block(Stmt) // Function definition
|
||||
| Pattern '=' Block(Stmt) // Value definition
|
||||
|
||||
Case ::= Pattern '=>' Block(Stmt)
|
||||
| Pattern Block(GuardedCase)
|
||||
|
||||
GuardedCase ::= '|' Sep1(Expr, ',') '=>' Block(Stmt)
|
||||
|
||||
Pattern ::= Expr
|
||||
```
|
||||
|
||||
@ -215,10 +233,12 @@ Expr ::= '(' LamArgs ')' '=>' Block(Stmt) // Anonymous function (x) => x +
|
||||
| '[' Expr '..' Expr ']' // List range [1..n]
|
||||
| '{' Sep(FieldUpdate, ',') '}' // Record or map value {x = 0, y = 1}, {[key] = val}
|
||||
| '(' Expr ')' // Parens (1 + 2) * 3
|
||||
| '(' Expr '=' Expr ')' // Assign pattern (y = x::_)
|
||||
| Id | Con | QId | QCon // Identifiers x, None, Map.member, AELib.Token
|
||||
| Int | Bytes | String | Char // Literals 123, 0xff, #00abc123, "foo", '%'
|
||||
| AccountAddress | ContractAddress // Chain identifiers
|
||||
| OracleAddress | OracleQueryId // Chain identifiers
|
||||
| Signature // Signature
|
||||
| '???' // Hole expression 1 + ???
|
||||
|
||||
Generator ::= Pattern '<-' Expr // Generator
|
||||
| 'if' '(' Expr ')' // Guard
|
||||
@ -235,8 +255,8 @@ Path ::= Id // Record field
|
||||
|
||||
BinOp ::= '||' | '&&' | '<' | '>' | '=<' | '>=' | '==' | '!='
|
||||
| '::' | '++' | '+' | '-' | '*' | '/' | 'mod' | '^'
|
||||
| '|>'
|
||||
UnOp ::= '-' | '!'
|
||||
| 'band' | 'bor' | 'bxor' | '<<' | '>>' | '|>'
|
||||
UnOp ::= '-' | '!' | 'bnot'
|
||||
```
|
||||
|
||||
## Operators types
|
||||
@ -245,23 +265,28 @@ UnOp ::= '-' | '!'
|
||||
| --- | ---
|
||||
| `-` `+` `*` `/` `mod` `^` | arithmetic operators
|
||||
| `!` `&&` `\|\|` | logical operators
|
||||
| `band` `bor` `bxor` `bnot` `<<` `>>` | bitwise operators
|
||||
| `==` `!=` `<` `>` `=<` `>=` | comparison operators
|
||||
| `::` `++` | list operators
|
||||
| `\|>` | functional operators
|
||||
|
||||
## Operator precendences
|
||||
## Operator precedence
|
||||
|
||||
In order of highest to lowest precedence.
|
||||
|
||||
| Operators | Associativity
|
||||
| --- | ---
|
||||
| `!` | right
|
||||
| `!` `bnot`| right
|
||||
| `^` | left
|
||||
| `*` `/` `mod` | left
|
||||
| `-` (unary) | right
|
||||
| `+` `-` | left
|
||||
| `<<` `>>` | left
|
||||
| `::` `++` | right
|
||||
| `<` `>` `=<` `>=` `==` `!=` | none
|
||||
| `band` | left
|
||||
| `bxor` | left
|
||||
| `bor` | left
|
||||
| `&&` | right
|
||||
| `\|\|` | right
|
||||
| `\|>` | left
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
-define(LET_P(X, P, Q), aeso_parse_lib:bind(P, fun(X) -> Q end)).
|
||||
-define(LAZY_P(P), aeso_parse_lib:lazy(fun() -> P end)).
|
||||
-define(MEMO_P(P), aeso_parse_lib:lazy(aeso_parse_lib:memoised(fun() -> P end))).
|
||||
-define(LET_P(X, P, Q), so_parse_lib:bind(P, fun(X) -> Q end)).
|
||||
-define(LAZY_P(P), so_parse_lib:lazy(fun() -> P end)).
|
||||
-define(MEMO_P(P), so_parse_lib:lazy(so_parse_lib:memoised(fun() -> P end))).
|
||||
|
||||
-define(GUARD_P(G, P),
|
||||
case G of
|
||||
@ -18,7 +18,7 @@
|
||||
-define(RULE(A, B, C, D, E, F, G, Do), map(fun({_1, _2, _3, _4, _5, _6, _7}) -> Do end, {A, B, C, D, E, F, G} )).
|
||||
-define(RULE(A, B, C, D, E, F, G, H, Do), map(fun({_1, _2, _3, _4, _5, _6, _7, _8}) -> Do end, {A, B, C, D, E, F, G, H})).
|
||||
|
||||
-import(aeso_parse_lib,
|
||||
-import(so_parse_lib,
|
||||
[tok/1, tok/2, between/3, many/1, many1/1, sep/2, sep1/2,
|
||||
infixl/1, infixr/1, choice/1, choice/2, return/1, layout/0,
|
||||
fail/0, fail/1, fail/2, map/2, infixl/2, infixr/2, infixl1/2, infixr1/2,
|
17
priv/stdlib/AENSCompat.aes
Normal file
17
priv/stdlib/AENSCompat.aes
Normal file
@ -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
|
||||
asc : (('a, 'a) => bool, 'a, list('a), list('a)) => list(list('a))
|
||||
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)
|
||||
asc(_, x, acc, []) = [List.reverse(x::acc)]
|
||||
asc(_, x, acc, []) = [reverse(x::acc)]
|
||||
|
||||
/** Merges list of sorted lists
|
||||
*/
|
||||
|
@ -1,5 +1,8 @@
|
||||
include "List.aes"
|
||||
namespace String =
|
||||
// Gives a bytes() representation of the string
|
||||
function to_bytes(s : string) : bytes() = StringInternal.to_bytes(s)
|
||||
|
||||
// Computes the SHA3/Keccak hash of the string
|
||||
function sha3(s : string) : hash = StringInternal.sha3(s)
|
||||
// Computes the SHA256 hash of the string.
|
||||
|
10
rebar.config
10
rebar.config
@ -2,8 +2,9 @@
|
||||
|
||||
{erl_opts, [debug_info]}.
|
||||
|
||||
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {tag, "v3.1.1"}}}
|
||||
, {getopt, "1.0.1"}
|
||||
{deps, [ {gmbytecode,
|
||||
{git, "https://git.qpq.swiss/QPQ-AG/gmbytecode.git",
|
||||
{ref, "97cea33be8f3a35d26055664da7aa59531ff5537"}}}
|
||||
, {eblake2, "1.0.0"}
|
||||
, {jsx, {git, "https://github.com/talentdeficit/jsx.git", {tag, "2.8.0"}}}
|
||||
]}.
|
||||
@ -14,10 +15,11 @@
|
||||
{base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]}
|
||||
]}.
|
||||
|
||||
{relx, [{release, {aesophia, "7.0.0"},
|
||||
[aesophia, aebytecode, getopt]},
|
||||
{relx, [{release, {sophia, "9.0.0"},
|
||||
[sophia, gmbytecode]},
|
||||
|
||||
{dev_mode, true},
|
||||
{include_erts, false},
|
||||
|
||||
{extended_start_script, true}]}.
|
||||
|
||||
|
39
rebar.lock
39
rebar.lock
@ -1,31 +1,30 @@
|
||||
{"1.2.0",
|
||||
[{<<"aebytecode">>,
|
||||
{git,"https://github.com/aeternity/aebytecode.git",
|
||||
{ref,"8269dbd71e9011921c60141636f1baa270a0e784"}},
|
||||
[{<<"gmbytecode">>,
|
||||
{git,"https://git.qpq.swiss/QPQ-AG/gmbytecode.git",
|
||||
{ref, "97cea33be8f3a35d26055664da7aa59531ff5537"}},
|
||||
0},
|
||||
{<<"aeserialization">>,
|
||||
{git,"https://github.com/aeternity/aeserialization.git",
|
||||
{ref,"eb68fe331bd476910394966b7f5ede7a74d37e35"}},
|
||||
{<<"gmserialization">>,
|
||||
{git,"https://git.qpq.swiss/QPQ-AG/gmserialization.git",
|
||||
{ref,"ac64e01b0f675c1a34c70a827062f381920742db"}},
|
||||
1},
|
||||
{<<"base58">>,
|
||||
{git,"https://github.com/aeternity/erl-base58.git",
|
||||
{ref,"60a335668a60328a29f9731b67c4a0e9e3d50ab6"}},
|
||||
{git,"https://git.qpq.swiss/QPQ-AG/erl-base58.git",
|
||||
{ref,"e6aa62eeae3d4388311401f06e4b939bf4e94b9c"}},
|
||||
2},
|
||||
{<<"eblake2">>,{pkg,<<"eblake2">>,<<"1.0.0">>},0},
|
||||
{<<"eblake2">>,
|
||||
{git,"https://git.qpq.swiss/QPQ-AG/eblake2.git",
|
||||
{ref,"b29d585b8760746142014884007eb8441a3b6a14"}},
|
||||
0},
|
||||
{<<"enacl">>,
|
||||
{git,"https://github.com/aeternity/enacl.git",
|
||||
{ref,"793ddb502f7fe081302e1c42227dca70b09f8e17"}},
|
||||
{git,"https://git.qpq.swiss/QPQ-AG/enacl.git",
|
||||
{ref,"4eb7ec70084ba7c87b1af8797c4c4e90c84f95a2"}},
|
||||
2},
|
||||
{<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},0},
|
||||
{<<"getopt">>,
|
||||
{git,"https://git.qpq.swiss/QPQ-AG/getopt.git",
|
||||
{ref,"dbab6262a2430809430deda9d8650f58f9d80898"}},
|
||||
1},
|
||||
{<<"jsx">>,
|
||||
{git,"https://github.com/talentdeficit/jsx.git",
|
||||
{ref,"3074d4865b3385a050badf7828ad31490d860df5"}},
|
||||
0}]}.
|
||||
[
|
||||
{pkg_hash,[
|
||||
{<<"eblake2">>, <<"EC8AD20E438AAB3F2E8D5D118C366A0754219195F8A0F536587440F8F9BCF2EF">>},
|
||||
{<<"getopt">>, <<"C73A9FA687B217F2FF79F68A3B637711BB1936E712B521D8CE466B29CBF7808A">>}]},
|
||||
{pkg_hash_ext,[
|
||||
{<<"eblake2">>, <<"3C4D300A91845B25D501929A26AC2E6F7157480846FAB2347A4C11AE52E08A99">>},
|
||||
{<<"getopt">>, <<"53E1AB83B9CEB65C9672D3E7A35B8092E9BDC9B3EE80721471A161C10C59959C">>}]}
|
||||
].
|
||||
|
||||
|
@ -1,13 +1,14 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @author Robert Virding
|
||||
%%% @copyright (C) 2025, QPQ AG
|
||||
%%% @copyright (C) 2019, Aeternity Anstalt
|
||||
%%% @doc
|
||||
%%% ACI interface
|
||||
%%% @end
|
||||
%%% Created : 12 Jan 2019
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(aeso_aci).
|
||||
-module(so_aci).
|
||||
-vsn("9.0.0").
|
||||
|
||||
-export([ file/2
|
||||
, file/3
|
||||
@ -21,7 +22,7 @@
|
||||
, json_encode_expr/1
|
||||
, json_encode_type/1]).
|
||||
|
||||
-include("aeso_utils.hrl").
|
||||
-include("so_utils.hrl").
|
||||
|
||||
-type aci_type() :: json | string.
|
||||
-type json() :: jsx:json_term().
|
||||
@ -35,7 +36,7 @@ file(Type, File) ->
|
||||
file(Type, File, []).
|
||||
|
||||
file(Type, File, Options0) ->
|
||||
Options = aeso_compiler:add_include_path(File, Options0),
|
||||
Options = so_compiler:add_include_path(File, Options0),
|
||||
case file:read_file(File) of
|
||||
{ok, BinCode} ->
|
||||
do_contract_interface(Type, binary_to_list(BinCode), Options);
|
||||
@ -56,11 +57,11 @@ contract_interface(Type, ContractString, CompilerOpts) ->
|
||||
render_aci_json(Json) ->
|
||||
do_render_aci_json(Json).
|
||||
|
||||
-spec json_encode_expr(aeso_syntax:expr()) -> json().
|
||||
-spec json_encode_expr(so_syntax:expr()) -> json().
|
||||
json_encode_expr(Expr) ->
|
||||
encode_expr(Expr).
|
||||
|
||||
-spec json_encode_type(aeso_syntax:type()) -> json().
|
||||
-spec json_encode_type(so_syntax:type()) -> json().
|
||||
json_encode_type(Type) ->
|
||||
encode_type(Type).
|
||||
|
||||
@ -69,8 +70,8 @@ do_contract_interface(Type, Contract, Options) when is_binary(Contract) ->
|
||||
do_contract_interface(Type, binary_to_list(Contract), Options);
|
||||
do_contract_interface(Type, ContractString, Options) ->
|
||||
try
|
||||
Ast = aeso_compiler:parse(ContractString, Options),
|
||||
{TypedAst, _, _} = aeso_ast_infer_types:infer(Ast, [dont_unfold | Options]),
|
||||
Ast = so_compiler:parse(ContractString, Options),
|
||||
{TypedAst, _, _} = so_ast_infer_types:infer(Ast, [dont_unfold | Options]),
|
||||
from_typed_ast(Type, TypedAst)
|
||||
catch
|
||||
throw:{error, Errors} -> {error, Errors}
|
||||
@ -91,7 +92,7 @@ encode_contract(Contract = {Head, _, {con, _, Name}, _, _}) when ?IS_CONTRACT_HE
|
||||
{Es, Tdefs1} = lists:partition(FilterT(<<"event">>), Tdefs0),
|
||||
{Ss, Tdefs} = lists:partition(FilterT(<<"state">>), Tdefs1),
|
||||
|
||||
C1 = C0#{type_defs => Tdefs},
|
||||
C1 = C0#{typedefs => Tdefs},
|
||||
|
||||
C2 = case Es of
|
||||
[] -> C1;
|
||||
@ -111,7 +112,7 @@ encode_contract(Contract = {Head, _, {con, _, Name}, _, _}) when ?IS_CONTRACT_HE
|
||||
encode_contract(Namespace = {namespace, _, {con, _, Name}, _}) ->
|
||||
Tdefs = [ encode_typedef(T) || T <- sort_decls(contract_types(Namespace)) ],
|
||||
#{namespace => #{name => encode_name(Name),
|
||||
type_defs => Tdefs}}.
|
||||
typedefs => Tdefs}}.
|
||||
|
||||
%% Encode a function definition. Currently we are only interested in
|
||||
%% the interface and type.
|
||||
@ -198,8 +199,9 @@ encode_expr({bytes, _, B}) ->
|
||||
<<N:Digits/unit:8>> = B,
|
||||
list_to_binary(lists:flatten(io_lib:format("#~*.16.0b", [Digits*2, N])));
|
||||
encode_expr({Lit, _, L}) when Lit == oracle_pubkey; Lit == oracle_query_id;
|
||||
Lit == contract_pubkey; Lit == account_pubkey ->
|
||||
aeser_api_encoder:encode(Lit, L);
|
||||
Lit == contract_pubkey; Lit == account_pubkey;
|
||||
Lit == signature ->
|
||||
gmser_api_encoder:encode(Lit, L);
|
||||
encode_expr({app, _, {'-', _}, [{int, _, N}]}) ->
|
||||
encode_expr({int, [], -N});
|
||||
encode_expr({app, _, F, As}) ->
|
||||
@ -234,7 +236,7 @@ do_render_aci_json(Json) ->
|
||||
decode_contract(#{contract := #{name := Name,
|
||||
kind := Kind,
|
||||
payable := Payable,
|
||||
type_defs := Ts0,
|
||||
typedefs := Ts0,
|
||||
functions := Fs} = C}) ->
|
||||
MkTDef = fun(N, T) -> #{name => N, vars => [], typedef => T} end,
|
||||
Ts = [ MkTDef(<<"state">>, maps:get(state, C)) || maps:is_key(state, C) ] ++
|
||||
@ -246,7 +248,7 @@ decode_contract(#{contract := #{name := Name,
|
||||
end,
|
||||
io_lib:format("~s", [Name])," =\n",
|
||||
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",
|
||||
decode_tdefs(Ts)];
|
||||
decode_contract(_) -> [].
|
||||
@ -282,6 +284,8 @@ decode_type(#{list := [Et]}) ->
|
||||
decode_type(#{map := Ets}) ->
|
||||
Ts = decode_types(Ets),
|
||||
["map",$(,lists:join(",", Ts),$)];
|
||||
decode_type(#{bytes := any}) ->
|
||||
["bytes()"];
|
||||
decode_type(#{bytes := Len}) ->
|
||||
["bytes(", integer_to_list(Len), ")"];
|
||||
decode_type(#{variant := Ets}) ->
|
||||
@ -358,14 +362,14 @@ is_type(_) -> false.
|
||||
|
||||
sort_decls(Ds) ->
|
||||
Sort = fun (D1, D2) ->
|
||||
aeso_syntax:get_ann(line, D1, 0) =<
|
||||
aeso_syntax:get_ann(line, D2, 0)
|
||||
so_syntax:get_ann(line, D1, 0) =<
|
||||
so_syntax:get_ann(line, D2, 0)
|
||||
end,
|
||||
lists:sort(Sort, Ds).
|
||||
|
||||
is_entrypoint(Node) -> aeso_syntax:get_ann(entrypoint, Node, false).
|
||||
is_stateful(Node) -> aeso_syntax:get_ann(stateful, Node, false).
|
||||
is_payable(Node) -> aeso_syntax:get_ann(payable, Node, false).
|
||||
is_entrypoint(Node) -> so_syntax:get_ann(entrypoint, Node, false).
|
||||
is_stateful(Node) -> so_syntax:get_ann(stateful, Node, false).
|
||||
is_payable(Node) -> so_syntax:get_ann(payable, Node, false).
|
||||
|
||||
typedef_name({type_def, _, {id, _, Name}, _, _}) -> Name.
|
||||
|
@ -1,4 +1,5 @@
|
||||
-module(aeso_ast).
|
||||
-module(so_ast).
|
||||
-vsn("9.0.0").
|
||||
|
||||
-export([int/2,
|
||||
line/1,
|
||||
@ -17,11 +18,11 @@ line({symbol, Line, _}) -> Line.
|
||||
symbol_name({symbol, _, Name}) -> Name.
|
||||
|
||||
pp(Ast) ->
|
||||
String = prettypr:format(aeso_pretty:decls(Ast, [])),
|
||||
String = prettypr:format(so_pretty:decls(Ast, [])),
|
||||
io:format("Ast:\n~s\n", [String]).
|
||||
|
||||
pp_typed(TypedAst) ->
|
||||
%% io:format("Typed tree:\n~p\n",[TypedAst]),
|
||||
String = prettypr:format(aeso_pretty:decls(TypedAst, [show_generated])),
|
||||
String = prettypr:format(so_pretty:decls(TypedAst, [show_generated])),
|
||||
io:format("Type ast:\n~s\n",[String]).
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,17 +1,20 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @author Happi (Erik Stenman)
|
||||
%%% @copyright (C) 2025, QPQ AG
|
||||
%%% @copyright (C) 2017, Aeternity Anstalt
|
||||
%%% @doc
|
||||
%%% Compiler from Aeterinty Sophia language to FATE.
|
||||
%%% Compiler from Sophia language to FATE.
|
||||
%%% @end
|
||||
%%% Created : 12 Dec 2017
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeso_compiler).
|
||||
-module(so_compiler).
|
||||
-vsn("9.0.0").
|
||||
|
||||
-export([ file/1
|
||||
, file/2
|
||||
, from_string/2
|
||||
, check_call/4
|
||||
, decode_value/4
|
||||
, encode_value/4
|
||||
, create_calldata/3
|
||||
, create_calldata/4
|
||||
, version/0
|
||||
@ -25,8 +28,8 @@
|
||||
, validate_byte_code/3
|
||||
]).
|
||||
|
||||
-include_lib("aebytecode/include/aeb_opcodes.hrl").
|
||||
-include("aeso_utils.hrl").
|
||||
-include_lib("gmbytecode/include/gmb_opcodes.hrl").
|
||||
-include("so_utils.hrl").
|
||||
|
||||
|
||||
-type option() :: pp_sophia_code
|
||||
@ -40,7 +43,8 @@
|
||||
| {include, {file_system, [string()]} |
|
||||
{explicit_files, #{string() => binary()}}}
|
||||
| {src_file, string()}
|
||||
| {aci, aeso_aci:aci_type()}.
|
||||
| {src_dir, string()}
|
||||
| {aci, so_aci:aci_type()}.
|
||||
-type options() :: [option()].
|
||||
|
||||
-export_type([ option/0
|
||||
@ -49,15 +53,15 @@
|
||||
|
||||
-spec version() -> {ok, binary()} | {error, term()}.
|
||||
version() ->
|
||||
case lists:keyfind(aesophia, 1, application:loaded_applications()) of
|
||||
case lists:keyfind(sophia, 1, application:loaded_applications()) of
|
||||
false ->
|
||||
case application:load(aesophia) of
|
||||
case application:load(sophia) of
|
||||
ok ->
|
||||
case application:get_key(aesophia, vsn) of
|
||||
case application:get_key(sophia, vsn) of
|
||||
{ok, VsnString} ->
|
||||
{ok, list_to_binary(VsnString)};
|
||||
undefined ->
|
||||
{error, failed_to_load_aesophia}
|
||||
{error, failed_to_load_sophia}
|
||||
end;
|
||||
Err = {error, _} ->
|
||||
Err
|
||||
@ -77,18 +81,20 @@ numeric_version() ->
|
||||
Err
|
||||
end.
|
||||
|
||||
-spec file(string()) -> {ok, map()} | {error, [aeso_errors:error()]}.
|
||||
-spec file(string()) -> {ok, map()} | {error, [so_errors:error()]}.
|
||||
file(Filename) ->
|
||||
file(Filename, []).
|
||||
|
||||
-spec file(string(), options()) -> {ok, map()} | {error, [aeso_errors:error()]}.
|
||||
-spec file(string(), options()) -> {ok, map()} | {error, [so_errors:error()]}.
|
||||
file(File, Options0) ->
|
||||
Options = add_include_path(File, Options0),
|
||||
case read_contract(File) of
|
||||
{ok, Bin} -> from_string(Bin, [{src_file, File} | Options]);
|
||||
{ok, Bin} ->
|
||||
SrcDir = so_utils:canonical_dir(filename:dirname(File)),
|
||||
from_string(Bin, [{src_file, File}, {src_dir, SrcDir} | Options]);
|
||||
{error, Error} ->
|
||||
Msg = lists:flatten([File,": ",file:format_error(Error)]),
|
||||
{error, [aeso_errors:new(file_error, Msg)]}
|
||||
{error, [so_errors:new(file_error, Msg)]}
|
||||
end.
|
||||
|
||||
add_include_path(File, Options) ->
|
||||
@ -97,10 +103,10 @@ add_include_path(File, Options) ->
|
||||
false ->
|
||||
Dir = filename:dirname(File),
|
||||
{ok, Cwd} = file:get_cwd(),
|
||||
[{include, {file_system, [Cwd, Dir]}} | Options]
|
||||
[{include, {file_system, [Cwd, so_utils:canonical_dir(Dir)]}} | Options]
|
||||
end.
|
||||
|
||||
-spec from_string(binary() | string(), options()) -> {ok, map()} | {error, [aeso_errors:error()]}.
|
||||
-spec from_string(binary() | string(), options()) -> {ok, map()} | {error, [so_errors:error()]}.
|
||||
from_string(ContractBin, Options) when is_binary(ContractBin) ->
|
||||
from_string(binary_to_list(ContractBin), Options);
|
||||
from_string(ContractString, Options) ->
|
||||
@ -112,19 +118,21 @@ from_string(ContractString, Options) ->
|
||||
|
||||
from_string1(ContractString, Options) ->
|
||||
#{ fcode := FCode
|
||||
, fcode_env := #{child_con_env := ChildContracts}
|
||||
, fcode_env := FCodeEnv
|
||||
, folded_typed_ast := FoldedTypedAst
|
||||
, 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 = so_fcode_to_fate:compile(ChildContracts, FCode, SavedFreshNames, Options),
|
||||
pp_assembler(FateCode, Options),
|
||||
ByteCode = aeb_fate_code:serialize(FateCode, []),
|
||||
ByteCode = gmb_fate_code:serialize(FateCode, []),
|
||||
{ok, Version} = version(),
|
||||
Res = #{byte_code => ByteCode,
|
||||
compiler_version => Version,
|
||||
contract_source => ContractString,
|
||||
type_info => [],
|
||||
fate_code => FateCode,
|
||||
abi_version => aeb_fate_abi:abi_version(),
|
||||
abi_version => gmb_fate_abi:abi_version(),
|
||||
payable => maps:get(payable, FCode),
|
||||
warnings => Warnings
|
||||
},
|
||||
@ -135,7 +143,7 @@ maybe_generate_aci(Result, FoldedTypedAst, Options) ->
|
||||
undefined ->
|
||||
Result;
|
||||
Type ->
|
||||
{ok, Aci} = aeso_aci:from_typed_ast(Type, FoldedTypedAst),
|
||||
{ok, Aci} = so_aci:from_typed_ast(Type, FoldedTypedAst),
|
||||
maps:put(aci, Aci, Result)
|
||||
end.
|
||||
|
||||
@ -144,9 +152,9 @@ string_to_code(ContractString, Options) ->
|
||||
Ast = parse(ContractString, Options),
|
||||
pp_sophia_code(Ast, Options),
|
||||
pp_ast(Ast, Options),
|
||||
{TypeEnv, FoldedTypedAst, UnfoldedTypedAst, Warnings} = aeso_ast_infer_types:infer(Ast, [return_env | Options]),
|
||||
{TypeEnv, FoldedTypedAst, UnfoldedTypedAst, Warnings} = so_ast_infer_types:infer(Ast, [return_env | Options]),
|
||||
pp_typed_ast(UnfoldedTypedAst, Options),
|
||||
{Env, Fcode} = aeso_ast_to_fcode:ast_to_fcode(UnfoldedTypedAst, [{original_src, ContractString}|Options]),
|
||||
{Env, Fcode} = so_ast_to_fcode:ast_to_fcode(UnfoldedTypedAst, [{original_src, ContractString}|Options]),
|
||||
#{ fcode => Fcode
|
||||
, fcode_env => Env
|
||||
, unfolded_typed_ast => UnfoldedTypedAst
|
||||
@ -164,7 +172,7 @@ string_to_code(ContractString, Options) ->
|
||||
%% NOTE: Special treatment for "init" since it might be implicit and has
|
||||
%% a special return type (typerep, T)
|
||||
-spec check_call(string(), string(), [string()], options()) -> {ok, string(), [term()]}
|
||||
| {error, [aeso_errors:error()]}.
|
||||
| {error, [so_errors:error()]}.
|
||||
check_call(Source, "init" = FunName, Args, Options) ->
|
||||
case check_call1(Source, FunName, Args, Options) of
|
||||
Err = {error, _} when Args == [] ->
|
||||
@ -180,35 +188,63 @@ check_call(Source, FunName, Args, Options) ->
|
||||
check_call1(Source, FunName, Args, Options).
|
||||
|
||||
check_call1(ContractString0, FunName, Args, Options) ->
|
||||
case add_extra_call(ContractString0, {call, FunName, Args}, Options) of
|
||||
{ok, CallName, Code} ->
|
||||
{def, _, _, FcodeArgs} = get_call_body(CallName, Code),
|
||||
{ok, FunName, [ so_fcode_to_fate:term_to_fate(A) || A <- FcodeArgs ]};
|
||||
Err = {error, _} ->
|
||||
Err
|
||||
end.
|
||||
|
||||
add_extra_call(Contract0, Call, Options) ->
|
||||
try
|
||||
%% First check the contract without the __call function
|
||||
#{fcode := OrgFcode
|
||||
, fcode_env := #{child_con_env := ChildContracts}
|
||||
, ast := Ast} = string_to_code(ContractString0, Options),
|
||||
FateCode = aeso_fcode_to_fate:compile(ChildContracts, OrgFcode, []),
|
||||
, ast := Ast} = string_to_code(Contract0, Options),
|
||||
FateCode = so_fcode_to_fate:compile(ChildContracts, OrgFcode, #{}, []),
|
||||
%% collect all hashes and compute the first name without hash collision to
|
||||
SymbolHashes = maps:keys(aeb_fate_code:symbols(FateCode)),
|
||||
SymbolHashes = maps:keys(gmb_fate_code:symbols(FateCode)),
|
||||
CallName = first_none_match(?CALL_NAME, SymbolHashes,
|
||||
lists:seq($1, $9) ++ lists:seq($A, $Z) ++ lists:seq($a, $z)),
|
||||
ContractString = insert_call_function(Ast, ContractString0, CallName, FunName, Args),
|
||||
#{fcode := Fcode} = string_to_code(ContractString, Options),
|
||||
CallArgs = arguments_of_body(CallName, FunName, Fcode),
|
||||
|
||||
{ok, FunName, CallArgs}
|
||||
Contract = insert_call_function(Ast, Contract0, CallName, Call),
|
||||
{ok, CallName, string_to_code(Contract, Options)}
|
||||
catch
|
||||
throw:{error, Errors} -> {error, Errors}
|
||||
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)),
|
||||
{def, _FName, Args} = Body,
|
||||
%% FName is either {entrypoint, list_to_binary(FunName)} or 'init'
|
||||
[ aeso_fcode_to_fate:term_to_fate(A) || A <- Args ].
|
||||
Body.
|
||||
|
||||
encode_value(Contract0, Type, Value, Options) ->
|
||||
case add_extra_call(Contract0, {value, Type, Value}, Options) of
|
||||
{ok, CallName, Code} ->
|
||||
Body = get_call_body(CallName, Code),
|
||||
{ok, gmb_fate_encoding:serialize(so_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 = so_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, []) ->
|
||||
error(unable_to_find_unique_call_name);
|
||||
first_none_match(CallName, Hashes, [Char|Chars]) ->
|
||||
case not lists:member(aeb_fate_code:symbol_identifier(list_to_binary(CallName)), Hashes) of
|
||||
case not lists:member(gmb_fate_code:symbol_identifier(list_to_binary(CallName)), Hashes) of
|
||||
true ->
|
||||
CallName;
|
||||
false ->
|
||||
@ -216,14 +252,31 @@ first_none_match(CallName, Hashes, [Char|Chars]) ->
|
||||
end.
|
||||
|
||||
%% Add the __call function to a contract.
|
||||
-spec insert_call_function(aeso_syntax:ast(), string(), string(), string(), [string()]) -> string().
|
||||
insert_call_function(Ast, Code, Call, FunName, Args) ->
|
||||
-spec insert_call_function(so_syntax:ast(), string(), string(),
|
||||
{call, string(), [string()]} | {value, string(), string()} | {type, string()}) -> string().
|
||||
insert_call_function(Ast, Code, Call, {call, FunName, Args}) ->
|
||||
Ind = last_contract_indent(Ast),
|
||||
lists:flatten(
|
||||
[ Code,
|
||||
"\n\n",
|
||||
lists:duplicate(Ind, " "),
|
||||
"stateful entrypoint ", Call, "() = ", FunName, "(", string:join(Args, ","), ")\n"
|
||||
]);
|
||||
insert_call_function(Ast, Code, Call, {value, Type, Value}) ->
|
||||
Ind = last_contract_indent(Ast),
|
||||
lists:flatten(
|
||||
[ Code,
|
||||
"\n\n",
|
||||
lists:duplicate(Ind, " "),
|
||||
"entrypoint ", Call, "() : ", Type, " = ", Value, "\n"
|
||||
]);
|
||||
insert_call_function(Ast, Code, Call, {type, Type}) ->
|
||||
Ind = last_contract_indent(Ast),
|
||||
lists:flatten(
|
||||
[ Code,
|
||||
"\n\n",
|
||||
lists:duplicate(Ind, " "),
|
||||
"entrypoint ", Call, "(val : ", Type, ") : ", Type, " = val\n"
|
||||
]).
|
||||
|
||||
-spec insert_init_function(string(), options()) -> string().
|
||||
@ -238,100 +291,109 @@ insert_init_function(Code, Options) ->
|
||||
|
||||
last_contract_indent(Decls) ->
|
||||
case lists:last(Decls) of
|
||||
{_, _, _, _, [Decl | _]} -> aeso_syntax:get_ann(col, Decl, 1) - 1;
|
||||
{_, _, _, _, [Decl | _]} -> so_syntax:get_ann(col, Decl, 1) - 1;
|
||||
_ -> 0
|
||||
end.
|
||||
|
||||
-spec to_sophia_value(string(), string(), ok | error | revert, binary()) ->
|
||||
{ok, aeso_syntax:expr()} | {error, [aeso_errors:error()]}.
|
||||
{ok, so_syntax:expr()} | {error, [so_errors:error()]}.
|
||||
to_sophia_value(ContractString, Fun, ResType, Data) ->
|
||||
to_sophia_value(ContractString, Fun, ResType, Data, []).
|
||||
-spec to_sophia_value(string(), string(), ok | error | revert, binary(), options()) ->
|
||||
{ok, aeso_syntax:expr()} | {error, [aeso_errors:error()]}.
|
||||
{ok, so_syntax:expr()} | {error, [so_errors:error()]}.
|
||||
to_sophia_value(_, _, error, Err, _Options) ->
|
||||
{ok, {app, [], {id, [], "error"}, [{string, [], Err}]}};
|
||||
to_sophia_value(_, _, revert, Data, _Options) ->
|
||||
try aeso_vm_decode:from_fate({id, [], "string"}, aeb_fate_encoding:deserialize(Data)) of
|
||||
try so_vm_decode:from_fate({id, [], "string"}, gmb_fate_encoding:deserialize(Data)) of
|
||||
Err ->
|
||||
{ok, {app, [], {id, [], "abort"}, [Err]}}
|
||||
catch _:_ ->
|
||||
Msg = "Could not deserialize the revert message",
|
||||
{error, [aeso_errors:new(data_error, Msg)]}
|
||||
{error, [so_errors:new(data_error, Msg)]}
|
||||
end;
|
||||
to_sophia_value(ContractString, FunName, ok, Data, Options0) ->
|
||||
Options = [no_code | Options0],
|
||||
try
|
||||
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),
|
||||
Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]),
|
||||
Type = so_ast_infer_types:unfold_types_in_type(TypeEnv, Type0,
|
||||
[ unfold_record_types
|
||||
, unfold_variant_types
|
||||
, not_unfold_system_alias_types]),
|
||||
|
||||
try
|
||||
{ok, aeso_vm_decode:from_fate(Type, aeb_fate_encoding:deserialize(Data))}
|
||||
catch throw:cannot_translate_to_sophia ->
|
||||
Type1 = prettypr:format(aeso_pretty:type(Type0)),
|
||||
Msg = io_lib:format("Cannot translate FATE value ~p\n of Sophia type ~s",
|
||||
[aeb_fate_encoding:deserialize(Data), Type1]),
|
||||
{error, [aeso_errors:new(data_error, Msg)]};
|
||||
_:_ ->
|
||||
Type1 = prettypr:format(aeso_pretty:type(Type0)),
|
||||
Msg = io_lib:format("Failed to decode binary as type ~s", [Type1]),
|
||||
{error, [aeso_errors:new(data_error, Msg)]}
|
||||
end
|
||||
fate_data_to_sophia_value(Type0, Type, Data)
|
||||
catch
|
||||
throw:{error, Errors} -> {error, Errors}
|
||||
end.
|
||||
|
||||
fate_data_to_sophia_value(Type, UnfoldedType, FateData) ->
|
||||
try
|
||||
{ok, so_vm_decode:from_fate(UnfoldedType, gmb_fate_encoding:deserialize(FateData))}
|
||||
catch throw:cannot_translate_to_sophia ->
|
||||
Type1 = prettypr:format(so_pretty:type(Type)),
|
||||
Msg = io_lib:format("Cannot translate FATE value ~p\n of Sophia type ~s",
|
||||
[gmb_fate_encoding:deserialize(FateData), Type1]),
|
||||
{error, [so_errors:new(data_error, Msg)]};
|
||||
_:_ ->
|
||||
Type1 = prettypr:format(so_pretty:type(Type)),
|
||||
Msg = io_lib:format("Failed to decode binary as type ~s", [Type1]),
|
||||
{error, [so_errors:new(data_error, Msg)]}
|
||||
end.
|
||||
|
||||
-spec create_calldata(string(), string(), [string()]) ->
|
||||
{ok, binary()} | {error, [aeso_errors:error()]}.
|
||||
{ok, binary()} | {error, [so_errors:error()]}.
|
||||
create_calldata(Code, Fun, Args) ->
|
||||
create_calldata(Code, Fun, Args, []).
|
||||
-spec create_calldata(string(), string(), [string()], [{atom(), any()}]) ->
|
||||
{ok, binary()} | {error, [aeso_errors:error()]}.
|
||||
{ok, binary()} | {error, [so_errors:error()]}.
|
||||
create_calldata(Code, Fun, Args, Options0) ->
|
||||
Options = [no_code | Options0],
|
||||
case check_call(Code, Fun, Args, Options) of
|
||||
{ok, FunName, FateArgs} ->
|
||||
aeb_fate_abi:create_calldata(FunName, FateArgs);
|
||||
gmb_fate_abi:create_calldata(FunName, FateArgs);
|
||||
{error, _} = Err -> Err
|
||||
end.
|
||||
|
||||
-spec decode_calldata(string(), string(), binary()) ->
|
||||
{ok, [aeso_syntax:type()], [aeso_syntax:expr()]}
|
||||
| {error, [aeso_errors:error()]}.
|
||||
{ok, [so_syntax:type()], [so_syntax:expr()]}
|
||||
| {error, [so_errors:error()]}.
|
||||
decode_calldata(ContractString, FunName, Calldata) ->
|
||||
decode_calldata(ContractString, FunName, Calldata, []).
|
||||
-spec decode_calldata(string(), string(), binary(), options()) ->
|
||||
{ok, [aeso_syntax:type()], [aeso_syntax:expr()]}
|
||||
| {error, [aeso_errors:error()]}.
|
||||
{ok, [so_syntax:type()], [so_syntax:expr()]}
|
||||
| {error, [so_errors:error()]}.
|
||||
decode_calldata(ContractString, FunName, Calldata, Options0) ->
|
||||
Options = [no_code | Options0],
|
||||
try
|
||||
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),
|
||||
GetType = fun({typed, _, _, T}) -> T; (T) -> T end,
|
||||
ArgTypes = lists:map(GetType, Args),
|
||||
Type0 = {tuple_t, [], ArgTypes},
|
||||
%% user defined data types such as variants needed to match against
|
||||
Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]),
|
||||
case aeb_fate_abi:decode_calldata(FunName, Calldata) of
|
||||
Type = so_ast_infer_types:unfold_types_in_type(TypeEnv, Type0,
|
||||
[ unfold_record_types
|
||||
, unfold_variant_types
|
||||
, not_unfold_system_alias_types]),
|
||||
case gmb_fate_abi:decode_calldata(FunName, Calldata) of
|
||||
{ok, FateArgs} ->
|
||||
try
|
||||
{tuple_t, [], ArgTypes1} = Type,
|
||||
AstArgs = [ aeso_vm_decode:from_fate(ArgType, FateArg)
|
||||
AstArgs = [ so_vm_decode:from_fate(ArgType, FateArg)
|
||||
|| {ArgType, FateArg} <- lists:zip(ArgTypes1, FateArgs)],
|
||||
{ok, ArgTypes, AstArgs}
|
||||
catch throw:cannot_translate_to_sophia ->
|
||||
Type0Str = prettypr:format(aeso_pretty:type(Type0)),
|
||||
Type0Str = prettypr:format(so_pretty:type(Type0)),
|
||||
Msg = io_lib:format("Cannot translate FATE value ~p\n to Sophia type ~s",
|
||||
[FateArgs, Type0Str]),
|
||||
{error, [aeso_errors:new(data_error, Msg)]}
|
||||
{error, [so_errors:new(data_error, Msg)]}
|
||||
end;
|
||||
{error, _} ->
|
||||
Msg = io_lib:format("Failed to decode calldata binary", []),
|
||||
{error, [aeso_errors:new(data_error, Msg)]}
|
||||
{error, [so_errors:new(data_error, Msg)]}
|
||||
end
|
||||
catch
|
||||
throw:{error, Errors} -> {error, Errors}
|
||||
@ -349,8 +411,8 @@ get_decode_type(FunName, [{Contract, Ann, _, _, Defs}]) when ?IS_CONTRACT_HEAD(C
|
||||
"init" -> {ok, [], {tuple_t, [], []}};
|
||||
_ ->
|
||||
Msg = io_lib:format("Function '~s' is missing in contract", [FunName]),
|
||||
Pos = aeso_errors:pos(Ann),
|
||||
aeso_errors:throw(aeso_errors:new(data_error, Pos, Msg))
|
||||
Pos = so_errors:pos(Ann),
|
||||
so_errors:throw(so_errors:new(data_error, Pos, Msg))
|
||||
end
|
||||
end;
|
||||
get_decode_type(FunName, [_ | Contracts]) ->
|
||||
@ -358,12 +420,12 @@ get_decode_type(FunName, [_ | Contracts]) ->
|
||||
get_decode_type(FunName, Contracts).
|
||||
|
||||
pp_sophia_code(C, Opts)-> pp(C, Opts, pp_sophia_code, fun(Code) ->
|
||||
io:format("~s\n", [prettypr:format(aeso_pretty:decls(Code))])
|
||||
io:format("~s\n", [prettypr:format(so_pretty:decls(Code))])
|
||||
end).
|
||||
pp_ast(C, Opts) -> pp(C, Opts, pp_ast, fun aeso_ast:pp/1).
|
||||
pp_typed_ast(C, Opts)-> pp(C, Opts, pp_typed_ast, fun aeso_ast:pp_typed/1).
|
||||
pp_ast(C, Opts) -> pp(C, Opts, pp_ast, fun so_ast:pp/1).
|
||||
pp_typed_ast(C, Opts)-> pp(C, Opts, pp_typed_ast, fun so_ast:pp_typed/1).
|
||||
|
||||
pp_assembler(C, Opts) -> pp(C, Opts, pp_assembler, fun(Asm) -> io:format("~s", [aeb_fate_asm:pp(Asm)]) end).
|
||||
pp_assembler(C, Opts) -> pp(C, Opts, pp_assembler, fun(Asm) -> io:format("~s", [gmb_fate_asm:pp(Asm)]) end).
|
||||
|
||||
pp(Code, Options, Option, PPFun) ->
|
||||
case proplists:lookup(Option, Options) of
|
||||
@ -377,18 +439,18 @@ pp(Code, Options, Option, PPFun) ->
|
||||
|
||||
-define(protect(Tag, Code), fun() -> try Code catch _:Err1 -> throw({Tag, Err1}) end end()).
|
||||
|
||||
-spec validate_byte_code(map(), string(), options()) -> ok | {error, [aeso_errors:error()]}.
|
||||
-spec validate_byte_code(map(), string(), options()) -> ok | {error, [so_errors:error()]}.
|
||||
validate_byte_code(#{ byte_code := ByteCode, payable := Payable }, Source, Options) ->
|
||||
Fail = fun(Err) -> {error, [aeso_errors:new(data_error, Err)]} end,
|
||||
Fail = fun(Err) -> {error, [so_errors:new(data_error, Err)]} end,
|
||||
try
|
||||
FCode1 = ?protect(deserialize, aeb_fate_code:strip_init_function(aeb_fate_code:deserialize(ByteCode))),
|
||||
FCode1 = ?protect(deserialize, gmb_fate_code:strip_init_function(gmb_fate_code:deserialize(ByteCode))),
|
||||
{FCode2, SrcPayable} =
|
||||
?protect(compile,
|
||||
begin
|
||||
{ok, #{ byte_code := SrcByteCode, payable := SrcPayable }} =
|
||||
from_string1(Source, Options),
|
||||
FCode = aeb_fate_code:deserialize(SrcByteCode),
|
||||
{aeb_fate_code:strip_init_function(FCode), SrcPayable}
|
||||
FCode = gmb_fate_code:deserialize(SrcByteCode),
|
||||
{gmb_fate_code:strip_init_function(FCode), SrcPayable}
|
||||
end),
|
||||
case compare_fate_code(FCode1, FCode2) of
|
||||
ok when SrcPayable /= Payable ->
|
||||
@ -404,10 +466,10 @@ validate_byte_code(#{ byte_code := ByteCode, payable := Payable }, Source, Optio
|
||||
end.
|
||||
|
||||
compare_fate_code(FCode1, FCode2) ->
|
||||
Funs1 = aeb_fate_code:functions(FCode1),
|
||||
Funs2 = aeb_fate_code:functions(FCode2),
|
||||
Syms1 = aeb_fate_code:symbols(FCode1),
|
||||
Syms2 = aeb_fate_code:symbols(FCode2),
|
||||
Funs1 = gmb_fate_code:functions(FCode1),
|
||||
Funs2 = gmb_fate_code:functions(FCode2),
|
||||
Syms1 = gmb_fate_code:symbols(FCode1),
|
||||
Syms2 = gmb_fate_code:symbols(FCode2),
|
||||
FunHashes1 = maps:keys(Funs1),
|
||||
FunHashes2 = maps:keys(Funs2),
|
||||
case FunHashes1 == FunHashes2 of
|
||||
@ -452,13 +514,13 @@ pp_fate_type(T) -> io_lib:format("~w", [T]).
|
||||
|
||||
%% -------------------------------------------------------------------
|
||||
|
||||
-spec parse(string(), aeso_compiler:options()) -> none() | aeso_syntax:ast().
|
||||
-spec parse(string(), so_compiler:options()) -> none() | so_syntax:ast().
|
||||
parse(Text, Options) ->
|
||||
parse(Text, sets:new(), Options).
|
||||
|
||||
-spec parse(string(), sets:set(), aeso_compiler:options()) -> none() | aeso_syntax:ast().
|
||||
-spec parse(string(), sets:set(), so_compiler:options()) -> none() | so_syntax:ast().
|
||||
parse(Text, Included, Options) ->
|
||||
aeso_parser:string(Text, Included, Options).
|
||||
so_parser:string(Text, Included, Options).
|
||||
|
||||
read_contract(Name) ->
|
||||
file:read_file(Name).
|
@ -1,11 +1,13 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @copyright (C) 2025, QPQ AG
|
||||
%%% @copyright (C) 2019, Aeternity Anstalt
|
||||
%%% @doc ADT for structured error messages + formatting.
|
||||
%%%
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(aeso_errors).
|
||||
-module(so_errors).
|
||||
-vsn("9.0.0").
|
||||
|
||||
-type src_file() :: no_file | iolist().
|
||||
|
||||
@ -55,9 +57,9 @@ new(Type, Pos, Msg, 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),
|
||||
File = so_syntax:get_ann(file, Ann, no_file),
|
||||
Line = so_syntax:get_ann(line, Ann, 0),
|
||||
Col = so_syntax:get_ann(col, Ann, 0),
|
||||
pos(File, Line, Col).
|
||||
|
||||
pos(Line, Col) ->
|
File diff suppressed because it is too large
Load Diff
@ -1,12 +1,14 @@
|
||||
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @copyright (C) 2025, QPQ AG
|
||||
%%% @copyright (C) 2018, Aeternity Anstalt
|
||||
%%% @doc Parser combinators for the Sophia parser. Based on
|
||||
%%% Koen Claessen. 2004. Parallel Parsing Processes. J. Functional
|
||||
%%% Programming 14, 6 (November 2004)
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeso_parse_lib).
|
||||
-module(so_parse_lib).
|
||||
-vsn("9.0.0").
|
||||
|
||||
-export([parse/2,
|
||||
return/1, fail/0, fail/1, fail/2, map/2, bind/2,
|
||||
@ -15,7 +17,7 @@
|
||||
many/1, many1/1, sep/2, sep1/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]).
|
||||
|
||||
%% -- Types ------------------------------------------------------------------
|
||||
@ -27,16 +29,16 @@
|
||||
-type tokens() :: [token()].
|
||||
-type error() :: {pos(), string() | no_error}.
|
||||
|
||||
-define(lazy(F), {aeso_parse_lazy, F}).
|
||||
-define(fail(Err), {aeso_parse_fail, Err}).
|
||||
-define(choice(Ps), {aeso_parse_choice, Ps}).
|
||||
-define(bind(P, F), {aeso_parse_bind, P, F}).
|
||||
-define(right(P, Q), {aeso_parse_right, P, Q}).
|
||||
-define(left(P, Q), {aeso_parse_left, P, Q}).
|
||||
-define(map(F, P), {aeso_parse_map, F, P}).
|
||||
-define(layout, aeso_parse_layout).
|
||||
-define(tok(Atom), {aeso_parse_tok, Atom}).
|
||||
-define(return(X), {aeso_parse_return, X}).
|
||||
-define(lazy(F), {so_parse_lazy, F}).
|
||||
-define(fail(Err), {so_parse_fail, Err}).
|
||||
-define(choice(Ps), {so_parse_choice, Ps}).
|
||||
-define(bind(P, F), {so_parse_bind, P, F}).
|
||||
-define(right(P, Q), {so_parse_right, P, Q}).
|
||||
-define(left(P, Q), {so_parse_left, P, Q}).
|
||||
-define(map(F, P), {so_parse_map, F, P}).
|
||||
-define(layout, so_parse_layout).
|
||||
-define(tok(Atom), {so_parse_tok, Atom}).
|
||||
-define(return(X), {so_parse_return, X}).
|
||||
|
||||
%% Type synonyms since you can't have function types as macro arguments for some reason.
|
||||
-type delayed(A) :: fun(() -> A).
|
||||
@ -480,6 +482,13 @@ current_file() ->
|
||||
set_current_file(File) ->
|
||||
put('$current_file', File).
|
||||
|
||||
%% Current source directory
|
||||
current_dir() ->
|
||||
get('$current_dir').
|
||||
|
||||
set_current_dir(File) ->
|
||||
put('$current_dir', File).
|
||||
|
||||
add_current_file({L, C}) -> {current_file(), L, C};
|
||||
add_current_file(Pos) -> Pos.
|
||||
|
@ -1,8 +1,9 @@
|
||||
%%% File : aeso_parser.erl
|
||||
%%% File : so_parser.erl
|
||||
%%% Author : Ulf Norell
|
||||
%%% Description :
|
||||
%%% Created : 1 Mar 2018 by Ulf Norell
|
||||
-module(aeso_parser).
|
||||
-module(so_parser).
|
||||
-vsn("9.0.0").
|
||||
-compile({no_auto_import,[map_get/2]}).
|
||||
|
||||
-export([string/1,
|
||||
@ -17,11 +18,12 @@
|
||||
run_parser/2,
|
||||
run_parser/3]).
|
||||
|
||||
-include("aeso_parse_lib.hrl").
|
||||
-import(aeso_parse_lib, [current_file/0, set_current_file/1,
|
||||
-include("so_parse_lib.hrl").
|
||||
-import(so_parse_lib, [current_file/0, set_current_file/1,
|
||||
current_dir/0, set_current_dir/1,
|
||||
current_include_type/0, set_current_include_type/1]).
|
||||
|
||||
-type parse_result() :: aeso_syntax:ast() | {aeso_syntax:ast(), sets:set(include_hash())} | none().
|
||||
-type parse_result() :: so_syntax:ast() | {so_syntax:ast(), sets:set(include_hash())} | none().
|
||||
|
||||
-type include_hash() :: {string(), binary()}.
|
||||
|
||||
@ -35,14 +37,14 @@ escape_errors({error, Err}) ->
|
||||
string(String) ->
|
||||
string(String, sets:new(), []).
|
||||
|
||||
-spec string(string(), aeso_compiler:options()) -> parse_result().
|
||||
-spec string(string(), so_compiler:options()) -> parse_result().
|
||||
string(String, Opts) ->
|
||||
case lists:keyfind(src_file, 1, Opts) of
|
||||
{src_file, File} -> string(String, sets:add_element(File, sets:new()), Opts);
|
||||
false -> string(String, sets:new(), Opts)
|
||||
end.
|
||||
|
||||
-spec string(string(), sets:set(include_hash()), aeso_compiler:options()) -> parse_result().
|
||||
-spec string(string(), sets:set(include_hash()), so_compiler:options()) -> parse_result().
|
||||
string(String, Included, Opts) ->
|
||||
AST = run_parser(file(), String, Opts),
|
||||
case expand_includes(AST, Included, Opts) of
|
||||
@ -58,19 +60,20 @@ run_parser(P, Inp, Opts) ->
|
||||
|
||||
parse_and_scan(P, S, Opts) ->
|
||||
set_current_file(proplists:get_value(src_file, Opts, no_file)),
|
||||
set_current_dir(proplists:get_value(src_dir, Opts, no_file)),
|
||||
set_current_include_type(proplists:get_value(include_type, Opts, none)),
|
||||
case aeso_scan:scan(S) of
|
||||
{ok, Tokens} -> aeso_parse_lib:parse(P, Tokens);
|
||||
case so_scan:scan(S) of
|
||||
{ok, Tokens} -> so_parse_lib:parse(P, Tokens);
|
||||
{error, {{Input, Pos}, _}} ->
|
||||
{error, {Pos, scan_error, Input}}
|
||||
end.
|
||||
|
||||
-dialyzer({nowarn_function, parse_error/1}).
|
||||
parse_error(Err) ->
|
||||
aeso_errors:throw(mk_error(Err)).
|
||||
so_errors:throw(mk_error(Err)).
|
||||
|
||||
mk_p_err(Pos, Msg) ->
|
||||
aeso_errors:new(parse_error, mk_pos(Pos), lists:flatten(Msg)).
|
||||
so_errors:new(parse_error, mk_pos(Pos), lists:flatten(Msg)).
|
||||
|
||||
mk_error({Pos, scan_error, Input}) ->
|
||||
mk_p_err(Pos, io_lib:format("Lexical error on input: ~s\n", [Input]));
|
||||
@ -84,8 +87,8 @@ mk_error({Pos, include_error, File}) ->
|
||||
Msg = io_lib:format("Couldn't find include file '~s'\n", [File]),
|
||||
mk_p_err(Pos, Msg).
|
||||
|
||||
mk_pos({Line, Col}) -> aeso_errors:pos(Line, Col);
|
||||
mk_pos({File, Line, Col}) -> aeso_errors:pos(File, Line, Col).
|
||||
mk_pos({Line, Col}) -> so_errors:pos(Line, Col);
|
||||
mk_pos({File, Line, Col}) -> so_errors:pos(File, Line, Col).
|
||||
|
||||
%% -- Parsing rules ----------------------------------------------------------
|
||||
|
||||
@ -264,10 +267,11 @@ type300() ->
|
||||
type400() ->
|
||||
choice(
|
||||
[?RULE(typeAtom(), optional(type_args()),
|
||||
case _2 of
|
||||
none -> _1;
|
||||
{ok, Args} -> {app_t, get_ann(_1), _1, Args}
|
||||
end),
|
||||
any_bytes(
|
||||
case _2 of
|
||||
none -> _1;
|
||||
{ok, Args} -> {app_t, get_ann(_1), _1, Args}
|
||||
end)),
|
||||
?RULE(id("bytes"), parens(token(int)),
|
||||
{bytes_t, get_ann(_1), element(3, _2)})
|
||||
]).
|
||||
@ -299,7 +303,7 @@ stmt() ->
|
||||
, {switch, keyword(switch), parens(expr()), maybe_block(branch())}
|
||||
, {'if', keyword('if'), parens(expr()), body()}
|
||||
, {elif, keyword(elif), parens(expr()), body()}
|
||||
, {else, keyword(else), body()}
|
||||
, {'else', keyword('else'), body()}
|
||||
])).
|
||||
|
||||
branch() ->
|
||||
@ -323,7 +327,7 @@ expr100() ->
|
||||
Expr150 = ?LAZY_P(expr150()),
|
||||
choice(
|
||||
[ ?RULE(lam_args(), keyword('=>'), body(), {lam, _2, _1, _3}) %% TODO: better location
|
||||
, {'if', keyword('if'), parens(Expr100), Expr150, right(tok(else), Expr100)}
|
||||
, {'if', keyword('if'), parens(Expr100), Expr150, right(tok('else'), Expr100)}
|
||||
, ?RULE(Expr150, optional(right(tok(':'), type())),
|
||||
case _2 of
|
||||
none -> _1;
|
||||
@ -333,14 +337,19 @@ expr100() ->
|
||||
|
||||
expr150() -> infixl(expr200(), 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(['<', '>', '=<', '>=', '==', '!='])).
|
||||
expr500() -> infixr(expr600(), binop(['::', '++'])).
|
||||
expr500() -> infixr(expr550(), binop(['::', '++'])).
|
||||
expr550() -> infixl(expr600(), binop(['<<', '>>'])).
|
||||
expr600() -> infixl(expr650(), binop(['+', '-'])).
|
||||
expr650() -> ?RULE(many(token('-')), expr700(), prefixes(_1, _2)).
|
||||
expr700() -> infixl(expr750(), binop(['*', '/', mod])).
|
||||
expr750() -> infixl(expr800(), binop(['^'])).
|
||||
expr800() -> ?RULE(many(token('!')), 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)).
|
||||
|
||||
exprAtom() ->
|
||||
@ -359,9 +368,12 @@ exprAtom() ->
|
||||
, ?RULE(tok('['), Expr, binop('..'), Expr, tok(']'), _3(_2, _4))
|
||||
, ?RULE(keyword('('), comma_sep(Expr), tok(')'), tuple_e(_1, _2))
|
||||
, letpat()
|
||||
, hole()
|
||||
])
|
||||
end).
|
||||
|
||||
hole() -> ?RULE(token('???'), {id, get_ann(_1), "???"}).
|
||||
|
||||
comprehension_exp() ->
|
||||
?LAZY_P(choice(
|
||||
[ comprehension_bind()
|
||||
@ -400,7 +412,7 @@ map_key(Key, {ok, {_, Val}}) -> {map_key, Key, Val}.
|
||||
|
||||
elim(E, []) -> E;
|
||||
elim(E, [{proj, Ann, P} | Es]) -> elim({proj, Ann, E, P}, Es);
|
||||
elim(E, [{app, _Ann, Args} | Es]) -> elim({app, aeso_syntax:get_ann(E), E, Args}, Es);
|
||||
elim(E, [{app, _Ann, Args} | Es]) -> elim({app, so_syntax:get_ann(E), E, Args}, Es);
|
||||
elim(E, [{rec_upd, Ann, Flds} | Es]) -> elim(record_update(Ann, E, Flds), Es);
|
||||
elim(E, [{map_get, Ann, Key} | Es]) -> elim({map_get, Ann, E, Key}, Es);
|
||||
elim(E, [{map_get, Ann, Key, Val} | Es]) -> elim({map_get, Ann, E, Key, Val}, Es).
|
||||
@ -514,10 +526,10 @@ id_or_addr() ->
|
||||
?RULE(id(), parse_addr_literal(_1)).
|
||||
|
||||
parse_addr_literal(Id = {id, Ann, Name}) ->
|
||||
case lists:member(lists:sublist(Name, 3), ["ak_", "ok_", "oq_", "ct_"]) of
|
||||
case lists:member(lists:sublist(Name, 3), ["ak_", "ok_", "oq_", "ct_", "sg_"]) of
|
||||
false -> Id;
|
||||
true ->
|
||||
try aeser_api_encoder:decode(list_to_binary(Name)) of
|
||||
try gmser_api_encoder:decode(list_to_binary(Name)) of
|
||||
{Type, Bin} -> {Type, Ann, Bin}
|
||||
catch _:_ ->
|
||||
Id
|
||||
@ -546,13 +558,14 @@ bracket_list(P) -> brackets(comma_sep(P)).
|
||||
|
||||
%% -- Annotations ------------------------------------------------------------
|
||||
|
||||
-type ann() :: aeso_syntax:ann().
|
||||
-type ann_line() :: aeso_syntax:ann_line().
|
||||
-type ann_col() :: aeso_syntax:ann_col().
|
||||
-type ann() :: so_syntax:ann().
|
||||
-type ann_line() :: so_syntax:ann_line().
|
||||
-type ann_col() :: so_syntax:ann_col().
|
||||
|
||||
-spec pos_ann(ann_line(), ann_col()) -> ann().
|
||||
pos_ann(Line, Col) ->
|
||||
[ {file, current_file()}
|
||||
, {dir, current_dir()}
|
||||
, {include_type, current_include_type()}
|
||||
, {line, Line}
|
||||
, {col, Col} ].
|
||||
@ -600,7 +613,7 @@ group_ifs([], Acc) ->
|
||||
group_ifs([{'if', Ann, Cond, Then} | Stmts], Acc) ->
|
||||
{Elses, Rest} = else_branches(Stmts, []),
|
||||
group_ifs(Rest, [build_if(Ann, Cond, Then, Elses) | Acc]);
|
||||
group_ifs([{else, Ann, _} | _], _) ->
|
||||
group_ifs([{'else', Ann, _} | _], _) ->
|
||||
fail({Ann, "No matching 'if' for 'else'"});
|
||||
group_ifs([{elif, Ann, _, _} | _], _) ->
|
||||
fail({Ann, "No matching 'if' for 'elif'"});
|
||||
@ -610,14 +623,14 @@ group_ifs([Stmt | Stmts], Acc) ->
|
||||
build_if(Ann, Cond, Then, [{elif, Ann1, Cond1, Then1} | Elses]) ->
|
||||
{'if', Ann, Cond, Then,
|
||||
set_ann(format, elif, build_if(Ann1, Cond1, Then1, Elses))};
|
||||
build_if(Ann, Cond, Then, [{else, _Ann, Else}]) ->
|
||||
build_if(Ann, Cond, Then, [{'else', _Ann, Else}]) ->
|
||||
{'if', Ann, Cond, Then, Else};
|
||||
build_if(Ann, Cond, Then, []) ->
|
||||
{'if', Ann, Cond, Then, {tuple, [{origin, system}], []}}.
|
||||
|
||||
else_branches([Elif = {elif, _, _, _} | Stmts], Acc) ->
|
||||
else_branches(Stmts, [Elif | Acc]);
|
||||
else_branches([Else = {else, _, _} | Stmts], Acc) ->
|
||||
else_branches([Else = {'else', _, _} | Stmts], Acc) ->
|
||||
{lists:reverse([Else | Acc]), Stmts};
|
||||
else_branches(Stmts, Acc) ->
|
||||
{lists:reverse(Acc), Stmts}.
|
||||
@ -635,7 +648,7 @@ tuple_e(Ann, Exprs) -> {tuple, Ann, Exprs}.
|
||||
|
||||
list_comp_e(Ann, Expr, Binds) -> {list_comp, Ann, Expr, Binds}.
|
||||
|
||||
-spec parse_pattern(aeso_syntax:expr()) -> aeso_parse_lib:parser(aeso_syntax:pat()).
|
||||
-spec parse_pattern(so_syntax:expr()) -> so_parse_lib:parser(so_syntax:pat()).
|
||||
parse_pattern({letpat, Ann, Id, Pat}) ->
|
||||
{letpat, Ann, Id, parse_pattern(Pat)};
|
||||
parse_pattern({app, Ann, Con = {'::', _}, Es}) ->
|
||||
@ -662,19 +675,19 @@ parse_pattern(E = {string, _, _}) -> E;
|
||||
parse_pattern(E = {char, _, _}) -> E;
|
||||
parse_pattern(E) -> bad_expr_err("Not a valid pattern", E).
|
||||
|
||||
-spec parse_field_pattern(aeso_syntax:field(aeso_syntax:expr())) -> aeso_parse_lib:parser(aeso_syntax:field(aeso_syntax:pat())).
|
||||
-spec parse_field_pattern(so_syntax:field(so_syntax:expr())) -> so_parse_lib:parser(so_syntax:field(so_syntax:pat())).
|
||||
parse_field_pattern({field, Ann, F, E}) ->
|
||||
{field, Ann, F, parse_pattern(E)}.
|
||||
|
||||
-spec ret_doc_err(ann(), prettypr:document()) -> aeso_parse_lib:parser(none()).
|
||||
-spec ret_doc_err(ann(), prettypr:document()) -> so_parse_lib:parser(none()).
|
||||
ret_doc_err(Ann, Doc) ->
|
||||
fail(ann_pos(Ann), prettypr:format(Doc)).
|
||||
|
||||
-spec bad_expr_err(string(), aeso_syntax:expr()) -> aeso_parse_lib:parser(none()).
|
||||
-spec bad_expr_err(string(), so_syntax:expr()) -> so_parse_lib:parser(none()).
|
||||
bad_expr_err(Reason, E) ->
|
||||
ret_doc_err(get_ann(E),
|
||||
prettypr:sep([prettypr:text(Reason ++ ":"),
|
||||
prettypr:nest(2, aeso_pretty:expr(E))])).
|
||||
prettypr:nest(2, so_pretty:expr(E))])).
|
||||
|
||||
%% -- Helper functions -------------------------------------------------------
|
||||
|
||||
@ -693,7 +706,7 @@ expand_includes([], Included, Acc, Opts) ->
|
||||
end;
|
||||
expand_includes([{include, Ann, {string, _SAnn, File}} | AST], Included, Acc, Opts) ->
|
||||
case get_include_code(File, Ann, Opts) of
|
||||
{ok, Code} ->
|
||||
{ok, AbsDir, Code} ->
|
||||
Hashed = hash_include(File, Code),
|
||||
case sets:is_element(Hashed, Included) of
|
||||
false ->
|
||||
@ -703,9 +716,10 @@ expand_includes([{include, Ann, {string, _SAnn, File}} | AST], Included, Acc, Op
|
||||
_ -> indirect
|
||||
end,
|
||||
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),
|
||||
case parse_and_scan(file(), Code, Opts2) of
|
||||
case parse_and_scan(file(), Code, Opts3) of
|
||||
{ok, AST1} ->
|
||||
expand_includes(AST1 ++ AST, Included1, Acc, Opts);
|
||||
Err = {error, _} ->
|
||||
@ -723,22 +737,21 @@ expand_includes([E | AST], Included, Acc, Opts) ->
|
||||
read_file(File, Opts) ->
|
||||
case proplists:get_value(include, Opts, {explicit_files, #{}}) of
|
||||
{file_system, Paths} ->
|
||||
CandidateNames = [ filename:join(Dir, File) || Dir <- Paths ],
|
||||
lists:foldr(fun(F, {error, _}) -> file:read_file(F);
|
||||
(_F, OK) -> OK end, {error, not_found}, CandidateNames);
|
||||
lists:foldr(fun(Path, {error, _}) -> read_file_(Path, File);
|
||||
(_Path, OK) -> OK end, {error, not_found}, Paths);
|
||||
{explicit_files, Files} ->
|
||||
case maps:get(binary_to_list(File), Files, not_found) of
|
||||
not_found -> {error, not_found};
|
||||
Src -> {ok, Src}
|
||||
Src -> {ok, File, Src}
|
||||
end;
|
||||
escript ->
|
||||
try
|
||||
Escript = escript:script_name(),
|
||||
{ok, Sections} = escript:extract(Escript, []),
|
||||
Archive = proplists:get_value(archive, Sections),
|
||||
FileName = binary_to_list(filename:join([aesophia, priv, stdlib, File])),
|
||||
FileName = binary_to_list(filename:join([sophia, priv, stdlib, File])),
|
||||
case zip:extract(Archive, [{file_list, [FileName]}, memory]) of
|
||||
{ok, [{_, Src}]} -> {ok, Src};
|
||||
{ok, [{_, Src}]} -> {ok, escript, Src};
|
||||
_ -> {error, not_found}
|
||||
end
|
||||
catch _:_ ->
|
||||
@ -746,31 +759,52 @@ read_file(File, Opts) ->
|
||||
end
|
||||
end.
|
||||
|
||||
read_file_(Path, File) ->
|
||||
AbsFile = filename:join(Path, File),
|
||||
case file:read_file(AbsFile) of
|
||||
{ok, Bin} -> {ok, so_utils:canonical_dir(filename:dirname(AbsFile)), Bin};
|
||||
Err -> Err
|
||||
end.
|
||||
|
||||
stdlib_options() ->
|
||||
StdLibDir = aeso_stdlib:stdlib_include_path(),
|
||||
StdLibDir = so_stdlib:stdlib_include_path(),
|
||||
case filelib:is_dir(StdLibDir) of
|
||||
true -> [{include, {file_system, [StdLibDir]}}];
|
||||
false -> [{include, escript}]
|
||||
end.
|
||||
|
||||
get_include_code(File, Ann, Opts) ->
|
||||
case {read_file(File, Opts), read_file(File, stdlib_options())} of
|
||||
{{ok, Bin}, {ok, _}} ->
|
||||
%% Temporarily extend include paths with the directory of the current file
|
||||
Opts1 = include_current_file_dir(Opts, Ann),
|
||||
case {read_file(File, Opts1), read_file(File, stdlib_options())} of
|
||||
{{ok, Dir, Bin}, {ok, _}} ->
|
||||
case filename:basename(File) == File of
|
||||
true -> { error
|
||||
, fail( ann_pos(Ann)
|
||||
, "Illegal redefinition of standard library " ++ binary_to_list(File))};
|
||||
%% If a path is provided then the stdlib takes lower priority
|
||||
false -> {ok, binary_to_list(Bin)}
|
||||
false -> {ok, Dir, binary_to_list(Bin)}
|
||||
end;
|
||||
{_, {ok, Bin}} ->
|
||||
{ok, binary_to_list(Bin)};
|
||||
{{ok, Bin}, _} ->
|
||||
{ok, binary_to_list(Bin)};
|
||||
{_, {ok, _, Bin}} ->
|
||||
{ok, stdlib, binary_to_list(Bin)};
|
||||
{{ok, Dir, Bin}, _} ->
|
||||
{ok, Dir, binary_to_list(Bin)};
|
||||
{_, _} ->
|
||||
{error, {ann_pos(Ann), include_error, File}}
|
||||
end.
|
||||
|
||||
include_current_file_dir(Opts, Ann) ->
|
||||
case {proplists:get_value(dir, Ann, undefined),
|
||||
proplists:get_value(include, Opts, undefined)} of
|
||||
{undefined, _} -> Opts;
|
||||
{CurrDir, {file_system, Paths}} ->
|
||||
case lists:member(CurrDir, Paths) of
|
||||
false -> [{include, {file_system, [CurrDir | Paths]}} | Opts];
|
||||
true -> Opts
|
||||
end;
|
||||
{_, _} -> Opts
|
||||
end.
|
||||
|
||||
-spec hash_include(string() | binary(), string()) -> include_hash().
|
||||
hash_include(File, Code) when is_binary(File) ->
|
||||
hash_include(binary_to_list(File), Code);
|
||||
@ -784,3 +818,7 @@ auto_imports(L) when is_list(L) ->
|
||||
auto_imports(T) when is_tuple(T) ->
|
||||
auto_imports(tuple_to_list(T));
|
||||
auto_imports(_) -> [].
|
||||
|
||||
any_bytes({id, Ann, "bytes"}) -> {bytes_t, Ann, any};
|
||||
any_bytes({app_t, _, {id, Ann, "bytes"}, []}) -> {bytes_t, Ann, any};
|
||||
any_bytes(Type) -> Type.
|
@ -1,11 +1,13 @@
|
||||
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @copyright (C) 2025, QPQ AG
|
||||
%%% @copyright (C) 2017, Aeternity Anstalt
|
||||
%%% @doc Pretty printer for Sophia.
|
||||
%%%
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeso_pretty).
|
||||
-module(so_pretty).
|
||||
-vsn("9.0.0").
|
||||
|
||||
-import(prettypr, [text/1, sep/1, above/2, beside/2, nest/2, empty/0]).
|
||||
|
||||
@ -13,7 +15,7 @@
|
||||
|
||||
-export_type([options/0]).
|
||||
|
||||
-include("aeso_utils.hrl").
|
||||
-include("so_utils.hrl").
|
||||
|
||||
-type doc() :: prettypr:document().
|
||||
-type options() :: [{indent, non_neg_integer()} | show_generated].
|
||||
@ -24,11 +26,11 @@
|
||||
|
||||
%% -- Options ----------------------------------------------------------------
|
||||
|
||||
-define(aeso_pretty_opts, aeso_pretty_opts).
|
||||
-define(so_pretty_opts, so_pretty_opts).
|
||||
|
||||
-spec options() -> options().
|
||||
options() ->
|
||||
case get(?aeso_pretty_opts) of
|
||||
case get(?so_pretty_opts) of
|
||||
undefined -> [];
|
||||
Opts -> Opts
|
||||
end.
|
||||
@ -45,9 +47,9 @@ indent() -> option(indent, 2).
|
||||
|
||||
-spec with_options(options(), fun(() -> A)) -> A.
|
||||
with_options(Options, Fun) ->
|
||||
put(?aeso_pretty_opts, Options),
|
||||
put(?so_pretty_opts, Options),
|
||||
Res = Fun(),
|
||||
erase(?aeso_pretty_opts),
|
||||
erase(?so_pretty_opts),
|
||||
Res.
|
||||
|
||||
%% -- Pretty printing helpers ------------------------------------------------
|
||||
@ -125,9 +127,9 @@ record(Ds) ->
|
||||
equals(A, B) -> follow(hsep(A, text("=")), B).
|
||||
|
||||
%% typed(A, B) -> A : B.
|
||||
-spec typed(doc(), aeso_syntax:type()) -> doc().
|
||||
-spec typed(doc(), so_syntax:type()) -> doc().
|
||||
typed(A, Type) ->
|
||||
case aeso_syntax:get_ann(origin, Type) == system andalso
|
||||
case so_syntax:get_ann(origin, Type) == system andalso
|
||||
not show_generated() of
|
||||
true -> A;
|
||||
false -> follow(hsep(A, text(":")), type(Type))
|
||||
@ -139,18 +141,18 @@ contract_head(contract_interface) -> text("contract interface").
|
||||
|
||||
%% -- Exports ----------------------------------------------------------------
|
||||
|
||||
-spec decls([aeso_syntax:decl()], options()) -> doc().
|
||||
-spec decls([so_syntax:decl()], options()) -> doc().
|
||||
decls(Ds, Options) ->
|
||||
with_options(Options, fun() -> decls(Ds) end).
|
||||
|
||||
-spec decls([aeso_syntax:decl()]) -> doc().
|
||||
-spec decls([so_syntax:decl()]) -> doc().
|
||||
decls(Ds) -> above([ decl(D) || D <- Ds ]).
|
||||
|
||||
-spec decl(aeso_syntax:decl(), options()) -> doc().
|
||||
-spec decl(so_syntax:decl(), options()) -> doc().
|
||||
decl(D, Options) ->
|
||||
with_options(Options, fun() -> decl(D) end).
|
||||
|
||||
-spec decl(aeso_syntax:decl()) -> doc().
|
||||
-spec decl(so_syntax:decl()) -> doc().
|
||||
decl({Con, Attrs, C, Is, Ds}) when ?IS_CONTRACT_HEAD(Con) ->
|
||||
Mod = fun({Mod, true}) when Mod == payable ->
|
||||
text(atom_to_list(Mod));
|
||||
@ -172,7 +174,7 @@ decl({fun_decl, Ann, F, T}) ->
|
||||
Mod = fun({Mod, true}) when Mod == private; Mod == stateful; Mod == payable ->
|
||||
text(atom_to_list(Mod));
|
||||
(_) -> empty() end,
|
||||
Fun = case aeso_syntax:get_ann(entrypoint, Ann, false) of
|
||||
Fun = case so_syntax:get_ann(entrypoint, Ann, false) of
|
||||
true -> text("entrypoint");
|
||||
false -> text("function")
|
||||
end,
|
||||
@ -181,7 +183,7 @@ decl(D = {letfun, Attrs, _, _, _, _}) ->
|
||||
Mod = fun({Mod, true}) when Mod == private; Mod == stateful; Mod == payable ->
|
||||
text(atom_to_list(Mod));
|
||||
(_) -> empty() end,
|
||||
Fun = case aeso_syntax:get_ann(entrypoint, Attrs, false) of
|
||||
Fun = case so_syntax:get_ann(entrypoint, Attrs, false) of
|
||||
true -> "entrypoint";
|
||||
false -> "function"
|
||||
end,
|
||||
@ -192,20 +194,20 @@ decl(D = {letval, _, _, _}) -> letdecl("let", D);
|
||||
decl({block, _, Ds}) ->
|
||||
above([ decl(D) || D <- Ds ]).
|
||||
|
||||
-spec pragma(aeso_syntax:pragma()) -> doc().
|
||||
-spec pragma(so_syntax:pragma()) -> doc().
|
||||
pragma({compiler, Op, Ver}) ->
|
||||
text("@compiler " ++ atom_to_list(Op) ++ " " ++ string:join([integer_to_list(N) || N <- Ver], ".")).
|
||||
|
||||
-spec expr(aeso_syntax:expr(), options()) -> doc().
|
||||
-spec expr(so_syntax:expr(), options()) -> doc().
|
||||
expr(E, Options) ->
|
||||
with_options(Options, fun() -> expr(E) end).
|
||||
|
||||
-spec expr(aeso_syntax:expr()) -> doc().
|
||||
-spec expr(so_syntax:expr()) -> doc().
|
||||
expr(E) -> expr_p(0, E).
|
||||
|
||||
%% -- Not exported -----------------------------------------------------------
|
||||
|
||||
-spec name(aeso_syntax:id() | aeso_syntax:qid() | aeso_syntax:con() | aeso_syntax:qcon() | aeso_syntax:tvar()) -> doc().
|
||||
-spec name(so_syntax:id() | so_syntax:qid() | so_syntax:con() | so_syntax:qcon() | so_syntax:tvar()) -> doc().
|
||||
name({id, _, Name}) -> text(Name);
|
||||
name({con, _, Name}) -> text(Name);
|
||||
name({qid, _, Names}) -> text(string:join(Names, "."));
|
||||
@ -213,7 +215,7 @@ name({qcon, _, Names}) -> text(string:join(Names, "."));
|
||||
name({tvar, _, Name}) -> text(Name);
|
||||
name({typed, _, Name, _}) -> name(Name).
|
||||
|
||||
-spec letdecl(string(), aeso_syntax:letbind()) -> doc().
|
||||
-spec letdecl(string(), so_syntax:letbind()) -> doc().
|
||||
letdecl(Let, {letval, _, P, E}) ->
|
||||
block_expr(0, hsep([text(Let), expr(P), text("=")]), E);
|
||||
letdecl(Let, {letfun, _, F, Args, T, [GuardedBody]}) ->
|
||||
@ -221,14 +223,14 @@ letdecl(Let, {letfun, _, F, Args, T, [GuardedBody]}) ->
|
||||
letdecl(Let, {letfun, _, F, Args, T, GuardedBodies}) ->
|
||||
block(hsep([text(Let), typed(beside(name(F), expr({tuple, [], Args})), T)]), above(lists:map(fun(GB) -> guarded_body(GB, "=") end, GuardedBodies))).
|
||||
|
||||
-spec args([aeso_syntax:arg()]) -> doc().
|
||||
-spec args([so_syntax:arg()]) -> doc().
|
||||
args(Args) ->
|
||||
tuple(lists:map(fun arg/1, Args)).
|
||||
|
||||
-spec arg(aeso_syntax:arg()) -> doc().
|
||||
-spec arg(so_syntax:arg()) -> doc().
|
||||
arg({arg, _, X, T}) -> typed(name(X), T).
|
||||
|
||||
-spec typedecl(alias_t | record_t | variant_t, aeso_syntax:id(), [aeso_syntax:tvar()]) -> doc().
|
||||
-spec typedecl(alias_t | record_t | variant_t, so_syntax:id(), [so_syntax:tvar()]) -> doc().
|
||||
typedecl(Kind, T, Vars) ->
|
||||
KW = case Kind of
|
||||
alias_t -> text("type");
|
||||
@ -241,26 +243,28 @@ typedecl(Kind, T, Vars) ->
|
||||
tuple(lists:map(fun name/1, Vars)))
|
||||
end.
|
||||
|
||||
-spec typedef(aeso_syntax:typedef()) -> doc().
|
||||
-spec typedef(so_syntax:typedef()) -> doc().
|
||||
typedef({alias_t, Type}) -> type(Type);
|
||||
typedef({record_t, Fields}) ->
|
||||
record(lists:map(fun field_t/1, Fields));
|
||||
typedef({variant_t, Constructors}) ->
|
||||
par(punctuate(text(" |"), lists:map(fun constructor_t/1, Constructors))).
|
||||
|
||||
-spec constructor_t(aeso_syntax:constructor_t()) -> doc().
|
||||
-spec constructor_t(so_syntax:constructor_t()) -> doc().
|
||||
constructor_t({constr_t, _, C, []}) -> name(C);
|
||||
constructor_t({constr_t, _, C, Args}) -> beside(name(C), args_type(Args)).
|
||||
|
||||
-spec field_t(aeso_syntax:field_t()) -> doc().
|
||||
-spec field_t(so_syntax:field_t()) -> doc().
|
||||
field_t({field_t, _, Name, Type}) ->
|
||||
typed(name(Name), Type).
|
||||
|
||||
-spec type(aeso_syntax:type(), options()) -> doc().
|
||||
-spec type(so_syntax:type(), options()) -> doc().
|
||||
type(Type, Options) ->
|
||||
with_options(Options, fun() -> type(Type) end).
|
||||
|
||||
-spec type(aeso_syntax:type()) -> doc().
|
||||
-spec type(so_syntax:type()) -> doc().
|
||||
type(F = {fun_t, _, _, var_args, _}) ->
|
||||
type(setelement(4, F, [var_args]));
|
||||
type({fun_t, _, Named, Args, Ret}) ->
|
||||
follow(hsep(args_type(Named ++ Args), text("=>")), type(Ret));
|
||||
type({type_sig, _, Named, Args, Ret}) ->
|
||||
@ -273,7 +277,9 @@ type({tuple_t, _, Args}) ->
|
||||
tuple_type(Args);
|
||||
type({args_t, _, 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}) ->
|
||||
text(lists:concat(["bytes(", Len, ")"]));
|
||||
type({if_t, _, Id, Then, Else}) ->
|
||||
@ -288,13 +294,15 @@ type(T = {id, _, _}) -> name(T);
|
||||
type(T = {qid, _, _}) -> name(T);
|
||||
type(T = {con, _, _}) -> name(T);
|
||||
type(T = {qcon, _, _}) -> name(T);
|
||||
type(T = {tvar, _, _}) -> name(T).
|
||||
type(T = {tvar, _, _}) -> name(T);
|
||||
|
||||
-spec args_type([aeso_syntax:type()]) -> doc().
|
||||
type(var_args) -> text("var_args").
|
||||
|
||||
-spec args_type([so_syntax:type()]) -> doc().
|
||||
args_type(Args) ->
|
||||
tuple(lists:map(fun type/1, Args)).
|
||||
|
||||
-spec tuple_type([aeso_syntax:type()]) -> doc().
|
||||
-spec tuple_type([so_syntax:type()]) -> doc().
|
||||
tuple_type([]) ->
|
||||
text("unit");
|
||||
tuple_type(Factors) ->
|
||||
@ -304,7 +312,7 @@ tuple_type(Factors) ->
|
||||
, text(")")
|
||||
]).
|
||||
|
||||
-spec expr_p(integer(), aeso_syntax:arg_expr()) -> doc().
|
||||
-spec expr_p(integer(), so_syntax:arg_expr()) -> doc().
|
||||
expr_p(P, {letpat, _, Id, Pat}) ->
|
||||
paren(P > 100, follow(hsep(expr(Id), text("=")), expr(Pat)));
|
||||
expr_p(P, {named_arg, _, Name, E}) ->
|
||||
@ -312,7 +320,7 @@ expr_p(P, {named_arg, _, Name, E}) ->
|
||||
expr_p(P, {lam, _, Args, E}) ->
|
||||
paren(P > 100, follow(hsep(args(Args), text("=>")), expr_p(100, E)));
|
||||
expr_p(P, If = {'if', Ann, Cond, Then, Else}) ->
|
||||
Format = aeso_syntax:get_ann(format, If),
|
||||
Format = so_syntax:get_ann(format, If),
|
||||
if Format == '?:' ->
|
||||
paren(P > 100,
|
||||
follow(expr_p(200, Cond),
|
||||
@ -355,7 +363,7 @@ expr_p(P, {assign, _, LV, E}) ->
|
||||
expr_p(_, {app, _, {'..', _}, [A, B]}) ->
|
||||
list([infix(0, '..', A, B)]);
|
||||
expr_p(P, E = {app, _, F = {Op, _}, Args}) when is_atom(Op) ->
|
||||
case {aeso_syntax:get_ann(format, E), Args} of
|
||||
case {so_syntax:get_ann(format, E), Args} of
|
||||
{infix, [A, B]} -> infix(P, Op, A, B);
|
||||
{prefix, [A]} -> prefix(P, Op, A);
|
||||
_ -> app(P, F, Args)
|
||||
@ -366,7 +374,7 @@ expr_p(P, {app, _, F, Args}) ->
|
||||
app(P, F, Args);
|
||||
%% -- Constants
|
||||
expr_p(_, E = {int, _, N}) ->
|
||||
S = case aeso_syntax:get_ann(format, E) of
|
||||
S = case so_syntax:get_ann(format, E) of
|
||||
hex -> "0x" ++ integer_to_list(N, 16);
|
||||
_ -> integer_to_list(N)
|
||||
end,
|
||||
@ -381,8 +389,9 @@ expr_p(_, {Type, _, Bin})
|
||||
when Type == account_pubkey;
|
||||
Type == contract_pubkey;
|
||||
Type == oracle_pubkey;
|
||||
Type == oracle_query_id ->
|
||||
text(binary_to_list(aeser_api_encoder:encode(Type, Bin)));
|
||||
Type == oracle_query_id;
|
||||
Type == signature ->
|
||||
text(binary_to_list(gmser_api_encoder:encode(Type, Bin)));
|
||||
expr_p(_, {string, _, <<>>}) -> text("\"\"");
|
||||
expr_p(_, {string, _, S}) ->
|
||||
text(io_lib:format("\"~s\"", [binary_to_list(S)]));
|
||||
@ -395,7 +404,7 @@ expr_p(_, {char, _, C}) ->
|
||||
text("'" ++ tl(lists:droplast(S)) ++ "'");
|
||||
_ ->
|
||||
S = lists:flatten(
|
||||
io_lib:format("'~ts'", [list_to_binary(aeso_scan:utf8_encode([C]))])),
|
||||
io_lib:format("'~ts'", [list_to_binary(so_scan:utf8_encode([C]))])),
|
||||
text(S)
|
||||
end;
|
||||
%% -- Names
|
||||
@ -412,9 +421,9 @@ stmt_p({'if', _, Cond, Then}) ->
|
||||
block_expr(200, beside(text("if"), paren(expr(Cond))), Then);
|
||||
stmt_p({elif, _, Cond, Then}) ->
|
||||
block_expr(200, beside(text("elif"), paren(expr(Cond))), Then);
|
||||
stmt_p({else, Else}) ->
|
||||
stmt_p({'else', Else}) ->
|
||||
HideGenerated = not show_generated(),
|
||||
case aeso_syntax:get_ann(origin, Else) of
|
||||
case so_syntax:get_ann(origin, Else) of
|
||||
system when HideGenerated -> empty();
|
||||
_ -> block_expr(200, text("else"), Else)
|
||||
end.
|
||||
@ -426,20 +435,26 @@ lc_bind({comprehension_if, _, E}) ->
|
||||
lc_bind(Let) ->
|
||||
letdecl("let", Let).
|
||||
|
||||
-spec bin_prec(aeso_syntax:bin_op()) -> {integer(), integer(), integer()}.
|
||||
-spec bin_prec(so_syntax:bin_op()) -> {integer(), integer(), integer()}.
|
||||
bin_prec('..') -> { 0, 0, 0}; %% Always printed inside '[ ]'
|
||||
bin_prec('=') -> { 0, 0, 0}; %% Always printed inside '[ ]'
|
||||
bin_prec('@') -> { 0, 0, 0}; %% Only in error messages
|
||||
bin_prec('|>') -> {150, 150, 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('++') -> {500, 600, 500};
|
||||
bin_prec('::') -> {500, 600, 500};
|
||||
bin_prec('++') -> {500, 550, 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('*') -> {700, 700, 750};
|
||||
@ -447,14 +462,15 @@ bin_prec('/') -> {700, 700, 750};
|
||||
bin_prec(mod) -> {700, 700, 750};
|
||||
bin_prec('^') -> {750, 750, 800}.
|
||||
|
||||
-spec un_prec(aeso_syntax:un_op()) -> {integer(), integer()}.
|
||||
-spec un_prec(so_syntax:un_op()) -> {integer(), integer()}.
|
||||
un_prec('-') -> {650, 650};
|
||||
un_prec('!') -> {800, 800}.
|
||||
un_prec('!') -> {800, 800};
|
||||
un_prec('bnot') -> {850, 850}.
|
||||
|
||||
equals(Ann, A, B) ->
|
||||
{app, [{format, infix} | Ann], {'=', Ann}, [A, B]}.
|
||||
|
||||
-spec infix(integer(), aeso_syntax:bin_op(), aeso_syntax:expr(), aeso_syntax:expr()) -> doc().
|
||||
-spec infix(integer(), so_syntax:bin_op(), so_syntax:expr(), so_syntax:expr()) -> doc().
|
||||
infix(P, Op, A, B) ->
|
||||
{Top, L, R} = bin_prec(Op),
|
||||
paren(P > Top,
|
||||
@ -516,9 +532,9 @@ statement(E) -> expr(E).
|
||||
get_elifs(Expr) -> get_elifs(Expr, []).
|
||||
|
||||
get_elifs(If = {'if', Ann, Cond, Then, Else}, Elifs) ->
|
||||
case aeso_syntax:get_ann(format, If) of
|
||||
case so_syntax:get_ann(format, If) of
|
||||
elif -> get_elifs(Else, [{elif, Ann, Cond, Then} | Elifs]);
|
||||
_ -> {lists:reverse(Elifs), If}
|
||||
end;
|
||||
get_elifs(Else, Elifs) -> {lists:reverse(Elifs), {else, Else}}.
|
||||
get_elifs(Else, Elifs) -> {lists:reverse(Elifs), {'else', Else}}.
|
||||
|
@ -1,15 +1,17 @@
|
||||
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @copyright (C) 2025, QPQ AG
|
||||
%%% @copyright (C) 2017, Aeternity Anstalt
|
||||
%%% @doc The Sophia lexer.
|
||||
%%%
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeso_scan).
|
||||
-module(so_scan).
|
||||
-vsn("9.0.0").
|
||||
|
||||
-export([scan/1, utf8_encode/1]).
|
||||
|
||||
-import(aeso_scan_lib, [token/1, token/2, symbol/0, skip/0,
|
||||
-import(so_scan_lib, [token/1, token/2, symbol/0, skip/0,
|
||||
override/2, push/2, pop/1]).
|
||||
|
||||
lexer() ->
|
||||
@ -45,7 +47,7 @@ lexer() ->
|
||||
|
||||
Keywords = ["contract", "include", "let", "switch", "type", "record", "datatype", "if", "elif", "else", "function",
|
||||
"stateful", "payable", "true", "false", "mod", "public", "entrypoint", "private", "indexed", "namespace",
|
||||
"interface", "main", "using", "as", "for", "hiding"
|
||||
"interface", "main", "using", "as", "for", "hiding", "band", "bor", "bxor", "bnot"
|
||||
],
|
||||
KW = string:join(Keywords, "|"),
|
||||
|
||||
@ -79,8 +81,8 @@ lexer() ->
|
||||
[{code, Rules}, {comment, CommentRules}].
|
||||
|
||||
scan(String) ->
|
||||
Lexer = aeso_scan_lib:compile(lexer()),
|
||||
aeso_scan_lib:string(Lexer, code, String).
|
||||
Lexer = so_scan_lib:compile(lexer()),
|
||||
so_scan_lib:string(Lexer, code, String).
|
||||
|
||||
%% -- Helpers ----------------------------------------------------------------
|
||||
|
@ -1,10 +1,12 @@
|
||||
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @copyright (C) 2025, QPQ AG
|
||||
%%% @copyright (C) 2017, Aeternity Anstalt
|
||||
%%% @doc A customisable lexer.
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeso_scan_lib).
|
||||
-module(so_scan_lib).
|
||||
-vsn("9.0.0").
|
||||
|
||||
-export([compile/1, string/3,
|
||||
token/1, token/2, symbol/0, skip/0,
|
@ -1,17 +1,18 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @author Radosław Rowicki
|
||||
%%% @copyright (C) 2025, QPQ AG
|
||||
%%% @copyright (C) 2019, Aeternity Anstalt
|
||||
%%% @doc
|
||||
%%% Standard library for Sophia
|
||||
%%% @end
|
||||
%%% Created : 6 July 2019
|
||||
%%%
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(aeso_stdlib).
|
||||
-module(so_stdlib).
|
||||
-vsn("9.0.0").
|
||||
|
||||
-export([stdlib_include_path/0]).
|
||||
|
||||
stdlib_include_path() ->
|
||||
filename:join([code:priv_dir(aesophia), "stdlib"]).
|
||||
{file, BEAM} = code:is_loaded(?MODULE),
|
||||
filename:join(filename:dirname(filename:dirname(BEAM)), "priv/stdlib").
|
||||
|
@ -1,19 +1,21 @@
|
||||
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @copyright (C) 2025, QPQ AG
|
||||
%%% @copyright (C) 2017, Aeternity Anstalt
|
||||
%%% @doc Sophia abstract syntax types.
|
||||
%%%
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(aeso_syntax).
|
||||
-module(so_syntax).
|
||||
-vsn("9.0.0").
|
||||
|
||||
-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([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([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]).
|
||||
@ -24,8 +26,9 @@
|
||||
-type ann_col() :: integer().
|
||||
-type ann_origin() :: system | user.
|
||||
-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 | entrypoint].
|
||||
|
||||
-type name() :: string().
|
||||
@ -99,6 +102,7 @@
|
||||
| {contract_pubkey, ann(), binary()}
|
||||
| {oracle_pubkey, ann(), binary()}
|
||||
| {oracle_query_id, ann(), binary()}
|
||||
| {signature, ann(), binary()}
|
||||
| {string, ann(), binary()}
|
||||
| {char, ann(), integer()}.
|
||||
|
||||
@ -106,8 +110,8 @@
|
||||
|
||||
-type bin_op() :: '+' | '-' | '*' | '/' | mod | '^'
|
||||
| '++' | '::' | '<' | '>' | '=<' | '>=' | '==' | '!='
|
||||
| '||' | '&&' | '..'.
|
||||
-type un_op() :: '-' | '!'.
|
||||
| '||' | '&&' | '..' | 'band' | 'bor' | 'bxor' | '>>' | '<<' | '|>'.
|
||||
-type un_op() :: '-' | '!' | 'bnot'.
|
||||
|
||||
-type expr()
|
||||
:: {lam, ann(), [arg()], expr()}
|
@ -1,12 +1,14 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @copyright (C) 2025, QPQ AG
|
||||
%%% @copyright (C) 2018, Aeternity Anstalt
|
||||
%%% @doc
|
||||
%%% Sophia syntax utilities.
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeso_syntax_utils).
|
||||
-module(so_syntax_utils).
|
||||
-vsn("9.0.0").
|
||||
|
||||
-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}).
|
||||
|
||||
@ -17,25 +19,27 @@
|
||||
-type kind() :: decl | type | bind_type | expr | bind_expr.
|
||||
|
||||
-spec fold(alg(A), fun((kind(), _) -> A), kind(), E | [E]) -> A
|
||||
when E :: aeso_syntax:decl()
|
||||
| aeso_syntax:typedef()
|
||||
| aeso_syntax:field_t()
|
||||
| aeso_syntax:constructor_t()
|
||||
| aeso_syntax:type()
|
||||
| aeso_syntax:expr()
|
||||
| aeso_syntax:pat()
|
||||
| aeso_syntax:arg()
|
||||
| aeso_syntax:alt()
|
||||
| aeso_syntax:elim()
|
||||
| aeso_syntax:arg_expr()
|
||||
| aeso_syntax:field(aeso_syntax:expr())
|
||||
| aeso_syntax:stmt().
|
||||
when E :: so_syntax:decl()
|
||||
| so_syntax:typedef()
|
||||
| so_syntax:field_t()
|
||||
| so_syntax:constructor_t()
|
||||
| so_syntax:type()
|
||||
| so_syntax:expr()
|
||||
| so_syntax:pat()
|
||||
| so_syntax:arg()
|
||||
| so_syntax:alt()
|
||||
| so_syntax:elim()
|
||||
| so_syntax:arg_expr()
|
||||
| so_syntax:field(so_syntax:expr())
|
||||
| so_syntax:stmt().
|
||||
fold(Alg = #alg{zero = Zero, plus = Plus, scoped = Scoped}, Fun, K, X) ->
|
||||
ExprKind = if K == bind_expr -> bind_expr; true -> expr end,
|
||||
TypeKind = if K == bind_type -> bind_type; true -> type end,
|
||||
Sum = fun(Xs) -> lists:foldl(Plus, Zero, Xs) end,
|
||||
Same = fun(A) -> fold(Alg, Fun, K, A) end,
|
||||
Decl = fun(D) -> fold(Alg, Fun, decl, D) end,
|
||||
Type = fun(T) -> fold(Alg, Fun, type, T) end,
|
||||
Expr = fun(E) -> fold(Alg, Fun, expr, E) end,
|
||||
Type = fun(T) -> fold(Alg, Fun, TypeKind, T) end,
|
||||
Expr = fun(E) -> fold(Alg, Fun, ExprKind, E) end,
|
||||
BindExpr = fun(P) -> fold(Alg, Fun, bind_expr, P) end,
|
||||
BindType = fun(T) -> fold(Alg, Fun, bind_type, T) end,
|
||||
Top = Fun(K, X),
|
||||
@ -108,8 +112,16 @@ fold(Alg = #alg{zero = Zero, plus = Plus, scoped = Scoped}, Fun, K, X) ->
|
||||
|
||||
%% Name dependencies
|
||||
|
||||
%% Used ids, top level
|
||||
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) ->
|
||||
F = fun({{type, [X]}, _}) -> [X];
|
||||
@ -122,7 +134,7 @@ used_types([Top] = _CurrentNS, T) ->
|
||||
| {type, [string()]}
|
||||
| {namespace, [string()]}.
|
||||
|
||||
-spec entity_alg() -> alg(#{entity() => aeso_syntax:ann()}).
|
||||
-spec entity_alg() -> alg(#{entity() => so_syntax:ann()}).
|
||||
entity_alg() ->
|
||||
IsBound = fun({K, _}) -> lists:member(K, [bound_term, bound_type]) end,
|
||||
Unbind = fun(bound_term) -> term; (bound_type) -> type end,
|
||||
@ -137,7 +149,7 @@ entity_alg() ->
|
||||
, plus = fun maps:merge/2
|
||||
, scoped = Scoped }.
|
||||
|
||||
-spec used(_) -> [{entity(), aeso_syntax:ann()}].
|
||||
-spec used(_) -> [{entity(), so_syntax:ann()}].
|
||||
used(D) ->
|
||||
Kind = fun(expr) -> term;
|
||||
(bind_expr) -> bound_term;
|
||||
@ -155,4 +167,3 @@ used(D) ->
|
||||
(_, _) -> #{}
|
||||
end, decl, D)),
|
||||
lists:filter(NotBound, Xs).
|
||||
|
@ -1,15 +1,29 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @copyright (C) 2025, QPQ AG
|
||||
%%% @copyright (C) 2018, Aeternity Anstalt
|
||||
%%% @doc
|
||||
%%% Sophia utility functions.
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeso_utils).
|
||||
-module(so_utils).
|
||||
-vsn("9.0.0").
|
||||
|
||||
-export([scc/1]).
|
||||
-export([scc/1, canonical_dir/1]).
|
||||
|
||||
-export_type([graph/1]).
|
||||
|
||||
%% -- Simplistic canonical directory
|
||||
%% Note: no attempts to be 100% complete
|
||||
|
||||
canonical_dir(Dir) ->
|
||||
{ok, Cwd} = file:get_cwd(),
|
||||
AbsName = filename:absname(Dir),
|
||||
RelAbsName = filename:join(tl(filename:split(AbsName))),
|
||||
case filelib:safe_relative_path(RelAbsName, Cwd) of
|
||||
unsafe -> AbsName;
|
||||
Simplified -> filename:absname(Simplified, "")
|
||||
end.
|
||||
|
||||
%% -- Topological sort
|
||||
|
||||
-type graph(Node) :: #{Node => [Node]}. %% List of incoming edges (dependencies).
|
@ -1,20 +1,24 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @copyright (C) 2025, QPQ AG
|
||||
%%% @copyright (C) 2017, Aeternity Anstalt
|
||||
%%% @doc Decoding fate data to AST
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(aeso_vm_decode).
|
||||
-module(so_vm_decode).
|
||||
-vsn("9.0.0").
|
||||
|
||||
-export([ from_fate/2 ]).
|
||||
|
||||
-include_lib("aebytecode/include/aeb_fate_data.hrl").
|
||||
-include_lib("gmbytecode/include/gmb_fate_data.hrl").
|
||||
|
||||
-spec from_fate(aeso_syntax:type(), aeb_fate_data:fate_type()) -> aeso_syntax:expr().
|
||||
-spec from_fate(so_syntax:type(), gmb_fate_data:fate_type()) -> so_syntax:expr().
|
||||
from_fate({id, _, "address"}, ?FATE_ADDRESS(Bin)) -> {account_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({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({con, _, _Name}, ?FATE_CONTRACT(Bin)) -> {contract_pubkey, [], Bin};
|
||||
from_fate({bytes_t, _, any}, ?FATE_BYTES(Bin)) -> make_any_bytes(Bin);
|
||||
from_fate({bytes_t, _, N}, ?FATE_BYTES(Bin)) when byte_size(Bin) == N -> {bytes, [], Bin};
|
||||
from_fate({id, _, "bits"}, ?FATE_BITS(N)) -> make_bits(N);
|
||||
from_fate({id, _, "int"}, N) when is_integer(N) ->
|
||||
@ -78,6 +82,7 @@ from_fate_builtin(QType, Val) ->
|
||||
Hsh = {bytes_t, [], 32},
|
||||
I32 = {bytes_t, [], 32},
|
||||
I48 = {bytes_t, [], 48},
|
||||
Bts = {bytes_t, [], any},
|
||||
Qid = fun(Name) -> {qid, [], Name} end,
|
||||
Map = fun(KT, VT) -> {app_t, [], {id, [], "map"}, [KT, VT]} end,
|
||||
ChainTxArities = [3, 0, 0, 0, 0, 0, 1, 1, 1, 2, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 0],
|
||||
@ -88,7 +93,7 @@ from_fate_builtin(QType, Val) ->
|
||||
|
||||
{["AENS", "name"], {variant, [3], 0, {Addr, TTL, Ptrs}}} ->
|
||||
App(["AENS","Name"], [Chk(Adr, Addr), Chk(Qid(["Chain", "ttl"]), TTL),
|
||||
Chk(Map(Str, Qid(["AENS", "pointee"])), Ptrs)]);
|
||||
Chk(Map(Str, Qid(["AENS", "pointee"])), Ptrs)]);
|
||||
|
||||
{["AENS", "pointee"], {variant, [1, 1, 1, 1], 0, {Addr}}} ->
|
||||
App(["AENS","AccountPt"], [Chk(Adr, Addr)]);
|
||||
@ -99,6 +104,21 @@ from_fate_builtin(QType, Val) ->
|
||||
{["AENS", "pointee"], {variant, [1, 1, 1, 1], 3, {Addr}}} ->
|
||||
App(["AENS","ChannelPt"], [Chk(Adr, Addr)]);
|
||||
|
||||
{["AENSv2", "name"], {variant, [3], 0, {Addr, TTL, Ptrs}}} ->
|
||||
App(["AENSv2","Name"], [Chk(Adr, Addr), Chk(Qid(["Chain", "ttl"]), TTL),
|
||||
Chk(Map(Str, Qid(["AENSv2", "pointee"])), Ptrs)]);
|
||||
|
||||
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 0, {Value}}} ->
|
||||
App(["AENSv2","AccountPt"], [Chk(Adr, Value)]);
|
||||
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 1, {Value}}} ->
|
||||
App(["AENSv2","OraclePt"], [Chk(Adr, Value)]);
|
||||
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 2, {Value}}} ->
|
||||
App(["AENSv2","ContractPt"], [Chk(Adr, Value)]);
|
||||
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 3, {Value}}} ->
|
||||
App(["AENSv2","ChannelPt"], [Chk(Adr, Value)]);
|
||||
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 4, {Value}}} ->
|
||||
App(["AENSv2","DataPt"], [Chk(Bts, Value)]);
|
||||
|
||||
{["Chain", "ga_meta_tx"], {variant, [2], 0, {Addr, X}}} ->
|
||||
App(["Chain","GAMetaTx"], [Chk(Adr, Addr), Chk(Int, X)]);
|
||||
|
||||
@ -170,3 +190,5 @@ make_bits(Set, Zero, I, N) when 0 == N rem 2 ->
|
||||
make_bits(Set, Zero, I, N) ->
|
||||
{app, [], Set, [make_bits(Set, Zero, I + 1, N div 2), {int, [], I}]}.
|
||||
|
||||
make_any_bytes(Bin) ->
|
||||
{app, [], {qid, [], ["Bytes", "to_any_size"]}, [{bytes, [], Bin}]}.
|
@ -1,6 +1,7 @@
|
||||
-module(aeso_warnings).
|
||||
-module(so_warnings).
|
||||
-vsn("9.0.0").
|
||||
|
||||
-record(warn, { pos :: aeso_errors:pos()
|
||||
-record(warn, { pos :: so_errors:pos()
|
||||
, message :: iolist()
|
||||
}).
|
||||
|
||||
@ -16,16 +17,16 @@
|
||||
]).
|
||||
|
||||
new(Msg) ->
|
||||
new(aeso_errors:pos(0, 0), Msg).
|
||||
new(so_errors:pos(0, 0), Msg).
|
||||
|
||||
new(Pos, Msg) ->
|
||||
#warn{ pos = Pos, message = Msg }.
|
||||
|
||||
warn_to_err(Kind, #warn{ pos = Pos, message = Msg }) ->
|
||||
aeso_errors:new(Kind, Pos, lists:flatten(Msg)).
|
||||
so_errors:new(Kind, Pos, lists:flatten(Msg)).
|
||||
|
||||
sort_warnings(Warnings) ->
|
||||
lists:sort(fun(W1, W2) -> W1#warn.pos =< W2#warn.pos end, Warnings).
|
||||
|
||||
pp(#warn{ pos = Pos, message = Msg }) ->
|
||||
lists:flatten(io_lib:format("Warning~s:\n~s", [aeso_errors:pp_pos(Pos), Msg])).
|
||||
lists:flatten(io_lib:format("Warning~s:\n~s", [so_errors:pp_pos(Pos), Msg])).
|
@ -1,14 +1,13 @@
|
||||
{application, aesophia,
|
||||
[{description, "Compiler for Aeternity Sophia language"},
|
||||
{vsn, "7.0.0"},
|
||||
{application, sophia,
|
||||
[{description, "Compiler for Sophia language"},
|
||||
{vsn, "9.0.0"},
|
||||
{registered, []},
|
||||
{applications,
|
||||
[kernel,
|
||||
stdlib,
|
||||
jsx,
|
||||
syntax_tools,
|
||||
getopt,
|
||||
aebytecode,
|
||||
gmbytecode,
|
||||
eblake2
|
||||
]},
|
||||
{env,[]},
|
@ -1,22 +0,0 @@
|
||||
-module(aeso_eunit_SUITE).
|
||||
|
||||
-compile([export_all, nowarn_export_all]).
|
||||
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
|
||||
all() ->
|
||||
[{group, eunit}].
|
||||
|
||||
groups() ->
|
||||
[{eunit, [], [ aeso_scan_tests
|
||||
, aeso_parser_tests
|
||||
, aeso_compiler_tests
|
||||
, aeso_abi_tests
|
||||
, aeso_aci_tests
|
||||
]}].
|
||||
|
||||
aeso_scan_tests(_Config) -> ok = eunit:test(aeso_scan_tests).
|
||||
aeso_parser_tests(_Config) -> ok = eunit:test(aeso_parser_tests).
|
||||
aeso_compiler_tests(_Config) -> ok = eunit:test(aeso_compiler_tests).
|
||||
aeso_abi_tests(_Config) -> ok = eunit:test(aeso_abi_tests).
|
||||
aeso_aci_tests(_Config) -> ok = eunit:test(aeso_aci_tests).
|
@ -2,27 +2,12 @@ contract interface Remote =
|
||||
entrypoint main_fun : (int) => unit
|
||||
|
||||
contract AddrChain =
|
||||
type o_type = oracle(string, map(string, int))
|
||||
type oq_type = oracle_query(string, map(string, int))
|
||||
|
||||
entrypoint is_o(a : address) =
|
||||
Address.is_oracle(a)
|
||||
|
||||
entrypoint is_c(a : address) =
|
||||
Address.is_contract(a)
|
||||
|
||||
// entrypoint get_o(a : address) : option(o_type) =
|
||||
// Address.get_oracle(a)
|
||||
|
||||
// entrypoint get_c(a : address) : option(Remote) =
|
||||
// Address.get_contract(a)
|
||||
|
||||
entrypoint check_o(o : o_type) =
|
||||
Oracle.check(o)
|
||||
|
||||
entrypoint check_oq(o : o_type, oq : oq_type) =
|
||||
Oracle.check_query(o, oq)
|
||||
|
||||
// entrypoint h_to_i(h : hash) : int =
|
||||
// Hash.to_int(h)
|
||||
|
||||
|
@ -5,10 +5,6 @@ contract interface Remote =
|
||||
contract AddressLiterals =
|
||||
entrypoint addr() : address =
|
||||
ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt
|
||||
entrypoint oracle() : oracle(int, bool) =
|
||||
ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5
|
||||
entrypoint query() : oracle_query(int, bool) =
|
||||
oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY
|
||||
entrypoint contr() : Remote =
|
||||
ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ
|
||||
entrypoint contr_addr() : Remote =
|
||||
|
@ -6,77 +6,71 @@ main contract AENSTest =
|
||||
// Name resolution
|
||||
|
||||
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) =
|
||||
AENS.resolve(name, key)
|
||||
AENSv2.resolve(name, key)
|
||||
|
||||
stateful entrypoint resolve_contract(name : string, key : string) : option(C) =
|
||||
AENS.resolve(name, key)
|
||||
|
||||
stateful entrypoint resolve_oracle(name : string, key : string) : option(oracle(int, int)) =
|
||||
AENS.resolve(name, key)
|
||||
|
||||
stateful entrypoint resolve_oracle_query(name : string, key : string) : option(oracle_query(int, int)) =
|
||||
AENS.resolve(name, key)
|
||||
AENSv2.resolve(name, key)
|
||||
|
||||
// Transactions
|
||||
|
||||
stateful entrypoint preclaim(addr : address, // Claim on behalf of this account (can be Contract.address)
|
||||
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)
|
||||
chash : hash, // Commitment hash
|
||||
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,
|
||||
name : string,
|
||||
salt : int,
|
||||
name_fee : int) : unit =
|
||||
AENS.claim(addr, name, salt, name_fee)
|
||||
AENSv2.claim(addr, name, salt, name_fee)
|
||||
|
||||
stateful entrypoint signedClaim(addr : address,
|
||||
name : string,
|
||||
salt : int,
|
||||
name_fee : int,
|
||||
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,
|
||||
name : string,
|
||||
ttl : option(Chain.ttl),
|
||||
client_ttl : option(int),
|
||||
pointers : option(map(string, AENS.pointee))) : unit =
|
||||
AENS.update(owner, name, ttl, client_ttl, pointers)
|
||||
pointers : option(map(string, AENSv2.pointee))) : unit =
|
||||
AENSv2.update(owner, name, ttl, client_ttl, pointers)
|
||||
|
||||
stateful entrypoint signedUpdate(owner : address,
|
||||
name : string,
|
||||
ttl : option(Chain.ttl),
|
||||
client_ttl : option(int),
|
||||
pointers : option(map(string, AENS.pointee)),
|
||||
pointers : option(map(string, AENSv2.pointee)),
|
||||
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,
|
||||
new_owner : address,
|
||||
name : string) : unit =
|
||||
AENS.transfer(owner, new_owner, name)
|
||||
AENSv2.transfer(owner, new_owner, name)
|
||||
|
||||
stateful entrypoint signedTransfer(owner : address,
|
||||
new_owner : address,
|
||||
name : string,
|
||||
sign : signature) : unit =
|
||||
AENS.transfer(owner, new_owner, name, signature = sign)
|
||||
AENSv2.transfer(owner, new_owner, name, signature = sign)
|
||||
|
||||
stateful entrypoint revoke(owner : address,
|
||||
name : string) : unit =
|
||||
AENS.revoke(owner, name)
|
||||
AENSv2.revoke(owner, name)
|
||||
|
||||
stateful entrypoint signedRevoke(owner : address,
|
||||
name : string,
|
||||
sign : signature) : unit =
|
||||
AENS.revoke(owner, name, signature = sign)
|
||||
AENSv2.revoke(owner, name, signature = sign)
|
||||
|
@ -1,17 +1,29 @@
|
||||
contract AENSUpdate =
|
||||
stateful entrypoint update_name(owner : address, name : string) =
|
||||
let p1 : AENS.pointee = AENS.AccountPt(Call.caller)
|
||||
let p2 : AENS.pointee = AENS.OraclePt(Call.caller)
|
||||
let p3 : AENS.pointee = AENS.ContractPt(Call.caller)
|
||||
let p4 : AENS.pointee = AENS.ChannelPt(Call.caller)
|
||||
AENS.update(owner, name, None, None,
|
||||
Some({ ["account_pubkey"] = p1, ["oracle_pubkey"] = p2,
|
||||
["contract_pubkey"] = p3, ["misc"] = p4 }))
|
||||
include "Option.aes"
|
||||
include "String.aes"
|
||||
include "AENSCompat.aes"
|
||||
contract interface OldAENSContract =
|
||||
entrypoint set : (string, string, AENS.pointee) => unit
|
||||
entrypoint lookup : (string, string) => AENS.pointee
|
||||
|
||||
main contract AENSUpdate =
|
||||
stateful entrypoint update_name(owner : address, name : string, b : bytes(2)) =
|
||||
let p1 : AENSv2.pointee = AENSv2.AccountPt(Call.caller)
|
||||
let p2 : AENSv2.pointee = AENSv2.OraclePt(Call.caller)
|
||||
let p3 : AENSv2.pointee = AENSv2.ContractPt(Call.caller)
|
||||
let p4 : AENSv2.pointee = AENSv2.ChannelPt(Call.caller)
|
||||
let p5 : AENSv2.pointee = AENSv2.DataPt(String.to_bytes("any something will do"))
|
||||
let p6 : AENSv2.pointee = AENSv2.DataPt(Int.to_bytes(1345, 4))
|
||||
AENSv2.update(owner, name, None, None,
|
||||
Some({ ["account_pubkey"] = p1,
|
||||
["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) =
|
||||
switch(AENS.lookup(name))
|
||||
Some(AENS.Name(_, FixedTTL(ttl), _)) => ttl
|
||||
|
||||
entrypoint expiry(o : oracle(int, int)) : int =
|
||||
Oracle.expiry(o)
|
||||
|
||||
switch(AENSv2.lookup(name))
|
||||
Some(AENSv2.Name(_, FixedTTL(ttl), _)) => ttl
|
||||
|
@ -6,6 +6,7 @@
|
||||
namespace Ns =
|
||||
datatype d('a) = D | S(int) | M('a, list('a), int)
|
||||
private function fff() = 123
|
||||
let const = 1
|
||||
|
||||
stateful entrypoint
|
||||
f (1, x) = (_) => x
|
||||
@ -33,6 +34,8 @@ contract AllSyntax =
|
||||
|
||||
type state = shakespeare(int)
|
||||
|
||||
let cc = "str"
|
||||
|
||||
entrypoint init() = {
|
||||
johann = 1000,
|
||||
wolfgang = -10,
|
||||
|
@ -7,27 +7,9 @@ contract AddressLiterals =
|
||||
ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt
|
||||
entrypoint addr2() : Remote =
|
||||
ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt
|
||||
entrypoint addr3() : oracle(int, bool) =
|
||||
ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt
|
||||
|
||||
entrypoint oracle1() : oracle_query(int, bool) =
|
||||
ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5
|
||||
entrypoint oracle2() : bytes(32) =
|
||||
ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5
|
||||
entrypoint oracle3() : Remote =
|
||||
ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5
|
||||
|
||||
entrypoint query1() : oracle(int, bool) =
|
||||
oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY
|
||||
entrypoint query2() : bytes(32) =
|
||||
oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY
|
||||
entrypoint query3() : Remote =
|
||||
oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY
|
||||
|
||||
entrypoint contr1() : address =
|
||||
ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ
|
||||
entrypoint contr2() : oracle(int, bool) =
|
||||
ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ
|
||||
entrypoint contr3() : bytes(32) =
|
||||
ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ
|
||||
entrypoint contr4() : address =
|
||||
|
@ -3,7 +3,7 @@ contract BadAENSresolve =
|
||||
type t('a) = option(list('a))
|
||||
|
||||
function fail() : t(int) =
|
||||
AENS.resolve("foo.aet", "whatever")
|
||||
AENSv2.resolve("foo.aet", "whatever")
|
||||
|
||||
entrypoint main_fun() = ()
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
contract BadAENSresolve =
|
||||
using AENS
|
||||
using AENSv2
|
||||
|
||||
type t('a) = option(list('a))
|
||||
|
||||
function fail() : t(int) =
|
||||
resolve("foo.aet", "whatever")
|
||||
|
||||
entrypoint main_fun() = ()
|
||||
entrypoint main_fun() = ()
|
||||
|
5
test/contracts/bad_bytes_to_x.aes
Normal file
5
test/contracts/bad_bytes_to_x.aes
Normal file
@ -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)
|
27
test/contracts/bytes_misc.aes
Normal file
27
test/contracts/bytes_misc.aes
Normal file
@ -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))
|
||||
entrypoint to_str_big(b : bytes(65)) : string =
|
||||
Bytes.to_str(b)
|
||||
entrypoint to_fixed(b : bytes()) : option(bytes(4)) = Bytes.to_fixed_size(b)
|
||||
entrypoint to_any(b : bytes(4)) = Bytes.to_any_size(b)
|
||||
|
5
test/contracts/calling_child_contract_entrypoint.aes
Normal file
5
test/contracts/calling_child_contract_entrypoint.aes
Normal file
@ -0,0 +1,5 @@
|
||||
contract F =
|
||||
entrypoint g() = 1
|
||||
|
||||
main contract C =
|
||||
entrypoint f() = F.g()
|
15
test/contracts/ceres.aes
Normal file
15
test/contracts/ceres.aes
Normal file
@ -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 =
|
||||
|
||||
record state = { last_bf : address }
|
||||
record state = { last_bf : address
|
||||
, nw_id : string }
|
||||
|
||||
function init() : state =
|
||||
{last_bf = Contract.address}
|
||||
@ -11,3 +12,6 @@ contract ChainTest =
|
||||
|
||||
function save_coinbase() =
|
||||
put(state{last_bf = Chain.coinbase})
|
||||
|
||||
function save_network_id() =
|
||||
put(state{nw_id = Chain.network_id})
|
||||
|
@ -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 =
|
||||
|
||||
record state = { worker : Remote }
|
||||
record state = { worker : ComplexTypes }
|
||||
|
||||
entrypoint init(worker) = {worker = worker}
|
||||
|
||||
|
4
test/contracts/dir1/bar.aes
Normal file
4
test/contracts/dir1/bar.aes
Normal file
@ -0,0 +1,4 @@
|
||||
include "../dir2/baz.aes"
|
||||
namespace D =
|
||||
function g() = E.h()
|
||||
|
3
test/contracts/dir2/baz.aes
Normal file
3
test/contracts/dir2/baz.aes
Normal file
@ -0,0 +1,3 @@
|
||||
namespace E =
|
||||
function h() = 42
|
||||
|
@ -1,14 +1,9 @@
|
||||
|
||||
// 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 =
|
||||
|
||||
record state = {remote : Interface}
|
||||
record state = {remote : Environment}
|
||||
|
||||
entrypoint init(remote) = {remote = remote}
|
||||
|
||||
|
@ -11,8 +11,6 @@ contract Events =
|
||||
type ix5 = hash // bytes(32)
|
||||
type ix6 = address
|
||||
type ix7 = Remote
|
||||
type ix8 = oracle(int, int)
|
||||
type ix9 = oracle_query(int, int)
|
||||
|
||||
// Valid payload types
|
||||
type data1 = string
|
||||
@ -26,7 +24,6 @@ contract Events =
|
||||
| Nodata3(ix4, ix5, ix6)
|
||||
| Data0(data1)
|
||||
| Data1(data2, ix7)
|
||||
| Data2(ix8, data3, ix9)
|
||||
| Data3(ix1, ix2, ix5, data1)
|
||||
|
||||
entrypoint nodata0() = Chain.event(Nodata0)
|
||||
@ -35,6 +32,5 @@ contract Events =
|
||||
entrypoint nodata3(ix4, ix5, ix6) = Chain.event(Nodata3(ix4, ix5, ix6))
|
||||
entrypoint data0(data1) = Chain.event(Data0(data1))
|
||||
entrypoint data1(data2, ix7) = Chain.event(Data1(data2, ix7))
|
||||
entrypoint data2(ix8, data3, ix9) = Chain.event(Data2(ix8, data3, ix9))
|
||||
entrypoint data3(ix1, ix2, ix5, data1) = Chain.event(Data3(ix1, ix2, ix5, data1))
|
||||
|
||||
|
@ -20,6 +20,8 @@ contract FunctionArguments =
|
||||
entrypoint read(a : answer(int)) =
|
||||
a.result
|
||||
|
||||
entrypoint any_bytes(b : bytes()) = b
|
||||
|
||||
entrypoint sjutton(b : bytes(17)) =
|
||||
b
|
||||
|
||||
@ -29,12 +31,6 @@ contract FunctionArguments =
|
||||
entrypoint trettiotva(b : bytes(32)) =
|
||||
b
|
||||
|
||||
entrypoint find_oracle(o : oracle(int, bool)) =
|
||||
true
|
||||
|
||||
entrypoint find_query(q : oracle_query(int, bool)) =
|
||||
true
|
||||
|
||||
datatype colour() = Green | Yellow | Red | Pantone(int)
|
||||
|
||||
entrypoint traffic_light(c : colour) =
|
||||
@ -57,3 +53,5 @@ contract FunctionArguments =
|
||||
entrypoint chain_ga_meta_tx(tx : Chain.ga_meta_tx) = true
|
||||
entrypoint chain_paying_for_tx(tx : Chain.paying_for_tx) = true
|
||||
entrypoint chain_base_tx(tx : Chain.base_tx) = true
|
||||
|
||||
entrypoint sig(sg : signature) = true
|
||||
|
@ -1,5 +0,0 @@
|
||||
contract HigherOrderQueryType =
|
||||
stateful function foo(o) : oracle_query(_, string ) =
|
||||
Oracle.query(o, (x) => x + 1, 100, RelativeTTL(100), RelativeTTL(100))
|
||||
|
||||
entrypoint main_fun() = ()
|
@ -1,5 +0,0 @@
|
||||
contract HigherOrderResponseType =
|
||||
stateful function foo(o, q : oracle_query(string, _)) =
|
||||
Oracle.respond(o, q, (x) => x + 1)
|
||||
|
||||
entrypoint main_fun() = ()
|
13
test/contracts/hole_expression.aes
Normal file
13
test/contracts/hole_expression.aes
Normal file
@ -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() = ???
|
@ -1,110 +0,0 @@
|
||||
contract Oracles =
|
||||
|
||||
type fee = int
|
||||
type ttl = Chain.ttl
|
||||
|
||||
type query_t = string
|
||||
type answer_t = int
|
||||
|
||||
type oracle_id = oracle(query_t, answer_t)
|
||||
type query_id = oracle_query(query_t, answer_t)
|
||||
|
||||
stateful entrypoint registerOracle(acct : address,
|
||||
qfee : fee,
|
||||
ttl : ttl) : oracle_id =
|
||||
Oracle.register(acct, qfee, ttl)
|
||||
|
||||
stateful entrypoint registerIntIntOracle(acct : address,
|
||||
qfee : fee,
|
||||
ttl : ttl) : oracle(int, int) =
|
||||
Oracle.register(acct, qfee, ttl)
|
||||
|
||||
stateful entrypoint registerStringStringOracle(acct : address,
|
||||
qfee : fee,
|
||||
ttl : ttl) : oracle(string, string) =
|
||||
Oracle.register(acct, qfee, ttl)
|
||||
|
||||
stateful entrypoint signedRegisterOracle(acct : address,
|
||||
sign : signature,
|
||||
qfee : fee,
|
||||
ttl : ttl) : oracle_id =
|
||||
Oracle.register(acct, qfee, ttl, signature = sign)
|
||||
|
||||
entrypoint queryFee(o : oracle_id) : fee =
|
||||
Oracle.query_fee(o)
|
||||
|
||||
stateful entrypoint createQuery(o : oracle_id,
|
||||
q : query_t,
|
||||
qfee : fee,
|
||||
qttl : ttl,
|
||||
rttl : ttl) : query_id =
|
||||
require(qfee =< Call.value, "insufficient value for qfee")
|
||||
Oracle.query(o, q, qfee, qttl, rttl)
|
||||
|
||||
// Do not use in production!
|
||||
stateful entrypoint unsafeCreateQuery(o : oracle_id,
|
||||
q : query_t,
|
||||
qfee : fee,
|
||||
qttl : ttl,
|
||||
rttl : ttl) : query_id =
|
||||
Oracle.query(o, q, qfee, qttl, rttl)
|
||||
|
||||
// Do not use in production!
|
||||
stateful entrypoint unsafeCreateQueryThenErr(o : oracle_id,
|
||||
q : query_t,
|
||||
qfee : fee,
|
||||
qttl : ttl,
|
||||
rttl : ttl) : query_id =
|
||||
let res = Oracle.query(o, q, qfee, qttl, rttl)
|
||||
require(qfee >= 100000000000000000, "causing a late error")
|
||||
res
|
||||
|
||||
stateful entrypoint extendOracle(o : oracle_id,
|
||||
ttl : ttl) : unit =
|
||||
Oracle.extend(o, ttl)
|
||||
|
||||
stateful entrypoint signedExtendOracle(o : oracle_id,
|
||||
sign : signature, // Signed oracle address
|
||||
ttl : ttl) : unit =
|
||||
Oracle.extend(o, signature = sign, ttl)
|
||||
|
||||
stateful entrypoint respond(o : oracle_id,
|
||||
q : query_id,
|
||||
r : answer_t) : unit =
|
||||
Oracle.respond(o, q, r)
|
||||
|
||||
stateful entrypoint signedRespond(o : oracle_id,
|
||||
q : query_id,
|
||||
sign : signature,
|
||||
r : answer_t) : unit =
|
||||
Oracle.respond(o, q, signature = sign, r)
|
||||
|
||||
entrypoint getQuestion(o : oracle_id,
|
||||
q : query_id) : query_t =
|
||||
Oracle.get_question(o, q)
|
||||
|
||||
entrypoint hasAnswer(o : oracle_id,
|
||||
q : query_id) =
|
||||
switch(Oracle.get_answer(o, q))
|
||||
None => false
|
||||
Some(_) => true
|
||||
|
||||
entrypoint getAnswer(o : oracle_id,
|
||||
q : query_id) : option(answer_t) =
|
||||
Oracle.get_answer(o, q)
|
||||
|
||||
datatype complexQuestion = Why(int) | How(string)
|
||||
datatype complexAnswer = NoAnswer | Answer(complexQuestion, string, int)
|
||||
|
||||
stateful entrypoint complexOracle(question) =
|
||||
let o = Oracle.register(Contract.address, 0, FixedTTL(1000)) : oracle(complexQuestion, complexAnswer)
|
||||
let q = Oracle.query(o, question, 0, RelativeTTL(100), RelativeTTL(100))
|
||||
Oracle.respond(o, q, Answer(question, "magic", 1337))
|
||||
Oracle.get_answer(o, q)
|
||||
|
||||
stateful entrypoint signedComplexOracle(question, sig) =
|
||||
let o = Oracle.register(signature = sig, Contract.address, 0, FixedTTL(1000)) : oracle(complexQuestion, complexAnswer)
|
||||
let q = Oracle.query(o, question, 0, RelativeTTL(100), RelativeTTL(100))
|
||||
Oracle.respond(o, q, Answer(question, "magic", 1337), signature = sig)
|
||||
Oracle.get_answer(o, q)
|
||||
|
@ -1,7 +1,7 @@
|
||||
contract PolymorphicAENSresolve =
|
||||
|
||||
function fail() : option('a) =
|
||||
AENS.resolve("foo.aet", "whatever")
|
||||
AENSv2.resolve("foo.aet", "whatever")
|
||||
|
||||
entrypoint main_fun() = ()
|
||||
|
||||
|
@ -1,5 +0,0 @@
|
||||
contract PolymorphicQueryType =
|
||||
stateful function is_oracle(o) =
|
||||
Oracle.check(o)
|
||||
|
||||
entrypoint main_fun() = ()
|
@ -1,5 +0,0 @@
|
||||
contract PolymorphicResponseType =
|
||||
function is_oracle(o : oracle(string, 'r)) =
|
||||
Oracle.check(o)
|
||||
|
||||
entrypoint main_fun(o : oracle(string, int)) = is_oracle(o)
|
5
test/contracts/polymorphism_add_stateful_entrypoint.aes
Normal file
5
test/contracts/polymorphism_add_stateful_entrypoint.aes
Normal file
@ -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 @@
|
||||
payable contract interface I =
|
||||
payable entrypoint f : () => int
|
||||
|
||||
contract C : I =
|
||||
entrypoint f() = 123
|
@ -0,0 +1,8 @@
|
||||
payable contract interface I =
|
||||
payable entrypoint f : () => int
|
||||
|
||||
contract interface H : I =
|
||||
payable entrypoint f : () => int
|
||||
|
||||
payable contract C : H =
|
||||
entrypoint f() = 123
|
@ -0,0 +1,14 @@
|
||||
contract interface F =
|
||||
entrypoint f : () => int
|
||||
|
||||
payable contract interface G : F =
|
||||
payable entrypoint f : () => int
|
||||
entrypoint g : () => int
|
||||
|
||||
payable contract interface H =
|
||||
payable entrypoint h : () => int
|
||||
|
||||
payable contract C : G, H =
|
||||
payable entrypoint f() = 1
|
||||
payable entrypoint g() = 2
|
||||
payable entrypoint h() = 3
|
@ -0,0 +1,7 @@
|
||||
contract interface I =
|
||||
payable entrypoint f : () => int
|
||||
entrypoint g : () => int
|
||||
|
||||
contract C : I =
|
||||
payable entrypoint f() = 1
|
||||
payable entrypoint g() = 2
|
@ -0,0 +1,7 @@
|
||||
contract interface I =
|
||||
stateful entrypoint f : () => int
|
||||
stateful entrypoint g : () => int
|
||||
|
||||
contract C : I =
|
||||
stateful entrypoint f() = 1
|
||||
entrypoint g() = 2
|
@ -0,0 +1,5 @@
|
||||
contract interface I =
|
||||
payable entrypoint f : () => int
|
||||
|
||||
contract C : I =
|
||||
entrypoint f() = 1
|
@ -0,0 +1,29 @@
|
||||
contract interface I =
|
||||
entrypoint f : () => int
|
||||
|
||||
contract C1 : I =
|
||||
entrypoint f() = 123
|
||||
|
||||
contract C2 : I =
|
||||
entrypoint f() = 888
|
||||
|
||||
namespace Make =
|
||||
stateful function new1() : I = Chain.create() : C1
|
||||
stateful function new2() : I = Chain.create() : C2
|
||||
|
||||
stateful function new(c : I) : int = c.f()
|
||||
|
||||
main contract Main =
|
||||
stateful entrypoint test1() =
|
||||
let c = Make.new1()
|
||||
Make.new(c)
|
||||
|
||||
stateful entrypoint test2() =
|
||||
let c = Make.new2()
|
||||
Make.new(c)
|
||||
|
||||
stateful entrypoint test3() =
|
||||
let c1 = Chain.create() : C1 // succeeds
|
||||
let c2 : I = Chain.create() : C1 // succeeds
|
||||
let c3 : C1 = Chain.create() // succeeds
|
||||
()
|
@ -0,0 +1,12 @@
|
||||
contract interface I =
|
||||
entrypoint f : () => int
|
||||
|
||||
contract C : I =
|
||||
entrypoint f() = 123
|
||||
|
||||
main contract Main =
|
||||
stateful entrypoint test() =
|
||||
let c1 : I = Chain.create() // fails
|
||||
let c2 : C = Chain.create() : I // fails
|
||||
let c3 = Chain.create() : I // fails
|
||||
()
|
@ -1,47 +0,0 @@
|
||||
contract interface Animal =
|
||||
entrypoint sound : () => string
|
||||
|
||||
contract Cat : Animal =
|
||||
entrypoint sound() = "meow"
|
||||
|
||||
main contract Main =
|
||||
entrypoint oracle() = ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5
|
||||
|
||||
entrypoint query() = oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY
|
||||
|
||||
entrypoint init() =
|
||||
let o01 : oracle(Animal, Animal) = oracle() : oracle(Animal, Animal) // success
|
||||
let o02 : oracle(Animal, Animal) = oracle() : oracle(Animal, Cat) // success
|
||||
let o03 : oracle(Animal, Animal) = oracle() : oracle(Cat, Animal) // fail
|
||||
let o04 : oracle(Animal, Animal) = oracle() : oracle(Cat, Cat) // fail
|
||||
let o05 : oracle(Animal, Cat) = oracle() : oracle(Animal, Animal) // fail
|
||||
let o06 : oracle(Animal, Cat) = oracle() : oracle(Animal, Cat) // success
|
||||
let o07 : oracle(Animal, Cat) = oracle() : oracle(Cat, Animal) // fail
|
||||
let o08 : oracle(Animal, Cat) = oracle() : oracle(Cat, Cat) // fail
|
||||
let o09 : oracle(Cat, Animal) = oracle() : oracle(Animal, Animal) // success
|
||||
let o10 : oracle(Cat, Animal) = oracle() : oracle(Animal, Cat) // success
|
||||
let o11 : oracle(Cat, Animal) = oracle() : oracle(Cat, Animal) // success
|
||||
let o12 : oracle(Cat, Animal) = oracle() : oracle(Cat, Cat) // success
|
||||
let o13 : oracle(Cat, Cat) = oracle() : oracle(Animal, Animal) // fail
|
||||
let o14 : oracle(Cat, Cat) = oracle() : oracle(Animal, Cat) // success
|
||||
let o15 : oracle(Cat, Cat) = oracle() : oracle(Cat, Animal) // fail
|
||||
let o16 : oracle(Cat, Cat) = oracle() : oracle(Cat, Cat) // success
|
||||
|
||||
let q01 : oracle_query(Animal, Animal) = query() : oracle_query(Animal, Animal) // success
|
||||
let q02 : oracle_query(Animal, Animal) = query() : oracle_query(Animal, Cat) // success
|
||||
let q03 : oracle_query(Animal, Animal) = query() : oracle_query(Cat, Animal) // success
|
||||
let q04 : oracle_query(Animal, Animal) = query() : oracle_query(Cat, Cat) // success
|
||||
let q05 : oracle_query(Animal, Cat) = query() : oracle_query(Animal, Animal) // fail
|
||||
let q06 : oracle_query(Animal, Cat) = query() : oracle_query(Animal, Cat) // success
|
||||
let q07 : oracle_query(Animal, Cat) = query() : oracle_query(Cat, Animal) // fail
|
||||
let q08 : oracle_query(Animal, Cat) = query() : oracle_query(Cat, Cat) // success
|
||||
let q09 : oracle_query(Cat, Animal) = query() : oracle_query(Animal, Animal) // fail
|
||||
let q10 : oracle_query(Cat, Animal) = query() : oracle_query(Animal, Cat) // fail
|
||||
let q11 : oracle_query(Cat, Animal) = query() : oracle_query(Cat, Animal) // success
|
||||
let q12 : oracle_query(Cat, Animal) = query() : oracle_query(Cat, Cat) // success
|
||||
let q13 : oracle_query(Cat, Cat) = query() : oracle_query(Animal, Animal) // fail
|
||||
let q14 : oracle_query(Cat, Cat) = query() : oracle_query(Animal, Cat) // fail
|
||||
let q15 : oracle_query(Cat, Cat) = query() : oracle_query(Cat, Animal) // fail
|
||||
let q16 : oracle_query(Cat, Cat) = query() : oracle_query(Cat, Cat) // success
|
||||
|
||||
()
|
@ -0,0 +1,5 @@
|
||||
payable contract interface SalesOffer =
|
||||
entrypoint init : (address, address, int, int) => unit
|
||||
|
||||
payable contract Test : SalesOffer =
|
||||
entrypoint init(_, _, _, _) = ()
|
@ -0,0 +1,5 @@
|
||||
payable contract interface SalesOffer =
|
||||
entrypoint init : (address, address, int, int) => void
|
||||
|
||||
payable contract Test : SalesOffer =
|
||||
entrypoint init(_ : address, _ : address, _ : int, _ : int) = ()
|
3
test/contracts/relative_include.aes
Normal file
3
test/contracts/relative_include.aes
Normal file
@ -0,0 +1,3 @@
|
||||
include "./dir1/bar.aes"
|
||||
contract C =
|
||||
entrypoint f() = D.g()
|
9
test/contracts/resolve_field_constraint_by_arity.aes
Normal file
9
test/contracts/resolve_field_constraint_by_arity.aes
Normal file
@ -0,0 +1,9 @@
|
||||
contract First =
|
||||
entrypoint print_num(x) = 1 + x
|
||||
|
||||
contract Second =
|
||||
entrypoint print_num() = 1
|
||||
|
||||
main contract Test =
|
||||
entrypoint f(c) = c.print_num(1)
|
||||
entrypoint g(c) = c.print_num()
|
@ -1,7 +1,4 @@
|
||||
|
||||
contract interface SpendContract =
|
||||
entrypoint withdraw : (int) => int
|
||||
|
||||
contract SpendTest =
|
||||
|
||||
stateful entrypoint spend(to, amount) =
|
||||
|
@ -1,5 +1,5 @@
|
||||
// This is a custom test file if you need to run a compiler without
|
||||
// changing aeso_compiler_tests.erl
|
||||
// changing so_compiler_tests.erl
|
||||
|
||||
include "List.aes"
|
||||
|
||||
|
60
test/contracts/too_many_tvars.aes
Normal file
60
test/contracts/too_many_tvars.aes
Normal file
@ -0,0 +1,60 @@
|
||||
contract C =
|
||||
entrypoint too_many(
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _)) = 0
|
||||
|
||||
entrypoint not_too_many(
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _, _, _, _, _),
|
||||
(_, _, _, _, _, _)) = 0
|
60
test/contracts/toplevel_constants.aes
Normal file
60
test/contracts/toplevel_constants.aes
Normal file
@ -0,0 +1,60 @@
|
||||
namespace N0 =
|
||||
let nsconst = 1
|
||||
|
||||
namespace N =
|
||||
let nsconst = N0.nsconst
|
||||
|
||||
contract C =
|
||||
datatype event = EventX(int, string)
|
||||
|
||||
record account = { name : string,
|
||||
balance : int }
|
||||
|
||||
let c01 = 2425
|
||||
let c02 = -5
|
||||
let c03 = ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt
|
||||
let c04 = true
|
||||
let c05 = Bits.none
|
||||
let c06 = #fedcba9876543210
|
||||
let c07 = "str"
|
||||
let c08 = [1, 2, 3]
|
||||
let c09 = [(true, 24), (false, 19), (false, -42)]
|
||||
let c10 = (42, "Foo", true)
|
||||
let c11 = { name = "str", balance = 100000000 }
|
||||
let c12 = {["foo"] = 19, ["bar"] = 42}
|
||||
let c13 = Some(42)
|
||||
let c14 = 11 : int
|
||||
let c15 = EventX(0, "Hello")
|
||||
let c16 = #000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
|
||||
let c17 = #000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
|
||||
let c18 = RelativeTTL(50)
|
||||
let c21 = ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ : C
|
||||
let c22 = N.nsconst
|
||||
let c23 = c01
|
||||
let c24 = c11.name
|
||||
let c25 : int = 1
|
||||
|
||||
entrypoint f01() = c01
|
||||
entrypoint f02() = c02
|
||||
entrypoint f03() = c03
|
||||
entrypoint f04() = c04
|
||||
entrypoint f05() = c05
|
||||
entrypoint f06() = c06
|
||||
entrypoint f07() = c07
|
||||
entrypoint f08() = c08
|
||||
entrypoint f09() = c09
|
||||
entrypoint f10() = c10
|
||||
entrypoint f11() = c11
|
||||
entrypoint f12() = c12
|
||||
entrypoint f13() = c13
|
||||
entrypoint f14() = c14
|
||||
entrypoint f15() = c15
|
||||
entrypoint f16() = c16
|
||||
entrypoint f17() = c17
|
||||
entrypoint f18() = c18
|
||||
entrypoint f21() = c21
|
||||
entrypoint f22() = c22
|
||||
entrypoint f23() = c23
|
||||
entrypoint f24() = c24
|
||||
entrypoint f25() = c25
|
||||
entrypoint fqual() = C.c01
|
11
test/contracts/toplevel_constants_contract_as_namespace.aes
Normal file
11
test/contracts/toplevel_constants_contract_as_namespace.aes
Normal file
@ -0,0 +1,11 @@
|
||||
contract G =
|
||||
let const = 1
|
||||
|
||||
main contract C =
|
||||
let c = G.const
|
||||
|
||||
stateful entrypoint f() =
|
||||
let g = Chain.create() : G
|
||||
|
||||
g.const
|
||||
g.const()
|
6
test/contracts/toplevel_constants_cycles.aes
Normal file
6
test/contracts/toplevel_constants_cycles.aes
Normal file
@ -0,0 +1,6 @@
|
||||
contract C =
|
||||
let selfcycle = selfcycle
|
||||
|
||||
let cycle1 = cycle2
|
||||
let cycle2 = cycle3
|
||||
let cycle3 = cycle1
|
7
test/contracts/toplevel_constants_in_interface.aes
Normal file
7
test/contracts/toplevel_constants_in_interface.aes
Normal file
@ -0,0 +1,7 @@
|
||||
contract interface I =
|
||||
let (x::y::_) = [1,2,3]
|
||||
let c = 10
|
||||
let d = 10
|
||||
|
||||
contract C =
|
||||
entrypoint init() = ()
|
21
test/contracts/toplevel_constants_invalid_expr.aes
Normal file
21
test/contracts/toplevel_constants_invalid_expr.aes
Normal file
@ -0,0 +1,21 @@
|
||||
main contract C =
|
||||
record account = { name : string,
|
||||
balance : int }
|
||||
|
||||
let one = 1
|
||||
let opt = Some(5)
|
||||
let acc = { name = "str", balance = 100000 }
|
||||
let mpp = {["foo"] = 19, ["bar"] = 42}
|
||||
|
||||
let c01 = [x | x <- [1,2,3,4,5]]
|
||||
let c02 = [x + k | x <- [1,2,3,4,5], let k = x*x]
|
||||
let c03 = [x + y | x <- [1,2,3,4,5], let k = x*x, if (k > 5), y <- [k, k+1, k+2]]
|
||||
let c04 = if (one > 2) 3 else 4
|
||||
let c05 = switch (opt)
|
||||
Some(x) => x
|
||||
None => 2
|
||||
let c07 = acc{ balance = one }
|
||||
let c08 = mpp["foo"]
|
||||
let c09 = mpp["non" = 10]
|
||||
let c10 = mpp{["foo"] = 20}
|
||||
let c11 = (x) => x + 1
|
3
test/contracts/toplevel_constants_invalid_id.aes
Normal file
3
test/contracts/toplevel_constants_invalid_id.aes
Normal file
@ -0,0 +1,3 @@
|
||||
contract C =
|
||||
let x::_ = [1,2,3,4]
|
||||
let y::(p = z::_) = [1,2,3,4]
|
@ -1,3 +0,0 @@
|
||||
contract C =
|
||||
let this_is_illegal = 2/0
|
||||
entrypoint this_is_legal() = 2/0
|
@ -1,16 +1,12 @@
|
||||
// Builtins without named arguments can appear unapplied.
|
||||
// Named argument builtins are:
|
||||
// Oracle.register
|
||||
// Oracle.respond
|
||||
// AENS.preclaim
|
||||
// AENS.claim
|
||||
// AENS.transfer
|
||||
// AENS.revoke
|
||||
// Oracle.extend
|
||||
// AENSv2.preclaim
|
||||
// AENSv2.claim
|
||||
// AENSv2.transfer
|
||||
// AENSv2.revoke
|
||||
include "String.aes"
|
||||
contract UnappliedBuiltins =
|
||||
entrypoint main_fun() = ()
|
||||
type o = oracle(int, int)
|
||||
type t = list(int * string)
|
||||
type m = map(int, int)
|
||||
datatype event = Event(int)
|
||||
@ -21,14 +17,7 @@ contract UnappliedBuiltins =
|
||||
function call_gas_left() = Call.gas_left
|
||||
function b_abort() = abort
|
||||
function b_require() = require
|
||||
function oracle_query_fee() = Oracle.query_fee
|
||||
function oracle_expiry() = Oracle.expiry
|
||||
stateful function oracle_query() = Oracle.query : (o, _, _, _, _) => _
|
||||
function oracle_get_question() = Oracle.get_question : (o, _) => _
|
||||
function oracle_get_answer() = Oracle.get_answer : (o, _) => _
|
||||
function oracle_check() = Oracle.check : o => _
|
||||
function oracle_check_query() = Oracle.check_query : (o, _) => _
|
||||
function aens_resolve() = AENS.resolve : (_, _) => option(string)
|
||||
function aens_resolve() = AENSv2.resolve : (_, _) => option(string)
|
||||
function map_lookup() = Map.lookup : (_, m) => _
|
||||
function map_lookup_default() = Map.lookup_default : (_, m, _) => _
|
||||
function map_member() = Map.member : (_, m) => _
|
||||
@ -36,7 +25,7 @@ contract UnappliedBuiltins =
|
||||
function map_delete() = Map.delete : (_, m) => _
|
||||
function map_from_list() = Map.from_list : _ => m
|
||||
function map_to_list() = Map.to_list : m => _
|
||||
function crypto_verify_sig() = Crypto.verify_sig
|
||||
function crypto_verify_sig() = Crypto.verify_sig : (bytes(), _, _) => _
|
||||
function crypto_verify_sig_secp256k1() = Crypto.verify_sig_secp256k1
|
||||
function crypto_ecverify_secp256k1() = Crypto.ecverify_secp256k1
|
||||
function crypto_ecrecover_secp256k1() = Crypto.ecrecover_secp256k1
|
||||
@ -57,7 +46,6 @@ contract UnappliedBuiltins =
|
||||
function bits_sum() = Bits.sum
|
||||
function int_to_str() = Int.to_str
|
||||
function address_to_str() = Address.to_str
|
||||
function address_is_oracle() = Address.is_oracle
|
||||
function address_is_contract() = Address.is_contract
|
||||
function address_is_payable() = Address.is_payable
|
||||
function bytes_to_int() = Bytes.to_int : bytes(10) => int
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user