Renaming and preparing to remove oracles (#985)
All checks were successful
Sophia Tests / tests (push) Successful in 48m54s

A few references to oracles still remain, but they have been removed as a feature, at least.

Reviewed-on: #985
Reviewed-by: Ulf Wiger <ulfwiger@qpq.swiss>
Co-authored-by: Craig Everett <zxq9@zxq9.com>
Co-committed-by: Craig Everett <zxq9@zxq9.com>
This commit is contained in:
Craig Everett 2025-03-13 12:53:01 +09:00 committed by Craig Everett
parent 927cd42592
commit dbab49936d
70 changed files with 927 additions and 1633 deletions

View File

@ -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

View 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

View File

@ -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

View File

@ -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

View File

@ -1,5 +0,0 @@
mkdocs==1.4.2
mkdocs-simple-hooks==0.1.5
mkdocs-material==9.0.9
mike==1.1.2
pygments==2.17.2

2
.gitignore vendored
View File

@ -18,7 +18,7 @@ _build
rebar3.crashdump
*.erl~
*.aes~
aesophia
sophia
.qcci
current_counterexample.eqc
test/contracts/test.aes

View File

@ -11,6 +11,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### 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

1
Emakefile Normal file
View File

@ -0,0 +1 @@
{"src/*", [debug_info, {i, "include/"}, {outdir, "ebin/"}]}.

View File

@ -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

View File

@ -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
@ -16,7 +16,7 @@ The compiler is currently being used three places
* [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
@ -31,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)

View File

@ -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.

View File

@ -1,8 +1,8 @@
# aeso_aci
# so_aci
### Module
### aeso_aci
### so_aci
The ACI interface encoder and decoder.
@ -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">>,
@ -144,7 +144,7 @@ be included inside another contract.
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

View File

@ -1,8 +1,8 @@
# aeso_compiler
# so_compiler
### Module
### aeso_compiler
### so_compiler
The Sophia compiler

View File

@ -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.

View File

@ -274,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
@ -340,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`
@ -556,8 +546,6 @@ 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
@ -580,8 +568,6 @@ Sophia has the following types:
| hash | `#000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f` |
| 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
@ -891,77 +877,9 @@ 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
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.
@ -1009,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
@ -1032,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`
@ -1113,34 +1028,15 @@ however is in the gas consumption — while `abort` returns unused gas, a call t
## Delegation signature
Some chain operations (`Oracle.<operation>` and `AENSv2.<operation>`) have an
Some chain operations (`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.
### From Ceres
From the Ceres protocol version the delegation signatures have more structure,
including a unique tag, `network_id` and identifiers; there are five different
delegation signatures:
There are five different delegation signatures:
- AENS wildcard - the user signs: `owner account + contract`
- `AENS_PRECLAIM` - the user signs: `owner account + contract`
- `AENS_CLAIM, AENS_UPDATE, AENS_TRANSFER, AENS_REVOKE` - the user signs: `owner account + name hash + contract`
- `ORACLE_REGISTER, ORACLE_EXTEND` - the user signs: `owner account + contract`
- `ORACLE_RESPOND` - the user signs: `query id + contract`
See [Serialized signature
data](https://github.com/aeternity/protocol/blob/master/contracts/fate.md#from-ceres-serialized-signature-data)
See [Serialized signature data](https://git.qpq.swiss/QPQ-AG/protocol/src/branch/master/serializations.md)
for the exact structure used.
### Before ceres
The exact data to be signed varies for the different operations, but in all
cases you should prepend the signature data with the `network_id` (`ae_mainnet`
for the æternity mainnet, etc.).
There are four different delegation signatures:
- `AENS_PRECLAIM` - the user signs: owner `network_id + account + contract`
- `AENS_CLAIM, AENS_UPDATE, AENS_TRANSFER, AENS_REVOKE` - the user signs: `network_id + owner account + name hash + contract`
- `ORACLE_REGISTER, ORACLE_EXTEND` - the user signs: `network_id + owner account + contract`
- `ORACLE_RESPOND` - the user signs: `network_id + query id + contract`

View File

@ -25,7 +25,6 @@ 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
```
@ -72,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
@ -99,7 +90,7 @@ Cast address to contract type C (where `C` is a contract)
### AENS
The old AENS namespace, kept in the compiler to be able to interact with
contracts from before Ceres, compiled using aesophia compiler version 7.x and
contracts from before Ceres, compiled using sophia compiler version 7.x and
earlier. Used in [AENSCompat](#aenscompat) when converting between old and new
pointers.
@ -114,8 +105,9 @@ 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
@ -127,7 +119,7 @@ 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),
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
@ -141,8 +133,10 @@ datatype name = Name(address, Chain.ttl, map(string, AENSv2.pointee))
##### pointee
```
datatype pointee = AccountPt(address) | OraclePt(address)
| ContractPt(address) | ChannelPt(address) | DataPt(bytes())
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.
@ -185,8 +179,7 @@ The [signature](./sophia_features.md#delegation-signature) should be a
serialized structure containing `network id`, `owner address`, and
`Contract.address`.
From Ceres (i.e. FATE VM version 3) the
[signature](./sophia_features.md#delegation-signature) can also be generic
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`.
@ -287,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) |
@ -305,7 +297,7 @@ Auth.tx_hash : option(hash)
Gets the transaction hash during authentication. Note: `Auth.tx_hash`
computation differs between protocol versions (changed in Ceres!), see
[aeserialisation](https://github.com/aeternity/protocol/blob/master/serializations.md)
[aeserialisation](https://git.qpq.swiss/QPQ-AG/protocol/src/branch/master/serializations.md)
specification for details.
@ -537,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) |
@ -944,118 +935,6 @@ 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).
* The [signature](./sophia_features.md#delegation-signature) should be a
serialized structure containing `network id`, `account address`, and
`contract address`. Using the private key of `account address` for signing.
Proving you have the private key of the oracle to be. If the address is the same
as the contract `sign` is ignored and can be left out entirely.
* 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) should be a serialized
structure containing `network id`, `oracle query id`, and `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)
@ -1664,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.

View File

@ -28,8 +28,6 @@ interface main using as for hiding
- `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
@ -46,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
@ -239,7 +237,6 @@ Expr ::= '(' LamArgs ')' '=>' Block(Stmt) // Anonymous function (x) => 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 + ???

View File

@ -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,

View File

@ -2,7 +2,9 @@
{erl_opts, [debug_info]}.
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {tag, "v3.4.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"}}}
]}.
@ -13,10 +15,11 @@
{base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]}
]}.
{relx, [{release, {aesophia, "8.0.1"},
[aesophia, aebytecode]},
{relx, [{release, {sophia, "9.0.0"},
[sophia, gmbytecode]},
{dev_mode, true},
{include_erts, false},
{extended_start_script, true}]}.

View File

@ -1,31 +1,30 @@
{"1.2.0",
[{<<"aebytecode">>,
{git,"https://github.com/aeternity/aebytecode.git",
{ref,"6bd6f82c70d800950ea1a2c70c364a4181ff5291"}},
[{<<"gmbytecode">>,
{git,"https://git.qpq.swiss/QPQ-AG/gmbytecode.git",
{ref, "97cea33be8f3a35d26055664da7aa59531ff5537"}},
0},
{<<"aeserialization">>,
{git,"https://github.com/aeternity/aeserialization.git",
{ref,"b26e6d105424748ba1c27917267b7cff07f37802"}},
{<<"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",
{git,"https://git.qpq.swiss/QPQ-AG/enacl.git",
{ref,"4eb7ec70084ba7c87b1af8797c4c4e90c84f95a2"}},
2},
{<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},1},
{<<"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">>}]}
].

BIN
rebar3

Binary file not shown.

View File

@ -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}
@ -200,7 +201,7 @@ encode_expr({bytes, _, B}) ->
encode_expr({Lit, _, L}) when Lit == oracle_pubkey; Lit == oracle_query_id;
Lit == contract_pubkey; Lit == account_pubkey;
Lit == signature ->
aeser_api_encoder:encode(Lit, L);
gmser_api_encoder:encode(Lit, L);
encode_expr({app, _, {'-', _}, [{int, _, N}]}) ->
encode_expr({int, [], -N});
encode_expr({app, _, F, As}) ->
@ -361,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.

View File

@ -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

View File

@ -1,18 +1,18 @@
%%%-------------------------------------------------------------------
%%% @author Ulf Norell
%%% @copyright (C) 2025, QPQ AG
%%% @copyright (C) 2019, Aeternity Anstalt
%%% @doc
%%% Compiler from Aeterinty Sophia language to Fate intermediate code.
%%% Compiler from Sophia language to Fate intermediate code.
%%% @end
%%% Created : 26 Mar 2019
%%%
%%%-------------------------------------------------------------------
-module(aeso_ast_to_fcode).
-module(so_ast_to_fcode).
-vsn("9.0.0").
-export([ast_to_fcode/2, format_fexpr/1]).
-export_type([fcode/0, fexpr/0, fun_def/0]).
-include("aeso_utils.hrl").
-include("so_utils.hrl").
%% -- Type definitions -------------------------------------------------------
@ -53,16 +53,14 @@
| {bytes, binary()}
| {account_pubkey, binary()}
| {contract_pubkey, binary()}
| {oracle_pubkey, binary()}
| {oracle_query_id, binary()}
| {signature, binary()}
| {bool, false | true}
| {contract_code, string()} %% for CREATE, by name
| {typerep, ftype()}.
-type fann() :: [ {file, aeso_syntax:ann_file()} |
{line, aeso_syntax:ann_line()} |
{col, aeso_syntax:ann_col()}
-type fann() :: [ {file, so_syntax:ann_file()} |
{line, so_syntax:ann_line()} |
{col, so_syntax:ann_col()}
].
-type fexpr() :: {lit, fann(), flit()}
@ -115,8 +113,6 @@
| address
| {bytes, non_neg_integer()}
| contract
| {oracle, ftype(), ftype()} %% Query type, Response type
| oracle_query
| name
| channel
| bits
@ -166,7 +162,7 @@
fun_env := fun_env(),
con_env := con_env(),
child_con_env := child_con_env(),
event_type => aeso_syntax:typedef(),
event_type => so_syntax:typedef(),
builtins := builtins(),
options := [option()],
state_layout => state_layout(),
@ -181,9 +177,9 @@
%% -- Entrypoint -------------------------------------------------------------
%% Main entrypoint. Takes typed syntax produced by aeso_ast_infer_types:infer/1,2
%% Main entrypoint. Takes typed syntax produced by so_ast_infer_types:infer/1,2
%% and produces Fate intermediate code.
-spec ast_to_fcode(aeso_syntax:ast(), [option()]) -> {env(), fcode()}.
-spec ast_to_fcode(so_syntax:ast(), [option()]) -> {env(), fcode()}.
ast_to_fcode(Code, Options) ->
init_fresh_names(Options),
{Env1, FCode1} = to_fcode(init_env(Options), Code),
@ -305,7 +301,7 @@ builtins() ->
{"to_any_size", 1}, {"size", 1}, {"split_any", 2}]},
{["Int"], [{"to_str", 1}, {"to_bytes", 2}, {"mulmod", 2}]},
{["Address"], [{"to_str", 1}, {"to_bytes", 1}, {"to_contract", 1},
{"is_oracle", 1}, {"is_contract", 1}, {"is_payable", 1}]}
{"is_contract", 1}, {"is_payable", 1}]}
],
maps:from_list([ {NS ++ [Fun], {MkName(NS, Fun), Arity}}
|| {NS, Funs} <- Scopes,
@ -333,8 +329,6 @@ init_type_env() ->
["address"] => ?type(address),
["hash"] => ?type(hash),
["signature"] => ?type(signature),
["oracle"] => ?type(Q, R, {oracle, Q, R}),
["oracle_query"] => ?type(_, _, oracle_query), %% TODO: not in Fate
["list"] => ?type(T, {list, T}),
["map"] => ?type(K, V, {map, K, V}),
["option"] => ?type(T, {variant, [[], [T]]}),
@ -364,7 +358,7 @@ get_option(Opt, Env, Default) ->
%% -- Compilation ------------------------------------------------------------
-spec to_fcode(env(), aeso_syntax:ast()) -> {env(), fcode()}.
-spec to_fcode(env(), so_syntax:ast()) -> {env(), fcode()}.
to_fcode(Env, [{Contract, Attrs, {con, _, Name}, _Impls, Decls}|Rest])
when ?IS_CONTRACT_HEAD(Contract) ->
case Contract =:= contract_interface of
@ -405,7 +399,7 @@ to_fcode(Env, [{namespace, _, {con, _, Con}, Decls} | Code]) ->
Env1 = decls_to_fcode(Env#{ context => {namespace, Con} }, Decls),
to_fcode(Env1, Code).
-spec ann_loc(aeso_syntax:ann() | fann()) -> {File, Line, Column} when
-spec ann_loc(so_syntax:ann() | fann()) -> {File, Line, Column} when
File :: string() | none,
Line :: non_neg_integer() | none,
Column :: non_neg_integer() | none.
@ -415,7 +409,7 @@ ann_loc(Ann) ->
Col = proplists:get_value(col, Ann, none),
{File, Line, Col}.
-spec to_fann(aeso_syntax:ann()) -> fann().
-spec to_fann(so_syntax:ann()) -> fann().
to_fann(Ann) ->
{File, Line, Col} = ann_loc(Ann),
[ {Tag, X} ||
@ -426,7 +420,7 @@ to_fann(Ann) ->
-spec get_fann(fexpr()) -> fann().
get_fann(FExpr) -> element(2, FExpr).
-spec decls_to_fcode(env(), [aeso_syntax:decl()]) -> env().
-spec decls_to_fcode(env(), [so_syntax:decl()]) -> env().
decls_to_fcode(Env, Decls) ->
%% First compute mapping from Sophia names to fun_names and add it to the
%% environment.
@ -434,7 +428,7 @@ decls_to_fcode(Env, Decls) ->
lists:foldl(fun(D, E) -> decl_to_fcode(E, D)
end, Env1, Decls).
-spec decl_to_fcode(env(), aeso_syntax:decl()) -> env().
-spec decl_to_fcode(env(), so_syntax:decl()) -> env().
decl_to_fcode(Env, {fun_decl, _, _, _}) -> Env;
decl_to_fcode(Env, {type_def, _Ann, Name, Args, Def}) ->
typedef_to_fcode(Env, Name, Args, Def);
@ -458,7 +452,7 @@ decl_to_fcode(Env = #{ consts := Consts }, {letval, _, {typed, _, {id, _, X}, _}
NewConsts = Consts#{ qname(Env, X) => FVal },
Env#{ consts := NewConsts }.
-spec typedef_to_fcode(env(), aeso_syntax:id(), [aeso_syntax:tvar()], aeso_syntax:typedef()) -> env().
-spec typedef_to_fcode(env(), so_syntax:id(), [so_syntax:tvar()], so_syntax:typedef()) -> env().
typedef_to_fcode(Env, {id, _, Name}, Xs, Def) ->
Q = qname(Env, Name),
FDef = fun(Args) when length(Args) == length(Xs) ->
@ -525,11 +519,11 @@ compute_state_layout(R, [H | T]) ->
compute_state_layout(R, _) ->
{R + 1, {reg, R}}.
-spec type_to_fcode(env(), aeso_syntax:type()) -> ftype().
-spec type_to_fcode(env(), so_syntax:type()) -> ftype().
type_to_fcode(Env, Type) ->
type_to_fcode(Env, #{}, Type).
-spec type_to_fcode(env(), #{var_name() => ftype()}, aeso_syntax:type()) -> ftype().
-spec type_to_fcode(env(), #{var_name() => ftype()}, so_syntax:type()) -> ftype().
type_to_fcode(_Env, _Sub, {con, _, _}) -> contract;
type_to_fcode(Env, Sub, {app_t, _, T = {Id, _, _}, Types}) when Id == id; Id == qid ->
lookup_type(Env, T, [type_to_fcode(Env, Sub, Type) || Type <- Types]);
@ -555,7 +549,7 @@ type_to_fcode(Env, Sub, {if_t, _, _, _, Else}) ->
type_to_fcode(_Env, _Sub, Type) ->
error({todo, Type}).
-spec args_to_fcode(env(), [aeso_syntax:pat()]) -> [{var_name(), ftype()}].
-spec args_to_fcode(env(), [so_syntax:pat()]) -> [{var_name(), ftype()}].
args_to_fcode(Env, Args) ->
[ case Arg of
{id, _, Name} -> {Name, type_to_fcode(Env, Type)};
@ -586,13 +580,13 @@ let_bind(Binds, Body) ->
lists:foldr(fun({X, E}, Rest) -> let_bind(X, E, Rest) end,
Body, Binds).
-spec expr_to_fcode(env(), aeso_syntax:expr()) -> fexpr().
-spec expr_to_fcode(env(), so_syntax:expr()) -> fexpr().
expr_to_fcode(Env, {typed, _, Expr, Type}) ->
expr_to_fcode(Env, Type, Expr);
expr_to_fcode(Env, Expr) ->
expr_to_fcode(Env, no_type, Expr).
-spec expr_to_fcode(env(), aeso_syntax:type() | no_type, aeso_syntax:expr()) -> fexpr().
-spec expr_to_fcode(env(), so_syntax:type() | no_type, so_syntax:expr()) -> fexpr().
%% Literals
expr_to_fcode(_Env, _Type, {int, Ann, N}) -> {lit, to_fann(Ann), {int, N}};
@ -602,25 +596,12 @@ expr_to_fcode(_Env, _Type, {string, Ann, S}) -> {lit, to_fann(Ann), {st
expr_to_fcode(_Env, _Type, {account_pubkey, Ann, K}) -> {lit, to_fann(Ann), {account_pubkey, K}};
expr_to_fcode(_Env, _Type, {signature, Ann, K}) -> {lit, to_fann(Ann), {signature, K}};
expr_to_fcode(_Env, _Type, {contract_pubkey, Ann, K}) -> {lit, to_fann(Ann), {contract_pubkey, K}};
expr_to_fcode(_Env, _Type, {oracle_pubkey, Ann, K}) -> {lit, to_fann(Ann), {oracle_pubkey, K}};
expr_to_fcode(_Env, _Type, {oracle_query_id, Ann, K}) -> {lit, to_fann(Ann), {oracle_query_id, K}};
expr_to_fcode(_Env, _Type, {bytes, Ann, B}) -> {lit, to_fann(Ann), {bytes, B}};
%% Variables
expr_to_fcode(Env, _Type, {id, Ann, X}) -> resolve_var(Env, Ann, [X]);
expr_to_fcode(Env, Type, {qid, Ann, X}) ->
case resolve_var(Env, Ann, X) of
{builtin_u, FAnn, B, Ar} when B =:= oracle_query;
B =:= oracle_get_question;
B =:= oracle_get_answer;
B =:= oracle_respond;
B =:= oracle_register;
B =:= oracle_check;
B =:= oracle_check_query ->
OType = get_oracle_type(B, Type),
{oracle, QType, RType} = type_to_fcode(Env, OType),
TypeArgs = [{lit, FAnn, {typerep, QType}}, {lit, FAnn, {typerep, RType}}],
{builtin_u, FAnn, B, Ar, TypeArgs};
{builtin_u, FAnn, B = aens_resolve, Ar} ->
{fun_t, _, _, _, ResType} = Type,
AensType = type_to_fcode(Env, ResType),
@ -705,7 +686,7 @@ expr_to_fcode(Env, {record_t, FieldTypes}, {record, Ann, Rec, Fields}) ->
%% Lists
expr_to_fcode(Env, _Type, {list, Ann, Es}) ->
lists:foldr(fun(E, L) -> {op, to_fann(aeso_syntax:get_ann(E)), '::', [expr_to_fcode(Env, E), L]} end,
lists:foldr(fun(E, L) -> {op, to_fann(so_syntax:get_ann(E)), '::', [expr_to_fcode(Env, E), L]} end,
{nil, to_fann(Ann)}, Es);
expr_to_fcode(Env, _Type, {app, As, {'..', _}, [A, B]}) ->
@ -783,15 +764,15 @@ expr_to_fcode(Env, _, {app, _, Fun = {typed, Ann, FunE, {fun_t, _, NamedArgsT, A
var_args -> fcode_error({var_args_not_set, FunE});
_ ->
%% Here we little cheat on the typechecker, but this inconsistency
%% is to be solved in `aeso_fcode_to_fate:type_to_scode/1`
FInitArgsT = aeb_fate_data:make_typerep([type_to_fcode(Env, T) || T <- ArgsT]),
%% is to be solved in `so_fcode_to_fate:type_to_scode/1`
FInitArgsT = gmb_fate_data:make_typerep([type_to_fcode(Env, T) || T <- ArgsT]),
builtin_to_fcode(state_layout(Env), FAnn, chain_clone, [{lit, FAnn, FInitArgsT}|FArgs])
end;
{builtin_u, FAnn, chain_create, _Ar} ->
case {ArgsT, Type} of
{var_args, _} -> fcode_error({var_args_not_set, FunE});
{_, {con, _, Contract}} ->
FInitArgsT = aeb_fate_data:make_typerep([type_to_fcode(Env, T) || T <- ArgsT]),
FInitArgsT = gmb_fate_data:make_typerep([type_to_fcode(Env, T) || T <- ArgsT]),
builtin_to_fcode(state_layout(Env), FAnn, chain_create, [{lit, FAnn, {contract_code, Contract}}, {lit, FAnn, FInitArgsT}|FArgs]);
{_, _} -> fcode_error({not_a_contract_type, Type})
end;
@ -884,21 +865,10 @@ make_tuple_fpat(Ps) -> {tuple, Ps}.
strip_singleton_tuples({tuple, _, [T]}) -> strip_singleton_tuples(T);
strip_singleton_tuples(T) -> T.
-spec get_oracle_type(OracleFun, FunT) -> OracleType when
OracleFun :: atom(),
FunT :: aeso_syntax:type(),
OracleType :: aeso_syntax:type().
get_oracle_type(oracle_register, {fun_t, _, _, _, OType}) -> OType;
get_oracle_type(oracle_query, {fun_t, _, _, [OType | _], _}) -> OType;
get_oracle_type(oracle_get_question, {fun_t, _, _, [OType | _], _}) -> OType;
get_oracle_type(oracle_get_answer, {fun_t, _, _, [OType | _], _}) -> OType;
get_oracle_type(oracle_check, {fun_t, _, _, [OType | _], _}) -> OType;
get_oracle_type(oracle_check_query, {fun_t, _, _, [OType | _], _}) -> OType;
get_oracle_type(oracle_respond, {fun_t, _, _, [OType | _], _}) -> OType.
%% -- Pattern matching --
-spec alts_to_fcode(env(), ftype(), var_name(), [aeso_syntax:alt()], aeso_syntax:expr()) -> fsplit().
-spec alts_to_fcode(env(), ftype(), var_name(), [so_syntax:alt()], so_syntax:expr()) -> fsplit().
alts_to_fcode(Env, Type, X, Alts, Switch) ->
FAlts = remove_guards(Env, Alts, Switch),
split_tree(Env, [{X, Type}], FAlts).
@ -915,7 +885,7 @@ alts_to_fcode(Env, Type, X, Alts, Switch) ->
| {con, arities(), tag(), [fpat()]}
| {assign, fpat(), fpat()}.
-spec remove_guards(env(), [aeso_syntax:alt()], aeso_syntax:expr()) -> [falt()].
-spec remove_guards(env(), [so_syntax:alt()], so_syntax:expr()) -> [falt()].
remove_guards(_Env, [], _Switch) ->
[];
remove_guards(Env, [Alt = {'case', _, _, [{guarded, _, [], _Expr}]} | Rest], Switch) ->
@ -1080,19 +1050,19 @@ next_split(Pats) ->
[I | _] -> I
end.
-spec alt_to_fcode(env(), aeso_syntax:alt()) -> falt().
-spec alt_to_fcode(env(), so_syntax:alt()) -> falt().
alt_to_fcode(Env, {'case', _, Pat, [{guarded, _, [], Expr}]}) ->
FPat = pat_to_fcode(Env, Pat),
FExpr = expr_to_fcode(bind_vars(Env, pat_vars(FPat)), Expr),
{'case', [FPat], FExpr}.
-spec pat_to_fcode(env(), aeso_syntax:pat()) -> fpat().
-spec pat_to_fcode(env(), so_syntax:pat()) -> fpat().
pat_to_fcode(Env, {typed, _, Pat, Type}) ->
pat_to_fcode(Env, Type, Pat);
pat_to_fcode(Env, Pat) ->
pat_to_fcode(Env, no_type, Pat).
-spec pat_to_fcode(env(), aeso_syntax:type() | no_type, aeso_syntax:pat()) -> fpat().
-spec pat_to_fcode(env(), so_syntax:type() | no_type, so_syntax:pat()) -> fpat().
pat_to_fcode(_Env, _Type, {id, _, X}) -> {var, X};
pat_to_fcode(Env, _Type, {C, _, _} = Con) when C == con; C == qcon ->
#con_tag{tag = I, arities = As} = lookup_con(Env, Con),
@ -1134,7 +1104,7 @@ pat_to_fcode(_Env, Type, Pat) ->
| {atom, fexpr()}
| {'if', fexpr(), decision_tree(), decision_tree()}.
-spec decision_op(aeso_syntax:op(), decision_tree(), decision_tree()) -> decision_tree().
-spec decision_op(so_syntax:op(), decision_tree(), decision_tree()) -> decision_tree().
decision_op('&&', {atom, A}, B) -> {'if', A, B, false};
decision_op('&&', false, _) -> false;
decision_op('&&', true, B) -> B;
@ -1144,7 +1114,7 @@ decision_op('||', true, _) -> true;
decision_op(Op, {'if', A, Then, Else}, B) ->
{'if', A, decision_op(Op, Then, B), decision_op(Op, Else, B)}.
-spec expr_to_decision_tree(env(), aeso_syntax:expr()) -> decision_tree().
-spec expr_to_decision_tree(env(), so_syntax:expr()) -> decision_tree().
expr_to_decision_tree(Env, {app, _Ann, {Op, _}, [A, B]}) when Op == '&&'; Op == '||' ->
decision_op(Op, expr_to_decision_tree(Env, A), expr_to_decision_tree(Env, B));
expr_to_decision_tree(Env, {typed, _, Expr, _}) -> expr_to_decision_tree(Env, Expr);
@ -1163,7 +1133,7 @@ decision_tree_to_fcode({'if', A, Then, Else}) ->
%% -- Statements --
-spec stmts_to_fcode(env(), [aeso_syntax:stmt()]) -> fexpr().
-spec stmts_to_fcode(env(), [so_syntax:stmt()]) -> fexpr().
stmts_to_fcode(Env, [{letval, Ann, {typed, _, {id, _, X}, _}, Expr} | Stmts]) ->
{'let', to_fann(Ann), X, expr_to_fcode(Env, Expr), stmts_to_fcode(bind_var(Env, X), Stmts)};
stmts_to_fcode(Env, [{letval, Ann, Pat, Expr} | Stmts]) ->
@ -1178,7 +1148,7 @@ stmts_to_fcode(Env, [{letfun, Ann, {id, _, X}, Args, _Type, [{guarded, _, [], Ex
stmts_to_fcode(Env, [Expr]) ->
expr_to_fcode(Env, Expr);
stmts_to_fcode(Env, [Expr | Stmts]) ->
{'let', to_fann(aeso_syntax:get_ann(Expr)), "_", expr_to_fcode(Env, Expr), stmts_to_fcode(Env, Stmts)}.
{'let', to_fann(so_syntax:get_ann(Expr)), "_", expr_to_fcode(Env, Expr), stmts_to_fcode(Env, Stmts)}.
%% -- Builtins --
@ -1810,7 +1780,7 @@ used_functions(Used, [Name | Rest], Defs) ->
%% -- Types --
-spec lookup_type(env(), aeso_syntax:id() | aeso_syntax:qid() | sophia_name(), [ftype()]) -> ftype().
-spec lookup_type(env(), so_syntax:id() | so_syntax:qid() | sophia_name(), [ftype()]) -> ftype().
lookup_type(Env, {id, _, Name}, Args) ->
lookup_type(Env, [Name], Args);
lookup_type(Env, {qid, _, Name}, Args) ->
@ -1842,7 +1812,7 @@ bind_constructors(Env = #{ con_env := ConEnv }, NewCons) ->
add_child_con(Env = #{child_con_env := CEnv}, Name, Fcode) ->
Env#{ child_con_env := CEnv#{Name => Fcode} }.
-spec add_fun_env(env(), [aeso_syntax:decl()]) -> env().
-spec add_fun_env(env(), [so_syntax:decl()]) -> env().
add_fun_env(Env = #{ context := {abstract_contract, _} }, _) -> Env; %% no functions from abstract contracts
add_fun_env(Env = #{ fun_env := FunEnv }, Decls) ->
Entry = fun({letfun, Ann, {id, _, Name}, Args, _, _}) ->
@ -1853,7 +1823,7 @@ add_fun_env(Env = #{ fun_env := FunEnv }, Decls) ->
FunEnv1 = maps:from_list(lists:flatmap(Entry, Decls)),
Env#{ fun_env := maps:merge(FunEnv, FunEnv1) }.
-spec make_fun_name(env(), aeso_syntax:ann(), aeso_syntax:name()) -> fun_name().
-spec make_fun_name(env(), so_syntax:ann(), so_syntax:name()) -> fun_name().
make_fun_name(#{ context := Context }, Ann, Name) ->
Entrypoint = proplists:get_value(entrypoint, Ann, false),
case Context of
@ -1884,7 +1854,7 @@ lookup_fun(#{ fun_env := FunEnv }, Name) ->
{FName, _} -> FName
end.
-spec lookup_con(env(), aeso_syntax:con() | aeso_syntax:qcon() | sophia_name()) -> con_tag().
-spec lookup_con(env(), so_syntax:con() | so_syntax:qcon() | sophia_name()) -> con_tag().
lookup_con(Env, {con, _, Con}) -> lookup_con(Env, [Con]);
lookup_con(Env, {qcon, _, Con}) -> lookup_con(Env, Con);
lookup_con(#{ con_env := ConEnv }, Con) ->
@ -1900,7 +1870,7 @@ bind_vars(Env, Xs) ->
-spec bind_var(env(), var_name()) -> env().
bind_var(Env = #{ vars := Vars }, X) -> Env#{ vars := [X | Vars] }.
-spec resolve_var(env(), aeso_syntax:ann(), [aeso_syntax:name()]) -> fexpr().
-spec resolve_var(env(), so_syntax:ann(), [so_syntax:name()]) -> fexpr().
resolve_var(#{ vars := Vars } = Env, Ann, [X]) ->
case lists:member(X, Vars) of
true -> {var, to_fann(Ann), X};
@ -1922,7 +1892,7 @@ resolve_const(#{ consts := Consts }, Q) ->
Val -> Val
end.
-spec resolve_fun(env(), aeso_syntax:ann(), [aeso_syntax:name()]) -> fexpr().
-spec resolve_fun(env(), so_syntax:ann(), [so_syntax:name()]) -> fexpr().
resolve_fun(#{ fun_env := Funs, builtins := Builtin } = Env, Ann, Q) ->
case {maps:get(Q, Funs, not_found), maps:get(Q, Builtin, not_found)} of
{not_found, not_found} -> internal_error({unbound_variable, Q});
@ -2102,7 +2072,7 @@ bottom_up(F, Env, Expr) ->
{'case', Pat, Split} -> {'case', Pat, bottom_up(F, Env, Split)}
end).
-spec get_named_args([aeso_syntax:named_arg_t()], [aeso_syntax:arg_expr()]) -> [aeso_syntax:expr()].
-spec get_named_args([so_syntax:named_arg_t()], [so_syntax:arg_expr()]) -> [so_syntax:expr()].
get_named_args(NamedArgsT, Args) ->
IsNamed = fun({named_arg, _, _, _}) -> true;
(_) -> false end,
@ -2110,7 +2080,7 @@ get_named_args(NamedArgsT, Args) ->
NamedArgs = [get_named_arg(NamedArg, Named) || NamedArg <- NamedArgsT],
NamedArgs ++ NotNamed.
-spec get_named_arg(aeso_syntax:named_arg_t(), [aeso_syntax:arg_expr()]) -> aeso_syntax:expr().
-spec get_named_arg(so_syntax:named_arg_t(), [so_syntax:arg_expr()]) -> so_syntax:expr().
get_named_arg({named_arg_t, _, {id, _, Name}, _, Default}, Args) ->
case [ Val || {named_arg, _, {id, _, X}, Val} <- Args, X == Name ] of
[Val] -> Val;
@ -2236,15 +2206,15 @@ update_rename(OldRen, NewRen) ->
%% -- Records --
-spec field_index(aeso_syntax:typedef(), aeso_syntax:name()) -> integer().
-spec field_index(so_syntax:typedef(), so_syntax:name()) -> integer().
field_index({record_t, Fields}, X) ->
IsX = fun({field_t, _, {id, _, Y}, _}) -> X == Y end,
[I] = [ I || {I, Field} <- indexed(Fields), IsX(Field) ],
I - 1. %% Tuples are 0-indexed
-spec field_value(aeso_syntax:field_t(), [aeso_syntax:field(aeso_syntax:pat())]) -> Res when
Res :: {upd, aeso_syntax:name(), Expr} | {set, Expr} | false,
Expr :: aeso_syntax:expr().
-spec field_value(so_syntax:field_t(), [so_syntax:field(so_syntax:pat())]) -> Res when
Res :: {upd, so_syntax:name(), Expr} | {set, Expr} | false,
Expr :: so_syntax:expr().
field_value({field_t, _, {id, _, X}, _}, Fields) ->
View = fun({field, _, [{proj, _, {id, _, Y}}], E}) -> {Y, {set, E}};
({field_upd, _, [{proj, _, {id, _, Y}}],
@ -2256,13 +2226,13 @@ field_value({field_t, _, {id, _, X}, _}, Fields) ->
%% -- Attributes --
-spec get_attributes(aeso_syntax:ann()) -> [stateful | payable | private].
-spec get_attributes(so_syntax:ann()) -> [stateful | payable | private].
get_attributes(Ann) ->
[stateful || proplists:get_value(stateful, Ann, false)] ++
[payable || proplists:get_value(payable, Ann, false)] ++
[private || not proplists:get_value(entrypoint, Ann, false)].
-spec get_attributes_debug(aeso_syntax:ann()) -> [stateful | payable | private | fann()].
-spec get_attributes_debug(so_syntax:ann()) -> [stateful | payable | private | fann()].
get_attributes_debug(Ann) ->
get_attributes(Ann) ++ to_fann(Ann).
@ -2283,14 +2253,14 @@ setnth(I, X, Xs) ->
-spec fcode_error(string()) -> no_return().
fcode_error(Error) ->
Pos = aeso_errors:pos(0, 0),
Pos = so_errors:pos(0, 0),
Msg = lists:flatten(io_lib:format("Unknown error: ~p\n", [Error])),
aeso_errors:throw(aeso_errors:new(code_error, Pos, Msg)).
so_errors:throw(so_errors:new(code_error, Pos, Msg)).
-spec internal_error(string()) -> no_return().
internal_error(Error) ->
Msg = lists:flatten(io_lib:format("~p\n", [Error])),
aeso_errors:throw(aeso_errors:new(internal_error, aeso_errors:pos(0, 0), Msg)).
so_errors:throw(so_errors:new(internal_error, so_errors:pos(0, 0), Msg)).
%% -- Pretty printing --------------------------------------------------------
@ -2367,7 +2337,7 @@ pp_fexpr({lit, _, {typerep, T}}) ->
pp_fexpr({lit, _, {contract_code, Contract}}) ->
pp_beside(pp_text("contract "), pp_text(Contract));
pp_fexpr({lit, _, {Tag, Lit}}) ->
aeso_pretty:expr({Tag, [], Lit});
so_pretty:expr({Tag, [], Lit});
pp_fexpr({nil, _}) ->
pp_text("[]");
pp_fexpr({var, _, X}) -> pp_text(X);
@ -2451,7 +2421,6 @@ pp_ftype(T) when is_atom(T) -> pp_text(T);
pp_ftype(any) -> pp_text("_");
pp_ftype({tvar, X}) -> pp_text(X);
pp_ftype({bytes, N}) -> pp_call(pp_text("bytes"), [{lit, [], {int, N}}]);
pp_ftype({oracle, Q, R}) -> pp_call_t("oracle", [Q, R]);
pp_ftype({tuple, Ts}) ->
pp_parens(pp_par(pp_punctuate(pp_text(" *"), [pp_ftype(T) || T <- Ts])));
pp_ftype({list, T}) ->

View File

@ -1,12 +1,13 @@
%%%-------------------------------------------------------------------
%%% @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
@ -27,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
@ -43,7 +44,7 @@
{explicit_files, #{string() => binary()}}}
| {src_file, string()}
| {src_dir, string()}
| {aci, aeso_aci:aci_type()}.
| {aci, so_aci:aci_type()}.
-type options() :: [option()].
-export_type([ option/0
@ -52,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
@ -80,20 +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} ->
SrcDir = aeso_utils:canonical_dir(filename:dirname(File)),
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) ->
@ -102,10 +103,10 @@ add_include_path(File, Options) ->
false ->
Dir = filename:dirname(File),
{ok, Cwd} = file:get_cwd(),
[{include, {file_system, [Cwd, aeso_utils:canonical_dir(Dir)]}} | Options]
[{include, {file_system, [Cwd, 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) ->
@ -122,16 +123,16 @@ from_string1(ContractString, Options) ->
, warnings := Warnings } = string_to_code(ContractString, Options),
#{ child_con_env := ChildContracts } = FCodeEnv,
SavedFreshNames = maps:get(saved_fresh_names, FCodeEnv, #{}),
FateCode = aeso_fcode_to_fate:compile(ChildContracts, FCode, SavedFreshNames, Options),
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
},
@ -142,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.
@ -151,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
@ -171,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 == [] ->
@ -190,7 +191,7 @@ check_call1(ContractString0, FunName, Args, Options) ->
case add_extra_call(ContractString0, {call, FunName, Args}, Options) of
{ok, CallName, Code} ->
{def, _, _, FcodeArgs} = get_call_body(CallName, Code),
{ok, FunName, [ aeso_fcode_to_fate:term_to_fate(A) || A <- FcodeArgs ]};
{ok, FunName, [ so_fcode_to_fate:term_to_fate(A) || A <- FcodeArgs ]};
Err = {error, _} ->
Err
end.
@ -201,9 +202,9 @@ add_extra_call(Contract0, Call, Options) ->
#{fcode := OrgFcode
, fcode_env := #{child_con_env := ChildContracts}
, ast := Ast} = string_to_code(Contract0, Options),
FateCode = aeso_fcode_to_fate:compile(ChildContracts, OrgFcode, #{}, []),
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)),
Contract = insert_call_function(Ast, Contract0, CallName, Call),
@ -220,7 +221,7 @@ encode_value(Contract0, Type, Value, Options) ->
case add_extra_call(Contract0, {value, Type, Value}, Options) of
{ok, CallName, Code} ->
Body = get_call_body(CallName, Code),
{ok, aeb_fate_encoding:serialize(aeso_fcode_to_fate:term_to_fate(Body))};
{ok, gmb_fate_encoding:serialize(so_fcode_to_fate:term_to_fate(Body))};
Err = {error, _} ->
Err
end.
@ -231,7 +232,7 @@ decode_value(Contract0, Type, FateValue, Options) ->
#{ folded_typed_ast := TypedAst
, type_env := TypeEnv} = Code,
{ok, _, Type0} = get_decode_type(CallName, TypedAst),
Type1 = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0,
Type1 = so_ast_infer_types:unfold_types_in_type(TypeEnv, Type0,
[ unfold_record_types
, unfold_variant_types
, not_unfold_system_alias_types ]),
@ -243,7 +244,7 @@ decode_value(Contract0, Type, FateValue, Options) ->
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 ->
@ -251,7 +252,7 @@ first_none_match(CallName, Hashes, [Char|Chars]) ->
end.
%% Add the __call function to a contract.
-spec insert_call_function(aeso_syntax:ast(), string(), string(),
-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),
@ -290,25 +291,25 @@ 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],
@ -316,7 +317,7 @@ to_sophia_value(ContractString, FunName, ok, Data, Options0) ->
Code = string_to_code(ContractString, Options),
#{ 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,
Type = so_ast_infer_types:unfold_types_in_type(TypeEnv, Type0,
[ unfold_record_types
, unfold_variant_types
, not_unfold_system_alias_types]),
@ -328,40 +329,40 @@ to_sophia_value(ContractString, FunName, ok, Data, Options0) ->
fate_data_to_sophia_value(Type, UnfoldedType, FateData) ->
try
{ok, aeso_vm_decode:from_fate(UnfoldedType, aeb_fate_encoding:deserialize(FateData))}
{ok, so_vm_decode:from_fate(UnfoldedType, gmb_fate_encoding:deserialize(FateData))}
catch throw:cannot_translate_to_sophia ->
Type1 = prettypr:format(aeso_pretty:type(Type)),
Type1 = prettypr:format(so_pretty:type(Type)),
Msg = io_lib:format("Cannot translate FATE value ~p\n of Sophia type ~s",
[aeb_fate_encoding:deserialize(FateData), Type1]),
{error, [aeso_errors:new(data_error, Msg)]};
[gmb_fate_encoding:deserialize(FateData), Type1]),
{error, [so_errors:new(data_error, Msg)]};
_:_ ->
Type1 = prettypr:format(aeso_pretty:type(Type)),
Type1 = prettypr:format(so_pretty:type(Type)),
Msg = io_lib:format("Failed to decode binary as type ~s", [Type1]),
{error, [aeso_errors:new(data_error, Msg)]}
{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
@ -373,26 +374,26 @@ decode_calldata(ContractString, FunName, Calldata, Options0) ->
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,
Type = so_ast_infer_types:unfold_types_in_type(TypeEnv, Type0,
[ unfold_record_types
, unfold_variant_types
, not_unfold_system_alias_types]),
case aeb_fate_abi:decode_calldata(FunName, Calldata) of
case 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}
@ -410,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]) ->
@ -419,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
@ -438,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 ->
@ -465,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
@ -513,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).

View File

@ -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) ->

View File

@ -1,13 +1,13 @@
%%%-------------------------------------------------------------------
%%% @author Ulf Norell
%%% @copyright (C) 2025, QPQ AG
%%% @copyright (C) 2019, Aeternity Anstalt
%%% @doc
%%% Fate backend for Sophia compiler
%%% @end
%%% Created : 11 Jan 2019
%%%
%%%-------------------------------------------------------------------
-module(aeso_fcode_to_fate).
-module(so_fcode_to_fate).
-vsn("9.0.0").
-export([compile/3, compile/4, term_to_fate/1, term_to_fate/2]).
@ -23,7 +23,7 @@
| loop
| tuple() | atom(). %% FATE instruction
-type arg() :: tuple(). %% Not exported: aeb_fate_ops:fate_arg().
-type arg() :: tuple(). %% Not exported: gmb_fate_ops:fate_arg().
%% Annotated scode
-type scode_a() :: [sinstr_a()].
@ -72,9 +72,9 @@ debug(Tag, Options, Fun) ->
-dialyzer({nowarn_function, [code_error/1]}).
code_error(Err) ->
Pos = aeso_errors:pos(0, 0),
Pos = so_errors:pos(0, 0),
Msg = lists:flatten(io_lib:format("Unknown error: ~p\n", [Err])),
aeso_errors:throw(aeso_errors:new(code_error, Pos, Msg)).
so_errors:throw(so_errors:new(code_error, Pos, Msg)).
%% -- Main -------------------------------------------------------------------
@ -87,14 +87,14 @@ compile(ChildContracts, FCode, SavedFreshNames, Options) ->
SFuns = functions_to_scode(ChildContracts, ContractName, Functions, SavedFreshNames, Options),
SFuns1 = optimize_scode(SFuns, Options),
FateCode = to_basic_blocks(SFuns1),
?debug(compile, Options, "~s\n", [aeb_fate_asm:pp(FateCode)]),
?debug(compile, Options, "~s\n", [gmb_fate_asm:pp(FateCode)]),
case proplists:get_value(include_child_contract_symbols, Options, false) of
false -> FateCode;
true -> add_child_symbols(ChildContracts, FateCode)
end.
make_function_id(X) ->
aeb_fate_code:symbol_identifier(make_function_name(X)).
gmb_fate_code:symbol_identifier(make_function_name(X)).
make_function_name(event) -> <<"Chain.event">>;
make_function_name({entrypoint, Name}) -> Name;
@ -103,7 +103,7 @@ make_function_name({local_fun, Xs}) -> list_to_binary("." ++ string:join(Xs,
add_child_symbols(ChildContracts, FateCode) ->
Funs = lists:flatten([ maps:keys(ChildFuns) || {_, #{functions := ChildFuns}} <- maps:to_list(ChildContracts) ]),
Symbols = maps:from_list([ {make_function_id(FName), make_function_name(FName)} || FName <- Funs ]),
aeb_fate_code:update_symbols(FateCode, Symbols).
gmb_fate_code:update_symbols(FateCode, Symbols).
functions_to_scode(ChildContracts, ContractName, Functions, SavedFreshNames, Options) ->
FunNames = maps:keys(Functions),
@ -140,8 +140,6 @@ type_to_scode(string) -> string;
type_to_scode(address) -> address;
type_to_scode({bytes, N}) -> {bytes, N};
type_to_scode(contract) -> contract;
type_to_scode({oracle, _, _}) -> oracle;
type_to_scode(oracle_query) -> oracle_query;
type_to_scode(name) -> name;
type_to_scode(channel) -> channel;
type_to_scode(bits) -> bits;
@ -212,17 +210,17 @@ serialize_contract_code(Env, C) ->
SavedFreshNames = Env#env.saved_fresh_names,
FCode = maps:get(C, Env#env.child_contracts),
FateCode = compile(Env#env.child_contracts, FCode, SavedFreshNames, Options),
ByteCode = aeb_fate_code:serialize(FateCode, []),
{ok, Version} = aeso_compiler:version(),
ByteCode = gmb_fate_code:serialize(FateCode, []),
{ok, Version} = so_compiler:version(),
OriginalSourceCode = proplists:get_value(original_src, Options, ""),
Code = #{byte_code => ByteCode,
compiler_version => Version,
source_hash => crypto:hash(sha256, OriginalSourceCode ++ [0] ++ C),
type_info => [],
abi_version => aeb_fate_abi:abi_version(),
abi_version => gmb_fate_abi:abi_version(),
payable => maps:get(payable, FCode)
},
Serialized = aeser_contract_code:serialize(Code),
Serialized = gmser_contract_code:serialize(Code),
put(contract_code_cache, maps:put(C, Serialized, Cache)),
Serialized;
Serialized -> Serialized
@ -230,17 +228,15 @@ serialize_contract_code(Env, C) ->
lit_to_fate(Env, L) ->
case L of
{int, N} -> aeb_fate_data:make_integer(N);
{string, S} -> aeb_fate_data:make_string(S);
{bytes, B} -> aeb_fate_data:make_bytes(B);
{bool, B} -> aeb_fate_data:make_boolean(B);
{account_pubkey, K} -> aeb_fate_data:make_address(K);
{signature, S} -> aeb_fate_data:make_bytes(S);
{contract_pubkey, K} -> aeb_fate_data:make_contract(K);
{oracle_pubkey, K} -> aeb_fate_data:make_oracle(K);
{oracle_query_id, H} -> aeb_fate_data:make_oracle_query(H);
{contract_code, C} -> aeb_fate_data:make_contract_bytearray(serialize_contract_code(Env, C));
{typerep, T} -> aeb_fate_data:make_typerep(type_to_scode(T))
{int, N} -> gmb_fate_data:make_integer(N);
{string, S} -> gmb_fate_data:make_string(S);
{bytes, B} -> gmb_fate_data:make_bytes(B);
{bool, B} -> gmb_fate_data:make_boolean(B);
{account_pubkey, K} -> gmb_fate_data:make_address(K);
{signature, S} -> gmb_fate_data:make_bytes(S);
{contract_pubkey, K} -> gmb_fate_data:make_contract(K);
{contract_code, C} -> gmb_fate_data:make_contract_bytearray(serialize_contract_code(Env, C));
{typerep, T} -> gmb_fate_data:make_typerep(type_to_scode(T))
end.
term_to_fate(E) -> term_to_fate(#env{}, #{}, E).
@ -250,21 +246,21 @@ term_to_fate(GlobEnv, _Env, {lit, _, L}) ->
lit_to_fate(GlobEnv, L);
%% negative literals are parsed as 0 - N
term_to_fate(_GlobEnv, _Env, {op, _, '-', [{lit, _, {int, 0}}, {lit, _, {int, N}}]}) ->
aeb_fate_data:make_integer(-N);
gmb_fate_data:make_integer(-N);
term_to_fate(_GlobEnv, _Env, {nil, _}) ->
aeb_fate_data:make_list([]);
gmb_fate_data:make_list([]);
term_to_fate(GlobEnv, Env, {op, _, '::', [Hd, Tl]}) ->
%% The Tl will translate into a list, because FATE lists are just lists
[term_to_fate(GlobEnv, Env, Hd) | term_to_fate(GlobEnv, Env, Tl)];
term_to_fate(GlobEnv, Env, {tuple, _, As}) ->
aeb_fate_data:make_tuple(list_to_tuple([ term_to_fate(GlobEnv, Env, A) || A<-As]));
gmb_fate_data:make_tuple(list_to_tuple([ term_to_fate(GlobEnv, Env, A) || A<-As]));
term_to_fate(GlobEnv, Env, {con, _, Ar, I, As}) ->
FateAs = [ term_to_fate(GlobEnv, Env, A) || A <- As ],
aeb_fate_data:make_variant(Ar, I, list_to_tuple(FateAs));
gmb_fate_data:make_variant(Ar, I, list_to_tuple(FateAs));
term_to_fate(_GlobEnv, _Env, {builtin, _, bits_all, []}) ->
aeb_fate_data:make_bits(-1);
gmb_fate_data:make_bits(-1);
term_to_fate(_GlobEnv, _Env, {builtin, _, bits_none, []}) ->
aeb_fate_data:make_bits(0);
gmb_fate_data:make_bits(0);
term_to_fate(GlobEnv, _Env, {op, _, bits_set, [B, I]}) ->
{bits, N} = term_to_fate(GlobEnv, B),
J = term_to_fate(GlobEnv, I),
@ -282,7 +278,7 @@ term_to_fate(_GlobEnv, Env, {var, _, X}) ->
V -> V
end;
term_to_fate(_GlobEnv, _Env, {builtin, _, map_empty, []}) ->
aeb_fate_data:make_map(#{});
gmb_fate_data:make_map(#{});
term_to_fate(GlobEnv, Env, {op, _, map_set, [M, K, V]}) ->
Map = term_to_fate(GlobEnv, Env, M),
Map#{term_to_fate(GlobEnv, Env, K) => term_to_fate(GlobEnv, Env, V)};
@ -304,7 +300,7 @@ to_scode1(Env, {lit, Ann, L}) ->
[ dbg_loc(Env, Ann), push(?i(lit_to_fate(Env, L))) ];
to_scode1(Env, {nil, Ann}) ->
[ dbg_loc(Env, Ann), aeb_fate_ops:nil(?a) ];
[ dbg_loc(Env, Ann), gmb_fate_ops:nil(?a) ];
to_scode1(Env, {var, Ann, X}) ->
[ dbg_loc(Env, Ann), push(lookup_var(Env, X)) ];
@ -313,7 +309,7 @@ to_scode1(Env, {con, Ann, Ar, I, As}) ->
N = length(As),
[ dbg_loc(Env, Ann),
[to_scode(notail(Env), A) || A <- As],
aeb_fate_ops:variant(?a, ?i(Ar), ?i(I), ?i(N)) ];
gmb_fate_ops:variant(?a, ?i(Ar), ?i(I), ?i(N)) ];
to_scode1(Env, {tuple, Ann, As}) ->
N = length(As),
@ -324,13 +320,13 @@ to_scode1(Env, {tuple, Ann, As}) ->
to_scode1(Env, {proj, Ann, E, I}) ->
[ dbg_loc(Env, Ann),
to_scode(notail(Env), E),
aeb_fate_ops:element_op(?a, ?i(I), ?a) ];
gmb_fate_ops:element_op(?a, ?i(I), ?a) ];
to_scode1(Env, {set_proj, Ann, R, I, E}) ->
[ dbg_loc(Env, Ann),
to_scode(notail(Env), E),
to_scode(notail(Env), R),
aeb_fate_ops:setelement(?a, ?i(I), ?a, ?a) ];
gmb_fate_ops:setelement(?a, ?i(I), ?a, ?a) ];
to_scode1(Env, {op, Ann, Op, Args}) ->
[ dbg_loc(Env, Ann) | call_to_scode(Env, op_to_scode(Op), Args) ];
@ -341,7 +337,7 @@ to_scode1(Env, {'let', Ann, X, {var, _, Y}, Body}) ->
to_scode1(Env, {'let', Ann, X, Expr, Body}) ->
{I, Env1} = bind_local(X, Env),
SCode = [ to_scode(notail(Env), Expr),
aeb_fate_ops:store({var, I}, {stack, 0}),
gmb_fate_ops:store({var, I}, {stack, 0}),
to_scode(Env1, Body) ],
[ dbg_loc(Env, Ann) | dbg_scoped_vars(Env1, [X], SCode) ];
@ -355,18 +351,18 @@ to_scode1(Env = #env{ current_function = Fun, tailpos = true, debug_info = false
{I, Env2} = bind_local("_", Env1),
ArgCode = to_scode(notail(Env2), Arg),
Acc1 = [Acc, ArgCode,
aeb_fate_ops:store({var, I}, ?a)],
gmb_fate_ops:store({var, I}, ?a)],
{[I | Is], Acc1, Env2}
end, {[], [], Env}, Args),
[ dbg_loc(Env, Ann),
Code,
[ aeb_fate_ops:store({arg, I}, {var, J})
[ gmb_fate_ops:store({arg, I}, {var, J})
|| {I, J} <- lists:zip(lists:seq(0, length(Vars) - 1),
lists:reverse(Vars)) ],
loop ];
to_scode1(Env, {def, Ann, Fun, Args}) ->
FName = make_function_id(Fun),
Lbl = aeb_fate_data:make_string(FName),
Lbl = gmb_fate_data:make_string(FName),
[ dbg_loc(Env, Ann) | call_to_scode(Env, local_call(Env, ?i(Lbl)), Args) ];
to_scode1(Env, {funcall, Ann, Fun, Args}) ->
[ dbg_loc(Env, Ann) | call_to_scode(Env, [to_scode(Env, Fun), local_call(Env, ?a)], Args) ];
@ -377,23 +373,23 @@ to_scode1(Env, {builtin, Ann, B, Args}) ->
to_scode1(Env, {remote, Ann, ArgsT, RetT, Ct, Fun, [Gas, Value, Protected | Args]}) ->
Lbl = make_function_id(Fun),
{ArgTypes, RetType0} = typesig_to_scode([{"_", T} || T <- ArgsT], RetT),
ArgType = ?i(aeb_fate_data:make_typerep({tuple, ArgTypes})),
RetType = ?i(aeb_fate_data:make_typerep(RetType0)),
ArgType = ?i(gmb_fate_data:make_typerep({tuple, ArgTypes})),
RetType = ?i(gmb_fate_data:make_typerep(RetType0)),
SCode = case Protected of
{lit, _, {bool, false}} ->
case Gas of
{builtin, _, call_gas_left, _} ->
Call = aeb_fate_ops:call_r(?a, Lbl, ArgType, RetType, ?a),
Call = gmb_fate_ops:call_r(?a, Lbl, ArgType, RetType, ?a),
call_to_scode(Env, Call, [Ct, Value | Args]);
_ ->
Call = aeb_fate_ops:call_gr(?a, Lbl, ArgType, RetType, ?a, ?a),
Call = gmb_fate_ops:call_gr(?a, Lbl, ArgType, RetType, ?a, ?a),
call_to_scode(Env, Call, [Ct, Value, Gas | Args])
end;
{lit, _, {bool, true}} ->
Call = aeb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?i(true)),
Call = gmb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?i(true)),
call_to_scode(Env, Call, [Ct, Value, Gas | Args]);
_ ->
Call = aeb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?a),
Call = gmb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?a),
call_to_scode(Env, Call, [Ct, Value, Gas, Protected | Args])
end,
[ dbg_loc(Env, Ann) | SCode ];
@ -409,8 +405,8 @@ to_scode1(Env, {closure, Ann, Fun, FVs}) ->
to_scode1(Env, {switch, Ann, Case}) ->
[ dbg_loc(Env, Ann) | split_to_scode(Env, Case) ].
local_call( Env = #env{debug_info = false}, Fun) when Env#env.tailpos -> aeb_fate_ops:call_t(Fun);
local_call(_Env, Fun) -> aeb_fate_ops:call(Fun).
local_call( Env = #env{debug_info = false}, Fun) when Env#env.tailpos -> gmb_fate_ops:call_t(Fun);
local_call(_Env, Fun) -> gmb_fate_ops:call(Fun).
split_to_scode(Env, {nosplit, Renames, Expr}) ->
[switch_body, dbg_scoped_vars(Env, Renames, to_scode(Env, Expr))];
@ -448,13 +444,13 @@ split_to_scode(Env, {split, {list, _}, X, Alts}) ->
[{'case', {'::', Y, Z}, S} | _] ->
{I, Env1} = bind_local(Y, Env),
{J, Env2} = bind_local(Z, Env1),
[aeb_fate_ops:hd({var, I}, Arg),
aeb_fate_ops:tl({var, J}, Arg),
[gmb_fate_ops:hd({var, I}, Arg),
gmb_fate_ops:tl({var, J}, Arg),
split_to_scode(Env2, S)]
end
end,
SAlts = [GetAlt('::'), GetAlt(nil)],
[aeb_fate_ops:is_nil(?a, Arg),
[gmb_fate_ops:is_nil(?a, Arg),
{switch, ?a, boolean, SAlts, Def}];
split_to_scode(Env, {split, Type, X, Alts}) when Type == integer; Type == string ->
{Def, Alts1} = catchall_to_scode(Env, X, Alts),
@ -488,9 +484,9 @@ literal_split_to_scode(Env, Type, Arg, [{'case', Lit, Body} | Alts], Def) when T
end,
SLit = case Lit of
{int, N} -> N;
{string, S} -> aeb_fate_data:make_string(S)
{string, S} -> gmb_fate_data:make_string(S)
end,
[aeb_fate_ops:eq(?a, Arg, ?i(SLit)),
[gmb_fate_ops:eq(?a, Arg, ?i(SLit)),
{switch, ?a, boolean, [False, True], Def}].
catchall_to_scode(Env, X, Alts) -> catchall_to_scode(Env, X, Alts, []).
@ -504,10 +500,10 @@ catchall_to_scode(_, _, [], Acc) -> {missing, lists:reverse(Acc)}.
%% Tuple is in the accumulator. Arguments are the variable names.
match_tuple(Env, Arg, Xs) ->
match_tuple(Env, 0, fun aeb_fate_ops:element_op/3, Arg, Xs).
match_tuple(Env, 0, fun gmb_fate_ops:element_op/3, Arg, Xs).
match_variant(Env, Arg, Xs) ->
Elem = fun(Dst, I, Val) -> aeb_fate_ops:variant_element(Dst, Val, I) end,
Elem = fun(Dst, I, Val) -> gmb_fate_ops:variant_element(Dst, Val, I) end,
match_tuple(Env, 0, Elem, Arg, Xs).
match_tuple(Env, I, Elem, Arg, ["_" | Xs]) ->
@ -526,239 +522,215 @@ call_to_scode(Env, CallCode, Args) ->
CallCode].
builtin_to_scode(Env, chain_event, Args) ->
call_to_scode(Env, [erlang:apply(aeb_fate_ops, log, lists:duplicate(length(Args), ?a)),
call_to_scode(Env, [erlang:apply(gmb_fate_ops, log, lists:duplicate(length(Args), ?a)),
tuple(0)], Args);
builtin_to_scode(_Env, map_empty, []) ->
[aeb_fate_ops:map_empty(?a)];
[gmb_fate_ops:map_empty(?a)];
builtin_to_scode(_Env, bits_none, []) ->
[aeb_fate_ops:bits_none(?a)];
[gmb_fate_ops:bits_none(?a)];
builtin_to_scode(_Env, bits_all, []) ->
[aeb_fate_ops:bits_all(?a)];
[gmb_fate_ops:bits_all(?a)];
builtin_to_scode(Env, bytes_to_int, [_] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_to_int(?a, ?a), Args);
call_to_scode(Env, gmb_fate_ops:bytes_to_int(?a, ?a), Args);
builtin_to_scode(Env, bytes_to_str, [_] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_to_str(?a, ?a), Args);
call_to_scode(Env, gmb_fate_ops:bytes_to_str(?a, ?a), Args);
builtin_to_scode(Env, bytes_concat, [_, _] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_concat(?a, ?a, ?a), Args);
call_to_scode(Env, gmb_fate_ops:bytes_concat(?a, ?a, ?a), Args);
builtin_to_scode(Env, bytes_split, [_, _] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_split(?a, ?a, ?a), Args);
call_to_scode(Env, gmb_fate_ops:bytes_split(?a, ?a, ?a), Args);
builtin_to_scode(Env, bytes_split_any, [_, _] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_split_any(?a, ?a, ?a), Args);
call_to_scode(Env, gmb_fate_ops:bytes_split_any(?a, ?a, ?a), Args);
builtin_to_scode(Env, bytes_to_fixed_size, [_, _] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_to_fixed_size(?a, ?a, ?a), Args);
call_to_scode(Env, gmb_fate_ops:bytes_to_fixed_size(?a, ?a, ?a), Args);
builtin_to_scode(Env, bytes_to_any_size, [A]) ->
[to_scode(Env, A)]; %% no_op!
builtin_to_scode(Env, bytes_size, [_] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_size(?a, ?a), Args);
call_to_scode(Env, gmb_fate_ops:bytes_size(?a, ?a), Args);
builtin_to_scode(Env, abort, [_] = Args) ->
call_to_scode(Env, aeb_fate_ops:abort(?a), Args);
call_to_scode(Env, gmb_fate_ops:abort(?a), Args);
builtin_to_scode(Env, exit, [_] = Args) ->
call_to_scode(Env, aeb_fate_ops:exit(?a), Args);
call_to_scode(Env, gmb_fate_ops:exit(?a), Args);
builtin_to_scode(Env, chain_spend, [_, _] = Args) ->
call_to_scode(Env, [aeb_fate_ops:spend(?a, ?a),
call_to_scode(Env, [gmb_fate_ops:spend(?a, ?a),
tuple(0)], Args);
builtin_to_scode(Env, chain_balance, [_] = Args) ->
call_to_scode(Env, aeb_fate_ops:balance_other(?a, ?a), Args);
call_to_scode(Env, gmb_fate_ops:balance_other(?a, ?a), Args);
builtin_to_scode(Env, chain_block_hash, [_] = Args) ->
call_to_scode(Env, aeb_fate_ops:blockhash(?a, ?a), Args);
call_to_scode(Env, gmb_fate_ops:blockhash(?a, ?a), Args);
builtin_to_scode(_Env, chain_coinbase, []) ->
[aeb_fate_ops:beneficiary(?a)];
[gmb_fate_ops:beneficiary(?a)];
builtin_to_scode(_Env, chain_timestamp, []) ->
[aeb_fate_ops:timestamp(?a)];
[gmb_fate_ops:timestamp(?a)];
builtin_to_scode(_Env, chain_block_height, []) ->
[aeb_fate_ops:generation(?a)];
[gmb_fate_ops:generation(?a)];
builtin_to_scode(_Env, chain_difficulty, []) ->
[aeb_fate_ops:difficulty(?a)];
[gmb_fate_ops:difficulty(?a)];
builtin_to_scode(_Env, chain_gas_limit, []) ->
[aeb_fate_ops:gaslimit(?a)];
[gmb_fate_ops:gaslimit(?a)];
builtin_to_scode(_Env, chain_network_id, []) ->
[aeb_fate_ops:network_id(?a)];
[gmb_fate_ops:network_id(?a)];
builtin_to_scode(_Env, contract_balance, []) ->
[aeb_fate_ops:balance(?a)];
[gmb_fate_ops:balance(?a)];
builtin_to_scode(_Env, contract_address, []) ->
[aeb_fate_ops:address(?a)];
[gmb_fate_ops:address(?a)];
builtin_to_scode(_Env, contract_creator, []) ->
[aeb_fate_ops:contract_creator(?a)];
[gmb_fate_ops:contract_creator(?a)];
builtin_to_scode(_Env, call_origin, []) ->
[aeb_fate_ops:origin(?a)];
[gmb_fate_ops:origin(?a)];
builtin_to_scode(_Env, call_caller, []) ->
[aeb_fate_ops:caller(?a)];
[gmb_fate_ops:caller(?a)];
builtin_to_scode(_Env, call_value, []) ->
[aeb_fate_ops:call_value(?a)];
[gmb_fate_ops:call_value(?a)];
builtin_to_scode(_Env, call_gas_price, []) ->
[aeb_fate_ops:gasprice(?a)];
[gmb_fate_ops:gasprice(?a)];
builtin_to_scode(_Env, call_fee, []) ->
[aeb_fate_ops:fee(?a)];
[gmb_fate_ops:fee(?a)];
builtin_to_scode(_Env, call_gas_left, []) ->
[aeb_fate_ops:gas(?a)];
builtin_to_scode(Env, oracle_register, [_Sign,_Account,_QFee,_TTL,_QType,_RType] = Args) ->
call_to_scode(Env, aeb_fate_ops:oracle_register(?a, ?a, ?a, ?a, ?a, ?a, ?a), Args);
builtin_to_scode(Env, oracle_expiry, [_Oracle] = Args) ->
call_to_scode(Env, aeb_fate_ops:oracle_expiry(?a, ?a), Args);
builtin_to_scode(Env, oracle_query_fee, [_Oracle] = Args) ->
call_to_scode(Env, aeb_fate_ops:oracle_query_fee(?a, ?a), Args);
builtin_to_scode(Env, oracle_query, [_Oracle, _Question, _QFee, _QTTL, _RTTL, _QType, _RType] = Args) ->
call_to_scode(Env, aeb_fate_ops:oracle_query(?a, ?a, ?a, ?a, ?a, ?a, ?a, ?a), Args);
builtin_to_scode(Env, oracle_get_question, [_Oracle, _QueryId, _QType, _RType] = Args) ->
call_to_scode(Env, aeb_fate_ops:oracle_get_question(?a, ?a, ?a, ?a, ?a), Args);
builtin_to_scode(Env, oracle_respond, [_Sign, _Oracle, _QueryId, _Response, _QType, _RType] = Args) ->
call_to_scode(Env, [aeb_fate_ops:oracle_respond(?a, ?a, ?a, ?a, ?a, ?a),
tuple(0)], Args);
builtin_to_scode(Env, oracle_extend, [_Sign, _Oracle, _TTL] = Args) ->
call_to_scode(Env, [aeb_fate_ops:oracle_extend(?a, ?a, ?a),
tuple(0)], Args);
builtin_to_scode(Env, oracle_get_answer, [_Oracle, _QueryId, _QType, _RType] = Args) ->
call_to_scode(Env, aeb_fate_ops:oracle_get_answer(?a, ?a, ?a, ?a, ?a), Args);
builtin_to_scode(Env, oracle_check, [_Oracle, _QType, _RType] = Args) ->
call_to_scode(Env, aeb_fate_ops:oracle_check(?a, ?a, ?a, ?a), Args);
builtin_to_scode(Env, oracle_check_query, [_Oracle, _Query, _QType, _RType] = Args) ->
call_to_scode(Env, aeb_fate_ops:oracle_check_query(?a, ?a, ?a, ?a, ?a), Args);
builtin_to_scode(Env, address_is_oracle, [_] = Args) ->
call_to_scode(Env, aeb_fate_ops:is_oracle(?a, ?a), Args);
[gmb_fate_ops:gas(?a)];
builtin_to_scode(Env, address_is_contract, [_] = Args) ->
call_to_scode(Env, aeb_fate_ops:is_contract(?a, ?a), Args);
call_to_scode(Env, gmb_fate_ops:is_contract(?a, ?a), Args);
builtin_to_scode(Env, address_is_payable, [_] = Args) ->
call_to_scode(Env, aeb_fate_ops:is_payable(?a, ?a), Args);
call_to_scode(Env, gmb_fate_ops:is_payable(?a, ?a), Args);
builtin_to_scode(Env, aens_resolve, [_Name, _Key, _Type] = Args) ->
call_to_scode(Env, aeb_fate_ops:aens_resolve(?a, ?a, ?a, ?a), Args);
call_to_scode(Env, gmb_fate_ops:aens_resolve(?a, ?a, ?a, ?a), Args);
builtin_to_scode(Env, aens_preclaim, [_Sign, _Account, _Hash] = Args) ->
call_to_scode(Env, [aeb_fate_ops:aens_preclaim(?a, ?a, ?a),
call_to_scode(Env, [gmb_fate_ops:aens_preclaim(?a, ?a, ?a),
tuple(0)], Args);
builtin_to_scode(Env, aens_claim, [_Sign, _Account, _NameString, _Salt, _NameFee] = Args) ->
call_to_scode(Env, [aeb_fate_ops:aens_claim(?a, ?a, ?a, ?a, ?a),
call_to_scode(Env, [gmb_fate_ops:aens_claim(?a, ?a, ?a, ?a, ?a),
tuple(0)], Args);
builtin_to_scode(Env, aens_transfer, [_Sign, _From, _To, _Name] = Args) ->
call_to_scode(Env, [aeb_fate_ops:aens_transfer(?a, ?a, ?a, ?a),
call_to_scode(Env, [gmb_fate_ops:aens_transfer(?a, ?a, ?a, ?a),
tuple(0)], Args);
builtin_to_scode(Env, aens_revoke, [_Sign, _Account, _Name] = Args) ->
call_to_scode(Env, [aeb_fate_ops:aens_revoke(?a, ?a, ?a),
call_to_scode(Env, [gmb_fate_ops:aens_revoke(?a, ?a, ?a),
tuple(0)], Args);
builtin_to_scode(Env, aens_update, [_Sign, _Account, _NameString, _TTL, _ClientTTL, _Pointers] = Args) ->
call_to_scode(Env, [aeb_fate_ops:aens_update(?a, ?a, ?a, ?a, ?a, ?a),
call_to_scode(Env, [gmb_fate_ops:aens_update(?a, ?a, ?a, ?a, ?a, ?a),
tuple(0)], Args);
builtin_to_scode(Env, aens_lookup, [_Name] = Args) ->
call_to_scode(Env, aeb_fate_ops:aens_lookup(?a, ?a), Args);
call_to_scode(Env, gmb_fate_ops:aens_lookup(?a, ?a), Args);
builtin_to_scode(_Env, auth_tx_hash, []) ->
[aeb_fate_ops:auth_tx_hash(?a)];
[gmb_fate_ops:auth_tx_hash(?a)];
builtin_to_scode(_Env, auth_tx, []) ->
[aeb_fate_ops:auth_tx(?a)];
[gmb_fate_ops:auth_tx(?a)];
builtin_to_scode(Env, chain_bytecode_hash, [_Addr] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytecode_hash(?a, ?a), Args);
call_to_scode(Env, gmb_fate_ops:bytecode_hash(?a, ?a), Args);
builtin_to_scode(Env, chain_clone,
[InitArgsT, GasCap, Value, Prot, Contract | InitArgs]) ->
case GasCap of
{builtin, _, call_gas_left, _} ->
call_to_scode(Env, aeb_fate_ops:clone(?a, ?a, ?a, ?a),
call_to_scode(Env, gmb_fate_ops:clone(?a, ?a, ?a, ?a),
[Contract, InitArgsT, Value, Prot | InitArgs]
);
_ ->
call_to_scode(Env, aeb_fate_ops:clone_g(?a, ?a, ?a, ?a, ?a),
call_to_scode(Env, gmb_fate_ops:clone_g(?a, ?a, ?a, ?a, ?a),
[Contract, InitArgsT, Value, GasCap, Prot | InitArgs]
)
end;
builtin_to_scode(Env, chain_create,
[ Code, InitArgsT, Value | InitArgs]) ->
call_to_scode(Env, aeb_fate_ops:create(?a, ?a, ?a),
call_to_scode(Env, gmb_fate_ops:create(?a, ?a, ?a),
[Code, InitArgsT, Value | InitArgs]
).
%% -- Operators --
op_to_scode('+') -> aeb_fate_ops:add(?a, ?a, ?a);
op_to_scode('-') -> aeb_fate_ops:sub(?a, ?a, ?a);
op_to_scode('*') -> aeb_fate_ops:mul(?a, ?a, ?a);
op_to_scode('/') -> aeb_fate_ops:divide(?a, ?a, ?a);
op_to_scode(mod) -> aeb_fate_ops:modulo(?a, ?a, ?a);
op_to_scode('^') -> aeb_fate_ops:pow(?a, ?a, ?a);
op_to_scode('++') -> aeb_fate_ops:append(?a, ?a, ?a);
op_to_scode('::') -> aeb_fate_ops:cons(?a, ?a, ?a);
op_to_scode('<') -> aeb_fate_ops:lt(?a, ?a, ?a);
op_to_scode('>') -> aeb_fate_ops:gt(?a, ?a, ?a);
op_to_scode('=<') -> aeb_fate_ops:elt(?a, ?a, ?a);
op_to_scode('>=') -> aeb_fate_ops:egt(?a, ?a, ?a);
op_to_scode('==') -> aeb_fate_ops:eq(?a, ?a, ?a);
op_to_scode('!=') -> aeb_fate_ops:neq(?a, ?a, ?a);
op_to_scode('!') -> aeb_fate_ops:not_op(?a, ?a);
op_to_scode('bnot') -> aeb_fate_ops:bin_not(?a, ?a);
op_to_scode('band') -> aeb_fate_ops:bin_and(?a, ?a, ?a);
op_to_scode('bor') -> aeb_fate_ops:bin_or(?a, ?a, ?a);
op_to_scode('bxor') -> aeb_fate_ops:bin_xor(?a, ?a, ?a);
op_to_scode('<<') -> aeb_fate_ops:bin_sl(?a, ?a, ?a);
op_to_scode('>>') -> aeb_fate_ops:bin_sr(?a, ?a, ?a);
op_to_scode(map_get) -> aeb_fate_ops:map_lookup(?a, ?a, ?a);
op_to_scode(map_get_d) -> aeb_fate_ops:map_lookup(?a, ?a, ?a, ?a);
op_to_scode(map_set) -> aeb_fate_ops:map_update(?a, ?a, ?a, ?a);
op_to_scode(map_from_list) -> aeb_fate_ops:map_from_list(?a, ?a);
op_to_scode(map_to_list) -> aeb_fate_ops:map_to_list(?a, ?a);
op_to_scode(map_delete) -> aeb_fate_ops:map_delete(?a, ?a, ?a);
op_to_scode(map_member) -> aeb_fate_ops:map_member(?a, ?a, ?a);
op_to_scode(map_size) -> aeb_fate_ops:map_size_(?a, ?a);
op_to_scode(stringinternal_length) -> aeb_fate_ops:str_length(?a, ?a);
op_to_scode(stringinternal_concat) -> aeb_fate_ops:str_join(?a, ?a, ?a);
op_to_scode(stringinternal_to_bytes) -> aeb_fate_ops:str_to_bytes(?a, ?a);
op_to_scode(stringinternal_to_list) -> aeb_fate_ops:str_to_list(?a, ?a);
op_to_scode(stringinternal_from_list) -> aeb_fate_ops:str_from_list(?a, ?a);
op_to_scode(stringinternal_to_lower) -> aeb_fate_ops:str_to_lower(?a, ?a);
op_to_scode(stringinternal_to_upper) -> aeb_fate_ops:str_to_upper(?a, ?a);
op_to_scode(char_to_int) -> aeb_fate_ops:char_to_int(?a, ?a);
op_to_scode(char_from_int) -> aeb_fate_ops:char_from_int(?a, ?a);
op_to_scode(bits_set) -> aeb_fate_ops:bits_set(?a, ?a, ?a);
op_to_scode(bits_clear) -> aeb_fate_ops:bits_clear(?a, ?a, ?a);
op_to_scode(bits_test) -> aeb_fate_ops:bits_test(?a, ?a, ?a);
op_to_scode(bits_sum) -> aeb_fate_ops:bits_sum(?a, ?a);
op_to_scode(bits_intersection) -> aeb_fate_ops:bits_and(?a, ?a, ?a);
op_to_scode(bits_union) -> aeb_fate_ops:bits_or(?a, ?a, ?a);
op_to_scode(bits_difference) -> aeb_fate_ops:bits_diff(?a, ?a, ?a);
op_to_scode(address_to_str) -> aeb_fate_ops:addr_to_str(?a, ?a);
op_to_scode(address_to_bytes) -> aeb_fate_ops:addr_to_bytes(?a, ?a);
op_to_scode(int_to_str) -> aeb_fate_ops:int_to_str(?a, ?a);
op_to_scode(int_to_bytes) -> aeb_fate_ops:int_to_bytes(?a, ?a, ?a);
op_to_scode(int_mulmod) -> aeb_fate_ops:mulmod(?a, ?a, ?a, ?a);
op_to_scode(contract_to_address) -> aeb_fate_ops:contract_to_address(?a, ?a);
op_to_scode(address_to_contract) -> aeb_fate_ops:address_to_contract(?a, ?a);
op_to_scode(crypto_verify_sig) -> aeb_fate_ops:verify_sig(?a, ?a, ?a, ?a);
op_to_scode(crypto_verify_sig_secp256k1) -> aeb_fate_ops:verify_sig_secp256k1(?a, ?a, ?a, ?a);
op_to_scode(crypto_ecverify_secp256k1) -> aeb_fate_ops:ecverify_secp256k1(?a, ?a, ?a, ?a);
op_to_scode(crypto_ecrecover_secp256k1) -> aeb_fate_ops:ecrecover_secp256k1(?a, ?a, ?a);
op_to_scode(crypto_sha3) -> aeb_fate_ops:sha3(?a, ?a);
op_to_scode(crypto_sha256) -> aeb_fate_ops:sha256(?a, ?a);
op_to_scode(crypto_blake2b) -> aeb_fate_ops:blake2b(?a, ?a);
op_to_scode(crypto_poseidon) -> aeb_fate_ops:poseidon(?a, ?a, ?a);
op_to_scode(stringinternal_sha3) -> aeb_fate_ops:sha3(?a, ?a);
op_to_scode(stringinternal_sha256) -> aeb_fate_ops:sha256(?a, ?a);
op_to_scode(stringinternal_blake2b) -> aeb_fate_ops:blake2b(?a, ?a);
op_to_scode(mcl_bls12_381_g1_neg) -> aeb_fate_ops:bls12_381_g1_neg(?a, ?a);
op_to_scode(mcl_bls12_381_g1_norm) -> aeb_fate_ops:bls12_381_g1_norm(?a, ?a);
op_to_scode(mcl_bls12_381_g1_valid) -> aeb_fate_ops:bls12_381_g1_valid(?a, ?a);
op_to_scode(mcl_bls12_381_g1_is_zero) -> aeb_fate_ops:bls12_381_g1_is_zero(?a, ?a);
op_to_scode(mcl_bls12_381_g1_add) -> aeb_fate_ops:bls12_381_g1_add(?a, ?a, ?a);
op_to_scode(mcl_bls12_381_g1_mul) -> aeb_fate_ops:bls12_381_g1_mul(?a, ?a, ?a);
op_to_scode(mcl_bls12_381_g2_neg) -> aeb_fate_ops:bls12_381_g2_neg(?a, ?a);
op_to_scode(mcl_bls12_381_g2_norm) -> aeb_fate_ops:bls12_381_g2_norm(?a, ?a);
op_to_scode(mcl_bls12_381_g2_valid) -> aeb_fate_ops:bls12_381_g2_valid(?a, ?a);
op_to_scode(mcl_bls12_381_g2_is_zero) -> aeb_fate_ops:bls12_381_g2_is_zero(?a, ?a);
op_to_scode(mcl_bls12_381_g2_add) -> aeb_fate_ops:bls12_381_g2_add(?a, ?a, ?a);
op_to_scode(mcl_bls12_381_g2_mul) -> aeb_fate_ops:bls12_381_g2_mul(?a, ?a, ?a);
op_to_scode(mcl_bls12_381_gt_inv) -> aeb_fate_ops:bls12_381_gt_inv(?a, ?a);
op_to_scode(mcl_bls12_381_gt_add) -> aeb_fate_ops:bls12_381_gt_add(?a, ?a, ?a);
op_to_scode(mcl_bls12_381_gt_mul) -> aeb_fate_ops:bls12_381_gt_mul(?a, ?a, ?a);
op_to_scode(mcl_bls12_381_gt_pow) -> aeb_fate_ops:bls12_381_gt_pow(?a, ?a, ?a);
op_to_scode(mcl_bls12_381_gt_is_one) -> aeb_fate_ops:bls12_381_gt_is_one(?a, ?a);
op_to_scode(mcl_bls12_381_pairing) -> aeb_fate_ops:bls12_381_pairing(?a, ?a, ?a);
op_to_scode(mcl_bls12_381_miller_loop) -> aeb_fate_ops:bls12_381_miller_loop(?a, ?a, ?a);
op_to_scode(mcl_bls12_381_final_exp) -> aeb_fate_ops:bls12_381_final_exp(?a, ?a);
op_to_scode(mcl_bls12_381_int_to_fr) -> aeb_fate_ops:bls12_381_int_to_fr(?a, ?a);
op_to_scode(mcl_bls12_381_int_to_fp) -> aeb_fate_ops:bls12_381_int_to_fp(?a, ?a);
op_to_scode(mcl_bls12_381_fr_to_int) -> aeb_fate_ops:bls12_381_fr_to_int(?a, ?a);
op_to_scode(mcl_bls12_381_fp_to_int) -> aeb_fate_ops:bls12_381_fp_to_int(?a, ?a).
op_to_scode('+') -> gmb_fate_ops:add(?a, ?a, ?a);
op_to_scode('-') -> gmb_fate_ops:sub(?a, ?a, ?a);
op_to_scode('*') -> gmb_fate_ops:mul(?a, ?a, ?a);
op_to_scode('/') -> gmb_fate_ops:divide(?a, ?a, ?a);
op_to_scode(mod) -> gmb_fate_ops:modulo(?a, ?a, ?a);
op_to_scode('^') -> gmb_fate_ops:pow(?a, ?a, ?a);
op_to_scode('++') -> gmb_fate_ops:append(?a, ?a, ?a);
op_to_scode('::') -> gmb_fate_ops:cons(?a, ?a, ?a);
op_to_scode('<') -> gmb_fate_ops:lt(?a, ?a, ?a);
op_to_scode('>') -> gmb_fate_ops:gt(?a, ?a, ?a);
op_to_scode('=<') -> gmb_fate_ops:elt(?a, ?a, ?a);
op_to_scode('>=') -> gmb_fate_ops:egt(?a, ?a, ?a);
op_to_scode('==') -> gmb_fate_ops:eq(?a, ?a, ?a);
op_to_scode('!=') -> gmb_fate_ops:neq(?a, ?a, ?a);
op_to_scode('!') -> gmb_fate_ops:not_op(?a, ?a);
op_to_scode('bnot') -> gmb_fate_ops:bin_not(?a, ?a);
op_to_scode('band') -> gmb_fate_ops:bin_and(?a, ?a, ?a);
op_to_scode('bor') -> gmb_fate_ops:bin_or(?a, ?a, ?a);
op_to_scode('bxor') -> gmb_fate_ops:bin_xor(?a, ?a, ?a);
op_to_scode('<<') -> gmb_fate_ops:bin_sl(?a, ?a, ?a);
op_to_scode('>>') -> gmb_fate_ops:bin_sr(?a, ?a, ?a);
op_to_scode(map_get) -> gmb_fate_ops:map_lookup(?a, ?a, ?a);
op_to_scode(map_get_d) -> gmb_fate_ops:map_lookup(?a, ?a, ?a, ?a);
op_to_scode(map_set) -> gmb_fate_ops:map_update(?a, ?a, ?a, ?a);
op_to_scode(map_from_list) -> gmb_fate_ops:map_from_list(?a, ?a);
op_to_scode(map_to_list) -> gmb_fate_ops:map_to_list(?a, ?a);
op_to_scode(map_delete) -> gmb_fate_ops:map_delete(?a, ?a, ?a);
op_to_scode(map_member) -> gmb_fate_ops:map_member(?a, ?a, ?a);
op_to_scode(map_size) -> gmb_fate_ops:map_size_(?a, ?a);
op_to_scode(stringinternal_length) -> gmb_fate_ops:str_length(?a, ?a);
op_to_scode(stringinternal_concat) -> gmb_fate_ops:str_join(?a, ?a, ?a);
op_to_scode(stringinternal_to_bytes) -> gmb_fate_ops:str_to_bytes(?a, ?a);
op_to_scode(stringinternal_to_list) -> gmb_fate_ops:str_to_list(?a, ?a);
op_to_scode(stringinternal_from_list) -> gmb_fate_ops:str_from_list(?a, ?a);
op_to_scode(stringinternal_to_lower) -> gmb_fate_ops:str_to_lower(?a, ?a);
op_to_scode(stringinternal_to_upper) -> gmb_fate_ops:str_to_upper(?a, ?a);
op_to_scode(char_to_int) -> gmb_fate_ops:char_to_int(?a, ?a);
op_to_scode(char_from_int) -> gmb_fate_ops:char_from_int(?a, ?a);
op_to_scode(bits_set) -> gmb_fate_ops:bits_set(?a, ?a, ?a);
op_to_scode(bits_clear) -> gmb_fate_ops:bits_clear(?a, ?a, ?a);
op_to_scode(bits_test) -> gmb_fate_ops:bits_test(?a, ?a, ?a);
op_to_scode(bits_sum) -> gmb_fate_ops:bits_sum(?a, ?a);
op_to_scode(bits_intersection) -> gmb_fate_ops:bits_and(?a, ?a, ?a);
op_to_scode(bits_union) -> gmb_fate_ops:bits_or(?a, ?a, ?a);
op_to_scode(bits_difference) -> gmb_fate_ops:bits_diff(?a, ?a, ?a);
op_to_scode(address_to_str) -> gmb_fate_ops:addr_to_str(?a, ?a);
op_to_scode(address_to_bytes) -> gmb_fate_ops:addr_to_bytes(?a, ?a);
op_to_scode(int_to_str) -> gmb_fate_ops:int_to_str(?a, ?a);
op_to_scode(int_to_bytes) -> gmb_fate_ops:int_to_bytes(?a, ?a, ?a);
op_to_scode(int_mulmod) -> gmb_fate_ops:mulmod(?a, ?a, ?a, ?a);
op_to_scode(contract_to_address) -> gmb_fate_ops:contract_to_address(?a, ?a);
op_to_scode(address_to_contract) -> gmb_fate_ops:address_to_contract(?a, ?a);
op_to_scode(crypto_verify_sig) -> gmb_fate_ops:verify_sig(?a, ?a, ?a, ?a);
op_to_scode(crypto_verify_sig_secp256k1) -> gmb_fate_ops:verify_sig_secp256k1(?a, ?a, ?a, ?a);
op_to_scode(crypto_ecverify_secp256k1) -> gmb_fate_ops:ecverify_secp256k1(?a, ?a, ?a, ?a);
op_to_scode(crypto_ecrecover_secp256k1) -> gmb_fate_ops:ecrecover_secp256k1(?a, ?a, ?a);
op_to_scode(crypto_sha3) -> gmb_fate_ops:sha3(?a, ?a);
op_to_scode(crypto_sha256) -> gmb_fate_ops:sha256(?a, ?a);
op_to_scode(crypto_blake2b) -> gmb_fate_ops:blake2b(?a, ?a);
op_to_scode(crypto_poseidon) -> gmb_fate_ops:poseidon(?a, ?a, ?a);
op_to_scode(stringinternal_sha3) -> gmb_fate_ops:sha3(?a, ?a);
op_to_scode(stringinternal_sha256) -> gmb_fate_ops:sha256(?a, ?a);
op_to_scode(stringinternal_blake2b) -> gmb_fate_ops:blake2b(?a, ?a);
op_to_scode(mcl_bls12_381_g1_neg) -> gmb_fate_ops:bls12_381_g1_neg(?a, ?a);
op_to_scode(mcl_bls12_381_g1_norm) -> gmb_fate_ops:bls12_381_g1_norm(?a, ?a);
op_to_scode(mcl_bls12_381_g1_valid) -> gmb_fate_ops:bls12_381_g1_valid(?a, ?a);
op_to_scode(mcl_bls12_381_g1_is_zero) -> gmb_fate_ops:bls12_381_g1_is_zero(?a, ?a);
op_to_scode(mcl_bls12_381_g1_add) -> gmb_fate_ops:bls12_381_g1_add(?a, ?a, ?a);
op_to_scode(mcl_bls12_381_g1_mul) -> gmb_fate_ops:bls12_381_g1_mul(?a, ?a, ?a);
op_to_scode(mcl_bls12_381_g2_neg) -> gmb_fate_ops:bls12_381_g2_neg(?a, ?a);
op_to_scode(mcl_bls12_381_g2_norm) -> gmb_fate_ops:bls12_381_g2_norm(?a, ?a);
op_to_scode(mcl_bls12_381_g2_valid) -> gmb_fate_ops:bls12_381_g2_valid(?a, ?a);
op_to_scode(mcl_bls12_381_g2_is_zero) -> gmb_fate_ops:bls12_381_g2_is_zero(?a, ?a);
op_to_scode(mcl_bls12_381_g2_add) -> gmb_fate_ops:bls12_381_g2_add(?a, ?a, ?a);
op_to_scode(mcl_bls12_381_g2_mul) -> gmb_fate_ops:bls12_381_g2_mul(?a, ?a, ?a);
op_to_scode(mcl_bls12_381_gt_inv) -> gmb_fate_ops:bls12_381_gt_inv(?a, ?a);
op_to_scode(mcl_bls12_381_gt_add) -> gmb_fate_ops:bls12_381_gt_add(?a, ?a, ?a);
op_to_scode(mcl_bls12_381_gt_mul) -> gmb_fate_ops:bls12_381_gt_mul(?a, ?a, ?a);
op_to_scode(mcl_bls12_381_gt_pow) -> gmb_fate_ops:bls12_381_gt_pow(?a, ?a, ?a);
op_to_scode(mcl_bls12_381_gt_is_one) -> gmb_fate_ops:bls12_381_gt_is_one(?a, ?a);
op_to_scode(mcl_bls12_381_pairing) -> gmb_fate_ops:bls12_381_pairing(?a, ?a, ?a);
op_to_scode(mcl_bls12_381_miller_loop) -> gmb_fate_ops:bls12_381_miller_loop(?a, ?a, ?a);
op_to_scode(mcl_bls12_381_final_exp) -> gmb_fate_ops:bls12_381_final_exp(?a, ?a);
op_to_scode(mcl_bls12_381_int_to_fr) -> gmb_fate_ops:bls12_381_int_to_fr(?a, ?a);
op_to_scode(mcl_bls12_381_int_to_fp) -> gmb_fate_ops:bls12_381_int_to_fp(?a, ?a);
op_to_scode(mcl_bls12_381_fr_to_int) -> gmb_fate_ops:bls12_381_fr_to_int(?a, ?a);
op_to_scode(mcl_bls12_381_fp_to_int) -> gmb_fate_ops:bls12_381_fp_to_int(?a, ?a).
%% PUSH and STORE ?a are the same, so we use STORE to make optimizations
%% easier, and specialize to PUSH (which is cheaper) at the end.
push(A) -> {'STORE', ?a, A}.
tuple(0) -> push(?i({tuple, {}}));
tuple(N) -> aeb_fate_ops:tuple(?a, N).
tuple(N) -> gmb_fate_ops:tuple(?a, N).
%% -- Debug info functions --
@ -825,8 +797,8 @@ dbg_undef(Undef, SCode) when is_tuple(SCode); is_atom(SCode) ->
true -> tuple_to_list(SCode);
false -> [SCode]
end,
Op = aeb_fate_opcodes:m_to_op(Mnemonic),
case aeb_fate_opcodes:end_bb(Op) of
Op = gmb_fate_opcodes:m_to_op(Mnemonic),
case gmb_fate_opcodes:end_bb(Op) of
true -> [Undef, SCode];
false -> [SCode, Undef]
end.
@ -898,7 +870,7 @@ pp_ann(_, []) -> [].
pp_op(switch_body) -> "SWITCH-BODY";
pp_op(loop) -> "LOOP";
pp_op(I) ->
aeb_fate_pp:format_op(I, #{}).
gmb_fate_pp:format_op(I, #{}).
pp_arg(?i(I)) -> io_lib:format("~w", [I]);
pp_arg({arg, N}) -> io_lib:format("arg~p", [N]);
@ -1668,14 +1640,14 @@ unannotate(Code) when is_list(Code) ->
unannotate({i, _Ann, I}) -> [I].
%% Desugar and specialize
desugar({'ADD', ?a, ?i(1), ?a}) -> [aeb_fate_ops:inc()];
desugar({'ADD', A, ?i(1), A}) -> [aeb_fate_ops:inc(desugar_arg(A))];
desugar({'ADD', ?a, ?a, ?i(1)}) -> [aeb_fate_ops:inc()];
desugar({'ADD', A, A, ?i(1)}) -> [aeb_fate_ops:inc(desugar_arg(A))];
desugar({'SUB', ?a, ?a, ?i(1)}) -> [aeb_fate_ops:dec()];
desugar({'SUB', A, A, ?i(1)}) -> [aeb_fate_ops:dec(desugar_arg(A))];
desugar({'STORE', ?a, A}) -> [aeb_fate_ops:push(desugar_arg(A))];
desugar({'STORE', R, ?a}) -> [aeb_fate_ops:pop(desugar_arg(R))];
desugar({'ADD', ?a, ?i(1), ?a}) -> [gmb_fate_ops:inc()];
desugar({'ADD', A, ?i(1), A}) -> [gmb_fate_ops:inc(desugar_arg(A))];
desugar({'ADD', ?a, ?a, ?i(1)}) -> [gmb_fate_ops:inc()];
desugar({'ADD', A, A, ?i(1)}) -> [gmb_fate_ops:inc(desugar_arg(A))];
desugar({'SUB', ?a, ?a, ?i(1)}) -> [gmb_fate_ops:dec()];
desugar({'SUB', A, A, ?i(1)}) -> [gmb_fate_ops:dec(desugar_arg(A))];
desugar({'STORE', ?a, A}) -> [gmb_fate_ops:push(desugar_arg(A))];
desugar({'STORE', R, ?a}) -> [gmb_fate_ops:pop(desugar_arg(R))];
desugar({switch, Arg, Type, Alts, Def}) ->
[{switch, desugar_arg(Arg), Type, [desugar(A) || A <- Alts], desugar(Def)}];
desugar(missing) -> missing;
@ -1695,11 +1667,11 @@ desugar_arg(A) -> A.
%% Constructing basic blocks
to_basic_blocks(Funs) ->
to_basic_blocks(maps:to_list(Funs), aeb_fate_code:new()).
to_basic_blocks(maps:to_list(Funs), gmb_fate_code:new()).
to_basic_blocks([{Name, {Attrs, Sig, Code}}|Left], Acc) ->
BB = bb(Name, Code ++ [aeb_fate_ops:return()]),
to_basic_blocks(Left, aeb_fate_code:insert_fun(Name, Attrs, Sig, BB, Acc));
BB = bb(Name, Code ++ [gmb_fate_ops:return()]),
to_basic_blocks(Left, gmb_fate_code:insert_fun(Name, Attrs, Sig, BB, Acc));
to_basic_blocks([], Acc) ->
Acc.
@ -1770,7 +1742,7 @@ block(Blk = #blk{code = [{switch, Arg, Type, Alts, Default} | Code],
{DefRef, DefBlk} =
case Default of
missing when Catchall == none ->
FreshBlk([aeb_fate_ops:abort(?i(<<"Incomplete patterns">>))], none);
FreshBlk([gmb_fate_ops:abort(?i(<<"Incomplete patterns">>))], none);
missing -> {Catchall, []};
_ -> FreshBlk(Default ++ [{jump, RestRef}], Catchall)
%% ^ fall-through to the outer catchall
@ -1945,14 +1917,14 @@ split_calls(Ref, [I | Code], Acc, Blocks) ->
set_labels(Labels, {Ref, Code}) when is_reference(Ref) ->
{maps:get(Ref, Labels), [ set_labels(Labels, I) || I <- Code ]};
set_labels(_Labels, loop) -> aeb_fate_ops:jump(0);
set_labels(Labels, {jump, Ref}) -> aeb_fate_ops:jump(maps:get(Ref, Labels));
set_labels(Labels, {jumpif, Arg, Ref}) -> aeb_fate_ops:jumpif(Arg, maps:get(Ref, Labels));
set_labels(_Labels, loop) -> gmb_fate_ops:jump(0);
set_labels(Labels, {jump, Ref}) -> gmb_fate_ops:jump(maps:get(Ref, Labels));
set_labels(Labels, {jumpif, Arg, Ref}) -> gmb_fate_ops:jumpif(Arg, maps:get(Ref, Labels));
set_labels(Labels, {switch, Arg, Refs}) ->
case [ maps:get(Ref, Labels) || Ref <- Refs ] of
[R1, R2] -> aeb_fate_ops:switch(Arg, R1, R2);
[R1, R2, R3] -> aeb_fate_ops:switch(Arg, R1, R2, R3);
Rs -> aeb_fate_ops:switch(Arg, Rs)
[R1, R2] -> gmb_fate_ops:switch(Arg, R1, R2);
[R1, R2, R3] -> gmb_fate_ops:switch(Arg, R1, R2, R3);
Rs -> gmb_fate_ops:switch(Arg, Rs)
end;
set_labels(_, I) -> I.

View File

@ -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,
@ -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).

View File

@ -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,12 +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()}.
@ -36,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
@ -61,18 +62,18 @@ 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]));
@ -86,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 ----------------------------------------------------------
@ -411,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).
@ -528,7 +529,7 @@ parse_addr_literal(Id = {id, Ann, Name}) ->
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
@ -557,9 +558,9 @@ 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) ->
@ -647,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}) ->
@ -674,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 -------------------------------------------------------
@ -748,7 +749,7 @@ read_file(File, Opts) ->
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, escript, Src};
_ -> {error, not_found}
@ -761,12 +762,12 @@ read_file(File, Opts) ->
read_file_(Path, File) ->
AbsFile = filename:join(Path, File),
case file:read_file(AbsFile) of
{ok, Bin} -> {ok, aeso_utils:canonical_dir(filename:dirname(AbsFile)), Bin};
{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}]

View File

@ -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,26 @@ 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}) ->
@ -296,11 +298,11 @@ type(T = {tvar, _, _}) -> name(T);
type(var_args) -> text("var_args").
-spec args_type([aeso_syntax:type()]) -> doc().
-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) ->
@ -310,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}) ->
@ -318,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),
@ -361,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)
@ -372,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,
@ -389,7 +391,7 @@ expr_p(_, {Type, _, Bin})
Type == oracle_pubkey;
Type == oracle_query_id;
Type == signature ->
text(binary_to_list(aeser_api_encoder:encode(Type, Bin)));
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)]));
@ -402,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
@ -421,7 +423,7 @@ stmt_p({elif, _, Cond, Then}) ->
block_expr(200, beside(text("elif"), paren(expr(Cond))), Then);
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.
@ -433,7 +435,7 @@ 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
@ -460,7 +462,7 @@ 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('bnot') -> {850, 850}.
@ -468,7 +470,7 @@ 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,
@ -530,7 +532,7 @@ 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;

View File

@ -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() ->
@ -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 ----------------------------------------------------------------

View File

@ -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,

View File

@ -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").

View File

@ -1,12 +1,14 @@
%%% -*- 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]).

View File

@ -1,10 +1,12 @@
%%%-------------------------------------------------------------------
%%% @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_ids/2, used_types/2, used/1]).
@ -17,19 +19,19 @@
-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,
@ -132,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,
@ -147,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;

View File

@ -1,10 +1,12 @@
%%%-------------------------------------------------------------------
%%% @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, canonical_dir/1]).

View File

@ -1,22 +1,22 @@
%%%-------------------------------------------------------------------
%%% @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({id, _, "signature"}, ?FATE_BYTES(Bin)) -> {signature, [], Bin};
from_fate({id, _, "hash"}, ?FATE_BYTES(Bin)) -> {bytes, [], Bin};
from_fate({id, _, "unit"}, ?FATE_UNIT) -> {tuple, [], []};
from_fate({app_t, _, {id, _, "oracle"}, _}, ?FATE_ORACLE(Bin)) -> {oracle_pubkey, [], Bin};
from_fate({app_t, _, {id, _, "oracle_query"}, _}, ?FATE_ORACLE_Q(Bin)) -> {oracle_query_id, [], Bin};
from_fate({con, _, _Name}, ?FATE_CONTRACT(Bin)) -> {contract_pubkey, [], Bin};
from_fate({bytes_t, _, any}, ?FATE_BYTES(Bin)) -> make_any_bytes(Bin);
from_fate({bytes_t, _, N}, ?FATE_BYTES(Bin)) when byte_size(Bin) == N -> {bytes, [], Bin};

View File

@ -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])).

View File

@ -1,13 +1,13 @@
{application, aesophia,
[{description, "Compiler for Aeternity Sophia language"},
{vsn, "8.0.1"},
{application, sophia,
[{description, "Compiler for Sophia language"},
{vsn, "9.0.0"},
{registered, []},
{applications,
[kernel,
stdlib,
jsx,
syntax_tools,
aebytecode,
gmbytecode,
eblake2
]},
{env,[]},

View File

@ -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).

View File

@ -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)

View File

@ -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 =

View File

@ -14,12 +14,6 @@ main contract AENSTest =
stateful entrypoint resolve_contract(name : string, key : string) : option(C) =
AENSv2.resolve(name, key)
stateful entrypoint resolve_oracle(name : string, key : string) : option(oracle(int, int)) =
AENSv2.resolve(name, key)
stateful entrypoint resolve_oracle_query(name : string, key : string) : option(oracle_query(int, int)) =
AENSv2.resolve(name, key)
// Transactions
stateful entrypoint preclaim(addr : address, // Claim on behalf of this account (can be Contract.address)

View File

@ -14,7 +14,7 @@ main contract AENSUpdate =
let p5 : AENSv2.pointee = AENSv2.DataPt(String.to_bytes("any something will do"))
let p6 : AENSv2.pointee = AENSv2.DataPt(Int.to_bytes(1345, 4))
AENSv2.update(owner, name, None, None,
Some({ ["account_pubkey"] = p1, ["oracle_pubkey"] = p2,
Some({ ["account_pubkey"] = p1,
["contract_pubkey"] = p3, ["misc"] = p4, ["data"] = p5, ["data2"] = p6 }))
stateful entrypoint old_interaction(c : OldAENSContract, owner : address, name : string) =
@ -27,6 +27,3 @@ main contract AENSUpdate =
entrypoint get_ttl(name : string) =
switch(AENSv2.lookup(name))
Some(AENSv2.Name(_, FixedTTL(ttl), _)) => ttl
entrypoint expiry(o : oracle(int, int)) : int =
Oracle.expiry(o)

View File

@ -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 =

View File

@ -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))

View File

@ -31,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) =

View File

@ -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() = ()

View File

@ -1,5 +0,0 @@
contract HigherOrderResponseType =
stateful function foo(o, q : oracle_query(string, _)) =
Oracle.respond(o, q, (x) => x + 1)
entrypoint main_fun() = ()

View File

@ -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)

View File

@ -1,5 +0,0 @@
contract PolymorphicQueryType =
stateful function is_oracle(o) =
Oracle.check(o)
entrypoint main_fun() = ()

View File

@ -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)

View File

@ -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
()

View File

@ -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"

View File

@ -28,8 +28,6 @@ contract C =
let c16 = #000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
let c17 = #000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
let c18 = RelativeTTL(50)
let c19 = ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5
let c20 = oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY
let c21 = ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ : C
let c22 = N.nsconst
let c23 = c01
@ -54,8 +52,6 @@ contract C =
entrypoint f16() = c16
entrypoint f17() = c17
entrypoint f18() = c18
entrypoint f19() = c19
entrypoint f20() = c20
entrypoint f21() = c21
entrypoint f22() = c22
entrypoint f23() = c23

View File

@ -1,16 +1,12 @@
// Builtins without named arguments can appear unapplied.
// Named argument builtins are:
// Oracle.register
// Oracle.respond
// AENSv2.preclaim
// AENSv2.claim
// AENSv2.transfer
// AENSv2.revoke
// Oracle.extend
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,13 +17,6 @@ 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() = AENSv2.resolve : (_, _) => option(string)
function map_lookup() = Map.lookup : (_, m) => _
function map_lookup_default() = Map.lookup_default : (_, m, _) => _
@ -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

View File

@ -1,4 +0,0 @@
contract UnappliedNamedArgBuiltin =
stateful entrypoint main_fun(s) =
let reg = Oracle.register
reg(signature = s, Contract.address, 100, RelativeTTL(100)) : oracle(int, int)

View File

@ -1,4 +1,4 @@
-module(aeso_abi_tests).
-module(so_abi_tests).
-include_lib("eunit/include/eunit.hrl").
-compile([export_all, nowarn_export_all]).
@ -65,15 +65,15 @@ to_sophia_value_mcl_bls12_381_test() ->
Opts = [{backend, fate}],
CallValue32 = aeb_fate_encoding:serialize({bytes, <<20:256>>}),
CallValue48 = aeb_fate_encoding:serialize({bytes, <<55:384>>}),
CallValueTp = aeb_fate_encoding:serialize({tuple, {{bytes, <<15:256>>}, {bytes, <<160:256>>}, {bytes, <<1234:256>>}}}),
CallValue32 = gmb_fate_encoding:serialize({bytes, <<20:256>>}),
CallValue48 = gmb_fate_encoding:serialize({bytes, <<55:384>>}),
CallValueTp = gmb_fate_encoding:serialize({tuple, {{bytes, <<15:256>>}, {bytes, <<160:256>>}, {bytes, <<1234:256>>}}}),
{ok, _} = aeso_compiler:to_sophia_value(Code, "test_bls12_381_fp", ok, CallValue32, Opts),
{error, _} = aeso_compiler:to_sophia_value(Code, "test_bls12_381_fp", ok, CallValue48, Opts),
{ok, _} = aeso_compiler:to_sophia_value(Code, "test_bls12_381_fr", ok, CallValue48, Opts),
{error, _} = aeso_compiler:to_sophia_value(Code, "test_bls12_381_fr", ok, CallValue32, Opts),
{ok, _} = aeso_compiler:to_sophia_value(Code, "test_bls12_381_g1", ok, CallValueTp, Opts),
{ok, _} = so_compiler:to_sophia_value(Code, "test_bls12_381_fp", ok, CallValue32, Opts),
{error, _} = so_compiler:to_sophia_value(Code, "test_bls12_381_fp", ok, CallValue48, Opts),
{ok, _} = so_compiler:to_sophia_value(Code, "test_bls12_381_fr", ok, CallValue48, Opts),
{error, _} = so_compiler:to_sophia_value(Code, "test_bls12_381_fr", ok, CallValue32, Opts),
{ok, _} = so_compiler:to_sophia_value(Code, "test_bls12_381_g1", ok, CallValueTp, Opts),
ok.
@ -81,11 +81,11 @@ to_sophia_value_neg_test() ->
Code = [ "contract Foo =\n"
" entrypoint f(x : int) : string = \"hello\"\n" ],
{error, [Err1]} = aeso_compiler:to_sophia_value(Code, "f", ok, encode(12)),
?assertEqual("Data error:\nCannot translate FATE value 12\n of Sophia type string\n", aeso_errors:pp(Err1)),
{error, [Err1]} = so_compiler:to_sophia_value(Code, "f", ok, encode(12)),
?assertEqual("Data error:\nCannot translate FATE value 12\n of Sophia type string\n", so_errors:pp(Err1)),
{error, [Err2]} = aeso_compiler:to_sophia_value(Code, "f", revert, encode(12)),
?assertEqual("Data error:\nCould not deserialize the revert message\n", aeso_errors:pp(Err2)),
{error, [Err2]} = so_compiler:to_sophia_value(Code, "f", revert, encode(12)),
?assertEqual("Data error:\nCould not deserialize the revert message\n", so_errors:pp(Err2)),
ok.
encode_calldata_neg_test() ->
@ -97,8 +97,8 @@ encode_calldata_neg_test() ->
" `f : (int) => string`\n"
"to arguments\n"
" `true : bool`\n",
{error, [Err1]} = aeso_compiler:create_calldata(Code, "f", ["true"]),
?assertEqual(ExpErr1, aeso_errors:pp(Err1)),
{error, [Err1]} = so_compiler:create_calldata(Code, "f", ["true"]),
?assertEqual(ExpErr1, so_errors:pp(Err1)),
ok.
@ -108,15 +108,15 @@ decode_calldata_neg_test() ->
Code2 = [ "contract Foo =\n"
" entrypoint f(x : string) : int = 42\n" ],
{ok, CallDataFATE} = aeso_compiler:create_calldata(Code1, "f", ["42"]),
{ok, CallDataFATE} = so_compiler:create_calldata(Code1, "f", ["42"]),
{error, [Err1]} = aeso_compiler:decode_calldata(Code2, "f", <<1,2,3>>),
?assertEqual("Data error:\nFailed to decode calldata binary\n", aeso_errors:pp(Err1)),
{error, [Err2]} = aeso_compiler:decode_calldata(Code2, "f", CallDataFATE),
?assertEqual("Data error:\nCannot translate FATE value \"*\"\n to Sophia type (string)\n", aeso_errors:pp(Err2)),
{error, [Err1]} = so_compiler:decode_calldata(Code2, "f", <<1,2,3>>),
?assertEqual("Data error:\nFailed to decode calldata binary\n", so_errors:pp(Err1)),
{error, [Err2]} = so_compiler:decode_calldata(Code2, "f", CallDataFATE),
?assertEqual("Data error:\nCannot translate FATE value \"*\"\n to Sophia type (string)\n", so_errors:pp(Err2)),
{error, [Err3]} = aeso_compiler:decode_calldata(Code2, "x", CallDataFATE),
?assertEqual("Data error at line 1, col 1:\nFunction 'x' is missing in contract\n", aeso_errors:pp(Err3)),
{error, [Err3]} = so_compiler:decode_calldata(Code2, "x", CallDataFATE),
?assertEqual("Data error at line 1, col 1:\nFunction 'x' is missing in contract\n", so_errors:pp(Err3)),
ok.
@ -128,12 +128,12 @@ encode_decode_sophia_string(SophiaType, String) ->
, " record r = {x : an_alias(int), y : variant}\n"
, " datatype variant = Red | Blue(map(string, int))\n"
, " entrypoint foo : arg_type => arg_type\n" ],
case aeso_compiler:check_call(lists:flatten(Code), "foo", [String], [no_code]) of
case so_compiler:check_call(lists:flatten(Code), "foo", [String], [no_code]) of
{ok, _, [Arg]} ->
Data = encode(Arg),
case aeso_compiler:to_sophia_value(Code, "foo", ok, Data, [no_code]) of
case so_compiler:to_sophia_value(Code, "foo", ok, Data, [no_code]) of
{ok, Sophia} ->
lists:flatten(io_lib:format("~s", [prettypr:format(aeso_pretty:expr(Sophia))]));
lists:flatten(io_lib:format("~s", [prettypr:format(so_pretty:expr(Sophia))]));
{error, Err} ->
io:format("~s\n", [Err]),
{error, Err}
@ -194,40 +194,18 @@ parameterized_contract(ExtraCode, FunName, Types) ->
" datatype variant = Red | Blue(map(string, int))\n"
" entrypoint ", FunName, " : (", string:join(Types, ", "), ") => int\n" ]).
oracle_test() ->
Contract =
"contract OracleTest =\n"
" entrypoint question(o, q : oracle_query(list(string), option(int))) =\n"
" Oracle.get_question(o, q)\n",
?assertEqual({ok, "question", [{oracle, <<291:256>>}, {oracle_query, <<1110:256>>}]},
aeso_compiler:check_call(Contract, "question", ["ok_111111111111111111111111111111ZrdqRz9",
"oq_1111111111111111111111111111113AFEFpt5"], [no_code])),
ok.
permissive_literals_fail_test() ->
Contract =
"contract OracleTest =\n"
" stateful entrypoint haxx(o : oracle(list(string), option(int))) =\n"
" Chain.spend(o, 1000000)\n",
{error, [Err]} =
aeso_compiler:check_call(Contract, "haxx", ["#123"], []),
?assertMatch("Type error at line 3, col 5:\nCannot unify" ++ _, aeso_errors:pp(Err)),
?assertEqual(type_error, aeso_errors:type(Err)),
ok.
encode_decode_calldata(FunName, Types, Args) ->
Code = parameterized_contract(FunName, Types),
encode_decode_calldata_(Code, FunName, Args).
encode_decode_calldata_(Code, FunName, Args) ->
{ok, Calldata} = aeso_compiler:create_calldata(Code, FunName, Args, []),
{ok, _, _} = aeso_compiler:check_call(Code, FunName, Args, [no_code]),
{ok, Calldata} = so_compiler:create_calldata(Code, FunName, Args, []),
{ok, _, _} = so_compiler:check_call(Code, FunName, Args, [no_code]),
case FunName of
"init" ->
[];
_ ->
{ok, FateArgs} = aeb_fate_abi:decode_calldata(FunName, Calldata),
{ok, FateArgs} = gmb_fate_abi:decode_calldata(FunName, Calldata),
FateArgs
end.
@ -236,7 +214,7 @@ encode_decode(D) ->
D.
encode(D) ->
aeb_fate_encoding:serialize(D).
gmb_fate_encoding:serialize(D).
decode(B) ->
aeb_fate_encoding:deserialize(B).
gmb_fate_encoding:deserialize(B).

View File

@ -1,4 +1,4 @@
-module(aeso_aci_tests).
-module(so_aci_tests).
-include_lib("eunit/include/eunit.hrl").
@ -9,11 +9,11 @@ simple_aci_test_() ->
test_contract(N) ->
{Contract,MapACI,DecACI} = test_cases(N),
{ok,JSON} = aeso_aci:contract_interface(json, Contract),
{ok,JSON} = so_aci:contract_interface(json, Contract),
?assertEqual([MapACI], JSON),
?assertEqual({ok, DecACI}, aeso_aci:render_aci_json(JSON)),
?assertEqual({ok, DecACI}, so_aci:render_aci_json(JSON)),
%% Check if the compiler provides correct aci
{ok,#{aci := JSON2}} = aeso_compiler:from_string(Contract, [{aci, json}]),
{ok,#{aci := JSON2}} = so_compiler:from_string(Contract, [{aci, json}]),
?assertEqual(JSON, JSON2).
test_cases(1) ->
@ -95,39 +95,39 @@ aci_test_() ->
fun() -> aci_test_contract(ContractName) end}
|| ContractName <- all_contracts()].
all_contracts() -> aeso_compiler_tests:compilable_contracts().
all_contracts() -> so_compiler_tests:compilable_contracts().
aci_test_contract(Name) ->
String = aeso_test_utils:read_contract(Name),
Opts = case lists:member(Name, aeso_compiler_tests:debug_mode_contracts()) of
String = so_test_utils:read_contract(Name),
Opts = case lists:member(Name, so_compiler_tests:debug_mode_contracts()) of
true -> [debug_mode];
false -> []
end ++ [{include, {file_system, [aeso_test_utils:contract_path()]}}],
JSON = case aeso_aci:contract_interface(json, String, Opts) of
end ++ [{include, {file_system, [so_test_utils:contract_path()]}}],
JSON = case so_aci:contract_interface(json, String, Opts) of
{ok, J} -> J;
{error, ErrorStringJ} when is_binary(ErrorStringJ) -> error(ErrorStringJ);
{error, ErrorJ} -> aeso_compiler_tests:print_and_throw(ErrorJ)
{error, ErrorJ} -> so_compiler_tests:print_and_throw(ErrorJ)
end,
case aeso_compiler:from_string(String, [{aci, json} | Opts]) of
case so_compiler:from_string(String, [{aci, json} | Opts]) of
{ok, #{aci := JSON1}} ->
?assertEqual(JSON, JSON1),
io:format("JSON:\n~p\n", [JSON]),
{ok, ContractStub} = aeso_aci:render_aci_json(JSON),
{ok, ContractStub} = so_aci:render_aci_json(JSON),
io:format("STUB:\n~s\n", [ContractStub]),
check_stub(ContractStub, [{src_file, Name}]),
ok;
{error, ErrorString} when is_binary(ErrorString) -> error(ErrorString);
{error, Error} -> aeso_compiler_tests:print_and_throw(Error)
{error, Error} -> so_compiler_tests:print_and_throw(Error)
end.
check_stub(Stub, Options) ->
try aeso_parser:string(binary_to_list(Stub), Options) of
try so_parser:string(binary_to_list(Stub), Options) of
Ast ->
try
%% io:format("AST: ~120p\n", [Ast]),
aeso_ast_infer_types:infer(Ast, [no_code])
so_ast_infer_types:infer(Ast, [no_code])
catch throw:{type_errors, TE} ->
io:format("Type error:\n~s\n", [TE]),
error(TE);
@ -136,6 +136,6 @@ check_stub(Stub, Options) ->
error(R)
end
catch throw:{error, Errs} ->
_ = [ io:format("~s\n", [aeso_errors:pp(E)]) || E <- Errs ],
_ = [ io:format("~s\n", [so_errors:pp(E)]) || E <- Errs ],
error({parse_errors, Errs})
end.

View File

@ -6,7 +6,7 @@
%%% @end
%%%-------------------------------------------------------------------
-module(aeso_calldata_tests).
-module(so_calldata_tests).
-compile([export_all, nowarn_export_all]).
@ -18,7 +18,7 @@
calldata_test_() ->
[ {"Testing " ++ ContractName ++ " contract calling " ++ Fun,
fun() ->
ContractString = aeso_test_utils:read_contract(ContractName),
ContractString = so_test_utils:read_contract(ContractName),
FateExprs = ast_exprs(ContractString, Fun, Args),
ParsedExprs = parse_args(Fun, Args),
?assertEqual(ParsedExprs, FateExprs),
@ -28,8 +28,8 @@ calldata_test_() ->
calldata_aci_test_() ->
[ {"Testing " ++ ContractName ++ " contract calling " ++ Fun,
fun() ->
ContractString = aeso_test_utils:read_contract(ContractName),
{ok, ContractACIBin} = aeso_aci:contract_interface(string, ContractString, [no_code]),
ContractString = so_test_utils:read_contract(ContractName),
{ok, ContractACIBin} = so_aci:contract_interface(string, ContractString, [no_code]),
ContractACI = binary_to_list(ContractACIBin),
io:format("ACI:\n~s\n", [ContractACIBin]),
FateExprs = ast_exprs(ContractACI, Fun, Args),
@ -40,7 +40,7 @@ calldata_aci_test_() ->
parse_args(Fun, Args) ->
[{contract_main, _, _, _, [{letfun, _, _, _, _, [{guarded, _, [], {app, _, _, AST}}]}]}] =
aeso_parser:string("main contract Temp = function foo() = " ++ Fun ++ "(" ++ string:join(Args, ", ") ++ ")"),
so_parser:string("main contract Temp = function foo() = " ++ Fun ++ "(" ++ string:join(Args, ", ") ++ ")"),
strip_ann(AST).
strip_ann(T) when is_tuple(T) ->
@ -58,8 +58,8 @@ strip_ann1(X) -> X.
ast_exprs(ContractString, Fun, Args) ->
ast_exprs(ContractString, Fun, Args, []).
ast_exprs(ContractString, Fun, Args, Opts) ->
{ok, Data} = (catch aeso_compiler:create_calldata(ContractString, Fun, Args, Opts)),
{ok, _Types, Exprs} = (catch aeso_compiler:decode_calldata(ContractString, Fun, Data, Opts)),
{ok, Data} = (catch so_compiler:create_calldata(ContractString, Fun, Args, Opts)),
{ok, _Types, Exprs} = (catch so_compiler:decode_calldata(ContractString, Fun, Data, Opts)),
?assert(is_list(Exprs)),
strip_ann(Exprs).
@ -90,8 +90,6 @@ compilable_contracts() ->
{"funargs", "sextiosju", ["#01020304050607080910111213141516171819202122232425262728293031323334353637383940"
"414243444546474849505152535455565758596061626364656667"]},
{"funargs", "trettiotva", ["#0102030405060708091011121314151617181920212223242526272829303132"]},
{"funargs", "find_oracle", ["ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5"]},
{"funargs", "find_query", ["oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY"]},
{"funargs", "traffic_light", ["Green"]},
{"funargs", "traffic_light", ["Pantone(12)"]},
{"funargs", "tuples", ["()"]},

View File

@ -6,7 +6,7 @@
%%% @end
%%%-------------------------------------------------------------------
-module(aeso_compiler_tests).
-module(so_compiler_tests).
-compile([export_all, nowarn_export_all]).
@ -28,14 +28,14 @@ simple_compile_test_() ->
fun() ->
case compile(ContractName) of
#{fate_code := Code} ->
Code1 = aeb_fate_code:deserialize(aeb_fate_code:serialize(Code)),
Code1 = gmb_fate_code:deserialize(gmb_fate_code:serialize(Code)),
?assertMatch({X, X}, {Code1, Code});
Error -> io:format("\n\n~p\n\n", [Error]), print_and_throw(Error)
end
end} || ContractName <- compilable_contracts()] ++
[ {"Test file not found error",
fun() ->
{error, Errors} = aeso_compiler:file("does_not_exist.aes"),
{error, Errors} = so_compiler:file("does_not_exist.aes"),
ExpErr = <<"File error:\ndoes_not_exist.aes: no such file or directory">>,
check_errors([ExpErr], Errors)
end} ] ++
@ -49,7 +49,7 @@ simple_compile_test_() ->
fun() ->
FileSystem = maps:from_list(
[ begin
{ok, Bin} = file:read_file(filename:join([aeso_test_utils:contract_path(), File])),
{ok, Bin} = file:read_file(filename:join([so_test_utils:contract_path(), File])),
{File, Bin}
end || File <- ["included.aes", "../contracts/included2.aes"] ]),
#{byte_code := Code1} = compile("include", [{include, {explicit_files, FileSystem}}]),
@ -77,14 +77,14 @@ simple_compile_test_() ->
%% Check if all modules in the standard library compile
stdlib_test_() ->
{ok, Files} = file:list_dir(aeso_stdlib:stdlib_include_path()),
{ok, Files} = file:list_dir(so_stdlib:stdlib_include_path()),
[ { "Testing " ++ File ++ " from the stdlib",
fun() ->
String = "include \"" ++ File ++ "\"\nmain contract Test =\n entrypoint f(x) = x",
Options = [{src_file, File}],
case aeso_compiler:from_string(String, Options) of
case so_compiler:from_string(String, Options) of
{ok, #{fate_code := Code}} ->
Code1 = aeb_fate_code:deserialize(aeb_fate_code:serialize(Code)),
Code1 = gmb_fate_code:deserialize(gmb_fate_code:serialize(Code)),
?assertMatch({X, X}, {Code1, Code});
{error, Error} -> io:format("\n\n~p\n\n", [Error]), print_and_throw(Error)
end
@ -97,7 +97,7 @@ check_errors(Expect, #{}) ->
?assertEqual({error, Expect}, ok);
check_errors(Expect0, Actual0) ->
Expect = lists:sort(Expect0),
Actual = [ list_to_binary(string:trim(aeso_errors:pp(Err))) || Err <- Actual0 ],
Actual = [ list_to_binary(string:trim(so_errors:pp(Err))) || Err <- Actual0 ],
case {Expect -- Actual, Actual -- Expect} of
{[], Extra} -> ?assertMatch({unexpected, []}, {unexpected, Extra});
{Missing, []} -> ?assertMatch({missing, []}, {missing, Missing});
@ -106,7 +106,7 @@ check_errors(Expect0, Actual0) ->
check_warnings(Expect0, Actual0) ->
Expect = lists:sort(Expect0),
Actual = [ list_to_binary(string:trim(aeso_warnings:pp(Warn))) || Warn <- Actual0 ],
Actual = [ list_to_binary(string:trim(so_warnings:pp(Warn))) || Warn <- Actual0 ],
case {Expect -- Actual, Actual -- Expect} of
{[], Extra} -> ?assertMatch({unexpected, []}, {unexpected, Extra});
{Missing, []} -> ?assertMatch({missing, []}, {missing, Missing});
@ -114,19 +114,19 @@ check_warnings(Expect0, Actual0) ->
end.
compile(Name) ->
compile( Name, [{include, {file_system, [aeso_test_utils:contract_path()]}}]).
compile( Name, [{include, {file_system, [so_test_utils:contract_path()]}}]).
compile(Name, Options) ->
String = aeso_test_utils:read_contract(Name),
String = so_test_utils:read_contract(Name),
Options1 =
case lists:member(Name, debug_mode_contracts()) of
true -> [debug_mode];
false -> []
end ++
[ {src_file, Name ++ ".aes"}
, {include, {file_system, [aeso_test_utils:contract_path()]}}
, {include, {file_system, [so_test_utils:contract_path()]}}
] ++ Options,
case aeso_compiler:from_string(String, Options1) of
case so_compiler:from_string(String, Options1) of
{ok, Map} -> Map;
{error, ErrorString} when is_binary(ErrorString) -> ErrorString;
{error, Errors} -> Errors
@ -145,7 +145,6 @@ compilable_contracts() ->
"fundme",
"identity",
"maps",
"oracles",
"remote_call",
"remote_call_ambiguous_record",
"simple",
@ -223,7 +222,6 @@ compilable_contracts() ->
"polymorphic_entrypoint_return",
"polymorphic_map_keys",
"unapplied_contract_call",
"unapplied_named_arg_builtin",
"resolve_field_constraint_by_arity",
"toplevel_constants",
"ceres",
@ -299,7 +297,7 @@ warnings() ->
]).
failing_contracts() ->
{ok, V} = aeso_compiler:numeric_version(),
{ok, V} = so_compiler:numeric_version(),
Version = list_to_binary(string:join([integer_to_list(N) || N <- V], ".")),
%% Parse errors
[ ?PARSE_ERROR(field_parse_error,
@ -466,11 +464,7 @@ failing_contracts() ->
[<<?Pos(2, 12)
"Nested contracts are not allowed. Contract `Con` is not defined at top level.">>])
, ?TYPE_ERROR(bad_address_literals,
[<<?Pos(11, 5)
"Cannot unify `address` and `oracle(int, bool)`\n"
"when checking the type of the expression `ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt : address` "
"against the expected type `oracle(int, bool)`">>,
<<?Pos(9, 5)
[<<?Pos(9, 5)
"Cannot unify `address` and `Remote`\n"
"when checking the type of the expression `ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt : address` "
"against the expected type `Remote`">>,
@ -478,52 +472,17 @@ failing_contracts() ->
"Cannot unify `address` and `bytes(32)`\n"
"when checking the type of the expression `ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt : address` "
"against the expected type `bytes(32)`">>,
<<?Pos(14, 5)
"Cannot unify `oracle('a, 'b)` and `oracle_query(int, bool)`\n"
"when checking the type of the expression "
"`ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 : oracle('a, 'b)` "
"against the expected type `oracle_query(int, bool)`">>,
<<?Pos(16, 5)
"Cannot unify `oracle('c, 'd)` and `bytes(32)`\n"
"when checking the type of the expression "
"`ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 : oracle('c, 'd)` "
"against the expected type `bytes(32)`">>,
<<?Pos(18, 5)
"Cannot unify `oracle('e, 'f)` and `Remote`\n"
"when checking the type of the expression "
"`ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 : oracle('e, 'f)` "
"against the expected type `Remote`">>,
<<?Pos(21, 5)
"Cannot unify `oracle_query('g, 'h)` and `oracle(int, bool)`\n"
"when checking the type of the expression "
"`oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY : oracle_query('g, 'h)` "
"against the expected type `oracle(int, bool)`">>,
<<?Pos(23, 5)
"Cannot unify `oracle_query('i, 'j)` and `bytes(32)`\n"
"when checking the type of the expression "
"`oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY : oracle_query('i, 'j)` "
"against the expected type `bytes(32)`">>,
<<?Pos(25, 5)
"Cannot unify `oracle_query('k, 'l)` and `Remote`\n"
"when checking the type of the expression "
"`oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY : oracle_query('k, 'l)` "
"against the expected type `Remote`">>,
<<?Pos(28, 5)
<<?Pos(12, 5)
"The type `address` is not a contract type\n"
"when checking that the contract literal "
"`ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ` "
"has the type `address`">>,
<<?Pos(30, 5)
"The type `oracle(int, bool)` is not a contract type\n"
"when checking that the contract literal "
"`ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ` "
"has the type `oracle(int, bool)`">>,
<<?Pos(32, 5)
<<?Pos(14, 5)
"The type `bytes(32)` is not a contract type\n"
"when checking that the contract literal "
"`ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ` "
"has the type `bytes(32)`">>,
<<?Pos(34, 5),
<<?Pos(16, 5),
"The type `address` is not a contract type\n"
"when checking that the call to `Address.to_contract` "
"has the type `address`">>])
@ -1036,56 +995,6 @@ failing_contracts() ->
"Cannot unify `Animal` and `Cat` in a invariant context\n"
"when checking the type of the pattern `r11 : rec_inv(Cat)` against the expected type `Main.rec_inv(Animal)`">>
])
, ?TYPE_ERROR(polymorphism_variance_switching_oracles,
[<<?Pos(15,13)
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
"when checking the type of the pattern `o03 : oracle(Animal, Animal)` against the expected type `oracle(Cat, Animal)`">>,
<<?Pos(16,13)
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
"when checking the type of the pattern `o04 : oracle(Animal, Animal)` against the expected type `oracle(Cat, Cat)`">>,
<<?Pos(17,13)
"Cannot unify `Animal` and `Cat` in a covariant context\n"
"when checking the type of the pattern `o05 : oracle(Animal, Cat)` against the expected type `oracle(Animal, Animal)`">>,
<<?Pos(19,13)
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
"when checking the type of the pattern `o07 : oracle(Animal, Cat)` against the expected type `oracle(Cat, Animal)`">>,
<<?Pos(19,13)
"Cannot unify `Animal` and `Cat` in a covariant context\n"
"when checking the type of the pattern `o07 : oracle(Animal, Cat)` against the expected type `oracle(Cat, Animal)`">>,
<<?Pos(20,13)
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
"when checking the type of the pattern `o08 : oracle(Animal, Cat)` against the expected type `oracle(Cat, Cat)`">>,
<<?Pos(25,13)
"Cannot unify `Animal` and `Cat` in a covariant context\n"
"when checking the type of the pattern `o13 : oracle(Cat, Cat)` against the expected type `oracle(Animal, Animal)`">>,
<<?Pos(27,13)
"Cannot unify `Animal` and `Cat` in a covariant context\n"
"when checking the type of the pattern `o15 : oracle(Cat, Cat)` against the expected type `oracle(Cat, Animal)`">>,
<<?Pos(34,13)
"Cannot unify `Animal` and `Cat` in a covariant context\n"
"when checking the type of the pattern `q05 : oracle_query(Animal, Cat)` against the expected type `oracle_query(Animal, Animal)`">>,
<<?Pos(36,13)
"Cannot unify `Animal` and `Cat` in a covariant context\n"
"when checking the type of the pattern `q07 : oracle_query(Animal, Cat)` against the expected type `oracle_query(Cat, Animal)`">>,
<<?Pos(38,13)
"Cannot unify `Animal` and `Cat` in a covariant context\n"
"when checking the type of the pattern `q09 : oracle_query(Cat, Animal)` against the expected type `oracle_query(Animal, Animal)`">>,
<<?Pos(39,13)
"Cannot unify `Animal` and `Cat` in a covariant context\n"
"when checking the type of the pattern `q10 : oracle_query(Cat, Animal)` against the expected type `oracle_query(Animal, Cat)`">>,
<<?Pos(42,13)
"Cannot unify `Animal` and `Cat` in a covariant context\n"
"when checking the type of the pattern `q13 : oracle_query(Cat, Cat)` against the expected type `oracle_query(Animal, Animal)`">>,
<<?Pos(42,13)
"Cannot unify `Animal` and `Cat` in a covariant context\n"
"when checking the type of the pattern `q13 : oracle_query(Cat, Cat)` against the expected type `oracle_query(Animal, Animal)`">>,
<<?Pos(43,13)
"Cannot unify `Animal` and `Cat` in a covariant context\n"
"when checking the type of the pattern `q14 : oracle_query(Cat, Cat)` against the expected type `oracle_query(Animal, Cat)`">>,
<<?Pos(44,13)
"Cannot unify `Animal` and `Cat` in a covariant context\n"
"when checking the type of the pattern `q15 : oracle_query(Cat, Cat)` against the expected type `oracle_query(Cat, Animal)`">>
])
, ?TYPE_ERROR(polymorphism_variance_switching_chain_create_fail,
[<<?Pos(9,22)
"I is not implemented.\n"
@ -1156,34 +1065,6 @@ failing_contracts() ->
" `list(int)`\n"
"It must be a `string` or a pubkey type (`address`, `oracle`, etc)">>
])
, ?TYPE_ERROR(polymorphic_query_type,
[<<?Pos(3,5)
"Invalid oracle type\n"
" `oracle('a, 'b)`\n"
"The query type must not be polymorphic (contain type variables)">>,
<<?Pos(3,5)
"Invalid oracle type\n"
" `oracle('a, 'b)`\n"
"The response type must not be polymorphic (contain type variables)">>
])
, ?TYPE_ERROR(polymorphic_response_type,
[<<?Pos(3,5)
"Invalid oracle type\n"
" `oracle(string, 'r)`\n"
"The response type must not be polymorphic (contain type variables)">>
])
, ?TYPE_ERROR(higher_order_query_type,
[<<?Pos(3,5)
"Invalid oracle type\n"
" `oracle((int) => int, string)`\n"
"The query type must not be higher-order (contain function types)">>
])
, ?TYPE_ERROR(higher_order_response_type,
[<<?Pos(3,5)
"Invalid oracle type\n"
" `oracle(string, (int) => int)`\n"
"The response type must not be higher-order (contain function types)">>
])
, ?TYPE_ERROR(var_args_unify_let,
[<<?Pos(3,9)
"Cannot infer types for variable argument list.\n"
@ -1345,16 +1226,16 @@ validation_fails() ->
validate(Contract1, Contract2) ->
case compile(Contract1) of
ByteCode = #{ fate_code := FCode } ->
FCode1 = aeb_fate_code:serialize(aeb_fate_code:strip_init_function(FCode)),
Source = aeso_test_utils:read_contract(Contract2),
aeso_compiler:validate_byte_code(
FCode1 = gmb_fate_code:serialize(gmb_fate_code:strip_init_function(FCode)),
Source = so_test_utils:read_contract(Contract2),
so_compiler:validate_byte_code(
ByteCode#{ byte_code := FCode1 }, Source,
case lists:member(Contract2, debug_mode_contracts()) of
true -> [debug_mode];
false -> []
end ++
[ {src_file, lists:concat([Contract2, ".aes"])}
, {include, {file_system, [aeso_test_utils:contract_path()]}}
, {include, {file_system, [so_test_utils:contract_path()]}}
]);
Error -> print_and_throw(Error)
end.
@ -1365,6 +1246,6 @@ print_and_throw(Err) ->
io:format("\n~s", [ErrBin]),
error(ErrBin);
Errors ->
io:format("Compilation error:\n~s", [string:join([aeso_errors:pp(E) || E <- Errors], "\n\n")]),
io:format("Compilation error:\n~s", [string:join([so_errors:pp(E) || E <- Errors], "\n\n")]),
error(compilation_error)
end.

View File

@ -1,4 +1,4 @@
-module(aeso_encode_decode_tests).
-module(so_encode_decode_tests).
-compile([export_all, nowarn_export_all]).
@ -9,9 +9,9 @@
encode_decode_test_() ->
[ {lists:flatten(io_lib:format("Testing encode-decode roundtrip for ~p : ~p", [Value, {EType, DType}])),
fun() ->
{ok, SerRes} = aeso_compiler:encode_value(?EMPTY, EType, Value, []),
{ok, Expr} = aeso_compiler:decode_value(?EMPTY, DType, SerRes, []),
Value2 = prettypr:format(aeso_pretty:expr(Expr)),
{ok, SerRes} = so_compiler:encode_value(?EMPTY, EType, Value, []),
{ok, Expr} = so_compiler:decode_value(?EMPTY, DType, SerRes, []),
Value2 = prettypr:format(so_pretty:expr(Expr)),
?assertEqual(Value, Value2)
end} || {Value, EType, DType} <- test_data() ].

22
test/so_eunit_SUITE.erl Normal file
View File

@ -0,0 +1,22 @@
-module(so_eunit_SUITE).
-compile([export_all, nowarn_export_all]).
-include_lib("common_test/include/ct.hrl").
all() ->
[{group, eunit}].
groups() ->
[{eunit, [], [ so_scan_tests
, so_parser_tests
, so_compiler_tests
, so_abi_tests
, so_aci_tests
]}].
so_scan_tests(_Config) -> ok = eunit:test(so_scan_tests).
so_parser_tests(_Config) -> ok = eunit:test(so_parser_tests).
so_compiler_tests(_Config) -> ok = eunit:test(so_compiler_tests).
so_abi_tests(_Config) -> ok = eunit:test(so_abi_tests).
so_aci_tests(_Config) -> ok = eunit:test(so_aci_tests).

View File

@ -1,4 +1,4 @@
-module(aeso_parser_tests).
-module(so_parser_tests).
-export([parse_contract/1]).
@ -68,15 +68,15 @@ simple_contracts_test_() ->
}.
parse_contract(Name) ->
parse_string(aeso_test_utils:read_contract(Name)).
parse_string(so_test_utils:read_contract(Name)).
roundtrip_contract(Name) ->
round_trip(aeso_test_utils:read_contract(Name)).
round_trip(so_test_utils:read_contract(Name)).
parse_string(Text) -> parse_string(Text, []).
parse_string(Text, Opts) ->
aeso_parser:string(Text, Opts).
so_parser:string(Text, Opts).
parse_expr(Text) ->
[{letval, _, _, Expr}] =
@ -85,8 +85,8 @@ parse_expr(Text) ->
round_trip(Text) ->
Contract = parse_string(Text),
Text1 = prettypr:format(aeso_pretty:decls(strip_stdlib(Contract))),
Contract1 = parse_string(aeso_scan:utf8_encode(Text1)),
Text1 = prettypr:format(so_pretty:decls(strip_stdlib(Contract))),
Contract1 = parse_string(so_scan:utf8_encode(Text1)),
NoSrcLoc = remove_line_numbers(Contract),
NoSrcLoc1 = remove_line_numbers(Contract1),
?assertMatch(NoSrcLoc, diff(NoSrcLoc, NoSrcLoc1)).

View File

@ -1,4 +1,4 @@
-module(aeso_scan_tests).
-module(so_scan_tests).
-include_lib("eunit/include/eunit.hrl").
@ -13,7 +13,7 @@ empty_contract_test_() ->
[{"Scan an empty contract.",
fun() ->
Text = " ",
{ok, []} = aeso_scan:scan(Text),
{ok, []} = so_scan:scan(Text),
ok
end}
]}.
@ -26,7 +26,7 @@ all_tokens_test_() ->
Tokens = all_tokens(),
Text = string:join(lists:map(fun show_token/1, Tokens), " "),
io:format("~s\n", [Text]),
{ok, Tokens1} = aeso_scan:scan(Text),
{ok, Tokens1} = so_scan:scan(Text),
true = compare_tokens(Tokens, Tokens1),
ok
end}

View File

@ -6,12 +6,12 @@
%%% @end
%%%-------------------------------------------------------------------
-module(aeso_test_utils).
-module(so_test_utils).
-export([read_contract/1, contract_path/0]).
contract_path() ->
filename:join(code:lib_dir(aesophia, test), "contracts").
filename:join(code:lib_dir(sophia, test), "contracts").
%% Read a contract file from the test/contracts directory.
-spec read_contract(string() | atom()) -> string().

20
zomp.meta Normal file
View File

@ -0,0 +1,20 @@
{name,"Sophia Compiler"}.
{type,lib}.
{modules,[]}.
{prefix,none}.
{desc,"The Sophia smart contract language for the FATE VM"}.
{author,"QPQ AG"}.
{package_id,{"otpr","sophia",{9,0,0}}}.
{deps,[{"otpr","gmbytecode",{3,4,1}},
{"otpr","getopt",{1,0,2}},
{"otpr","eblake2",{1,0,0}},
{"otpr","zj",{1,1,0}}]}.
{key_name,none}.
{a_email,[]}.
{c_email,[]}.
{copyright,"(c) 2024 QPQ AG"}.
{file_exts,[]}.
{license,skip}.
{repo_url,"https://git.qpq.swiss/QPQ-AG/sophia"}.
{tags,["gaju","gajumaru","blockchain","sophia","crypto","compiler","puck"]}.
{ws_url,[]}.

19
zomp_prep Executable file
View File

@ -0,0 +1,19 @@
#! /bin/bash
# This is a small pre-packaging source generation and include correction script that should be
# run before packaging this project for use with ZX/Zomp.
rm -rf _build
cd src
for f in $(ls *.erl)
do
echo "Updating includes in: $f"
sed -i 's/gmbytecode\/include\///g' "$f"
sed -i 's/\.\.\/include\///g' "$f"
sed -i 's/include_lib/include/g' "$f"
done
sed -i 's/gmb_opcodes\.hrl/\$gmbytecode_include\/gmb_opcodes\.hrl/' so_compiler.erl
sed -i 's/gmb_fate_data\.hrl/\$gmbytecode_include\/gmb_fate_data\.hrl/' so_vm_decode.erl
cd ..
rm -f ebin/*.beam
rm -f rebar*