Compare commits

..

6 Commits

Author SHA1 Message Date
Hans Svensson fd6cde535e Merge pull request #296 from davidyuk/patch-3
docs: Use consistent event definitions between examples
2021-06-24 09:30:08 +02:00
Hans Svensson b25339bb8f Merge pull request #303 from aeternity/radrow-patch-1
Mention `init` quirk with Call.value
2021-04-29 18:51:13 +02:00
Hans Svensson cf793667ca Merge pull request #304 from aeternity/clarify-aesocompiler
Clarify aeso_compiler use
2021-04-29 18:50:32 +02:00
Artur Puzio c54a0cec3d Clarify aeso_compiler use 2021-03-24 10:34:39 +00:00
Radosław Rowicki bc47c25138 Mention init quirk with Call.value 2021-03-22 10:26:34 +01:00
Denis Davidyuk 4630f8a09b Use consistent event definitions between examples 2021-02-16 14:52:37 +03:00
117 changed files with 2123 additions and 5173 deletions
-16
View File
@@ -8,15 +8,6 @@ executors:
working_directory: ~/aesophia working_directory: ~/aesophia
jobs: jobs:
verify_rebar_lock:
executor: aebuilder
steps:
- checkout
- run:
name: Ensure lock file is up-to-date
command: |
./rebar3 upgrade
git diff --quiet -- rebar.lock || (echo "rebar.lock is not up-to-date" && exit 1)
build: build:
executor: aebuilder executor: aebuilder
steps: steps:
@@ -44,10 +35,3 @@ jobs:
- _build/default/rebar3_20.3.8_plt - _build/default/rebar3_20.3.8_plt
- store_artifacts: - store_artifacts:
path: _build/test/logs path: _build/test/logs
workflows:
version: 2
build_test:
jobs:
- build
- verify_rebar_lock
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

