Compare commits

..

1 Commits

Author SHA1 Message Date
radrow a5cbf2fd79 Updated CHANGELOG 2020-03-23 12:48:47 +01:00
62 changed files with 764 additions and 2391 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
+10 -98
View File
@@ -6,100 +6,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
### Added ### Added
- Added documentation (actually, moved from `protocol`)
- Added standard library docs. Moved builtin docs from `sophia.md` to
`sophia_stdlib.md`
- Added library for rational numbers
### Changed ### Changed
- Optimized `List.aes` library
### Removed ### 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]
### Added
- Added documentation (moved from `protocol`)
- `Frac.aes` library for rational numbers
- Added some more meaningful error messages
- Exported several parsing functionalities
- With option `keep_included` it is possible to see which files were included during the parse
- There is a function `run_parser` that be used to evaluate any parsing rule
- Exported parsers: `body`, `type` and `decl`
### Changed
- Performance improvements in the standard library
- Fixed ACI encoder to handle `-` unary operator
- Fixed including by absolute path
- Fixed variant type printing in the ACI error messages
- Fixed pretty printing of combined function clauses
### Removed
- `let` definitions are no longer supported in the toplevel of the contract
- type declarations are no longer supported
## [4.2.0] - 2020-01-15 ## [4.2.0] - 2020-01-15
### Added ### Added
- Allow separate entrypoint/function type signature and definition, and pattern - Allow separate entrypoint/function type signature and definition, and pattern
@@ -144,7 +58,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
(de-)construct byte arrays. (de-)construct byte arrays.
- `[a..b]` language construct, returning the list of numbers between - `[a..b]` language construct, returning the list of numbers between
`a` and `b` (inclusive). Returns the empty list if `a` > `b`. `a` and `b` (inclusive). Returns the empty list if `a` > `b`.
- [Standard libraries](https://github.com/aeternity/aesophia/blob/master/docs/sophia_stdlib.md) - [Standard libraries] (https://github.com/aeternity/protocol/blob/master/contracts/sophia_stdlib.md)
- Checks that `init` is not called from other functions. - Checks that `init` is not called from other functions.
- FATE backend - the compiler is able to produce VM code for both `AEVM` and `FATE`. Many - FATE backend - the compiler is able to produce VM code for both `AEVM` and `FATE`. Many
of the APIs now take `{backend, aevm | fate}` to decide wich backend to produce artifacts of the APIs now take `{backend, aevm | fate}` to decide wich backend to produce artifacts
@@ -283,12 +197,10 @@ 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/v5.0.0...HEAD [Unreleased]: https://github.com/aeternity/aesophia/compare/v4.2.0...HEAD
[5.0.0]: https://github.com/aeternity/aesophia/compare/v4.3.0...v5.0.0 [4.2.0]: https://github.com/aeternity/aesophia/compare/v4.2.0...v4.1.0
[4.3.0]: https://github.com/aeternity/aesophia/compare/v4.2.0...v4.3.0 [4.1.0]: https://github.com/aeternity/aesophia/compare/v4.1.0...v4.0.0
[4.2.0]: https://github.com/aeternity/aesophia/compare/v4.1.0...v4.2.0 [4.0.0]: https://github.com/aeternity/aesophia/compare/v4.0.0...v3.2.0
[4.1.0]: https://github.com/aeternity/aesophia/compare/v4.0.0...v4.1.0
[4.0.0]: https://github.com/aeternity/aesophia/compare/v3.2.0...v4.0.0
[3.2.0]: https://github.com/aeternity/aesophia/compare/v3.1.0...v3.2.0 [3.2.0]: https://github.com/aeternity/aesophia/compare/v3.1.0...v3.2.0
[3.1.0]: https://github.com/aeternity/aesophia/compare/v3.0.0...v3.1.0 [3.1.0]: https://github.com/aeternity/aesophia/compare/v3.0.0...v3.1.0
[3.0.0]: https://github.com/aeternity/aesophia/compare/v2.1.0...v3.0.0 [3.0.0]: https://github.com/aeternity/aesophia/compare/v2.1.0...v3.0.0
+8 -5
View File
@@ -1,11 +1,12 @@
# aesophia # aesophia
This is the __sophia__ compiler for the æternity system which compiles contracts written in __sophia__ to [FATE](https://github.com/aeternity/protocol/blob/master/contracts/fate.md) instructions. This is the __sophia__ compiler for the æternity system which compiles contracts written in __sophia__ code to the æternity VM code.
It is an OTP application written in Erlang and is by default included in
[the æternity node](https://github.com/aeternity/epoch). However, it can
also be included in other systems to compile contracts coded in sophia which
can then be loaded into the æternity system.
The compiler is currently being used three places
- [The command line compiler](https://github.com/aeternity/aesophia_cli)
- [The HTTP compiler](https://github.com/aeternity/aesophia_http)
- In [Aeternity node](https://github.com/aeternity/aeternity) tests
## Documentation ## Documentation
@@ -13,6 +14,7 @@ The compiler is currently being used three places
* [Sophia Documentation](docs/sophia.md). * [Sophia Documentation](docs/sophia.md).
* [Sophia Standard Library](docs/sophia_stdlib.md). * [Sophia Standard Library](docs/sophia_stdlib.md).
## Versioning ## Versioning
`aesophia` has a version that is only loosely connected to the version of the `aesophia` has a version that is only loosely connected to the version of the
@@ -21,6 +23,7 @@ minor/patch version. The `aesophia` compiler version MUST be bumped whenever
there is a change in how byte code is generated, but it MAY also be bumped upon there is a change in how byte code is generated, but it MAY also be bumped upon
API changes etc. API changes etc.
## Interface Modules ## Interface Modules
The basic modules for interfacing the compiler: The basic modules for interfacing the compiler:
+18 -117
View File
@@ -6,7 +6,6 @@
- [Language Features](#language-features) - [Language Features](#language-features)
- [Contracts](#contracts) - [Contracts](#contracts)
- [Calling other contracts](#calling-other-contracts) - [Calling other contracts](#calling-other-contracts)
- [Protected contract calls](#protected-contract-calls)
- [Mutable state](#mutable-state) - [Mutable state](#mutable-state)
- [Stateful functions](#stateful-functions) - [Stateful functions](#stateful-functions)
- [Payable](#payable) - [Payable](#payable)
@@ -28,7 +27,6 @@
- [Updating a value](#updating-a-value) - [Updating a value](#updating-a-value)
- [Map implementation](#map-implementation) - [Map implementation](#map-implementation)
- [Strings](#strings) - [Strings](#strings)
- [Chars](#chars)
- [Byte arrays](#byte-arrays) - [Byte arrays](#byte-arrays)
- [Cryptographic builins](#cryptographic-builins) - [Cryptographic builins](#cryptographic-builins)
- [AEVM note](#aevm-note) - [AEVM note](#aevm-note)
@@ -37,7 +35,6 @@
- [Example](#example) - [Example](#example)
- [Sanity checks](#sanity-checks) - [Sanity checks](#sanity-checks)
- [AENS interface](#aens-interface) - [AENS interface](#aens-interface)
- [Example](#example-1)
- [Events](#events) - [Events](#events)
- [Argument order](#argument-order) - [Argument order](#argument-order)
- [Compiler pragmas](#compiler-pragmas) - [Compiler pragmas](#compiler-pragmas)
@@ -56,8 +53,8 @@
- [Operators types](#operators-types) - [Operators types](#operators-types)
- [Operator precendences](#operator-precendences) - [Operator precendences](#operator-precendences)
- [Examples](#examples) - [Examples](#examples)
- [Delegation signature](#delegation-signature) - [The lifetime of a contract](#the-lifetime-of-a-contract)
- [Killing a contract](#killing-a-contract)
## The Sophia Language ## The Sophia Language
An Æternity BlockChain Language An Æternity BlockChain Language
@@ -141,36 +138,6 @@ without calling it you can write
Chain.spend(v.address, amount) 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 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.
### Mutable state ### Mutable state
Sophia does not have arbitrary mutable state, but only a limited form of Sophia does not have arbitrary mutable state, but only a limited form of
@@ -219,7 +186,6 @@ Without the `stateful` annotation the compiler does not allow the call to
- `AENS.claim` - `AENS.claim`
- `AENS.transfer` - `AENS.transfer`
- `AENS.revoke` - `AENS.revoke`
- `AENS.update`
* Call a `stateful` function in the current contract * Call a `stateful` function in the current contract
* Call another contract with a non-zero `value` argument. * Call another contract with a non-zero `value` argument.
@@ -480,13 +446,6 @@ Example syntax:
// yields [12,13,14,20,21,22,30,31,32] // yields [12,13,14,20,21,22,30,31,32]
``` ```
Lists can be constructed using the range syntax using special `..` operator:
```
[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. Please refer to the [standard library](sophia_stdlib.md#List) for the predefined functionalities.
### Maps and records ### Maps and records
@@ -574,16 +533,7 @@ Strings can be compared for equality (`==`, `!=`), used as keys in maps and
records, and used in builtin functions `String.length`, `String.concat` and records, and used in builtin functions `String.length`, `String.concat` and
the hash functions described below. the hash functions described below.
Please refer to the `String` [library documentation](sophia_stdlib.md#String). Please refer to the `Map` [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
@@ -610,10 +560,11 @@ string`, `String.sha3(s)` and `Crypto.sha3(s)` will give different results on AE
### Authorization interface ### Authorization interface
When a Generalized account is authorized, the authorization function needs When a Generalized account is authorized, the authorization function needs
access to the transaction and the transaction hash for the wrapped transaction. (A `GAMetaTx` access to the transaction hash for the wrapped transaction. (A `GAMetaTx`
wrapping a transaction.) The transaction and the transaction hash is available in the primitive wrapping a transaction.) 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 `Auth.tx_hash`, it is *only* available during authentication if invoked by a
normal contract call they return `None`. normal contract call it returns `None`.
### Oracle interface ### Oracle interface
You can attach an oracle to the current contract and you can interact with oracles You can attach an oracle to the current contract and you can interact with oracles
@@ -631,7 +582,7 @@ Example for an oracle answering questions of type `string` with answers of type
contract Oracles = contract Oracles =
stateful entrypoint registerOracle(acct : address, stateful entrypoint registerOracle(acct : address,
sign : signature, // Signed network id + oracle address + contract address sign : signature, // Signed oracle address + contract address
qfee : int, qfee : int,
ttl : Chain.ttl) : oracle(string, int) = ttl : Chain.ttl) : oracle(string, int) =
Oracle.register(acct, signature = sign, qfee, ttl) Oracle.register(acct, signature = sign, qfee, ttl)
@@ -652,13 +603,13 @@ contract Oracles =
Oracle.extend(o, ttl) Oracle.extend(o, ttl)
stateful entrypoint signExtendOracle(o : oracle(string, int), stateful entrypoint signExtendOracle(o : oracle(string, int),
sign : signature, // Signed network id + oracle address + contract address sign : signature, // Signed oracle address + contract address
ttl : Chain.ttl) : unit = ttl : Chain.ttl) : unit =
Oracle.extend(o, signature = sign, ttl) Oracle.extend(o, signature = sign, ttl)
stateful entrypoint respond(o : oracle(string, int), stateful entrypoint respond(o : oracle(string, int),
q : oracle_query(string, int), q : oracle_query(string, int),
sign : signature, // Signed network id + oracle query id + contract address sign : signature, // Signed oracle query id + contract address
r : int) = r : int) =
Oracle.respond(o, q, signature = sign, r) Oracle.respond(o, q, signature = sign, r)
@@ -689,46 +640,6 @@ Contracts can interact with the
[Aeternity Naming System](https://github.com/aeternity/protocol/blob/master/AENS.md). [Aeternity Naming System](https://github.com/aeternity/protocol/blob/master/AENS.md).
For this purpose the [AENS](sophia_stdlib.md#AENS) library was exposed. 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:
```
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:
```
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 ### Events
@@ -914,17 +825,15 @@ In describing the syntax below, we use the following conventions:
A Sophia file consists of a sequence of *declarations* in a layout block. A Sophia file consists of a sequence of *declarations* in a layout block.
```c ```c
File ::= Block(TopDecl) File ::= Block(Decl)
Decl ::= ['payable'] 'contract' Con '=' Block(Decl)
TopDecl ::= ['payable'] 'contract' Con '=' Block(Decl)
| 'namespace' Con '=' Block(Decl) | 'namespace' Con '=' Block(Decl)
| '@compiler' PragmaOp Version | '@compiler' PragmaOp Version
| 'include' String | 'include' String
| 'type' Id ['(' TVar* ')'] ['=' TypeAlias]
Decl ::= 'type' Id ['(' TVar* ')'] '=' TypeAlias
| 'record' Id ['(' TVar* ')'] '=' RecordType | 'record' Id ['(' TVar* ')'] '=' RecordType
| 'datatype' Id ['(' TVar* ')'] '=' DataType | 'datatype' Id ['(' TVar* ')'] '=' DataType
| (EModifier* 'entrypoint' | FModifier* 'function') Block(FunDecl) | EModifier* ('entrypoint' | 'function') Block(FunDecl)
FunDecl ::= Id ':' Type // Type signature FunDecl ::= Id ':' Type // Type signature
| Id Args [':' Type] '=' Block(Stmt) // Definition | Id Args [':' Type] '=' Block(Stmt) // Definition
@@ -1036,7 +945,6 @@ Expr ::= '(' LamArgs ')' '=>' Block(Stmt) // Anonymous function (x) => x +
| '[' Sep(Expr, ',') ']' // List [1, 2, 3] | '[' Sep(Expr, ',') ']' // List [1, 2, 3]
| '[' Expr '|' Sep(Generator, ',') ']' | '[' Expr '|' Sep(Generator, ',') ']'
// List comprehension [k | x <- [1], if (f(x)), let k = x+1] // 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} | '{' Sep(FieldUpdate, ',') '}' // Record or map value {x = 0, y = 1}, {[key] = val}
| '(' Expr ')' // Parens (1 + 2) * 3 | '(' Expr ')' // Parens (1 + 2) * 3
| Id | Con | QId | QCon // Identifiers x, None, Map.member, AELib.Token | Id | Con | QId | QCon // Identifiers x, None, Map.member, AELib.Token
@@ -1104,8 +1012,8 @@ contract FundMe =
deadline : int, deadline : int,
goal : int } goal : int }
stateful function spend(args : spend_args) = function spend(args : spend_args) =
Chain.spend(args.recipient, args.amount) raw_spend(args.recipient, args.amount)
entrypoint init(beneficiary, deadline, goal) : state = entrypoint init(beneficiary, deadline, goal) : state =
{ contributions = {}, { contributions = {},
@@ -1144,6 +1052,7 @@ contract FundMe =
require(state.total >= state.goal, "Project was not funded") require(state.total >= state.goal, "Project was not funded")
spend({recipient = state.beneficiary, spend({recipient = state.beneficiary,
amount = Contract.balance }) amount = Contract.balance })
put(state{ beneficiary = #0 })
stateful function withdraw_contributor() = stateful function withdraw_contributor() =
if(state.total >= state.goal) if(state.total >= state.goal)
@@ -1153,11 +1062,3 @@ contract FundMe =
amount = state.contributions[to]}) amount = state.contributions[to]})
put(state{ contributions @ c = Map.delete(to, c) }) put(state{ contributions @ c = Map.delete(to, c) })
``` ```
### 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 Aeternity mainnet, etc.).
+265 -526
View File
File diff suppressed because it is too large Load Diff
-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)))
+7 -14
View File
@@ -56,14 +56,11 @@ namespace Frac =
/** Integer to rational division /** Integer to rational division
*/ */
function make_frac(n : int, d : int) : frac = function make_frac(n : int, d : int) : frac =
if (d == 0) abort("Zero denominator") if (d == 0) abort("Division by zero")
elif (n == 0) Zero elif (n == 0) Zero
elif ((n < 0) == (d < 0)) simplify(Pos(abs_int(n), abs_int(d))) elif ((n < 0) == (d < 0)) simplify(Pos(abs_int(n), abs_int(d)))
else simplify(Neg(abs_int(n), abs_int(d))) else simplify(Neg(abs_int(n), abs_int(d)))
function one() : frac = Pos(1, 1)
function zero() : frac = Zero
function eq(a : frac, b : frac) : bool = function eq(a : frac, b : frac) : bool =
let (na, da) = to_pair(a) let (na, da) = to_pair(a)
let (nb, db) = to_pair(b) let (nb, db) = to_pair(b)
@@ -149,10 +146,7 @@ namespace Frac =
function mul(a : frac, b : frac) : frac = make_frac(num(a) * num(b), den(a) * den(b)) function mul(a : frac, b : frac) : frac = make_frac(num(a) * num(b), den(a) * den(b))
function div(a : frac, b : frac) : frac = switch(b) function div(a : frac, b : frac) : frac = mul(a, inv(b))
Neg(n, d) => mul(a, Neg(d, n))
Zero => abort("Division by zero")
Pos(n, d) => mul(a, Pos(d, n))
/** `b` to the power of `e` /** `b` to the power of `e`
*/ */
@@ -174,10 +168,9 @@ namespace Frac =
function optimize(f : frac, loss : frac) : frac = function optimize(f : frac, loss : frac) : frac =
require(geq(loss, Zero), "negative loss optimize") require(geq(loss, Zero), "negative loss optimize")
let s = sign(f) let s = sign(f)
mul(from_int(s), run_optimize(abs(f), abs(f), loss)) mul(from_int(s), run_optimize(abs(f), loss))
private function run_optimize(orig : frac, f : frac, loss : frac) : frac = private function run_optimize(f : frac, loss : frac) : frac =
let (n, d) = to_pair(f) let t = make_frac((num(f) + 1) / 2, (den(f) + 1)/2)
let t = make_frac((n+1)/2, (d+1)/2) if(gt(abs(sub(t, f)), loss)) f
if(gt(abs(sub(t, orig)), loss)) f
elif (eq(t, f)) f elif (eq(t, f)) f
else run_optimize(orig, t, loss) else run_optimize(t, loss)
+84 -149
View File
@@ -19,20 +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)
[] => false
h::t => h == e || contains(e, t)
/** Finds first element of `l` fulfilling predicate `p` as `Some` or `None` /** Finds first element of `l` fulfilling predicate `p` as `Some` or `None`
* if no such element exists. * if no such element exists.
*/ */
@@ -42,15 +28,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,44 +64,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, [])
from_to_step_(a, b - (b-a) mod s, s, []) private function from_to_step_(a, b, s, acc) =
private function from_to_step_(a : int, b : int, s : int, acc : list(int)) : list(int) = if (a > b) reverse(acc) else from_to_step_(a + s, b, s, a :: acc)
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)
@@ -134,52 +119,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
*/ */
@@ -190,15 +172,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
@@ -214,99 +199,49 @@ namespace List =
/** 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 // TODO: Improve?
*/ function sort(lesser_cmp : ('a, 'a) => bool, l : list('a)) : list('a) = switch(l)
function [] => []
merge : (('a, 'a) => bool, list('a), list('a)) => list('a) h::t => switch (partition((x) => lesser_cmp(x, h), t))
merge(lt, x::xs, y::ys) = (lesser, bigger) => sort(lesser_cmp, lesser) ++ h::sort(lesser_cmp, bigger)
if(lt(x, y)) x::merge(lt, xs, y::ys)
else y::merge(lt, x::xs, ys)
merge(_, [], ys) = ys
merge(_, xs, []) = xs
/** Mergesort inspired by
* https://hackage.haskell.org/package/base-4.14.1.0/docs/src/Data.OldList.html#sort
*/
function
sort : (('a, 'a) => bool, list('a)) => list('a)
sort(_, []) = []
sort(lt, l) =
merge_all(lt, monotonic_subs(lt, l))
/** Splits list into compound increasing sublists
*/
private function
monotonic_subs : (('a, 'a) => bool, list('a)) => list(list('a))
monotonic_subs(lt, x::y::rest) =
if(lt(y, x)) desc(lt, y, [x], rest)
else asc(lt, y, [x], rest)
monotonic_subs(_, l) = [l]
/** Extracts the longest descending prefix and proceeds with monotonic split
*/
private function
desc : (('a, 'a) => bool, 'a, list('a), list('a)) => list(list('a))
desc(lt, x, acc, h::t) =
if(lt(x, h)) (x::acc) :: monotonic_subs(lt, h::t)
else desc(lt, h, x::acc, t)
desc(_, x, acc, []) = [x::acc]
/** Extracts the longest ascending prefix and proceeds with monotonic split
*/
private function
asc : (('a, 'a) => bool, 'a, list('a), list('a)) => list(list('a))
asc(lt, x, acc, h::t) =
if(lt(h, x)) List.reverse(x::acc) :: monotonic_subs(lt, h::t)
else asc(lt, h, x::acc, t)
asc(_, x, acc, []) = [List.reverse(x::acc)]
/** Merges list of sorted lists
*/
private function
merge_all : (('a, 'a) => bool, list(list('a))) => list('a)
merge_all(_, [part]) = part
merge_all(lt, parts) = merge_all(lt, merge_pairs(lt, parts))
/** Single round of `merge_all` pairs of lists in a list of list
*/
private function
merge_pairs : (('a, 'a) => bool, list(list('a))) => list(list('a))
merge_pairs(lt, x::y::rest) = merge(lt, x, y) :: merge_pairs(lt, rest)
merge_pairs(_, l) = l
/** 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)
+12 -16
View File
@@ -22,11 +22,7 @@ namespace Option =
/** Assume it is `Some` /** Assume it is `Some`
*/ */
function force(o : option('a)) : 'a = switch(o) function force(o : option('a)) : 'a = default(abort("Forced None value"), o)
None => abort("Forced None value")
Some(x) => x
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)
@@ -69,21 +65,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
*/ */
@@ -96,3 +91,4 @@ namespace Option =
[] => None [] => None
None::t => choose_first(t) None::t => choose_first(t)
Some(x)::_ => Some(x) Some(x)::_ => Some(x)
-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,"7f0d309"}}} {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, "5.0.0"}, {relx, [{release, {aesophia, "4.2.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,"7f0d3090d4dc6c4d5fca7645b0c21eb0e65ad208"}}, {ref,"4f4d6d30cd2c46b3830454d650a424d513f69134"}},
0}, 0},
{<<"aeserialization">>, {<<"aeserialization">>,
{git,"https://github.com/aeternity/aeserialization.git", {git,"https://github.com/aeternity/aeserialization.git",
+5 -13
View File
@@ -14,8 +14,6 @@
, contract_interface/2 , contract_interface/2
, contract_interface/3 , contract_interface/3
, from_typed_ast/2
, render_aci_json/1 , render_aci_json/1
, json_encode_expr/1 , json_encode_expr/1
@@ -25,8 +23,6 @@
-type json() :: jsx:json_term(). -type json() :: jsx:json_term().
-type json_text() :: binary(). -type json_text() :: binary().
-export_type([aci_type/0]).
%% External API %% External API
-spec file(aci_type(), string()) -> {ok, json() | string()} | {error, term()}. -spec file(aci_type(), string()) -> {ok, json() | string()} | {error, term()}.
file(Type, File) -> file(Type, File) ->
@@ -69,18 +65,16 @@ 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]), %% io:format("~p\n", [Ast]),
{TypedAst, _} = aeso_ast_infer_types:infer(Ast, [dont_unfold | Options]), TypedAst = aeso_ast_infer_types:infer(Ast, [dont_unfold]),
%% io:format("~p\n", [TypedAst]), %% io:format("~p\n", [TypedAst]),
from_typed_ast(Type, TypedAst)
catch
throw:{error, Errors} -> {error, Errors}
end.
from_typed_ast(Type, TypedAst) ->
JArray = [ encode_contract(C) || C <- TypedAst ], JArray = [ encode_contract(C) || C <- TypedAst ],
case Type of case Type of
json -> {ok, JArray}; json -> {ok, JArray};
string -> do_render_aci_json(JArray) string -> do_render_aci_json(JArray)
end
catch
throw:{error, Errors} -> {error, Errors}
end. end.
encode_contract(Contract = {contract, _, {con, _, Name}, _}) -> encode_contract(Contract = {contract, _, {con, _, Name}, _}) ->
@@ -200,8 +194,6 @@ encode_expr({bytes, _, B}) ->
encode_expr({Lit, _, L}) when Lit == oracle_pubkey; Lit == oracle_query_id; encode_expr({Lit, _, L}) when Lit == oracle_pubkey; Lit == oracle_query_id;
Lit == contract_pubkey; Lit == account_pubkey -> Lit == contract_pubkey; Lit == account_pubkey ->
aeser_api_encoder:encode(Lit, L); aeser_api_encoder:encode(Lit, L);
encode_expr({app, _, {'-', _}, [{int, _, N}]}) ->
encode_expr({int, [], -N});
encode_expr({app, _, F, As}) -> encode_expr({app, _, F, As}) ->
Ef = encode_expr(F), Ef = encode_expr(F),
Eas = encode_exprs(As), Eas = encode_exprs(As),
+69 -347
View File
@@ -12,11 +12,7 @@
-module(aeso_ast_infer_types). -module(aeso_ast_infer_types).
-export([ infer/1 -export([infer/1, infer/2, unfold_types_in_type/3]).
, infer/2
, unfold_types_in_type/3
, pp_type/2
]).
-type utype() :: {fun_t, aeso_syntax:ann(), named_args_t(), [utype()], utype()} -type utype() :: {fun_t, aeso_syntax:ann(), named_args_t(), [utype()], utype()}
| {app_t, aeso_syntax:ann(), utype(), [utype()]} | {app_t, aeso_syntax:ann(), utype(), [utype()]}
@@ -24,7 +20,6 @@
| aeso_syntax:id() | aeso_syntax:qid() | aeso_syntax:id() | aeso_syntax:qid()
| aeso_syntax:con() | aeso_syntax:qcon() %% contracts | aeso_syntax:con() | aeso_syntax:qcon() %% contracts
| aeso_syntax:tvar() | aeso_syntax:tvar()
| {if_t, aeso_syntax:ann(), aeso_syntax:id(), utype(), utype()} %% Can branch on named argument (protected)
| uvar(). | uvar().
-type uvar() :: {uvar, aeso_syntax:ann(), reference()}. -type uvar() :: {uvar, aeso_syntax:ann(), reference()}.
@@ -48,14 +43,7 @@
name :: aeso_syntax:id(), name :: aeso_syntax:id(),
type :: utype()}). type :: utype()}).
-record(dependent_type_constraint, -type named_argument_constraint() :: #named_argument_constraint{}.
{ named_args_t :: named_args_t()
, named_args :: [aeso_syntax:arg_expr()]
, general_type :: utype()
, specialized_type :: utype()
, context :: term() }).
-type named_argument_constraint() :: #named_argument_constraint{} | #dependent_type_constraint{}.
-record(field_constraint, -record(field_constraint,
{ record_t :: utype() { record_t :: utype()
@@ -244,21 +232,16 @@ bind_fields([], Env) -> Env;
bind_fields([{Id, Info} | Rest], Env) -> bind_fields([{Id, Info} | Rest], Env) ->
bind_fields(Rest, bind_field(Id, Info, Env)). bind_fields(Rest, bind_field(Id, Info, Env)).
%% Contract entrypoints take three named arguments %% Contract entrypoints take two named arguments (gas : int = Call.gas_left(), value : int = 0).
%% gas : int = Call.gas_left()
%% value : int = 0
%% protected : bool = false
contract_call_type({fun_t, Ann, [], Args, Ret}) -> contract_call_type({fun_t, Ann, [], Args, Ret}) ->
Id = fun(X) -> {id, Ann, X} end, Id = fun(X) -> {id, Ann, X} end,
Int = Id("int"), Int = Id("int"),
Typed = fun(E, T) -> {typed, Ann, E, T} end, Typed = fun(E, T) -> {typed, Ann, E, T} end,
Named = fun(Name, Default = {typed, _, _, T}) -> {named_arg_t, Ann, Id(Name), T, Default} end, Named = fun(Name, Default) -> {named_arg_t, Ann, Id(Name), Int, Default} end,
{fun_t, Ann, [Named("gas", Typed({app, Ann, Typed({qid, Ann, ["Call", "gas_left"]}, {fun_t, Ann, [Named("gas", Typed({app, Ann, Typed({qid, Ann, ["Call", "gas_left"]},
{fun_t, Ann, [], [], Int}), {fun_t, Ann, [], [], Int}),
[]}, Int)), []}, Int)),
Named("value", Typed({int, Ann, 0}, Int)), Named("value", Typed({int, Ann, 0}, Int))], Args, Ret}.
Named("protected", Typed({bool, Ann, false}, Id("bool")))],
Args, {if_t, Ann, Id("protected"), {app_t, Ann, {id, Ann, "option"}, [Ret]}, Ret}}.
-spec bind_contract(aeso_syntax:decl(), env()) -> env(). -spec bind_contract(aeso_syntax:decl(), env()) -> env().
bind_contract({contract, Ann, Id, Contents}, Env) -> bind_contract({contract, Ann, Id, Contents}, Env) ->
@@ -381,7 +364,6 @@ is_private(Ann) -> proplists:get_value(private, Ann, false).
global_env() -> global_env() ->
Ann = [{origin, system}], Ann = [{origin, system}],
Int = {id, Ann, "int"}, Int = {id, Ann, "int"},
Char = {id, Ann, "char"},
Bool = {id, Ann, "bool"}, Bool = {id, Ann, "bool"},
String = {id, Ann, "string"}, String = {id, Ann, "string"},
Address = {id, Ann, "address"}, Address = {id, Ann, "address"},
@@ -407,24 +389,6 @@ global_env() ->
Signature = {named_arg_t, Ann, SignId, SignId, {typed, Ann, SignDef, SignId}}, Signature = {named_arg_t, Ann, SignId, SignId, {typed, Ann, SignDef, SignId}},
SignFun = fun(Ts, T) -> {type_sig, [stateful|Ann], none, [Signature], Ts, T} end, SignFun = fun(Ts, T) -> {type_sig, [stateful|Ann], none, [Signature], Ts, T} end,
TTL = {qid, Ann, ["Chain", "ttl"]}, TTL = {qid, Ann, ["Chain", "ttl"]},
Pointee = {qid, Ann, ["AENS", "pointee"]},
AENSName = {qid, Ann, ["AENS", "name"]},
Fr = {qid, Ann, ["MCL_BLS12_381", "fr"]},
Fp = {qid, Ann, ["MCL_BLS12_381", "fp"]},
Fp2 = {tuple_t, Ann, [Fp, Fp]},
G1 = {tuple_t, Ann, [Fp, Fp, Fp]},
G2 = {tuple_t, Ann, [Fp2, Fp2, Fp2]},
GT = {tuple_t, Ann, lists:duplicate(12, Fp)},
Tx = {qid, Ann, ["Chain", "tx"]},
GAMetaTx = {qid, Ann, ["Chain", "ga_meta_tx"]},
BaseTx = {qid, Ann, ["Chain", "base_tx"]},
PayForTx = {qid, Ann, ["Chain", "paying_for_tx"]},
FldT = fun(Id, T) -> {field_t, Ann, {id, Ann, Id}, T} end,
TxFlds = [{"paying_for", Option(PayForTx)}, {"ga_metas", List(GAMetaTx)},
{"actor", Address}, {"fee", Int}, {"ttl", Int}, {"tx", BaseTx}],
TxType = {record_t, [FldT(N, T) || {N, T} <- TxFlds ]},
Fee = Int, Fee = Int,
[A, Q, R, K, V] = lists:map(TVar, ["a", "q", "r", "k", "v"]), [A, Q, R, K, V] = lists:map(TVar, ["a", "q", "r", "k", "v"]),
@@ -462,36 +426,8 @@ global_env() ->
{"timestamp", Int}, {"timestamp", Int},
{"block_height", Int}, {"block_height", Int},
{"difficulty", Int}, {"difficulty", Int},
{"gas_limit", Int}, {"gas_limit", Int}])
%% Tx constructors , types = MkDefs([{"ttl", 0}]) },
{"GAMetaTx", Fun([Address, Int], GAMetaTx)},
{"PayingForTx", Fun([Address, Int], PayForTx)},
{"SpendTx", Fun([Address, Int, String], BaseTx)},
{"OracleRegisterTx", BaseTx},
{"OracleQueryTx", BaseTx},
{"OracleResponseTx", BaseTx},
{"OracleExtendTx", BaseTx},
{"NamePreclaimTx", BaseTx},
{"NameClaimTx", Fun([String], BaseTx)},
{"NameUpdateTx", Fun([Hash], BaseTx)},
{"NameRevokeTx", Fun([Hash], BaseTx)},
{"NameTransferTx", Fun([Address, Hash], BaseTx)},
{"ChannelCreateTx", Fun([Address], BaseTx)},
{"ChannelDepositTx", Fun([Address, Int], BaseTx)},
{"ChannelWithdrawTx", Fun([Address, Int], BaseTx)},
{"ChannelForceProgressTx", Fun([Address], BaseTx)},
{"ChannelCloseMutualTx", Fun([Address], BaseTx)},
{"ChannelCloseSoloTx", Fun([Address], BaseTx)},
{"ChannelSlashTx", Fun([Address], BaseTx)},
{"ChannelSettleTx", Fun([Address], BaseTx)},
{"ChannelSnapshotSoloTx", Fun([Address], BaseTx)},
{"ContractCreateTx", Fun([Int], BaseTx)},
{"ContractCallTx", Fun([Address, Int], BaseTx)},
{"GAAttachTx", BaseTx}
])
, types = MkDefs([{"ttl", 0}, {"tx", {[], TxType}},
{"base_tx", 0},
{"paying_for_tx", 0}, {"ga_meta_tx", 0}]) },
ContractScope = #scope ContractScope = #scope
{ funs = MkDefs( { funs = MkDefs(
@@ -511,7 +447,6 @@ global_env() ->
OracleScope = #scope OracleScope = #scope
{ funs = MkDefs( { funs = MkDefs(
[{"register", SignFun([Address, Fee, TTL], Oracle(Q, R))}, [{"register", SignFun([Address, Fee, TTL], Oracle(Q, R))},
{"expiry", Fun([Oracle(Q, R)], Fee)},
{"query_fee", Fun([Oracle(Q, R)], Fee)}, {"query_fee", Fun([Oracle(Q, R)], Fee)},
{"query", StateFun([Oracle(Q, R), Q, Fee, TTL, TTL], Query(Q, R))}, {"query", StateFun([Oracle(Q, R), Q, Fee, TTL, TTL], Query(Q, R))},
{"get_question", Fun([Oracle(Q, R), Query(Q, R)], Q)}, {"get_question", Fun([Oracle(Q, R), Query(Q, R)], Q)},
@@ -527,18 +462,7 @@ global_env() ->
{"preclaim", SignFun([Address, Hash], Unit)}, {"preclaim", SignFun([Address, Hash], Unit)},
{"claim", SignFun([Address, String, Int, Int], Unit)}, {"claim", SignFun([Address, String, Int, Int], Unit)},
{"transfer", SignFun([Address, Address, String], Unit)}, {"transfer", SignFun([Address, Address, String], Unit)},
{"revoke", SignFun([Address, String], Unit)}, {"revoke", SignFun([Address, String], Unit)}]) },
{"update", SignFun([Address, String, Option(TTL), Option(Int), Option(Map(String, Pointee))], Unit)},
{"lookup", Fun([String], option_t(Ann, AENSName))},
%% AENS pointee constructors
{"AccountPt", Fun1(Address, Pointee)},
{"OraclePt", Fun1(Address, Pointee)},
{"ContractPt", Fun1(Address, Pointee)},
{"ChannelPt", Fun1(Address, Pointee)},
%% Name object constructor
{"Name", Fun([Address, TTL, Map(String, Pointee)], AENSName)}
])
, types = MkDefs([{"pointee", 0}, {"name", 0}]) },
MapScope = #scope MapScope = #scope
{ funs = MkDefs( { funs = MkDefs(
@@ -561,65 +485,19 @@ global_env() ->
{"sha256", Fun1(A, Hash)}, {"sha256", Fun1(A, Hash)},
{"blake2b", Fun1(A, Hash)}]) }, {"blake2b", Fun1(A, Hash)}]) },
%% Fancy BLS12-381 crypto operations
MCL_BLS12_381_Scope = #scope
{ funs = MkDefs(
[{"g1_neg", Fun1(G1, G1)},
{"g1_norm", Fun1(G1, G1)},
{"g1_valid", Fun1(G1, Bool)},
{"g1_is_zero", Fun1(G1, Bool)},
{"g1_add", Fun ([G1, G1], G1)},
{"g1_mul", Fun ([Fr, G1], G1)},
{"g2_neg", Fun1(G2, G2)},
{"g2_norm", Fun1(G2, G2)},
{"g2_valid", Fun1(G2, Bool)},
{"g2_is_zero", Fun1(G2, Bool)},
{"g2_add", Fun ([G2, G2], G2)},
{"g2_mul", Fun ([Fr, G2], G2)},
{"gt_inv", Fun1(GT, GT)},
{"gt_add", Fun ([GT, GT], GT)},
{"gt_mul", Fun ([GT, GT], GT)},
{"gt_pow", Fun ([GT, Fr], GT)},
{"gt_is_one", Fun1(GT, Bool)},
{"pairing", Fun ([G1, G2], GT)},
{"miller_loop", Fun ([G1, G2], GT)},
{"final_exp", Fun1(GT, GT)},
{"int_to_fr", Fun1(Int, Fr)},
{"int_to_fp", Fun1(Int, Fp)},
{"fr_to_int", Fun1(Fr, Int)},
{"fp_to_int", Fun1(Fp, Int)}
]),
types = MkDefs(
[{"fr", 0}, {"fp", 0}]) },
%% Authentication %% Authentication
AuthScope = #scope AuthScope = #scope
{ funs = MkDefs( { funs = MkDefs(
[{"tx_hash", Option(Hash)}, [{"tx_hash", Option(Hash)}]) },
{"tx", Option(Tx)} ]) },
%% Strings %% Strings
StringScope = #scope StringScope = #scope
{ funs = MkDefs( { funs = MkDefs(
[{"length", Fun1(String, Int)}, [{"length", Fun1(String, Int)},
{"concat", Fun([String, String], String)}, {"concat", Fun([String, String], String)},
{"to_list", Fun1(String, List(Char))},
{"from_list", Fun1(List(Char), String)},
{"to_upper", Fun1(String, String)},
{"to_lower", Fun1(String, String)},
{"sha3", Fun1(String, Hash)}, {"sha3", Fun1(String, Hash)},
{"sha256", Fun1(String, Hash)}, {"sha256", Fun1(String, Hash)},
{"blake2b", Fun1(String, Hash)} {"blake2b", Fun1(String, Hash)}]) },
]) },
%% Chars
CharScope = #scope
{ funs = MkDefs(
[{"to_int", Fun1(Char, Int)},
{"from_int", Fun1(Int, Option(Char))}]) },
%% Bits %% Bits
BitsScope = #scope BitsScope = #scope
@@ -651,7 +529,6 @@ global_env() ->
{"is_contract", Fun1(Address, Bool)}, {"is_contract", Fun1(Address, Bool)},
{"is_payable", Fun1(Address, Bool)}]) }, {"is_payable", Fun1(Address, Bool)}]) },
#env{ scopes = #env{ scopes =
#{ [] => TopScope #{ [] => TopScope
, ["Chain"] => ChainScope , ["Chain"] => ChainScope
@@ -662,38 +539,27 @@ global_env() ->
, ["Map"] => MapScope , ["Map"] => MapScope
, ["Auth"] => AuthScope , ["Auth"] => AuthScope
, ["Crypto"] => CryptoScope , ["Crypto"] => CryptoScope
, ["MCL_BLS12_381"] => MCL_BLS12_381_Scope , ["String"] => StringScope
, ["StringInternal"] => StringScope
, ["Char"] => CharScope
, ["Bits"] => BitsScope , ["Bits"] => BitsScope
, ["Bytes"] => BytesScope , ["Bytes"] => BytesScope
, ["Int"] => IntScope , ["Int"] => IntScope
, ["Address"] => AddressScope , ["Address"] => AddressScope
} } }.
, fields =
maps:from_list([{N, [#field_info{ ann = [], field_t = T, record_t = Tx, kind = record }]}
|| {N, T} <- TxFlds ])
}.
option_t(As, T) -> {app_t, As, {id, As, "option"}, [T]}. option_t(As, T) -> {app_t, As, {id, As, "option"}, [T]}.
map_t(As, K, V) -> {app_t, As, {id, As, "map"}, [K, V]}. map_t(As, K, V) -> {app_t, As, {id, As, "map"}, [K, V]}.
-spec infer(aeso_syntax:ast()) -> {aeso_syntax:ast(), aeso_syntax:ast()} | {env(), aeso_syntax:ast(), aeso_syntax:ast()}. -spec infer(aeso_syntax:ast()) -> aeso_syntax:ast() | {env(), aeso_syntax:ast()}.
infer(Contracts) -> infer(Contracts) ->
infer(Contracts, []). infer(Contracts, []).
-type option() :: return_env | dont_unfold | no_code | debug_mode | term(). -type option() :: return_env | dont_unfold | no_code | term().
-spec init_env(list(option())) -> env(). -spec init_env(list(option())) -> env().
init_env(_Options) -> global_env(). init_env(_Options) -> global_env().
-spec infer(aeso_syntax:ast(), list(option())) -> -spec infer(aeso_syntax:ast(), list(option())) ->
{aeso_syntax:ast(), aeso_syntax:ast()} | {env(), aeso_syntax:ast(), aeso_syntax:ast()}. aeso_syntax:ast() | {env(), aeso_syntax:ast()}.
infer([], Options) ->
create_type_errors(),
type_error({no_decls, proplists:get_value(src_file, Options, no_file)}),
destroy_and_report_type_errors(init_env(Options));
infer(Contracts, Options) -> infer(Contracts, Options) ->
ets_init(), %% Init the ETS table state ets_init(), %% Init the ETS table state
try try
@@ -702,15 +568,15 @@ infer(Contracts, Options) ->
ets_new(type_vars, [set]), ets_new(type_vars, [set]),
check_modifiers(Env, Contracts), check_modifiers(Env, Contracts),
{Env1, Decls} = infer1(Env, Contracts, [], Options), {Env1, Decls} = infer1(Env, Contracts, [], Options),
{Env2, DeclsFolded, DeclsUnfolded} = {Env2, Decls2} =
case proplists:get_value(dont_unfold, Options, false) of case proplists:get_value(dont_unfold, Options, false) of
true -> {Env1, Decls, Decls}; true -> {Env1, Decls};
false -> E = on_scopes(Env1, fun(Scope) -> unfold_record_types(Env1, Scope) end), false -> E = on_scopes(Env1, fun(Scope) -> unfold_record_types(Env1, Scope) end),
{E, Decls, unfold_record_types(E, Decls)} {E, unfold_record_types(E, Decls)}
end, end,
case proplists:get_value(return_env, Options, false) of case proplists:get_value(return_env, Options, false) of
false -> {DeclsFolded, DeclsUnfolded}; false -> Decls2;
true -> {Env2, DeclsFolded, DeclsUnfolded} true -> {Env2, Decls2}
end end
after after
clean_up_ets() clean_up_ets()
@@ -748,23 +614,15 @@ check_scope_name_clash(Env, Kind, Name) ->
-spec infer_contract_top(env(), main_contract | contract | namespace, [aeso_syntax:decl()], list(option())) -> -spec infer_contract_top(env(), main_contract | contract | namespace, [aeso_syntax:decl()], list(option())) ->
{env(), [aeso_syntax:decl()]}. {env(), [aeso_syntax:decl()]}.
infer_contract_top(Env, Kind, Defs0, Options) -> infer_contract_top(Env, Kind, Defs0, _Options) ->
create_type_errors(),
Defs = desugar(Defs0), Defs = desugar(Defs0),
destroy_and_report_type_errors(Env), infer_contract(Env, Kind, Defs).
infer_contract(Env, Kind, Defs, Options).
%% infer_contract takes a proplist mapping global names to types, and %% infer_contract takes a proplist mapping global names to types, and
%% a list of definitions. %% a list of definitions.
-spec infer_contract(env(), main_contract | contract | namespace, [aeso_syntax:decl()], list(option())) -> {env(), [aeso_syntax:decl()]}. -spec infer_contract(env(), main_contract | contract | namespace, [aeso_syntax:decl()]) -> {env(), [aeso_syntax:decl()]}.
infer_contract(Env0, What, Defs0, Options) -> infer_contract(Env0, What, Defs0) ->
create_type_errors(), Defs = process_blocks(Defs0),
Defs01 = process_blocks(Defs0),
Defs = case lists:member(debug_mode, Options) of
true -> expose_internals(Defs01, What);
false -> Defs01
end,
destroy_and_report_type_errors(Env0),
Env = Env0#env{ what = What }, Env = Env0#env{ what = What },
Kind = fun({type_def, _, _, _, _}) -> type; Kind = fun({type_def, _, _, _, _}) -> type;
({letfun, _, _, _, _, _}) -> function; ({letfun, _, _, _, _, _}) -> function;
@@ -811,39 +669,20 @@ process_blocks(Decls) ->
-spec process_block(aeso_syntax:ann(), [aeso_syntax:decl()]) -> [aeso_syntax:decl()]. -spec process_block(aeso_syntax:ann(), [aeso_syntax:decl()]) -> [aeso_syntax:decl()].
process_block(_, []) -> []; process_block(_, []) -> [];
process_block(_, [Decl]) -> [Decl]; process_block(_, [Decl]) -> [Decl];
process_block(_Ann, [Decl | Decls]) -> process_block(Ann, [Decl | Decls]) ->
IsThis = fun(Name) -> fun({letfun, _, {id, _, Name1}, _, _, _}) -> Name == Name1; IsThis = fun(Name) -> fun({letfun, _, {id, _, Name1}, _, _, _}) -> Name == Name1;
(_) -> false end end, (_) -> false end end,
case Decl of case Decl of
{fun_decl, Ann1, Id = {id, _, Name}, Type} -> {fun_decl, Ann1, Id = {id, _, Name}, Type} ->
{Clauses, Rest} = lists:splitwith(IsThis(Name), Decls), {Clauses, Rest} = lists:splitwith(IsThis(Name), Decls),
[type_error({mismatched_decl_in_funblock, Name, D1}) || D1 <- Rest], [{fun_clauses, Ann1, Id, Type, Clauses} |
[{fun_clauses, Ann1, Id, Type, Clauses}]; process_block(Ann, Rest)];
{letfun, Ann1, Id = {id, _, Name}, _, _, _} -> {letfun, Ann1, Id = {id, _, Name}, _, _, _} ->
{Clauses, Rest} = lists:splitwith(IsThis(Name), [Decl | Decls]), {Clauses, Rest} = lists:splitwith(IsThis(Name), [Decl | Decls]),
[type_error({mismatched_decl_in_funblock, Name, D1}) || D1 <- Rest], [{fun_clauses, Ann1, Id, {id, [{origin, system} | Ann1], "_"}, Clauses} |
[{fun_clauses, Ann1, Id, {id, [{origin, system} | Ann1], "_"}, Clauses}] process_block(Ann, Rest)]
end. end.
%% Turns private stuff into public stuff
expose_internals(Defs, What) ->
[ begin
Ann = element(2, Def),
NewAnn = case What of
namespace -> [A ||A <- Ann, A /= {private, true}, A /= private];
main_contract -> [{entrypoint, true}|Ann]; % minor duplication
contract -> Ann
end,
Def1 = setelement(2, Def, NewAnn),
case Def1 of % fix inner clauses
{fun_clauses, Ans, Id, T, Clauses} ->
{fun_clauses, Ans, Id, T, expose_internals(Clauses, What)};
_ -> Def1
end
end
|| Def <- Defs
].
-spec check_typedefs(env(), [aeso_syntax:decl()]) -> {env(), [aeso_syntax:decl()]}. -spec check_typedefs(env(), [aeso_syntax:decl()]) -> {env(), [aeso_syntax:decl()]}.
check_typedefs(Env = #env{ namespace = Ns }, Defs) -> check_typedefs(Env = #env{ namespace = Ns }, Defs) ->
create_type_errors(), create_type_errors(),
@@ -1407,7 +1246,7 @@ infer_expr(Env, {typed, As, Body, Type}) ->
Type1 = check_type(Env, Type), Type1 = check_type(Env, Type),
{typed, _, NewBody, NewType} = check_expr(Env, Body, Type1), {typed, _, NewBody, NewType} = check_expr(Env, Body, Type1),
{typed, As, NewBody, NewType}; {typed, As, NewBody, NewType};
infer_expr(Env, {app, Ann, Fun, Args0} = App) -> infer_expr(Env, {app, Ann, Fun, Args0}) ->
NamedArgs = [ Arg || Arg = {named_arg, _, _, _} <- Args0 ], NamedArgs = [ Arg || Arg = {named_arg, _, _, _} <- Args0 ],
Args = Args0 -- NamedArgs, Args = Args0 -- NamedArgs,
case aeso_syntax:get_ann(format, Ann) of case aeso_syntax:get_ann(format, Ann) of
@@ -1422,16 +1261,8 @@ infer_expr(Env, {app, Ann, Fun, Args0} = App) ->
NewFun={typed, _, _, FunType} = infer_expr(Env, Fun), NewFun={typed, _, _, FunType} = infer_expr(Env, Fun),
NewArgs = [infer_expr(Env, A) || A <- Args], NewArgs = [infer_expr(Env, A) || A <- Args],
ArgTypes = [T || {typed, _, _, T} <- NewArgs], ArgTypes = [T || {typed, _, _, T} <- NewArgs],
GeneralResultType = fresh_uvar(Ann),
ResultType = fresh_uvar(Ann), ResultType = fresh_uvar(Ann),
When = {infer_app, Fun, NamedArgs1, Args, FunType, ArgTypes}, unify(Env, FunType, {fun_t, [], NamedArgsVar, ArgTypes, ResultType}, {infer_app, Fun, Args, FunType, ArgTypes}),
unify(Env, FunType, {fun_t, [], NamedArgsVar, ArgTypes, GeneralResultType}, When),
add_named_argument_constraint(
#dependent_type_constraint{ named_args_t = NamedArgsVar,
named_args = NamedArgs1,
general_type = GeneralResultType,
specialized_type = ResultType,
context = {check_return, App} }),
{typed, Ann, {app, Ann, NewFun, NamedArgs1 ++ NewArgs}, dereference(ResultType)} {typed, Ann, {app, Ann, NewFun, NamedArgs1 ++ NewArgs}, dereference(ResultType)}
end; end;
infer_expr(Env, {'if', Attrs, Cond, Then, Else}) -> infer_expr(Env, {'if', Attrs, Cond, Then, Else}) ->
@@ -1590,7 +1421,7 @@ infer_op(Env, As, Op, Args, InferOp) ->
TypedArgs = [infer_expr(Env, A) || A <- Args], TypedArgs = [infer_expr(Env, A) || A <- Args],
ArgTypes = [T || {typed, _, _, T} <- TypedArgs], ArgTypes = [T || {typed, _, _, T} <- TypedArgs],
Inferred = {fun_t, _, _, OperandTypes, ResultType} = InferOp(Op), Inferred = {fun_t, _, _, OperandTypes, ResultType} = InferOp(Op),
unify(Env, ArgTypes, OperandTypes, {infer_app, Op, [], Args, Inferred, ArgTypes}), unify(Env, ArgTypes, OperandTypes, {infer_app, Op, Args, Inferred, ArgTypes}),
{typed, As, {app, As, Op, TypedArgs}, ResultType}. {typed, As, {app, As, Op, TypedArgs}, ResultType}.
infer_pattern(Env, Pattern) -> infer_pattern(Env, Pattern) ->
@@ -1691,15 +1522,6 @@ free_vars(L) when is_list(L) ->
[V || Elem <- L, [V || Elem <- L,
V <- free_vars(Elem)]. V <- free_vars(Elem)].
next_count() ->
V = case get(counter) of
undefined ->
0;
X -> X
end,
put(counter, V + 1),
V.
%% Clean up all the ets tables (in case of an exception) %% Clean up all the ets tables (in case of an exception)
ets_tables() -> ets_tables() ->
@@ -1745,18 +1567,6 @@ ets_tab2list(Name) ->
TabId = ets_tabid(Name), TabId = ets_tabid(Name),
ets:tab2list(TabId). ets:tab2list(TabId).
ets_insert_ordered(_, []) -> true;
ets_insert_ordered(Name, [H|T]) ->
ets_insert_ordered(Name, H),
ets_insert_ordered(Name, T);
ets_insert_ordered(Name, Object) ->
Count = next_count(),
TabId = ets_tabid(Name),
ets:insert(TabId, {Count, Object}).
ets_tab2list_ordered(Name) ->
[E || {_, E} <- ets_tab2list(Name)].
%% Options %% Options
create_options(Options) -> create_options(Options) ->
@@ -1782,27 +1592,27 @@ create_constraints() ->
create_field_constraints(). create_field_constraints().
destroy_and_report_unsolved_constraints(Env) -> destroy_and_report_unsolved_constraints(Env) ->
solve_field_constraints(Env),
solve_named_argument_constraints(Env), solve_named_argument_constraints(Env),
solve_bytes_constraints(Env), solve_bytes_constraints(Env),
solve_field_constraints(Env),
destroy_and_report_unsolved_field_constraints(Env),
destroy_and_report_unsolved_bytes_constraints(Env), destroy_and_report_unsolved_bytes_constraints(Env),
destroy_and_report_unsolved_named_argument_constraints(Env), destroy_and_report_unsolved_named_argument_constraints(Env).
destroy_and_report_unsolved_field_constraints(Env).
%% -- Named argument constraints -- %% -- Named argument constraints --
create_named_argument_constraints() -> create_named_argument_constraints() ->
ets_new(named_argument_constraints, [ordered_set]). ets_new(named_argument_constraints, [bag]).
destroy_named_argument_constraints() -> destroy_named_argument_constraints() ->
ets_delete(named_argument_constraints). ets_delete(named_argument_constraints).
get_named_argument_constraints() -> get_named_argument_constraints() ->
ets_tab2list_ordered(named_argument_constraints). ets_tab2list(named_argument_constraints).
-spec add_named_argument_constraint(named_argument_constraint()) -> ok. -spec add_named_argument_constraint(named_argument_constraint()) -> ok.
add_named_argument_constraint(Constraint) -> add_named_argument_constraint(Constraint) ->
ets_insert_ordered(named_argument_constraints, Constraint), ets_insert(named_argument_constraints, Constraint),
ok. ok.
solve_named_argument_constraints(Env) -> solve_named_argument_constraints(Env) ->
@@ -1827,43 +1637,8 @@ check_named_argument_constraint(Env,
type_error({bad_named_argument, Args, Id}), type_error({bad_named_argument, Args, Id}),
false; false;
[T] -> unify(Env, T, Type, {check_named_arg_constraint, C}), true [T] -> unify(Env, T, Type, {check_named_arg_constraint, C}), true
end;
check_named_argument_constraint(Env,
#dependent_type_constraint{ named_args_t = NamedArgsT0,
named_args = NamedArgs,
general_type = GenType,
specialized_type = SpecType,
context = {check_return, App} }) ->
NamedArgsT = dereference(NamedArgsT0),
case dereference(NamedArgsT0) of
[_ | _] = NamedArgsT ->
GetVal = fun(Name, Default) ->
hd([ Val || {named_arg, _, {id, _, N}, Val} <- NamedArgs, N == Name] ++
[ Default ])
end,
ArgEnv = maps:from_list([ {Name, GetVal(Name, Default)}
|| {named_arg_t, _, {id, _, Name}, _, Default} <- NamedArgsT ]),
GenType1 = specialize_dependent_type(ArgEnv, GenType),
unify(Env, GenType1, SpecType, {check_expr, App, GenType1, SpecType}),
true;
_ -> unify(Env, GenType, SpecType, {check_expr, App, GenType, SpecType}), true
end. end.
specialize_dependent_type(Env, Type) ->
case dereference(Type) of
{if_t, _, {id, _, Arg}, Then, Else} ->
Val = maps:get(Arg, Env),
case Val of
{typed, _, {bool, _, true}, _} -> Then;
{typed, _, {bool, _, false}, _} -> Else;
_ ->
type_error({named_argument_must_be_literal_bool, Arg, Val}),
fresh_uvar(aeso_syntax:get_ann(Val))
end;
_ -> Type %% Currently no deep dependent types
end.
destroy_and_report_unsolved_named_argument_constraints(Env) -> destroy_and_report_unsolved_named_argument_constraints(Env) ->
Unsolved = solve_named_argument_constraints(Env, get_named_argument_constraints()), Unsolved = solve_named_argument_constraints(Env, get_named_argument_constraints()),
[ type_error({unsolved_named_argument_constraint, C}) || C <- Unsolved ], [ type_error({unsolved_named_argument_constraint, C}) || C <- Unsolved ],
@@ -1876,14 +1651,14 @@ destroy_and_report_unsolved_named_argument_constraints(Env) ->
| {add_bytes, aeso_syntax:ann(), concat | split, utype(), utype(), utype()}. | {add_bytes, aeso_syntax:ann(), concat | split, utype(), utype(), utype()}.
create_bytes_constraints() -> create_bytes_constraints() ->
ets_new(bytes_constraints, [ordered_set]). ets_new(bytes_constraints, [bag]).
get_bytes_constraints() -> get_bytes_constraints() ->
ets_tab2list_ordered(bytes_constraints). ets_tab2list(bytes_constraints).
-spec add_bytes_constraint(byte_constraint()) -> true. -spec add_bytes_constraint(byte_constraint()) -> true.
add_bytes_constraint(Constraint) -> add_bytes_constraint(Constraint) ->
ets_insert_ordered(bytes_constraints, Constraint). ets_insert(bytes_constraints, Constraint).
solve_bytes_constraints(Env) -> solve_bytes_constraints(Env) ->
[ solve_bytes_constraint(Env, C) || C <- get_bytes_constraints() ], [ solve_bytes_constraint(Env, C) || C <- get_bytes_constraints() ],
@@ -1937,18 +1712,18 @@ check_bytes_constraint(Env, {add_bytes, Ann, Fun, A0, B0, C0}) ->
create_field_constraints() -> create_field_constraints() ->
%% A relation from uvars to constraints %% A relation from uvars to constraints
ets_new(field_constraints, [ordered_set]). ets_new(field_constraints, [bag]).
destroy_field_constraints() -> destroy_field_constraints() ->
ets_delete(field_constraints). ets_delete(field_constraints).
-spec constrain([field_constraint()]) -> true. -spec constrain([field_constraint()]) -> true.
constrain(FieldConstraints) -> constrain(FieldConstraints) ->
ets_insert_ordered(field_constraints, FieldConstraints). ets_insert(field_constraints, FieldConstraints).
-spec get_field_constraints() -> [field_constraint()]. -spec get_field_constraints() -> [field_constraint()].
get_field_constraints() -> get_field_constraints() ->
ets_tab2list_ordered(field_constraints). ets_tab2list(field_constraints).
solve_field_constraints(Env) -> solve_field_constraints(Env) ->
FieldCs = FieldCs =
@@ -2089,11 +1864,9 @@ destroy_and_report_unsolved_field_constraints(Env) ->
{FieldCs, OtherCs} = {FieldCs, OtherCs} =
lists:partition(fun(#field_constraint{}) -> true; (_) -> false end, lists:partition(fun(#field_constraint{}) -> true; (_) -> false end,
get_field_constraints()), get_field_constraints()),
{CreateCs, OtherCs1} = {CreateCs, ContractCs} =
lists:partition(fun(#record_create_constraint{}) -> true; (_) -> false end, lists:partition(fun(#record_create_constraint{}) -> true; (_) -> false end,
OtherCs), OtherCs),
{ContractCs, []} =
lists:partition(fun(#is_contract_constraint{}) -> true; (_) -> false end, OtherCs1),
Unknown = solve_known_record_types(Env, FieldCs), Unknown = solve_known_record_types(Env, FieldCs),
if Unknown == [] -> ok; if Unknown == [] -> ok;
true -> true ->
@@ -2167,8 +1940,7 @@ unfold_record_types(Env, T) ->
unfold_types(Env, T, [unfold_record_types]). unfold_types(Env, T, [unfold_record_types]).
unfold_types(Env, {typed, Attr, E, Type}, Options) -> unfold_types(Env, {typed, Attr, E, Type}, Options) ->
Options1 = [{ann, Attr} | lists:keydelete(ann, 1, Options)], {typed, Attr, unfold_types(Env, E, Options), unfold_types_in_type(Env, Type, Options)};
{typed, Attr, unfold_types(Env, E, Options), unfold_types_in_type(Env, Type, Options1)};
unfold_types(Env, {arg, Attr, Id, Type}, Options) -> unfold_types(Env, {arg, Attr, Id, Type}, Options) ->
{arg, Attr, Id, unfold_types_in_type(Env, Type, Options)}; {arg, Attr, Id, unfold_types_in_type(Env, Type, Options)};
unfold_types(Env, {type_sig, Ann, Constr, NamedArgs, Args, Ret}, Options) -> unfold_types(Env, {type_sig, Ann, Constr, NamedArgs, Args, Ret}, Options) ->
@@ -2194,8 +1966,7 @@ unfold_types_in_type(Env, T) ->
unfold_types_in_type(Env, {app_t, Ann, Id = {id, _, "map"}, Args = [KeyType0, _]}, Options) -> unfold_types_in_type(Env, {app_t, Ann, Id = {id, _, "map"}, Args = [KeyType0, _]}, Options) ->
Args1 = [KeyType, _] = unfold_types_in_type(Env, Args, Options), Args1 = [KeyType, _] = unfold_types_in_type(Env, Args, Options),
Ann1 = proplists:get_value(ann, Options, aeso_syntax:get_ann(KeyType0)), [ type_error({map_in_map_key, KeyType0}) || has_maps(KeyType) ],
[ type_error({map_in_map_key, Ann1, KeyType0}) || has_maps(KeyType) ],
{app_t, Ann, Id, Args1}; {app_t, Ann, Id, Args1};
unfold_types_in_type(Env, {app_t, Ann, Id, Args}, Options) when ?is_type_id(Id) -> unfold_types_in_type(Env, {app_t, Ann, Id, Args}, Options) when ?is_type_id(Id) ->
UnfoldRecords = proplists:get_value(unfold_record_types, Options, false), UnfoldRecords = proplists:get_value(unfold_record_types, Options, false),
@@ -2269,13 +2040,8 @@ subst_tvars1(_Env, X) ->
unify(_, {id, _, "_"}, _, _When) -> true; unify(_, {id, _, "_"}, _, _When) -> true;
unify(_, _, {id, _, "_"}, _When) -> true; unify(_, _, {id, _, "_"}, _When) -> true;
unify(Env, A, B, When) -> unify(Env, A, B, When) ->
Options = A1 = dereference(unfold_types_in_type(Env, A)),
case When of %% Improve source location for map_in_map_key errors B1 = dereference(unfold_types_in_type(Env, B)),
{check_expr, E, _, _} -> [{ann, aeso_syntax:get_ann(E)}];
_ -> []
end,
A1 = dereference(unfold_types_in_type(Env, A, Options)),
B1 = dereference(unfold_types_in_type(Env, B, Options)),
unify1(Env, A1, B1, When). unify1(Env, A1, B1, When).
unify1(_Env, {uvar, _, R}, {uvar, _, R}, _When) -> unify1(_Env, {uvar, _, R}, {uvar, _, R}, _When) ->
@@ -2306,9 +2072,6 @@ unify1(_Env, {qcon, _, Name}, {qcon, _, Name}, _When) ->
true; true;
unify1(_Env, {bytes_t, _, Len}, {bytes_t, _, Len}, _When) -> unify1(_Env, {bytes_t, _, Len}, {bytes_t, _, Len}, _When) ->
true; true;
unify1(Env, {if_t, _, {id, _, Id}, Then1, Else1}, {if_t, _, {id, _, Id}, Then2, Else2}, When) ->
unify(Env, Then1, Then2, When) andalso
unify(Env, Else1, Else2, When);
unify1(Env, {fun_t, _, Named1, Args1, Result1}, {fun_t, _, Named2, Args2, Result2}, When) unify1(Env, {fun_t, _, Named1, Args1, Result1}, {fun_t, _, Named2, Args2, Result2}, When)
when length(Args1) == length(Args2) -> when length(Args1) == length(Args2) ->
unify(Env, Named1, Named2, When) andalso unify(Env, Named1, Named2, When) andalso
@@ -2370,8 +2133,6 @@ occurs_check1(R, {record_t, Fields}) ->
occurs_check(R, Fields); occurs_check(R, Fields);
occurs_check1(R, {field_t, _, _, T}) -> occurs_check1(R, {field_t, _, _, T}) ->
occurs_check(R, T); occurs_check(R, T);
occurs_check1(R, {if_t, _, _, Then, Else}) ->
occurs_check(R, [Then, Else]);
occurs_check1(R, [H | T]) -> occurs_check1(R, [H | T]) ->
occurs_check(R, H) orelse occurs_check(R, T); occurs_check(R, H) orelse occurs_check(R, T);
occurs_check1(_, []) -> false. occurs_check1(_, []) -> false.
@@ -2501,30 +2262,6 @@ mk_t_err(Pos, Msg) ->
mk_t_err(Pos, Msg, Ctxt) -> mk_t_err(Pos, Msg, Ctxt) ->
aeso_errors:new(type_error, Pos, lists:flatten(Msg), lists:flatten(Ctxt)). aeso_errors:new(type_error, Pos, lists:flatten(Msg), lists:flatten(Ctxt)).
mk_error({no_decls, File}) ->
Pos = aeso_errors:pos(File, 0, 0),
mk_t_err(Pos, "Empty contract\n");
mk_error({mismatched_decl_in_funblock, Name, Decl}) ->
Msg = io_lib:format("Mismatch in the function block. Expected implementation/type declaration of ~s function\n", [Name]),
mk_t_err(pos(Decl), Msg);
mk_error({higher_kinded_typevar, T}) ->
Msg = io_lib:format("Type ~s is a higher kinded type variable\n"
"(takes another type as an argument)\n", [pp(instantiate(T))]
),
mk_t_err(pos(T), Msg);
mk_error({wrong_type_arguments, X, ArityGiven, ArityReal}) ->
Msg = io_lib:format("Arity for ~s doesn't match. Expected ~p, got ~p\n"
, [pp(instantiate(X)), ArityReal, ArityGiven]
),
mk_t_err(pos(X), Msg);
mk_error({unnamed_map_update_with_default, Upd}) ->
Msg = "Invalid map update with default\n",
mk_t_err(pos(Upd), Msg);
mk_error({fundecl_must_have_funtype, _Ann, Id, Type}) ->
Msg = io_lib:format("~s at ~s was declared with an invalid type ~s.\n"
"Entrypoints and functions must have functional types"
, [pp(Id), pp_loc(Id), pp(instantiate(Type))]),
mk_t_err(pos(Id), Msg);
mk_error({cannot_unify, A, B, When}) -> mk_error({cannot_unify, A, B, When}) ->
Msg = io_lib:format("Cannot unify ~s\n and ~s\n", Msg = io_lib:format("Cannot unify ~s\n and ~s\n",
[pp(instantiate(A)), pp(instantiate(B))]), [pp(instantiate(A)), pp(instantiate(B))]),
@@ -2607,6 +2344,14 @@ mk_error({indexed_type_must_be_word, Type, Type1}) ->
Msg = io_lib:format("The indexed type ~s (at ~s) equals ~s which is not a word type\n", Msg = io_lib:format("The indexed type ~s (at ~s) equals ~s which is not a word type\n",
[pp_type("", Type), pp_loc(Type), pp_type("", Type1)]), [pp_type("", Type), pp_loc(Type), pp_type("", Type1)]),
mk_t_err(pos(Type), Msg); mk_t_err(pos(Type), Msg);
mk_error({payload_type_must_be_string, Type, Type}) ->
Msg = io_lib:format("The payload type ~s (at ~s) should be string\n",
[pp_type("", Type), pp_loc(Type)]),
mk_t_err(pos(Type), Msg);
mk_error({payload_type_must_be_string, Type, Type1}) ->
Msg = io_lib:format("The payload type ~s (at ~s) equals ~s but it should be string\n",
[pp_type("", Type), pp_loc(Type), pp_type("", Type1)]),
mk_t_err(pos(Type), Msg);
mk_error({event_0_to_3_indexed_values, Constr}) -> mk_error({event_0_to_3_indexed_values, Constr}) ->
Msg = io_lib:format("The event constructor ~s (at ~s) has too many indexed values (max 3)\n", Msg = io_lib:format("The event constructor ~s (at ~s) has too many indexed values (max 3)\n",
[name(Constr), pp_loc(Constr)]), [name(Constr), pp_loc(Constr)]),
@@ -2650,21 +2395,13 @@ mk_error({include, _, {string, Pos, Name}}) ->
[binary_to_list(Name), pp_loc(Pos)]), [binary_to_list(Name), pp_loc(Pos)]),
mk_t_err(pos(Pos), Msg); mk_t_err(pos(Pos), Msg);
mk_error({namespace, _Pos, {con, Pos, Name}, _Def}) -> mk_error({namespace, _Pos, {con, Pos, Name}, _Def}) ->
Msg = io_lib:format("Nested namespaces are not allowed\nNamespace '~s' at ~s not defined at top level.\n", Msg = io_lib:format("Nested namespace not allowed\nNamespace '~s' at ~s not defined at top level.\n",
[Name, pp_loc(Pos)]),
mk_t_err(pos(Pos), Msg);
mk_error({contract, _Pos, {con, Pos, Name}, _Def}) ->
Msg = io_lib:format("Nested contracts are not allowed\nContract '~s' at ~s not defined at top level.\n",
[Name, pp_loc(Pos)]),
mk_t_err(pos(Pos), Msg);
mk_error({type_decl, _, {id, Pos, Name}, _}) ->
Msg = io_lib:format("Empty type declarations are not supported\nType ~s at ~s lacks a definition\n",
[Name, pp_loc(Pos)]),
mk_t_err(pos(Pos), Msg);
mk_error({letval, _Pos, {id, Pos, Name}, _Def}) ->
Msg = io_lib:format("Toplevel \"let\" definitions are not supported\nValue ~s at ~s could be replaced by 0-argument function\n",
[Name, pp_loc(Pos)]), [Name, pp_loc(Pos)]),
mk_t_err(pos(Pos), Msg); mk_t_err(pos(Pos), Msg);
mk_error({repeated_arg, Fun, Arg}) ->
Msg = io_lib:format("Repeated argument ~s to function ~s (at ~s).\n",
[Arg, pp(Fun), pp_loc(Fun)]),
mk_t_err(pos(Fun), Msg);
mk_error({stateful_not_allowed, Id, Fun}) -> mk_error({stateful_not_allowed, Id, Fun}) ->
Msg = io_lib:format("Cannot reference stateful function ~s (at ~s)\nin the definition of non-stateful function ~s.\n", Msg = io_lib:format("Cannot reference stateful function ~s (at ~s)\nin the definition of non-stateful function ~s.\n",
[pp(Id), pp_loc(Id), pp(Fun)]), [pp(Id), pp_loc(Id), pp(Fun)]),
@@ -2739,10 +2476,10 @@ mk_error({new_tuple_syntax, Ann, Ts}) ->
Msg = io_lib:format("Invalid type\n~s (at ~s)\nThe syntax of tuple types changed in Sophia version 4.0. Did you mean\n~s\n", Msg = io_lib:format("Invalid type\n~s (at ~s)\nThe syntax of tuple types changed in Sophia version 4.0. Did you mean\n~s\n",
[pp_type(" ", {args_t, Ann, Ts}), pp_loc(Ann), pp_type(" ", {tuple_t, Ann, Ts})]), [pp_type(" ", {args_t, Ann, Ts}), pp_loc(Ann), pp_type(" ", {tuple_t, Ann, Ts})]),
mk_t_err(pos(Ann), Msg); mk_t_err(pos(Ann), Msg);
mk_error({map_in_map_key, Ann, KeyType}) -> mk_error({map_in_map_key, KeyType}) ->
Msg = io_lib:format("Invalid key type\n~s\n", [pp_type(" ", KeyType)]), Msg = io_lib:format("Invalid key type\n~s\n", [pp_type(" ", KeyType)]),
Cxt = "Map keys cannot contain other maps.\n", Cxt = "Map keys cannot contain other maps.\n",
mk_t_err(pos(Ann), Msg, Cxt); mk_t_err(pos(KeyType), Msg, Cxt);
mk_error({cannot_call_init_function, Ann}) -> mk_error({cannot_call_init_function, Ann}) ->
Msg = "The 'init' function is called exclusively by the create contract transaction\n" Msg = "The 'init' function is called exclusively by the create contract transaction\n"
"and cannot be called from the contract code.\n", "and cannot be called from the contract code.\n",
@@ -2792,12 +2529,6 @@ mk_error({mixed_record_and_map, Expr}) ->
Msg = io_lib:format("Mixed record fields and map keys in\n~s", Msg = io_lib:format("Mixed record fields and map keys in\n~s",
[pp_expr(" ", Expr)]), [pp_expr(" ", Expr)]),
mk_t_err(pos(Expr), Msg); mk_t_err(pos(Expr), Msg);
mk_error({named_argument_must_be_literal_bool, Name, Arg}) ->
Msg = io_lib:format("Invalid '~s' argument\n~s\nIt must be either 'true' or 'false'.", [Name, pp_expr(" ", instantiate(Arg))]),
mk_t_err(pos(Arg), Msg);
mk_error({conflicting_updates_for_field, Upd, Key}) ->
Msg = io_lib:format("Conflicting updates for field '~s'\n", [Key]),
mk_t_err(pos(Upd), Msg);
mk_error(Err) -> mk_error(Err) ->
Msg = io_lib:format("Unknown error: ~p\n", [Err]), Msg = io_lib:format("Unknown error: ~p\n", [Err]),
mk_t_err(pos(0, 0), Msg). mk_t_err(pos(0, 0), Msg).
@@ -2816,7 +2547,7 @@ pp_when({check_typesig, Name, Inferred, Given}) ->
" inferred type: ~s\n" " inferred type: ~s\n"
" given type: ~s\n", " given type: ~s\n",
[Name, pp_loc(Given), pp(instantiate(Inferred)), pp(instantiate(Given))])}; [Name, pp_loc(Given), pp(instantiate(Inferred)), pp(instantiate(Given))])};
pp_when({infer_app, Fun, NamedArgs, Args, Inferred0, ArgTypes0}) -> pp_when({infer_app, Fun, Args, Inferred0, ArgTypes0}) ->
Inferred = instantiate(Inferred0), Inferred = instantiate(Inferred0),
ArgTypes = instantiate(ArgTypes0), ArgTypes = instantiate(ArgTypes0),
{pos(Fun), {pos(Fun),
@@ -2825,7 +2556,6 @@ pp_when({infer_app, Fun, NamedArgs, Args, Inferred0, ArgTypes0}) ->
"to arguments\n~s", "to arguments\n~s",
[pp_loc(Fun), [pp_loc(Fun),
pp_typed(" ", Fun, Inferred), pp_typed(" ", Fun, Inferred),
[ [pp_expr(" ", NamedArg), "\n"] || NamedArg <- NamedArgs ] ++
[ [pp_typed(" ", Arg, ArgT), "\n"] [ [pp_typed(" ", Arg, ArgT), "\n"]
|| {Arg, ArgT} <- lists:zip(Args, ArgTypes) ] ])}; || {Arg, ArgT} <- lists:zip(Args, ArgTypes) ] ])};
pp_when({field_constraint, FieldType0, InferredType0, Fld}) -> pp_when({field_constraint, FieldType0, InferredType0, Fld}) ->
@@ -2902,12 +2632,6 @@ pp_when({list_comp, BindExpr, Inferred0, Expected0}) ->
io_lib:format("when checking rvalue of list comprehension binding at ~s\n~s\n" io_lib:format("when checking rvalue of list comprehension binding at ~s\n~s\n"
"against type \n~s\n", "against type \n~s\n",
[pp_loc(BindExpr), pp_typed(" ", BindExpr, Inferred), pp_type(" ", Expected)])}; [pp_loc(BindExpr), pp_typed(" ", BindExpr, Inferred), pp_type(" ", Expected)])};
pp_when({check_named_arg_constraint, C}) ->
{id, _, Name} = Arg = C#named_argument_constraint.name,
[Type | _] = [ Type || {named_arg_t, _, {id, _, Name1}, Type, _} <- C#named_argument_constraint.args, Name1 == Name ],
Err = io_lib:format("when checking named argument\n~s\nagainst inferred type\n~s",
[pp_typed(" ", Arg, Type), pp_type(" ", C#named_argument_constraint.type)]),
{pos(Arg), Err};
pp_when(unknown) -> {pos(0,0), ""}. pp_when(unknown) -> {pos(0,0), ""}.
-spec pp_why_record(why_record()) -> {pos(), iolist()}. -spec pp_why_record(why_record()) -> {pos(), iolist()}.
@@ -2985,8 +2709,6 @@ pp({uvar, _, Ref}) ->
["?u" | integer_to_list(erlang:phash2(Ref, 16384)) ]; ["?u" | integer_to_list(erlang:phash2(Ref, 16384)) ];
pp({tvar, _, Name}) -> pp({tvar, _, Name}) ->
Name; Name;
pp({if_t, _, Id, Then, Else}) ->
["if(", pp([Id, Then, Else]), ")"];
pp({tuple_t, _, []}) -> pp({tuple_t, _, []}) ->
"unit"; "unit";
pp({tuple_t, _, Cpts}) -> pp({tuple_t, _, Cpts}) ->
@@ -2998,8 +2720,8 @@ pp({app_t, _, T, []}) ->
pp(T); pp(T);
pp({app_t, _, Type, Args}) -> pp({app_t, _, Type, Args}) ->
[pp(Type), "(", pp(Args), ")"]; [pp(Type), "(", pp(Args), ")"];
pp({named_arg_t, _, Name, Type, _Default}) -> pp({named_arg_t, _, Name, Type, Default}) ->
[pp(Name), " : ", pp(Type)]; [pp(Name), " : ", pp(Type), " = ", pp(Default)];
pp({fun_t, _, Named = {uvar, _, _}, As, B}) -> pp({fun_t, _, Named = {uvar, _, _}, As, B}) ->
["(", pp(Named), " | ", pp(As), ") => ", pp(B)]; ["(", pp(Named), " | ", pp(As), ") => ", pp(B)];
pp({fun_t, _, Named, As, B}) when is_list(Named) -> pp({fun_t, _, Named, As, B}) when is_list(Named) ->
@@ -3029,7 +2751,7 @@ desugar_updates([Upd | Updates]) ->
{More, Updates1} = updates_key(Key, Updates), {More, Updates1} = updates_key(Key, Updates),
%% Check conflicts %% Check conflicts
case length([ [] || [] <- [Rest | More] ]) of case length([ [] || [] <- [Rest | More] ]) of
N when N > 1 -> type_error({conflicting_updates_for_field, Upd, Key}); N when N > 1 -> error({conflicting_updates_for_field, Upd, Key});
_ -> ok _ -> ok
end, end,
[MakeField(lists:append([Rest | More])) | desugar_updates(Updates1)]. [MakeField(lists:append([Rest | More])) | desugar_updates(Updates1)].
+13 -79
View File
@@ -36,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()}
@@ -178,43 +171,13 @@ ast_to_fcode(Code, Options) ->
-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(),
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 => #{} }.
@@ -231,25 +194,18 @@ builtins() ->
{["Contract"], [{"address", none}, {"balance", none}, {"creator", none}]}, {["Contract"], [{"address", none}, {"balance", none}, {"creator", none}]},
{["Call"], [{"origin", none}, {"caller", none}, {"value", none}, {"gas_price", 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}]},
@@ -268,11 +224,6 @@ 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],
[hash], [hash], [address, hash], [address],
[address, integer], [address, integer], [address],
[address], [address], [address], [address], [address],
[integer], [address, integer], []]},
#{ ["int"] => ?type(integer), #{ ["int"] => ?type(integer),
["bool"] => ?type(boolean), ["bool"] => ?type(boolean),
["bits"] => ?type(bits), ["bits"] => ?type(bits),
@@ -282,18 +233,11 @@ init_type_env() ->
["hash"] => ?type(hash), ["hash"] => ?type(hash),
["signature"] => ?type(signature), ["signature"] => ?type(signature),
["oracle"] => ?type(Q, R, {oracle, Q, R}), ["oracle"] => ?type(Q, R, {oracle, Q, R}),
["oracle_query"] => ?type(_, _, oracle_query), %% TODO: not in Fate ["oracle_query"] => ?type(_, _, oracle_query),
["list"] => ?type(T, {list, T}), ["list"] => ?type(T, {list, T}),
["map"] => ?type(K, V, {map, K, V}), ["map"] => ?type(K, V, {map, K, V}),
["option"] => ?type(T, {variant, [[], [T]]}), ["option"] => ?type(T, {variant, [[], [T]]}),
["Chain", "ttl"] => ?type({variant, [[integer], [integer]]}), ["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) ->
@@ -347,6 +291,7 @@ decls_to_fcode(Env, Decls) ->
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, {type_decl, _, _, _}) -> Env;
decl_to_fcode(Env = #{context := {main_contract, _}}, {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});
@@ -467,8 +412,6 @@ 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}).
@@ -1043,21 +986,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) ->
@@ -1218,8 +1152,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) ->
+1 -13
View File
@@ -469,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;
@@ -625,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) ->
@@ -934,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)}.
+30 -51
View File
@@ -38,13 +38,10 @@
| pp_assembler | pp_assembler
| pp_bytecode | pp_bytecode
| no_code | no_code
| keep_included
| debug_mode
| {backend, aevm | fate} | {backend, aevm | fate}
| {include, {file_system, [string()]} | | {include, {file_system, [string()]} |
{explicit_files, #{string() => binary()}}} {explicit_files, #{string() => binary()}}}
| {src_file, string()} | {src_file, string()}.
| {aci, aeso_aci:aci_type()}.
-type options() :: [option()]. -type options() :: [option()].
-export_type([ option/0 -export_type([ option/0
@@ -118,8 +115,7 @@ from_string(Backend, ContractString, Options) ->
end. end.
from_string1(aevm, ContractString, Options) -> from_string1(aevm, ContractString, Options) ->
#{ icode := Icode #{icode := Icode} = string_to_code(ContractString, Options),
, folded_typed_ast := FoldedTypedAst } = string_to_code(ContractString, Options),
TypeInfo = extract_type_info(Icode), TypeInfo = extract_type_info(Icode),
Assembler = assemble(Icode, Options), Assembler = assemble(Icode, Options),
pp_assembler(aevm, Assembler, Options), pp_assembler(aevm, Assembler, Options),
@@ -127,63 +123,47 @@ from_string1(aevm, ContractString, Options) ->
ByteCode = << << B:8 >> || B <- ByteCodeList >>, ByteCode = << << B:8 >> || B <- ByteCodeList >>,
pp_bytecode(ByteCode, Options), pp_bytecode(ByteCode, Options),
{ok, Version} = version(), {ok, Version} = version(),
Res = #{byte_code => ByteCode, {ok, #{byte_code => ByteCode,
compiler_version => Version, compiler_version => Version,
contract_source => ContractString, contract_source => ContractString,
type_info => TypeInfo, type_info => TypeInfo,
abi_version => aeb_aevm_abi:abi_version(), abi_version => aeb_aevm_abi:abi_version(),
payable => maps:get(payable, Icode) payable => maps:get(payable, Icode)
}, }};
{ok, maybe_generate_aci(Res, FoldedTypedAst, Options)};
from_string1(fate, ContractString, Options) -> from_string1(fate, ContractString, Options) ->
#{ fcode := FCode #{fcode := FCode} = string_to_code(ContractString, Options),
, folded_typed_ast := FoldedTypedAst } = string_to_code(ContractString, Options),
FateCode = aeso_fcode_to_fate:compile(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(),
Res = #{byte_code => ByteCode, {ok, #{byte_code => ByteCode,
compiler_version => Version, compiler_version => Version,
contract_source => ContractString, contract_source => ContractString,
type_info => [], type_info => [],
fate_code => FateCode, fate_code => FateCode,
abi_version => aeb_fate_abi:abi_version(), abi_version => aeb_fate_abi:abi_version(),
payable => maps:get(payable, FCode) payable => maps:get(payable, FCode)
}, }}.
{ok, maybe_generate_aci(Res, FoldedTypedAst, Options)}.
maybe_generate_aci(Result, FoldedTypedAst, Options) ->
case proplists:get_value(aci, Options) of
undefined ->
Result;
Type ->
{ok, Aci} = aeso_aci:from_typed_ast(Type, FoldedTypedAst),
maps:put(aci, Aci, Result)
end.
-spec string_to_code(string(), options()) -> map(). -spec string_to_code(string(), options()) -> map().
string_to_code(ContractString, Options) -> string_to_code(ContractString, Options) ->
Ast = parse(ContractString, Options), Ast = parse(ContractString, Options),
pp_sophia_code(Ast, Options), pp_sophia_code(Ast, Options),
pp_ast(Ast, Options), pp_ast(Ast, Options),
{TypeEnv, FoldedTypedAst, UnfoldedTypedAst} = aeso_ast_infer_types:infer(Ast, [return_env | Options]), {TypeEnv, TypedAst} = aeso_ast_infer_types:infer(Ast, [return_env | Options]),
pp_typed_ast(UnfoldedTypedAst, Options), pp_typed_ast(TypedAst, Options),
case proplists:get_value(backend, Options, aevm) of case proplists:get_value(backend, Options, aevm) of
aevm -> aevm ->
Icode = ast_to_icode(UnfoldedTypedAst, Options), Icode = ast_to_icode(TypedAst, Options),
pp_icode(Icode, Options), pp_icode(Icode, Options),
#{ icode => Icode #{ icode => Icode,
, unfolded_typed_ast => UnfoldedTypedAst typed_ast => TypedAst,
, folded_typed_ast => FoldedTypedAst type_env => TypeEnv};
, type_env => TypeEnv
, ast => Ast };
fate -> fate ->
Fcode = aeso_ast_to_fcode:ast_to_fcode(UnfoldedTypedAst, Options), Fcode = aeso_ast_to_fcode:ast_to_fcode(TypedAst, Options),
#{ fcode => Fcode #{ fcode => Fcode,
, unfolded_typed_ast => UnfoldedTypedAst typed_ast => TypedAst,
, folded_typed_ast => FoldedTypedAst type_env => TypeEnv}
, type_env => TypeEnv
, ast => Ast }
end. end.
-define(CALL_NAME, "__call"). -define(CALL_NAME, "__call").
@@ -218,9 +198,9 @@ check_call1(ContractString0, FunName, Args, Options) ->
case proplists:get_value(backend, Options, aevm) of case proplists:get_value(backend, Options, aevm) of
aevm -> aevm ->
%% First check the contract without the __call function %% First check the contract without the __call function
#{ast := Ast} = string_to_code(ContractString0, Options), #{} = string_to_code(ContractString0, Options),
ContractString = insert_call_function(Ast, ContractString0, ?CALL_NAME, FunName, Args), ContractString = insert_call_function(ContractString0, ?CALL_NAME, FunName, Args, Options),
#{unfolded_typed_ast := TypedAst, #{typed_ast := TypedAst,
icode := Icode} = string_to_code(ContractString, Options), icode := Icode} = string_to_code(ContractString, Options),
{ok, {FunName, {fun_t, _, _, ArgTypes, RetType}}} = get_call_type(TypedAst), {ok, {FunName, {fun_t, _, _, ArgTypes, RetType}}} = get_call_type(TypedAst),
ArgVMTypes = [ aeso_ast_to_icode:ast_typerep(T, Icode) || T <- ArgTypes ], ArgVMTypes = [ aeso_ast_to_icode:ast_typerep(T, Icode) || T <- ArgTypes ],
@@ -240,14 +220,13 @@ check_call1(ContractString0, FunName, Args, Options) ->
{ok, FunName, {ArgVMTypes, RetVMType1}, ArgTerms}; {ok, FunName, {ArgVMTypes, RetVMType1}, ArgTerms};
fate -> fate ->
%% First check the contract without the __call function %% First check the contract without the __call function
#{ fcode := OrgFcode #{fcode := OrgFcode} = string_to_code(ContractString0, Options),
, ast := Ast } = string_to_code(ContractString0, Options),
FateCode = aeso_fcode_to_fate:compile(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,
lists:seq($1, $9) ++ lists:seq($A, $Z) ++ lists:seq($a, $z)), lists:seq($1, $9) ++ lists:seq($A, $Z) ++ lists:seq($a, $z)),
ContractString = insert_call_function(Ast, ContractString0, CallName, FunName, Args), ContractString = insert_call_function(ContractString0, CallName, FunName, Args, Options),
#{fcode := Fcode} = string_to_code(ContractString, Options), #{fcode := Fcode} = string_to_code(ContractString, Options),
CallArgs = arguments_of_body(CallName, FunName, Fcode), CallArgs = arguments_of_body(CallName, FunName, Fcode),
{ok, FunName, CallArgs} {ok, FunName, CallArgs}
@@ -273,8 +252,9 @@ first_none_match(CallName, Hashes, [Char|Chars]) ->
end. end.
%% Add the __call function to a contract. %% Add the __call function to a contract.
-spec insert_call_function(aeso_syntax:ast(), string(), string(), string(), [string()]) -> string(). -spec insert_call_function(string(), string(), string(), [string()], options()) -> string().
insert_call_function(Ast, Code, Call, FunName, Args) -> insert_call_function(Code, Call, FunName, Args, Options) ->
Ast = parse(Code, Options),
Ind = last_contract_indent(Ast), Ind = last_contract_indent(Ast),
lists:flatten( lists:flatten(
[ Code, [ Code,
@@ -330,7 +310,7 @@ to_sophia_value(ContractString, FunName, ok, Data, Options0) ->
Options = [no_code | Options0], Options = [no_code | Options0],
try try
Code = string_to_code(ContractString, Options), Code = string_to_code(ContractString, Options),
#{ unfolded_typed_ast := TypedAst, type_env := TypeEnv} = Code, #{ typed_ast := TypedAst, type_env := TypeEnv} = Code,
{ok, _, Type0} = get_decode_type(FunName, TypedAst), {ok, _, Type0} = get_decode_type(FunName, TypedAst),
Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]), Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]),
@@ -356,12 +336,12 @@ to_sophia_value(ContractString, FunName, ok, Data, Options0) ->
try try
{ok, aeso_vm_decode:from_fate(Type, aeb_fate_encoding:deserialize(Data))} {ok, aeso_vm_decode:from_fate(Type, aeb_fate_encoding:deserialize(Data))}
catch throw:cannot_translate_to_sophia -> catch throw:cannot_translate_to_sophia ->
Type1 = prettypr:format(aeso_pretty:type(Type0)), Type1 = prettypr:format(aeso_pretty:type(Type)),
Msg = io_lib:format("Cannot translate FATE value ~p\n of Sophia type ~s\n", Msg = io_lib:format("Cannot translate FATE value ~p\n of Sophia type ~s\n",
[aeb_fate_encoding:deserialize(Data), Type1]), [aeb_fate_encoding:deserialize(Data), Type1]),
{error, [aeso_errors:new(data_error, Msg)]}; {error, [aeso_errors:new(data_error, Msg)]};
_:_ -> _:_ ->
Type1 = prettypr:format(aeso_pretty:type(Type0)), Type1 = prettypr:format(aeso_pretty:type(Type)),
Msg = io_lib:format("Failed to decode binary as type ~s\n", [Type1]), Msg = io_lib:format("Failed to decode binary as type ~s\n", [Type1]),
{error, [aeso_errors:new(data_error, Msg)]} {error, [aeso_errors:new(data_error, Msg)]}
end end
@@ -406,7 +386,7 @@ decode_calldata(ContractString, FunName, Calldata, Options0) ->
Options = [no_code | Options0], Options = [no_code | Options0],
try try
Code = string_to_code(ContractString, Options), Code = string_to_code(ContractString, Options),
#{ unfolded_typed_ast := TypedAst, type_env := TypeEnv} = Code, #{ typed_ast := TypedAst, type_env := TypeEnv} = Code,
{ok, Args, _} = get_decode_type(FunName, TypedAst), {ok, Args, _} = get_decode_type(FunName, TypedAst),
GetType = fun({typed, _, _, T}) -> T; (T) -> T end, GetType = fun({typed, _, _, T}) -> T; (T) -> T end,
@@ -670,9 +650,8 @@ pp_fate_type(T) -> io_lib:format("~w", [T]).
%% ------------------------------------------------------------------- %% -------------------------------------------------------------------
-spec sophia_type_to_typerep(string()) -> {error, bad_type} | {ok, aeb_aevm_data:type()}.
sophia_type_to_typerep(String) -> sophia_type_to_typerep(String) ->
Ast = aeso_parser:run_parser(aeso_parser:type(), String), {ok, Ast} = aeso_parser:type(String),
try aeso_ast_to_icode:ast_typerep(Ast) of try aeso_ast_to_icode:ast_typerep(Ast) of
Type -> {ok, Type} Type -> {ok, Type}
catch _:_ -> {error, bad_type} catch _:_ -> {error, bad_type}
+9 -92
View File
@@ -302,13 +302,11 @@ 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
{lit, {bool, false}} ->
case Gas of case Gas of
{builtin, call_gas_left, _} -> {builtin, call_gas_left, _} ->
Call = aeb_fate_ops:call_r(?a, Lbl, ArgType, RetType, ?a), Call = aeb_fate_ops:call_r(?a, Lbl, ArgType, RetType, ?a),
@@ -317,13 +315,6 @@ to_scode1(Env, {remote, ArgsT, RetT, Ct, Fun, [Gas, Value, Protected | Args]}) -
Call = aeb_fate_ops:call_gr(?a, Lbl, ArgType, RetType, ?a, ?a), Call = aeb_fate_ops:call_gr(?a, Lbl, ArgType, RetType, ?a, ?a),
call_to_scode(Env, Call, [Ct, Value, Gas | Args]) call_to_scode(Env, Call, [Ct, Value, Gas | Args])
end; end;
{lit, {bool, true}} ->
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_to_scode(Env, Call, [Ct, Value, Gas, Protected | Args])
end;
to_scode1(_Env, {get_state, Reg}) -> to_scode1(_Env, {get_state, Reg}) ->
[push(?s(Reg))]; [push(?s(Reg))];
@@ -507,8 +498,6 @@ 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) ->
@@ -547,15 +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)].
%% -- Operators -- %% -- Operators --
@@ -582,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);
@@ -608,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.
@@ -782,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, []);
@@ -864,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]);
@@ -903,44 +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]);
{'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, [])
@@ -1693,7 +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) == '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) ->
+2 -6
View File
@@ -88,18 +88,13 @@ builtin_types() ->
, "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.
+7 -15
View File
@@ -74,31 +74,25 @@
%% first argument. I.e. no backtracking to the second argument if the first %% first argument. I.e. no backtracking to the second argument if the first
%% fails. %% fails.
trampoline({bounce, Cont}) when is_function(Cont, 0) ->
trampoline(Cont());
trampoline(Res) ->
Res.
-define(BOUNCE(X), {bounce, fun() -> X end}).
%% Apply a parser to its continuation. This compiles a parser to its low-level representation. %% Apply a parser to its continuation. This compiles a parser to its low-level representation.
-spec apply_p(parser(A), fun((A) -> parser1(B))) -> parser1(B). -spec apply_p(parser(A), fun((A) -> parser1(B))) -> parser1(B).
apply_p(?lazy(F), K) -> apply_p(F(), K); apply_p(?lazy(F), K) -> apply_p(F(), K);
apply_p(?fail(Err), _) -> {fail, Err}; apply_p(?fail(Err), _) -> {fail, Err};
apply_p(?choice([P | Ps]), K) -> lists:foldl(fun(Q, R) -> choice1(trampoline(apply_p(Q, K)), R) end, apply_p(?choice([P | Ps]), K) -> lists:foldl(fun(Q, R) -> choice1(apply_p(Q, K), R) end,
trampoline(apply_p(P, K)), Ps); apply_p(P, K), Ps);
apply_p(?bind(P, F), K) -> apply_p(P, fun(X) -> apply_p(F(X), K) end); apply_p(?bind(P, F), K) -> apply_p(P, fun(X) -> apply_p(F(X), K) end);
apply_p(?right(P, Q), K) -> apply_p(P, fun(_) -> apply_p(Q, K) end); apply_p(?right(P, Q), K) -> apply_p(P, fun(_) -> apply_p(Q, K) end);
apply_p(?left(P, Q), K) -> apply_p(P, fun(X) -> apply_p(Q, fun(_) -> K(X) end) end); apply_p(?left(P, Q), K) -> apply_p(P, fun(X) -> apply_p(Q, fun(_) -> K(X) end) end);
apply_p(?map(F, P), K) -> apply_p(P, fun(X) -> K(F(X)) end); apply_p(?map(F, P), K) -> apply_p(P, fun(X) -> K(F(X)) end);
apply_p(?layout, K) -> {layout, K, {fail, {expected, layout_block}}}; apply_p(?layout, K) -> {layout, K, {fail, {expected, layout_block}}};
apply_p(?tok(Atom), K) -> {tok_bind, #{Atom => K}}; apply_p(?tok(Atom), K) -> {tok_bind, #{Atom => K}};
apply_p(?return(X), K) -> ?BOUNCE(K(X)); apply_p(?return(X), K) -> K(X);
apply_p([P | Q], K) -> apply_p(P, fun(H) -> apply_p(Q, fun(T) -> K([H | T]) end) end); apply_p([P | Q], K) -> apply_p(P, fun(H) -> apply_p(Q, fun(T) -> K([H | T]) end) end);
apply_p(T, K) when is_tuple(T) -> apply_p(tuple_to_list(T), fun(Xs) -> K(list_to_tuple(Xs)) end); apply_p(T, K) when is_tuple(T) -> apply_p(tuple_to_list(T), fun(Xs) -> K(list_to_tuple(Xs)) end);
apply_p(M, K) when is_map(M) -> apply_p(M, K) when is_map(M) ->
{Keys, Ps} = lists:unzip(maps:to_list(M)), {Keys, Ps} = lists:unzip(maps:to_list(M)),
apply_p(Ps, fun(Vals) -> K(maps:from_list(lists:zip(Keys, Vals))) end); apply_p(Ps, fun(Vals) -> K(maps:from_list(lists:zip(Keys, Vals))) end);
apply_p(X, K) -> ?BOUNCE(K(X)). apply_p(X, K) -> K(X).
%% -- Primitive combinators -------------------------------------------------- %% -- Primitive combinators --------------------------------------------------
@@ -166,7 +160,7 @@ layout() -> ?layout.
%% @doc Parse a sequence of tokens using a parser. Fails if the parse is ambiguous. %% @doc Parse a sequence of tokens using a parser. Fails if the parse is ambiguous.
-spec parse(parser(A), tokens()) -> {ok, A} | {error, term()}. -spec parse(parser(A), tokens()) -> {ok, A} | {error, term()}.
parse(P, S) -> parse(P, S) ->
case parse1(trampoline(apply_p(P, fun(X) -> {return_plus, X, {fail, no_error}} end)), S) of case parse1(apply_p(P, fun(X) -> {return_plus, X, {fail, no_error}} end), S) of
{[], {Pos, Err}} -> {error, {add_current_file(Pos), parse_error, flatten_error(Err)}}; {[], {Pos, Err}} -> {error, {add_current_file(Pos), parse_error, flatten_error(Err)}};
{[A], _} -> {ok, A}; {[A], _} -> {ok, A};
{As, _} -> {error, {{1, 1}, ambiguous_parse, As}} {As, _} -> {error, {{1, 1}, ambiguous_parse, As}}
@@ -247,7 +241,7 @@ col(T) when is_tuple(T) -> element(2, pos(T)).
%% If both parsers want the next token we grab it and merge the continuations. %% If both parsers want the next token we grab it and merge the continuations.
choice1({tok_bind, Map1}, {tok_bind, Map2}) -> choice1({tok_bind, Map1}, {tok_bind, Map2}) ->
{tok_bind, merge_with(fun(F, G) -> fun(T) -> choice1(trampoline(F(T)), trampoline(G(T))) end end, Map1, Map2)}; {tok_bind, merge_with(fun(F, G) -> fun(T) -> choice1(F(T), G(T)) end end, Map1, Map2)};
%% If both parsers fail we combine the error messages. If only one fails we discard it. %% If both parsers fail we combine the error messages. If only one fails we discard it.
choice1({fail, E1}, {fail, E2}) -> {fail, add_error(E1, E2)}; choice1({fail, E1}, {fail, E2}) -> {fail, add_error(E1, E2)};
@@ -261,7 +255,7 @@ choice1(P, {return_plus, X, Q}) -> {return_plus, X, choice1(P, Q)};
%% If both sides want a layout block we combine them. If only one side wants a layout block we %% If both sides want a layout block we combine them. If only one side wants a layout block we
%% will commit to a layout block is there is one. %% will commit to a layout block is there is one.
choice1({layout, F, P}, {layout, G, Q}) -> choice1({layout, F, P}, {layout, G, Q}) ->
{layout, fun(N) -> choice1(trampoline(F(N)), trampoline(G(N))) end, choice1(P, Q)}; {layout, fun(N) -> choice1(F(N), G(N)) end, choice1(P, Q)};
choice1({layout, F, P}, Q) -> {layout, F, choice1(P, Q)}; choice1({layout, F, P}, Q) -> {layout, F, choice1(P, Q)};
choice1(P, {layout, G, Q}) -> {layout, G, choice1(P, Q)}. choice1(P, {layout, G, Q}) -> {layout, G, choice1(P, Q)}.
@@ -284,8 +278,6 @@ parse1(P, S) ->
%% The main work horse. Returns a list of possible parses and an error message in case parsing %% The main work horse. Returns a list of possible parses and an error message in case parsing
%% fails. %% fails.
-spec parse1(parser1(A), #ts{}, [A], term()) -> {[A], error()}. -spec parse1(parser1(A), #ts{}, [A], term()) -> {[A], error()}.
parse1({bounce, F}, Ts, Acc, Err) ->
parse1(F(), Ts, Acc, Err);
parse1({tok_bind, Map}, Ts, Acc, Err) -> parse1({tok_bind, Map}, Ts, Acc, Err) ->
case next_token(Ts) of case next_token(Ts) of
{T, Ts1} -> {T, Ts1} ->
+15 -35
View File
@@ -3,33 +3,20 @@
%%% Description : %%% Description :
%%% Created : 1 Mar 2018 by Ulf Norell %%% Created : 1 Mar 2018 by Ulf Norell
-module(aeso_parser). -module(aeso_parser).
-compile({no_auto_import,[map_get/2]}).
-export([string/1, -export([string/1,
string/2, string/2,
string/3, string/3,
auto_imports/1,
hash_include/2, hash_include/2,
decl/0, type/1]).
type/0,
body/0,
maybe_block/1,
run_parser/2,
run_parser/3]).
-include("aeso_parse_lib.hrl"). -include("aeso_parse_lib.hrl").
-import(aeso_parse_lib, [current_file/0, set_current_file/1]). -import(aeso_parse_lib, [current_file/0, set_current_file/1]).
-type parse_result() :: aeso_syntax:ast() | {aeso_syntax:ast(), sets:set(include_hash())} | none(). -type parse_result() :: aeso_syntax:ast() | none().
-type include_hash() :: {string(), binary()}. -type include_hash() :: {string(), binary()}.
escape_errors({ok, Ok}) ->
Ok;
escape_errors({error, Err}) ->
parse_error(Err).
-spec string(string()) -> parse_result(). -spec string(string()) -> parse_result().
string(String) -> string(String) ->
string(String, sets:new(), []). string(String, sets:new(), []).
@@ -43,17 +30,21 @@ string(String, Opts) ->
-spec string(string(), sets:set(include_hash()), aeso_compiler:options()) -> parse_result(). -spec string(string(), sets:set(include_hash()), aeso_compiler:options()) -> parse_result().
string(String, Included, Opts) -> string(String, Included, Opts) ->
AST = run_parser(file(), String, Opts), case parse_and_scan(file(), String, Opts) of
{ok, AST} ->
case expand_includes(AST, Included, Opts) of case expand_includes(AST, Included, Opts) of
{ok, AST1} -> AST1; {ok, AST1} -> AST1;
{error, Err} -> parse_error(Err) {error, Err} -> parse_error(Err)
end;
{error, Err} ->
parse_error(Err)
end. end.
type(String) ->
run_parser(P, Inp) -> case parse_and_scan(type(), String, []) of
escape_errors(parse_and_scan(P, Inp, [])). {ok, AST} -> {ok, AST};
run_parser(P, Inp, Opts) -> {error, Err} -> {error, [mk_error(Err)]}
escape_errors(parse_and_scan(P, Inp, Opts)). end.
parse_and_scan(P, S, Opts) -> parse_and_scan(P, S, Opts) ->
set_current_file(proplists:get_value(src_file, Opts, no_file)), set_current_file(proplists:get_value(src_file, Opts, no_file)),
@@ -605,13 +596,8 @@ expand_includes(AST, Included, Opts) ->
|| File <- lists:usort(auto_imports(AST)) ] ++ AST, || File <- lists:usort(auto_imports(AST)) ] ++ AST,
expand_includes(AST1, Included, [], Opts). expand_includes(AST1, Included, [], Opts).
expand_includes([], Included, Acc, Opts) -> expand_includes([], _Included, Acc, _Opts) ->
case lists:member(keep_included, Opts) of
false ->
{ok, lists:reverse(Acc)}; {ok, lists:reverse(Acc)};
true ->
{ok, {lists:reverse(Acc), Included}}
end;
expand_includes([{include, Ann, {string, _SAnn, File}} | AST], Included, Acc, Opts) -> expand_includes([{include, Ann, {string, _SAnn, File}} | AST], Included, Acc, Opts) ->
case get_include_code(File, Ann, Opts) of case get_include_code(File, Ann, Opts) of
{ok, Code} -> {ok, Code} ->
@@ -670,14 +656,8 @@ stdlib_options() ->
get_include_code(File, Ann, Opts) -> get_include_code(File, Ann, Opts) ->
case {read_file(File, Opts), read_file(File, stdlib_options())} of case {read_file(File, Opts), read_file(File, stdlib_options())} of
{{ok, Bin}, {ok, _}} -> {{ok, _}, {ok,_ }} ->
case filename:basename(File) == File of fail(ann_pos(Ann), "Illegal redefinition of standard library " ++ File);
true -> { error
, fail( ann_pos(Ann)
, "Illegal redefinition of standard library " ++ binary_to_list(File))};
%% If a path is provided then the stdlib takes lower priority
false -> {ok, binary_to_list(Bin)}
end;
{_, {ok, Bin}} -> {_, {ok, Bin}} ->
{ok, binary_to_list(Bin)}; {ok, binary_to_list(Bin)};
{{ok, Bin}, _} -> {{ok, Bin}, _} ->
+17 -26
View File
@@ -145,12 +145,8 @@ 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({contract, Attrs, C, Ds}) -> decl({contract, _, C, Ds}) ->
Mod = fun({Mod, true}) when Mod == payable -> block(follow(text("contract"), hsep(name(C), text("="))), decls(Ds));
text(atom_to_list(Mod));
(_) -> empty() end,
block(follow( hsep(lists:map(Mod, Attrs) ++ [text("contract")])
, 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));
decl({pragma, _, Pragma}) -> pragma(Pragma); decl({pragma, _, Pragma}) -> pragma(Pragma);
@@ -159,16 +155,13 @@ decl({type_def, _, T, Vars, Def}) ->
Kind = element(1, Def), Kind = element(1, Def),
equals(typedecl(Kind, T, Vars), typedef(Def)); equals(typedecl(Kind, T, Vars), typedef(Def));
decl({fun_decl, Ann, F, T}) -> decl({fun_decl, Ann, F, T}) ->
Mod = fun({Mod, true}) when Mod == private; Mod == stateful; Mod == payable ->
text(atom_to_list(Mod));
(_) -> empty() end,
Fun = case aeso_syntax:get_ann(entrypoint, Ann, false) of Fun = case aeso_syntax:get_ann(entrypoint, Ann, false) of
true -> text("entrypoint"); true -> text("entrypoint");
false -> text("function") false -> text("function")
end, end,
hsep(lists:map(Mod, Ann) ++ [Fun, typed(name(F), T)]); hsep(Fun, typed(name(F), T));
decl(D = {letfun, Attrs, _, _, _, _}) -> decl(D = {letfun, Attrs, _, _, _, _}) ->
Mod = fun({Mod, true}) when Mod == private; Mod == stateful; Mod == payable -> Mod = fun({Mod, true}) when Mod == private; Mod == stateful ->
text(atom_to_list(Mod)); text(atom_to_list(Mod));
(_) -> empty() end, (_) -> empty() end,
Fun = case aeso_syntax:get_ann(entrypoint, Attrs, false) of Fun = case aeso_syntax:get_ann(entrypoint, Attrs, false) of
@@ -264,8 +257,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));
@@ -292,9 +283,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, {named_arg, _, Name, E}) -> arg_expr({named_arg, _, Name, E}) ->
paren(P > 100, follow(hsep(expr(Name), text("=")), expr(E))); follow(hsep(expr(Name), text("=")), expr(E));
arg_expr(E) -> 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}) ->
@@ -370,19 +364,13 @@ expr_p(_, {Type, _, Bin})
Type == oracle_query_id -> Type == oracle_query_id ->
text(binary_to_list(aeser_api_encoder:encode(Type, Bin))); text(binary_to_list(aeser_api_encoder:encode(Type, Bin)));
expr_p(_, {string, _, <<>>}) -> text("\"\""); expr_p(_, {string, _, <<>>}) -> text("\"\"");
expr_p(_, {string, _, S}) -> expr_p(_, {string, _, S}) -> term(binary_to_list(S));
text(io_lib:format("\"~s\"", [binary_to_list(S)]));
expr_p(_, {char, _, C}) -> 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);
@@ -454,7 +442,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));
@@ -498,3 +486,6 @@ get_elifs(If = {'if', Ann, Cond, Then, Else}, Elifs) ->
end; end;
get_elifs(Else, Elifs) -> {lists:reverse(Elifs), {else, Else}}. get_elifs(Else, Elifs) -> {lists:reverse(Elifs), {else, Else}}.
fmt(Fmt, Args) -> text(lists:flatten(io_lib:format(Fmt, Args))).
term(X) -> fmt("~p", [X]).
+30 -36
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())},
@@ -83,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);
@@ -121,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).
+6 -12
View File
@@ -37,26 +37,20 @@
-type decl() :: {contract, ann(), con(), [decl()]} -type decl() :: {contract, 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()]}
| {type_def, ann(), id(), [tvar()], typedef()} | {type_def, ann(), id(), [tvar()], typedef()}
| {fun_clauses, ann(), id(), type(), [letfun() | fundecl()]} | {fun_decl, ann(), id(), type()}
| {fun_clauses, ann(), id(), type(), [letbind()]}
| {block, ann(), [decl()]} | {block, ann(), [decl()]}
| fundecl() | letbind().
| letfun()
| letval(). % Only for error msgs
-type compiler_version() :: [non_neg_integer()]. -type compiler_version() :: [non_neg_integer()].
-type pragma() :: {compiler, '==' | '<' | '>' | '=<' | '>=', compiler_version()}. -type pragma() :: {compiler, '==' | '<' | '>' | '=<' | '>=', compiler_version()}.
-type letval() :: {letval, ann(), pat(), expr()}.
-type letfun() :: {letfun, ann(), id(), [pat()], type(), expr()}.
-type fundecl() :: {fun_decl, ann(), id(), type()}.
-type letbind() -type letbind()
:: letfun() :: {letval, ann(), pat(), expr()}
| letval(). | {letfun, ann(), id(), [pat()], type(), expr()}.
-type arg() :: {arg, ann(), id(), type()}. -type arg() :: {arg, ann(), id(), type()}.
+1
View File
@@ -45,6 +45,7 @@ fold(Alg = #alg{zero = Zero, plus = Plus, scoped = Scoped}, Fun, K, X) ->
%% decl() %% decl()
{contract, _, _, Ds} -> Decl(Ds); {contract, _, _, Ds} -> Decl(Ds);
{namespace, _, _, Ds} -> Decl(Ds); {namespace, _, _, Ds} -> Decl(Ds);
{type_decl, _, I, _} -> BindType(I);
{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));
-2
View File
@@ -95,8 +95,6 @@ from_fate({tuple_t, _, Types}, ?FATE_TUPLE(Val))
when length(Types) == tuple_size(Val) -> when length(Types) == tuple_size(Val) ->
{tuple, [], [from_fate(Type, X) {tuple, [], [from_fate(Type, X)
|| {Type, X} <- lists:zip(Types, tuple_to_list(Val))]}; || {Type, X} <- lists:zip(Types, tuple_to_list(Val))]};
from_fate({record_t, [{field_t, _, FName, FType}]}, Val) ->
{record, [], [{field, [], [{proj, [], FName}], from_fate(FType, Val)}]};
from_fate({record_t, Fields}, ?FATE_TUPLE(Val)) from_fate({record_t, Fields}, ?FATE_TUPLE(Val))
when length(Fields) == tuple_size(Val) -> when length(Fields) == tuple_size(Val) ->
{record, [], [ {field, [], [{proj, [], FName}], from_fate(FType, X)} {record, [], [ {field, [], [{proj, [], FName}], from_fate(FType, X)}
+1 -1
View File
@@ -1,6 +1,6 @@
{application, aesophia, {application, aesophia,
[{description, "Contract Language for aeternity"}, [{description, "Contract Language for aeternity"},
{vsn, "5.0.0"}, {vsn, "4.2.0"},
{registered, []}, {registered, []},
{applications, {applications,
[kernel, [kernel,
+4 -11
View File
@@ -11,10 +11,7 @@ test_contract(N) ->
{Contract,MapACI,DecACI} = test_cases(N), {Contract,MapACI,DecACI} = test_cases(N),
{ok,JSON} = aeso_aci:contract_interface(json, Contract), {ok,JSON} = aeso_aci:contract_interface(json, Contract),
?assertEqual([MapACI], JSON), ?assertEqual([MapACI], JSON),
?assertEqual({ok, DecACI}, aeso_aci:render_aci_json(JSON)), ?assertEqual({ok, DecACI}, aeso_aci:render_aci_json(JSON)).
%% Check if the compiler provides correct aci
{ok,#{aci := JSON2}} = aeso_compiler:from_string(Contract, [{aci, json}]),
?assertEqual(JSON, JSON2).
test_cases(1) -> test_cases(1) ->
Contract = <<"payable contract C =\n" Contract = <<"payable contract C =\n"
@@ -87,7 +84,7 @@ test_cases(3) ->
" entrypoint a : (C.bert(string)) => int\n">>, " entrypoint a : (C.bert(string)) => int\n">>,
{Contract,MapACI,DecACI}. {Contract,MapACI,DecACI}.
%% Roundtrip %% Rounttrip
aci_test_() -> aci_test_() ->
[{"Testing ACI generation for " ++ ContractName, [{"Testing ACI generation for " ++ ContractName,
fun() -> aci_test_contract(ContractName) end} fun() -> aci_test_contract(ContractName) end}
@@ -97,13 +94,8 @@ all_contracts() -> aeso_compiler_tests:compilable_contracts().
aci_test_contract(Name) -> aci_test_contract(Name) ->
String = aeso_test_utils:read_contract(Name), String = aeso_test_utils:read_contract(Name),
Opts = case lists:member(Name, aeso_compiler_tests:debug_mode_contracts()) of Opts = [{include, {file_system, [aeso_test_utils:contract_path()]}}],
true -> [debug_mode];
false -> []
end ++ [{include, {file_system, [aeso_test_utils:contract_path()]}}],
{ok, JSON} = aeso_aci:contract_interface(json, String, Opts), {ok, JSON} = aeso_aci:contract_interface(json, String, Opts),
{ok, #{aci := JSON1}} = aeso_compiler:from_string(String, [{aci, json}, {backend, fate} | Opts]),
?assertEqual(JSON, JSON1),
io:format("JSON:\n~p\n", [JSON]), io:format("JSON:\n~p\n", [JSON]),
{ok, ContractStub} = aeso_aci:render_aci_json(JSON), {ok, ContractStub} = aeso_aci:render_aci_json(JSON),
@@ -130,3 +122,4 @@ check_stub(Stub, Options) ->
_ = [ io:format("~s\n", [aeso_errors:pp(E)]) || E <- Errs ], _ = [ io:format("~s\n", [aeso_errors:pp(E)]) || E <- Errs ],
error({parse_errors, Errs}) error({parse_errors, Errs})
end. end.
+1 -2
View File
@@ -113,7 +113,6 @@ compilable_contracts() ->
{"funargs", "traffic_light", ["Pantone(12)"]}, {"funargs", "traffic_light", ["Pantone(12)"]},
{"funargs", "tuples", ["()"]}, {"funargs", "tuples", ["()"]},
%% TODO {"funargs", "due", ["FixedTTL(1020)"]}, %% TODO {"funargs", "due", ["FixedTTL(1020)"]},
{"funargs", "singleton_rec", ["{x = 1000}"]},
{"variant_types", "init", []}, {"variant_types", "init", []},
{"basic_auth", "init", []}, {"basic_auth", "init", []},
{"address_literals", "init", []}, {"address_literals", "init", []},
@@ -143,4 +142,4 @@ compilable_contracts() ->
not_yet_compilable(fate) -> not_yet_compilable(fate) ->
[]; [];
not_yet_compilable(aevm) -> not_yet_compilable(aevm) ->
["funargs", "strings"]. [].
+15 -108
View File
@@ -39,7 +39,7 @@ simple_compile_test_() ->
error(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_yet_compilable(Backend))] ++
[ {"Test file not found error", [ {"Test file not found error",
fun() -> fun() ->
{error, Errors} = aeso_compiler:file("does_not_exist.aes"), {error, Errors} = aeso_compiler:file("does_not_exist.aes"),
@@ -110,15 +110,7 @@ compile(Backend, Name) ->
compile(Backend, Name, Options) -> compile(Backend, Name, Options) ->
String = aeso_test_utils:read_contract(Name), String = aeso_test_utils:read_contract(Name),
Options1 = case aeso_compiler:from_string(String, [{src_file, Name ++ ".aes"}, {backend, Backend} | Options]) of
case lists:member(Name, debug_mode_contracts()) of
true -> [debug_mode];
false -> []
end ++
[ {src_file, Name ++ ".aes"}, {backend, Backend}
, {include, {file_system, [aeso_test_utils:contract_path()]}}
] ++ Options,
case aeso_compiler:from_string(String, Options1) of
{ok, Map} -> Map; {ok, Map} -> Map;
{error, ErrorString} when is_binary(ErrorString) -> ErrorString; {error, ErrorString} when is_binary(ErrorString) -> ErrorString;
{error, Errors} -> Errors {error, Errors} -> Errors
@@ -154,7 +146,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",
@@ -163,7 +154,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",
@@ -173,26 +163,13 @@ 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",
"protected_call",
"hermetization_turnoff"
]. ].
not_compilable_on(fate) -> []; not_yet_compilable(fate) -> [];
not_compilable_on(aevm) -> not_yet_compilable(aevm) -> [].
[ "stdlib_include", "manual_stdlib_include", "pairing_crypto"
, "aens_update", "basic_auth_tx", "more_strings"
, "unapplied_builtins", "bytes_to_x", "state_handling", "protected_call"
, "hermetization_turnoff"
].
debug_mode_contracts() ->
["hermetization_turnoff"].
%% Contracts that should produce type errors %% Contracts that should produce type errors
@@ -394,19 +371,15 @@ 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(bad_include_and_ns,
[<<?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.">>,
, ?TYPE_ERROR(not_toplevel_namespace, <<?Pos(3, 13)
[<<?Pos(2, 13) "Nested namespace not allowed\nNamespace 'Foo' at line 3, column 13 not defined at top level.">>])
"Nested namespaces are not allowed\nNamespace 'Foo' at line 2, column 13 not defined at top level.">>])
, ?TYPE_ERROR(not_toplevel_contract,
[<<?Pos(2, 12)
"Nested contracts are not allowed\nContract 'Con' at line 2, column 12 not defined at top level.">>])
, ?TYPE_ERROR(bad_address_literals, , ?TYPE_ERROR(bad_address_literals,
[<<?Pos(11, 5) [<<?Pos(11, 5)
"Cannot unify address\n" "Cannot unify address\n"
@@ -562,15 +535,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.">>])
@@ -643,44 +612,6 @@ failing_contracts() ->
[<<?Pos(5, 28) [<<?Pos(5, 28)
"Invalid call to contract entrypoint 'Foo.foo'.\n" "Invalid call to contract entrypoint 'Foo.foo'.\n"
"It must be called as 'c.foo' for some c : Foo.">>]) "It must be called as 'c.foo' for some c : Foo.">>])
, ?TYPE_ERROR(toplevel_let,
[<<?Pos(2, 7)
"Toplevel \"let\" definitions are not supported\n"
"Value this_is_illegal at line 2, column 7 could be replaced by 0-argument function">>])
, ?TYPE_ERROR(empty_typedecl,
[<<?Pos(2, 8)
"Empty type declarations are not supported\n"
"Type t at line 2, column 8 lacks a definition">>])
, ?TYPE_ERROR(higher_kinded_type,
[<<?Pos(2, 35)
"Type 'm is a higher kinded type variable\n"
"(takes another type as an argument)">>])
, ?TYPE_ERROR(bad_arity,
[<<?Pos(3, 20)
"Arity for id doesn't match. Expected 1, got 0">>,
<<?Pos(3, 25)
"Cannot unify int\n"
" and id\n"
"when checking the type of the expression at line 3, column 25\n"
" 123 : int\n"
"against the expected type\n"
" id">>,
<<?Pos(4, 20)
"Arity for id doesn't match. Expected 1, got 2">>,
<<?Pos(4, 35)
"Cannot unify int\n"
" and id(int, int)\n"
"when checking the type of the expression at line 4, column 35\n"
" 123 : int\n"
"against the expected type\n"
" id(int, int)">>])
, ?TYPE_ERROR(bad_unnamed_map_update_default,
[<<?Pos(4, 17)
"Invalid map update with default">>])
, ?TYPE_ERROR(non_functional_entrypoint,
[<<?Pos(2, 14)
"f at line 2, column 14 was declared with an invalid type int.\n"
"Entrypoints and functions must have functional types">>])
, ?TYPE_ERROR(bad_records, , ?TYPE_ERROR(bad_records,
[<<?Pos(3, 16) [<<?Pos(3, 16)
"Mixed record fields and map keys in\n" "Mixed record fields and map keys in\n"
@@ -692,22 +623,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,
[<<?Pos(4, 5)
"Mismatch in the function block. Expected implementation/type declaration of g function">>,
<<?Pos(5, 5)
"Mismatch in the function block. Expected implementation/type declaration of g function">>
])
, ?TYPE_ERROR(just_an_empty_file,
[<<?Pos(0, 0)
"Empty contract">>
])
, ?TYPE_ERROR(bad_number_of_args, , ?TYPE_ERROR(bad_number_of_args,
[<<?Pos(3, 39) [<<?Pos(3, 39)
"Cannot unify () => unit\n" "Cannot unify () => unit\n"
@@ -730,9 +645,6 @@ failing_contracts() ->
" g : (int, string) => 'c\nto arguments\n" " g : (int, string) => 'c\nto arguments\n"
" \"Litwo, ojczyzno moja\" : string">> " \"Litwo, ojczyzno moja\" : string">>
]) ])
, ?TYPE_ERROR(bad_state,
[<<?Pos(4, 16)
"Conflicting updates for field 'foo'">>])
]. ].
-define(Path(File), "code_errors/" ??File). -define(Path(File), "code_errors/" ??File).
@@ -886,11 +798,6 @@ validate(Contract1, Contract2) ->
ByteCode = #{ fate_code := FCode } = compile(fate, Contract1), ByteCode = #{ fate_code := FCode } = compile(fate, Contract1),
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
true -> [debug_mode];
false -> []
end ++
[{backend, fate}, {include, {file_system, [aeso_test_utils:contract_path()]}}]). [{backend, fate}, {include, {file_system, [aeso_test_utils:contract_path()]}}]).
+2 -3
View File
@@ -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)).
+2 -1
View File
@@ -58,7 +58,8 @@ contract Greeter =
let state = { greeting = "Hello" } let state = { greeting = "Hello" }
function setGreeting(greeting: string) = let setGreeting =
(greeting: string) =>
state{ greeting = greeting } state{ greeting = greeting }
+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)
+29 -67
View File
@@ -1,82 +1,44 @@
// Try to cover all syntactic constructs. // Try to cover all syntactic constructs.
@compiler > 0
@compiler =< 10.1.1.1.1.1.2.3.4
contract AllSyntaxType =
type typeDecl /* bla */
type paramTypeDecl('a, 'b)
namespace Ns =
datatype d('a) = D | S(int) | M('a, list('a), int)
private function fff() = 123
stateful entrypoint
f (1, x) = (_) => x
payable contract AllSyntaxType =
/** Multi- /** Multi-
* line * line
* comment * comment
*/ */
stateful function foo : _ function foo : _
entrypoint bar : int => (int * 'a)
contract AllSyntax = contract AllSyntax =
datatype mickiewicz = Adam | Mickiewicz type typeDecl = int
record goethe('a, 'b) = { type paramTypeDecl('a, 'b) = (('a, 'b) => 'b) => list('a) => 'b => 'b
johann : int,
wolfgang : 'a,
von : 'a * 'b * int,
goethe : unit
}
type dante = Ns.d(int)
type shakespeare('a) = goethe('a, 'a)
type state = shakespeare(int) record nestedRecord = { x : int }
record recordType = { z : nestedRecord, y : int }
datatype variantType('a) = None | Some('a)
entrypoint init() = { let valWithType : map(int, int) => option(int) = (m) => Map.get(m, 42)
johann = 1000, let valNoType =
wolfgang = -10, if(valWithType(Map.empty) == None)
print(42 mod 10 * 5 / 3)
/* TODO: This does not compile because of bug in the parser tester. function funWithType(x : int, y) : int * list(int) = (x, 0 :: [y] ++ [])
von = (2 + 2, 0, List.sum([x | k <- [1,2,3] function funNoType() =
, let l = k + 1 let foo = (x, y : bool) =>
, if(l < 10) if (! (y && x =< 0x0b || true)) [x]
, let f(x) = x + 100 else [11..20]
, Adam <- [Adam, Mickiewicz] let setY(r : recordType) : unit = r{ y = 5 }
, let x = f(l) let setX(r : recordType, x : int) : recordType = r { z.x = x } // nested record update
])), let getY(r) = switch(r) {y = y} => y
*/ switch (funWithType(1, -2))
von = (2 + 2, 0, List.sum([1,2,3,4])), (x, [y, z]) => bar({x = z, y = -y + - -z * (-1)})
goethe = () } (x, y :: _) => ()
function f() = let hash : address = #01ab0fff11
let kp = "nietzsche" let b = false
// let p = "Пушкин" // TODO: this also doesn't do right round_trip... let qcon = Mod.Con
let k(x : bytes(8)) : bytes(8) = Bytes.to_int(#fedcba9876543210) let str = "blabla\nfoo"
let chr = '"'
let f : () => address = () => ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt
if(Bits.test(Bits.all, 10))
abort("ohno")
if(true && false)
require(true, "ohyes")
elif(false || 2 == 2)
()
else
()
if(true) f(1,2)((1,2))
else switch(1::[1,2,3])
[] => 1
a::b => 123
1::2::3 => 123123
[2,3,4] => 1
_ => 13
1::[2] => 2138
put(state{johann = 1})
let m = {["foo"] = 19, /*hey wanna talk about inlined comments?*/ ["bar"] = 42}
let n = {}
m{ ["x" = 0] @ z = z + state.johann }
let sh : shakespeare(shakespeare(int)) =
{wolfgang = state}
sh{wolfgang.wolfgang = sh.wolfgang} // comment
-4
View File
@@ -1,4 +0,0 @@
contract C =
type id('a) = 'a
entrypoint f() : id = 123
entrypoint g() : id(int, int) = 123
-5
View File
@@ -1,5 +0,0 @@
contract C =
function
g(1) = 2
f(2) = 3
h(1) = 123
@@ -1,4 +1,5 @@
contract BadCon = contract Bad =
include "included.aes"
namespace Foo = namespace Foo =
function foo() = 42 function foo() = 42
-6
View File
@@ -1,6 +0,0 @@
contract Remote =
entrypoint id : int => int
contract ProtectedCall =
entrypoint bad(r : Remote) =
r.id(protected = 0 == 1, 18)
-5
View File
@@ -1,5 +0,0 @@
contract C =
record state = { foo : int }
entrypoint init(i : int) =
state{ foo = i,
foo = 42 }
@@ -1,5 +0,0 @@
contract C =
entrypoint f() =
let z = 123
{}{ [1 = 0] = z + 1 }
2
-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)
@@ -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)
-3
View File
@@ -1,3 +0,0 @@
contract C =
type t
entrypoint f() = 123
+1 -6
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) =
@@ -45,8 +45,3 @@ contract FunctionArguments =
entrypoint due(t : Chain.ttl) = entrypoint due(t : Chain.ttl) =
true true
record singleton_r = { x : int }
entrypoint singleton_rec(r : singleton_r) =
r.x
-11
View File
@@ -1,11 +0,0 @@
namespace M =
function mf() = mg()
function mg() = mf()
namespace N =
function nf() = ng() + M.mf() + M.mg()
private function ng() = nf() + M.mf() + M.mg()
contract C =
entrypoint f() = N.ng() + N.nf() + g()
function g() = N.ng() + N.nf() + f()
-3
View File
@@ -1,3 +0,0 @@
contract IWantToBelieve =
type stateT('s, 'm, 'a) = 's => 'm('a * 's)
entrypoint s() = 123
-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)
+2 -2
View File
@@ -15,7 +15,7 @@ contract MultiSig =
| OwnerRemoved (address) // of { .removedOwner : Address } | OwnerRemoved (address) // of { .removedOwner : Address }
| ReqChanged (int) // of { .newReq : int } | ReqChanged (int) // of { .newReq : int }
function maxOwners() : int = 250 let maxOwners : int = 250
record state = { nRequired : int record state = { nRequired : int
, nOwners : int , nOwners : int
@@ -68,7 +68,7 @@ contract MultiSig =
switch(check_pending(callhash())) switch(check_pending(callhash()))
CheckFail(state') => { state = state' } CheckFail(state') => { state = state' }
CheckOk(state') => CheckOk(state') =>
if(state.nOwners >= maxOwners()) () /* TODO */ if(state.nOwners >= maxOwners) () /* TODO */
else else
let nOwners' = state'.nOwners + 1 let nOwners' = state'.nOwners + 1
{ state = state' { owners = Map.insert(nOwners', newOwner, state'.owners) { state = state' { owners = Map.insert(nOwners', newOwner, state'.owners)
@@ -1,5 +0,0 @@
contract C1 =
entrypoint f : int
contract C =
entrypoint f() = 123
-6
View File
@@ -1,6 +0,0 @@
namespace BadNs =
contract Con =
entrypoint e : () => int
contract Con =
entrypoint foo() = 43
-5
View File
@@ -1,5 +0,0 @@
namespace BadNs =
include "included.aes"
contract Con =
entrypoint foo() = 43
-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)
-14
View File
@@ -1,14 +0,0 @@
contract Remote =
entrypoint id : int => int
contract ProtectedCall =
function higher_order(r : Remote) =
r.id
entrypoint test_ok(r : Remote) =
let f = higher_order(r)
let Some(n) = r.id(protected = true, 10)
let Some(m) = f(protected = true, 5)
n + m + r.id(protected = false, 100) + f(1)
-1
View File
@@ -1,4 +1,3 @@
include "String.aes"
contract Remote = contract Remote =
record rstate = { i : int, s : string, m : map(int, int) } record rstate = { i : int, s : string, m : map(int, int) }
-1
View File
@@ -1,4 +1,3 @@
include "String.aes"
contract Strings = contract Strings =
entrypoint str_len(s) = String.length(s) entrypoint str_len(s) = String.length(s)
entrypoint str_concat(s1, s2) = String.concat(s1, s2) entrypoint str_concat(s1, s2) = String.concat(s1, s2)
-3
View File
@@ -1,3 +0,0 @@
contract C =
let this_is_illegal = 2/0
entrypoint this_is_legal() = 2/0
-2
View File
@@ -7,7 +7,6 @@
// AENS.transfer // AENS.transfer
// AENS.revoke // AENS.revoke
// Oracle.extend // Oracle.extend
include "String.aes"
contract UnappliedBuiltins = contract UnappliedBuiltins =
entrypoint main() = () entrypoint main() = ()
type o = oracle(int, int) type o = oracle(int, int)
@@ -22,7 +21,6 @@ contract UnappliedBuiltins =
function b_abort() = abort function b_abort() = abort
function b_require() = require function b_require() = require
function oracle_query_fee() = Oracle.query_fee function oracle_query_fee() = Oracle.query_fee
function oracle_expiry() = Oracle.expiry
stateful function oracle_query() = Oracle.query : (o, _, _, _, _) => _ stateful function oracle_query() = Oracle.query : (o, _, _, _, _) => _
function oracle_get_question() = Oracle.get_question : (o, _) => _ function oracle_get_question() = Oracle.get_question : (o, _) => _
function oracle_get_answer() = Oracle.get_answer : (o, _) => _ function oracle_get_answer() = Oracle.get_answer : (o, _) => _
-21
View File
@@ -1,21 +0,0 @@
contract UTF8 =
entrypoint f1() : char = '1'
entrypoint f2() : char = '+'
entrypoint f3() : char = 'd'
entrypoint f4() : char = 'X'
entrypoint f5() : char = 'å'
entrypoint f6() : char = 'Ä'
entrypoint f7() : char = 'æ'
entrypoint f8() : char = 'ë'
entrypoint f9() : char = 'ẻ'
entrypoint f10() : char = '\x27'
entrypoint f11() : char = '\x{2200}'
entrypoint f12() : char = '💩'
entrypoint f13() : char = '\n'
// entrypoint f13() : char = 'e̊'
// entrypoint f14() : char = '\Ì'
// '💩' vs. map('a,'b)
+13
View File
@@ -1,3 +1,16 @@
/* Contract type */
contract VotingType =
type state
function init : list(string) => state
function giveRightToVote : address => unit
function delegate : address => unit
function vote : int => unit
function winnerName : unit => string
function currentTally : unit => list(string * int)
/* Contract implementation */
contract Voting = contract Voting =
// Types // Types