-7
View File
@@ -1,7 +0,0 @@
import glob
import shutil
def pre_build(**kwargs):
for file in glob.glob('../docs/*.md'):
shutil.copy(file, 'docs')
shutil.copy('../CHANGELOG.md', 'docs')
-55
View File
@@ -1,55 +0,0 @@
site_name: æternity Sophia Language
plugins:
- search
- mkdocs-simple-hooks:
hooks:
on_pre_build: 'hook:pre_build'
repo_url: 'https://github.com/aeternity/aesophia'
edit_uri: ''
extra:
version:
provider: mike
theme:
favicon: favicon.png
name: material
custom_dir: overrides
language: en
palette:
- scheme: default
primary: pink
accent: pink
toggle:
icon: material/weather-night
name: Switch to dark mode
- scheme: slate
primary: pink
accent: pink
toggle:
icon: material/weather-sunny
name: Switch to light mode
features:
- content.tabs.link
- search.highlight
- search.share
- search.suggest
# Don't include MkDocs' JavaScript
include_search_page: false
search_index_only: true
markdown_extensions:
- admonition
- pymdownx.highlight
- pymdownx.superfences
- toc:
toc_depth: 3
nav:
- Introduction: index.md
- Syntax: sophia_syntax.md
- Features: sophia_features.md
- Standard library: sophia_stdlib.md
- Contract examples: sophia_examples.md
- Changelog: CHANGELOG.md
-8
View File
@@ -1,8 +0,0 @@
{% extends "base.html" %}
{% block outdated %}
You're not viewing the latest version.
<a href="{{ '../' ~ base_url }}">
<strong>Click here to go to latest.</strong>
</a>
{% endblock %}
-25
View File
@@ -1,25 +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
- uses: actions/cache@v2
with:
path: ~/.cache/pip3
key: ${{ runner.os }}-pip-${{ hashFiles('.github/workflows/requirements.txt') }}
- run: pip3 install -r .github/workflows/requirements.txt
- 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
-26
View File
@@ -1,26 +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
- uses: actions/cache@v2
with:
path: ~/.cache/pip3
key: ${{ runner.os }}-pip-${{ hashFiles('.github/workflows/requirements.txt') }}
- run: pip3 install -r .github/workflows/requirements.txt
- 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
-4
View File
@@ -1,4 +0,0 @@
mkdocs==1.2.3
mkdocs-simple-hooks==0.1.3
mkdocs-material==7.1.9
mike==1.0.1
-3
View File
@@ -21,6 +21,3 @@ rebar3.crashdump
aesophia aesophia
.qcci .qcci
current_counterexample.eqc current_counterexample.eqc
test/contracts/test.aes
__pycache__
.docssite/docs/*.md
+1 -127
View File
@@ -9,127 +9,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
### Removed ### Removed
## [6.1.0] - 2021-10-20
### Added
- `Bitwise` stdlib
- `Set` stdlib
- `Option.force_msg`
- Loading namespaces into the current scope (e.g. `using Pair`)
- Assign patterns to variables (e.g. `let x::(t = y::_) = [1, 2, 3, 4]` where `t == [2, 3, 4]`)
- Add builtin types (`AENS.name, AENS.pointee, Chain.ttl, Chain.base_tx, Chain.ga_meta_tx, Chain.paying_for_tx`) to
the calldata and result decoder
- Patterns guards
```
switch(x)
a::[] | a > 10 => 1
_ => 2
```
```
function
f(a::[]) | a > 10 = 1
f(_) = 2
```
### Changed
- Fixed the ACI renderer, it shouldn't drop the `stateful` modifier
## [6.0.2] 2021-07-05
### Changed
- `List.from_to_step` now forbids non-positive step (this change does
*not* alter the behavior of the previously deployed contracts)
- Fixed leaking state between contracts
## [6.0.1] 2021-06-24
### Changed
- Fixed a bug in calldata encoding for contracts containing multiple contracts
- Fixed a missing `include` in the `Frac` standard library
## [6.0.0] 2021-05-26
### Added
- Child contracts
- `Chain.clone`
- `Chain.create`
- `Chain.bytecode_hash`
- Minor support for variadic functions
- `void` type that represents an empty type
- `Call.fee` builtin
### Changed
- Contract interfaces must be now invocated by `contract interface` keywords
- `main` keyword to indicate the main contract in case there are child contracts around
- `List.sum` and `List.product` no longer use `List.foldl`
### Removed
## [5.0.0] 2021-04-30
### Added
- A new and improved [`String` standard library](https://github.com/aeternity/aesophia/blob/master/docs/sophia_stdlib.md#string)
has been added. Use it by `include "String.aes"`. It includes functions for
turning strings into lists of characters for detailed manipulation. For
example:
```
include "String.aes"
contract C =
entrypoint filter_all_a(s: string) : string =
String.from_list(List.filter((c : char) => c != 'a', String.to_list(s)))
```
will return a list with all `a`'s removed.
There are also convenience functions `split`, `concat`, `to_upper`,
`to_lower`, etc.
All String functions in FATEv2 operate on unicode code points.
- Operations for pairing-based cryptography has been added the operations
are in the standard library [BLS12_381](https://github.com/aeternity/aesophia/blob/master/docs/sophia_stdlib.md#bls12_381).
With these operations it is possible to do Zero Knowledge-proofs, etc.
The operations are for the BLS12-381 curve (as the name suggests).
- Calls to functions in other contracts (i.e. _remote calls_) can now be
[`protected`](https://github.com/aeternity/aesophia/blob/master/docs/sophia.md#protected-contract-calls).
If a contract call fails for any reason (for instance, the remote contract
crashes or runs out of gas, or the entrypoint doesn't exist or has the
wrong type) the parent call also fails. To make it possible to recover
from failures, contract calls takes a named argument `protected : bool`
(default `false`).
If `protected = true` the result of the contract call is wrapped in an
`option`, and `Some(value)` indicates a succesful execution and `None`
indicates that the contract call failed. Note: any gas consumed until
the failure is still charged, but all side effects in the remote
contract are rolled back on failure.
- A new chain operation [`AENS.update`](https://github.com/aeternity/aesophia/blob/master/docs/sophia.md#aens-interface)
is supported.
- New chain exploring operations `AENS.lookup` and `Oracle.expiry` to
look up an AENS record and the expiry of an Oracle respectively, are added.
- Transaction introspection (`Auth.tx`) has been added. When a Generalized
account is authorized, the authorization function needs access to the
transaction (and the transaction hash) for the wrapped transaction. The
transaction and the transaction hash is available `Auth.tx`, it is only
available during authentication if invoked by a normal contract call
it returns `None`. Example:
```
switch(Auth.tx)
None => abort("Not in Auth context")
Some(tx0) =>
switch(tx0.tx)
Chain.SpendTx(_, amount, _) => amount > 400
Chain.ContractCallTx(_, _) => true
_ => false
```
- A debug mode is a added to the compiler. Right now its only use is to
turn off hermetization.
### Changed
- The function `Chain.block_hash(height)` is now (in FATEv2) defined for
the current height - this used to be an error.
- Standard library: Sort is optimized to do `mergesort` and a `contains`
function is added.
- Improved type errors and explicit errors for some syntax errors (empty code
blocks, etc.).
- Compiler optimization: The ACI is generated alongside bytecode. This means
that multiple compiler passes can be avoided.
- Compiler optimization: Improved parsing (less stack used when transpiled).
- A bug where constraints were handled out of order fixed.
- Fixed calldata decoding for singleton records.
- Improved the documentation w.r.t. signatures, especially stressing the fact that
the network ID is a part of what is signed.
### Removed
## [4.3.0] ## [4.3.0]
### Added ### Added
- Added documentation (moved from `protocol`) - Added documentation (moved from `protocol`)
@@ -332,12 +211,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Simplify calldata creation - instead of passing a compiled contract, simply - Simplify calldata creation - instead of passing a compiled contract, simply
pass a (stubbed) contract string. pass a (stubbed) contract string.
[Unreleased]: https://github.com/aeternity/aesophia/compare/v6.1.0...HEAD [Unreleased]: https://github.com/aeternity/aesophia/compare/v4.3.0...HEAD
[6.1.0]: https://github.com/aeternity/aesophia/compare/v6.0.2...v6.1.0
[6.0.2]: https://github.com/aeternity/aesophia/compare/v6.0.1...v6.0.2
[6.0.1]: https://github.com/aeternity/aesophia/compare/v6.0.0...v6.0.1
[6.0.0]: https://github.com/aeternity/aesophia/compare/v5.0.0...v6.0.0
[5.0.0]: https://github.com/aeternity/aesophia/compare/v4.3.0...v5.0.0
[4.3.0]: https://github.com/aeternity/aesophia/compare/v4.2.0...v4.3.0 [4.3.0]: https://github.com/aeternity/aesophia/compare/v4.2.0...v4.3.0
[4.2.0]: https://github.com/aeternity/aesophia/compare/v4.1.0...v4.2.0 [4.2.0]: https://github.com/aeternity/aesophia/compare/v4.1.0...v4.2.0
[4.1.0]: https://github.com/aeternity/aesophia/compare/v4.0.0...v4.1.0 [4.1.0]: https://github.com/aeternity/aesophia/compare/v4.0.0...v4.1.0
+1 -1
View File
@@ -1,6 +1,6 @@
ISC License ISC License
Copyright (c) 2017, æternity developers Copyright (c) 2017, aeternity developers
Permission to use, copy, modify, and/or distribute this software for any Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above purpose with or without fee is hereby granted, provided that the above
+11 -16
View File
@@ -5,30 +5,25 @@ This is the __sophia__ compiler for the æternity system which compiles contract
The compiler is currently being used three places The compiler is currently being used three places
- [The command line compiler](https://github.com/aeternity/aesophia_cli) - [The command line compiler](https://github.com/aeternity/aesophia_cli)
- [The HTTP compiler](https://github.com/aeternity/aesophia_http) - [The HTTP compiler](https://github.com/aeternity/aesophia_http)
- In [æternity node](https://github.com/aeternity/aeternity) tests - In [Aeternity node](https://github.com/aeternity/aeternity) tests
## Documentation ## Documentation
* [Introduction](docs/index.md) * [Smart Contracts on aeternity Blockchain](https://github.com/aeternity/protocol/blob/master/contracts/contracts.md).
* [Syntax](docs/sophia_syntax.md) * [Sophia Documentation](docs/sophia.md).
* [Features](docs/sophia_features.md) * [Sophia Standard Library](docs/sophia_stdlib.md).
* [Standard library](docs/sophia_stdlib.md)
* [Contract examples](docs/sophia_examples.md)
Additionally you can check out the [contracts section](https://github.com/aeternity/protocol/blob/master/contracts/contracts.md) of the æternity blockchain specification.
## Versioning ## Versioning
Versioning should follow the [semantic versioning](https://semver.org/spec/v2.0.0) guidelines. Id est, given a version number MAJOR.MINOR.PATCH, increment the: `aesophia` has a version that is only loosely connected to the version of the
Aeternity node - in principle they will share the major version but not
- MAJOR version when you make incompatible API changes minor/patch version. The `aesophia` compiler version MUST be bumped whenever
- MINOR version when you add functionality in a backwards compatible manner there is a change in how byte code is generated, but it MAY also be bumped upon
- PATCH version when you make backwards compatible bug fixes API changes etc.
## Interface Modules ## Interface Modules
The basic modules for interfacing the compiler: The basic modules for interfacing the compiler:
* [aeso_compiler: the Sophia compiler](docs/aeso_compiler.md) * [aeso_compiler: the Sophia compiler](./docs/aeso_compiler.md)
* [aeso_aci: the ACI interface](docs/aeso_aci.md) * [aeso_aci: the ACI interface](./docs/aeso_aci.md)
-12
View File
@@ -1,12 +0,0 @@
# Introduction
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.
!!! 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/)!
+1071 -1
View File
File diff suppressed because it is too large Load Diff
-73
View File
@@ -1,73 +0,0 @@
# Contract examples
## Crowdfunding
```sophia
/*
* A simple crowd-funding example
*/
contract FundMe =
record spend_args = { recipient : address,
amount : int }
record state = { contributions : map(address, int),
total : int,
beneficiary : address,
deadline : int,
goal : int }
stateful function spend(args : spend_args) =
Chain.spend(args.recipient, args.amount)
entrypoint init(beneficiary, deadline, goal) : state =
{ contributions = {},
beneficiary = beneficiary,
deadline = deadline,
total = 0,
goal = goal }
function is_contributor(addr) =
Map.member(addr, state.contributions)
stateful entrypoint contribute() =
if(Chain.block_height >= state.deadline)
spend({ recipient = Call.caller, amount = Call.value }) // Refund money
false
else
let amount =
switch(Map.lookup(Call.caller, state.contributions))
None => Call.value
Some(n) => n + Call.value
put(state{ contributions[Call.caller] = amount,
total @ tot = tot + Call.value })
true
stateful entrypoint withdraw() =
if(Chain.block_height < state.deadline)
abort("Cannot withdraw before deadline")
if(Call.caller == state.beneficiary)
withdraw_beneficiary()
elif(is_contributor(Call.caller))
withdraw_contributor()
else
abort("Not a contributor or beneficiary")
stateful function withdraw_beneficiary() =
require(state.total >= state.goal, "Project was not funded")
spend({recipient = state.beneficiary,
amount = Contract.balance })
stateful function withdraw_contributor() =
if(state.total >= state.goal)
abort("Project was funded")
let to = Call.caller
spend({recipient = to,
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.
-877
View File
@@ -1,877 +0,0 @@
# Features
## Contracts
The main unit of code in Sophia is the *contract*.
- A contract implementation, or simply a contract, is the code for a
smart contract and consists of a list of types, entrypoints and local
functions. Only the entrypoints can be called from outside the contract.
- A contract instance is an entity living on the block chain (or in a state
channel). Each instance has an address that can be used to call its
entrypoints, either from another contract or in a call transaction.
- A contract may define a type `state` encapsulating its local
state. When creating a new contract the `init` entrypoint is executed and the
state is initialized to its return value.
The language offers some primitive functions to interact with the blockchain and contracts.
Please refer to the [Chain](sophia_stdlib.md#chain), [Contract](sophia_stdlib.md#contract)
and the [Call](sophia_stdlib.md#call) namespaces in the documentation.
### Calling other contracts
To call a function in another contract you need the address to an instance of
the contract. The type of the address must be a contract type, which consists
of a number of type definitions and entrypoint declarations. For instance,
```sophia
// A contract type
contract interface VotingType =
entrypoint vote : string => unit
```
Now given contract address of type `VotingType` you can call the `vote`
entrypoint of that contract:
```sophia
contract VoteTwice =
entrypoint voteTwice(v : VotingType, alt : string) =
v.vote(alt)
v.vote(alt)
```
Contract calls take two optional named arguments `gas : int` and `value : int`
that lets you set a gas limit and provide tokens to a contract call. If omitted
the defaults are no gas limit and no tokens. Suppose there is a fee for voting:
```sophia
entrypoint voteTwice(v : VotingType, fee : int, alt : string) =
v.vote(value = fee, alt)
v.vote(value = fee, alt)
```
Named arguments can be given in any order.
Note that reentrant calls are not permitted. In other words, when calling
another contract it cannot call you back (directly or indirectly).
To construct a value of a contract type you can give a contract address literal
(for instance `ct_2gPXZnZdKU716QBUFKaT4VdBZituK93KLvHJB3n4EnbrHHw4Ay`), or
convert an account address to a contract address using `Address.to_contract`.
Note that if the contract does not exist, or it doesn't have the entrypoint, or
the type of the entrypoint does not match the stated contract type, the call
fails.
To recover the underlying `address` of a contract instance there is a field
`address : address`. For instance, to send tokens to the voting contract (given that it is payable)
without calling it you can write
```sophia
entrypoint pay(v : VotingType, amount : int) =
Chain.spend(v.address, amount)
```
### Protected contract calls
If a contract call fails for any reason (for instance, the remote contract
crashes or runs out of gas, or the entrypoint doesn't exist or has the wrong
type) the parent call also fails. To make it possible to recover from failures,
contract calls takes a named argument `protected : bool` (default `false`).
The protected argument must be a literal boolean, and when set to `true`
changes the type of the contract call, wrapping the result in an `option` type.
If the call fails the result is `None`, otherwise it's `Some(r)` where `r` is
the return value of the call.
```sophia
contract interface VotingType =
entrypoint : vote : string => unit
contract Voter =
entrypoint tryVote(v : VotingType, alt : string) =
switch(v.vote(alt, protected = true) : option(unit))
None => "Voting failed"
Some(_) => "Voting successful"
```
Any gas that was consumed by the contract call before the failure stays
consumed, which means that in order to protect against the remote contract
running out of gas it is necessary to set a gas limit using the `gas` argument.
However, note that errors that would normally consume all the gas in the
transaction still only uses up the gas spent running the contract.
### Contract factories and child contracts
Since the version 6.0.0 Sophia supports deploying contracts by other
contracts. This can be done in two ways:
- Contract cloning via [`Chain.clone`](sophia_stdlib.md#clone)
- Direct deploy via [`Chain.create`](sophia_stdlib.md#create)
These functions take variable number of arguments that must match the created
contract's `init` function. Beside that they take some additional named
arguments please refer to their documentation for the details.
While `Chain.clone` requires only a `contract interface` and a living instance
of a given contract on the chain, `Chain.create` needs a full definition of a
to-create contract defined by the standard `contract` syntax, for example
```sophia
contract IntHolder =
type state = int
entrypoint init(x) = x
entrypoint get() = state
main contract IntHolderFactory =
stateful entrypoint new(x : int) : IntHolder =
let ih = Chain.create(x) : IntHolder
ih
```
In case of a presence of child contracts (`IntHolder` in this case), the main
contract must be pointed out with the `main` keyword as shown in the example.
## Mutable state
Sophia does not have arbitrary mutable state, but only a limited form of state
associated with each contract instance.
- Each contract defines a type `state` encapsulating its mutable state.
The type `state` defaults to the `unit`.
- The initial state of a contract is computed by the contract's `init`
function. The `init` function is *pure* and returns the initial state as its
return value.
If the type `state` is `unit`, the `init` function defaults to returning the value `()`.
At contract creation time, the `init` function is executed and
its result is stored as the contract state.
- The value of the state is accessible from inside the contract
through an implicitly bound variable `state`.
- State updates are performed by calling a function `put : state => unit`.
- Aside from the `put` function (and similar functions for transactions
and events), the language is purely functional.
- Functions modifying the state need to be annotated with the `stateful` keyword (see below).
To make it convenient to update parts of a deeply nested state Sophia
provides special syntax for map/record updates.
### Stateful functions
Top-level functions and entrypoints must be annotated with the
`stateful` keyword to be allowed to affect the state of the running contract.
For instance,
```sophia
stateful entrypoint set_state(s : state) =
put(s)
```
Without the `stateful` annotation the compiler does not allow the call to
`put`. A `stateful` annotation is required 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`
- `AENS.revoke`
- `AENS.update`
* Call a `stateful` function in the current contract
* Call another contract with a non-zero `value` argument.
A `stateful` annotation *is not* required to
* Read the contract state.
* Issue an event using the `event` function.
* Call another contract with `value = 0`, even if the called function is stateful.
## Payable
### Payable contracts
A concrete contract is by default *not* payable. Any attempt at spending to such
a contract (either a `Chain.spend` or a normal spend transaction) will fail. If a
contract shall be able to receive funds in this way it has to be declared `payable`:
```sophia
// A payable contract
payable contract ExampleContract =
stateful entrypoint do_stuff() = ...
```
If in doubt, it is possible to check if an address is payable using
`Address.is_payable(addr)`.
### Payable entrypoints
A contract entrypoint is by default *not* payable. Any call to such a function
(either a [Remote call](#calling-other-contracts) or a contract call transaction)
that has a non-zero `value` will fail. Contract entrypoints that should be called
with a non-zero value should be declared `payable`.
```sophia
payable stateful entrypoint buy(to : address) =
if(Call.value > 42)
transfer_item(to)
else
abort("Value too low")
```
Note: In the æternity VM (AEVM) contracts and entrypoints were by default
payable until the Lima release.
## Namespaces
Code can be split into libraries using the `namespace` construct. Namespaces
can appear at the top-level and can contain type and function definitions, but
not entrypoints. Outside the namespace you can refer to the (non-private) names
by qualifying them with the namespace (`Namespace.name`).
For example,
```sophia
namespace Library =
type number = int
function inc(x : number) : number = x + 1
contract MyContract =
entrypoint plus2(x) : Library.number =
Library.inc(Library.inc(x))
```
Functions in namespaces have access to the same environment (including the
`Chain`, `Call`, and `Contract`, builtin namespaces) as function in a contract,
with the exception of `state`, `put` and `Chain.event` since these are
dependent on the specific state and event types of the contract.
To avoid mentioning the namespace every time it is used, Sophia allows
including the namespace in the current scope with the `using` keyword:
```
include "Pair.aes"
using Pair
contract C =
type state = int
entrypoint init() =
let p = (1, 2)
fst(p) // this is the same as Pair.fst(p)
```
It is also possible to make an alias for the namespace with the `as` keyword:
```
include "Pair.aes"
contract C =
using Pair as P
type state = int
entrypoint init() =
let p = (1, 2)
P.fst(p) // this is the same as Pair.fst(p)
```
Having the same alias for multiple namespaces is possible and it allows
referening functions that are defined in different namespaces and have
different names with the same alias:
```
namespace Xa = function f() = 1
namespace Xb = function g() = 2
contract Cntr =
using Xa as A
using Xb as A
type state = int
entrypoint init() = A.f() + A.g()
```
Note that using functions with the same name would result in an ambiguous name
error:
```
namespace Xa = function f() = 1
namespace Xb = function f() = 2
contract Cntr =
using Xa as A
using Xb as A
type state = int
// the next line has an error because f is defined in both Xa and Xb
entrypoint init() = A.f()
```
Importing specific parts of a namespace or hiding these parts can also be
done like this:
```
using Pair for [fst, snd] // this will only import fst and snd
using Triple hiding [fst, snd] // this will import everything except for fst and snd
```
Note that it is possible to use a namespace in the top level of the file, in the
contract level, namespace level, or in the function level.
## Splitting code over multiple files
Code from another file can be included in a contract using an `include`
statement. These must appear at the top-level (outside the main contract). The
included file can contain one or more namespaces and abstract contracts. For
example, if the file `library.aes` contains
```sophia
namespace Library =
function inc(x) = x + 1
```
you can use it from another file using an `include`:
```sophia
include "library.aes"
contract MyContract =
entrypoint plus2(x) = Library.inc(Library.inc(x))
```
This behaves as if the contents of `library.aes` was textually inserted into
the file, except that error messages will refer to the original source
locations. The language will try to include each file at most one time automatically,
so even cyclic includes should be working without any special tinkering.
## Standard library
Sophia offers [standard library](sophia_stdlib.md) which exposes some
primitive operations and some higher level utilities. The builtin
namespaces like `Chain`, `Contract`, `Map`
are included by default and are supported internally by the compiler.
Others like `List`, `Frac`, `Option` need to be manually included using the
`include` directive. For example
```sophia
include "List.aes"
include "Pair.aes"
-- Map is already there!
namespace C =
entrypoint keys(m : map('a, 'b)) : list('a) =
List.map(Pair.fst, (Map.to_list(m)))
```
## Types
Sophia has the following types:
| Type | Description | Example |
|----------------------|---------------------------------------------------------------------------------------------|--------------------------------------------------------------|
| int | A 2-complement integer | ```-1``` |
| address | æternity address, 32 bytes | ```Call.origin``` |
| bool | A Boolean | ```true``` |
| bits | A bit field | ```Bits.none``` |
| bytes(n) | A byte array with `n` bytes | ```#fedcba9876543210``` |
| string | An array of bytes | ```"Foo"``` |
| list | A homogeneous immutable singly linked list. | ```[1, 2, 3]``` |
| ('a, 'b) => 'c | A function. Parentheses can be skipped if there is only one argument | ```(x : int, y : int) => x + y``` |
| tuple | An ordered heterogeneous array | ```(42, "Foo", true)``` |
| record | An immutable key value store with fixed key names and typed values | ``` record balance = { owner: address, value: int } ``` |
| map | An immutable key value store with dynamic mapping of keys of one type to values of one type | ```type accounts = map(string, address)``` |
| option('a) | An optional value either None or Some('a) | ```Some(42)``` |
| state | A user defined type holding the contract state | ```record state = { owner: address, magic_key: bytes(4) }``` |
| event | An append only list of blockchain events (or log entries) | ```datatype event = EventX(indexed int, string)``` |
| hash | A 32-byte hash - equivalent to `bytes(32)` | |
| signature | A signature - equivalent to `bytes(64)` | |
| Chain.ttl | Time-to-live (fixed height or relative to current block) | ```FixedTTL(1050)``` ```RelativeTTL(50)``` |
| oracle('a, 'b) | And oracle answering questions of type 'a with answers of type 'b | ```Oracle.register(acct, qfee, ttl)``` |
| oracle_query('a, 'b) | A specific oracle query | ```Oracle.query(o, q, qfee, qttl, rttl)``` |
| contract | A user defined, typed, contract address | ```function call_remote(r : RemoteContract) = r.fun()``` |
## Literals
| Type | Constant/Literal example(s) |
| ---------- | ------------------------------- |
| int | `-1`, `2425`, `4598275923475723498573485768` |
| address | `ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt` |
| bool | `true`, `false` |
| bits | `Bits.none`, `Bits.all` |
| bytes(8) | `#fedcba9876543210` |
| string | `"This is a string"` |
| list | `[1, 2, 3]`, `[(true, 24), (false, 19), (false, -42)]` |
| tuple | `(42, "Foo", true)` |
| record | `{ owner = Call.origin, value = 100000000 }` |
| map | `{["foo"] = 19, ["bar"] = 42}`, `{}` |
| option(int) | `Some(42)`, `None` |
| state | `state{ owner = Call.origin, magic_key = #a298105f }` |
| event | `EventX(0, "Hello")` |
| hash | `#000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f` |
| signature | `#000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f` |
| Chain.ttl | `FixedTTL(1050)`, `RelativeTTL(50)` |
| oracle('a, 'b) | `ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5` |
| oracle_query('a, 'b) | `oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY` |
| contract | `ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ` |
## Arithmetic
Sophia integers (`int`) are represented by 256-bit (AEVM) or arbitrary-sized (FATE) signed words and supports the following
arithmetic operations:
- addition (`x + y`)
- subtraction (`x - y`)
- multiplication (`x * y`)
- division (`x / y`), truncated towards zero
- remainder (`x mod y`), satisfying `y * (x / y) + x mod y == x` for non-zero `y`
- exponentiation (`x ^ y`)
All operations are *safe* with respect to overflow and underflow. On AEVM they behave as the corresponding
operations on arbitrary-size integers and fail with `arithmetic_error` if the
result cannot be represented by a 256-bit signed word. For example, `2 ^ 255`
fails rather than wrapping around to -2²⁵⁵.
The division and modulo operations also throw an arithmetic error if the
second argument is zero.
## Bit fields
Sophia integers do not support bit arithmetic. Instead there is a separate
type `bits`. See the standard library [documentation](sophia_stdlib.md#bits).
On the AEVM a bit field is represented by a 256-bit word and reading or writing
a bit outside the 0..255 range fails with an `arithmetic_error`. On FATE a bit
field can be of arbitrary size (but it is still represented by the
corresponding integer, so setting very high bits can be expensive).
## Type aliases
Type aliases can be introduced with the `type` keyword and can be
parameterized. For instance
```sophia
type number = int
type string_map('a) = map(string, 'a)
```
A type alias and its definition can be used interchangeably. Sophia does not support
higher-kinded types, meaning that following type alias is invalid: `type wrap('f, 'a) = 'f('a)`
## Algebraic data types
Sophia supports algebraic data types (variant types) and pattern matching. Data
types are declared by giving a list of constructors with
their respective arguments. For instance,
```sophia
datatype one_or_both('a, 'b) = Left('a) | Right('b) | Both('a, 'b)
```
Elements of data types can be pattern matched against, using the `switch` construct:
```sophia
function get_left(x : one_or_both('a, 'b)) : option('a) =
switch(x)
Left(x) => Some(x)
Right(_) => None
Both(x, _) => Some(x)
```
or directly in the left-hand side:
```sophia
function
get_left : one_or_both('a, 'b) => option('a)
get_left(Left(x)) = Some(x)
get_left(Right(_)) = None
get_left(Both(x, _)) = Some(x)
```
*NOTE: Data types cannot currently be recursive.*
Sophia also supports the assignment of patterns to variables:
```sophia
function f(x) = switch(x)
h1::(t = h2::_) => (h1 + h2)::t // same as `h1::h2::k => (h1 + h2)::h2::k`
_ => x
function g(p : int * option(int)) : int =
let (a, (o = Some(b))) = p // o is equal to Pair.snd(p)
b
```
Guards are boolean expressions that can be used on patterns in both switch
statements and functions definitions. If a guard expression evaluates to
`true`, then the corresponding body will be used. Otherwise, the next pattern
will be checked:
```sophia
function get_left_if_positive(x : one_or_both(int, 'b)) : option(int) =
switch(x)
Left(x) | x > 0 => Some(x)
Both(x, _) | x > 0 => Some(x)
_ => None
```
```sophia
function
get_left_if_positive : one_or_both(int, 'b) => option(int)
get_left_if_positive(Left(x)) | x > 0 = Some(x)
get_left_if_positive(Both(x, _)) | x > 0 = Some(x)
get_left_if_positive(_) = None
```
Guards cannot be stateful even when used inside a stateful function.
## Lists
A Sophia list is a dynamically sized, homogenous, immutable, singly
linked list. A list is constructed with the syntax `[1, 2, 3]`. The
elements of a list can be any of datatype but they must have the same
type. The type of lists with elements of type `'e` is written
`list('e)`. For example we can have the following lists:
```sophia
[1, 33, 2, 666] : list(int)
[(1, "aaa"), (10, "jjj"), (666, "the beast")] : list(int * string)
[{[1] = "aaa", [10] = "jjj"}, {[5] = "eee", [666] = "the beast"}] : list(map(int, string))
```
New elements can be prepended to the front of a list with the `::`
operator. So `42 :: [1, 2, 3]` returns the list `[42, 1, 2, 3]`. The
concatenation operator `++` appends its second argument to its first
and returns the resulting list. So concatenating two lists
`[1, 22, 33] ++ [10, 18, 55]` returns the list `[1, 22, 33, 10, 18, 55]`.
Sophia supports list comprehensions known from languages like Python, Haskell or Erlang.
Example syntax:
```sophia
[x + y | x <- [1,2,3,4,5], let k = x*x, if (k > 5), y <- [k, k+1, k+2]]
// yields [12,13,14,20,21,22,30,31,32]
```
Lists can be constructed using the range syntax using special `..` operator:
```sophia
[1..4] == [1,2,3,4]
```
The ranges are always ascending and have step equal to 1.
Please refer to the [standard library](sophia_stdlib.md#list) for the predefined functionalities.
## Maps and records
A Sophia record type is given by a fixed set of fields with associated,
possibly different, types. For instance
```sophia
record account = { name : string,
balance : int,
history : list(transaction) }
```
Maps, on the other hand, can contain an arbitrary number of key-value bindings,
but of a fixed type. The type of maps with keys of type `'k` and values of type
`'v` is written `map('k, 'v)`. The key type can be any type that does not
contain a map or a function type.
Please refer to the [standard library](sophia_stdlib.md#map) for the predefined functionalities.
### Constructing maps and records
A value of record type is constructed by giving a value for each of the fields.
For the example above,
```sophia
function new_account(name) =
{name = name, balance = 0, history = []}
```
Maps are constructed similarly, with keys enclosed in square brackets
```sophia
function example_map() : map(string, int) =
{["key1"] = 1, ["key2"] = 2}
```
The empty map is written `{}`.
### Accessing values
Record fields access is written `r.f` and map lookup `m[k]`. For instance,
```sophia
function get_balance(a : address, accounts : map(address, account)) =
accounts[a].balance
```
Looking up a non-existing key in a map results in contract execution failing. A
default value to return for non-existing keys can be provided using the syntax
`m[k = default]`. See also `Map.member` and `Map.lookup` below.
### Updating a value
Record field updates are written `r{f = v}`. This creates a new record value
which is the same as `r`, but with the value of the field `f` replaced by `v`.
Similarly, `m{[k] = v}` constructs a map with the same values as `m` except
that `k` maps to `v`. It makes no difference if `m` has a mapping for `k` or
not.
It is possible to give a name to the old value of a field or mapping in an
update: instead of `acc{ balance = acc.balance + 100 }` it is possible to write
`acc{ balance @ b = b + 100 }`, binding `b` to `acc.balance`. When giving a
name to a map value (`m{ [k] @ x = v }`), the corresponding key must be present
in the map or execution fails, but a default value can be provided:
`m{ [k = default] @ x = v }`. In this case `x` is bound to `default` if
`k` is not in the map.
Updates can be nested:
```sophia
function clear_history(a : address, accounts : map(address, account)) : map(address, account) =
accounts{ [a].history = [] }
```
This is equivalent to `accounts{ [a] @ acc = acc{ history = [] } }` and thus
requires `a` to be present in the accounts map. To have `clear_history` create
an account if `a` is not in the map you can write (given a function `empty_account`):
```sophia
accounts{ [a = empty_account()].history = [] }
```
### Map implementation
Internally in the VM maps are implemented as hash maps and support fast lookup
and update. Large maps can be stored in the contract state and the size of the
map does not contribute to the gas costs of a contract call reading or updating
it.
## Strings
There is a builtin type `string`, which can be seen as an array of bytes.
Strings can be compared for equality (`==`, `!=`), used as keys in maps and
records, and used in builtin functions `String.length`, `String.concat` and
the hash functions described below.
Please refer to the `String` [library documentation](sophia_stdlib.md#string).
## Chars
There is a builtin type `char` (the underlying representation being an integer),
mainly used to manipulate strings via `String.to_list`/`String.from_list`.
Characters can also be introduced as character literals (`'x', '+', ...).
Please refer to the `Char` [library documentation](sophia_stdlib.md#char).
## Byte arrays
Byte arrays are fixed size arrays of 8-bit integers. They are described in hexadecimal system,
for example the literal `#cafe` creates a two-element array of bytes `ca` (202) and `fe` (254)
and thus is a value of type `bytes(2)`.
Please refer to the `Bytes` [library documentation](sophia_stdlib.md#bytes).
## Cryptographic builtins
Libraries [Crypto](sophia_stdlib.md#crypto) and [String](sophia_stdlib.md#string) provide functions to
hash objects, verify signatures etc. The `hash` is a type alias for `bytes(32)`.
## Authorization interface
When a Generalized account is authorized, the authorization function needs
access to the transaction and the transaction hash for the wrapped transaction. (A `GAMetaTx`
wrapping a transaction.) The transaction and the transaction hash is available in the primitive
`Auth.tx` and `Auth.tx_hash` respectively, they are *only* available during authentication if invoked by a
normal contract call they return `None`.
## Oracle interface
You can attach an oracle to the current contract and you can interact with oracles
through the Oracle interface.
For a full description of how Oracle works see
[Oracles](https://github.com/aeternity/protocol/blob/master/oracles/oracles.md#oracles).
For a functionality documentation refer to the [standard library](sophia_stdlib.md#oracle).
### Example
Example for an oracle answering questions of type `string` with answers of type `int`:
```sophia
contract Oracles =
stateful entrypoint registerOracle(acct : address,
sign : signature, // Signed network id + oracle address + contract address
qfee : int,
ttl : Chain.ttl) : oracle(string, int) =
Oracle.register(acct, signature = sign, qfee, ttl)
entrypoint queryFee(o : oracle(string, int)) : int =
Oracle.query_fee(o)
payable stateful entrypoint createQuery(o : oracle_query(string, int),
q : string,
qfee : int,
qttl : Chain.ttl,
rttl : int) : oracle_query(string, int) =
require(qfee =< Call.value, "insufficient value for qfee")
Oracle.query(o, q, qfee, qttl, RelativeTTL(rttl))
stateful entrypoint extendOracle(o : oracle(string, int),
ttl : Chain.ttl) : unit =
Oracle.extend(o, ttl)
stateful entrypoint signExtendOracle(o : oracle(string, int),
sign : signature, // Signed network id + oracle address + contract address
ttl : Chain.ttl) : unit =
Oracle.extend(o, signature = sign, ttl)
stateful entrypoint respond(o : oracle(string, int),
q : oracle_query(string, int),
sign : signature, // Signed network id + oracle query id + contract address
r : int) =
Oracle.respond(o, q, signature = sign, r)
entrypoint getQuestion(o : oracle(string, int),
q : oracle_query(string, int)) : string =
Oracle.get_question(o, q)
entrypoint hasAnswer(o : oracle(string, int),
q : oracle_query(string, int)) =
switch(Oracle.get_answer(o, q))
None => false
Some(_) => true
entrypoint getAnswer(o : oracle(string, int),
q : oracle_query(string, int)) : option(int) =
Oracle.get_answer(o, q)
```
### Sanity checks
When an Oracle literal is passed to a contract, no deep checks are performed.
For extra safety [Oracle.check](sophia_stdlib.md#check) and [Oracle.check_query](sophia_stdlib.md#check_query)
functions are provided.
## AENS interface
Contracts can interact with the
[æternity naming system](https://github.com/aeternity/protocol/blob/master/AENS.md).
For this purpose the [AENS](sophia_stdlib.md#aens) library was exposed.
### Example
In this example we assume that the name `name` already exists, and is owned by
an account with address `addr`. In order to allow a contract `ct` to handle
`name` the account holder needs to create a
[signature](#delegation-signature) `sig` of `addr | name.hash | ct.address`.
Armed with this information we can for example write a function that extends
the name if it expires within 1000 blocks:
```sophia
stateful entrypoint extend_if_necessary(addr : address, name : string, sig : signature) =
switch(AENS.lookup(name))
None => ()
Some(AENS.Name(_, FixedTTL(expiry), _)) =>
if(Chain.block_height + 1000 > expiry)
AENS.update(addr, name, Some(RelativeTTL(50000)), None, None, signature = sig)
```
And we can write functions that adds and removes keys from the pointers of the
name:
```sophia
stateful entrypoint add_key(addr : address, name : string, key : string,
pt : AENS.pointee, sig : signature) =
switch(AENS.lookup(name))
None => ()
Some(AENS.Name(_, _, ptrs)) =>
AENS.update(addr, name, None, None, Some(ptrs{[key] = pt}), signature = sig)
stateful entrypoint delete_key(addr : address, name : string,
key : string, sig : signature) =
switch(AENS.lookup(name))
None => ()
Some(AENS.Name(_, _, ptrs)) =>
let ptrs = Map.delete(key, ptrs)
AENS.update(addr, name, None, None, Some(ptrs), signature = sig)
```
*Note:* From the Iris hardfork more strict rules apply for AENS pointers, when
a Sophia contract lookup or update (bad) legacy pointers, the bad keys are
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).
To use events a contract must declare a datatype `event`, and events are then
logged using the `Chain.event` function:
```sophia
datatype event
= Event1(int, int, string)
| Event2(string, address)
Chain.event(e : event) : unit
```
The event can have 0-3 *indexed* fields, and an optional *payload* field. A
field is indexed if it fits in a 32-byte word, i.e.
- `bool`
- `int`
- `bits`
- `address`
- `oracle(_, _)`
- `oracle_query(_, _)`
- contract types
- `bytes(n)` for `n` ≤ 32, in particular `hash`
The payload field must be either a string or a byte array of more than 32 bytes.
The fields can appear in any order.
*NOTE:* Indexing is not part of the core æternity node.
Events are emitted by using the `Chain.event` function. The following function
will emit one Event of each kind in the example.
```sophia
entrypoint emit_events() : () =
Chain.event(Event1(42, 34, "foo"))
Chain.event(Event2("This is not indexed", Contract.address))
```
### Argument order
It is only possible to have one (1) `string` parameter in the event, but it can
be placed in any position (and its value will end up in the `data` field), i.e.
```sophia
AnotherEvent(string, indexed address)
...
Chain.event(AnotherEvent("This is not indexed", Contract.address))
```
would yield exactly the same result in the example above!
## Compiler pragmas
To enforce that a contract is only compiled with specific versions of the
Sophia compiler, you can give one or more `@compiler` pragmas at the
top-level (typically at the beginning) of a file. For instance, to enforce that
a contract is compiled with version 4.3 of the compiler you write
```sophia
@compiler >= 4.3
@compiler < 4.4
```
Valid operators in compiler pragmas are `<`, `=<`, `==`, `>=`, and `>`. Version
numbers are given as a sequence of non-negative integers separated by dots.
Trailing zeros are ignored, so `4.0.0 == 4`. If a constraint is violated an
error is reported and compilation fails.
## Exceptions
Contracts can fail with an (uncatchable) exception using the built-in function
```sophia
abort(reason : string) : 'a
```
Calling abort causes the top-level call transaction to return an error result
containing the `reason` string. Only the gas used up to and including the abort
call is charged. This is different from termination due to a crash which
consumes all available gas.
For convenience the following function is also built-in:
```sophia
function require(b : bool, err : string) =
if(!b) abort(err)
```
## Delegation signature
Some chain operations (`Oracle.<operation>` and `AENS.<operation>`) have an
optional delegation signature. This is typically used when a user/accounts
would like to allow a contract to act on it's behalf. 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.).
+311 -924
View File
File diff suppressed because it is too large Load Diff
-263
View File
@@ -1,263 +0,0 @@
# Syntax
## Lexical syntax
### Comments
Single line comments start with `//` and block comments are enclosed in `/*`
and `*/` and can be nested.
### Keywords
```
contract elif else entrypoint false function if import include let mod namespace
private payable stateful switch true type record datatype main interface
```
### Tokens
- `Id = [a-z_][A-Za-z0-9_']*` identifiers start with a lower case letter.
- `Con = [A-Z][A-Za-z0-9_']*` constructors start with an upper case letter.
- `QId = (Con\.)+Id` qualified identifiers (e.g. `Map.member`)
- `QCon = (Con\.)+Con` qualified constructor
- `TVar = 'Id` type variable (e.g `'a`, `'b`)
- `Int = [0-9]+(_[0-9]+)*|0x[0-9A-Fa-f]+(_[0-9A-Fa-f]+)*` integer literal with optional `_` separators
- `Bytes = #[0-9A-Fa-f]+(_[0-9A-Fa-f]+)*` byte array literal with optional `_` separators
- `String` string literal enclosed in `"` with escape character `\`
- `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
Valid string escape codes are
| Escape | ASCII | |
|---------------|-------------|---|
| `\b` | 8 | |
| `\t` | 9 | |
| `\n` | 10 | |
| `\v` | 11 | |
| `\f` | 12 | |
| `\r` | 13 | |
| `\e` | 27 | |
| `\xHexDigits` | *HexDigits* | |
See the [identifier encoding scheme](https://github.com/aeternity/protocol/blob/master/node/api/api_encoding.md) for the
details on the base58 literals.
## Layout blocks
Sophia uses Python-style layout rules to group declarations and statements. A
layout block with more than one element must start on a separate line and be
indented more than the currently enclosing layout block. Blocks with a single
element can be written on the same line as the previous token.
Each element of the block must share the same indentation and no part of an
element may be indented less than the indentation of the block. For instance
```sophia
contract Layout =
function foo() = 0 // no layout
function bar() = // layout block starts on next line
let x = foo() // indented more than 2 spaces
x
+ 1 // the '+' is indented more than the 'x'
```
## Notation
In describing the syntax below, we use the following conventions:
- Upper-case identifiers denote non-terminals (like `Expr`) or terminals with
some associated value (like `Id`).
- Keywords and symbols are enclosed in single quotes: `'let'` or `'='`.
- Choices are separated by vertical bars: `|`.
- Optional elements are enclosed in `[` square brackets `]`.
- `(` Parentheses `)` are used for grouping.
- Zero or more repetitions are denoted by a postfix `*`, and one or more
repetitions by a `+`.
- `Block(X)` denotes a layout block of `X`s.
- `Sep(X, S)` is short for `[X (S X)*]`, i.e. a possibly empty sequence of `X`s
separated by `S`s.
- `Sep1(X, S)` is short for `X (S X)*`, i.e. same as `Sep`, but must not be empty.
## Declarations
A Sophia file consists of a sequence of *declarations* in a layout block.
```c
File ::= Block(TopDecl)
TopDecl ::= ['payable'] 'contract' Con '=' Block(Decl)
| 'namespace' Con '=' Block(Decl)
| '@compiler' PragmaOp Version
| 'include' String
Decl ::= 'type' Id ['(' TVar* ')'] '=' TypeAlias
| 'record' Id ['(' TVar* ')'] '=' RecordType
| 'datatype' Id ['(' TVar* ')'] '=' DataType
| (EModifier* 'entrypoint' | FModifier* 'function') Block(FunDecl)
FunDecl ::= Id ':' Type // Type signature
| Id Args [':' Type] '=' Block(Stmt) // Definition
PragmaOp ::= '<' | '=<' | '==' | '>=' | '>'
Version ::= Sep1(Int, '.')
EModifier ::= 'payable' | 'stateful'
FModifier ::= 'stateful' | 'private'
Args ::= '(' Sep(Pattern, ',') ')'
```
Contract declarations must appear at the top-level.
For example,
```sophia
contract Test =
type t = int
entrypoint add (x : t, y : t) = x + y
```
There are three forms of type declarations: type aliases (declared with the
`type` keyword), record type definitions (`record`) and data type definitions
(`datatype`):
```c
TypeAlias ::= Type
RecordType ::= '{' Sep(FieldType, ',') '}'
DataType ::= Sep1(ConDecl, '|')
FieldType ::= Id ':' Type
ConDecl ::= Con ['(' Sep1(Type, ',') ')']
```
For example,
```sophia
record point('a) = {x : 'a, y : 'a}
datatype shape('a) = Circle(point('a), 'a) | Rect(point('a), point('a))
type int_shape = shape(int)
```
## Types
```c
Type ::= Domain '=>' Type // Function type
| Type '(' Sep(Type, ',') ')' // Type application
| '(' Type ')' // Parens
| 'unit' | Sep(Type, '*') // Tuples
| Id | QId | TVar
Domain ::= Type // Single argument
| '(' Sep(Type, ',') ')' // Multiple arguments
```
The function type arrow associates to the right.
Example,
```sophia
'a => list('a) => (int * list('a))
```
## Statements
Function bodies are blocks of *statements*, where a statement is one of the following
```c
Stmt ::= 'switch' '(' Expr ')' Block(Case)
| 'if' '(' Expr ')' Block(Stmt)
| 'elif' '(' Expr ')' Block(Stmt)
| 'else' Block(Stmt)
| 'let' LetDef
| Expr
LetDef ::= Id Args [':' Type] '=' Block(Stmt) // Function definition
| Pattern '=' Block(Stmt) // Value definition
Case ::= Pattern '=>' Block(Stmt)
Pattern ::= Expr
```
`if` statements can be followed by zero or more `elif` statements and an optional final `else` statement. For example,
```sophia
let x : int = 4
switch(f(x))
None => 0
Some(y) =>
if(y > 10)
"too big"
elif(y < 3)
"too small"
else
"just right"
```
## Expressions
```c
Expr ::= '(' LamArgs ')' '=>' Block(Stmt) // Anonymous function (x) => x + 1
| 'if' '(' Expr ')' Expr 'else' Expr // If expression if(x < y) y else x
| Expr ':' Type // Type annotation 5 : int
| Expr BinOp Expr // Binary operator x + y
| UnOp Expr // Unary operator ! b
| Expr '(' Sep(Expr, ',') ')' // Application f(x, y)
| Expr '.' Id // Projection state.x
| Expr '[' Expr ']' // Map lookup map[key]
| Expr '{' Sep(FieldUpdate, ',') '}' // Record or map update r{ fld[key].x = y }
| '[' Sep(Expr, ',') ']' // List [1, 2, 3]
| '[' Expr '|' Sep(Generator, ',') ']'
// List comprehension [k | x <- [1], if (f(x)), let k = x+1]
| '[' Expr '..' Expr ']' // List range [1..n]
| '{' Sep(FieldUpdate, ',') '}' // Record or map value {x = 0, y = 1}, {[key] = val}
| '(' Expr ')' // Parens (1 + 2) * 3
| 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
Generator ::= Pattern '<-' Expr // Generator
| 'if' '(' Expr ')' // Guard
| LetDef // Definition
LamArgs ::= '(' Sep(LamArg, ',') ')'
LamArg ::= Id [':' Type]
FieldUpdate ::= Path '=' Expr
Path ::= Id // Record field
| '[' Expr ']' // Map key
| Path '.' Id // Nested record field
| Path '[' Expr ']' // Nested map key
BinOp ::= '||' | '&&' | '<' | '>' | '=<' | '>=' | '==' | '!='
| '::' | '++' | '+' | '-' | '*' | '/' | 'mod' | '^'
UnOp ::= '-' | '!'
```
## Operators types
| Operators | Type
| --- | ---
| `-` `+` `*` `/` `mod` `^` | arithmetic operators
| `!` `&&` `\|\|` | logical operators
| `==` `!=` `<` `>` `=<` `>=` | comparison operators
| `::` `++` | list operators
## Operator precendences
In order of highest to lowest precedence.
| Operators | Associativity
| --- | ---
| `!` | right
| `^` | left
| `*` `/` `mod` | left
| `-` (unary) | right
| `+` `-` | left
| `::` `++` | right
| `<` `>` `=<` `>=` `==` `!=` | none
| `&&` | right
| `\|\|` | right
-68
View File
@@ -1,68 +0,0 @@
namespace BLS12_381 =
type fr = MCL_BLS12_381.fr
type fp = MCL_BLS12_381.fp
record fp2 = { x1 : fp, x2 : fp }
record g1 = { x : fp, y : fp, z : fp }
record g2 = { x : fp2, y : fp2, z : fp2 }
record gt = { x1 : fp, x2 : fp, x3 : fp, x4 : fp, x5 : fp, x6 : fp,
x7 : fp, x8 : fp, x9 : fp, x10 : fp, x11 : fp, x12 : fp }
function pairing_check(xs : list(g1), ys : list(g2)) =
switch((xs, ys))
([], []) => true
(x :: xs, y :: ys) => pairing_check_(pairing(x, y), xs, ys)
function pairing_check_(acc : gt, xs : list(g1), ys : list(g2)) =
switch((xs, ys))
([], []) => gt_is_one(acc)
(x :: xs, y :: ys) =>
pairing_check_(gt_mul(acc, pairing(x, y)), xs, ys)
function int_to_fr(x : int) = MCL_BLS12_381.int_to_fr(x)
function int_to_fp(x : int) = MCL_BLS12_381.int_to_fp(x)
function fr_to_int(x : fr) = MCL_BLS12_381.fr_to_int(x)
function fp_to_int(x : fp) = MCL_BLS12_381.fp_to_int(x)
function mk_g1(x : int, y : int, z : int) : g1 =
{ x = int_to_fp(x), y = int_to_fp(y), z = int_to_fp(z) }
function mk_g2(x1 : int, x2 : int, y1 : int, y2 : int, z1 : int, z2 : int) : g2 =
{ x = {x1 = int_to_fp(x1), x2 = int_to_fp(x2)},
y = {x1 = int_to_fp(y1), x2 = int_to_fp(y2)},
z = {x1 = int_to_fp(z1), x2 = int_to_fp(z2)} }
function pack_g1(t) = switch(t)
(x, y, z) => {x = x, y = y, z = z} : g1
function pack_g2(t) = switch(t)
((x1, x2), (y1, y2), (z1, z2)) =>
{x = {x1 = x1, x2 = x2}, y = {x1 = y1, x2 = y2}, z = {x1 = z1, x2 = z2}} : g2
function pack_gt(t) = switch(t)
(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12) =>
{x1 = x1, x2 = x2, x3 = x3, x4 = x4, x5 = x5, x6 = x6,
x7 = x7, x8 = x8, x9 = x9, x10 = x10, x11 = x11, x12 = x12} : gt
function g1_neg(p : g1) = pack_g1(MCL_BLS12_381.g1_neg((p.x, p.y, p.z)))
function g1_norm(p : g1) = pack_g1(MCL_BLS12_381.g1_norm((p.x, p.y, p.z)))
function g1_valid(p : g1) = MCL_BLS12_381.g1_valid((p.x, p.y, p.z))
function g1_is_zero(p : g1) = MCL_BLS12_381.g1_is_zero((p.x, p.y, p.z))
function g1_add(p : g1, q : g1) = pack_g1(MCL_BLS12_381.g1_add((p.x, p.y, p.z), (q.x, q.y, q.z)))
function g1_mul(k : fr, p : g1) = pack_g1(MCL_BLS12_381.g1_mul(k, (p.x, p.y, p.z)))
function g2_neg(p : g2) = pack_g2(MCL_BLS12_381.g2_neg(((p.x.x1, p.x.x2), (p.y.x1, p.y.x2), (p.z.x1, p.z.x2))))
function g2_norm(p : g2) = pack_g2(MCL_BLS12_381.g2_norm(((p.x.x1, p.x.x2), (p.y.x1, p.y.x2), (p.z.x1, p.z.x2))))
function g2_valid(p : g2) = MCL_BLS12_381.g2_valid(((p.x.x1, p.x.x2), (p.y.x1, p.y.x2), (p.z.x1, p.z.x2)))
function g2_is_zero(p : g2) = MCL_BLS12_381.g2_is_zero(((p.x.x1, p.x.x2), (p.y.x1, p.y.x2), (p.z.x1, p.z.x2)))
function g2_add(p : g2, q : g2) = pack_g2(MCL_BLS12_381.g2_add(((p.x.x1, p.x.x2), (p.y.x1, p.y.x2), (p.z.x1, p.z.x2)),
((q.x.x1, q.x.x2), (q.y.x1, q.y.x2), (q.z.x1, q.z.x2))))
function g2_mul(k : fr, p : g2) = pack_g2(MCL_BLS12_381.g2_mul(k, ((p.x.x1, p.x.x2), (p.y.x1, p.y.x2), (p.z.x1, p.z.x2))))
function gt_inv(p : gt) = pack_gt(MCL_BLS12_381.gt_inv((p.x1, p.x2, p.x3, p.x4, p.x5, p.x6, p.x7, p.x8, p.x9, p.x10, p.x11, p.x12)))
function gt_add(p : gt, q : gt) = pack_gt(MCL_BLS12_381.gt_add((p.x1, p.x2, p.x3, p.x4, p.x5, p.x6, p.x7, p.x8, p.x9, p.x10, p.x11, p.x12),
(q.x1, q.x2, q.x3, q.x4, q.x5, q.x6, q.x7, q.x8, q.x9, q.x10, q.x11, q.x12)))
function gt_mul(p : gt, q : gt) = pack_gt(MCL_BLS12_381.gt_mul((p.x1, p.x2, p.x3, p.x4, p.x5, p.x6, p.x7, p.x8, p.x9, p.x10, p.x11, p.x12),
(q.x1, q.x2, q.x3, q.x4, q.x5, q.x6, q.x7, q.x8, q.x9, q.x10, q.x11, q.x12)))
function gt_pow(p : gt, k : fr) = pack_gt(MCL_BLS12_381.gt_pow((p.x1, p.x2, p.x3, p.x4, p.x5, p.x6, p.x7, p.x8, p.x9, p.x10, p.x11, p.x12), k))
function gt_is_one(p : gt) = MCL_BLS12_381.gt_is_one((p.x1, p.x2, p.x3, p.x4, p.x5, p.x6, p.x7, p.x8, p.x9, p.x10, p.x11, p.x12))
function pairing(p : g1, q : g2) = pack_gt(MCL_BLS12_381.pairing((p.x, p.y, p.z), ((q.x.x1, q.x.x2), (q.y.x1, q.y.x2), (q.z.x1, q.z.x2))))
function miller_loop(p : g1, q : g2) = pack_gt(MCL_BLS12_381.miller_loop((p.x, p.y, p.z), ((q.x.x1, q.x.x2), (q.y.x1, q.y.x2), (q.z.x1, q.z.x2))))
function final_exp(p : gt) = pack_gt(MCL_BLS12_381.final_exp((p.x1, p.x2, p.x3, p.x4, p.x5, p.x6, p.x7, p.x8, p.x9, p.x10, p.x11, p.x12)))
-136
View File
@@ -1,136 +0,0 @@
@compiler >= 4.3
namespace Bitwise =
// bit shift 'x' right 'n' postions
function bsr(n : int, x : int) : int =
let step = 2^n
let res = x / step
if (x >= 0 || x mod step == 0)
res
else
res - 1
// bit shift 'x' left 'n' positions
function bsl(n : int, x : int) : int =
x * 2^n
// bit shift 'x' left 'n' positions, limit at 'lim' bits
function bsli(n : int, x : int, lim : int) : int =
(x * 2^n) mod (2^lim)
// bitwise 'and' for arbitrary precision integers
function band(a : int, b : int) : int =
if (a >= 0 && b >= 0)
uband_(a, b)
elif (b >= 0)
ubnand_(b, -1 - a)
elif (a >= 0)
ubnand_(a, -1 - b)
else
-1 - ubor_(-1 - a, -1 - b)
// bitwise 'or' for arbitrary precision integers
function
bor : (int, int) => int
bor(0, b) = b
bor(a, 0) = a
bor(a : int, b : int) : int =
if (a >= 0 && b >= 0)
ubor_(a, b)
elif (b >= 0)
-1 - ubnand_(-1 - a, b)
elif (a >= 0)
-1 - ubnand_(-1 - b, a)
else
-1 - uband_(-1 - a, -1 - b)
// bitwise 'xor' for arbitrary precision integers
function
bxor : (int, int) => int
bxor(0, b) = b
bxor(a, 0) = a
bxor(a, b) =
if (a >= 0 && b >= 0)
ubxor_(a, b)
elif (b >= 0)
-1 - ubxor_(-1 - a, b)
elif (a >= 0)
-1 - ubxor_(a, -1 - b)
else
ubxor_(-1 - a, -1 - b)
// bitwise 'not' for arbitrary precision integers
function bnot(a : int) = bxor(a, -1)
// Bitwise 'and' for non-negative integers
function uband(a : int, b : int) : int =
require(a >= 0 && b >= 0, "uband is only defined for non-negative integers")
switch((a, b))
(0, _) => 0
(_, 0) => 0
_ => uband__(a, b, 1, 0)
private function uband_(a, b) = uband__(a, b, 1, 0)
private function
uband__(0, b, val, acc) = acc
uband__(a, 0, val, acc) = acc
uband__(a, b, val, acc) =
switch (a mod 2 + b mod 2)
2 => uband__(a / 2, b / 2, val * 2, acc + val)
_ => uband__(a / 2, b / 2, val * 2, acc)
// Bitwise 'or' for non-negative integers
function ubor(a, b) =
require(a >= 0 && b >= 0, "ubor is only defined for non-negative integers")
switch((a, b))
(0, _) => b
(_, 0) => a
_ => ubor__(a, b, 1, 0)
private function ubor_(a, b) = ubor__(a, b, 1, 0)
private function
ubor__(0, 0, val, acc) = acc
ubor__(a, b, val, acc) =
switch (a mod 2 + b mod 2)
0 => ubor__(a / 2, b / 2, val * 2, acc)
_ => ubor__(a / 2, b / 2, val * 2, acc + val)
//Bitwise 'xor' for non-negative integers
function
ubxor : (int, int) => int
ubxor(0, b) = b
ubxor(a, 0) = a
ubxor(a, b) =
require(a >= 0 && b >= 0, "ubxor is only defined for non-negative integers")
ubxor__(a, b, 1, 0)
private function ubxor_(a, b) = ubxor__(a, b, 1, 0)
private function
ubxor__(0, 0, val, acc) = acc
ubxor__(a, b, val, acc) =
switch(a mod 2 + b mod 2)
1 => ubxor__(a / 2, b / 2, val * 2, acc + val)
_ => ubxor__(a / 2, b / 2, val * 2, acc)
// Bitwise combined 'and' and 'not' of second argument for positive integers
// x 'bnand' y = x 'band' ('bnot' y)
// The tricky bit is that after negation the second argument has an infinite number of 1's
// use as many as needed!
//
// NOTE: this function is not symmetric!
private function ubnand(a, b) =
require(a >= 0 && b >= 0, "ubxor is only defined for non-negative integers")
ubnand__(a, b, 1, 0)
private function ubnand_(a, b) = ubnand__(a, b, 1, 0)
private function
ubnand__(0, b, val, acc) = acc
ubnand__(a, b, val, acc) =
switch((a mod 2, b mod 2))
(1, 0) => ubnand__(a / 2, b / 2, val * 2, acc + val)
_ => ubnand__(a / 2, b / 2, val * 2, acc)
-2
View File
@@ -1,5 +1,3 @@
include "String.aes"
namespace Frac = namespace Frac =
private function gcd(a : int, b : int) = private function gcd(a : int, b : int) =
+82 -91
View File
@@ -19,16 +19,6 @@ namespace List =
[x] => Some(x) [x] => Some(x)
_::t => last(t) _::t => last(t)
function drop_last(l : list('a)) : option(list('a)) = switch(l)
[] => None
_ => Some(drop_last_unsafe(l))
function drop_last_unsafe(l : list('a)) : list('a) = switch(l)
[_] => []
h::t => h::drop_last_unsafe(t)
[] => abort("drop_last_unsafe: list empty")
function contains(e : 'a, l : list('a)) = switch(l) function contains(e : 'a, l : list('a)) = switch(l)
[] => false [] => false
h::t => h == e || contains(e, t) h::t => h == e || contains(e, t)
@@ -42,15 +32,14 @@ namespace List =
/** Returns list of all indices of elements from `l` that fulfill the predicate `p`. /** Returns list of all indices of elements from `l` that fulfill the predicate `p`.
*/ */
function find_indices(p : 'a => bool, l : list('a)) : list(int) = find_indices_(p, l, 0) function find_indices(p : 'a => bool, l : list('a)) : list(int) = find_indices_(p, l, 0, [])
private function find_indices_( p : 'a => bool private function find_indices_( p : 'a => bool
, l : list('a) , l : list('a)
, n : int , n : int
, acc : list(int)
) : list(int) = switch(l) ) : list(int) = switch(l)
[] => [] [] => reverse(acc)
h::t => h::t => find_indices_(p, t, n+1, if(p(h)) n::acc else acc)
let rest = find_indices_(p, t, n+1)
if(p(h)) n::rest else rest
function nth(n : int, l : list('a)) : option('a) = function nth(n : int, l : list('a)) : option('a) =
switch(l) switch(l)
@@ -79,45 +68,44 @@ namespace List =
* `a` and `b` jumping by given `step`. Includes `a` and takes * `a` and `b` jumping by given `step`. Includes `a` and takes
* `b` only if `(b - a) mod step == 0`. `step` should be bigger than 0. * `b` only if `(b - a) mod step == 0`. `step` should be bigger than 0.
*/ */
function from_to_step(a : int, b : int, s : int) : list(int) = function from_to_step(a : int, b : int, s : int) : list(int) = from_to_step_(a, b, s, [])
require(s > 0, "List.from_to_step: non-positive step") private function from_to_step_(a, b, s, acc) =
from_to_step_(a, b - (b-a) mod s, s, []) if (a > b) reverse(acc) else from_to_step_(a + s, b, s, a :: acc)
private function from_to_step_(a : int, b : int, s : int, acc : list(int)) : list(int) =
if(b < a) acc
else from_to_step_(a, b - s, s, b::acc)
/** Unsafe. Replaces `n`th element of `l` with `e`. Crashes on over/underflow /** Unsafe. Replaces `n`th element of `l` with `e`. Crashes on over/underflow
*/ */
function replace_at(n : int, e : 'a, l : list('a)) : list('a) = function replace_at(n : int, e : 'a, l : list('a)) : list('a) =
if(n<0) abort("insert_at underflow") else replace_at_(n, e, l) if(n<0) abort("insert_at underflow") else replace_at_(n, e, l, [])
private function replace_at_(n : int, e : 'a, l : list('a)) : list('a) = private function replace_at_(n : int, e : 'a, l : list('a), acc : list('a)) : list('a) =
switch(l) switch(l)
[] => abort("replace_at overflow") [] => abort("replace_at overflow")
h::t => if (n == 0) e::t h::t => if (n == 0) reverse(e::acc) ++ t
else h::replace_at_(n-1, e, t) else replace_at_(n-1, e, t, h::acc)
/** Unsafe. Adds `e` to `l` to be its `n`th element. Crashes on over/underflow /** Unsafe. Adds `e` to `l` to be its `n`th element. Crashes on over/underflow
*/ */
function insert_at(n : int, e : 'a, l : list('a)) : list('a) = function insert_at(n : int, e : 'a, l : list('a)) : list('a) =
if(n<0) abort("insert_at underflow") else insert_at_(n, e, l) if(n<0) abort("insert_at underflow") else insert_at_(n, e, l, [])
private function insert_at_(n : int, e : 'a, l : list('a)) : list('a) = private function insert_at_(n : int, e : 'a, l : list('a), acc : list('a)) : list('a) =
if (n == 0) e::l if (n == 0) reverse(e::acc) ++ l
else switch(l) else switch(l)
[] => abort("insert_at overflow") [] => abort("insert_at overflow")
h::t => h::insert_at_(n-1, e, t) h::t => insert_at_(n-1, e, t, h::acc)
/** Assuming that cmp represents `<` comparison, inserts `x` before /** Assuming that cmp represents `<` comparison, inserts `x` before
* the first element in the list `l` which is greater than it * the first element in the list `l` which is greater than it
*/ */
function insert_by(cmp : (('a, 'a) => bool), x : 'a, l : list('a)) : list('a) = function insert_by(cmp : (('a, 'a) => bool), x : 'a, l : list('a)) : list('a) =
insert_by_(cmp, x, l, [])
private function insert_by_(cmp : (('a, 'a) => bool), x : 'a, l : list('a), acc : list('a)) : list('a) =
switch(l) switch(l)
[] => [x] [] => reverse(x::acc)
h::t => h::t =>
if(cmp(x, h)) // x < h if(cmp(x, h)) // x < h
x::l reverse(acc) ++ (x::l)
else else
h::insert_by(cmp, x, t) insert_by_(cmp, x, t, h::acc)
function foldr(cons : ('a, 'b) => 'b, nil : 'b, l : list('a)) : 'b = switch(l) function foldr(cons : ('a, 'b) => 'b, nil : 'b, l : list('a)) : 'b = switch(l)
@@ -135,52 +123,49 @@ namespace List =
f(e) f(e)
foreach(l', f) foreach(l', f)
function reverse(l : list('a)) : list('a) = reverse_(l, []) function reverse(l : list('a)) : list('a) = foldl((lst, el) => el :: lst, [], l)
private function reverse_(l : list('a), acc : list('a)) : list('a) = switch(l)
[] => acc
h::t => reverse_(t, h::acc)
function map(f : 'a => 'b, l : list('a)) : list('b) = switch(l) function map(f : 'a => 'b, l : list('a)) : list('b) = map_(f, l, [])
[] => [] private function map_(f : 'a => 'b, l : list('a), acc : list('b)) : list('b) = switch(l)
h::t => f(h)::map(f, t) [] => reverse(acc)
h::t => map_(f, t, f(h)::acc)
/** Effectively composition of `map` and `flatten` /** Effectively composition of `map` and `flatten`
*/ */
function flat_map(f : 'a => list('b), l : list('a)) : list('b) = function flat_map(f : 'a => list('b), l : list('a)) : list('b) =
ListInternal.flat_map(f, l) ListInternal.flat_map(f, l)
function filter(p : 'a => bool, l : list('a)) : list('a) = switch(l) function filter(p : 'a => bool, l : list('a)) : list('a) = filter_(p, l, [])
[] => [] private function filter_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l)
h::t => [] => reverse(acc)
let rest = filter(p, t) h::t => filter_(p, t, if(p(h)) h::acc else acc)
if(p(h)) h::rest else rest
/** Take up to `n` first elements /** Take `n` first elements
*/ */
function take(n : int, l : list('a)) : list('a) = function take(n : int, l : list('a)) : list('a) =
if(n < 0) abort("Take negative number of elements") else take_(n, l) if(n < 0) abort("Take negative number of elements") else take_(n, l, [])
private function take_(n : int, l : list('a)) : list('a) = private function take_(n : int, l : list('a), acc : list('a)) : list('a) =
if(n == 0) [] if(n == 0) reverse(acc)
else switch(l) else switch(l)
[] => [] [] => reverse(acc)
h::t => h::take_(n-1, t) h::t => take_(n-1, t, h::acc)
/** Drop up to `n` first elements /** Drop `n` first elements
*/ */
function drop(n : int, l : list('a)) : list('a) = function drop(n : int, l : list('a)) : list('a) =
if(n < 0) abort("Drop negative number of elements") else drop_(n, l) if(n < 0) abort("Drop negative number of elements")
private function drop_(n : int, l : list('a)) : list('a) = elif (n == 0) l
if (n == 0) l
else switch(l) else switch(l)
[] => [] [] => []
h::t => drop_(n-1, t) h::t => drop(n-1, t)
/** Get the longest prefix of a list in which every element /** Get the longest prefix of a list in which every element
* matches predicate `p` * matches predicate `p`
*/ */
function take_while(p : 'a => bool, l : list('a)) : list('a) = switch(l) function take_while(p : 'a => bool, l : list('a)) : list('a) = take_while_(p, l, [])
[] => [] private function take_while_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l)
h::t => if(p(h)) h::take_while(p, t) else [] [] => reverse(acc)
h::t => if(p(h)) take_while_(p, t, h::acc) else reverse(acc)
/** Drop elements from `l` until `p` holds /** Drop elements from `l` until `p` holds
*/ */
@@ -191,15 +176,18 @@ namespace List =
/** Splits list into two lists of elements that respectively /** Splits list into two lists of elements that respectively
* match and don't match predicate `p` * match and don't match predicate `p`
*/ */
function partition(p : 'a => bool, l : list('a)) : (list('a) * list('a)) = switch(l) function partition(p : 'a => bool, l : list('a)) : (list('a) * list('a)) = partition_(p, l, [], [])
[] => ([], []) private function partition_( p : 'a => bool
h::t => , l : list('a)
let (l, r) = partition(p, t) , acc_t : list('a)
if(p(h)) (h::l, r) else (l, h::r) , acc_f : list('a)
) : (list('a) * list('a)) = switch(l)
[] => (reverse(acc_t), reverse(acc_f))
h::t => if(p(h)) partition_(p, t, h::acc_t, acc_f) else partition_(p, t, acc_t, h::acc_f)
function flatten(l : list(list('a))) : list('a) = switch(l) /** Flattens list of lists into a single list
[] => [] */
h::t => h ++ flatten(t) function flatten(ll : list(list('a))) : list('a) = foldr((l1, l2) => l1 ++ l2, [], ll)
function all(p : 'a => bool, l : list('a)) : bool = switch(l) function all(p : 'a => bool, l : list('a)) : bool = switch(l)
[] => true [] => true
@@ -209,34 +197,34 @@ namespace List =
[] => false [] => false
h::t => if(p(h)) true else any(p, t) h::t => if(p(h)) true else any(p, t)
function sum(l : list(int)) : int = switch(l) function sum(l : list(int)) : int = foldl ((a, b) => a + b, 0, l)
[] => 0
h::t => h + sum(t) function product(l : list(int)) : int = foldl((a, b) => a * b, 1, l)
function product(l : list(int)) : int = switch(l)
[] => 1
h::t => h * sum(t)
/** Zips two list by applying bimapping function on respective elements. /** Zips two list by applying bimapping function on respective elements.
* Drops the tail of the longer list. * Drops longer tail.
*/ */
private function zip_with( f : ('a, 'b) => 'c function zip_with(f : ('a, 'b) => 'c, l1 : list('a), l2 : list('b)) : list('c) = zip_with_(f, l1, l2, [])
private function zip_with_( f : ('a, 'b) => 'c
, l1 : list('a) , l1 : list('a)
, l2 : list('b) , l2 : list('b)
, acc : list('c)
) : list('c) = switch ((l1, l2)) ) : list('c) = switch ((l1, l2))
(h1::t1, h2::t2) => f(h1, h2)::zip_with(f, t1, t2) (h1::t1, h2::t2) => zip_with_(f, t1, t2, f(h1, h2)::acc)
_ => [] _ => reverse(acc)
/** Zips two lists into list of pairs. /** Zips two lists into list of pairs. Drops longer tail.
* Drops the tail of the longer list.
*/ */
function zip(l1 : list('a), l2 : list('b)) : list('a * 'b) = zip_with((a, b) => (a, b), l1, l2) function zip(l1 : list('a), l2 : list('b)) : list('a * 'b) = zip_with((a, b) => (a, b), l1, l2)
function unzip(l : list('a * 'b)) : (list('a) * list('b)) = switch(l) function unzip(l : list('a * 'b)) : list('a) * list('b) = unzip_(l, [], [])
[] => ([], []) private function unzip_( l : list('a * 'b)
(h1, h2)::t => , acc_l : list('a)
let (t1, t2) = unzip(t) , acc_r : list('b)
(h1::t1, h2::t2) ) : (list('a) * list('b)) = switch(l)
[] => (reverse(acc_l), reverse(acc_r))
(left, right)::t => unzip_(t, left::acc_l, right::acc_r)
/** Merges two sorted lists using `lt` comparator /** Merges two sorted lists using `lt` comparator
@@ -303,14 +291,17 @@ namespace List =
/** Puts `delim` between every two members of the list /** Puts `delim` between every two members of the list
*/ */
function intersperse(delim : 'a, l : list('a)) : list('a) = switch(l) function intersperse(delim : 'a, l : list('a)) : list('a) = intersperse_(delim, l, [])
[] => [] private function intersperse_(delim : 'a, l : list('a), acc : list('a)) : list('a) = switch(l)
[e] => [e] [] => reverse(acc)
h::t => h::delim::intersperse(delim, t) [e] => reverse(e::acc)
h::t => intersperse_(delim, t, delim::h::acc)
/** Effectively a zip with an infinite sequence of natural numbers /** Effectively a zip with an infinite sequence of natural numbers
*/ */
function enumerate(l : list('a)) : list(int * 'a) = enumerate_(l, 0) function enumerate(l : list('a)) : list(int * 'a) = enumerate_(l, 0, [])
private function enumerate_(l : list('a), n : int) : list(int * 'a) = switch(l) private function enumerate_(l : list('a), n : int, acc : list(int * 'a)) : list(int * 'a) = switch(l)
[] => [] [] => reverse(acc)
h::t => (n, h)::enumerate_(t, n + 1) h::t => enumerate_(t, n + 1, (n, h)::acc)
+11 -17
View File
@@ -26,12 +26,6 @@ namespace Option =
None => abort("Forced None value") None => abort("Forced None value")
Some(x) => x Some(x) => x
/** Assume it is `Some` with custom error message
*/
function force_msg(o : option('a), err : string) : 'a = switch(o)
None => abort(err)
Some(x) => x
function contains(e : 'a, o : option('a)) = o == Some(e) function contains(e : 'a, o : option('a)) = o == Some(e)
function on_elem(o : option('a), f : 'a => unit) : unit = match((), f, o) function on_elem(o : option('a), f : 'a => unit) : unit = match((), f, o)
@@ -75,21 +69,20 @@ namespace Option =
/** Turns list of options into a list of elements that are under `Some`s. /** Turns list of options into a list of elements that are under `Some`s.
* Safe. * Safe.
*/ */
function filter_options(l : list(option('a))) : list('a) = switch(l) function filter_options(l : list(option('a))) : list('a) = filter_options_(l, [])
[] => [] private function filter_options_(l : list (option('a)), acc : list('a)) : list('a) = switch(l)
None::t => filter_options(t) [] => List.reverse(acc)
Some(x)::t => x::filter_options(t) None::t => filter_options_(t, acc)
Some(x)::t => filter_options_(t, x::acc)
/** Just like `filter_options` but requires all elements to be `Some` and returns /** Just like `filter_options` but requires all elements to be `Some` and returns
* None if any of them is not * None if any of them is not
*/ */
function seq_options(l : list (option('a))) : option (list('a)) = switch(l) function seq_options(l : list (option('a))) : option (list('a)) = seq_options_(l, [])
[] => Some([]) private function seq_options_(l : list (option('a)), acc : list('a)) : option(list('a)) = switch(l)
None::_ => None [] => Some(List.reverse(acc))
Some(x)::t => switch(seq_options(t)) None::t => None
None => None Some(x)::t => seq_options_(t, x::acc)
Some(st) => Some(x::st)
/** Choose `Some` out of two if possible /** Choose `Some` out of two if possible
*/ */
@@ -102,3 +95,4 @@ namespace Option =
[] => None [] => None
None::t => choose_first(t) None::t => choose_first(t)
Some(x)::_ => Some(x) Some(x)::_ => Some(x)
-51
View File
@@ -1,51 +0,0 @@
include "List.aes"
include "Option.aes"
include "Pair.aes"
namespace Set =
record set('a) = { to_map : map('a, unit) }
function new() : set('a) =
{ to_map = {} }
function member(e : 'a, s : set('a)) : bool =
Map.member(e, s.to_map)
function insert(e : 'a, s : set('a)) : set('a) =
{ to_map = s.to_map{[e] = ()} }
function delete(e : 'a, s : set('a)) : set('a) =
{ to_map = Map.delete(e, s.to_map) }
function size(s : set('a)) : int =
Map.size(s.to_map)
function to_list(s : set('a)) : list('a) =
List.map(Pair.fst, Map.to_list(s.to_map))
function from_list(l : list('a)) : set('a) =
{ to_map = Map.from_list(List.map((x) => (x, ()), l)) }
function filter(p : 'a => bool, s : set('a)) : set('a) =
from_list(List.filter(p, to_list(s)))
function fold(f : ('a, 'b) => 'b, acc : 'b, s : set('a)) : 'b =
List.foldr(f, acc, to_list(s))
function subtract(s1 : set('a), s2 : set('a)) : set('a) =
filter((x) => !member(x, s2), s1)
function intersection(s1 : set('a), s2 : set('a)) : set('a) =
filter((x) => member(x, s2), s1)
function intersection_list(sets : list(set('a))) : set('a) =
List.foldr(
intersection,
Option.default(new(), List.first(sets)),
Option.default([], List.tail(sets)))
function union(s1 : set('a), s2 : set('a)) : set('a) =
from_list(to_list(s1) ++ to_list(s2))
function union_list(sets : list(set('a))) : set('a) =
List.foldr(union, new(), sets)
-117
View File
@@ -1,117 +0,0 @@
include "List.aes"
namespace String =
// Computes the SHA3/Keccak hash of the string
function sha3(s : string) : hash = StringInternal.sha3(s)
// Computes the SHA256 hash of the string.
function sha256(s : string) : hash = StringInternal.sha256(s)
// Computes the Blake2B hash of the string.
function blake2b(s : string) : hash = StringInternal.blake2b(s)
// The length of a string - equivalent to List.lenght(to_list(s))
function length(s : string) : int = StringInternal.length(s)
// Concatenates `s1` and `s2`.
function concat(s1 : string, s2 : string) : string = StringInternal.concat(s1, s2)
// Concatenates a list of strings.
function
concats : (list(string)) => string
concats([]) = ""
concats(s :: ss) = List.foldl(StringInternal.concat, s, ss)
// Converts a `string` to a list of `char` - the code points are normalized, but
// composite characters are possibly converted to multiple `char`s.
function from_list(cs : list(char)) : string = StringInternal.from_list(cs)
// Converts a list of characters into a normalized UTF-8 string.
function to_list(s : string) : list(char) = StringInternal.to_list(s)
// Converts a string to lowercase.
function to_lower(s : string) = StringInternal.to_lower(s)
// Converts a string to uppercase.
function to_upper(s : string) = StringInternal.to_upper(s)
// Splits a string at (zero-based) index `ix`.
function split(i : int, s : string) : string * string =
let cs = StringInternal.to_list(s)
(StringInternal.from_list(List.take(i, cs)), StringInternal.from_list(List.drop(i, cs)))
// Returns the character/codepoint at (zero-based) index `ix`.
function at(ix : int, s : string) =
switch(List.drop(ix, StringInternal.to_list(s)))
[] => None
x :: _ => Some(x)
// Searches for `pat` in `str`, returning `Some(ix)` if `pat` is a substring
// of `str` starting at position `ix`, otherwise returns `None`.
function contains(str : string, substr : string) : option(int) =
if(substr == "") Some(0)
else
contains_(0, StringInternal.to_list(str), StringInternal.to_list(substr))
// Splits `s` into tokens, `pat` is the divider of tokens.
function tokens(s : string, pat : string) =
require(pat != "", "String.tokens: empty pattern")
tokens_(StringInternal.to_list(pat), StringInternal.to_list(s), [])
// Converts a decimal ("123", "-253") or a hexadecimal ("0xa2f", "-0xBBB") string
// into an integer. If the string doesn't contain a valid number `None` is returned.
function to_int(s : string) : option(int) =
let s = StringInternal.to_list(s)
switch(is_prefix(['-'], s))
None => to_int_pos(s)
Some(s) => switch(to_int_pos(s))
None => None
Some(x) => Some(-x)
// Private helper functions below
private function to_int_pos(s : list(char)) =
switch(is_prefix(['0', 'x'], s))
None =>
to_int_(s, ch_to_int_10, 0, 10)
Some(s) =>
to_int_(s, ch_to_int_16, 0, 16)
private function
tokens_(_, [], acc) = [StringInternal.from_list(List.reverse(acc))]
tokens_(pat, str, acc) =
switch(is_prefix(pat, str))
Some(str') =>
StringInternal.from_list(List.reverse(acc)) :: tokens_(pat, str', [])
None =>
let c :: cs = str
tokens_(pat, cs, c :: acc)
private function
contains_(_, [], _) = None
contains_(ix, str, substr) =
switch(is_prefix(substr, str))
None =>
let _ :: str = str
contains_(ix + 1, str, substr)
Some(_) =>
Some(ix)
private function
is_prefix([], ys) = Some(ys)
is_prefix(_, []) = None
is_prefix(x :: xs, y :: ys) =
if(x == y) is_prefix(xs, ys)
else None
private function
to_int_([], _, x, _) = Some(x)
to_int_(i :: is, value, x, b) =
switch(value(i))
None => None
Some(i) => to_int_(is, value, x * b + i, b)
private function ch_to_int_10(c) =
let c = Char.to_int(c)
if(c >= 48 && c =< 57) Some(c - 48)
else None
private function ch_to_int_16(c) =
let c = Char.to_int(c)
if(c >= 48 && c =< 57) Some(c - 48)
elif(c >= 65 && c =< 70) Some(c - 55)
elif(c >= 97 && c =< 102) Some(c - 87)
else None
+2 -2
View File
@@ -2,7 +2,7 @@
{erl_opts, [debug_info]}. {erl_opts, [debug_info]}.
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"05dfd7f"}}} {deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"4f4d6d3"}}}
, {getopt, "1.0.1"} , {getopt, "1.0.1"}
, {eblake2, "1.0.0"} , {eblake2, "1.0.0"}
, {jsx, {git, "https://github.com/talentdeficit/jsx.git", , {jsx, {git, "https://github.com/talentdeficit/jsx.git",
@@ -15,7 +15,7 @@
{base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]} {base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]}
]}. ]}.
{relx, [{release, {aesophia, "6.1.0"}, {relx, [{release, {aesophia, "4.3.0"},
[aesophia, aebytecode, getopt]}, [aesophia, aebytecode, getopt]},
{dev_mode, true}, {dev_mode, true},
+1 -1
View File
@@ -1,7 +1,7 @@
{"1.1.0", {"1.1.0",
[{<<"aebytecode">>, [{<<"aebytecode">>,
{git,"https://github.com/aeternity/aebytecode.git", {git,"https://github.com/aeternity/aebytecode.git",
{ref,"05dfd7ffc7fb1e07ecc0b1e516da571f56d7dc8f"}}, {ref,"4f4d6d30cd2c46b3830454d650a424d513f69134"}},
0}, 0},
{<<"aeserialization">>, {<<"aeserialization">>,
{git,"https://github.com/aeternity/aeserialization.git", {git,"https://github.com/aeternity/aeserialization.git",
+9 -18
View File
@@ -21,8 +21,6 @@
, json_encode_expr/1 , json_encode_expr/1
, json_encode_type/1]). , json_encode_type/1]).
-include("aeso_utils.hrl").
-type aci_type() :: json | string. -type aci_type() :: json | string.
-type json() :: jsx:json_term(). -type json() :: jsx:json_term().
-type json_text() :: binary(). -type json_text() :: binary().
@@ -70,7 +68,9 @@ do_contract_interface(Type, Contract, Options) when is_binary(Contract) ->
do_contract_interface(Type, ContractString, Options) -> do_contract_interface(Type, ContractString, Options) ->
try try
Ast = aeso_compiler:parse(ContractString, Options), Ast = aeso_compiler:parse(ContractString, Options),
%% io:format("~p\n", [Ast]),
{TypedAst, _} = aeso_ast_infer_types:infer(Ast, [dont_unfold | Options]), {TypedAst, _} = aeso_ast_infer_types:infer(Ast, [dont_unfold | Options]),
%% io:format("~p\n", [TypedAst]),
from_typed_ast(Type, TypedAst) from_typed_ast(Type, TypedAst)
catch catch
throw:{error, Errors} -> {error, Errors} throw:{error, Errors} -> {error, Errors}
@@ -83,7 +83,7 @@ from_typed_ast(Type, TypedAst) ->
string -> do_render_aci_json(JArray) string -> do_render_aci_json(JArray)
end. end.
encode_contract(Contract = {Head, _, {con, _, Name}, _}) when ?IS_CONTRACT_HEAD(Head) -> encode_contract(Contract = {contract, _, {con, _, Name}, _}) ->
C0 = #{name => encode_name(Name)}, C0 = #{name => encode_name(Name)},
Tdefs0 = [ encode_typedef(T) || T <- sort_decls(contract_types(Contract)) ], Tdefs0 = [ encode_typedef(T) || T <- sort_decls(contract_types(Contract)) ],
@@ -107,7 +107,7 @@ encode_contract(Contract = {Head, _, {con, _, Name}, _}) when ?IS_CONTRACT_HEAD(
|| F <- sort_decls(contract_funcs(Contract)), || F <- sort_decls(contract_funcs(Contract)),
is_entrypoint(F) ], is_entrypoint(F) ],
#{contract => C3#{kind => Head, functions => Fdefs, payable => is_payable(Contract)}}; #{contract => C3#{functions => Fdefs, payable => is_payable(Contract)}};
encode_contract(Namespace = {namespace, _, {con, _, Name}, _}) -> encode_contract(Namespace = {namespace, _, {con, _, Name}, _}) ->
Tdefs = [ encode_typedef(T) || T <- sort_decls(contract_types(Namespace)) ], Tdefs = [ encode_typedef(T) || T <- sort_decls(contract_types(Namespace)) ],
#{namespace => #{name => encode_name(Name), #{namespace => #{name => encode_name(Name),
@@ -232,19 +232,13 @@ do_render_aci_json(Json) ->
{ok, list_to_binary(string:join(DecodedContracts, "\n"))}. {ok, list_to_binary(string:join(DecodedContracts, "\n"))}.
decode_contract(#{contract := #{name := Name, decode_contract(#{contract := #{name := Name,
kind := Kind,
payable := Payable, payable := Payable,
type_defs := Ts0, type_defs := Ts0,
functions := Fs} = C}) -> functions := Fs} = C}) ->
MkTDef = fun(N, T) -> #{name => N, vars => [], typedef => T} end, MkTDef = fun(N, T) -> #{name => N, vars => [], typedef => T} end,
Ts = [ MkTDef(<<"state">>, maps:get(state, C)) || maps:is_key(state, C) ] ++ Ts = [ MkTDef(<<"state">>, maps:get(state, C)) || maps:is_key(state, C) ] ++
[ MkTDef(<<"event">>, maps:get(event, C)) || maps:is_key(event, C) ] ++ Ts0, [ MkTDef(<<"event">>, maps:get(event, C)) || maps:is_key(event, C) ] ++ Ts0,
[payable(Payable), case Kind of [payable(Payable), "contract ", io_lib:format("~s", [Name])," =\n",
contract_main -> "main contract ";
contract_child -> "contract ";
contract_interface -> "contract interface "
end,
io_lib:format("~s", [Name])," =\n",
decode_tdefs(Ts), decode_funcs(Fs)]; decode_tdefs(Ts), decode_funcs(Fs)];
decode_contract(#{namespace := #{name := Name, type_defs := Ts}}) when Ts /= [] -> decode_contract(#{namespace := #{name := Name, type_defs := Ts}}) when Ts /= [] ->
["namespace ", io_lib:format("~s", [Name])," =\n", ["namespace ", io_lib:format("~s", [Name])," =\n",
@@ -254,8 +248,8 @@ decode_contract(_) -> [].
decode_funcs(Fs) -> [ decode_func(F) || F <- Fs ]. decode_funcs(Fs) -> [ decode_func(F) || F <- Fs ].
%% decode_func(#{name := init}) -> []; %% decode_func(#{name := init}) -> [];
decode_func(#{name := Name, stateful:= Stateful, payable := Payable, arguments := As, returns := T}) -> decode_func(#{name := Name, payable := Payable, arguments := As, returns := T}) ->
[" ", payable(Payable), stateful(Stateful), "entrypoint ", io_lib:format("~s", [Name]), " : ", [" ", payable(Payable), "entrypoint ", io_lib:format("~s", [Name]), " : ",
decode_args(As), " => ", decode_type(T), $\n]. decode_args(As), " => ", decode_type(T), $\n].
decode_args(As) -> decode_args(As) ->
@@ -336,15 +330,12 @@ decode_tvar(#{name := N}) -> io_lib:format("~s", [N]).
payable(true) -> "payable "; payable(true) -> "payable ";
payable(false) -> "". payable(false) -> "".
stateful(true) -> "stateful ";
stateful(false) -> "".
%% #contract{Ann, Con, [Declarations]}. %% #contract{Ann, Con, [Declarations]}.
contract_funcs({C, _, _, Decls}) when ?IS_CONTRACT_HEAD(C); C == namespace -> contract_funcs({C, _, _, Decls}) when C == contract; C == namespace ->
[ D || D <- Decls, is_fun(D)]. [ D || D <- Decls, is_fun(D)].
contract_types({C, _, _, Decls}) when ?IS_CONTRACT_HEAD(C); C == namespace -> contract_types({C, _, _, Decls}) when C == contract; C == namespace ->
[ D || D <- Decls, is_type(D) ]. [ D || D <- Decls, is_type(D) ].
is_fun({letfun, _, _, _, _, _}) -> true; is_fun({letfun, _, _, _, _, _}) -> true;
File diff suppressed because it is too large Load Diff
+95 -276
View File
@@ -12,8 +12,6 @@
-export([ast_to_fcode/2, format_fexpr/1]). -export([ast_to_fcode/2, format_fexpr/1]).
-export_type([fcode/0, fexpr/0, fun_def/0]). -export_type([fcode/0, fexpr/0, fun_def/0]).
-include("aeso_utils.hrl").
%% -- Type definitions ------------------------------------------------------- %% -- Type definitions -------------------------------------------------------
-type option() :: term(). -type option() :: term().
@@ -38,14 +36,7 @@
bits_intersection | bits_union | bits_difference | bits_intersection | bits_union | bits_difference |
contract_to_address | address_to_contract | crypto_verify_sig | crypto_verify_sig_secp256k1 | contract_to_address | address_to_contract | crypto_verify_sig | crypto_verify_sig_secp256k1 |
crypto_sha3 | crypto_sha256 | crypto_blake2b | crypto_sha3 | crypto_sha256 | crypto_blake2b |
crypto_ecverify_secp256k1 | crypto_ecrecover_secp256k1 | crypto_ecverify_secp256k1 | crypto_ecrecover_secp256k1.
mcl_bls12_381_g1_neg | mcl_bls12_381_g1_norm | mcl_bls12_381_g1_valid |
mcl_bls12_381_g1_is_zero | mcl_bls12_381_g1_add | mcl_bls12_381_g1_mul |
mcl_bls12_381_g2_neg | mcl_bls12_381_g2_norm | mcl_bls12_381_g2_valid |
mcl_bls12_381_g2_is_zero | mcl_bls12_381_g2_add | mcl_bls12_381_g2_mul |
mcl_bls12_381_gt_inv | mcl_bls12_381_gt_add | mcl_bls12_381_gt_mul | mcl_bls12_381_gt_pow |
mcl_bls12_381_gt_is_one | mcl_bls12_381_pairing | mcl_bls12_381_miller_loop | mcl_bls12_381_final_exp |
mcl_bls12_381_int_to_fr | mcl_bls12_381_int_to_fp | mcl_bls12_381_fr_to_int | mcl_bls12_381_fp_to_int.
-type flit() :: {int, integer()} -type flit() :: {int, integer()}
| {string, binary()} | {string, binary()}
@@ -55,7 +46,6 @@
| {oracle_pubkey, binary()} | {oracle_pubkey, binary()}
| {oracle_query_id, binary()} | {oracle_query_id, binary()}
| {bool, false | true} | {bool, false | true}
| {contract_code, string()} %% for CREATE, by name
| {typerep, ftype()}. | {typerep, ftype()}.
-type fexpr() :: {lit, flit()} -type fexpr() :: {lit, flit()}
@@ -96,8 +86,7 @@
| nil | nil
| {'::', var_name(), var_name()} | {'::', var_name(), var_name()}
| {con, arities(), tag(), [var_name()]} | {con, arities(), tag(), [var_name()]}
| {tuple, [var_name()]} | {tuple, [var_name()]}.
| {assign, var_name(), var_name()}.
-type ftype() :: integer -type ftype() :: integer
| boolean | boolean
@@ -140,27 +129,24 @@
-type type_env() :: #{ sophia_name() => type_def() }. -type type_env() :: #{ sophia_name() => type_def() }.
-type fun_env() :: #{ sophia_name() => {fun_name(), non_neg_integer()} }. -type fun_env() :: #{ sophia_name() => {fun_name(), non_neg_integer()} }.
-type con_env() :: #{ sophia_name() => con_tag() }. -type con_env() :: #{ sophia_name() => con_tag() }.
-type child_con_env() :: #{sophia_name() => fcode()}. -type builtins() :: #{ sophia_name() => {builtin(), non_neg_integer() | none} }.
-type builtins() :: #{ sophia_name() => {builtin(), non_neg_integer() | none | variable} }.
-type context() :: {contract_def, string()} -type context() :: {main_contract, string()}
| {namespace, string()} | {namespace, string()}
| {abstract_contract, string()}. | {abstract_contract, string()}.
-type state_layout() :: {tuple, [state_layout()]} | {reg, state_reg()}. -type state_layout() :: {tuple, [state_layout()]} | {reg, state_reg()}.
-type env() :: #{ type_env := type_env(), -type env() :: #{ type_env := type_env(),
fun_env := fun_env(), fun_env := fun_env(),
con_env := con_env(), con_env := con_env(),
child_con_env := child_con_env(), event_type => aeso_syntax:typedef(),
event_type => aeso_syntax:typedef(), builtins := builtins(),
builtins := builtins(), options := [option()],
options := [option()], state_layout => state_layout(),
state_layout => state_layout(), context => context(),
context => context(), vars => [var_name()],
vars => [var_name()], functions := #{ fun_name() => fun_def() } }.
functions := #{ fun_name() => fun_def() }
}.
-define(HASH_BYTES, 32). -define(HASH_BYTES, 32).
@@ -168,74 +154,33 @@
%% Main entrypoint. Takes typed syntax produced by aeso_ast_infer_types:infer/1,2 %% Main entrypoint. Takes typed syntax produced by aeso_ast_infer_types:infer/1,2
%% and produces Fate intermediate code. %% and produces Fate intermediate code.
-spec ast_to_fcode(aeso_syntax:ast(), [option()]) -> {env(), fcode()}. -spec ast_to_fcode(aeso_syntax:ast(), [option()]) -> fcode().
ast_to_fcode(Code, Options) -> ast_to_fcode(Code, Options) ->
init_fresh_names(),
{Env1, FCode1} = to_fcode(init_env(Options), Code),
FCode2 = optimize(FCode1, Options),
Env2 = Env1#{ child_con_env :=
maps:map(
fun (_, FC) -> optimize(FC, Options) end,
maps:get(child_con_env, Env1)
)},
clear_fresh_names(),
{Env2, FCode2}.
optimize(FCode1, Options) ->
Verbose = lists:member(pp_fcode, Options), Verbose = lists:member(pp_fcode, Options),
init_fresh_names(),
FCode1 = to_fcode(init_env(Options), Code),
[io:format("-- Before lambda lifting --\n~s\n\n", [format_fcode(FCode1)]) || Verbose], [io:format("-- Before lambda lifting --\n~s\n\n", [format_fcode(FCode1)]) || Verbose],
FCode2 = optimize_fcode(FCode1), FCode2 = optimize_fcode(FCode1),
[ io:format("-- After optimization --\n~s\n\n", [format_fcode(FCode2)]) || Verbose, FCode2 /= FCode1 ], [ io:format("-- After optimization --\n~s\n\n", [format_fcode(FCode2)]) || Verbose, FCode2 /= FCode1 ],
FCode3 = lambda_lift(FCode2), FCode3 = lambda_lift(FCode2),
[ io:format("-- After lambda lifting --\n~s\n\n", [format_fcode(FCode3)]) || Verbose, FCode3 /= FCode2 ], [ io:format("-- After lambda lifting --\n~s\n\n", [format_fcode(FCode3)]) || Verbose, FCode3 /= FCode2 ],
clear_fresh_names(),
FCode3. FCode3.
%% -- Environment ------------------------------------------------------------ %% -- Environment ------------------------------------------------------------
-spec init_env([option()]) -> env(). -spec init_env([option()]) -> env().
init_env(Options) -> init_env(Options) ->
ChainTxArities = [3, 0, 0, 0, 0, 0, 1, 1, 1, 2, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 0],
#{ type_env => init_type_env(), #{ type_env => init_type_env(),
fun_env => #{}, fun_env => #{},
builtins => builtins(), builtins => builtins(),
child_con_env => #{},
con_env => #{["None"] => #con_tag{ tag = 0, arities = [0, 1] }, con_env => #{["None"] => #con_tag{ tag = 0, arities = [0, 1] },
["Some"] => #con_tag{ tag = 1, arities = [0, 1] }, ["Some"] => #con_tag{ tag = 1, arities = [0, 1] },
["RelativeTTL"] => #con_tag{ tag = 0, arities = [1, 1] }, ["RelativeTTL"] => #con_tag{ tag = 0, arities = [1, 1] },
["FixedTTL"] => #con_tag{ tag = 1, arities = [1, 1] }, ["FixedTTL"] => #con_tag{ tag = 1, arities = [1, 1] }
["AENS", "AccountPt"] => #con_tag{ tag = 0, arities = [1, 1, 1, 1] },
["AENS", "OraclePt"] => #con_tag{ tag = 1, arities = [1, 1, 1, 1] },
["AENS", "ContractPt"] => #con_tag{ tag = 2, arities = [1, 1, 1, 1] },
["AENS", "ChannelPt"] => #con_tag{ tag = 3, arities = [1, 1, 1, 1] },
["AENS", "Name"] => #con_tag{ tag = 0, arities = [3] },
["Chain", "GAMetaTx"] => #con_tag{ tag = 0, arities = [2] },
["Chain", "PayingForTx"] => #con_tag{ tag = 0, arities = [2] },
["Chain", "SpendTx"] => #con_tag{ tag = 0, arities = ChainTxArities },
["Chain", "OracleRegisterTx"] => #con_tag{ tag = 1, arities = ChainTxArities },
["Chain", "OracleQueryTx"] => #con_tag{ tag = 2, arities = ChainTxArities },
["Chain", "OracleResponseTx"] => #con_tag{ tag = 3, arities = ChainTxArities },
["Chain", "OracleExtendTx"] => #con_tag{ tag = 4, arities = ChainTxArities },
["Chain", "NamePreclaimTx"] => #con_tag{ tag = 5, arities = ChainTxArities },
["Chain", "NameClaimTx"] => #con_tag{ tag = 6, arities = ChainTxArities },
["Chain", "NameUpdateTx"] => #con_tag{ tag = 7, arities = ChainTxArities },
["Chain", "NameRevokeTx"] => #con_tag{ tag = 8, arities = ChainTxArities },
["Chain", "NameTransferTx"] => #con_tag{ tag = 9, arities = ChainTxArities },
["Chain", "ChannelCreateTx"] => #con_tag{ tag = 10, arities = ChainTxArities },
["Chain", "ChannelDepositTx"] => #con_tag{ tag = 11, arities = ChainTxArities },
["Chain", "ChannelWithdrawTx"] => #con_tag{ tag = 12, arities = ChainTxArities },
["Chain", "ChannelForceProgressTx"] => #con_tag{ tag = 13, arities = ChainTxArities },
["Chain", "ChannelCloseMutualTx"] => #con_tag{ tag = 14, arities = ChainTxArities },
["Chain", "ChannelCloseSoloTx"] => #con_tag{ tag = 15, arities = ChainTxArities },
["Chain", "ChannelSlashTx"] => #con_tag{ tag = 16, arities = ChainTxArities },
["Chain", "ChannelSettleTx"] => #con_tag{ tag = 17, arities = ChainTxArities },
["Chain", "ChannelSnapshotSoloTx"] => #con_tag{ tag = 18, arities = ChainTxArities },
["Chain", "ContractCreateTx"] => #con_tag{ tag = 19, arities = ChainTxArities },
["Chain", "ContractCallTx"] => #con_tag{ tag = 20, arities = ChainTxArities },
["Chain", "GAAttachTx"] => #con_tag{ tag = 21, arities = ChainTxArities }
}, },
options => Options, options => Options,
functions => #{} functions => #{} }.
}.
-spec builtins() -> builtins(). -spec builtins() -> builtins().
builtins() -> builtins() ->
@@ -245,29 +190,22 @@ builtins() ->
Scopes = [{[], [{"abort", 1}, {"require", 2}]}, Scopes = [{[], [{"abort", 1}, {"require", 2}]},
{["Chain"], [{"spend", 2}, {"balance", 1}, {"block_hash", 1}, {"coinbase", none}, {["Chain"], [{"spend", 2}, {"balance", 1}, {"block_hash", 1}, {"coinbase", none},
{"timestamp", none}, {"block_height", none}, {"difficulty", none}, {"timestamp", none}, {"block_height", none}, {"difficulty", none},
{"gas_limit", none}, {"bytecode_hash", 1}, {"create", variable}, {"clone", variable}]}, {"gas_limit", none}]},
{["Contract"], [{"address", none}, {"balance", none}, {"creator", none}]}, {["Contract"], [{"address", none}, {"balance", none}, {"creator", none}]},
{["Call"], [{"origin", none}, {"caller", none}, {"value", none}, {"gas_price", none}, {"fee", none}, {["Call"], [{"origin", none}, {"caller", none}, {"value", none}, {"gas_price", none},
{"gas_left", 0}]}, {"gas_left", 0}]},
{["Oracle"], [{"register", 4}, {"expiry", 1}, {"query_fee", 1}, {"query", 5}, {"get_question", 2}, {["Oracle"], [{"register", 4}, {"query_fee", 1}, {"query", 5}, {"get_question", 2},
{"respond", 4}, {"extend", 3}, {"get_answer", 2}, {"respond", 4}, {"extend", 3}, {"get_answer", 2},
{"check", 1}, {"check_query", 2}]}, {"check", 1}, {"check_query", 2}]},
{["AENS"], [{"resolve", 2}, {"preclaim", 3}, {"claim", 5}, {"transfer", 4}, {["AENS"], [{"resolve", 2}, {"preclaim", 3}, {"claim", 5}, {"transfer", 4},
{"revoke", 3}, {"update", 6}, {"lookup", 1}]}, {"revoke", 3}]},
{["Map"], [{"from_list", 1}, {"to_list", 1}, {"lookup", 2}, {["Map"], [{"from_list", 1}, {"to_list", 1}, {"lookup", 2},
{"lookup_default", 3}, {"delete", 2}, {"member", 2}, {"size", 1}]}, {"lookup_default", 3}, {"delete", 2}, {"member", 2}, {"size", 1}]},
{["Crypto"], [{"verify_sig", 3}, {"verify_sig_secp256k1", 3}, {["Crypto"], [{"verify_sig", 3}, {"verify_sig_secp256k1", 3},
{"ecverify_secp256k1", 3}, {"ecrecover_secp256k1", 2}, {"ecverify_secp256k1", 3}, {"ecrecover_secp256k1", 2},
{"sha3", 1}, {"sha256", 1}, {"blake2b", 1}]}, {"sha3", 1}, {"sha256", 1}, {"blake2b", 1}]},
{["MCL_BLS12_381"], [{"g1_neg", 1}, {"g1_norm", 1}, {"g1_valid", 1}, {"g1_is_zero", 1}, {"g1_add", 2}, {"g1_mul", 2}, {["Auth"], [{"tx_hash", none}]},
{"g2_neg", 1}, {"g2_norm", 1}, {"g2_valid", 1}, {"g2_is_zero", 1}, {"g2_add", 2}, {"g2_mul", 2}, {["String"], [{"length", 1}, {"concat", 2}, {"sha3", 1}, {"sha256", 1}, {"blake2b", 1}]},
{"gt_inv", 1}, {"gt_add", 2}, {"gt_mul", 2}, {"gt_pow", 2}, {"gt_is_one", 1},
{"pairing", 2}, {"miller_loop", 2}, {"final_exp", 1},
{"int_to_fr", 1}, {"int_to_fp", 1}, {"fr_to_int", 1}, {"fp_to_int", 1}]},
{["StringInternal"], [{"length", 1}, {"concat", 2}, {"to_list", 1}, {"from_list", 1},
{"sha3", 1}, {"sha256", 1}, {"blake2b", 1}, {"to_lower", 1}, {"to_upper", 1}]},
{["Char"], [{"to_int", 1}, {"from_int", 1}]},
{["Auth"], [{"tx_hash", none}, {"tx", none}]},
{["Bits"], [{"set", 2}, {"clear", 2}, {"test", 2}, {"sum", 1}, {"intersection", 2}, {["Bits"], [{"set", 2}, {"clear", 2}, {"test", 2}, {"sum", 1}, {"intersection", 2},
{"union", 2}, {"difference", 2}, {"none", none}, {"all", none}]}, {"union", 2}, {"difference", 2}, {"none", none}, {"all", none}]},
{["Bytes"], [{"to_int", 1}, {"to_str", 1}, {"concat", 2}, {"split", 1}]}, {["Bytes"], [{"to_int", 1}, {"to_str", 1}, {"concat", 2}, {"split", 1}]},
@@ -286,32 +224,20 @@ state_layout(Env) -> maps:get(state_layout, Env, {reg, 1}).
-spec init_type_env() -> type_env(). -spec init_type_env() -> type_env().
init_type_env() -> init_type_env() ->
BaseTx = {variant, [[address, integer, string], [], [], [], [], [], [string], #{ ["int"] => ?type(integer),
[{bytes, 32}], [{bytes, 32}], [address, {bytes, 32}], [address], ["bool"] => ?type(boolean),
[address, integer], [address, integer], [address], ["bits"] => ?type(bits),
[address], [address], [address], [address], [address], ["char"] => ?type(integer),
[integer], [address, integer], []]}, ["string"] => ?type(string),
#{ ["int"] => ?type(integer), ["address"] => ?type(address),
["bool"] => ?type(boolean), ["hash"] => ?type(hash),
["bits"] => ?type(bits), ["signature"] => ?type(signature),
["char"] => ?type(integer), ["oracle"] => ?type(Q, R, {oracle, Q, R}),
["string"] => ?type(string), ["oracle_query"] => ?type(_, _, oracle_query),
["address"] => ?type(address), ["list"] => ?type(T, {list, T}),
["hash"] => ?type(hash), ["map"] => ?type(K, V, {map, K, V}),
["signature"] => ?type(signature), ["option"] => ?type(T, {variant, [[], [T]]}),
["oracle"] => ?type(Q, R, {oracle, Q, R}), ["Chain", "ttl"] => ?type({variant, [[integer], [integer]]})
["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]]}),
["Chain", "ttl"] => ?type({variant, [[integer], [integer]]}),
["AENS", "pointee"] => ?type({variant, [[address], [address], [address], [address]]}),
["AENS", "name"] => ?type({variant, [[address, {variant, [[integer], [integer]]}, {map, string, {variant, [[address], [address], [address], [address]]}}]]}),
["Chain", "ga_meta_tx"] => ?type({variant, [[address, integer]]}),
["Chain", "paying_for_tx"] => ?type({variant, [[address, integer]]}),
["Chain", "base_tx"] => ?type(BaseTx),
["MCL_BLS12_381", "fr"] => ?type({bytes, 32}),
["MCL_BLS12_381", "fp"] => ?type({bytes, 48})
}. }.
is_no_code(Env) -> is_no_code(Env) ->
@@ -325,45 +251,31 @@ get_option(Opt, Env, Default) ->
%% -- Compilation ------------------------------------------------------------ %% -- Compilation ------------------------------------------------------------
-spec to_fcode(env(), aeso_syntax:ast()) -> {env(), fcode()}. -spec to_fcode(env(), aeso_syntax:ast()) -> fcode().
to_fcode(Env, [{Contract, Attrs, Con = {con, _, Name}, Decls}|Rest]) to_fcode(Env, [{contract, Attrs, MainCon = {con, _, Main}, Decls}]) ->
when ?IS_CONTRACT_HEAD(Contract) -> #{ builtins := Builtins } = Env,
case Contract =:= contract_interface of MainEnv = Env#{ context => {main_contract, Main},
false -> builtins => Builtins#{[Main, "state"] => {get_state, none},
#{ builtins := Builtins } = Env, [Main, "put"] => {set_state, 1},
ConEnv = maps:remove(state_layout, [Main, "Chain", "event"] => {chain_event, 1}} },
Env#{ context => {contract_def, Name}, #{ functions := Funs } = Env1 =
builtins => Builtins#{[Name, "state"] => {get_state, none}, decls_to_fcode(MainEnv, Decls),
[Name, "put"] => {set_state, 1}, StateType = lookup_type(Env1, [Main, "state"], [], {tuple, []}),
[Name, "Chain", "event"] => {chain_event, 1}} }), EventType = lookup_type(Env1, [Main, "event"], [], none),
#{ functions := PrevFuns } = ConEnv, StateLayout = state_layout(Env1),
#{ functions := Funs } = Env1 = Payable = proplists:get_value(payable, Attrs, false),
decls_to_fcode(ConEnv, Decls), #{ contract_name => Main,
StateType = lookup_type(Env1, [Name, "state"], [], {tuple, []}), state_type => StateType,
EventType = lookup_type(Env1, [Name, "event"], [], none), state_layout => StateLayout,
StateLayout = state_layout(Env1), event_type => EventType,
Payable = proplists:get_value(payable, Attrs, false), payable => Payable,
ConFcode = #{ contract_name => Name, functions => add_init_function(Env1, MainCon, StateType,
state_type => StateType, add_event_function(Env1, EventType, Funs)) };
state_layout => StateLayout, to_fcode(_Env, [NotContract]) ->
event_type => EventType, fcode_error({last_declaration_must_be_contract, NotContract});
payable => Payable, to_fcode(Env, [{contract, _, {con, _, Con}, Decls} | Code]) ->
functions => add_init_function( Env1 = decls_to_fcode(Env#{ context => {abstract_contract, Con} }, Decls),
Env1, Con, StateType, to_fcode(Env1, Code);
add_event_function(Env1, EventType, Funs)) },
case Contract of
contract_main -> [] = Rest, {Env1, ConFcode};
contract_child ->
Env2 = add_child_con(Env1, Name, ConFcode),
Env3 = Env2#{ functions := PrevFuns },
to_fcode(Env3, Rest)
end;
true ->
Env1 = decls_to_fcode(Env#{ context => {abstract_contract, Name} }, Decls),
to_fcode(Env1, Rest)
end;
to_fcode(_Env, [NotMain = {NotMainHead, _ ,_ , _}]) when NotMainHead =/= contract_def ->
fcode_error({last_declaration_must_be_contract_def, NotMain});
to_fcode(Env, [{namespace, _, {con, _, Con}, Decls} | Code]) -> to_fcode(Env, [{namespace, _, {con, _, Con}, Decls} | Code]) ->
Env1 = decls_to_fcode(Env#{ context => {namespace, Con} }, Decls), Env1 = decls_to_fcode(Env#{ context => {namespace, Con} }, Decls),
to_fcode(Env1, Code). to_fcode(Env1, Code).
@@ -373,11 +285,13 @@ decls_to_fcode(Env, Decls) ->
%% First compute mapping from Sophia names to fun_names and add it to the %% First compute mapping from Sophia names to fun_names and add it to the
%% environment. %% environment.
Env1 = add_fun_env(Env, Decls), Env1 = add_fun_env(Env, Decls),
lists:foldl(fun(D, E) -> decl_to_fcode(E, D) lists:foldl(fun(D, E) ->
R = decl_to_fcode(E, D),
R
end, Env1, Decls). end, Env1, Decls).
-spec decl_to_fcode(env(), aeso_syntax:decl()) -> env(). -spec decl_to_fcode(env(), aeso_syntax:decl()) -> env().
decl_to_fcode(Env = #{context := {contract_def, _}}, {fun_decl, _, Id, _}) -> decl_to_fcode(Env = #{context := {main_contract, _}}, {fun_decl, _, Id, _}) ->
case is_no_code(Env) of case is_no_code(Env) of
false -> fcode_error({missing_definition, Id}); false -> fcode_error({missing_definition, Id});
true -> Env true -> Env
@@ -385,7 +299,7 @@ decl_to_fcode(Env = #{context := {contract_def, _}}, {fun_decl, _, Id, _}) ->
decl_to_fcode(Env, {fun_decl, _, _, _}) -> Env; decl_to_fcode(Env, {fun_decl, _, _, _}) -> Env;
decl_to_fcode(Env, {type_def, _Ann, Name, Args, Def}) -> decl_to_fcode(Env, {type_def, _Ann, Name, Args, Def}) ->
typedef_to_fcode(Env, Name, Args, Def); typedef_to_fcode(Env, Name, Args, Def);
decl_to_fcode(Env = #{ functions := Funs }, {letfun, Ann, Id = {id, _, Name}, Args, Ret, [{guarded, _, [], Body}]}) -> decl_to_fcode(Env = #{ functions := Funs }, {letfun, Ann, Id = {id, _, Name}, Args, Ret, Body}) ->
Attrs = get_attributes(Ann), Attrs = get_attributes(Ann),
FName = lookup_fun(Env, qname(Env, Name)), FName = lookup_fun(Env, qname(Env, Name)),
FArgs = args_to_fcode(Env, Args), FArgs = args_to_fcode(Env, Args),
@@ -440,7 +354,7 @@ typedef_to_fcode(Env, Id = {id, _, Name}, Xs, Def) ->
Env3 = compute_state_layout(Env2, Name, FDef), Env3 = compute_state_layout(Env2, Name, FDef),
bind_type(Env3, Q, FDef). bind_type(Env3, Q, FDef).
compute_state_layout(Env = #{ context := {contract_def, _} }, "state", Type) -> compute_state_layout(Env = #{ context := {main_contract, _} }, "state", Type) ->
NoLayout = get_option(no_flatten_state, Env), NoLayout = get_option(no_flatten_state, Env),
Layout = Layout =
case Type([]) of case Type([]) of
@@ -466,7 +380,7 @@ compute_state_layout(R, [H | T]) ->
compute_state_layout(R, _) -> compute_state_layout(R, _) ->
{R + 1, {reg, R}}. {R + 1, {reg, R}}.
check_state_and_event_types(#{ context := {contract_def, _} }, Id, [_ | _]) -> check_state_and_event_types(#{ context := {main_contract, _} }, Id, [_ | _]) ->
case Id of case Id of
{id, _, "state"} -> fcode_error({parameterized_state, Id}); {id, _, "state"} -> fcode_error({parameterized_state, Id});
{id, _, "event"} -> fcode_error({parameterized_event, Id}); {id, _, "event"} -> fcode_error({parameterized_event, Id});
@@ -491,18 +405,12 @@ type_to_fcode(Env, Sub, {record_t, Fields}) ->
type_to_fcode(Env, Sub, {tuple_t, [], lists:map(FieldType, Fields)}); type_to_fcode(Env, Sub, {tuple_t, [], lists:map(FieldType, Fields)});
type_to_fcode(_Env, _Sub, {bytes_t, _, N}) -> type_to_fcode(_Env, _Sub, {bytes_t, _, N}) ->
{bytes, N}; {bytes, N};
type_to_fcode(_Env, _Sub, {tvar, Ann, "void"}) ->
fcode_error({found_void, Ann});
type_to_fcode(_Env, Sub, {tvar, _, X}) -> type_to_fcode(_Env, Sub, {tvar, _, X}) ->
maps:get(X, Sub, {tvar, X}); maps:get(X, Sub, {tvar, X});
type_to_fcode(_Env, _Sub, {fun_t, Ann, _, var_args, _}) ->
fcode_error({var_args_not_set, {id, Ann, "a very suspicious function"}});
type_to_fcode(Env, Sub, {fun_t, _, Named, Args, Res}) -> type_to_fcode(Env, Sub, {fun_t, _, Named, Args, Res}) ->
FNamed = [type_to_fcode(Env, Sub, Arg) || {named_arg_t, _, _, Arg, _} <- Named], FNamed = [type_to_fcode(Env, Sub, Arg) || {named_arg_t, _, _, Arg, _} <- Named],
FArgs = [type_to_fcode(Env, Sub, Arg) || Arg <- Args], FArgs = [type_to_fcode(Env, Sub, Arg) || Arg <- Args],
{function, FNamed ++ FArgs, type_to_fcode(Env, Sub, Res)}; {function, FNamed ++ FArgs, type_to_fcode(Env, Sub, Res)};
type_to_fcode(Env, Sub, {if_t, _, _, _, Else}) ->
type_to_fcode(Env, Sub, Else); %% Hacky: this is only for remote calls, in which case we want the unprotected type
type_to_fcode(_Env, _Sub, Type) -> type_to_fcode(_Env, _Sub, Type) ->
error({todo, Type}). error({todo, Type}).
@@ -662,8 +570,8 @@ expr_to_fcode(Env, _Type, {list_comp, As, Yield, [{comprehension_bind, Pat = {ty
Arg = fresh_name(), Arg = fresh_name(),
Env1 = bind_var(Env, Arg), Env1 = bind_var(Env, Arg),
Bind = {lam, [Arg], expr_to_fcode(Env1, {switch, As, {typed, As, {id, As, Arg}, PatType}, Bind = {lam, [Arg], expr_to_fcode(Env1, {switch, As, {typed, As, {id, As, Arg}, PatType},
[{'case', As, Pat, [{guarded, As, [], {list_comp, As, Yield, Rest}}]}, [{'case', As, Pat, {list_comp, As, Yield, Rest}},
{'case', As, {id, As, "_"}, [{guarded, As, [], {list, As, []}}]}]})}, {'case', As, {id, As, "_"}, {list, As, []}}]})},
{def_u, FlatMap, _} = resolve_fun(Env, ["ListInternal", "flat_map"]), {def_u, FlatMap, _} = resolve_fun(Env, ["ListInternal", "flat_map"]),
{def, FlatMap, [Bind, expr_to_fcode(Env, BindExpr)]}; {def, FlatMap, [Bind, expr_to_fcode(Env, BindExpr)]};
expr_to_fcode(Env, Type, {list_comp, As, Yield, [{comprehension_if, _, Cond}|Rest]}) -> expr_to_fcode(Env, Type, {list_comp, As, Yield, [{comprehension_if, _, Cond}|Rest]}) ->
@@ -683,9 +591,9 @@ expr_to_fcode(Env, _Type, {'if', _, Cond, Then, Else}) ->
expr_to_fcode(Env, Else)); expr_to_fcode(Env, Else));
%% Switch %% Switch
expr_to_fcode(Env, _, S = {switch, _, Expr = {typed, _, E, Type}, Alts}) -> expr_to_fcode(Env, _, {switch, _, Expr = {typed, _, E, Type}, Alts}) ->
Switch = fun(X) -> Switch = fun(X) ->
{switch, alts_to_fcode(Env, type_to_fcode(Env, Type), X, Alts, S)} {switch, alts_to_fcode(Env, type_to_fcode(Env, Type), X, Alts)}
end, end,
case E of case E of
{id, _, X} -> Switch(X); {id, _, X} -> Switch(X);
@@ -712,31 +620,14 @@ expr_to_fcode(Env, _Type, {app, _Ann, {Op, _}, [A]}) when is_atom(Op) ->
end; end;
%% Function calls %% Function calls
expr_to_fcode(Env, Type, {app, _, Fun = {typed, _, FunE, {fun_t, _, NamedArgsT, ArgsT, _}}, Args}) -> expr_to_fcode(Env, _Type, {app, _, Fun = {typed, _, _, {fun_t, _, NamedArgsT, _, _}}, Args}) ->
Args1 = get_named_args(NamedArgsT, Args), Args1 = get_named_args(NamedArgsT, Args),
FArgs = [expr_to_fcode(Env, Arg) || Arg <- Args1], FArgs = [expr_to_fcode(Env, Arg) || Arg <- Args1],
case expr_to_fcode(Env, Fun) of case expr_to_fcode(Env, Fun) of
{builtin_u, B, _Ar, TypeArgs} -> builtin_to_fcode(state_layout(Env), B, FArgs ++ TypeArgs); {builtin_u, B, _Ar, TypeArgs} -> builtin_to_fcode(state_layout(Env), B, FArgs ++ TypeArgs);
{builtin_u, chain_clone, _Ar} ->
case ArgsT of
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]),
builtin_to_fcode(state_layout(Env), chain_clone, [{lit, FInitArgsT}|FArgs])
end;
{builtin_u, 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]),
builtin_to_fcode(state_layout(Env), chain_create, [{lit, {contract_code, Contract}}, {lit, FInitArgsT}|FArgs]);
{_, _} -> fcode_error({not_a_contract_type, Type})
end;
{builtin_u, B, _Ar} -> builtin_to_fcode(state_layout(Env), B, FArgs); {builtin_u, B, _Ar} -> builtin_to_fcode(state_layout(Env), B, FArgs);
{def_u, F, _Ar} -> {def, F, FArgs}; {def_u, F, _Ar} -> {def, F, FArgs};
{remote_u, RArgsT, RRetT, Ct, RFun} -> {remote, RArgsT, RRetT, Ct, RFun, FArgs}; {remote_u, ArgsT, RetT, Ct, RFun} -> {remote, ArgsT, RetT, Ct, RFun, FArgs};
FFun -> FFun ->
%% FFun is a closure, with first component the function name and %% FFun is a closure, with first component the function name and
%% second component the environment %% second component the environment
@@ -798,13 +689,6 @@ make_if(Cond, Then, Else) ->
X = fresh_name(), X = fresh_name(),
{'let', X, Cond, make_if({var, X}, Then, Else)}. {'let', X, Cond, make_if({var, X}, Then, Else)}.
make_if_no_else({var, X}, Then) ->
{switch, {split, boolean, X,
[{'case', {bool, true}, {nosplit, Then}}]}};
make_if_no_else(Cond, Then) ->
X = fresh_name(),
{'let', X, Cond, make_if_no_else({var, X}, Then)}.
-spec make_tuple([fexpr()]) -> fexpr(). -spec make_tuple([fexpr()]) -> fexpr().
make_tuple([E]) -> E; make_tuple([E]) -> E;
make_tuple(Es) -> {tuple, Es}. make_tuple(Es) -> {tuple, Es}.
@@ -870,9 +754,9 @@ is_first_order(_) -> true.
%% -- Pattern matching -- %% -- 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(), [aeso_syntax:alt()]) -> fsplit().
alts_to_fcode(Env, Type, X, Alts, Switch) -> alts_to_fcode(Env, Type, X, Alts) ->
FAlts = remove_guards(Env, Alts, Switch), FAlts = [alt_to_fcode(Env, Alt) || Alt <- Alts],
split_tree(Env, [{X, Type}], FAlts). split_tree(Env, [{X, Type}], FAlts).
%% Intermediate format before case trees (fcase() and fsplit()). %% Intermediate format before case trees (fcase() and fsplit()).
@@ -883,43 +767,7 @@ alts_to_fcode(Env, Type, X, Alts, Switch) ->
| {string, binary()} | {string, binary()}
| nil | {'::', fpat(), fpat()} | nil | {'::', fpat(), fpat()}
| {tuple, [fpat()]} | {tuple, [fpat()]}
| {con, arities(), tag(), [fpat()]} | {con, arities(), tag(), [fpat()]}.
| {assign, fpat(), fpat()}.
remove_guards(_Env, [], _Switch) ->
[];
remove_guards(Env, [Alt = {'case', _, _, [{guarded, _, [], _Expr}]} | Rest], Switch) ->
[alt_to_fcode(Env, Alt) | remove_guards(Env, Rest, Switch)];
remove_guards(Env, [{'case', AnnC, Pat, [{guarded, AnnG, [Guard | Guards], Body} | GuardedBodies]} | Rest], Switch = {switch, Ann, Expr, _}) ->
FPat = pat_to_fcode(Env, Pat),
FGuard = expr_to_fcode(bind_vars(Env, pat_vars(FPat)), Guard),
FBody = expr_to_fcode(bind_vars(Env, pat_vars(FPat)), Body),
case Guards of
[] ->
R = case GuardedBodies of
[] -> Rest;
_ -> [{'case', AnnC, Pat, GuardedBodies} | Rest]
end,
case R of
[] ->
[{'case', [FPat], make_if_no_else(FGuard, FBody)} | remove_guards(Env, Rest, Switch)];
_ ->
FSwitch = expr_to_fcode(Env, {switch, Ann, Expr, R}),
[{'case', [FPat], make_if(FGuard, FBody, FSwitch)} | remove_guards(Env, Rest, Switch)]
end;
_ ->
R1 = case GuardedBodies of
[] -> [{'case', AnnC, Pat, [{guarded, AnnG, Guards, Body}]} | Rest];
_ -> [{'case', AnnC, Pat, [{guarded, AnnG, Guards, Body} | GuardedBodies]} | Rest]
end,
R2 = case GuardedBodies of
[] -> Rest;
_ -> [{'case', AnnC, Pat, GuardedBodies} | Rest]
end,
FSwitch1 = expr_to_fcode(Env, {switch, Ann, Expr, R1}),
FSwitch2 = expr_to_fcode(Env, {switch, Ann, Expr, R2}),
[{'case', [FPat], make_if(FGuard, FSwitch1, FSwitch2)} | remove_guards(Env, Rest, Switch)]
end.
%% %% Invariant: the number of variables matches the number of patterns in each falt. %% %% Invariant: the number of variables matches the number of patterns in each falt.
-spec split_tree(env(), [{var_name(), ftype()}], [falt()]) -> fsplit(). -spec split_tree(env(), [{var_name(), ftype()}], [falt()]) -> fsplit().
@@ -1019,8 +867,6 @@ split_pat({'::', P, Q}) -> {{'::', fresh_name(), fresh_name()}, [P, Q]};
split_pat({con, As, I, Pats}) -> split_pat({con, As, I, Pats}) ->
Xs = [fresh_name() || _ <- Pats], Xs = [fresh_name() || _ <- Pats],
{{con, As, I, Xs}, Pats}; {{con, As, I, Xs}, Pats};
split_pat({assign, X = {var, _}, P}) ->
{{assign, fresh_name(), fresh_name()}, [X, P]};
split_pat({tuple, Pats}) -> split_pat({tuple, Pats}) ->
Xs = [fresh_name() || _ <- Pats], Xs = [fresh_name() || _ <- Pats],
{{tuple, Xs}, Pats}. {{tuple, Xs}, Pats}.
@@ -1031,7 +877,6 @@ split_vars({int, _}, integer) -> [];
split_vars({string, _}, string) -> []; split_vars({string, _}, string) -> [];
split_vars(nil, {list, _}) -> []; split_vars(nil, {list, _}) -> [];
split_vars({'::', X, Xs}, {list, T}) -> [{X, T}, {Xs, {list, T}}]; split_vars({'::', X, Xs}, {list, T}) -> [{X, T}, {Xs, {list, T}}];
split_vars({assign, X, P}, T) -> [{X, T}, {P, T}];
split_vars({con, _, I, Xs}, {variant, Cons}) -> split_vars({con, _, I, Xs}, {variant, Cons}) ->
lists:zip(Xs, lists:nth(I + 1, Cons)); lists:zip(Xs, lists:nth(I + 1, Cons));
split_vars({tuple, Xs}, {tuple, Ts}) -> split_vars({tuple, Xs}, {tuple, Ts}) ->
@@ -1047,7 +892,7 @@ next_split(Pats) ->
end. end.
-spec alt_to_fcode(env(), aeso_syntax:alt()) -> falt(). -spec alt_to_fcode(env(), aeso_syntax:alt()) -> falt().
alt_to_fcode(Env, {'case', _, Pat, [{guarded, _, [], Expr}]}) -> alt_to_fcode(Env, {'case', _, Pat, Expr}) ->
FPat = pat_to_fcode(Env, Pat), FPat = pat_to_fcode(Env, Pat),
FExpr = expr_to_fcode(bind_vars(Env, pat_vars(FPat)), Expr), FExpr = expr_to_fcode(bind_vars(Env, pat_vars(FPat)), Expr),
{'case', [FPat], FExpr}. {'case', [FPat], FExpr}.
@@ -1087,8 +932,6 @@ pat_to_fcode(Env, {record_t, Fields}, {record, _, FieldPats}) ->
end end, end end,
make_tuple([pat_to_fcode(Env, FieldPat(Field)) make_tuple([pat_to_fcode(Env, FieldPat(Field))
|| Field <- Fields]); || Field <- Fields]);
pat_to_fcode(Env, _Type, {letpat, _, Id = {typed, _, {id, _, _}, _}, Pattern}) ->
{assign, pat_to_fcode(Env, Id), pat_to_fcode(Env, Pattern)};
pat_to_fcode(_Env, Type, Pat) -> pat_to_fcode(_Env, Type, Pat) ->
error({todo, Pat, ':', Type}). error({todo, Pat, ':', Type}).
@@ -1125,8 +968,8 @@ decision_tree_to_fcode({'if', A, Then, Else}) ->
stmts_to_fcode(Env, [{letval, _, {typed, _, {id, _, X}, _}, Expr} | Stmts]) -> stmts_to_fcode(Env, [{letval, _, {typed, _, {id, _, X}, _}, Expr} | Stmts]) ->
{'let', X, expr_to_fcode(Env, Expr), stmts_to_fcode(bind_var(Env, X), Stmts)}; {'let', X, expr_to_fcode(Env, Expr), stmts_to_fcode(bind_var(Env, X), Stmts)};
stmts_to_fcode(Env, [{letval, Ann, Pat, Expr} | Stmts]) -> stmts_to_fcode(Env, [{letval, Ann, Pat, Expr} | Stmts]) ->
expr_to_fcode(Env, {switch, Ann, Expr, [{'case', Ann, Pat, [{guarded, Ann, [], {block, Ann, Stmts}}]}]}); expr_to_fcode(Env, {switch, Ann, Expr, [{'case', Ann, Pat, {block, Ann, Stmts}}]});
stmts_to_fcode(Env, [{letfun, Ann, {id, _, X}, Args, _Type, [{guarded, _, [], Expr}]} | Stmts]) -> stmts_to_fcode(Env, [{letfun, Ann, {id, _, X}, Args, _Type, Expr} | Stmts]) ->
LamArgs = [ case Arg of LamArgs = [ case Arg of
{typed, Ann1, Id, T} -> {arg, Ann1, Id, T}; {typed, Ann1, Id, T} -> {arg, Ann1, Id, T};
_ -> internal_error({bad_arg, Arg}) %% pattern matching has been desugared _ -> internal_error({bad_arg, Arg}) %% pattern matching has been desugared
@@ -1142,21 +985,12 @@ stmts_to_fcode(Env, [Expr | Stmts]) ->
op_builtins() -> op_builtins() ->
[map_from_list, map_to_list, map_delete, map_member, map_size, [map_from_list, map_to_list, map_delete, map_member, map_size,
stringinternal_length, stringinternal_concat, stringinternal_to_list, stringinternal_from_list, string_length, string_concat, string_sha3, string_sha256, string_blake2b,
stringinternal_sha3, stringinternal_sha256, stringinternal_blake2b,
char_to_int, char_from_int, stringinternal_to_lower, stringinternal_to_upper,
bits_set, bits_clear, bits_test, bits_sum, bits_intersection, bits_union, bits_set, bits_clear, bits_test, bits_sum, bits_intersection, bits_union,
bits_difference, int_to_str, address_to_str, crypto_verify_sig, bits_difference, int_to_str, address_to_str, crypto_verify_sig,
address_to_contract, address_to_contract,
crypto_verify_sig_secp256k1, crypto_sha3, crypto_sha256, crypto_blake2b, crypto_verify_sig_secp256k1, crypto_sha3, crypto_sha256, crypto_blake2b,
crypto_ecverify_secp256k1, crypto_ecrecover_secp256k1, crypto_ecverify_secp256k1, crypto_ecrecover_secp256k1
mcl_bls12_381_g1_neg, mcl_bls12_381_g1_norm, mcl_bls12_381_g1_valid,
mcl_bls12_381_g1_is_zero, mcl_bls12_381_g1_add, mcl_bls12_381_g1_mul,
mcl_bls12_381_g2_neg, mcl_bls12_381_g2_norm, mcl_bls12_381_g2_valid,
mcl_bls12_381_g2_is_zero, mcl_bls12_381_g2_add, mcl_bls12_381_g2_mul,
mcl_bls12_381_gt_inv, mcl_bls12_381_gt_add, mcl_bls12_381_gt_mul, mcl_bls12_381_gt_pow,
mcl_bls12_381_gt_is_one, mcl_bls12_381_pairing, mcl_bls12_381_miller_loop, mcl_bls12_381_final_exp,
mcl_bls12_381_int_to_fr, mcl_bls12_381_int_to_fp, mcl_bls12_381_fr_to_int, mcl_bls12_381_fp_to_int
]. ].
set_state({reg, R}, Val) -> set_state({reg, R}, Val) ->
@@ -1317,8 +1151,8 @@ lambda_lift_expr(Layout, UExpr) when element(1, UExpr) == def_u; element(1, UExp
lambda_lift_expr(Layout, {remote_u, ArgsT, RetT, Ct, F}) -> lambda_lift_expr(Layout, {remote_u, ArgsT, RetT, Ct, F}) ->
FVs = free_vars(Ct), FVs = free_vars(Ct),
Ct1 = lambda_lift_expr(Layout, Ct), Ct1 = lambda_lift_expr(Layout, Ct),
NamedArgCount = 3, GasAndValueArgs = 2,
Xs = [ lists:concat(["arg", I]) || I <- lists:seq(1, length(ArgsT) + NamedArgCount) ], Xs = [ lists:concat(["arg", I]) || I <- lists:seq(1, length(ArgsT) + GasAndValueArgs) ],
Args = [{var, X} || X <- Xs], Args = [{var, X} || X <- Xs],
make_closure(FVs, Xs, {remote, ArgsT, RetT, Ct1, F, Args}); make_closure(FVs, Xs, {remote, ArgsT, RetT, Ct1, F, Args});
lambda_lift_expr(Layout, Expr) -> lambda_lift_expr(Layout, Expr) ->
@@ -1579,7 +1413,6 @@ match_pat(L, {lit, L}) -> [];
match_pat(nil, nil) -> []; match_pat(nil, nil) -> [];
match_pat({'::', X, Y}, {op, '::', [A, B]}) -> [{X, A}, {Y, B}]; match_pat({'::', X, Y}, {op, '::', [A, B]}) -> [{X, A}, {Y, B}];
match_pat({var, X}, E) -> [{X, E}]; match_pat({var, X}, E) -> [{X, E}];
match_pat({assign, X, P}, E) -> [{X, E}, {P, E}];
match_pat(_, _) -> false. match_pat(_, _) -> false.
constructor_form(Env, Expr) -> constructor_form(Env, Expr) ->
@@ -1714,10 +1547,6 @@ bind_constructors(Env = #{ con_env := ConEnv }, NewCons) ->
%% -- Names -- %% -- Names --
-spec add_child_con(env(), sophia_name(), fcode()) -> env().
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(), [aeso_syntax:decl()]) -> env().
add_fun_env(Env = #{ context := {abstract_contract, _} }, _) -> Env; %% no functions from abstract contracts add_fun_env(Env = #{ context := {abstract_contract, _} }, _) -> Env; %% no functions from abstract contracts
add_fun_env(Env = #{ fun_env := FunEnv }, Decls) -> add_fun_env(Env = #{ fun_env := FunEnv }, Decls) ->
@@ -1732,7 +1561,7 @@ add_fun_env(Env = #{ fun_env := FunEnv }, Decls) ->
make_fun_name(#{ context := Context }, Ann, Name) -> make_fun_name(#{ context := Context }, Ann, Name) ->
Entrypoint = proplists:get_value(entrypoint, Ann, false), Entrypoint = proplists:get_value(entrypoint, Ann, false),
case Context of case Context of
{contract_def, Main} -> {main_contract, Main} ->
if Entrypoint -> {entrypoint, list_to_binary(Name)}; if Entrypoint -> {entrypoint, list_to_binary(Name)};
true -> {local_fun, [Main, Name]} true -> {local_fun, [Main, Name]}
end; end;
@@ -1744,7 +1573,7 @@ make_fun_name(#{ context := Context }, Ann, Name) ->
current_namespace(#{ context := Cxt }) -> current_namespace(#{ context := Cxt }) ->
case Cxt of case Cxt of
{abstract_contract, Con} -> Con; {abstract_contract, Con} -> Con;
{contract_def, Con} -> Con; {main_contract, Con} -> Con;
{namespace, NS} -> NS {namespace, NS} -> NS
end. end.
@@ -1815,7 +1644,6 @@ pat_vars(nil) -> [];
pat_vars({'::', P, Q}) -> pat_vars(P) ++ pat_vars(Q); pat_vars({'::', P, Q}) -> pat_vars(P) ++ pat_vars(Q);
pat_vars({tuple, Ps}) -> pat_vars(Ps); pat_vars({tuple, Ps}) -> pat_vars(Ps);
pat_vars({con, _, _, Ps}) -> pat_vars(Ps); pat_vars({con, _, _, Ps}) -> pat_vars(Ps);
pat_vars({assign, X, P}) -> pat_vars(X) ++ pat_vars(P);
pat_vars(Ps) when is_list(Ps) -> [X || P <- Ps, X <- pat_vars(P)]. pat_vars(Ps) when is_list(Ps) -> [X || P <- Ps, X <- pat_vars(P)].
-spec fsplit_pat_vars(fsplit_pat()) -> [var_name()]. -spec fsplit_pat_vars(fsplit_pat()) -> [var_name()].
@@ -2036,11 +1864,7 @@ rename_spat(Ren, {con, Ar, C, Xs}) ->
{{con, Ar, C, Zs}, Ren1}; {{con, Ar, C, Zs}, Ren1};
rename_spat(Ren, {tuple, Xs}) -> rename_spat(Ren, {tuple, Xs}) ->
{Zs, Ren1} = rename_bindings(Ren, Xs), {Zs, Ren1} = rename_bindings(Ren, Xs),
{{tuple, Zs}, Ren1}; {{tuple, Zs}, Ren1}.
rename_spat(Ren, {assign, X, P}) ->
{X1, Ren1} = rename_binding(Ren, X),
{P1, Ren2} = rename_binding(Ren1, P),
{{assign, X1, P1}, Ren2}.
rename_split(Ren, {split, Type, X, Cases}) -> rename_split(Ren, {split, Type, X, Cases}) ->
{split, Type, rename_var(Ren, X), [rename_case(Ren, C) || C <- Cases]}; {split, Type, rename_var(Ren, X), [rename_case(Ren, C) || C <- Cases]};
@@ -2096,11 +1920,8 @@ internal_error(Error) ->
%% -- Pretty printing -------------------------------------------------------- %% -- Pretty printing --------------------------------------------------------
format_fcode(#{ functions := Funs }) -> format_fcode(#{ functions := Funs }) ->
prettypr:format(format_funs(Funs)). prettypr:format(pp_above(
[ pp_fun(Name, Def) || {Name, Def} <- maps:to_list(Funs) ])).
format_funs(Funs) ->
pp_above(
[ pp_fun(Name, Def) || {Name, Def} <- maps:to_list(Funs) ]).
format_fexpr(E) -> format_fexpr(E) ->
prettypr:format(pp_fexpr(E)). prettypr:format(pp_fexpr(E)).
@@ -2217,9 +2038,7 @@ pp_fexpr({set_state, R, A}) ->
pp_call(pp_text("set_state"), [{lit, {int, R}}, A]); pp_call(pp_text("set_state"), [{lit, {int, R}}, A]);
pp_fexpr({get_state, R}) -> pp_fexpr({get_state, R}) ->
pp_call(pp_text("get_state"), [{lit, {int, R}}]); pp_call(pp_text("get_state"), [{lit, {int, R}}]);
pp_fexpr({switch, Split}) -> pp_split(Split); pp_fexpr({switch, Split}) -> pp_split(Split).
pp_fexpr({contract_code, Contract}) ->
pp_beside(pp_text("contract "), pp_text(Contract)).
pp_call(Fun, Args) -> pp_call(Fun, Args) ->
pp_beside(Fun, pp_fexpr({tuple, Args})). pp_beside(Fun, pp_fexpr({tuple, Args})).
+8 -22
View File
@@ -14,13 +14,12 @@
-include_lib("aebytecode/include/aeb_opcodes.hrl"). -include_lib("aebytecode/include/aeb_opcodes.hrl").
-include("aeso_icode.hrl"). -include("aeso_icode.hrl").
-include("aeso_utils.hrl").
-spec convert_typed(aeso_syntax:ast(), list()) -> aeso_icode:icode(). -spec convert_typed(aeso_syntax:ast(), list()) -> aeso_icode:icode().
convert_typed(TypedTree, Options) -> convert_typed(TypedTree, Options) ->
{Payable, Name} = {Payable, Name} =
case lists:last(TypedTree) of case lists:last(TypedTree) of
{Contr, Attrs, {con, _, Con}, _} when ?IS_CONTRACT_HEAD(Contr) -> {contract, Attrs, {con, _, Con}, _} ->
{proplists:get_value(payable, Attrs, false), Con}; {proplists:get_value(payable, Attrs, false), Con};
Decl -> Decl ->
gen_error({last_declaration_must_be_contract, Decl}) gen_error({last_declaration_must_be_contract, Decl})
@@ -30,8 +29,7 @@ convert_typed(TypedTree, Options) ->
Icode = code(TypedTree, NewIcode, Options), Icode = code(TypedTree, NewIcode, Options),
deadcode_elimination(Icode). deadcode_elimination(Icode).
code([{Contract, _Attribs, Con, Code}|Rest], Icode, Options) code([{contract, _Attribs, Con, Code}|Rest], Icode, Options) ->
when ?IS_CONTRACT_HEAD(Contract) ->
NewIcode = contract_to_icode(Code, aeso_icode:set_namespace(Con, Icode)), NewIcode = contract_to_icode(Code, aeso_icode:set_namespace(Con, Icode)),
code(Rest, NewIcode, Options); code(Rest, NewIcode, Options);
code([{namespace, _Ann, Name, Code}|Rest], Icode, Options) -> code([{namespace, _Ann, Name, Code}|Rest], Icode, Options) ->
@@ -96,7 +94,7 @@ contract_to_icode([Decl = {type_def, _Attrib, Id = {id, _, Name}, Args, Def} | R
_ -> Icode1 _ -> Icode1
end, end,
contract_to_icode(Rest, Icode2); contract_to_icode(Rest, Icode2);
contract_to_icode([{letfun, Attrib, Name, Args, _What, [{guarded, _, [], Body={typed,_,_,T}}]}|Rest], Icode) -> contract_to_icode([{letfun, Attrib, Name, Args, _What, Body={typed,_,_,T}}|Rest], Icode) ->
FunAttrs = [ stateful || proplists:get_value(stateful, Attrib, false) ] ++ FunAttrs = [ stateful || proplists:get_value(stateful, Attrib, false) ] ++
[ payable || proplists:get_value(payable, Attrib, false) ] ++ [ payable || proplists:get_value(payable, Attrib, false) ] ++
[ private || is_private(Attrib, Icode) ], [ private || is_private(Attrib, Icode) ],
@@ -323,8 +321,8 @@ ast_body({list_comp, _, Yield, []}, Icode) ->
ast_body({list_comp, As, Yield, [{comprehension_bind, {typed, _, Pat, ArgType}, BindExpr}|Rest]}, Icode) -> ast_body({list_comp, As, Yield, [{comprehension_bind, {typed, _, Pat, ArgType}, BindExpr}|Rest]}, Icode) ->
Arg = "%lc", Arg = "%lc",
Body = {switch, As, {typed, As, {id, As, Arg}, ArgType}, Body = {switch, As, {typed, As, {id, As, Arg}, ArgType},
[{'case', As, Pat, [{guarded, As, [], {list_comp, As, Yield, Rest}}]}, [{'case', As, Pat, {list_comp, As, Yield, Rest}},
{'case', As, {id, As, "_"}, [{guarded, As, [], {list, As, []}}]}]}, {'case', As, {id, As, "_"}, {list, As, []}}]},
#funcall #funcall
{ function = #var_ref{ name = ["ListInternal", "flat_map"] } { function = #var_ref{ name = ["ListInternal", "flat_map"] }
, args = , args =
@@ -349,14 +347,14 @@ ast_body({switch,_,A,Cases}, Icode) ->
%% patterns appear in cases. %% patterns appear in cases.
#switch{expr=ast_body(A, Icode), #switch{expr=ast_body(A, Icode),
cases=[{ast_body(Pat, Icode),ast_body(Body, Icode)} cases=[{ast_body(Pat, Icode),ast_body(Body, Icode)}
|| {'case',_,Pat,[{guarded, _, [], Body}]} <- Cases]}; || {'case',_,Pat,Body} <- Cases]};
ast_body({block, As, [{letval, _, Pat, E} | Rest]}, Icode) -> ast_body({block, As, [{letval, _, Pat, E} | Rest]}, Icode) ->
E1 = ast_body(E, Icode), E1 = ast_body(E, Icode),
Pat1 = ast_body(Pat, Icode), Pat1 = ast_body(Pat, Icode),
Rest1 = ast_body({block, As, Rest}, Icode), Rest1 = ast_body({block, As, Rest}, Icode),
#switch{expr = E1, #switch{expr = E1,
cases = [{Pat1, Rest1}]}; cases = [{Pat1, Rest1}]};
ast_body({block, As, [{letfun, Ann, F, Args, _Type, [{guarded, _, [], Expr}]} | Rest]}, Icode) -> ast_body({block, As, [{letfun, Ann, F, Args, _Type, Expr} | Rest]}, Icode) ->
ToArg = fun({typed, Ann1, Id, T}) -> {arg, Ann1, Id, T} end, %% Pattern matching has been desugared ToArg = fun({typed, Ann1, Id, T}) -> {arg, Ann1, Id, T} end, %% Pattern matching has been desugared
LamArgs = lists:map(ToArg, Args), LamArgs = lists:map(ToArg, Args),
ast_body({block, As, [{letval, Ann, F, {lam, Ann, LamArgs, Expr}} | Rest]}, Icode); ast_body({block, As, [{letval, Ann, F, {lam, Ann, LamArgs, Expr}} | Rest]}, Icode);
@@ -471,7 +469,6 @@ is_builtin_fun({qid, _, ["AENS", "preclaim"]}, _Icode) ->
is_builtin_fun({qid, _, ["AENS", "claim"]}, _Icode) -> true; is_builtin_fun({qid, _, ["AENS", "claim"]}, _Icode) -> true;
is_builtin_fun({qid, _, ["AENS", "transfer"]}, _Icode) -> true; is_builtin_fun({qid, _, ["AENS", "transfer"]}, _Icode) -> true;
is_builtin_fun({qid, _, ["AENS", "revoke"]}, _Icode) -> true; is_builtin_fun({qid, _, ["AENS", "revoke"]}, _Icode) -> true;
is_builtin_fun({qid, _, ["AENS", "update"]}, _Icode) -> true;
is_builtin_fun({qid, _, ["Map", "lookup"]}, _Icode) -> true; is_builtin_fun({qid, _, ["Map", "lookup"]}, _Icode) -> true;
is_builtin_fun({qid, _, ["Map", "lookup_default"]}, _Icode) -> true; is_builtin_fun({qid, _, ["Map", "lookup_default"]}, _Icode) -> true;
is_builtin_fun({qid, _, ["Map", "member"]}, _Icode) -> true; is_builtin_fun({qid, _, ["Map", "member"]}, _Icode) -> true;
@@ -627,12 +624,6 @@ builtin_code(_, {qid, _, ["AENS", "revoke"]}, Args, _, _, Icode) ->
[ast_body(Addr, Icode), ast_body(Name, Icode), ast_body(Sign, Icode)], [ast_body(Addr, Icode), ast_body(Name, Icode), ast_body(Sign, Icode)],
[word, word, sign_t()], {tuple, []}); [word, word, sign_t()], {tuple, []});
builtin_code(_, {qid, _, ["AENS", "update"]}, Args, _, _, Icode) ->
{Sign, [Addr, Name, TTL, ClientTTL, Pointers]} = get_signature_arg(Args),
prim_call(?PRIM_CALL_AENS_UPDATE, #integer{value = 0},
[ast_body(Addr, Icode), ast_body(Name, Icode), ast_body(TTL, Icode), ast_body(ClientTTL, Icode), ast_body(Pointers, Icode), ast_body(Sign, Icode)],
[word, string, word, word, word, sign_t()], {tuple, []});
%% -- Maps %% -- Maps
%% -- lookup functions %% -- lookup functions
builtin_code(_, {qid, _, ["Map", "lookup"]}, [Key, Map], _, _, Icode) -> builtin_code(_, {qid, _, ["Map", "lookup"]}, [Key, Map], _, _, Icode) ->
@@ -936,16 +927,11 @@ ast_typerep1({variant_t, Cons}, Icode) ->
{variant, [ begin {variant, [ begin
{constr_t, _, _, Args} = Con, {constr_t, _, _, Args} = Con,
[ ast_typerep1(Arg, Icode) || Arg <- Args ] [ ast_typerep1(Arg, Icode) || Arg <- Args ]
end || Con <- Cons ]}; end || Con <- Cons ]}.
ast_typerep1({if_t, _, _, _, Else}, Icode) ->
ast_typerep1(Else, Icode). %% protected remote calls are not in AEVM
ttl_t(Icode) -> ttl_t(Icode) ->
ast_typerep({qid, [], ["Chain", "ttl"]}, Icode). ast_typerep({qid, [], ["Chain", "ttl"]}, Icode).
%% pointee_t(Icode) ->
%% ast_typerep({qid, [], ["AENS", "pointee"]}, Icode).
sign_t() -> bytes_t(64). sign_t() -> bytes_t(64).
bytes_t(Len) when Len =< 32 -> word; bytes_t(Len) when Len =< 32 -> word;
bytes_t(Len) -> {tuple, lists:duplicate((31 + Len) div 32, word)}. bytes_t(Len) -> {tuple, lists:duplicate((31 + Len) div 32, word)}.
+3 -9
View File
@@ -10,9 +10,9 @@
-export([format/1, pos/1]). -export([format/1, pos/1]).
format({last_declaration_must_be_main_contract, Decl = {Kind, _, {con, _, C}, _}}) -> format({last_declaration_must_be_contract, Decl = {namespace, _, {con, _, C}, _}}) ->
Msg = io_lib:format("Expected a main contract as the last declaration instead of the ~p '~s'\n", Msg = io_lib:format("Expected a contract as the last declaration instead of the namespace '~s'\n",
[Kind, C]), [C]),
mk_err(pos(Decl), Msg); mk_err(pos(Decl), Msg);
format({missing_init_function, Con}) -> format({missing_init_function, Con}) ->
Msg = io_lib:format("Missing init function for the contract '~s'.\n", [pp_expr(Con)]), Msg = io_lib:format("Missing init function for the contract '~s'.\n", [pp_expr(Con)]),
@@ -87,12 +87,6 @@ format({higher_order_state, {type_def, Ann, _, _, State}}) ->
Msg = io_lib:format("Invalid state type\n~s\n", [pp_type(2, State)]), Msg = io_lib:format("Invalid state type\n~s\n", [pp_type(2, State)]),
Cxt = "The state cannot contain functions in the AEVM. Use FATE if you need this.\n", Cxt = "The state cannot contain functions in the AEVM. Use FATE if you need this.\n",
mk_err(pos(Ann), Msg, Cxt); mk_err(pos(Ann), Msg, Cxt);
format({var_args_not_set, Expr}) ->
mk_err( pos(Expr), "Could not deduce type of variable arguments list"
, "When compiling " ++ pp_expr(Expr)
);
format({found_void, Ann}) ->
mk_err(pos(Ann), "Found a void-typed value.", "`void` is a restricted, uninhabited type. Did you mean `unit`?");
format(Err) -> format(Err) ->
mk_err(aeso_errors:pos(0, 0), io_lib:format("Unknown error: ~p\n", [Err])). mk_err(aeso_errors:pos(0, 0), io_lib:format("Unknown error: ~p\n", [Err])).
+9 -13
View File
@@ -2,7 +2,7 @@
%%% @author Happi (Erik Stenman) %%% @author Happi (Erik Stenman)
%%% @copyright (C) 2017, Aeternity Anstalt %%% @copyright (C) 2017, Aeternity Anstalt
%%% @doc %%% @doc
%%% Compiler from Aeterinty Sophia language to the Aeternity VM, aevm. %%% Compiler from Aeterinty Sophia language to both AEVM and FATE VM.
%%% @end %%% @end
%%% Created : 12 Dec 2017 %%% Created : 12 Dec 2017
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
@@ -28,7 +28,6 @@
-include_lib("aebytecode/include/aeb_opcodes.hrl"). -include_lib("aebytecode/include/aeb_opcodes.hrl").
-include("aeso_icode.hrl"). -include("aeso_icode.hrl").
-include("aeso_utils.hrl").
-type option() :: pp_sophia_code -type option() :: pp_sophia_code
@@ -138,9 +137,8 @@ from_string1(aevm, ContractString, Options) ->
{ok, maybe_generate_aci(Res, FoldedTypedAst, Options)}; {ok, maybe_generate_aci(Res, FoldedTypedAst, Options)};
from_string1(fate, ContractString, Options) -> from_string1(fate, ContractString, Options) ->
#{ fcode := FCode #{ fcode := FCode
, fcode_env := #{child_con_env := ChildContracts}
, folded_typed_ast := FoldedTypedAst } = string_to_code(ContractString, Options), , folded_typed_ast := FoldedTypedAst } = string_to_code(ContractString, Options),
FateCode = aeso_fcode_to_fate:compile(ChildContracts, FCode, Options), FateCode = aeso_fcode_to_fate:compile(FCode, Options),
pp_assembler(fate, FateCode, Options), pp_assembler(fate, FateCode, Options),
ByteCode = aeb_fate_code:serialize(FateCode, []), ByteCode = aeb_fate_code:serialize(FateCode, []),
{ok, Version} = version(), {ok, Version} = version(),
@@ -180,9 +178,8 @@ string_to_code(ContractString, Options) ->
, type_env => TypeEnv , type_env => TypeEnv
, ast => Ast }; , ast => Ast };
fate -> fate ->
{Env, Fcode} = aeso_ast_to_fcode:ast_to_fcode(UnfoldedTypedAst, [{original_src, ContractString}|Options]), Fcode = aeso_ast_to_fcode:ast_to_fcode(UnfoldedTypedAst, Options),
#{ fcode => Fcode #{ fcode => Fcode
, fcode_env => Env
, unfolded_typed_ast => UnfoldedTypedAst , unfolded_typed_ast => UnfoldedTypedAst
, folded_typed_ast => FoldedTypedAst , folded_typed_ast => FoldedTypedAst
, type_env => TypeEnv , type_env => TypeEnv
@@ -244,9 +241,8 @@ check_call1(ContractString0, FunName, Args, Options) ->
fate -> fate ->
%% First check the contract without the __call function %% First check the contract without the __call function
#{ fcode := OrgFcode #{ fcode := OrgFcode
, fcode_env := #{child_con_env := ChildContracts}
, ast := Ast } = string_to_code(ContractString0, Options), , ast := Ast } = string_to_code(ContractString0, Options),
FateCode = aeso_fcode_to_fate:compile(ChildContracts, OrgFcode, []), FateCode = aeso_fcode_to_fate:compile(OrgFcode, []),
%% collect all hashes and compute the first name without hash collision to %% collect all hashes and compute the first name without hash collision to
SymbolHashes = maps:keys(aeb_fate_code:symbols(FateCode)), SymbolHashes = maps:keys(aeb_fate_code:symbols(FateCode)),
CallName = first_none_match(?CALL_NAME, SymbolHashes, CallName = first_none_match(?CALL_NAME, SymbolHashes,
@@ -472,12 +468,12 @@ error_missing_call_function() ->
Msg = "Internal error: missing '__call'-function", Msg = "Internal error: missing '__call'-function",
aeso_errors:throw(aeso_errors:new(internal_error, Msg)). aeso_errors:throw(aeso_errors:new(internal_error, Msg)).
get_call_type([{Contract, _, _, Defs}]) when ?IS_CONTRACT_HEAD(Contract) -> get_call_type([{contract, _, _, Defs}]) ->
case [ {lists:last(QFunName), FunType} case [ {lists:last(QFunName), FunType}
|| {letfun, _, {id, _, ?CALL_NAME}, [], _Ret, || {letfun, _, {id, _, ?CALL_NAME}, [], _Ret,
[{guarded, _, [], {typed, _, {typed, _,
{app, _, {app, _,
{typed, _, {qid, _, QFunName}, FunType}, _}, _}}]} <- Defs ] of {typed, _, {qid, _, QFunName}, FunType}, _}, _}} <- Defs ] of
[Call] -> {ok, Call}; [Call] -> {ok, Call};
[] -> error_missing_call_function() [] -> error_missing_call_function()
end; end;
@@ -486,7 +482,7 @@ get_call_type([_ | Contracts]) ->
get_call_type(Contracts). get_call_type(Contracts).
-dialyzer({nowarn_function, get_decode_type/2}). -dialyzer({nowarn_function, get_decode_type/2}).
get_decode_type(FunName, [{Contract, Ann, _, Defs}]) when ?IS_CONTRACT_HEAD(Contract) -> get_decode_type(FunName, [{contract, Ann, _, Defs}]) ->
GetType = fun({letfun, _, {id, _, Name}, Args, Ret, _}) when Name == FunName -> [{Args, Ret}]; GetType = fun({letfun, _, {id, _, Name}, Args, Ret, _}) when Name == FunName -> [{Args, Ret}];
({fun_decl, _, {id, _, Name}, {fun_t, _, _, Args, Ret}}) when Name == FunName -> [{Args, Ret}]; ({fun_decl, _, {id, _, Name}, {fun_t, _, _, Args, Ret}}) when Name == FunName -> [{Args, Ret}];
(_) -> [] end, (_) -> [] end,
+56 -193
View File
@@ -9,7 +9,7 @@
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(aeso_fcode_to_fate). -module(aeso_fcode_to_fate).
-export([compile/2, compile/3, term_to_fate/1, term_to_fate/2]). -export([compile/2, term_to_fate/1]).
-ifdef(TEST). -ifdef(TEST).
-export([optimize_fun/4, to_basic_blocks/1]). -export([optimize_fun/4, to_basic_blocks/1]).
@@ -45,7 +45,7 @@
-define(s(N), {store, N}). -define(s(N), {store, N}).
-define(void, {var, 9999}). -define(void, {var, 9999}).
-record(env, { contract, vars = [], locals = [], current_function, tailpos = true, child_contracts = #{}, options = []}). -record(env, { contract, vars = [], locals = [], current_function, tailpos = true }).
%% -- Debugging -------------------------------------------------------------- %% -- Debugging --------------------------------------------------------------
@@ -70,11 +70,9 @@ code_error(Err) ->
%% @doc Main entry point. %% @doc Main entry point.
compile(FCode, Options) -> compile(FCode, Options) ->
compile(#{}, FCode, Options).
compile(ChildContracts, FCode, Options) ->
#{ contract_name := ContractName, #{ contract_name := ContractName,
functions := Functions } = FCode, functions := Functions } = FCode,
SFuns = functions_to_scode(ChildContracts, ContractName, Functions, Options), SFuns = functions_to_scode(ContractName, Functions, Options),
SFuns1 = optimize_scode(SFuns, Options), SFuns1 = optimize_scode(SFuns, Options),
FateCode = to_basic_blocks(SFuns1), FateCode = to_basic_blocks(SFuns1),
?debug(compile, Options, "~s\n", [aeb_fate_asm:pp(FateCode)]), ?debug(compile, Options, "~s\n", [aeb_fate_asm:pp(FateCode)]),
@@ -87,20 +85,19 @@ make_function_name(event) -> <<"Chain.event">>;
make_function_name({entrypoint, Name}) -> Name; make_function_name({entrypoint, Name}) -> Name;
make_function_name({local_fun, Xs}) -> list_to_binary("." ++ string:join(Xs, ".")). make_function_name({local_fun, Xs}) -> list_to_binary("." ++ string:join(Xs, ".")).
functions_to_scode(ChildContracts, ContractName, Functions, Options) -> functions_to_scode(ContractName, Functions, Options) ->
FunNames = maps:keys(Functions), FunNames = maps:keys(Functions),
maps:from_list( maps:from_list(
[ {make_function_name(Name), function_to_scode(ChildContracts, ContractName, FunNames, Name, Attrs, Args, Body, Type, Options)} [ {make_function_name(Name), function_to_scode(ContractName, FunNames, Name, Attrs, Args, Body, Type, Options)}
|| {Name, #{args := Args, || {Name, #{args := Args,
body := Body, body := Body,
attrs := Attrs, attrs := Attrs,
return := Type}} <- maps:to_list(Functions)]). return := Type}} <- maps:to_list(Functions)]).
function_to_scode(ChildContracts, ContractName, Functions, Name, Attrs0, Args, Body, ResType, Options) -> function_to_scode(ContractName, Functions, Name, Attrs0, Args, Body, ResType, _Options) ->
{ArgTypes, ResType1} = typesig_to_scode(Args, ResType), {ArgTypes, ResType1} = typesig_to_scode(Args, ResType),
Attrs = Attrs0 -- [stateful], %% Only track private and payable from here. Attrs = Attrs0 -- [stateful], %% Only track private and payable from here.
Env = init_env(ChildContracts, ContractName, Functions, Name, Args, Options), SCode = to_scode(init_env(ContractName, Functions, Name, Args), Body),
SCode = to_scode(Env, Body),
{Attrs, {ArgTypes, ResType1}, SCode}. {Attrs, {ArgTypes, ResType1}, SCode}.
-define(tvars, '$tvars'). -define(tvars, '$tvars').
@@ -136,9 +133,7 @@ type_to_scode({tvar, X}) ->
put(?tvars, {I + 1, Vars#{ X => I }}), put(?tvars, {I + 1, Vars#{ X => I }}),
{tvar, I}; {tvar, I};
J -> {tvar, J} J -> {tvar, J}
end; end.
type_to_scode(L) when is_list(L) -> {tuple, types_to_scode(L)}.
types_to_scode(Ts) -> lists:map(fun type_to_scode/1, Ts). types_to_scode(Ts) -> lists:map(fun type_to_scode/1, Ts).
@@ -147,13 +142,11 @@ types_to_scode(Ts) -> lists:map(fun type_to_scode/1, Ts).
%% -- Environment functions -- %% -- Environment functions --
init_env(ChildContracts, ContractName, FunNames, Name, Args, Options) -> init_env(ContractName, FunNames, Name, Args) ->
#env{ vars = [ {X, {arg, I}} || {I, {X, _}} <- with_ixs(Args) ], #env{ vars = [ {X, {arg, I}} || {I, {X, _}} <- with_ixs(Args) ],
contract = ContractName, contract = ContractName,
child_contracts = ChildContracts,
locals = FunNames, locals = FunNames,
current_function = Name, current_function = Name,
options = Options,
tailpos = true }. tailpos = true }.
next_var(#env{ vars = Vars }) -> next_var(#env{ vars = Vars }) ->
@@ -176,7 +169,7 @@ lookup_var(#env{vars = Vars}, X) ->
%% -- The compiler -- %% -- The compiler --
lit_to_fate(Env, L) -> lit_to_fate(L) ->
case L of case L of
{int, N} -> aeb_fate_data:make_integer(N); {int, N} -> aeb_fate_data:make_integer(N);
{string, S} -> aeb_fate_data:make_string(S); {string, S} -> aeb_fate_data:make_string(S);
@@ -186,80 +179,63 @@ lit_to_fate(Env, L) ->
{contract_pubkey, K} -> aeb_fate_data:make_contract(K); {contract_pubkey, K} -> aeb_fate_data:make_contract(K);
{oracle_pubkey, K} -> aeb_fate_data:make_oracle(K); {oracle_pubkey, K} -> aeb_fate_data:make_oracle(K);
{oracle_query_id, H} -> aeb_fate_data:make_oracle_query(H); {oracle_query_id, H} -> aeb_fate_data:make_oracle_query(H);
{contract_code, C} ->
Options = Env#env.options,
FCode = maps:get(C, Env#env.child_contracts),
FateCode = compile(Env#env.child_contracts, FCode, Options),
ByteCode = aeb_fate_code:serialize(FateCode, []),
{ok, Version} = aeso_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(),
payable => maps:get(payable, FCode)
},
Serialized = aeser_contract_code:serialize(Code),
aeb_fate_data:make_contract_bytearray(Serialized);
{typerep, T} -> aeb_fate_data:make_typerep(type_to_scode(T)) {typerep, T} -> aeb_fate_data:make_typerep(type_to_scode(T))
end. end.
term_to_fate(E) -> term_to_fate(#env{}, #{}, E). term_to_fate(E) -> term_to_fate(#{}, E).
term_to_fate(GlobEnv, E) -> term_to_fate(GlobEnv, #{}, E).
term_to_fate(GlobEnv, _Env, {lit, L}) -> term_to_fate(_Env, {lit, L}) ->
lit_to_fate(GlobEnv, L); lit_to_fate(L);
%% negative literals are parsed as 0 - N %% negative literals are parsed as 0 - N
term_to_fate(_GlobEnv, _Env, {op, '-', [{lit, {int, 0}}, {lit, {int, N}}]}) -> term_to_fate(_Env, {op, '-', [{lit, {int, 0}}, {lit, {int, N}}]}) ->
aeb_fate_data:make_integer(-N); aeb_fate_data:make_integer(-N);
term_to_fate(_GlobEnv, _Env, nil) -> term_to_fate(_Env, nil) ->
aeb_fate_data:make_list([]); aeb_fate_data:make_list([]);
term_to_fate(GlobEnv, Env, {op, '::', [Hd, Tl]}) -> term_to_fate(Env, {op, '::', [Hd, Tl]}) ->
%% The Tl will translate into a list, because FATE lists are just lists %% The Tl will translate into a list, because FATE lists are just lists
[term_to_fate(GlobEnv, Env, Hd) | term_to_fate(GlobEnv, Env, Tl)]; [term_to_fate(Env, Hd) | term_to_fate(Env, Tl)];
term_to_fate(GlobEnv, Env, {tuple, As}) -> term_to_fate(Env, {tuple, As}) ->
aeb_fate_data:make_tuple(list_to_tuple([ term_to_fate(GlobEnv, Env, A) || A<-As])); aeb_fate_data:make_tuple(list_to_tuple([ term_to_fate(Env, A) || A<-As]));
term_to_fate(GlobEnv, Env, {con, Ar, I, As}) -> term_to_fate(Env, {con, Ar, I, As}) ->
FateAs = [ term_to_fate(GlobEnv, Env, A) || A <- As ], FateAs = [ term_to_fate(Env, A) || A <- As ],
aeb_fate_data:make_variant(Ar, I, list_to_tuple(FateAs)); aeb_fate_data:make_variant(Ar, I, list_to_tuple(FateAs));
term_to_fate(_GlobEnv, _Env, {builtin, bits_all, []}) -> term_to_fate(_Env, {builtin, bits_all, []}) ->
aeb_fate_data:make_bits(-1); aeb_fate_data:make_bits(-1);
term_to_fate(_GlobEnv, _Env, {builtin, bits_none, []}) -> term_to_fate(_Env, {builtin, bits_none, []}) ->
aeb_fate_data:make_bits(0); aeb_fate_data:make_bits(0);
term_to_fate(GlobEnv, _Env, {op, bits_set, [B, I]}) -> term_to_fate(_Env, {op, bits_set, [B, I]}) ->
{bits, N} = term_to_fate(GlobEnv, B), {bits, N} = term_to_fate(B),
J = term_to_fate(GlobEnv, I), J = term_to_fate(I),
{bits, N bor (1 bsl J)}; {bits, N bor (1 bsl J)};
term_to_fate(GlobEnv, _Env, {op, bits_clear, [B, I]}) -> term_to_fate(_Env, {op, bits_clear, [B, I]}) ->
{bits, N} = term_to_fate(GlobEnv, B), {bits, N} = term_to_fate(B),
J = term_to_fate(GlobEnv, I), J = term_to_fate(I),
{bits, N band bnot (1 bsl J)}; {bits, N band bnot (1 bsl J)};
term_to_fate(GlobEnv, Env, {'let', X, E, Body}) -> term_to_fate(Env, {'let', X, E, Body}) ->
Env1 = Env#{ X => term_to_fate(GlobEnv, Env, E) }, Env1 = Env#{ X => term_to_fate(Env, E) },
term_to_fate(GlobEnv, Env1, Body); term_to_fate(Env1, Body);
term_to_fate(_GlobEnv, Env, {var, X}) -> term_to_fate(Env, {var, X}) ->
case maps:get(X, Env, undefined) of case maps:get(X, Env, undefined) of
undefined -> throw(not_a_fate_value); undefined -> throw(not_a_fate_value);
V -> V V -> V
end; end;
term_to_fate(_GlobEnv, _Env, {builtin, map_empty, []}) -> term_to_fate(_Env, {builtin, map_empty, []}) ->
aeb_fate_data:make_map(#{}); aeb_fate_data:make_map(#{});
term_to_fate(GlobEnv, Env, {op, map_set, [M, K, V]}) -> term_to_fate(Env, {op, map_set, [M, K, V]}) ->
Map = term_to_fate(GlobEnv, Env, M), Map = term_to_fate(Env, M),
Map#{term_to_fate(GlobEnv, Env, K) => term_to_fate(GlobEnv, Env, V)}; Map#{term_to_fate(Env, K) => term_to_fate(Env, V)};
term_to_fate(_GlobEnv, _Env, _) -> term_to_fate(_Env, _) ->
throw(not_a_fate_value). throw(not_a_fate_value).
to_scode(Env, T) -> to_scode(Env, T) ->
try term_to_fate(Env, T) of try term_to_fate(T) of
V -> [push(?i(V))] V -> [push(?i(V))]
catch throw:not_a_fate_value -> catch throw:not_a_fate_value ->
to_scode1(Env, T) to_scode1(Env, T)
end. end.
to_scode1(Env, {lit, L}) -> to_scode1(_Env, {lit, L}) ->
[push(?i(lit_to_fate(Env, L)))]; [push(?i(lit_to_fate(L)))];
to_scode1(_Env, nil) -> to_scode1(_Env, nil) ->
[aeb_fate_ops:nil(?a)]; [aeb_fate_ops:nil(?a)];
@@ -326,27 +302,18 @@ to_scode1(Env, {funcall, Fun, Args}) ->
to_scode1(Env, {builtin, B, Args}) -> to_scode1(Env, {builtin, B, Args}) ->
builtin_to_scode(Env, B, Args); builtin_to_scode(Env, B, Args);
to_scode1(Env, {remote, ArgsT, RetT, Ct, Fun, [Gas, Value, Protected | Args]}) -> to_scode1(Env, {remote, ArgsT, RetT, Ct, Fun, [Gas, Value | Args]}) ->
Lbl = make_function_id(Fun), Lbl = make_function_id(Fun),
{ArgTypes, RetType0} = typesig_to_scode([{"_", T} || T <- ArgsT], RetT), {ArgTypes, RetType0} = typesig_to_scode([{"_", T} || T <- ArgsT], RetT),
ArgType = ?i(aeb_fate_data:make_typerep({tuple, ArgTypes})), ArgType = ?i(aeb_fate_data:make_typerep({tuple, ArgTypes})),
RetType = ?i(aeb_fate_data:make_typerep(RetType0)), RetType = ?i(aeb_fate_data:make_typerep(RetType0)),
case Protected of case Gas of
{lit, {bool, false}} -> {builtin, call_gas_left, _} ->
case Gas of Call = aeb_fate_ops:call_r(?a, Lbl, ArgType, RetType, ?a),
{builtin, call_gas_left, _} -> call_to_scode(Env, Call, [Ct, Value | Args]);
Call = aeb_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_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_to_scode(Env, Call, [Ct, Value, Gas | Args]);
_ -> _ ->
Call = aeb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?a), Call = aeb_fate_ops:call_gr(?a, Lbl, ArgType, RetType, ?a, ?a),
call_to_scode(Env, Call, [Ct, Value, Gas, Protected | Args]) call_to_scode(Env, Call, [Ct, Value, Gas | Args])
end; end;
to_scode1(_Env, {get_state, Reg}) -> to_scode1(_Env, {get_state, Reg}) ->
@@ -527,14 +494,10 @@ builtin_to_scode(_Env, call_value, []) ->
[aeb_fate_ops:call_value(?a)]; [aeb_fate_ops:call_value(?a)];
builtin_to_scode(_Env, call_gas_price, []) -> builtin_to_scode(_Env, call_gas_price, []) ->
[aeb_fate_ops:gasprice(?a)]; [aeb_fate_ops:gasprice(?a)];
builtin_to_scode(_Env, call_fee, []) ->
[aeb_fate_ops:fee(?a)];
builtin_to_scode(_Env, call_gas_left, []) -> builtin_to_scode(_Env, call_gas_left, []) ->
[aeb_fate_ops:gas(?a)]; [aeb_fate_ops:gas(?a)];
builtin_to_scode(Env, oracle_register, [_Sign,_Account,_QFee,_TTL,_QType,_RType] = Args) -> 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); 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) -> builtin_to_scode(Env, oracle_query_fee, [_Oracle] = Args) ->
call_to_scode(Env, aeb_fate_ops:oracle_query_fee(?a, ?a), 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) -> builtin_to_scode(Env, oracle_query, [_Oracle, _Question, _QFee, _QTTL, _RTTL, _QType, _RType] = Args) ->
@@ -573,35 +536,8 @@ builtin_to_scode(Env, aens_transfer, [_Sign, _From, _To, _Name] = Args) ->
builtin_to_scode(Env, aens_revoke, [_Sign, _Account, _Name] = 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, [aeb_fate_ops:aens_revoke(?a, ?a, ?a),
tuple(0)], Args); 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),
tuple(0)], Args);
builtin_to_scode(Env, aens_lookup, [_Name] = Args) ->
call_to_scode(Env, aeb_fate_ops:aens_lookup(?a, ?a), Args);
builtin_to_scode(_Env, auth_tx_hash, []) -> builtin_to_scode(_Env, auth_tx_hash, []) ->
[aeb_fate_ops:auth_tx_hash(?a)]; [aeb_fate_ops:auth_tx_hash(?a)].
builtin_to_scode(_Env, auth_tx, []) ->
[aeb_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);
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),
[Contract, InitArgsT, Value, Prot | InitArgs]
);
_ ->
call_to_scode(Env, aeb_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),
[Code, InitArgsT, Value | InitArgs]
).
%% -- Operators -- %% -- Operators --
@@ -628,14 +564,8 @@ 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_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_member) -> aeb_fate_ops:map_member(?a, ?a, ?a);
op_to_scode(map_size) -> aeb_fate_ops:map_size_(?a, ?a); op_to_scode(map_size) -> aeb_fate_ops:map_size_(?a, ?a);
op_to_scode(stringinternal_length) -> aeb_fate_ops:str_length(?a, ?a); op_to_scode(string_length) -> aeb_fate_ops:str_length(?a, ?a);
op_to_scode(stringinternal_concat) -> aeb_fate_ops:str_join(?a, ?a, ?a); op_to_scode(string_concat) -> aeb_fate_ops:str_join(?a, ?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_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_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_test) -> aeb_fate_ops:bits_test(?a, ?a, ?a);
@@ -654,33 +584,9 @@ op_to_scode(crypto_ecrecover_secp256k1) -> aeb_fate_ops:ecrecover_secp256k1(?a,
op_to_scode(crypto_sha3) -> aeb_fate_ops:sha3(?a, ?a); op_to_scode(crypto_sha3) -> aeb_fate_ops:sha3(?a, ?a);
op_to_scode(crypto_sha256) -> aeb_fate_ops:sha256(?a, ?a); op_to_scode(crypto_sha256) -> aeb_fate_ops:sha256(?a, ?a);
op_to_scode(crypto_blake2b) -> aeb_fate_ops:blake2b(?a, ?a); op_to_scode(crypto_blake2b) -> aeb_fate_ops:blake2b(?a, ?a);
op_to_scode(stringinternal_sha3) -> aeb_fate_ops:sha3(?a, ?a); op_to_scode(string_sha3) -> aeb_fate_ops:sha3(?a, ?a);
op_to_scode(stringinternal_sha256) -> aeb_fate_ops:sha256(?a, ?a); op_to_scode(string_sha256) -> aeb_fate_ops:sha256(?a, ?a);
op_to_scode(stringinternal_blake2b) -> aeb_fate_ops:blake2b(?a, ?a); op_to_scode(string_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).
%% PUSH and STORE ?a are the same, so we use STORE to make optimizations %% 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. %% easier, and specialize to PUSH (which is cheaper) at the end.
@@ -828,7 +734,6 @@ attributes(I) ->
{'CALL', A} -> Impure(?a, [A]); {'CALL', A} -> Impure(?a, [A]);
{'CALL_R', A, _, B, C, D} -> Impure(?a, [A, B, C, D]); {'CALL_R', A, _, B, C, D} -> Impure(?a, [A, B, C, D]);
{'CALL_GR', A, _, B, C, D, E} -> Impure(?a, [A, B, C, D, E]); {'CALL_GR', A, _, B, C, D, E} -> Impure(?a, [A, B, C, D, E]);
{'CALL_PGR', A, _, B, C, D, E, F} -> Impure(?a, [A, B, C, D, E, F]);
{'CALL_T', A} -> Impure(pc, [A]); {'CALL_T', A} -> Impure(pc, [A]);
{'CALL_VALUE', A} -> Pure(A, []); {'CALL_VALUE', A} -> Pure(A, []);
{'JUMP', _} -> Impure(pc, []); {'JUMP', _} -> Impure(pc, []);
@@ -910,7 +815,6 @@ attributes(I) ->
{'CONTRACT_TO_ADDRESS', A, B} -> Pure(A, [B]); {'CONTRACT_TO_ADDRESS', A, B} -> Pure(A, [B]);
{'ADDRESS_TO_CONTRACT', A, B} -> Pure(A, [B]); {'ADDRESS_TO_CONTRACT', A, B} -> Pure(A, [B]);
{'AUTH_TX_HASH', A} -> Pure(A, []); {'AUTH_TX_HASH', A} -> Pure(A, []);
{'AUTH_TX', A} -> Pure(A, []);
{'BYTES_TO_INT', A, B} -> Pure(A, [B]); {'BYTES_TO_INT', A, B} -> Pure(A, [B]);
{'BYTES_TO_STR', A, B} -> Pure(A, [B]); {'BYTES_TO_STR', A, B} -> Pure(A, [B]);
{'BYTES_CONCAT', A, B, C} -> Pure(A, [B, C]); {'BYTES_CONCAT', A, B, C} -> Pure(A, [B, C]);
@@ -927,7 +831,6 @@ attributes(I) ->
{'ORIGIN', A} -> Pure(A, []); {'ORIGIN', A} -> Pure(A, []);
{'CALLER', A} -> Pure(A, []); {'CALLER', A} -> Pure(A, []);
{'GASPRICE', A} -> Pure(A, []); {'GASPRICE', A} -> Pure(A, []);
{'FEE', A} -> Pure(A, []);
{'BLOCKHASH', A, B} -> Pure(A, [B]); {'BLOCKHASH', A, B} -> Pure(A, [B]);
{'BENEFICIARY', A} -> Pure(A, []); {'BENEFICIARY', A} -> Pure(A, []);
{'TIMESTAMP', A} -> Pure(A, []); {'TIMESTAMP', A} -> Pure(A, []);
@@ -950,48 +853,12 @@ attributes(I) ->
{'ORACLE_GET_ANSWER', A, B, C, D, E} -> Pure(A, [B, C, D, E]); {'ORACLE_GET_ANSWER', A, B, C, D, E} -> Pure(A, [B, C, D, E]);
{'ORACLE_GET_QUESTION', A, B, C, D, E}-> Pure(A, [B, C, D, E]); {'ORACLE_GET_QUESTION', A, B, C, D, E}-> Pure(A, [B, C, D, E]);
{'ORACLE_QUERY_FEE', A, B} -> Pure(A, [B]); {'ORACLE_QUERY_FEE', A, B} -> Pure(A, [B]);
{'ORACLE_EXPIRY', A, B} -> Impure(A, [B]); {'AENS_RESOLVE', A, B, C, D} -> Pure(A, [B, C, D]);
{'AENS_RESOLVE', A, B, C, D} -> Impure(A, [B, C, D]);
{'AENS_PRECLAIM', A, B, C} -> Impure(none, [A, B, C]); {'AENS_PRECLAIM', A, B, C} -> Impure(none, [A, B, C]);
{'AENS_CLAIM', A, B, C, D, E} -> Impure(none, [A, B, C, D, E]); {'AENS_CLAIM', A, B, C, D, E} -> Impure(none, [A, B, C, D, E]);
{'AENS_UPDATE', A, B, C, D, E, F} -> Impure(none, [A, B, C, D, E, F]); 'AENS_UPDATE' -> Impure(none, []);%% TODO
{'AENS_TRANSFER', A, B, C, D} -> Impure(none, [A, B, C, D]); {'AENS_TRANSFER', A, B, C, D} -> Impure(none, [A, B, C, D]);
{'AENS_REVOKE', A, B, C} -> Impure(none, [A, B, C]); {'AENS_REVOKE', A, B, C} -> Impure(none, [A, B, C]);
{'AENS_LOOKUP', A, B} -> Impure(A, [B]);
{'BLS12_381_G1_NEG', A, B} -> Pure(A, [B]);
{'BLS12_381_G1_NORM', A, B} -> Pure(A, [B]);
{'BLS12_381_G1_VALID', A, B} -> Pure(A, [B]);
{'BLS12_381_G1_IS_ZERO', A, B} -> Pure(A, [B]);
{'BLS12_381_G1_ADD', A, B, C} -> Pure(A, [B, C]);
{'BLS12_381_G1_MUL', A, B, C} -> Pure(A, [B, C]);
{'BLS12_381_G2_NEG', A, B} -> Pure(A, [B]);
{'BLS12_381_G2_NORM', A, B} -> Pure(A, [B]);
{'BLS12_381_G2_VALID', A, B} -> Pure(A, [B]);
{'BLS12_381_G2_IS_ZERO', A, B} -> Pure(A, [B]);
{'BLS12_381_G2_ADD', A, B, C} -> Pure(A, [B, C]);
{'BLS12_381_G2_MUL', A, B, C} -> Pure(A, [B, C]);
{'BLS12_381_GT_INV', A, B} -> Pure(A, [B]);
{'BLS12_381_GT_ADD', A, B, C} -> Pure(A, [B, C]);
{'BLS12_381_GT_MUL', A, B, C} -> Pure(A, [B, C]);
{'BLS12_381_GT_POW', A, B, C} -> Pure(A, [B, C]);
{'BLS12_381_GT_IS_ONE', A, B} -> Pure(A, [B]);
{'BLS12_381_PAIRING', A, B, C} -> Pure(A, [B, C]);
{'BLS12_381_MILLER_LOOP', A, B, C} -> Pure(A, [B, C]);
{'BLS12_381_FINAL_EXP', A, B} -> Pure(A, [B]);
{'BLS12_381_INT_TO_FR', A, B} -> Pure(A, [B]);
{'BLS12_381_INT_TO_FP', A, B} -> Pure(A, [B]);
{'BLS12_381_FR_TO_INT', A, B} -> Pure(A, [B]);
{'BLS12_381_FP_TO_INT', A, B} -> Pure(A, [B]);
{'STR_TO_LIST', A, B} -> Pure(A, [B]);
{'STR_FROM_LIST', A, B} -> Pure(A, [B]);
{'STR_TO_UPPER', A, B} -> Pure(A, [B]);
{'STR_TO_LOWER', A, B} -> Pure(A, [B]);
{'CHAR_TO_INT', A, B} -> Pure(A, [B]);
{'CHAR_FROM_INT', A, B} -> Pure(A, [B]);
{'CREATE', A, B, C} -> Impure(?a, [A, B, C]);
{'CLONE', A, B, C, D} -> Impure(?a, [A, B, C, D]);
{'CLONE_G', A, B, C, D, E} -> Impure(?a, [A, B, C, D, E]);
{'BYTECODE_HASH', A, B} -> Impure(A, [B]);
{'ABORT', A} -> Impure(pc, A); {'ABORT', A} -> Impure(pc, A);
{'EXIT', A} -> Impure(pc, A); {'EXIT', A} -> Impure(pc, A);
'NOP' -> Pure(none, []) 'NOP' -> Pure(none, [])
@@ -1744,10 +1611,6 @@ split_calls(Ref, [], Acc, Blocks) ->
split_calls(Ref, [I | Code], Acc, Blocks) when element(1, I) == 'CALL'; split_calls(Ref, [I | Code], Acc, Blocks) when element(1, I) == 'CALL';
element(1, I) == 'CALL_R'; element(1, I) == 'CALL_R';
element(1, I) == 'CALL_GR'; element(1, I) == 'CALL_GR';
element(1, I) == 'CALL_PGR';
element(1, I) == 'CREATE';
element(1, I) == 'CLONE';
element(1, I) == 'CLONE_G';
element(1, I) == 'jumpif' -> element(1, I) == 'jumpif' ->
split_calls(make_ref(), Code, [], [{Ref, lists:reverse([I | Acc])} | Blocks]); split_calls(make_ref(), Code, [], [{Ref, lists:reverse([I | Acc])} | Blocks]);
split_calls(Ref, [{'ABORT', _} = I | _Code], Acc, Blocks) -> split_calls(Ref, [{'ABORT', _} = I | _Code], Acc, Blocks) ->
+20 -24
View File
@@ -73,33 +73,28 @@ new(Options) ->
builtin_types() -> builtin_types() ->
Word = fun([]) -> word end, Word = fun([]) -> word end,
#{ "bool" => Word #{ "bool" => Word
, "int" => Word , "int" => Word
, "char" => Word , "char" => Word
, "bits" => Word , "bits" => Word
, "string" => fun([]) -> string end , "string" => fun([]) -> string end
, "address" => Word , "address" => Word
, "hash" => Word , "hash" => Word
, "unit" => fun([]) -> {tuple, []} end , "unit" => fun([]) -> {tuple, []} end
, "signature" => fun([]) -> {tuple, [word, word]} end , "signature" => fun([]) -> {tuple, [word, word]} end
, "oracle" => fun([_, _]) -> word end , "oracle" => fun([_, _]) -> word end
, "oracle_query" => fun([_, _]) -> word end , "oracle_query" => fun([_, _]) -> word end
, "list" => fun([A]) -> {list, A} end , "list" => fun([A]) -> {list, A} end
, "option" => fun([A]) -> {variant, [[], [A]]} end , "option" => fun([A]) -> {variant, [[], [A]]} end
, "map" => fun([K, V]) -> map_typerep(K, V) end , "map" => fun([K, V]) -> map_typerep(K, V) end
, ["Chain", "ttl"] => fun([]) -> {variant, [[word], [word]]} end , ["Chain", "ttl"] => fun([]) -> {variant, [[word], [word]]} end
, ["AENS", "pointee"] => fun([]) -> {variant, [[word], [word], [word]]} end
}. }.
builtin_constructors() -> builtin_constructors() ->
#{ ["RelativeTTL"] => 0 #{ ["RelativeTTL"] => 0
, ["FixedTTL"] => 1 , ["FixedTTL"] => 1
, ["None"] => 0 , ["None"] => 0
, ["Some"] => 1 , ["Some"] => 1 }.
, ["AccountPointee"] => 0
, ["OraclePointee"] => 1
, ["ContractPointee"] => 2
}.
map_typerep(K, V) -> map_typerep(K, V) ->
{map, K, V}. {map, K, V}.
@@ -151,3 +146,4 @@ get_constructor_tag(Name, #{constructors := Constructors}) ->
undefined -> error({undefined_constructor, Name}); undefined -> error({undefined_constructor, Name});
Tag -> Tag Tag -> Tag
end. end.
+6 -8
View File
@@ -9,14 +9,12 @@
false -> fail() false -> fail()
end). end).
-define(RULE(A, Do), map(fun(_1) -> Do end, A )). -define(RULE(A, Do), map(fun(_1) -> Do end, A )).
-define(RULE(A, B, Do), map(fun({_1, _2}) -> Do end, {A, B} )). -define(RULE(A, B, Do), map(fun({_1, _2}) -> Do end, {A, B} )).
-define(RULE(A, B, C, Do), map(fun({_1, _2, _3}) -> Do end, {A, B, C} )). -define(RULE(A, B, C, Do), map(fun({_1, _2, _3}) -> Do end, {A, B, C} )).
-define(RULE(A, B, C, D, Do), map(fun({_1, _2, _3, _4}) -> Do end, {A, B, C, D} )). -define(RULE(A, B, C, D, Do), map(fun({_1, _2, _3, _4}) -> Do end, {A, B, C, D} )).
-define(RULE(A, B, C, D, E, Do), map(fun({_1, _2, _3, _4, _5}) -> Do end, {A, B, C, D, E} )). -define(RULE(A, B, C, D, E, Do), map(fun({_1, _2, _3, _4, _5}) -> Do end, {A, B, C, D, E} )).
-define(RULE(A, B, C, D, E, F, Do), map(fun({_1, _2, _3, _4, _5, _6}) -> Do end, {A, B, C, D, E, F} )). -define(RULE(A, B, C, D, E, F, Do), map(fun({_1, _2, _3, _4, _5, _6}) -> Do end, {A, B, C, D, E, F})).
-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(aeso_parse_lib,
[tok/1, tok/2, between/3, many/1, many1/1, sep/2, sep1/2, [tok/1, tok/2, between/3, many/1, many1/1, sep/2, sep1/2,
+6 -53
View File
@@ -93,23 +93,10 @@ decl() ->
?LAZY_P( ?LAZY_P(
choice( choice(
%% Contract declaration %% Contract declaration
[ ?RULE(token(main), keyword(contract), [ ?RULE(keyword(contract), con(), tok('='), maybe_block(decl()), {contract, _1, _2, _4})
con(), tok('='), maybe_block(decl()), {contract_main, _2, _3, _5}) , ?RULE(token(payable), keyword(contract), con(), tok('='), maybe_block(decl()), add_modifiers([_1], {contract, _2, _3, _5}))
, ?RULE(keyword(contract),
con(), tok('='), maybe_block(decl()), {contract_child, _1, _2, _4})
, ?RULE(keyword(contract), token(interface),
con(), tok('='), maybe_block(decl()), {contract_interface, _1, _3, _5})
, ?RULE(token(payable), token(main), keyword(contract),
con(), tok('='), maybe_block(decl()), add_modifiers([_1], {contract_main, _3, _4, _6}))
, ?RULE(token(payable), keyword(contract),
con(), tok('='), maybe_block(decl()), add_modifiers([_1], {contract_child, _2, _3, _5}))
, ?RULE(token(payable), keyword(contract), token(interface),
con(), tok('='), maybe_block(decl()), add_modifiers([_1], {contract_interface, _2, _4, _6}))
, ?RULE(keyword(namespace), con(), tok('='), maybe_block(decl()), {namespace, _1, _2, _4}) , ?RULE(keyword(namespace), con(), tok('='), maybe_block(decl()), {namespace, _1, _2, _4})
, ?RULE(keyword(include), str(), {include, get_ann(_1), _2}) , ?RULE(keyword(include), str(), {include, get_ann(_1), _2})
, using()
, pragma() , pragma()
%% Type declarations TODO: format annotation for "type bla" vs "type bla()" %% Type declarations TODO: format annotation for "type bla" vs "type bla()"
@@ -136,21 +123,6 @@ fundef_or_decl() ->
choice([?RULE(id(), tok(':'), type(), {fun_decl, get_ann(_1), _1, _3}), choice([?RULE(id(), tok(':'), type(), {fun_decl, get_ann(_1), _1, _3}),
fundef()]). fundef()]).
using() ->
Alias = {keyword(as), con()},
For = ?RULE(keyword(for), bracket_list(id()), {for, _2}),
Hiding = ?RULE(keyword(hiding), bracket_list(id()), {hiding, _2}),
?RULE(keyword(using), con(), optional(Alias), optional(choice(For, Hiding)), using(get_ann(_1), _2, _3, _4)).
using(Ann, Con, none, none) ->
{using, Ann, Con, none, none};
using(Ann, Con, {ok, {_, Alias}}, none) ->
{using, Ann, Con, Alias, none};
using(Ann, Con, none, {ok, List}) ->
{using, Ann, Con, none, List};
using(Ann, Con, {ok, {_, Alias}}, {ok, List}) ->
{using, Ann, Con, Alias, List}.
pragma() -> pragma() ->
Op = choice([token(T) || T <- ['<', '=<', '==', '>=', '>']]), Op = choice([token(T) || T <- ['<', '=<', '==', '>=', '>']]),
?RULE(tok('@'), id("compiler"), Op, version(), {pragma, get_ann(_1), {compiler, element(1, _3), _4}}). ?RULE(tok('@'), id("compiler"), Op, version(), {pragma, get_ann(_1), {compiler, element(1, _3), _4}}).
@@ -211,16 +183,10 @@ letdef() -> choice(valdef(), fundef()).
valdef() -> valdef() ->
?RULE(pattern(), tok('='), body(), {letval, [], _1, _3}). ?RULE(pattern(), tok('='), body(), {letval, [], _1, _3}).
guarded_fundefs() ->
choice(
[ ?RULE(keyword('='), body(), [{guarded, _1, [], _2}])
, maybe_block(?RULE(keyword('|'), comma_sep(expr()), tok('='), body(), {guarded, _1, _2, _4}))
]).
fundef() -> fundef() ->
choice( choice(
[ ?RULE(id(), args(), guarded_fundefs(), {letfun, get_ann(_1), _1, _2, type_wildcard(get_ann(_1)), _3}) [ ?RULE(id(), args(), tok('='), body(), {letfun, get_ann(_1), _1, _2, type_wildcard(get_ann(_1)), _4})
, ?RULE(id(), args(), tok(':'), type(), guarded_fundefs(), {letfun, get_ann(_1), _1, _2, _4, _5}) , ?RULE(id(), args(), tok(':'), type(), tok('='), body(), {letfun, get_ann(_1), _1, _2, _4, _6})
]). ]).
args() -> paren_list(pattern()). args() -> paren_list(pattern()).
@@ -230,9 +196,6 @@ arg() -> choice(
?RULE(id(), {arg, get_ann(_1), _1, type_wildcard(get_ann(_1))}), ?RULE(id(), {arg, get_ann(_1), _1, type_wildcard(get_ann(_1))}),
?RULE(id(), tok(':'), type(), {arg, get_ann(_1), _1, _3})). ?RULE(id(), tok(':'), type(), {arg, get_ann(_1), _1, _3})).
letpat() ->
?RULE(keyword('('), id(), tok('='), pattern(), tok(')'), {letpat, get_ann(_1), _2, _4}).
%% -- Types ------------------------------------------------------------------ %% -- Types ------------------------------------------------------------------
type_vars() -> paren_list(tvar()). type_vars() -> paren_list(tvar()).
@@ -279,8 +242,7 @@ body() ->
stmt() -> stmt() ->
?LAZY_P(choice( ?LAZY_P(choice(
[ using() [ expr()
, expr()
, letdecl() , letdecl()
, {switch, keyword(switch), parens(expr()), maybe_block(branch())} , {switch, keyword(switch), parens(expr()), maybe_block(branch())}
, {'if', keyword('if'), parens(expr()), body()} , {'if', keyword('if'), parens(expr()), body()}
@@ -289,13 +251,7 @@ stmt() ->
])). ])).
branch() -> branch() ->
?RULE(pattern(), guarded_branches(), {'case', get_ann(lists:nth(1, _2)), _1, _2}). ?RULE(pattern(), keyword('=>'), body(), {'case', _2, _1, _3}).
guarded_branches() ->
choice(
[ ?RULE(keyword('=>'), body(), [{guarded, _1, [], _2}])
, maybe_block(?RULE(tok('|'), comma_sep(expr()), keyword('=>'), body(), {guarded, _3, _2, _4}))
]).
pattern() -> pattern() ->
?LET_P(E, expr(), parse_pattern(E)). ?LET_P(E, expr(), parse_pattern(E)).
@@ -343,7 +299,6 @@ exprAtom() ->
, ?RULE(keyword('['), Expr, token('|'), comma_sep(comprehension_exp()), tok(']'), list_comp_e(_1, _2, _4)) , ?RULE(keyword('['), Expr, token('|'), comma_sep(comprehension_exp()), tok(']'), list_comp_e(_1, _2, _4))
, ?RULE(tok('['), Expr, binop('..'), Expr, tok(']'), _3(_2, _4)) , ?RULE(tok('['), Expr, binop('..'), Expr, tok(']'), _3(_2, _4))
, ?RULE(keyword('('), comma_sep(Expr), tok(')'), tuple_e(_1, _2)) , ?RULE(keyword('('), comma_sep(Expr), tok(')'), tuple_e(_1, _2))
, letpat()
]) ])
end). end).
@@ -604,8 +559,6 @@ tuple_e(Ann, Exprs) -> {tuple, Ann, Exprs}.
list_comp_e(Ann, Expr, Binds) -> {list_comp, Ann, Expr, Binds}. list_comp_e(Ann, Expr, Binds) -> {list_comp, Ann, Expr, Binds}.
-spec parse_pattern(aeso_syntax:expr()) -> aeso_parse_lib:parser(aeso_syntax:pat()). -spec parse_pattern(aeso_syntax:expr()) -> aeso_parse_lib:parser(aeso_syntax:pat()).
parse_pattern({letpat, Ann, Id, Pat}) ->
{letpat, Ann, Id, parse_pattern(Pat)};
parse_pattern({app, Ann, Con = {'::', _}, Es}) -> parse_pattern({app, Ann, Con = {'::', _}, Es}) ->
{app, Ann, Con, lists:map(fun parse_pattern/1, Es)}; {app, Ann, Con, lists:map(fun parse_pattern/1, Es)};
parse_pattern({app, Ann, {'-', _}, [{int, _, N}]}) -> parse_pattern({app, Ann, {'-', _}, [{int, _, N}]}) ->
+15 -39
View File
@@ -13,8 +13,6 @@
-export_type([options/0]). -export_type([options/0]).
-include("aeso_utils.hrl").
-type doc() :: prettypr:document(). -type doc() :: prettypr:document().
-type options() :: [{indent, non_neg_integer()} | show_generated]. -type options() :: [{indent, non_neg_integer()} | show_generated].
@@ -133,10 +131,6 @@ typed(A, Type) ->
false -> follow(hsep(A, text(":")), type(Type)) false -> follow(hsep(A, text(":")), type(Type))
end. end.
contract_head(contract_main) -> text("main contract");
contract_head(contract_child) -> text("contract");
contract_head(contract_interface) -> text("contract interface").
%% -- Exports ---------------------------------------------------------------- %% -- Exports ----------------------------------------------------------------
-spec decls([aeso_syntax:decl()], options()) -> doc(). -spec decls([aeso_syntax:decl()], options()) -> doc().
@@ -151,11 +145,11 @@ decl(D, Options) ->
with_options(Options, fun() -> decl(D) end). with_options(Options, fun() -> decl(D) end).
-spec decl(aeso_syntax:decl()) -> doc(). -spec decl(aeso_syntax:decl()) -> doc().
decl({Con, Attrs, C, Ds}) when ?IS_CONTRACT_HEAD(Con) -> decl({contract, Attrs, C, Ds}) ->
Mod = fun({Mod, true}) when Mod == payable -> Mod = fun({Mod, true}) when Mod == payable ->
text(atom_to_list(Mod)); text(atom_to_list(Mod));
(_) -> empty() end, (_) -> empty() end,
block(follow( hsep(lists:map(Mod, Attrs) ++ [contract_head(Con)]) block(follow( hsep(lists:map(Mod, Attrs) ++ [text("contract")])
, hsep(name(C), text("="))), decls(Ds)); , hsep(name(C), text("="))), decls(Ds));
decl({namespace, _, C, Ds}) -> decl({namespace, _, C, Ds}) ->
block(follow(text("namespace"), hsep(name(C), text("="))), decls(Ds)); block(follow(text("namespace"), hsep(name(C), text("="))), decls(Ds));
@@ -212,10 +206,8 @@ name({typed, _, Name, _}) -> name(Name).
-spec letdecl(string(), aeso_syntax:letbind()) -> doc(). -spec letdecl(string(), aeso_syntax:letbind()) -> doc().
letdecl(Let, {letval, _, P, E}) -> letdecl(Let, {letval, _, P, E}) ->
block_expr(0, hsep([text(Let), expr(P), text("=")]), E); block_expr(0, hsep([text(Let), expr(P), text("=")]), E);
letdecl(Let, {letfun, _, F, Args, T, [GuardedBody]}) -> letdecl(Let, {letfun, _, F, Args, T, E}) ->
beside(hsep([text(Let), typed(beside(name(F), expr({tuple, [], Args})), T)]), guarded_body(GuardedBody, "=")); block_expr(0, hsep([text(Let), typed(beside(name(F), expr({tuple, [], Args})), T), text("=")]), E).
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([aeso_syntax:arg()]) -> doc().
args(Args) -> args(Args) ->
@@ -272,8 +264,6 @@ type({args_t, _, Args}) ->
type({bytes_t, _, any}) -> text("bytes(_)"); type({bytes_t, _, any}) -> text("bytes(_)");
type({bytes_t, _, Len}) -> type({bytes_t, _, Len}) ->
text(lists:concat(["bytes(", Len, ")"])); text(lists:concat(["bytes(", Len, ")"]));
type({if_t, _, Id, Then, Else}) ->
beside(text("if"), args_type([Id, Then, Else]));
type({named_arg_t, _, Name, Type, _Default}) -> type({named_arg_t, _, Name, Type, _Default}) ->
%% Drop the default value %% Drop the default value
%% follow(hsep(typed(name(Name), Type), text("=")), expr(Default)); %% follow(hsep(typed(name(Name), Type), text("=")), expr(Default));
@@ -300,11 +290,12 @@ tuple_type(Factors) ->
, text(")") , text(")")
]). ]).
-spec expr_p(integer(), aeso_syntax:arg_expr()) -> doc(). -spec arg_expr(aeso_syntax:arg_expr()) -> doc().
expr_p(P, {letpat, _, Id, Pat}) -> arg_expr({named_arg, _, Name, E}) ->
paren(P > 100, follow(hsep(expr(Id), text("=")), expr(Pat))); follow(hsep(expr(Name), text("=")), expr(E));
expr_p(P, {named_arg, _, Name, E}) -> arg_expr(E) -> expr(E).
paren(P > 100, follow(hsep(expr(Name), text("=")), expr(E)));
-spec expr_p(integer(), aeso_syntax:expr()) -> doc().
expr_p(P, {lam, _, Args, E}) -> expr_p(P, {lam, _, Args, E}) ->
paren(P > 100, follow(hsep(args(Args), text("=>")), expr_p(100, E))); paren(P > 100, follow(hsep(args(Args), text("=>")), expr_p(100, E)));
expr_p(P, If = {'if', Ann, Cond, Then, Else}) -> expr_p(P, If = {'if', Ann, Cond, Then, Else}) ->
@@ -386,13 +377,8 @@ expr_p(_, {char, _, C}) ->
case C of case C of
$' -> text("'\\''"); $' -> text("'\\''");
$" -> text("'\"'"); $" -> text("'\"'");
_ when C < 16#80 -> _ -> S = lists:flatten(io_lib:format("~p", [[C]])),
S = lists:flatten(io_lib:format("~p", [[C]])), text("'" ++ tl(lists:droplast(S)) ++ "'")
text("'" ++ tl(lists:droplast(S)) ++ "'");
_ ->
S = lists:flatten(
io_lib:format("'~ts'", [list_to_binary(aeso_scan:utf8_encode([C]))])),
text(S)
end; end;
%% -- Names %% -- Names
expr_p(_, E = {id, _, _}) -> name(E); expr_p(_, E = {id, _, _}) -> name(E);
@@ -464,7 +450,7 @@ prefix(P, Op, A) ->
app(P, F, Args) -> app(P, F, Args) ->
paren(P > 900, paren(P > 900,
beside(expr_p(900, F), beside(expr_p(900, F),
tuple(lists:map(fun expr/1, Args)))). tuple(lists:map(fun arg_expr/1, Args)))).
field({field, _, LV, E}) -> field({field, _, LV, E}) ->
follow(hsep(lvalue(LV), text("=")), expr(E)); follow(hsep(lvalue(LV), text("=")), expr(E));
@@ -484,18 +470,8 @@ elim1(Proj={proj, _, _}) -> beside(text("."), elim(Proj));
elim1(Get={map_get, _, _}) -> elim(Get); elim1(Get={map_get, _, _}) -> elim(Get);
elim1(Get={map_get, _, _, _}) -> elim(Get). elim1(Get={map_get, _, _, _}) -> elim(Get).
alt({'case', _, Pat, [GuardedBody]}) -> alt({'case', _, Pat, Body}) ->
beside(expr(Pat), guarded_body(GuardedBody, "=>")); block_expr(0, hsep(expr(Pat), text("=>")), Body).
alt({'case', _, Pat, GuardedBodies}) ->
block(expr(Pat), above(lists:map(fun(GB) -> guarded_body(GB, "=>") end, GuardedBodies))).
guarded_body({guarded, _, Guards, Body}, Then) ->
block_expr(0, hsep(guards(Guards), text(Then)), Body).
guards([]) ->
text("");
guards(Guards) ->
hsep([text(" |"), par(punctuate(text(","), lists:map(fun expr/1, Guards)), 0)]).
block_expr(_, Header, {block, _, Ss}) -> block_expr(_, Header, {block, _, Ss}) ->
block(Header, statements(Ss)); block(Header, statements(Ss));
+31 -39
View File
@@ -7,7 +7,7 @@
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(aeso_scan). -module(aeso_scan).
-export([scan/1, utf8_encode/1]). -export([scan/1]).
-import(aeso_scan_lib, [token/1, token/2, symbol/0, skip/0, -import(aeso_scan_lib, [token/1, token/2, symbol/0, skip/0,
override/2, push/2, pop/1]). override/2, push/2, pop/1]).
@@ -28,13 +28,7 @@ lexer() ->
QID = ["(", CON, "\\.)+", ID], QID = ["(", CON, "\\.)+", ID],
QCON = ["(", CON, "\\.)+", CON], QCON = ["(", CON, "\\.)+", CON],
OP = "[=!<>+\\-*/:&|?~@^]+", OP = "[=!<>+\\-*/:&|?~@^]+",
%% Five cases for a character CHAR = "'([^'\\\\]|(\\\\.))'",
%% * 1 7-bit ascii, not \ or '
%% * 2-4 8-bit values (UTF8)
%% * \ followed by a known modifier [aernrtv]
%% * \xhh
%% * \x{hhh...}
CHAR = "'(([\\x00-\\x26\\x28-\\x5b\\x5d-\\x7f])|([\\x00-\\xff][\\x80-\\xff]{1,3})|(\\\\[befnrtv'\\\\])|(\\\\x[0-9a-fA-F]{2,2})|(\\\\x\\{[0-9a-fA-F]*\\}))'",
STRING = "\"([^\"\\\\]|(\\\\.))*\"", STRING = "\"([^\"\\\\]|(\\\\.))*\"",
CommentStart = {"/\\*", push(comment, skip())}, CommentStart = {"/\\*", push(comment, skip())},
@@ -44,9 +38,7 @@ lexer() ->
, {"[^/*]+|[/*]", skip()} ], , {"[^/*]+|[/*]", skip()} ],
Keywords = ["contract", "include", "let", "switch", "type", "record", "datatype", "if", "elif", "else", "function", Keywords = ["contract", "include", "let", "switch", "type", "record", "datatype", "if", "elif", "else", "function",
"stateful", "payable", "true", "false", "mod", "public", "entrypoint", "private", "indexed", "namespace", "stateful", "payable", "true", "false", "mod", "public", "entrypoint", "private", "indexed", "namespace"],
"interface", "main", "using", "as", "for", "hiding"
],
KW = string:join(Keywords, "|"), KW = string:join(Keywords, "|"),
Rules = Rules =
@@ -85,34 +77,34 @@ scan(String) ->
%% -- Helpers ---------------------------------------------------------------- %% -- Helpers ----------------------------------------------------------------
parse_string([$" | Chars]) -> parse_string([$" | Chars]) ->
unicode:characters_to_nfc_binary(unescape(Chars)). unescape(Chars).
parse_char([$' | Chars]) -> parse_char([$', $\\, Code, $']) ->
case unicode:characters_to_nfc_list(unescape($', Chars, [])) of
[Char] -> Char;
_Bad -> {error, "Bad character literal: '" ++ Chars}
end.
utf8_encode(Cs) ->
binary_to_list(unicode:characters_to_binary(Cs)).
unescape(Str) -> unescape($", Str, []).
unescape(Delim, [Delim], Acc) ->
list_to_binary(lists:reverse(Acc));
unescape(Delim, [$\\, $x, ${ | Chars ], Acc) ->
{Ds, [_ | Cs]} = lists:splitwith(fun($}) -> false ; (_) -> true end, Chars),
C = list_to_integer(Ds, 16),
Utf8Cs = binary_to_list(unicode:characters_to_binary([C])),
unescape(Delim, Cs, [Utf8Cs | Acc]);
unescape(Delim, [$\\, $x, D1, D2 | Chars ], Acc) ->
C = list_to_integer([D1, D2], 16),
Utf8Cs = binary_to_list(unicode:characters_to_binary([C])),
unescape(Delim, Chars, [Utf8Cs | Acc]);
unescape(Delim, [$\\, Code | Chars], Acc) ->
Ok = fun(C) -> unescape(Delim, Chars, [C | Acc]) end,
case Code of case Code of
Delim -> Ok(Delim); $' -> $';
$\\ -> $\\;
$b -> $\b;
$e -> $\e;
$f -> $\f;
$n -> $\n;
$r -> $\r;
$t -> $\t;
$v -> $\v;
_ -> {error, "Bad control sequence: \\" ++ [Code]}
end;
parse_char([$', C, $']) -> C.
unescape(Str) -> unescape(Str, []).
unescape([$"], Acc) ->
list_to_binary(lists:reverse(Acc));
unescape([$\\, $x, D1, D2 | Chars ], Acc) ->
C = list_to_integer([D1, D2], 16),
unescape(Chars, [C | Acc]);
unescape([$\\, Code | Chars], Acc) ->
Ok = fun(C) -> unescape(Chars, [C | Acc]) end,
case Code of
$" -> Ok($");
$\\ -> Ok($\\); $\\ -> Ok($\\);
$b -> Ok($\b); $b -> Ok($\b);
$e -> Ok($\e); $e -> Ok($\e);
@@ -123,8 +115,8 @@ unescape(Delim, [$\\, Code | Chars], Acc) ->
$v -> Ok($\v); $v -> Ok($\v);
_ -> error("Bad control sequence: \\" ++ [Code]) %% TODO _ -> error("Bad control sequence: \\" ++ [Code]) %% TODO
end; end;
unescape(Delim, [C | Chars], Acc) -> unescape([C | Chars], Acc) ->
unescape(Delim, Chars, [C | Acc]). unescape(Chars, [C | Acc]).
strip_underscores(S) -> strip_underscores(S) ->
lists:filter(fun(C) -> C /= $_ end, S). lists:filter(fun(C) -> C /= $_ end, S).
+5 -17
View File
@@ -25,8 +25,7 @@
-type ann_origin() :: system | user. -type ann_origin() :: system | user.
-type ann_format() :: '?:' | hex | infix | prefix | elif. -type ann_format() :: '?:' | hex | infix | prefix | elif.
-type ann() :: [ {line, ann_line()} | {col, ann_col()} | {format, ann_format()} | {origin, ann_origin()} -type ann() :: [{line, ann_line()} | {col, ann_col()} | {format, ann_format()} | {origin, ann_origin()} | stateful | private].
| stateful | private | payable | main | interface].
-type name() :: string(). -type name() :: string().
-type id() :: {id, ann(), name()}. -type id() :: {id, ann(), name()}.
@@ -35,19 +34,13 @@
-type qcon() :: {qcon, ann(), [name()]}. -type qcon() :: {qcon, ann(), [name()]}.
-type tvar() :: {tvar, ann(), name()}. -type tvar() :: {tvar, ann(), name()}.
-type namespace_alias() :: none | con(). -type decl() :: {contract, ann(), con(), [decl()]}
-type namespace_parts() :: none | {for, [id()]} | {hiding, [id()]}.
-type decl() :: {contract_main, ann(), con(), [decl()]}
| {contract_child, ann(), con(), [decl()]}
| {contract_interface, ann(), con(), [decl()]}
| {namespace, ann(), con(), [decl()]} | {namespace, ann(), con(), [decl()]}
| {pragma, ann(), pragma()} | {pragma, ann(), pragma()}
| {type_decl, ann(), id(), [tvar()]} % Only for error msgs | {type_decl, ann(), id(), [tvar()]} % Only for error msgs
| {type_def, ann(), id(), [tvar()], typedef()} | {type_def, ann(), id(), [tvar()], typedef()}
| {fun_clauses, ann(), id(), type(), [letfun() | fundecl()]} | {fun_clauses, ann(), id(), type(), [letfun() | fundecl()]}
| {block, ann(), [decl()]} | {block, ann(), [decl()]}
| {using, ann(), con(), namespace_alias(), namespace_parts()}
| fundecl() | fundecl()
| letfun() | letfun()
| letval(). % Only for error msgs | letval(). % Only for error msgs
@@ -56,12 +49,9 @@
-type pragma() :: {compiler, '==' | '<' | '>' | '=<' | '>=', compiler_version()}. -type pragma() :: {compiler, '==' | '<' | '>' | '=<' | '>=', compiler_version()}.
-type guard() :: expr().
-type guarded_expr() :: {guarded, ann(), [guard()], expr()}.
-type letval() :: {letval, ann(), pat(), expr()}. -type letval() :: {letval, ann(), pat(), expr()}.
-type letfun() :: {letfun, ann(), id(), [pat()], type(), [guarded_expr(),...]}. -type letfun() :: {letfun, ann(), id(), [pat()], type(), expr()}.
-type letpat() :: {letpat, ann(), id(), pat()}.
-type fundecl() :: {fun_decl, ann(), id(), type()}. -type fundecl() :: {fun_decl, ann(), id(), type()}.
-type letbind() -type letbind()
@@ -126,8 +116,7 @@
| {block, ann(), [stmt()]} | {block, ann(), [stmt()]}
| {op(), ann()} | {op(), ann()}
| id() | qid() | con() | qcon() | id() | qid() | con() | qcon()
| constant() | constant().
| letpat().
-type record_or_map() :: record | map | record_or_map_error. -type record_or_map() :: record | map | record_or_map_error.
@@ -148,7 +137,7 @@
-type stmt() :: letbind() -type stmt() :: letbind()
| expr(). | expr().
-type alt() :: {'case', ann(), pat(), [guarded_expr(),...]}. -type alt() :: {'case', ann(), pat(), expr()}.
-type lvalue() :: nonempty_list(elim()). -type lvalue() :: nonempty_list(elim()).
@@ -161,7 +150,6 @@
| {list, ann(), [pat()]} | {list, ann(), [pat()]}
| {typed, ann(), pat(), type()} | {typed, ann(), pat(), type()}
| {record, ann(), [field(pat())]} | {record, ann(), [field(pat())]}
| letpat()
| constant() | constant()
| con() | con()
| id(). | id().
+9 -11
View File
@@ -41,15 +41,15 @@ fold(Alg = #alg{zero = Zero, plus = Plus, scoped = Scoped}, Fun, K, X) ->
Top = Fun(K, X), Top = Fun(K, X),
Rec = case X of Rec = case X of
%% lists (bound things in head scope over tail) %% lists (bound things in head scope over tail)
[A | As] -> Scoped(Same(A), Same(As)); [A | As] -> Scoped(Same(A), Same(As));
%% decl() %% decl()
{contract, _, _, Ds} -> Decl(Ds); {contract, _, _, Ds} -> Decl(Ds);
{namespace, _, _, Ds} -> Decl(Ds); {namespace, _, _, Ds} -> Decl(Ds);
{type_def, _, I, _, D} -> Plus(BindType(I), Decl(D)); {type_def, _, I, _, D} -> Plus(BindType(I), Decl(D));
{fun_decl, _, _, T} -> Type(T); {fun_decl, _, _, T} -> Type(T);
{letval, _, P, E} -> Scoped(BindExpr(P), Expr(E)); {letval, _, P, E} -> Scoped(BindExpr(P), Expr(E));
{letfun, _, F, Xs, T, GEs} -> Sum([BindExpr(F), Type(T), Expr(Xs ++ GEs)]); {letfun, _, F, Xs, T, E} -> Sum([BindExpr(F), Type(T), Expr(Xs ++ [E])]);
{fun_clauses, _, _, T, Cs} -> Sum([Type(T) | [Decl(C) || C <- Cs]]); {fun_clauses, _, _, T, Cs} -> Sum([Type(T) | [Decl(C) || C <- Cs]]);
%% typedef() %% typedef()
{alias_t, T} -> Type(T); {alias_t, T} -> Type(T);
{record_t, Fs} -> Type(Fs); {record_t, Fs} -> Type(Fs);
@@ -88,15 +88,13 @@ fold(Alg = #alg{zero = Zero, plus = Plus, scoped = Scoped}, Fun, K, X) ->
{map_get, _, A, B} -> Expr([A, B]); {map_get, _, A, B} -> Expr([A, B]);
{map_get, _, A, B, C} -> Expr([A, B, C]); {map_get, _, A, B, C} -> Expr([A, B, C]);
{block, _, Ss} -> Expr(Ss); {block, _, Ss} -> Expr(Ss);
{letpat, _, X, P} -> Plus(BindExpr(X), Expr(P));
{guarded, _, Gs, E} -> Expr([E | Gs]);
%% field() %% field()
{field, _, LV, E} -> Expr([LV, E]); {field, _, LV, E} -> Expr([LV, E]);
{field, _, LV, _, E} -> Expr([LV, E]); {field, _, LV, _, E} -> Expr([LV, E]);
%% arg() %% arg()
{arg, _, Y, T} -> Plus(BindExpr(Y), Type(T)); {arg, _, Y, T} -> Plus(BindExpr(Y), Type(T));
%% alt() %% alt()
{'case', _, P, GEs} -> Scoped(BindExpr(P), Expr(GEs)); {'case', _, P, E} -> Scoped(BindExpr(P), Expr(E));
%% elim() %% elim()
{proj, _, _} -> Zero; {proj, _, _} -> Zero;
{map_get, _, E} -> Expr(E); {map_get, _, E} -> Expr(E);
-6
View File
@@ -1,6 +0,0 @@
-define(IS_CONTRACT_HEAD(X),
(X =:= contract_main orelse
X =:= contract_interface orelse
X =:= contract_child
)
).
-88
View File
@@ -120,98 +120,10 @@ from_fate({constr_t, _, Con, Types}, Args)
when length(Types) == length(Args) -> when length(Types) == length(Args) ->
{app, [], Con, [ from_fate(Type, Arg) {app, [], Con, [ from_fate(Type, Arg)
|| {Type, Arg} <- lists:zip(Types, Args) ]}; || {Type, Arg} <- lists:zip(Types, Args) ]};
from_fate({qid, _, QType}, Val) ->
from_fate_builtin(QType, Val);
from_fate(_Type, _Data) -> from_fate(_Type, _Data) ->
throw(cannot_translate_to_sophia). throw(cannot_translate_to_sophia).
from_fate_builtin(QType, Val) ->
Con = fun([Name | _] = Names) when is_list(Name) -> {qcon, [], Names};
(Name) -> {con, [], Name} end,
App = fun(Name, []) -> Con(Name);
(Name, Value) -> {app, [], Con(Name), Value} end,
Chk = fun(Type, Value) -> from_fate(Type, Value) end,
Int = {id, [], "int"},
Str = {id, [], "string"},
Adr = {id, [], "address"},
Hsh = {bytes_t, [], 32},
Qid = fun(Name) -> {qid, [], Name} end,
Map = fun(KT, VT) -> {app_t, [], {id, [], "map"}, [KT, VT]} end,
ChainTxArities = [3, 0, 0, 0, 0, 0, 1, 1, 1, 2, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 0],
case {QType, Val} of
{["Chain", "ttl"], {variant, [1, 1], 0, {X}}} -> App("RelativeTTL", [Chk(Int, X)]);
{["Chain", "ttl"], {variant, [1, 1], 1, {X}}} -> App("FixedTTL", [Chk(Int, X)]);
{["AENS", "name"], {variant, [3], 0, {Addr, TTL, Ptrs}}} ->
App(["AENS","Name"], [Chk(Adr, Addr), Chk(Qid(["Chain", "ttl"]), TTL),
Chk(Map(Str, Qid(["AENS", "pointee"])), Ptrs)]);
{["AENS", "pointee"], {variant, [1, 1, 1, 1], 0, {Addr}}} ->
App(["AENS","AccountPt"], [Chk(Adr, Addr)]);
{["AENS", "pointee"], {variant, [1, 1, 1, 1], 1, {Addr}}} ->
App(["AENS","OraclePt"], [Chk(Adr, Addr)]);
{["AENS", "pointee"], {variant, [1, 1, 1, 1], 2, {Addr}}} ->
App(["AENS","ContractPt"], [Chk(Adr, Addr)]);
{["AENS", "pointee"], {variant, [1, 1, 1, 1], 3, {Addr}}} ->
App(["AENS","ChannelPt"], [Chk(Adr, Addr)]);
{["Chain", "ga_meta_tx"], {variant, [2], 0, {Addr, X}}} ->
App(["Chain","GAMetaTx"], [Chk(Adr, Addr), Chk(Int, X)]);
{["Chain", "paying_for_tx"], {variant, [2], 0, {Addr, X}}} ->
App(["Chain","PayingForTx"], [Chk(Adr, Addr), Chk(Int, X)]);
{["Chain", "base_tx"], {variant, ChainTxArities, 0, {Addr, Fee, Payload}}} ->
App(["Chain","SpendTx"], [Chk(Adr, Addr), Chk(Int, Fee), Chk(Str, Payload)]);
{["Chain", "base_tx"], {variant, ChainTxArities, 1, {}}} ->
App(["Chain","OracleRegisterTx"], []);
{["Chain", "base_tx"], {variant, ChainTxArities, 2, {}}} ->
App(["Chain","OracleQueryTx"], []);
{["Chain", "base_tx"], {variant, ChainTxArities, 3, {}}} ->
App(["Chain","OracleResponseTx"], []);
{["Chain", "base_tx"], {variant, ChainTxArities, 4, {}}} ->
App(["Chain","OracleExtendTx"], []);
{["Chain", "base_tx"], {variant, ChainTxArities, 5, {}}} ->
App(["Chain","NamePreclaimTx"], []);
{["Chain", "base_tx"], {variant, ChainTxArities, 6, {Name}}} ->
App(["Chain","NameClaimTx"], [Chk(Str, Name)]);
{["Chain", "base_tx"], {variant, ChainTxArities, 7, {NameHash}}} ->
App(["Chain","NameUpdateTx"], [Chk(Hsh, NameHash)]);
{["Chain", "base_tx"], {variant, ChainTxArities, 8, {NameHash}}} ->
App(["Chain","NameRevokeTx"], [Chk(Hsh, NameHash)]);
{["Chain", "base_tx"], {variant, ChainTxArities, 9, {NewOwner, NameHash}}} ->
App(["Chain","NameTransferTx"], [Chk(Adr, NewOwner), Chk(Hsh, NameHash)]);
{["Chain", "base_tx"], {variant, ChainTxArities, 10, {Addr}}} ->
App(["Chain","ChannelCreateTx"], [Chk(Adr, Addr)]);
{["Chain", "base_tx"], {variant, ChainTxArities, 11, {Addr, Amount}}} ->
App(["Chain","ChannelDepositTx"], [Chk(Adr, Addr), Chk(Int, Amount)]);
{["Chain", "base_tx"], {variant, ChainTxArities, 12, {Addr, Amount}}} ->
App(["Chain","ChannelWithdrawTx"], [Chk(Adr, Addr), Chk(Int, Amount)]);
{["Chain", "base_tx"], {variant, ChainTxArities, 13, {Addr}}} ->
App(["Chain","ChannelForceProgressTx"], [Chk(Adr, Addr)]);
{["Chain", "base_tx"], {variant, ChainTxArities, 14, {Addr}}} ->
App(["Chain","ChannelCloseMutualTx"], [Chk(Adr, Addr)]);
{["Chain", "base_tx"], {variant, ChainTxArities, 15, {Addr}}} ->
App(["Chain","ChannelCloseSoloTx"], [Chk(Adr, Addr)]);
{["Chain", "base_tx"], {variant, ChainTxArities, 16, {Addr}}} ->
App(["Chain","ChannelSlashTx"], [Chk(Adr, Addr)]);
{["Chain", "base_tx"], {variant, ChainTxArities, 17, {Addr}}} ->
App(["Chain","ChannelSettleTx"], [Chk(Adr, Addr)]);
{["Chain", "base_tx"], {variant, ChainTxArities, 18, {Addr}}} ->
App(["Chain","ChannelSnapshotSoloTx"], [Chk(Adr, Addr)]);
{["Chain", "base_tx"], {variant, ChainTxArities, 19, {Amount}}} ->
App(["Chain","ContractCreateTx"], [Chk(Int, Amount)]);
{["Chain", "base_tx"], {variant, ChainTxArities, 20, {Addr, Amount}}} ->
App(["Chain","ContractCallTx"], [Chk(Adr, Addr), Chk(Int, Amount)]);
{["Chain", "base_tx"], {variant, ChainTxArities, 21, {}}} ->
App(["Chain","GAAttachTx"], []);
_ ->
throw(cannot_translate_to_sophia)
end.
make_bits(N) -> make_bits(N) ->
Id = fun(F) -> {qid, [], ["Bits", F]} end, Id = fun(F) -> {qid, [], ["Bits", F]} end,
if N < 0 -> make_bits(Id("clear"), Id("all"), 0, bnot N); if N < 0 -> make_bits(Id("clear"), Id("all"), 0, bnot N);
+2 -2
View File
@@ -1,6 +1,6 @@
{application, aesophia, {application, aesophia,
[{description, "Compiler for Aeternity Sophia language"}, [{description, "Contract Language for aeternity"},
{vsn, "6.1.0"}, {vsn, "4.3.0"},
{registered, []}, {registered, []},
{applications, {applications,
[kernel, [kernel,
+1 -1
View File
@@ -190,7 +190,7 @@ parameterized_contract(ExtraCode, FunName, Types) ->
lists:flatten( lists:flatten(
["contract Remote =\n" ["contract Remote =\n"
" entrypoint bla : () => unit\n\n" " entrypoint bla : () => unit\n\n"
"main contract Dummy =\n", "contract Dummy =\n",
ExtraCode, "\n", ExtraCode, "\n",
" type an_alias('a) = string * 'a\n" " type an_alias('a) = string * 'a\n"
" record r = {x : an_alias(int), y : variant}\n" " record r = {x : an_alias(int), y : variant}\n"
+45 -54
View File
@@ -22,8 +22,7 @@ test_cases(1) ->
MapACI = #{contract => MapACI = #{contract =>
#{name => <<"C">>, #{name => <<"C">>,
type_defs => [], type_defs => [],
payable => true, payable => true,
kind => contract_main,
functions => functions =>
[#{name => <<"a">>, [#{name => <<"a">>,
arguments => arguments =>
@@ -32,57 +31,56 @@ test_cases(1) ->
returns => <<"int">>, returns => <<"int">>,
stateful => true, stateful => true,
payable => true}]}}, payable => true}]}},
DecACI = <<"payable main contract C =\n" DecACI = <<"payable contract C =\n"
" payable stateful entrypoint a : (int) => int\n">>, " payable entrypoint a : (int) => int\n">>,
{Contract,MapACI,DecACI}; {Contract,MapACI,DecACI};
test_cases(2) -> test_cases(2) ->
Contract = <<"main contract C =\n" Contract = <<"contract C =\n"
" type allan = int\n" " type allan = int\n"
" entrypoint a(i : allan) = i+1\n">>, " entrypoint a(i : allan) = i+1\n">>,
MapACI = #{contract => MapACI = #{contract =>
#{name => <<"C">>, payable => false, #{name => <<"C">>, payable => false,
kind => contract_main, type_defs =>
type_defs => [#{name => <<"allan">>,
[#{name => <<"allan">>, typedef => <<"int">>,
typedef => <<"int">>, vars => []}],
vars => []}], functions =>
functions => [#{arguments =>
[#{arguments => [#{name => <<"i">>,
[#{name => <<"i">>, type => <<"C.allan">>}],
type => <<"C.allan">>}], name => <<"a">>,
name => <<"a">>, returns => <<"int">>,
returns => <<"int">>, stateful => false,
stateful => false, payable => false}]}},
payable => false}]}}, DecACI = <<"contract C =\n"
DecACI = <<"main contract C =\n"
" type allan = int\n" " type allan = int\n"
" entrypoint a : (C.allan) => int\n">>, " entrypoint a : (C.allan) => int\n">>,
{Contract,MapACI,DecACI}; {Contract,MapACI,DecACI};
test_cases(3) -> test_cases(3) ->
Contract = <<"main contract C =\n" Contract = <<"contract C =\n"
" type state = unit\n" " type state = unit\n"
" datatype event = SingleEventDefined\n" " datatype event = SingleEventDefined\n"
" datatype bert('a) = Bin('a)\n" " datatype bert('a) = Bin('a)\n"
" entrypoint a(i : bert(string)) = 1\n">>, " entrypoint a(i : bert(string)) = 1\n">>,
MapACI = #{contract => MapACI = #{contract =>
#{functions => #{functions =>
[#{arguments => [#{arguments =>
[#{name => <<"i">>, [#{name => <<"i">>,
type => type =>
#{<<"C.bert">> => [<<"string">>]}}], #{<<"C.bert">> => [<<"string">>]}}],
name => <<"a">>,returns => <<"int">>, name => <<"a">>,returns => <<"int">>,
stateful => false, payable => false}], stateful => false, payable => false}],
name => <<"C">>, payable => false, kind => contract_main, name => <<"C">>, payable => false,
event => #{variant => [#{<<"SingleEventDefined">> => []}]}, event => #{variant => [#{<<"SingleEventDefined">> => []}]},
state => <<"unit">>, state => <<"unit">>,
type_defs => type_defs =>
[#{name => <<"bert">>, [#{name => <<"bert">>,
typedef => typedef =>
#{variant => #{variant =>
[#{<<"Bin">> => [<<"'a">>]}]}, [#{<<"Bin">> => [<<"'a">>]}]},
vars => [#{name => <<"'a">>}]}]}}, vars => [#{name => <<"'a">>}]}]}},
DecACI = <<"main contract C =\n" DecACI = <<"contract C =\n"
" type state = unit\n" " type state = unit\n"
" datatype event = SingleEventDefined\n" " datatype event = SingleEventDefined\n"
" datatype bert('a) = Bin('a)\n" " datatype bert('a) = Bin('a)\n"
@@ -103,24 +101,17 @@ aci_test_contract(Name) ->
true -> [debug_mode]; true -> [debug_mode];
false -> [] false -> []
end ++ [{include, {file_system, [aeso_test_utils:contract_path()]}}], end ++ [{include, {file_system, [aeso_test_utils:contract_path()]}}],
JSON = case aeso_aci:contract_interface(json, String, Opts) of {ok, JSON} = aeso_aci:contract_interface(json, String, Opts),
{ok, J} -> J; {ok, #{aci := JSON1}} = aeso_compiler:from_string(String, [{aci, json}, {backend, fate} | Opts]),
{error, ErrorStringJ} when is_binary(ErrorStringJ) -> error(ErrorStringJ); ?assertEqual(JSON, JSON1),
{error, ErrorJ} -> aeso_compiler_tests:print_and_throw(ErrorJ)
end,
case aeso_compiler:from_string(String, [{aci, json}, {backend, fate} | Opts]) of
{ok, #{aci := JSON1}} ->
?assertEqual(JSON, JSON1),
io:format("JSON:\n~p\n", [JSON]),
{ok, ContractStub} = aeso_aci:render_aci_json(JSON),
io:format("STUB:\n~s\n", [ContractStub]), io:format("JSON:\n~p\n", [JSON]),
check_stub(ContractStub, [{src_file, Name}]), {ok, ContractStub} = aeso_aci:render_aci_json(JSON),
ok; io:format("STUB:\n~s\n", [ContractStub]),
{error, ErrorString} when is_binary(ErrorString) -> error(ErrorString); check_stub(ContractStub, [{src_file, Name}]),
{error, Error} -> aeso_compiler_tests:print_and_throw(Error)
end. ok.
check_stub(Stub, Options) -> check_stub(Stub, Options) ->
try aeso_parser:string(binary_to_list(Stub), Options) of try aeso_parser:string(binary_to_list(Stub), Options) of
+4 -24
View File
@@ -59,8 +59,8 @@ calldata_aci_test_() ->
end} || {ContractName, Fun, Args} <- compilable_contracts()]. end} || {ContractName, Fun, Args} <- compilable_contracts()].
parse_args(Fun, Args) -> parse_args(Fun, Args) ->
[{contract_main, _, _, [{letfun, _, _, _, _, [{guarded, _, [], {app, _, _, AST}}]}]}] = [{contract, _, _, [{letfun, _, _, _, _, {app, _, _, AST}}]}] =
aeso_parser:string("main contract Temp = function foo() = " ++ Fun ++ "(" ++ string:join(Args, ", ") ++ ")"), aeso_parser:string("contract Temp = function foo() = " ++ Fun ++ "(" ++ string:join(Args, ", ") ++ ")"),
strip_ann(AST). strip_ann(AST).
strip_ann(T) when is_tuple(T) -> strip_ann(T) when is_tuple(T) ->
@@ -112,28 +112,8 @@ compilable_contracts() ->
{"funargs", "traffic_light", ["Green"]}, {"funargs", "traffic_light", ["Green"]},
{"funargs", "traffic_light", ["Pantone(12)"]}, {"funargs", "traffic_light", ["Pantone(12)"]},
{"funargs", "tuples", ["()"]}, {"funargs", "tuples", ["()"]},
{"funargs", "due", ["FixedTTL(1020)"]}, %% TODO {"funargs", "due", ["FixedTTL(1020)"]},
{"funargs", "singleton_rec", ["{x = 1000}"]}, {"funargs", "singleton_rec", ["{x = 1000}"]},
{"funargs", "aens_name", ["AENS.Name(ak_2dATVcZ9KJU5a8hdsVtTv21pYiGWiPbmVcU1Pz72FFqpk9pSRR, RelativeTTL(100), {[\"pt1\"] = AENS.AccountPt(ak_2dATVcZ9KJU5a8hdsVtTv21pYiGWiPbmVcU1Pz72FFqpk9pSRR)})"]},
{"funargs", "aens_pointee", ["AENS.AccountPt(ak_2dATVcZ9KJU5a8hdsVtTv21pYiGWiPbmVcU1Pz72FFqpk9pSRR)"]},
{"funargs", "aens_pointee", ["AENS.OraclePt(ak_2dATVcZ9KJU5a8hdsVtTv21pYiGWiPbmVcU1Pz72FFqpk9pSRR)"]},
{"funargs", "aens_pointee", ["AENS.ContractPt(ak_2dATVcZ9KJU5a8hdsVtTv21pYiGWiPbmVcU1Pz72FFqpk9pSRR)"]},
{"funargs", "aens_pointee", ["AENS.ChannelPt(ak_2dATVcZ9KJU5a8hdsVtTv21pYiGWiPbmVcU1Pz72FFqpk9pSRR)"]},
{"funargs", "chain_ga_meta_tx", ["Chain.GAMetaTx(ak_2dATVcZ9KJU5a8hdsVtTv21pYiGWiPbmVcU1Pz72FFqpk9pSRR, 42)"]},
{"funargs", "chain_paying_for_tx", ["Chain.PayingForTx(ak_2dATVcZ9KJU5a8hdsVtTv21pYiGWiPbmVcU1Pz72FFqpk9pSRR, 42)"]},
{"funargs", "chain_base_tx", ["Chain.SpendTx(ak_2dATVcZ9KJU5a8hdsVtTv21pYiGWiPbmVcU1Pz72FFqpk9pSRR, 42,\"foo\")"]},
{"funargs", "chain_base_tx", ["Chain.ContractCreateTx(12234)"]},
{"funargs", "chain_base_tx", ["Chain.ContractCallTx(ak_2dATVcZ9KJU5a8hdsVtTv21pYiGWiPbmVcU1Pz72FFqpk9pSRR, 12234)"]},
{"funargs", "chain_base_tx", ["Chain.OracleRegisterTx"]},
{"funargs", "chain_base_tx", ["Chain.OracleQueryTx"]},
{"funargs", "chain_base_tx", ["Chain.OracleResponseTx"]},
{"funargs", "chain_base_tx", ["Chain.OracleExtendTx"]},
{"funargs", "chain_base_tx", ["Chain.NamePreclaimTx"]},
{"funargs", "chain_base_tx", ["Chain.NameClaimTx(\"acoolname.chain\")"]},
{"funargs", "chain_base_tx", ["Chain.NameUpdateTx(#ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)"]},
{"funargs", "chain_base_tx", ["Chain.NameRevokeTx(#ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)"]},
{"funargs", "chain_base_tx", ["Chain.NameTransferTx(ak_2dATVcZ9KJU5a8hdsVtTv21pYiGWiPbmVcU1Pz72FFqpk9pSRR, #ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)"]},
{"funargs", "chain_base_tx", ["Chain.GAAttachTx"]},
{"variant_types", "init", []}, {"variant_types", "init", []},
{"basic_auth", "init", []}, {"basic_auth", "init", []},
{"address_literals", "init", []}, {"address_literals", "init", []},
@@ -163,4 +143,4 @@ compilable_contracts() ->
not_yet_compilable(fate) -> not_yet_compilable(fate) ->
[]; [];
not_yet_compilable(aevm) -> not_yet_compilable(aevm) ->
["funargs", "strings"]. [].
+29 -142
View File
@@ -34,7 +34,9 @@ simple_compile_test_() ->
#{fate_code := Code} when Backend == fate -> #{fate_code := Code} when Backend == fate ->
Code1 = aeb_fate_code:deserialize(aeb_fate_code:serialize(Code)), Code1 = aeb_fate_code:deserialize(aeb_fate_code:serialize(Code)),
?assertMatch({X, X}, {Code1, Code}); ?assertMatch({X, X}, {Code1, Code});
Error -> io:format("\n\n~p\n\n", [Error]), print_and_throw(Error) ErrBin ->
io:format("\n~s", [ErrBin]),
error(ErrBin)
end end
end} || ContractName <- compilable_contracts(), Backend <- [aevm, fate], end} || ContractName <- compilable_contracts(), Backend <- [aevm, fate],
not lists:member(ContractName, not_compilable_on(Backend))] ++ not lists:member(ContractName, not_compilable_on(Backend))] ++
@@ -90,23 +92,6 @@ simple_compile_test_() ->
end} || Backend <- [aevm, fate] ] ++ end} || Backend <- [aevm, fate] ] ++
[]. [].
%% Check if all modules in the standard library compile
stdlib_test_() ->
{ok, Files} = file:list_dir(aeso_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}, {backend, fate}],
case aeso_compiler:from_string(String, Options) of
{ok, #{fate_code := Code}} ->
Code1 = aeb_fate_code:deserialize(aeb_fate_code:serialize(Code)),
?assertMatch({X, X}, {Code1, Code});
{error, Error} -> io:format("\n\n~p\n\n", [Error]), print_and_throw(Error)
end
end} || File <- Files,
lists:suffix(".aes", File)
].
check_errors(no_error, Actual) -> ?assertMatch(#{}, Actual); check_errors(no_error, Actual) -> ?assertMatch(#{}, Actual);
check_errors(Expect, #{}) -> check_errors(Expect, #{}) ->
?assertEqual({error, Expect}, ok); ?assertEqual({error, Expect}, ok);
@@ -169,7 +154,6 @@ compilable_contracts() ->
"events", "events",
"include", "include",
"basic_auth", "basic_auth",
"basic_auth_tx",
"bitcoin_auth", "bitcoin_auth",
"address_literals", "address_literals",
"bytes_equality", "bytes_equality",
@@ -178,7 +162,6 @@ compilable_contracts() ->
"bytes_to_x", "bytes_to_x",
"bytes_concat", "bytes_concat",
"aens", "aens",
"aens_update",
"tuple_match", "tuple_match",
"cyclic_include", "cyclic_include",
"stdlib_include", "stdlib_include",
@@ -188,26 +171,18 @@ compilable_contracts() ->
"payable", "payable",
"unapplied_builtins", "unapplied_builtins",
"underscore_number_literals", "underscore_number_literals",
"pairing_crypto",
"qualified_constructor", "qualified_constructor",
"let_patterns", "let_patterns",
"lhs_matching", "lhs_matching",
"more_strings", "hermetization_turnoff"
"protected_call",
"hermetization_turnoff",
"multiple_contracts",
"clone",
"clone_simple",
"create",
"child_contract_init_bug",
"using_namespace",
"assign_patterns",
"patterns_guards",
"test" % Custom general-purpose test file. Keep it last on the list.
]. ].
not_compilable_on(fate) -> []; not_compilable_on(fate) -> [];
not_compilable_on(aevm) -> compilable_contracts(). not_compilable_on(aevm) ->
["stdlib_include",
"manual_stdlib_include",
"hermetization_turnoff"
].
debug_mode_contracts() -> debug_mode_contracts() ->
["hermetization_turnoff"]. ["hermetization_turnoff"].
@@ -238,7 +213,6 @@ failing_contracts() ->
, ?PARSE_ERROR(vsemi, [<<?Pos(3, 3) "Unexpected indentation. Did you forget a '}'?">>]) , ?PARSE_ERROR(vsemi, [<<?Pos(3, 3) "Unexpected indentation. Did you forget a '}'?">>])
, ?PARSE_ERROR(vclose, [<<?Pos(4, 3) "Unexpected indentation. Did you forget a ']'?">>]) , ?PARSE_ERROR(vclose, [<<?Pos(4, 3) "Unexpected indentation. Did you forget a ']'?">>])
, ?PARSE_ERROR(indent_fail, [<<?Pos(3, 2) "Unexpected token 'entrypoint'.">>]) , ?PARSE_ERROR(indent_fail, [<<?Pos(3, 2) "Unexpected token 'entrypoint'.">>])
, ?PARSE_ERROR(assign_pattern_to_pattern, [<<?Pos(3, 22) "Unexpected token '='.">>])
%% Type errors %% Type errors
, ?TYPE_ERROR(name_clash, , ?TYPE_ERROR(name_clash,
@@ -413,10 +387,10 @@ failing_contracts() ->
[<<?Pos(12, 42) [<<?Pos(12, 42)
"Cannot unify int\n" "Cannot unify int\n"
" and string\n" " and string\n"
"when checking the type of the expression at line 12, column 42\n" "when checking the record projection at line 12, column 42\n"
" r.foo() : map(int, string)\n" " r.foo : (gas : int, value : int) => Remote.themap\n"
"against the expected type\n" "against the expected type\n"
" map(string, int)">>]) " (gas : int, value : int) => map(string, int)">>])
, ?TYPE_ERROR(not_toplevel_include, , ?TYPE_ERROR(not_toplevel_include,
[<<?Pos(2, 11) [<<?Pos(2, 11)
"Include of 'included.aes' at line 2, column 11\nnot allowed, include only allowed at top level.">>]) "Include of 'included.aes' at line 2, column 11\nnot allowed, include only allowed at top level.">>])
@@ -581,15 +555,11 @@ failing_contracts() ->
"Cannot unify int\n and string\nwhen checking the type of the pattern at line 2, column 53\n x : int\nagainst the expected type\n string">> "Cannot unify int\n and string\nwhen checking the type of the pattern at line 2, column 53\n x : int\nagainst the expected type\n string">>
]) ])
, ?TYPE_ERROR(map_as_map_key, , ?TYPE_ERROR(map_as_map_key,
[<<?Pos(5, 47) [<<?Pos(5, 25)
"Invalid key type\n" "Invalid key type\n"
" map(int, int)\n" " map(int, int)\n"
"Map keys cannot contain other maps.">>, "Map keys cannot contain other maps.">>,
<<?Pos(6, 31) <<?Pos(6, 25)
"Invalid key type\n"
" list(map(int, int))\n"
"Map keys cannot contain other maps.">>,
<<?Pos(6, 31)
"Invalid key type\n" "Invalid key type\n"
" lm\n" " lm\n"
"Map keys cannot contain other maps.">>]) "Map keys cannot contain other maps.">>])
@@ -654,9 +624,9 @@ failing_contracts() ->
<<?Pos(2, 1) <<?Pos(2, 1)
"Cannot compile with this version of the compiler,\n" "Cannot compile with this version of the compiler,\n"
"because it does not satisfy the constraint ", Version/binary, " == 9.9.9">>]) "because it does not satisfy the constraint ", Version/binary, " == 9.9.9">>])
, ?TYPE_ERROR(interface_with_defs, , ?TYPE_ERROR(multiple_contracts,
[<<?Pos(2, 3) [<<?Pos(2, 3)
"Contract interfaces cannot contain defined functions or entrypoints.\n" "Only the main contract can contain defined functions or entrypoints.\n"
"Fix: replace the definition of 'foo' by a type signature.">>]) "Fix: replace the definition of 'foo' by a type signature.">>])
, ?TYPE_ERROR(contract_as_namespace, , ?TYPE_ERROR(contract_as_namespace,
[<<?Pos(5, 28) [<<?Pos(5, 28)
@@ -711,12 +681,6 @@ failing_contracts() ->
"Empty record/map update\n" "Empty record/map update\n"
" r {}">> " r {}">>
]) ])
, ?TYPE_ERROR(bad_protected_call,
[<<?Pos(6, 22)
"Invalid 'protected' argument\n"
" (0 : int) == (1 : int) : bool\n"
"It must be either 'true' or 'false'.">>
])
, ?TYPE_ERROR(bad_function_block, , ?TYPE_ERROR(bad_function_block,
[<<?Pos(4, 5) [<<?Pos(4, 5)
"Mismatch in the function block. Expected implementation/type declaration of g function">>, "Mismatch in the function block. Expected implementation/type declaration of g function">>,
@@ -752,71 +716,6 @@ failing_contracts() ->
, ?TYPE_ERROR(bad_state, , ?TYPE_ERROR(bad_state,
[<<?Pos(4, 16) [<<?Pos(4, 16)
"Conflicting updates for field 'foo'">>]) "Conflicting updates for field 'foo'">>])
, ?TYPE_ERROR(factories_type_errors,
[<<?Pos(10,18)
"Chain.clone requires `ref` named argument of contract type.">>,
<<?Pos(11,18)
"Cannot unify (gas : int, value : int, protected : bool) => if(protected, option(void), void)\n and (gas : int, value : int, protected : bool, int, bool) => 'b\n"
"when checking contract construction of type\n (gas : int, value : int, protected : bool) =>\n if(protected, option(void), void) (at line 11, column 18)\nagainst the expected type\n (gas : int, value : int, protected : bool, int, bool) => 'b">>,
<<?Pos(12,37)
"Cannot unify int\n and bool\n"
"when checking named argument\n gas : int\nagainst inferred type\n bool">>,
<<?Pos(13,18),
"Kaboom is not implemented.\n"
"when resolving arguments of variadic function\n Chain.create">>,
<<?Pos(18,18)
"Cannot unify (gas : int, value : int, protected : bool, int, bool) => if(protected, option(void), void)\n and (gas : int, value : int, protected : bool) => 'a\n"
"when checking contract construction of type\n (gas : int, value : int, protected : bool, int, bool) =>\n if(protected, option(void), void) (at line 18, column 18)\nagainst the expected type\n (gas : int, value : int, protected : bool) => 'a">>,
<<?Pos(19,42),
"Named argument protected (at line 19, column 42) is not one of the expected named arguments\n - value : int">>,
<<?Pos(20,42),
"Cannot unify int\n and bool\n"
"when checking named argument\n value : int\nagainst inferred type\n bool">>
])
, ?TYPE_ERROR(ambiguous_main,
[<<?Pos(1,1)
"Could not deduce the main contract. You can point it out manually with the `main` keyword.">>
])
, ?TYPE_ERROR(no_main_contract,
[<<?Pos(0,0)
"No contract defined.">>
])
, ?TYPE_ERROR(multiple_main_contracts,
[<<?Pos(1,6)
"Only one main contract can be defined.">>
])
, ?TYPE_ERROR(using_namespace_ambiguous_name,
[ <<?Pos(2,3)
"Ambiguous name: Xa.f at line 2, column 3\nXb.f at line 5, column 3">>
, <<?Pos(13,23)
"Unbound variable A.f at line 13, column 23">>
])
, ?TYPE_ERROR(using_namespace_wrong_scope,
[ <<?Pos(19,5)
"Unbound variable f at line 19, column 5">>
, <<?Pos(21,23)
"Unbound variable f at line 21, column 23">>
])
, ?TYPE_ERROR(using_namespace_undefined,
[<<?Pos(2,3)
"Cannot use undefined namespace MyUndefinedNamespace">>
])
, ?TYPE_ERROR(using_namespace_undefined_parts,
[<<?Pos(5,3)
"The namespace Nsp does not define the following names: a">>
])
, ?TYPE_ERROR(using_namespace_hidden_parts,
[<<?Pos(8,23)
"Unbound variable g at line 8, column 23">>
])
, ?TYPE_ERROR(stateful_pattern_guard,
[<<?Pos(8,12)
"Cannot reference stateful function g (at line 8, column 12) in a pattern guard.">>
])
, ?TYPE_ERROR(non_boolean_pattern_guard,
[<<?Pos(4,24)
"Cannot unify string\n and bool\nwhen checking the type of the expression at line 4, column 24\n \"y\" : string\nagainst the expected type\n bool">>
])
]. ].
-define(Path(File), "code_errors/" ??File). -define(Path(File), "code_errors/" ??File).
@@ -830,7 +729,9 @@ failing_contracts() ->
{fate, ?Msg(File, Line, Col, ErrFATE)}]}). {fate, ?Msg(File, Line, Col, ErrFATE)}]}).
failing_code_gen_contracts() -> failing_code_gen_contracts() ->
[ ?SAME(missing_definition, 2, 14, [ ?SAME(last_declaration_must_be_contract, 1, 1,
"Expected a contract as the last declaration instead of the namespace 'LastDeclarationIsNotAContract'")
, ?SAME(missing_definition, 2, 14,
"Missing definition of function 'foo'.") "Missing definition of function 'foo'.")
, ?AEVM(polymorphic_entrypoint, 2, 17, , ?AEVM(polymorphic_entrypoint, 2, 17,
"The argument\n" "The argument\n"
@@ -928,8 +829,6 @@ failing_code_gen_contracts() ->
"Invalid state type\n" "Invalid state type\n"
" {f : (int) => int}\n" " {f : (int) => int}\n"
"The state cannot contain functions in the AEVM. Use FATE if you need this.") "The state cannot contain functions in the AEVM. Use FATE if you need this.")
, ?FATE(child_with_decls, 2, 14,
"Missing definition of function 'f'.")
]. ].
validation_test_() -> validation_test_() ->
@@ -967,26 +866,14 @@ validation_fails() ->
"Byte code contract is not payable, but source code contract is.">>]}]. "Byte code contract is not payable, but source code contract is.">>]}].
validate(Contract1, Contract2) -> validate(Contract1, Contract2) ->
case compile(fate, Contract1) of ByteCode = #{ fate_code := FCode } = compile(fate, Contract1),
ByteCode = #{ fate_code := FCode } -> FCode1 = aeb_fate_code:serialize(aeb_fate_code:strip_init_function(FCode)),
FCode1 = aeb_fate_code:serialize(aeb_fate_code:strip_init_function(FCode)), Source = aeso_test_utils:read_contract(Contract2),
Source = aeso_test_utils:read_contract(Contract2), aeso_compiler:validate_byte_code(
aeso_compiler:validate_byte_code( ByteCode#{ byte_code := FCode1 }, Source,
ByteCode#{ byte_code := FCode1 }, Source, case lists:member(Contract2, debug_mode_contracts()) of
case lists:member(Contract2, debug_mode_contracts()) of true -> [debug_mode];
true -> [debug_mode]; false -> []
false -> [] end ++
end ++ [{backend, fate}, {include, {file_system, [aeso_test_utils:contract_path()]}}]).
[{backend, fate}, {include, {file_system, [aeso_test_utils:contract_path()]}}]);
Error -> print_and_throw(Error)
end.
print_and_throw(Err) ->
case Err of
ErrBin when is_binary(ErrBin) ->
io:format("\n~s", [ErrBin]),
error(ErrBin);
Errors ->
io:format("Compilation error:\n~s", [string:join([aeso_errors:pp(E) || E <- Errors], "\n\n")]),
error(compilation_error)
end.
+5 -6
View File
@@ -12,12 +12,12 @@ simple_contracts_test_() ->
fun(_) -> ok end, fun(_) -> ok end,
[{"Parse a contract with an identity function.", [{"Parse a contract with an identity function.",
fun() -> fun() ->
Text = "main contract Identity =\n" Text = "contract Identity =\n"
" function id(x) = x\n", " function id(x) = x\n",
?assertMatch( ?assertMatch(
[{contract_main, _, {con, _, "Identity"}, [{contract, _, {con, _, "Identity"},
[{letfun, _, {id, _, "id"}, [{id, _, "x"}], {id, _, "_"}, [{letfun, _, {id, _, "id"}, [{id, _, "x"}], {id, _, "_"},
[{guarded, _, [], {id, _, "x"}}]}]}], parse_string(Text)), {id, _, "x"}}]}], parse_string(Text)),
ok ok
end}, end},
{"Operator precedence test.", {"Operator precedence test.",
@@ -63,8 +63,7 @@ simple_contracts_test_() ->
%% Parse tests of example contracts %% Parse tests of example contracts
[ {lists:concat(["Parse the ", Contract, " contract."]), [ {lists:concat(["Parse the ", Contract, " contract."]),
fun() -> roundtrip_contract(Contract) end} fun() -> roundtrip_contract(Contract) end}
|| Contract <- [counter, voting, all_syntax, '05_greeter', aeproof, || Contract <- [counter, voting, all_syntax, '05_greeter', aeproof, multi_sig, simple_storage, fundme, dutch_auction] ]
multi_sig, simple_storage, fundme, dutch_auction, utf8] ]
}. }.
parse_contract(Name) -> parse_contract(Name) ->
@@ -86,7 +85,7 @@ parse_expr(Text) ->
round_trip(Text) -> round_trip(Text) ->
Contract = parse_string(Text), Contract = parse_string(Text),
Text1 = prettypr:format(aeso_pretty:decls(strip_stdlib(Contract))), Text1 = prettypr:format(aeso_pretty:decls(strip_stdlib(Contract))),
Contract1 = parse_string(aeso_scan:utf8_encode(Text1)), Contract1 = parse_string(Text1),
NoSrcLoc = remove_line_numbers(Contract), NoSrcLoc = remove_line_numbers(Contract),
NoSrcLoc1 = remove_line_numbers(Contract1), NoSrcLoc1 = remove_line_numbers(Contract1),
?assertMatch(NoSrcLoc, diff(NoSrcLoc, NoSrcLoc1)). ?assertMatch(NoSrcLoc, diff(NoSrcLoc, NoSrcLoc1)).
+1 -1
View File
@@ -1,5 +1,5 @@
contract Identity = contract Identity =
function main_fun (x:int) = x function main (x:int) = x
function __call() = 12 function __call() = 12
+2 -2
View File
@@ -1,5 +1,5 @@
contract interface Remote = contract Remote =
entrypoint main_fun : (int) => unit entrypoint main : (int) => unit
contract AddrChain = contract AddrChain =
type o_type = oracle(string, map(string, int)) type o_type = oracle(string, map(string, int))
+1 -1
View File
@@ -1,5 +1,5 @@
contract interface Remote = contract Remote =
entrypoint foo : () => unit entrypoint foo : () => unit
contract AddressLiterals = contract AddressLiterals =
+1 -16
View File
@@ -33,22 +33,7 @@ contract AENSTest =
sign : signature) : unit = sign : signature) : unit =
AENS.claim(addr, name, salt, name_fee, signature = sign) AENS.claim(addr, name, salt, name_fee, signature = sign)
// TODO: update() -- how to handle pointers?
stateful entrypoint update(owner : address,
name : string,
ttl : option(Chain.ttl),
client_ttl : option(int),
pointers : option(map(string, AENS.pointee))) : unit =
AENS.update(owner, name, ttl, client_ttl, pointers)
stateful entrypoint signedUpdate(owner : address,
name : string,
ttl : option(Chain.ttl),
client_ttl : option(int),
pointers : option(map(string, AENS.pointee)),
sign : signature) : unit =
AENS.update(owner, name, ttl, client_ttl, pointers, signature = sign)
stateful entrypoint transfer(owner : address, stateful entrypoint transfer(owner : address,
new_owner : address, new_owner : address,
-17
View File
@@ -1,17 +0,0 @@
contract AENSUpdate =
stateful entrypoint update_name(owner : address, name : string) =
let p1 : AENS.pointee = AENS.AccountPt(Call.caller)
let p2 : AENS.pointee = AENS.OraclePt(Call.caller)
let p3 : AENS.pointee = AENS.ContractPt(Call.caller)
let p4 : AENS.pointee = AENS.ChannelPt(Call.caller)
AENS.update(owner, name, None, None,
Some({ ["account_pubkey"] = p1, ["oracle_pubkey"] = p2,
["contract_pubkey"] = p3, ["misc"] = p4 }))
entrypoint get_ttl(name : string) =
switch(AENS.lookup(name))
Some(AENS.Name(_, FixedTTL(ttl), _)) => ttl
entrypoint expiry(o : oracle(int, int)) : int =
Oracle.expiry(o)
+1 -5
View File
@@ -36,8 +36,6 @@ contract AllSyntax =
entrypoint init() = { entrypoint init() = {
johann = 1000, johann = 1000,
wolfgang = -10, wolfgang = -10,
/* TODO: This does not compile because of bug in the parser tester.
von = (2 + 2, 0, List.sum([x | k <- [1,2,3] von = (2 + 2, 0, List.sum([x | k <- [1,2,3]
, let l = k + 1 , let l = k + 1
, if(l < 10) , if(l < 10)
@@ -45,13 +43,11 @@ contract AllSyntax =
, Adam <- [Adam, Mickiewicz] , Adam <- [Adam, Mickiewicz]
, let x = f(l) , let x = f(l)
])), ])),
*/
von = (2 + 2, 0, List.sum([1,2,3,4])),
goethe = () } goethe = () }
function f() = function f() =
let kp = "nietzsche" let kp = "nietzsche"
// let p = "Пушкин" // TODO: this also doesn't do right round_trip... let p = "Пушкин"
let k(x : bytes(8)) : bytes(8) = Bytes.to_int(#fedcba9876543210) let k(x : bytes(8)) : bytes(8) = Bytes.to_int(#fedcba9876543210)
let f : () => address = () => ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt let f : () => address = () => ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt
-5
View File
@@ -1,5 +0,0 @@
contract C =
entrypoint f() = 123
contract D =
entrypoint f() = 123
@@ -1,4 +0,0 @@
contract AssignPatternToPattern =
entrypoint f() =
let x::(t::z = y) = [1, 2, 3]
(x + t)::y
-16
View File
@@ -1,16 +0,0 @@
include "List.aes"
contract AssignPatterns =
entrypoint test() = foo([1, 0, 2], (2, Some(3)), Some([4, 5]))
entrypoint foo(xs : list(int), p : int * option(int), some : option(list(int))) =
let x::(t = y::_) = xs
let z::_ = t
let (a, (o = Some(b))) = p
let Some((f = g::_)) = some
g + List.get(1, f)
x + y + z + a + b
+1 -1
View File
@@ -1,5 +1,5 @@
contract interface Remote = contract Remote =
entrypoint foo : () => unit entrypoint foo : () => unit
contract AddressLiterals = contract AddressLiterals =
-6
View File
@@ -1,6 +0,0 @@
contract interface Remote =
entrypoint id : int => int
contract ProtectedCall =
entrypoint bad(r : Remote) =
r.id(protected = 0 == 1, 18)
+1 -1
View File
@@ -1,3 +1,3 @@
function square(x) = x ^ 2 function square(x) = x ^ 2
contract Main = contract Main =
entrypoint main_fun() = square(10) entrypoint main() = square(10)
-74
View File
@@ -1,74 +0,0 @@
// namespace Chain =
// record tx = { paying_for : option(Chain.paying_for_tx)
// , ga_metas : list(Chain.ga_meta_tx)
// , actor : address
// , fee : int
// , ttl : int
// , tx : Chain.base_tx }
// datatype ga_meta_tx = GAMetaTx(address, int)
// datatype paying_for_tx = PayingForTx(address, int)
// datatype base_tx = SpendTx(address, int, string)
// | OracleRegisterTx | OracleQueryTx | OracleResponseTx | OracleExtendTx
// | NamePreclaimTx | NameClaimTx(hash) | NameUpdateTx(string)
// | NameRevokeTx(hash) | NameTransferTx(address, string)
// | ChannelCreateTx(address) | ChannelDepositTx(address, int) | ChannelWithdrawTx(address, int) |
// | ChannelForceProgressTx(address) | ChannelCloseMutualTx(address) | ChannelCloseSoloTx(address)
// | ChannelSlashTx(address) | ChannelSettleTx(address) | ChannelSnapshotSoloTx(address)
// | ContractCreateTx(int) | ContractCallTx(address, int)
// | GAAttachTx
// Contract replicating "normal" Aeternity authentication
contract BasicAuthTx =
record state = { nonce : int, owner : address }
datatype foo = Bar | Baz()
entrypoint init() = { nonce = 1, owner = Call.caller }
stateful entrypoint authorize(n : int, s : signature) : bool =
require(n >= state.nonce, "Nonce too low")
require(n =< state.nonce, "Nonce too high")
put(state{ nonce = n + 1 })
switch(Auth.tx_hash)
None => abort("Not in Auth context")
Some(tx_hash) =>
let Some(tx0) = Auth.tx
let x : option(Chain.paying_for_tx) = tx0.paying_for
let x : list(Chain.ga_meta_tx) = tx0.ga_metas
let x : int = tx0.fee + tx0.ttl
let x : address = tx0.actor
let x : Chain.tx = { tx = Chain.NamePreclaimTx, paying_for = None, ga_metas = [],
fee = 123, ttl = 0, actor = Call.caller }
switch(tx0.tx)
Chain.SpendTx(receiver, amount, payload) => verify(tx_hash, n, s)
Chain.OracleRegisterTx => false
Chain.OracleQueryTx => false
Chain.OracleResponseTx => false
Chain.OracleExtendTx => false
Chain.NamePreclaimTx => false
Chain.NameClaimTx(name) => false
Chain.NameUpdateTx(name) => false
Chain.NameRevokeTx(name) => false
Chain.NameTransferTx(to, name) => false
Chain.ChannelCreateTx(other_party) => false
Chain.ChannelDepositTx(channel, amount) => false
Chain.ChannelWithdrawTx(channel, amount) => false
Chain.ChannelForceProgressTx(channel) => false
Chain.ChannelCloseMutualTx(channel) => false
Chain.ChannelCloseSoloTx(channel) => false
Chain.ChannelSlashTx(channel) => false
Chain.ChannelSettleTx(channel) => false
Chain.ChannelSnapshotSoloTx(channel) => false
Chain.ContractCreateTx(amount) => false
Chain.ContractCallTx(ct_address, amount) => false
Chain.GAAttachTx => false
function verify(tx_hash, n, s) =
Crypto.verify_sig(to_sign(tx_hash, n), state.owner, s)
entrypoint to_sign(h : hash, n : int) =
Crypto.blake2b((h, n))
entrypoint weird_string() : string =
"\x19Weird String\x42\nMore\n"
+1 -1
View File
@@ -1,4 +1,4 @@
include "String.aes"
contract BytesToX = contract BytesToX =
entrypoint to_int(b : bytes(42)) : int = Bytes.to_int(b) entrypoint to_int(b : bytes(42)) : int = Bytes.to_int(b)
@@ -17,7 +17,7 @@ contract ChannelOnChainContractOracle =
bets = {} bets = {}
} }
public stateful function place_bet(answer: string) = public stateful function place_bet(answer: string) =
switch(Map.lookup(answer, state.bets)) switch(Map.lookup(answer, state.bets))
None => None =>
put(state{ bets = state.bets{[answer] = Call.caller}}) put(state{ bets = state.bets{[answer] = Call.caller}})
@@ -25,9 +25,6 @@ contract ChannelOnChainContractOracle =
Some(_value) => Some(_value) =>
"bet_already_taken" "bet_already_taken"
public function expiry() =
Oracle.expiry(state.oracle)
public function query_fee() = public function query_fee() =
Oracle.query_fee(state.oracle) Oracle.query_fee(state.oracle)
@@ -38,7 +35,7 @@ contract ChannelOnChainContractOracle =
switch(Oracle.get_answer(state.oracle, q)) switch(Oracle.get_answer(state.oracle, q))
None => None =>
"no response" "no response"
Some(result) => Some(result) =>
if(state.question == Oracle.get_question(state.oracle, q)) if(state.question == Oracle.get_question(state.oracle, q))
switch(Map.lookup(result, state.bets)) switch(Map.lookup(result, state.bets))
None => None =>
@@ -1,8 +0,0 @@
contract Identity =
record state = {foo: int, bar: string}
entrypoint init() = {foo = 0, bar = ""}
main contract IdentityService =
stateful entrypoint createNewIdentity() : Identity =
put(())
Chain.create()
-28
View File
@@ -1,28 +0,0 @@
contract interface HigherOrderState =
entrypoint init : () => void
entrypoint apply : int => int
stateful entrypoint inc : int => unit
contract interface LowerDisorderAnarchy =
entrypoint init : (int) => void
main contract C =
// both `s` and `l` should be of type `HigherOrderState` in this test
stateful entrypoint run_clone(s : HigherOrderState, l : LowerDisorderAnarchy) : HigherOrderState =
let s1 = Chain.clone(ref=s)
let Some(s2) = Chain.clone(ref=s, protected=true)
let None = Chain.clone(ref=s, protected=true, gas=1)
let None = Chain.clone(ref=l, protected=true, 123) // since it should be HigherOrderState underneath
let s3 = Chain.clone(ref=s1)
require(s1.apply(2137) == 2137, "APPLY_S1_0")
require(s2.apply(2137) == 2137, "APPLY_S2_0")
require(s3.apply(2137) == 2137, "APPLY_S3_0")
s1.inc(1)
s2.inc(1)
s1.inc(1)
require(s1.apply(2137) == 2139, "APPLY_S1_2")
require(s2.apply(2137) == 2138, "APPLY_S2_1")
require(s3.apply(2137) == 2137, "APPLY_S3_0")
s1
-7
View File
@@ -1,7 +0,0 @@
contract interface I =
entrypoint init : () => void
contract C =
stateful entrypoint f(i : I) =
let Some(c1) = Chain.clone(ref=i, protected = true)
2
@@ -5,5 +5,5 @@ contract BadAENSresolve =
function fail() : t(int) = function fail() : t(int) =
AENS.resolve("foo.aet", "whatever") AENS.resolve("foo.aet", "whatever")
entrypoint main_fun() = () entrypoint main() = ()
@@ -1,5 +0,0 @@
contract C =
entrypoint f : () => unit
main contract M =
entrypoint f() = 123
@@ -3,4 +3,4 @@ contract MapAsMapKey =
function foo(m) : t(int => int) = {[m] = 0} function foo(m) : t(int => int) = {[m] = 0}
entrypoint main_fun() = () entrypoint main() = ()
@@ -2,4 +2,4 @@ contract HigherOrderQueryType =
stateful function foo(o) : oracle_query(_, string ) = stateful function foo(o) : oracle_query(_, string ) =
Oracle.query(o, (x) => x + 1, 100, RelativeTTL(100), RelativeTTL(100)) Oracle.query(o, (x) => x + 1, 100, RelativeTTL(100), RelativeTTL(100))
entrypoint main_fun() = () entrypoint main() = ()
@@ -2,4 +2,4 @@ contract HigherOrderResponseType =
stateful function foo(o, q : oracle_query(string, _)) = stateful function foo(o, q : oracle_query(string, _)) =
Oracle.respond(o, q, (x) => x + 1) Oracle.respond(o, q, (x) => x + 1)
entrypoint main_fun() = () entrypoint main() = ()
@@ -0,0 +1,2 @@
namespace LastDeclarationIsNotAContract =
function add(x, y) = x + y
@@ -1,3 +1,3 @@
contract MissingDefinition = contract MissingDefinition =
entrypoint foo : int => int entrypoint foo : int => int
entrypoint main_fun() = foo(0) entrypoint main() = foo(0)
@@ -3,5 +3,5 @@ contract PolymorphicAENSresolve =
function fail() : option('a) = function fail() : option('a) =
AENS.resolve("foo.aet", "whatever") AENS.resolve("foo.aet", "whatever")
entrypoint main_fun() = () entrypoint main() = ()
@@ -3,4 +3,4 @@ contract MapAsMapKey =
function foo(m) : t('a) = {[m] = 0} function foo(m) : t('a) = {[m] = 0}
entrypoint main_fun() = () entrypoint main() = ()
@@ -2,4 +2,4 @@ contract PolymorphicQueryType =
stateful function is_oracle(o) = stateful function is_oracle(o) =
Oracle.check(o) Oracle.check(o)
entrypoint main_fun() = () entrypoint main() = ()
@@ -2,4 +2,4 @@ contract PolymorphicResponseType =
function is_oracle(o : oracle(string, 'r)) = function is_oracle(o : oracle(string, 'r)) =
Oracle.check(o) Oracle.check(o)
entrypoint main_fun(o : oracle(string, int)) = is_oracle(o) entrypoint main(o : oracle(string, int)) = is_oracle(o)
@@ -1,4 +1,4 @@
contract interface Remote = contract Remote =
entrypoint foo : int => int entrypoint foo : int => int
contract UnappliedContractCall = contract UnappliedContractCall =
@@ -1,5 +1,5 @@
contract UnappliedNamedArgBuiltin = contract UnappliedNamedArgBuiltin =
// Allowed in FATE, but not AEVM // Allowed in FATE, but not AEVM
stateful entrypoint main_fun(s) = stateful entrypoint main(s) =
let reg = Oracle.register let reg = Oracle.register
reg(signature = s, Contract.address, 100, RelativeTTL(100)) : oracle(int, int) reg(signature = s, Contract.address, 100, RelativeTTL(100)) : oracle(int, int)
+1 -1
View File
@@ -1,5 +1,5 @@
contract interface Remote = contract Remote =
entrypoint up_to : (int) => list(int) entrypoint up_to : (int) => list(int)
entrypoint sum : (list(int)) => int entrypoint sum : (list(int)) => int
entrypoint some_string : () => string entrypoint some_string : () => string
+1 -1
View File
@@ -1,4 +1,4 @@
contract interface Foo = contract Foo =
entrypoint foo : () => int entrypoint foo : () => int
contract Fail = contract Fail =
-28
View File
@@ -1,28 +0,0 @@
contract IntegerAdder =
entrypoint init() = ()
entrypoint addIntegers(x, y) = x + y
contract IntegerAdderHolder =
type state = IntegerAdder
stateful entrypoint init() = Chain.create() : IntegerAdder
entrypoint get() = state
contract IntegerAdderFactory =
entrypoint init() = ()
stateful entrypoint new() =
let i = Chain.create() : IntegerAdderHolder
i.get()
payable contract ValueAdder =
entrypoint init() = ()
stateful entrypoint addValue(x) =
let integerAdderFactory = Chain.create()
let adder = integerAdderFactory.new()
adder.addIntegers(x, Contract.balance)
main contract EnterpriseContract =
entrypoint init() = ()
stateful payable entrypoint increaseByThree(x) =
require(Call.value >= 3, "Price for addition = 3AEtto, insufficient funds")
let threeAdder = Chain.create(value = 3)
threeAdder.addValue(x)
+1 -4
View File
@@ -1,6 +1,6 @@
// Testing primitives for accessing the block chain environment // Testing primitives for accessing the block chain environment
contract interface Interface = contract Interface =
entrypoint contract_address : () => address entrypoint contract_address : () => address
entrypoint call_origin : () => address entrypoint call_origin : () => address
entrypoint call_caller : () => address entrypoint call_caller : () => address
@@ -44,9 +44,6 @@ contract Environment =
// Gas price // Gas price
entrypoint call_gas_price() : int = Call.gas_price entrypoint call_gas_price() : int = Call.gas_price
// Fee
entrypoint call_fee() : int = Call.fee
// -- Information about the chain --- // -- Information about the chain ---
// Account balances // Account balances
+1 -1
View File
@@ -1,4 +1,4 @@
contract interface Remote = contract Remote =
entrypoint dummy : () => unit entrypoint dummy : () => unit
contract Events = contract Events =
+1 -1
View File
@@ -1,6 +1,6 @@
// An implementation of the factorial function where each recursive // An implementation of the factorial function where each recursive
// call is to another contract. Not the cheapest way to compute factorial. // call is to another contract. Not the cheapest way to compute factorial.
contract interface FactorialServer = contract FactorialServer =
entrypoint fac : (int) => int entrypoint fac : (int) => int
contract Factorial = contract Factorial =
-24
View File
@@ -1,24 +0,0 @@
contract interface Kaboom =
entrypoint init : () => void
contract Bakoom =
type state = int
entrypoint init(x, b) = if(b) x else 0
main contract Test =
stateful entrypoint test(k : Kaboom) =
let k_bad1 = Chain.clone() : Kaboom
let k_bad2 = Chain.clone(ref=k, 123, true)
let k_bad3 = Chain.clone(ref=k, gas=true)
let k_bad4 = Chain.create() : Kaboom
let k_gud1 = Chain.clone(ref=k)
let Some(k_gud2) = Chain.clone(ref=k, protected=true)
let Some(k_gud3) = Chain.clone(ref=k, value=10, protected=true, gas=123)
let b_bad1 = Chain.create() : Bakoom
let b_bad2 = Chain.create(123, true, protected=true) : Bakoom
let b_bad3 = Chain.create(123, true, value=true) : Bakoom
let b_gud1 = Chain.create(123, true) : Bakoom
let b_gud2 = Chain.create(123, true, value=100) : Bakoom
b_gud1
+1 -8
View File
@@ -1,4 +1,4 @@
include "String.aes"
contract FunctionArguments = contract FunctionArguments =
entrypoint sum(n : int, m: int) = entrypoint sum(n : int, m: int) =
@@ -50,10 +50,3 @@ contract FunctionArguments =
entrypoint singleton_rec(r : singleton_r) = entrypoint singleton_rec(r : singleton_r) =
r.x r.x
entrypoint aens_name(n : AENS.name) = true
entrypoint aens_pointee(p : AENS.pointee) = true
entrypoint chain_ga_meta_tx(tx : Chain.ga_meta_tx) = true
entrypoint chain_paying_for_tx(tx : Chain.paying_for_tx) = true
entrypoint chain_base_tx(tx : Chain.base_tx) = true
+3 -2
View File
@@ -1,2 +1,3 @@
main contract Identity =
entrypoint main_fun (x:int) = x contract Identity =
entrypoint main (x:int) = x
-5
View File
@@ -1,5 +0,0 @@
contract interface ContractOne =
entrypoint foo() = "foo"
contract ContractTwo =
entrypoint bar() = "bar"
+1 -1
View File
@@ -16,7 +16,7 @@ contract LHSMatching =
let null(_ :: _) = false let null(_ :: _) = false
!null(xs) !null(xs)
entrypoint main_fun() = entrypoint main() =
from_some(Some([0])) from_some(Some([0]))
++ append([length([true]), 2, 3], [4, 5, 6]) ++ append([length([true]), 2, 3], [4, 5, 6])
++ [7 | if (local_match([false]))] ++ [7 | if (local_match([false]))]
+1 -1
View File
@@ -1,3 +1,3 @@
contract MissingEventType = contract MissingEventType =
entrypoint main_fun() = entrypoint main() =
Chain.event("MAIN") Chain.event("MAIN")
-14
View File
@@ -1,14 +0,0 @@
include "String.aes"
contract StringX =
entrypoint test() =
let s1 = "a string"
let s2 = "another string"
let s = String.concat(s1, s2)
String.sha256(s)
String.length(s1)
String.from_list(String.to_list(s))
String.split(4, s1)
String.at(2, s2)
String.tokens(s, ",")
String.to_upper(s1)
String.to_lower(s2)
+4 -6
View File
@@ -1,7 +1,5 @@
contract Child = contract ContractOne =
entrypoint entrypoint foo() = "foo"
add2 : int => int
add2(x) = x + 2
main contract Main = contract ContractTwo =
entrypoint add4(x, c : Child) = c.add2(x) + 2 entrypoint bar() = "bar"
@@ -1,5 +0,0 @@
main contract C =
entrypoint f() = 123
main contract D =
entrypoint f() = 123
-2
View File
@@ -1,2 +0,0 @@
contract interface C =
entrypoint f : () => unit
@@ -1,4 +0,0 @@
contract C =
type state = int
entrypoint init(x) | "y" = 1
+1 -1
View File
@@ -1,4 +1,4 @@
contract interface C1 = contract C1 =
entrypoint f : int entrypoint f : int
contract C = contract C =
-30
View File
@@ -1,30 +0,0 @@
include "BLS12_381.aes"
contract GrothVerify =
type fr = BLS12_381.fr
type g1 = BLS12_381.g1
type g2 = BLS12_381.g2
record proof = { a : g1, b : g2, c : g1 }
record verify_key = { a : g1, b : g2, c : g2, d : g2, ic : list(g1) }
record state = { vk : verify_key }
entrypoint init(vk0 : verify_key) = {vk = vk0}
entrypoint verify_proof(p : proof, input : list(fr)) =
let vk = state.vk
let vk_x = calc_vk_x(vk.ic, input)
BLS12_381.pairing_check([BLS12_381.g1_neg(p.a), vk.a, vk_x, p.c],
[p.b, vk.b, vk.c, vk.d])
function calc_vk_x(ics : list(g1), xs : list(fr)) =
switch(ics)
(ic :: ics) => calc_vk_x_(ic, ics, xs)
function calc_vk_x_(vk_x : g1, ics : list(g1), xs : list(fr)) =
switch((ics, xs))
([], []) => vk_x
(ic :: ics, x :: xs) => calc_vk_x_(BLS12_381.g1_add(vk_x, BLS12_381.g1_mul(x, ic)), ics, xs)
-19
View File
@@ -1,19 +0,0 @@
include "List.aes"
contract C =
type state = int
entrypoint init() = f([1, 2, 3, 4])
function
f(x::[])
| x > 1, x < 10 = 1
| x < 1 = 9
f(x::y::[]) = 2
f(x::y::z) = switch(z)
[] => 4
a::[]
| a > 10, a < 20 => 5
| a > 5 => 8
b | List.length(b) > 5 => 6
c => 7

Some files were not shown because too many files have changed in this diff Show More