Compare commits
201 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d16fb82e25 | |||
| d2cd97def7 | |||
| 5455d0fcd7 | |||
| 2d3e6ab6e0 | |||
| 70a0f77793 | |||
| 04b3227317 | |||
| d9be8b2fca | |||
| a38afe7693 | |||
| 5719730d8c | |||
| 51b63f9559 | |||
| 5e6af18c7b | |||
| 4324bfd49e | |||
| faa0ef9772 | |||
| f07954f62c | |||
| ef761a4c57 | |||
| 330d8929fd | |||
| 491b1211d1 | |||
| e460b84bd0 | |||
| 9109712826 | |||
| d6a55e144e | |||
| db64978d2e | |||
| 2ed9d17ce5 | |||
| 7bf7cb0b8f | |||
| 4a01c852c9 | |||
| df00c3958b | |||
| 12cb37245b | |||
| 562ad5ee87 | |||
| 4e78756b90 | |||
| 9f32fb1925 | |||
| 549a0c2201 | |||
| 9f5f8d4444 | |||
| fd0dbdf207 | |||
| 0d8b7c7c79 | |||
| 3271d6fba4 | |||
| 30fbcc50c5 | |||
| 27bc5474cb | |||
| efeb391805 | |||
| 15ca37342c | |||
| 8b7e4db490 | |||
| d89fd134b5 | |||
| 37dfbf78ac | |||
| 188916c61f | |||
| f395649419 | |||
| 36395b597a | |||
| 177e32c117 | |||
| 4d61ee65df | |||
| a089af555f | |||
| cd116b23d7 | |||
| fba6609c3a | |||
| e7c477d4de | |||
| a44b787735 | |||
| cf46c9e303 | |||
| b6a789bbbc | |||
| ad34363673 | |||
| 257de08100 | |||
| 7ae4a98360 | |||
| 5e6e607fa4 | |||
| a69056c35e | |||
| 8cfa611b20 | |||
| e9bdd59def | |||
| 0da7376d11 | |||
| af4f2ad795 | |||
| abae4a7602 | |||
| 1cfd4c6f24 | |||
| f07a49c91d | |||
| e33e4cf2cd | |||
| 89971fb275 | |||
| 85a014958d | |||
| cdc7b901e6 | |||
| f266c5eed8 | |||
| 4d9d3077ad | |||
| 3efde2a2a1 | |||
| edc37bcf1b | |||
| 20f2a05638 | |||
| c2a5ed28cf | |||
| 56f70fea6c | |||
| 9e908369ec | |||
| 9984679a24 | |||
| 8f27168908 | |||
| 8619f47ee6 | |||
| 0d56130baa | |||
| 7448da16bb | |||
| 6f582af83e | |||
| 931f2d3dcb | |||
| 5d116b2e5a | |||
| cea581988d | |||
| 2f36380a81 | |||
| d330133b3f | |||
| 6fccc902d0 | |||
| eb77a73d15 | |||
| 62aa06cc3a | |||
| 6d6fff2612 | |||
| fadf3378b4 | |||
| 95bf0d4b6c | |||
| e94c1f9d84 | |||
| a263b09e57 | |||
| 5a3c8530b4 | |||
| 4c79f7b9f2 | |||
| e0fff00e64 | |||
| 7c95aafbb8 | |||
| 5a4a84805f | |||
| cc3e322179 | |||
| 54edba3164 | |||
| f16d699f6d | |||
| 7b474e439c | |||
| fc64ca572d | |||
| eb926b1352 | |||
| dc4a2ca2f9 | |||
| f866e24624 | |||
| ccad660eac | |||
| 53b85ce6f4 | |||
| a7af62c089 | |||
| abc70ba288 | |||
| 6342cd6a08 | |||
| 123d1d2fa2 | |||
| b7b54b38a8 | |||
| ae3f292f03 | |||
| bcdf311096 | |||
| 202a06a580 | |||
| b5b0d30fc4 | |||
| 236ef6eb89 | |||
| aa6d56ce9b | |||
| 0b86cdc318 | |||
| 27cbedc7ab | |||
| 2ac47059c1 | |||
| 421bc01012 | |||
| 2b7490776e | |||
| 0a5b80668f | |||
| 6cdba58e35 | |||
| 8262d7780f | |||
| e6f01481bf | |||
| d9188d58a7 | |||
| dfa286d43c | |||
| 478da2af33 | |||
| 10be09fe30 | |||
| e6c9d0fac1 | |||
| 367f87b612 | |||
| 026ff52528 | |||
| 3ba89d9f55 | |||
| 0b4c2f14fe | |||
| b65c4edd19 | |||
| 515f444e7c | |||
| 267fef3a5b | |||
| 362373c0d7 | |||
| 65b6791176 | |||
| 87e5562f74 | |||
| d3fa04483e | |||
| b8cb7ab1b5 | |||
| ceb7de2119 | |||
| 2edafe0adc | |||
| 3a7c8f905a | |||
| 9026e1fe6b | |||
| f1f2b09294 | |||
| 53299b9b17 | |||
| 64fd91197b | |||
| a73abf8e8e | |||
| db1c0fa05a | |||
| 70ad303e16 | |||
| fe1a2758c3 | |||
| 79de25b3a5 | |||
| 3e1290efaf | |||
| 9c77622c7c | |||
| a367d5040a | |||
| d8bf0bda45 | |||
| 922107e438 | |||
| f133483a90 | |||
| be42ee08ab | |||
| 86285a8ae0 | |||
| c5c73097fc | |||
| 23ccce4c22 | |||
| 387fdf5c34 | |||
| f0dcda27bc | |||
| 5fd24fec86 | |||
| 4bedbfee61 | |||
| e4c46ee16f | |||
| d8fff8f20f | |||
| b074aa3323 | |||
| 90e45e63d4 | |||
| 0d78a5e4a0 | |||
| b61e3270f9 | |||
| 7503ef2f3c | |||
| 028334ecb6 | |||
| 783d74dff1 | |||
| 335cd4743a | |||
| 77212b5eb3 | |||
| 79307c34df | |||
| 9187659a1e | |||
| dbf2aa45c3 | |||
| 73a4839d2f | |||
| 5924197b90 | |||
| 50179d2918 | |||
| 05b80a5517 | |||
| 7849fe302c | |||
| 5786b6d813 | |||
| ad028ddf98 | |||
| b8f4a6b67a | |||
| 4853d386e4 | |||
| 66b1d2e98a | |||
| 4b1e11a0ef | |||
| d4d02fd576 | |||
| 3ceb8c38db |
@@ -0,0 +1,37 @@
|
||||
version: 2.1
|
||||
|
||||
executors:
|
||||
aebuilder:
|
||||
docker:
|
||||
- image: aeternity/builder
|
||||
user: builder
|
||||
working_directory: ~/aesophia
|
||||
|
||||
jobs:
|
||||
build:
|
||||
executor: aebuilder
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- dialyzer-cache-v2-{{ .Branch }}-{{ .Revision }}
|
||||
- dialyzer-cache-v2-{{ .Branch }}-
|
||||
- dialyzer-cache-v2-
|
||||
- run:
|
||||
name: Build
|
||||
command: ./rebar3 compile
|
||||
- run:
|
||||
name: Static Analysis
|
||||
command: ./rebar3 dialyzer
|
||||
- run:
|
||||
name: Eunit
|
||||
command: ./rebar3 eunit
|
||||
- run:
|
||||
name: Common Tests
|
||||
command: ./rebar3 ct
|
||||
- save_cache:
|
||||
key: dialyzer-cache-v2-{{ .Branch }}-{{ .Revision }}
|
||||
paths:
|
||||
- _build/default/rebar3_20.3.8_plt
|
||||
- store_artifacts:
|
||||
path: _build/test/logs
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
.rebar3
|
||||
_*
|
||||
.eunit
|
||||
*.o
|
||||
*.beam
|
||||
*.plt
|
||||
*.swp
|
||||
*.swo
|
||||
.erlang.cookie
|
||||
ebin
|
||||
log
|
||||
erl_crash.dump
|
||||
.rebar
|
||||
logs
|
||||
_build
|
||||
.idea
|
||||
*.iml
|
||||
rebar3.crashdump
|
||||
*.erl~
|
||||
*.aes~
|
||||
aesophia
|
||||
@@ -0,0 +1,40 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
### Changed
|
||||
### Removed
|
||||
|
||||
## [2.1.0] - 2019-04-11
|
||||
### Added
|
||||
- Stubs (not yet wired up) for compilation to FATE
|
||||
- Add functions specific for Calldata decoding
|
||||
- Support for `Auth.tx_hash`, not available in AEVM until Fortuna release
|
||||
|
||||
### Changed
|
||||
- Improvements to the ACI generator
|
||||
|
||||
## [2.0.0] - 2019-03-11
|
||||
### Added
|
||||
- Add `Crypto.ecverify` to the compiler.
|
||||
- Add `Crypto.sha3`, `Crypto.blake2`, `Crypto.sha256`, `String.blake2` and
|
||||
`String.sha256` to the compiler.
|
||||
- Add the `bits` type for working with bit fields in Sophia.
|
||||
- Add Namespaces to Sophia in order to simplify using library contracts, etc.
|
||||
- Add a missig type check on the `init` function - detects programmer errors earlier.
|
||||
- Add the ACI (Aeternity Contract Interface) generator.
|
||||
|
||||
### Changed
|
||||
- Use native bit shift operations in builtin functions, reducing gas cost.
|
||||
- Improve type checking of `record` fields - generates more understandable error messages.
|
||||
- Improved, more coherent, error messages.
|
||||
- Simplify calldata creation - instead of passing a compiled contract, simply
|
||||
pass a (stubbed) contract string.
|
||||
|
||||
[Unreleased]: https://github.com/aeternity/aesophia/compare/v2.1.0...HEAD
|
||||
[2.1.0]: https://github.com/aeternity/aesophia/compare/v2.0.0...v2.1.0
|
||||
[2.0.0]: https://github.com/aeternity/aesophia/tag/v2.0.0
|
||||
@@ -0,0 +1,15 @@
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2017, aeternity developers
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
@@ -0,0 +1,25 @@
|
||||
# aesophia
|
||||
|
||||
This is the __sophia__ compiler for the æternity system which compiles contracts written in __sophia__ code to the æternity VM code.
|
||||
|
||||
For more information about æternity smart contracts and the sophia language see [Smart Contracts](https://github.com/aeternity/protocol/blob/master/contracts/contracts.md) and the [Sophia Language](https://github.com/aeternity/protocol/blob/master/contracts/sophia.md).
|
||||
|
||||
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.
|
||||
|
||||
## Versioning
|
||||
|
||||
`aesophia` has a version that is only loosely connected to the version of the
|
||||
Aeternity node - in principle they will share the major version but not
|
||||
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
|
||||
API changes etc.
|
||||
|
||||
## Interface Modules
|
||||
|
||||
The basic modules for interfacing the compiler:
|
||||
|
||||
* [aeso_compiler: the Sophia compiler](./docs/aeso_compiler.md)
|
||||
* [aeso_aci: the ACI interface](./docs/aeso_aci.md)
|
||||
@@ -0,0 +1,221 @@
|
||||
# aeso_aci
|
||||
|
||||
### Module
|
||||
|
||||
### aeso_aci
|
||||
|
||||
The ACI interface encoder and decoder.
|
||||
|
||||
### Description
|
||||
|
||||
This module provides an interface to generate and convert between
|
||||
Sophia contracts and a suitable JSON encoding of contract
|
||||
interface. As yet the interface is very basic.
|
||||
|
||||
Encoding this contract:
|
||||
|
||||
```
|
||||
contract Answers =
|
||||
record state = { a : answers }
|
||||
type answers() = map(string, int)
|
||||
|
||||
stateful function init() = { a = {} }
|
||||
private function the_answer() = 42
|
||||
function new_answer(q : string, a : int) : answers() = { [q] = a }
|
||||
```
|
||||
|
||||
generates the following JSON structure representing the contract interface:
|
||||
|
||||
|
||||
``` json
|
||||
{
|
||||
"contract": {
|
||||
"name": "Answers",
|
||||
"state": {
|
||||
"record": [
|
||||
{
|
||||
"name": "a",
|
||||
"type": {
|
||||
"map": {
|
||||
"key": "string",
|
||||
"value": "int"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"type_defs": [
|
||||
{
|
||||
"name": "answers",
|
||||
"vars": [],
|
||||
"typedef": {
|
||||
"map": {
|
||||
"key": "string",
|
||||
"value": "int"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"functions": [
|
||||
{
|
||||
"name": "init",
|
||||
"arguments": [],
|
||||
"returns": {
|
||||
"record": [
|
||||
{
|
||||
"name": "a",
|
||||
"type": {
|
||||
"map": {
|
||||
"key": "string",
|
||||
"value": "int"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"stateful": true
|
||||
},
|
||||
{
|
||||
"name": "new_answer",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "q",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "a",
|
||||
"type": "int"
|
||||
}
|
||||
],
|
||||
"returns": {
|
||||
"map": {
|
||||
"key": "string",
|
||||
"value": "int"
|
||||
}
|
||||
},
|
||||
"stateful": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When that encoding is decoded the following include definition is generated:
|
||||
|
||||
```
|
||||
contract Answers =
|
||||
function new_answer : (string, int) => map(string, int)
|
||||
```
|
||||
|
||||
### Types
|
||||
``` erlang
|
||||
contract_string() = string() | binary()
|
||||
json_string() = binary()
|
||||
```
|
||||
|
||||
### Exports
|
||||
|
||||
#### encode_contract(ContractString) -> {ok,JSONstring} | {error,ErrorString}
|
||||
|
||||
Types
|
||||
|
||||
``` erlang
|
||||
ConstractString = contract_string()
|
||||
JSONstring = json_string()
|
||||
```
|
||||
|
||||
This is equivalent to `aeso_aci:encode_contract(ConstractString, [])`.
|
||||
|
||||
#### encode_contract(ContractString, Options) -> {ok,JSONstring} | {error,ErrorString}
|
||||
|
||||
Types
|
||||
|
||||
``` erlang
|
||||
ConstractString = contract_string()
|
||||
Options = [option()]
|
||||
JSONstring = json_string()
|
||||
```
|
||||
|
||||
Generate the JSON encoding of the interface to a contract. The type definitions and non-private functions are included in the JSON string.
|
||||
|
||||
#### decode_contract(JSONstring) -> ConstractString.
|
||||
|
||||
Types
|
||||
|
||||
``` erlang
|
||||
ConstractString = contract_string()
|
||||
JSONstring = json_string()
|
||||
```
|
||||
|
||||
Take a JSON encoding of a contract interface and generate and generate a contract definition which can be included in another contract.
|
||||
|
||||
#### encode_type(TypeAST) -> JSONstring.
|
||||
|
||||
Types
|
||||
|
||||
``` erlang
|
||||
JSONstring = json_string()
|
||||
```
|
||||
|
||||
Generate the JSON encoding of a type from the AST of the type.
|
||||
|
||||
#### encode_arg(ArgAST) -> JSONstring.
|
||||
|
||||
Types
|
||||
|
||||
``` erlang
|
||||
JSONstring = json_string()
|
||||
```
|
||||
|
||||
Generate the JSON encoding of a function argument from the AST of the argument.
|
||||
|
||||
#### encode_stmt(StmtAST) -> JSONstring.
|
||||
|
||||
Types
|
||||
|
||||
``` erlang
|
||||
JSONstring = json_string()
|
||||
```
|
||||
|
||||
Generate the JSON encoding of a statement from the AST of the statement.
|
||||
|
||||
#### encode_expr(ExprAST) -> JSONstring.
|
||||
|
||||
Types
|
||||
|
||||
``` erlang
|
||||
JSONstring = json_string()
|
||||
```
|
||||
|
||||
Generate the JSON encoding of an expression from the AST of the expression.
|
||||
|
||||
### Notes
|
||||
|
||||
The deprecated functions `aseo_aci:encode/2` and `aeso_aci:decode/1` are still available but should not be used.
|
||||
|
||||
### Example run
|
||||
|
||||
This is an example of using the ACI generator from an Erlang shell. The file called `aci_test.aes` contains the contract in the description from which we want to generate files `aci_test.json` which is the JSON encoding of the contract interface and `aci_test.include` which is the contract definition to be included inside another contract.
|
||||
|
||||
``` erlang
|
||||
1> {ok,Contract} = file:read_file("aci_test.aes").
|
||||
{ok,<<"contract Answers =\n\n record state = { a : answers }\n type answers() = map(string, int)\n\n stateful functio"...>>}
|
||||
2> {ok,Encoding} = aeso_aci:encode_contract(Contract).
|
||||
{ok,<<"{\"contract\":{\"name\":\"Answers\",\"state\":{\"record\":[{\"name\":\"a\",\"type\":{\"map\":{\"key\":\"string\",\"value\":\"int\"}}}]"...>>}
|
||||
3> file:write_file("aci_test.aci", Encoding).
|
||||
ok
|
||||
4> Decoded = aeso_aci:decode_contract(Encoding).
|
||||
<<"contract Answers =\n function new_answer : (string, int) => map(string, int)\n">>
|
||||
5> file:write_file("aci_test.include", Decoded).
|
||||
ok
|
||||
6> jsx:prettify(Encoding).
|
||||
<<"{\n \"contract\": {\n \"name\": \"Answers\",\n \"state\": {\n \"record\": [\n {\n \"name\": \"a\",\n "...>>
|
||||
```
|
||||
|
||||
The final call to `jsx:prettify(Encoding)` returns the encoding in a
|
||||
more easily readable form. This is what is shown in the description
|
||||
above.
|
||||
|
||||
### Notes
|
||||
|
||||
The ACI generator currently cannot properly handle types defined using `datatype`.
|
||||
@@ -0,0 +1,86 @@
|
||||
# aeso_compiler
|
||||
|
||||
### Module
|
||||
|
||||
### aeso_compiler
|
||||
|
||||
The Sophia compiler
|
||||
|
||||
### Description
|
||||
|
||||
This module provides the interface to the standard Sophia compiler. It
|
||||
returns the compiled module in a map which can then be loaded.
|
||||
|
||||
### Types
|
||||
``` erlang
|
||||
contract_string() = string() | binary()
|
||||
contract_map() = #{bytecode => binary(),
|
||||
compiler_version => binary(),
|
||||
contract_souce => string(),
|
||||
type_info => type_info()}
|
||||
type_info()
|
||||
errorstring() = binary()
|
||||
```
|
||||
### Exports
|
||||
|
||||
#### file(File)
|
||||
#### file(File, Options) -> CompRet
|
||||
#### from_string(ContractString, Options) -> CompRet
|
||||
|
||||
Types
|
||||
|
||||
``` erlang
|
||||
ContractString = contract_string()
|
||||
Options = [Option]
|
||||
CompRet = {ok,ContractMap} | {error,ErrorString}
|
||||
ContractMap = contract_map()
|
||||
ErrorString = errorstring()
|
||||
```
|
||||
|
||||
Compile a contract defined in a file or in a string.
|
||||
|
||||
The **pp_** options all print to standard output the following:
|
||||
|
||||
`pp_sophia_code` - print the input Sophia code.
|
||||
|
||||
`pp_ast` - print the AST of the code
|
||||
|
||||
`pp_types` - print information about the types
|
||||
|
||||
`pp_typed_ast` - print the AST with type information at each node
|
||||
|
||||
`pp_icode` - print the internal code structure
|
||||
|
||||
`pp_assembler` - print the generated assembler code
|
||||
|
||||
`pp_bytecode` - print the bytecode instructions
|
||||
|
||||
#### check_call(ContractString, Options) -> CheckRet
|
||||
|
||||
Types
|
||||
```
|
||||
ContractString = string() | binary()
|
||||
CheckRet = {ok,string(),{Types,Type | any()},Terms} | {error,Term}
|
||||
Types = [Type]
|
||||
Type = term()
|
||||
```
|
||||
Check a call in contract through the `__call` function.
|
||||
|
||||
#### sophia_type_to_typerep(String) -> TypeRep
|
||||
|
||||
Types
|
||||
``` erlang
|
||||
{ok,TypeRep} | {error, badtype}
|
||||
```
|
||||
|
||||
Get the type representation of a type declaration.
|
||||
|
||||
#### version() -> {ok, Version} | {error, term()}
|
||||
|
||||
Types
|
||||
|
||||
``` erlang
|
||||
Version = binary()
|
||||
```
|
||||
|
||||
Get the current version of the Sophia compiler.
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Redirecting</title>
|
||||
<noscript>
|
||||
<meta http-equiv="refresh" content="1; url=latest/" />
|
||||
</noscript>
|
||||
<script>
|
||||
window.location.replace("latest/" + window.location.hash);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Redirecting to <a href="latest/">latest/</a>...
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,16 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Redirecting</title>
|
||||
<noscript>
|
||||
<meta http-equiv="refresh" content="1; url=../v7.4.0/404.html" />
|
||||
</noscript>
|
||||
<script>
|
||||
window.location.replace("../v7.4.0/404.html" + window.location.hash);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Redirecting to <a href="../v7.4.0/404.html">../v7.4.0/404.html</a>...
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,16 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Redirecting</title>
|
||||
<noscript>
|
||||
<meta http-equiv="refresh" content="1; url=../../v7.4.0/CHANGELOG/" />
|
||||
</noscript>
|
||||
<script>
|
||||
window.location.replace("../../v7.4.0/CHANGELOG/" + window.location.hash);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Redirecting to <a href="../../v7.4.0/CHANGELOG/">../../v7.4.0/CHANGELOG/</a>...
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,16 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Redirecting</title>
|
||||
<noscript>
|
||||
<meta http-equiv="refresh" content="1; url=../../v7.4.0/aeso_aci/" />
|
||||
</noscript>
|
||||
<script>
|
||||
window.location.replace("../../v7.4.0/aeso_aci/" + window.location.hash);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Redirecting to <a href="../../v7.4.0/aeso_aci/">../../v7.4.0/aeso_aci/</a>...
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,16 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Redirecting</title>
|
||||
<noscript>
|
||||
<meta http-equiv="refresh" content="1; url=../../v7.4.0/aeso_compiler/" />
|
||||
</noscript>
|
||||
<script>
|
||||
window.location.replace("../../v7.4.0/aeso_compiler/" + window.location.hash);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Redirecting to <a href="../../v7.4.0/aeso_compiler/">../../v7.4.0/aeso_compiler/</a>...
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,16 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Redirecting</title>
|
||||
<noscript>
|
||||
<meta http-equiv="refresh" content="1; url=../v7.4.0/" />
|
||||
</noscript>
|
||||
<script>
|
||||
window.location.replace("../v7.4.0/" + window.location.hash);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Redirecting to <a href="../v7.4.0/">../v7.4.0/</a>...
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,16 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Redirecting</title>
|
||||
<noscript>
|
||||
<meta http-equiv="refresh" content="1; url=../../v7.4.0/sophia/" />
|
||||
</noscript>
|
||||
<script>
|
||||
window.location.replace("../../v7.4.0/sophia/" + window.location.hash);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Redirecting to <a href="../../v7.4.0/sophia/">../../v7.4.0/sophia/</a>...
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,16 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Redirecting</title>
|
||||
<noscript>
|
||||
<meta http-equiv="refresh" content="1; url=../../v7.4.0/sophia_examples/" />
|
||||
</noscript>
|
||||
<script>
|
||||
window.location.replace("../../v7.4.0/sophia_examples/" + window.location.hash);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Redirecting to <a href="../../v7.4.0/sophia_examples/">../../v7.4.0/sophia_examples/</a>...
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,16 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Redirecting</title>
|
||||
<noscript>
|
||||
<meta http-equiv="refresh" content="1; url=../../v7.4.0/sophia_features/" />
|
||||
</noscript>
|
||||
<script>
|
||||
window.location.replace("../../v7.4.0/sophia_features/" + window.location.hash);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Redirecting to <a href="../../v7.4.0/sophia_features/">../../v7.4.0/sophia_features/</a>...
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,16 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Redirecting</title>
|
||||
<noscript>
|
||||
<meta http-equiv="refresh" content="1; url=../../v7.4.0/sophia_stdlib/" />
|
||||
</noscript>
|
||||
<script>
|
||||
window.location.replace("../../v7.4.0/sophia_stdlib/" + window.location.hash);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Redirecting to <a href="../../v7.4.0/sophia_stdlib/">../../v7.4.0/sophia_stdlib/</a>...
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,16 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Redirecting</title>
|
||||
<noscript>
|
||||
<meta http-equiv="refresh" content="1; url=../../v7.4.0/sophia_syntax/" />
|
||||
</noscript>
|
||||
<script>
|
||||
window.location.replace("../../v7.4.0/sophia_syntax/" + window.location.hash);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Redirecting to <a href="../../v7.4.0/sophia_syntax/">../../v7.4.0/sophia_syntax/</a>...
|
||||
</body>
|
||||
</html>
|
||||
-402
@@ -1,402 +0,0 @@
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en" class="no-js">
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="icon" href="/favicon.png">
|
||||
<meta name="generator" content="mkdocs-1.4.2, mkdocs-material-9.0.9">
|
||||
|
||||
|
||||
|
||||
<title>æternity Sophia Language</title>
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/assets/stylesheets/main.0d440cfe.min.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/assets/stylesheets/palette.2505c338.min.css">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
|
||||
<style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
|
||||
|
||||
|
||||
|
||||
<script>__md_scope=new URL("/",location),__md_hash=e=>[...e].reduce((e,_)=>(e<<5)-e+_.charCodeAt(0),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<body dir="ltr" data-md-color-scheme="default" data-md-color-primary="pink" data-md-color-accent="pink">
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&"object"==typeof palette.color)for(var key of Object.keys(palette.color))document.body.setAttribute("data-md-color-"+key,palette.color[key])</script>
|
||||
|
||||
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
|
||||
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
|
||||
<label class="md-overlay" for="__drawer"></label>
|
||||
<div data-md-component="skip">
|
||||
|
||||
</div>
|
||||
<div data-md-component="announce">
|
||||
|
||||
</div>
|
||||
|
||||
<div data-md-component="outdated" hidden>
|
||||
|
||||
<aside class="md-banner md-banner--warning">
|
||||
<div class="md-banner__inner md-grid md-typeset">
|
||||
|
||||
You're not viewing the latest version.
|
||||
<a href="..//">
|
||||
<strong>Click here to go to latest.</strong>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<script>var el=document.querySelector("[data-md-component=outdated]"),outdated=__md_get("__outdated",sessionStorage);!0===outdated&&el&&(el.hidden=!1)</script>
|
||||
</aside>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="/." title="æternity Sophia Language" class="md-header__button md-logo" aria-label="æternity Sophia Language" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54Z"/></svg>
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3V6m0 5h18v2H3v-2m0 5h18v2H3v-2Z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
æternity Sophia Language
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
|
||||
|
||||
<input class="md-option" data-md-color-media="" data-md-color-scheme="default" data-md-color-primary="pink" data-md-color-accent="pink" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_1">
|
||||
|
||||
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_2" hidden>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="m17.75 4.09-2.53 1.94.91 3.06-2.63-1.81-2.63 1.81.91-3.06-2.53-1.94L12.44 4l1.06-3 1.06 3 3.19.09m3.5 6.91-1.64 1.25.59 1.98-1.7-1.17-1.7 1.17.59-1.98L15.75 11l2.06-.05L18.5 9l.69 1.95 2.06.05m-2.28 4.95c.83-.08 1.72 1.1 1.19 1.85-.32.45-.66.87-1.08 1.27C15.17 23 8.84 23 4.94 19.07c-3.91-3.9-3.91-10.24 0-14.14.4-.4.82-.76 1.27-1.08.75-.53 1.93.36 1.85 1.19-.27 2.86.69 5.83 2.89 8.02a9.96 9.96 0 0 0 8.02 2.89m-1.64 2.02a12.08 12.08 0 0 1-7.8-3.47c-2.17-2.19-3.33-5-3.49-7.82-2.81 3.14-2.7 7.96.31 10.98 3.02 3.01 7.84 3.12 10.98.31Z"/></svg>
|
||||
</label>
|
||||
|
||||
|
||||
|
||||
<input class="md-option" data-md-color-media="" data-md-color-scheme="slate" data-md-color-primary="pink" data-md-color-accent="pink" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_2">
|
||||
|
||||
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_1" hidden>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 7a5 5 0 0 1 5 5 5 5 0 0 1-5 5 5 5 0 0 1-5-5 5 5 0 0 1 5-5m0 2a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3m0-7 2.39 3.42C13.65 5.15 12.84 5 12 5c-.84 0-1.65.15-2.39.42L12 2M3.34 7l4.16-.35A7.2 7.2 0 0 0 5.94 8.5c-.44.74-.69 1.5-.83 2.29L3.34 7m.02 10 1.76-3.77a7.131 7.131 0 0 0 2.38 4.14L3.36 17M20.65 7l-1.77 3.79a7.023 7.023 0 0 0-2.38-4.15l4.15.36m-.01 10-4.14.36c.59-.51 1.12-1.14 1.54-1.86.42-.73.69-1.5.83-2.29L20.64 17M12 22l-2.41-3.44c.74.27 1.55.44 2.41.44.82 0 1.63-.17 2.37-.44L12 22Z"/></svg>
|
||||
</label>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
|
||||
<label class="md-search__icon md-icon" for="__search">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"/></svg>
|
||||
</label>
|
||||
<nav class="md-search__options" aria-label="Search">
|
||||
|
||||
<a href="javascript:void(0)" class="md-search__icon md-icon" title="Share" aria-label="Share" data-clipboard data-clipboard-text="" data-md-component="search-share" tabindex="-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7 0-.24-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.15c-.05.21-.08.43-.08.66 0 1.61 1.31 2.91 2.92 2.91 1.61 0 2.92-1.3 2.92-2.91A2.92 2.92 0 0 0 18 16.08Z"/></svg>
|
||||
</a>
|
||||
|
||||
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41Z"/></svg>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<div class="md-search__suggest" data-md-component="search-suggest"></div>
|
||||
|
||||
</form>
|
||||
<div class="md-search__output">
|
||||
<div class="md-search__scrollwrap" data-md-scrollfix>
|
||||
<div class="md-search-result" data-md-component="search-result">
|
||||
<div class="md-search-result__meta">
|
||||
Initializing search
|
||||
</div>
|
||||
<ol class="md-search-result__list" role="presentation"></ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://github.com/aeternity/aesophia" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
GitHub
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
</header>
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
|
||||
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
|
||||
<div class="md-sidebar__scrollwrap">
|
||||
<div class="md-sidebar__inner">
|
||||
|
||||
|
||||
|
||||
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
|
||||
<label class="md-nav__title" for="__drawer">
|
||||
<a href="/." title="æternity Sophia Language" class="md-nav__button md-logo" aria-label="æternity Sophia Language" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54Z"/></svg>
|
||||
|
||||
</a>
|
||||
æternity Sophia Language
|
||||
</label>
|
||||
|
||||
<div class="md-nav__source">
|
||||
<a href="https://github.com/aeternity/aesophia" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
GitHub
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ul class="md-nav__list" data-md-scrollfix>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="/." class="md-nav__link">
|
||||
Introduction
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="/sophia_syntax/" class="md-nav__link">
|
||||
Syntax
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="/sophia_features/" class="md-nav__link">
|
||||
Features
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="/sophia_stdlib/" class="md-nav__link">
|
||||
Standard library
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="/sophia_examples/" class="md-nav__link">
|
||||
Contract examples
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="/CHANGELOG/" class="md-nav__link">
|
||||
Changelog
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
|
||||
<div class="md-sidebar__scrollwrap">
|
||||
<div class="md-sidebar__inner">
|
||||
|
||||
|
||||
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
|
||||
|
||||
|
||||
|
||||
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-content" data-md-component="content">
|
||||
<article class="md-content__inner md-typeset">
|
||||
|
||||
<h1>404 - Not found</h1>
|
||||
|
||||
</article>
|
||||
</div>
|
||||
|
||||
|
||||
<script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var tab,labels=set.querySelector(".tabbed-labels");for(tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script>
|
||||
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<footer class="md-footer">
|
||||
|
||||
<div class="md-footer-meta md-typeset">
|
||||
<div class="md-footer-meta__inner md-grid">
|
||||
<div class="md-copyright">
|
||||
|
||||
|
||||
Made with
|
||||
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
|
||||
Material for MkDocs
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
<div class="md-dialog" data-md-component="dialog">
|
||||
<div class="md-dialog__inner md-typeset"></div>
|
||||
</div>
|
||||
|
||||
<script id="__config" type="application/json">{"base": "/", "features": ["content.tabs.link", "search.highlight", "search.share", "search.suggest"], "search": "/assets/javascripts/workers/search.db81ec45.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": {"provider": "mike"}}</script>
|
||||
|
||||
|
||||
<script src="/assets/javascripts/bundle.a00a7c5e.min.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,596 +0,0 @@
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en" class="no-js">
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="icon" href="../favicon.png">
|
||||
<meta name="generator" content="mkdocs-1.4.2, mkdocs-material-9.0.9">
|
||||
|
||||
|
||||
|
||||
<title>aeso_aci - æternity Sophia Language</title>
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../assets/stylesheets/main.0d440cfe.min.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../assets/stylesheets/palette.2505c338.min.css">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
|
||||
<style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
|
||||
|
||||
|
||||
|
||||
<script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce((e,_)=>(e<<5)-e+_.charCodeAt(0),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<body dir="ltr" data-md-color-scheme="default" data-md-color-primary="pink" data-md-color-accent="pink">
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&"object"==typeof palette.color)for(var key of Object.keys(palette.color))document.body.setAttribute("data-md-color-"+key,palette.color[key])</script>
|
||||
|
||||
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
|
||||
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
|
||||
<label class="md-overlay" for="__drawer"></label>
|
||||
<div data-md-component="skip">
|
||||
|
||||
|
||||
<a href="#aeso_aci" class="md-skip">
|
||||
Skip to content
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<div data-md-component="announce">
|
||||
|
||||
</div>
|
||||
|
||||
<div data-md-component="outdated" hidden>
|
||||
|
||||
<aside class="md-banner md-banner--warning">
|
||||
<div class="md-banner__inner md-grid md-typeset">
|
||||
|
||||
You're not viewing the latest version.
|
||||
<a href="../..">
|
||||
<strong>Click here to go to latest.</strong>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<script>var el=document.querySelector("[data-md-component=outdated]"),outdated=__md_get("__outdated",sessionStorage);!0===outdated&&el&&(el.hidden=!1)</script>
|
||||
</aside>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href=".." title="æternity Sophia Language" class="md-header__button md-logo" aria-label="æternity Sophia Language" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54Z"/></svg>
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3V6m0 5h18v2H3v-2m0 5h18v2H3v-2Z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
æternity Sophia Language
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
aeso_aci
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
|
||||
|
||||
<input class="md-option" data-md-color-media="" data-md-color-scheme="default" data-md-color-primary="pink" data-md-color-accent="pink" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_1">
|
||||
|
||||
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_2" hidden>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="m17.75 4.09-2.53 1.94.91 3.06-2.63-1.81-2.63 1.81.91-3.06-2.53-1.94L12.44 4l1.06-3 1.06 3 3.19.09m3.5 6.91-1.64 1.25.59 1.98-1.7-1.17-1.7 1.17.59-1.98L15.75 11l2.06-.05L18.5 9l.69 1.95 2.06.05m-2.28 4.95c.83-.08 1.72 1.1 1.19 1.85-.32.45-.66.87-1.08 1.27C15.17 23 8.84 23 4.94 19.07c-3.91-3.9-3.91-10.24 0-14.14.4-.4.82-.76 1.27-1.08.75-.53 1.93.36 1.85 1.19-.27 2.86.69 5.83 2.89 8.02a9.96 9.96 0 0 0 8.02 2.89m-1.64 2.02a12.08 12.08 0 0 1-7.8-3.47c-2.17-2.19-3.33-5-3.49-7.82-2.81 3.14-2.7 7.96.31 10.98 3.02 3.01 7.84 3.12 10.98.31Z"/></svg>
|
||||
</label>
|
||||
|
||||
|
||||
|
||||
<input class="md-option" data-md-color-media="" data-md-color-scheme="slate" data-md-color-primary="pink" data-md-color-accent="pink" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_2">
|
||||
|
||||
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_1" hidden>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 7a5 5 0 0 1 5 5 5 5 0 0 1-5 5 5 5 0 0 1-5-5 5 5 0 0 1 5-5m0 2a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3m0-7 2.39 3.42C13.65 5.15 12.84 5 12 5c-.84 0-1.65.15-2.39.42L12 2M3.34 7l4.16-.35A7.2 7.2 0 0 0 5.94 8.5c-.44.74-.69 1.5-.83 2.29L3.34 7m.02 10 1.76-3.77a7.131 7.131 0 0 0 2.38 4.14L3.36 17M20.65 7l-1.77 3.79a7.023 7.023 0 0 0-2.38-4.15l4.15.36m-.01 10-4.14.36c.59-.51 1.12-1.14 1.54-1.86.42-.73.69-1.5.83-2.29L20.64 17M12 22l-2.41-3.44c.74.27 1.55.44 2.41.44.82 0 1.63-.17 2.37-.44L12 22Z"/></svg>
|
||||
</label>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
|
||||
<label class="md-search__icon md-icon" for="__search">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"/></svg>
|
||||
</label>
|
||||
<nav class="md-search__options" aria-label="Search">
|
||||
|
||||
<a href="javascript:void(0)" class="md-search__icon md-icon" title="Share" aria-label="Share" data-clipboard data-clipboard-text="" data-md-component="search-share" tabindex="-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7 0-.24-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.15c-.05.21-.08.43-.08.66 0 1.61 1.31 2.91 2.92 2.91 1.61 0 2.92-1.3 2.92-2.91A2.92 2.92 0 0 0 18 16.08Z"/></svg>
|
||||
</a>
|
||||
|
||||
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41Z"/></svg>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<div class="md-search__suggest" data-md-component="search-suggest"></div>
|
||||
|
||||
</form>
|
||||
<div class="md-search__output">
|
||||
<div class="md-search__scrollwrap" data-md-scrollfix>
|
||||
<div class="md-search-result" data-md-component="search-result">
|
||||
<div class="md-search-result__meta">
|
||||
Initializing search
|
||||
</div>
|
||||
<ol class="md-search-result__list" role="presentation"></ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://github.com/aeternity/aesophia" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
GitHub
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
</header>
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
|
||||
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
|
||||
<div class="md-sidebar__scrollwrap">
|
||||
<div class="md-sidebar__inner">
|
||||
|
||||
|
||||
|
||||
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
|
||||
<label class="md-nav__title" for="__drawer">
|
||||
<a href=".." title="æternity Sophia Language" class="md-nav__button md-logo" aria-label="æternity Sophia Language" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54Z"/></svg>
|
||||
|
||||
</a>
|
||||
æternity Sophia Language
|
||||
</label>
|
||||
|
||||
<div class="md-nav__source">
|
||||
<a href="https://github.com/aeternity/aesophia" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
GitHub
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ul class="md-nav__list" data-md-scrollfix>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href=".." class="md-nav__link">
|
||||
Introduction
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../sophia_syntax/" class="md-nav__link">
|
||||
Syntax
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../sophia_features/" class="md-nav__link">
|
||||
Features
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../sophia_stdlib/" class="md-nav__link">
|
||||
Standard library
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../sophia_examples/" class="md-nav__link">
|
||||
Contract examples
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../CHANGELOG/" class="md-nav__link">
|
||||
Changelog
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
|
||||
<div class="md-sidebar__scrollwrap">
|
||||
<div class="md-sidebar__inner">
|
||||
|
||||
|
||||
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-nav__title" for="__toc">
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
Table of contents
|
||||
</label>
|
||||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#module" class="md-nav__link">
|
||||
Module
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#aeso_aci_1" class="md-nav__link">
|
||||
aeso_aci
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#description" class="md-nav__link">
|
||||
Description
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#types" class="md-nav__link">
|
||||
Types
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#exports" class="md-nav__link">
|
||||
Exports
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#example-run" class="md-nav__link">
|
||||
Example run
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-content" data-md-component="content">
|
||||
<article class="md-content__inner md-typeset">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h1 id="aeso_aci">aeso_aci</h1>
|
||||
<h3 id="module">Module</h3>
|
||||
<h3 id="aeso_aci_1">aeso_aci</h3>
|
||||
<p>The ACI interface encoder and decoder.</p>
|
||||
<h3 id="description">Description</h3>
|
||||
<p>This module provides an interface to generate and convert between
|
||||
Sophia contracts and a suitable JSON encoding of contract
|
||||
interface. As yet the interface is very basic.</p>
|
||||
<p>Encoding this contract:</p>
|
||||
<div class="highlight"><pre><span></span><code>contract Answers =
|
||||
record state = { a : answers }
|
||||
type answers() = map(string, int)
|
||||
|
||||
stateful function init() = { a = {} }
|
||||
private function the_answer() = 42
|
||||
function new_answer(q : string, a : int) : answers() = { [q] = a }
|
||||
</code></pre></div>
|
||||
<p>generates the following JSON structure representing the contract interface:</p>
|
||||
<div class="highlight"><pre><span></span><code><span class="p">{</span>
|
||||
<span class="w"> </span><span class="nt">"contract"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||||
<span class="w"> </span><span class="nt">"functions"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
|
||||
<span class="w"> </span><span class="p">{</span>
|
||||
<span class="w"> </span><span class="nt">"arguments"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span>
|
||||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"init"</span><span class="p">,</span>
|
||||
<span class="w"> </span><span class="nt">"returns"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Answers.state"</span><span class="p">,</span>
|
||||
<span class="w"> </span><span class="nt">"stateful"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span>
|
||||
<span class="w"> </span><span class="p">},</span>
|
||||
<span class="w"> </span><span class="p">{</span>
|
||||
<span class="w"> </span><span class="nt">"arguments"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
|
||||
<span class="w"> </span><span class="p">{</span>
|
||||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"q"</span><span class="p">,</span>
|
||||
<span class="w"> </span><span class="nt">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"string"</span>
|
||||
<span class="w"> </span><span class="p">},</span>
|
||||
<span class="w"> </span><span class="p">{</span>
|
||||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"a"</span><span class="p">,</span>
|
||||
<span class="w"> </span><span class="nt">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"int"</span>
|
||||
<span class="w"> </span><span class="p">}</span>
|
||||
<span class="w"> </span><span class="p">],</span>
|
||||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"new_answer"</span><span class="p">,</span>
|
||||
<span class="w"> </span><span class="nt">"returns"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||||
<span class="w"> </span><span class="nt">"map"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
|
||||
<span class="w"> </span><span class="s2">"string"</span><span class="p">,</span>
|
||||
<span class="w"> </span><span class="s2">"int"</span>
|
||||
<span class="w"> </span><span class="p">]</span>
|
||||
<span class="w"> </span><span class="p">},</span>
|
||||
<span class="w"> </span><span class="nt">"stateful"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span>
|
||||
<span class="w"> </span><span class="p">}</span>
|
||||
<span class="w"> </span><span class="p">],</span>
|
||||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Answers"</span><span class="p">,</span>
|
||||
<span class="w"> </span><span class="nt">"state"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||||
<span class="w"> </span><span class="nt">"record"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
|
||||
<span class="w"> </span><span class="p">{</span>
|
||||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"a"</span><span class="p">,</span>
|
||||
<span class="w"> </span><span class="nt">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Answers.answers"</span>
|
||||
<span class="w"> </span><span class="p">}</span>
|
||||
<span class="w"> </span><span class="p">]</span>
|
||||
<span class="w"> </span><span class="p">},</span>
|
||||
<span class="w"> </span><span class="nt">"typedefs"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
|
||||
<span class="w"> </span><span class="p">{</span>
|
||||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"answers"</span><span class="p">,</span>
|
||||
<span class="w"> </span><span class="nt">"typedef"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||||
<span class="w"> </span><span class="nt">"map"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
|
||||
<span class="w"> </span><span class="s2">"string"</span><span class="p">,</span>
|
||||
<span class="w"> </span><span class="s2">"int"</span>
|
||||
<span class="w"> </span><span class="p">]</span>
|
||||
<span class="w"> </span><span class="p">},</span>
|
||||
<span class="w"> </span><span class="nt">"vars"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span>
|
||||
<span class="w"> </span><span class="p">}</span>
|
||||
<span class="w"> </span><span class="p">]</span>
|
||||
<span class="w"> </span><span class="p">}</span>
|
||||
<span class="p">}</span>
|
||||
</code></pre></div>
|
||||
<p>When that encoding is decoded the following include definition is generated:</p>
|
||||
<div class="highlight"><pre><span></span><code>contract Answers =
|
||||
record state = {a : Answers.answers}
|
||||
type answers = map(string, int)
|
||||
function init : () => Answers.state
|
||||
function new_answer : (string, int) => map(string, int)
|
||||
</code></pre></div>
|
||||
<h3 id="types">Types</h3>
|
||||
<div class="highlight"><pre><span></span><code><span class="p">-</span><span class="ni">type</span><span class="w"> </span><span class="n">aci_type</span><span class="p">()</span><span class="w"> </span><span class="p">::</span><span class="w"> </span><span class="n">json</span><span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="n">string</span><span class="p">.</span>
|
||||
<span class="p">-</span><span class="ni">type</span><span class="w"> </span><span class="n">json</span><span class="p">()</span><span class="w"> </span><span class="p">::</span><span class="w"> </span><span class="nn">jsx</span><span class="p">:</span><span class="nf">json_term</span><span class="p">().</span>
|
||||
<span class="p">-</span><span class="ni">type</span><span class="w"> </span><span class="n">json_text</span><span class="p">()</span><span class="w"> </span><span class="p">::</span><span class="w"> </span><span class="n">binary</span><span class="p">().</span>
|
||||
</code></pre></div>
|
||||
<h3 id="exports">Exports</h3>
|
||||
<h4 id="contract_interfaceaci_type-string-ok-json-string-error-term">contract_interface(aci_type(), string()) -> {ok, json() | string()} | {error, term()}</h4>
|
||||
<p>Generate the JSON encoding of the interface to a contract. The type definitions
|
||||
and non-private functions are included in the JSON string.</p>
|
||||
<h4 id="render_aci_jsonjson-json_text-string">render_aci_json(json() | json_text()) -> string().</h4>
|
||||
<p>Take a JSON encoding of a contract interface and generate a contract interface
|
||||
that can be included in another contract.</p>
|
||||
<h3 id="example-run">Example run</h3>
|
||||
<p>This is an example of using the ACI generator from an Erlang shell. The file
|
||||
called <code>aci_test.aes</code> contains the contract in the description from which we
|
||||
want to generate files <code>aci_test.json</code> which is the JSON encoding of the
|
||||
contract interface and <code>aci_test.include</code> which is the contract definition to
|
||||
be included inside another contract.</p>
|
||||
<div class="highlight"><pre><span></span><code><span class="mi">1</span><span class="o">></span><span class="w"> </span><span class="p">{</span><span class="n">ok</span><span class="p">,</span><span class="nv">Contract</span><span class="p">}</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nn">file</span><span class="p">:</span><span class="nf">read_file</span><span class="p">(</span><span class="s">"aci_test.aes"</span><span class="p">).</span>
|
||||
<span class="p">{</span><span class="n">ok</span><span class="p">,</span><span class="o"><<</span><span class="s">"contract Answers =</span><span class="se">\n</span><span class="s"> record state = { a : answers }</span><span class="se">\n</span><span class="s"> type answers() = map(string, int)</span><span class="se">\n\n</span><span class="s"> stateful function"</span><span class="p">...</span><span class="o">>></span><span class="p">}</span>
|
||||
<span class="mi">2</span><span class="o">></span><span class="w"> </span><span class="p">{</span><span class="n">ok</span><span class="p">,</span><span class="nv">JsonACI</span><span class="p">}</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nn">aeso_aci</span><span class="p">:</span><span class="nf">contract_interface</span><span class="p">(</span><span class="n">json</span><span class="p">,</span><span class="w"> </span><span class="nv">Contract</span><span class="p">).</span>
|
||||
<span class="p">{</span><span class="n">ok</span><span class="p">,[#{</span><span class="n">contract</span><span class="w"> </span><span class="o">=></span>
|
||||
<span class="w"> </span><span class="p">#{</span><span class="n">functions</span><span class="w"> </span><span class="o">=></span>
|
||||
<span class="w"> </span><span class="p">[#{</span><span class="n">arguments</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">[],</span><span class="n">name</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="o"><<</span><span class="s">"init"</span><span class="o">>></span><span class="p">,</span>
|
||||
<span class="w"> </span><span class="n">returns</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="o"><<</span><span class="s">"Answers.state"</span><span class="o">>></span><span class="p">,</span><span class="n">stateful</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="n">true</span><span class="p">},</span>
|
||||
<span class="w"> </span><span class="p">#{</span><span class="n">arguments</span><span class="w"> </span><span class="o">=></span>
|
||||
<span class="w"> </span><span class="p">[#{</span><span class="n">name</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="o"><<</span><span class="s">"q"</span><span class="o">>></span><span class="p">,</span><span class="n">type</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="o"><<</span><span class="s">"string"</span><span class="o">>></span><span class="p">},</span>
|
||||
<span class="w"> </span><span class="p">#{</span><span class="n">name</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="o"><<</span><span class="s">"a"</span><span class="o">>></span><span class="p">,</span><span class="n">type</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="o"><<</span><span class="s">"int"</span><span class="o">>></span><span class="p">}],</span>
|
||||
<span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="o"><<</span><span class="s">"new_answer"</span><span class="o">>></span><span class="p">,</span>
|
||||
<span class="w"> </span><span class="n">returns</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">#{</span><span class="o"><<</span><span class="s">"map"</span><span class="o">>></span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">[</span><span class="o"><<</span><span class="s">"string"</span><span class="o">>></span><span class="p">,</span><span class="o"><<</span><span class="s">"int"</span><span class="o">>></span><span class="p">]},</span>
|
||||
<span class="w"> </span><span class="n">stateful</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="n">false</span><span class="p">}],</span>
|
||||
<span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="o"><<</span><span class="s">"Answers"</span><span class="o">>></span><span class="p">,</span>
|
||||
<span class="w"> </span><span class="n">state</span><span class="w"> </span><span class="o">=></span>
|
||||
<span class="w"> </span><span class="p">#{</span><span class="n">record</span><span class="w"> </span><span class="o">=></span>
|
||||
<span class="w"> </span><span class="p">[#{</span><span class="n">name</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="o"><<</span><span class="s">"a"</span><span class="o">>></span><span class="p">,</span><span class="n">type</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="o"><<</span><span class="s">"Answers.answers"</span><span class="o">>></span><span class="p">}]},</span>
|
||||
<span class="w"> </span><span class="n">typedefs</span><span class="w"> </span><span class="o">=></span>
|
||||
<span class="w"> </span><span class="p">[#{</span><span class="n">name</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="o"><<</span><span class="s">"answers"</span><span class="o">>></span><span class="p">,</span>
|
||||
<span class="w"> </span><span class="n">typedef</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">#{</span><span class="o"><<</span><span class="s">"map"</span><span class="o">>></span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">[</span><span class="o"><<</span><span class="s">"string"</span><span class="o">>></span><span class="p">,</span><span class="o"><<</span><span class="s">"int"</span><span class="o">>></span><span class="p">]},</span>
|
||||
<span class="w"> </span><span class="n">vars</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">[]}]}}]}</span>
|
||||
<span class="mi">3</span><span class="o">></span><span class="w"> </span><span class="nn">file</span><span class="p">:</span><span class="nf">write_file</span><span class="p">(</span><span class="s">"aci_test.aci"</span><span class="p">,</span><span class="w"> </span><span class="nn">jsx</span><span class="p">:</span><span class="nf">encode</span><span class="p">(</span><span class="nv">JsonACI</span><span class="p">)).</span>
|
||||
<span class="n">ok</span>
|
||||
<span class="mi">4</span><span class="o">></span><span class="w"> </span><span class="p">{</span><span class="n">ok</span><span class="p">,</span><span class="nv">InterfaceStub</span><span class="p">}</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nn">aeso_aci</span><span class="p">:</span><span class="nf">render_aci_json</span><span class="p">(</span><span class="nv">JsonACI</span><span class="p">).</span>
|
||||
<span class="p">{</span><span class="n">ok</span><span class="p">,</span><span class="o"><<</span><span class="s">"contract Answers =</span><span class="se">\n</span><span class="s"> record state = {a : Answers.answers}</span><span class="se">\n</span><span class="s"> type answers = map(string, int)</span><span class="se">\n</span><span class="s"> function init "</span><span class="p">...</span><span class="o">>></span><span class="p">}</span>
|
||||
<span class="mi">5</span><span class="o">></span><span class="w"> </span><span class="nn">file</span><span class="p">:</span><span class="nf">write_file</span><span class="p">(</span><span class="s">"aci_test.include"</span><span class="p">,</span><span class="w"> </span><span class="nv">InterfaceStub</span><span class="p">).</span>
|
||||
<span class="n">ok</span>
|
||||
<span class="mi">6</span><span class="o">></span><span class="w"> </span><span class="nn">jsx</span><span class="p">:</span><span class="nf">prettify</span><span class="p">(</span><span class="nn">jsx</span><span class="p">:</span><span class="nf">encode</span><span class="p">(</span><span class="nv">JsonACI</span><span class="p">)).</span>
|
||||
<span class="o"><<</span><span class="s">"[</span><span class="se">\n</span><span class="s"> {</span><span class="se">\n</span><span class="s"> </span><span class="se">\"</span><span class="s">contract</span><span class="se">\"</span><span class="s">: {</span><span class="se">\n</span><span class="s"> </span><span class="se">\"</span><span class="s">functions</span><span class="se">\"</span><span class="s">: [</span><span class="se">\n</span><span class="s"> {</span><span class="se">\n</span><span class="s"> </span><span class="se">\"</span><span class="s">arguments</span><span class="se">\"</span><span class="s">: [],</span><span class="se">\n</span><span class="s"> </span><span class="se">\"</span><span class="s">name</span><span class="se">\"</span><span class="s">: </span><span class="se">\"</span><span class="s">init</span><span class="se">\"</span><span class="s">,</span><span class="se">\n</span><span class="s"> "</span><span class="p">...</span><span class="o">>></span>
|
||||
</code></pre></div>
|
||||
<p>The final call to <code>jsx:prettify(jsx:encode(JsonACI))</code> returns the encoding in a
|
||||
more easily readable form. This is what is shown in the description above.</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</article>
|
||||
</div>
|
||||
|
||||
|
||||
<script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var tab,labels=set.querySelector(".tabbed-labels");for(tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script>
|
||||
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<footer class="md-footer">
|
||||
|
||||
<div class="md-footer-meta md-typeset">
|
||||
<div class="md-footer-meta__inner md-grid">
|
||||
<div class="md-copyright">
|
||||
|
||||
|
||||
Made with
|
||||
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
|
||||
Material for MkDocs
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
<div class="md-dialog" data-md-component="dialog">
|
||||
<div class="md-dialog__inner md-typeset"></div>
|
||||
</div>
|
||||
|
||||
<script id="__config" type="application/json">{"base": "..", "features": ["content.tabs.link", "search.highlight", "search.share", "search.suggest"], "search": "../assets/javascripts/workers/search.db81ec45.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": {"provider": "mike"}}</script>
|
||||
|
||||
|
||||
<script src="../assets/javascripts/bundle.a00a7c5e.min.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,532 +0,0 @@
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en" class="no-js">
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="icon" href="../favicon.png">
|
||||
<meta name="generator" content="mkdocs-1.4.2, mkdocs-material-9.0.9">
|
||||
|
||||
|
||||
|
||||
<title>aeso_compiler - æternity Sophia Language</title>
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../assets/stylesheets/main.0d440cfe.min.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../assets/stylesheets/palette.2505c338.min.css">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
|
||||
<style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
|
||||
|
||||
|
||||
|
||||
<script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce((e,_)=>(e<<5)-e+_.charCodeAt(0),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<body dir="ltr" data-md-color-scheme="default" data-md-color-primary="pink" data-md-color-accent="pink">
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&"object"==typeof palette.color)for(var key of Object.keys(palette.color))document.body.setAttribute("data-md-color-"+key,palette.color[key])</script>
|
||||
|
||||
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
|
||||
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
|
||||
<label class="md-overlay" for="__drawer"></label>
|
||||
<div data-md-component="skip">
|
||||
|
||||
|
||||
<a href="#aeso_compiler" class="md-skip">
|
||||
Skip to content
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<div data-md-component="announce">
|
||||
|
||||
</div>
|
||||
|
||||
<div data-md-component="outdated" hidden>
|
||||
|
||||
<aside class="md-banner md-banner--warning">
|
||||
<div class="md-banner__inner md-grid md-typeset">
|
||||
|
||||
You're not viewing the latest version.
|
||||
<a href="../..">
|
||||
<strong>Click here to go to latest.</strong>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<script>var el=document.querySelector("[data-md-component=outdated]"),outdated=__md_get("__outdated",sessionStorage);!0===outdated&&el&&(el.hidden=!1)</script>
|
||||
</aside>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href=".." title="æternity Sophia Language" class="md-header__button md-logo" aria-label="æternity Sophia Language" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54Z"/></svg>
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3V6m0 5h18v2H3v-2m0 5h18v2H3v-2Z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
æternity Sophia Language
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
aeso_compiler
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
|
||||
|
||||
<input class="md-option" data-md-color-media="" data-md-color-scheme="default" data-md-color-primary="pink" data-md-color-accent="pink" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_1">
|
||||
|
||||
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_2" hidden>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="m17.75 4.09-2.53 1.94.91 3.06-2.63-1.81-2.63 1.81.91-3.06-2.53-1.94L12.44 4l1.06-3 1.06 3 3.19.09m3.5 6.91-1.64 1.25.59 1.98-1.7-1.17-1.7 1.17.59-1.98L15.75 11l2.06-.05L18.5 9l.69 1.95 2.06.05m-2.28 4.95c.83-.08 1.72 1.1 1.19 1.85-.32.45-.66.87-1.08 1.27C15.17 23 8.84 23 4.94 19.07c-3.91-3.9-3.91-10.24 0-14.14.4-.4.82-.76 1.27-1.08.75-.53 1.93.36 1.85 1.19-.27 2.86.69 5.83 2.89 8.02a9.96 9.96 0 0 0 8.02 2.89m-1.64 2.02a12.08 12.08 0 0 1-7.8-3.47c-2.17-2.19-3.33-5-3.49-7.82-2.81 3.14-2.7 7.96.31 10.98 3.02 3.01 7.84 3.12 10.98.31Z"/></svg>
|
||||
</label>
|
||||
|
||||
|
||||
|
||||
<input class="md-option" data-md-color-media="" data-md-color-scheme="slate" data-md-color-primary="pink" data-md-color-accent="pink" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_2">
|
||||
|
||||
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_1" hidden>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 7a5 5 0 0 1 5 5 5 5 0 0 1-5 5 5 5 0 0 1-5-5 5 5 0 0 1 5-5m0 2a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3m0-7 2.39 3.42C13.65 5.15 12.84 5 12 5c-.84 0-1.65.15-2.39.42L12 2M3.34 7l4.16-.35A7.2 7.2 0 0 0 5.94 8.5c-.44.74-.69 1.5-.83 2.29L3.34 7m.02 10 1.76-3.77a7.131 7.131 0 0 0 2.38 4.14L3.36 17M20.65 7l-1.77 3.79a7.023 7.023 0 0 0-2.38-4.15l4.15.36m-.01 10-4.14.36c.59-.51 1.12-1.14 1.54-1.86.42-.73.69-1.5.83-2.29L20.64 17M12 22l-2.41-3.44c.74.27 1.55.44 2.41.44.82 0 1.63-.17 2.37-.44L12 22Z"/></svg>
|
||||
</label>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
|
||||
<label class="md-search__icon md-icon" for="__search">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"/></svg>
|
||||
</label>
|
||||
<nav class="md-search__options" aria-label="Search">
|
||||
|
||||
<a href="javascript:void(0)" class="md-search__icon md-icon" title="Share" aria-label="Share" data-clipboard data-clipboard-text="" data-md-component="search-share" tabindex="-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7 0-.24-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.15c-.05.21-.08.43-.08.66 0 1.61 1.31 2.91 2.92 2.91 1.61 0 2.92-1.3 2.92-2.91A2.92 2.92 0 0 0 18 16.08Z"/></svg>
|
||||
</a>
|
||||
|
||||
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41Z"/></svg>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<div class="md-search__suggest" data-md-component="search-suggest"></div>
|
||||
|
||||
</form>
|
||||
<div class="md-search__output">
|
||||
<div class="md-search__scrollwrap" data-md-scrollfix>
|
||||
<div class="md-search-result" data-md-component="search-result">
|
||||
<div class="md-search-result__meta">
|
||||
Initializing search
|
||||
</div>
|
||||
<ol class="md-search-result__list" role="presentation"></ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://github.com/aeternity/aesophia" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
GitHub
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
</header>
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
|
||||
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
|
||||
<div class="md-sidebar__scrollwrap">
|
||||
<div class="md-sidebar__inner">
|
||||
|
||||
|
||||
|
||||
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
|
||||
<label class="md-nav__title" for="__drawer">
|
||||
<a href=".." title="æternity Sophia Language" class="md-nav__button md-logo" aria-label="æternity Sophia Language" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54Z"/></svg>
|
||||
|
||||
</a>
|
||||
æternity Sophia Language
|
||||
</label>
|
||||
|
||||
<div class="md-nav__source">
|
||||
<a href="https://github.com/aeternity/aesophia" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
GitHub
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ul class="md-nav__list" data-md-scrollfix>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href=".." class="md-nav__link">
|
||||
Introduction
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../sophia_syntax/" class="md-nav__link">
|
||||
Syntax
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../sophia_features/" class="md-nav__link">
|
||||
Features
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../sophia_stdlib/" class="md-nav__link">
|
||||
Standard library
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../sophia_examples/" class="md-nav__link">
|
||||
Contract examples
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../CHANGELOG/" class="md-nav__link">
|
||||
Changelog
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
|
||||
<div class="md-sidebar__scrollwrap">
|
||||
<div class="md-sidebar__inner">
|
||||
|
||||
|
||||
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-nav__title" for="__toc">
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
Table of contents
|
||||
</label>
|
||||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#module" class="md-nav__link">
|
||||
Module
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#aeso_compiler_1" class="md-nav__link">
|
||||
aeso_compiler
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#description" class="md-nav__link">
|
||||
Description
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#types" class="md-nav__link">
|
||||
Types
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#exports" class="md-nav__link">
|
||||
Exports
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-content" data-md-component="content">
|
||||
<article class="md-content__inner md-typeset">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h1 id="aeso_compiler">aeso_compiler</h1>
|
||||
<h3 id="module">Module</h3>
|
||||
<h3 id="aeso_compiler_1">aeso_compiler</h3>
|
||||
<p>The Sophia compiler</p>
|
||||
<h3 id="description">Description</h3>
|
||||
<p>This module provides the interface to the standard Sophia compiler. It
|
||||
returns the compiled module in a map which can then be loaded.</p>
|
||||
<h3 id="types">Types</h3>
|
||||
<div class="highlight"><pre><span></span><code><span class="nf">contract_string</span><span class="p">()</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">string</span><span class="p">()</span><span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="n">binary</span><span class="p">()</span>
|
||||
<span class="nf">contract_map</span><span class="p">()</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">#{</span><span class="n">bytecode</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="n">binary</span><span class="p">(),</span>
|
||||
<span class="w"> </span><span class="n">compiler_version</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="n">binary</span><span class="p">(),</span>
|
||||
<span class="w"> </span><span class="n">contract_souce</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="n">string</span><span class="p">(),</span>
|
||||
<span class="w"> </span><span class="n">type_info</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="n">type_info</span><span class="p">()}</span>
|
||||
<span class="nf">type_info</span><span class="p">()</span>
|
||||
<span class="nf">errorstring</span><span class="p">()</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">binary</span><span class="p">()</span>
|
||||
</code></pre></div>
|
||||
<h3 id="exports">Exports</h3>
|
||||
<h4 id="filefile">file(File)</h4>
|
||||
<h4 id="filefile-options-compret">file(File, Options) -> CompRet</h4>
|
||||
<h4 id="from_stringcontractstring-options-compret">from_string(ContractString, Options) -> CompRet</h4>
|
||||
<p>Types</p>
|
||||
<div class="highlight"><pre><span></span><code><span class="nv">ContractString</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">contract_string</span><span class="p">()</span>
|
||||
<span class="nv">Options</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="nv">Option</span><span class="p">]</span>
|
||||
<span class="nv">CompRet</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="n">ok</span><span class="p">,</span><span class="nv">ContractMap</span><span class="p">}</span><span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="p">{</span><span class="n">error</span><span class="p">,</span><span class="nv">ErrorString</span><span class="p">}</span>
|
||||
<span class="nv">ContractMap</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">contract_map</span><span class="p">()</span>
|
||||
<span class="nv">ErrorString</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">errorstring</span><span class="p">()</span>
|
||||
</code></pre></div>
|
||||
<p>Compile a contract defined in a file or in a string.</p>
|
||||
<p>The <strong>pp_</strong> options all print to standard output the following:</p>
|
||||
<p><code>pp_sophia_code</code> - print the input Sophia code.</p>
|
||||
<p><code>pp_ast</code> - print the AST of the code</p>
|
||||
<p><code>pp_types</code> - print information about the types</p>
|
||||
<p><code>pp_typed_ast</code> - print the AST with type information at each node</p>
|
||||
<p><code>pp_assembler</code> - print the generated assembler code</p>
|
||||
<p>The option <code>include_child_contract_symbols</code> includes the symbols of child contracts functions in the generated fate code. It is turned off by default to avoid making contracts bigger on chain.</p>
|
||||
<h4 id="options-to-control-which-compiler-optimizations-should-run">Options to control which compiler optimizations should run:</h4>
|
||||
<p>By default all optimizations are turned on, to disable an optimization, it should be
|
||||
explicitly set to false and passed as a compiler option.</p>
|
||||
<p>List of optimizations:</p>
|
||||
<ul>
|
||||
<li>optimize_inliner</li>
|
||||
<li>optimize_inline_local_functions</li>
|
||||
<li>optimize_bind_subexpressions</li>
|
||||
<li>optimize_let_floating</li>
|
||||
<li>optimize_simplifier</li>
|
||||
<li>optimize_drop_unused_lets</li>
|
||||
<li>optimize_push_consume</li>
|
||||
<li>optimize_one_shot_var</li>
|
||||
<li>optimize_write_to_dead_var</li>
|
||||
<li>optimize_inline_switch_target</li>
|
||||
<li>optimize_swap_push</li>
|
||||
<li>optimize_swap_pop</li>
|
||||
<li>optimize_swap_write</li>
|
||||
<li>optimize_constant_propagation</li>
|
||||
<li>optimize_prune_impossible_branches</li>
|
||||
<li>optimize_single_successful_branch</li>
|
||||
<li>optimize_inline_store</li>
|
||||
<li>optimize_float_switch_bod</li>
|
||||
</ul>
|
||||
<h4 id="check_callcontractstring-options-checkret">check_call(ContractString, Options) -> CheckRet</h4>
|
||||
<p>Types
|
||||
<div class="highlight"><pre><span></span><code>ContractString = string() | binary()
|
||||
CheckRet = {ok,string(),{Types,Type | any()},Terms} | {error,Term}
|
||||
Types = [Type]
|
||||
Type = term()
|
||||
</code></pre></div>
|
||||
Check a call in contract through the <code>__call</code> function.</p>
|
||||
<h4 id="version-ok-version-error-term">version() -> {ok, Version} | {error, term()}</h4>
|
||||
<p>Types</p>
|
||||
<div class="highlight"><pre><span></span><code><span class="nv">Version</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">binary</span><span class="p">()</span>
|
||||
</code></pre></div>
|
||||
<p>Get the current version of the Sophia compiler.</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</article>
|
||||
</div>
|
||||
|
||||
|
||||
<script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var tab,labels=set.querySelector(".tabbed-labels");for(tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script>
|
||||
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<footer class="md-footer">
|
||||
|
||||
<div class="md-footer-meta md-typeset">
|
||||
<div class="md-footer-meta__inner md-grid">
|
||||
<div class="md-copyright">
|
||||
|
||||
|
||||
Made with
|
||||
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
|
||||
Material for MkDocs
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
<div class="md-dialog" data-md-component="dialog">
|
||||
<div class="md-dialog__inner md-typeset"></div>
|
||||
</div>
|
||||
|
||||
<script id="__config" type="application/json">{"base": "..", "features": ["content.tabs.link", "search.highlight", "search.share", "search.suggest"], "search": "../assets/javascripts/workers/search.db81ec45.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": {"provider": "mike"}}</script>
|
||||
|
||||
|
||||
<script src="../assets/javascripts/bundle.a00a7c5e.min.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.8 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,18 +0,0 @@
|
||||
/*!
|
||||
* Lunr languages, `Danish` language
|
||||
* https://github.com/MihaiValentin/lunr-languages
|
||||
*
|
||||
* Copyright 2014, Mihai Valentin
|
||||
* http://www.mozilla.org/MPL/
|
||||
*/
|
||||
/*!
|
||||
* based on
|
||||
* Snowball JavaScript Library v0.3
|
||||
* http://code.google.com/p/urim/
|
||||
* http://snowball.tartarus.org/
|
||||
*
|
||||
* Copyright 2010, Oleg Mazko
|
||||
* http://www.mozilla.org/MPL/
|
||||
*/
|
||||
|
||||
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.da=function(){this.pipeline.reset(),this.pipeline.add(e.da.trimmer,e.da.stopWordFilter,e.da.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.da.stemmer))},e.da.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.da.trimmer=e.trimmerSupport.generateTrimmer(e.da.wordCharacters),e.Pipeline.registerFunction(e.da.trimmer,"trimmer-da"),e.da.stemmer=function(){var r=e.stemmerSupport.Among,i=e.stemmerSupport.SnowballProgram,n=new function(){function e(){var e,r=f.cursor+3;if(d=f.limit,0<=r&&r<=f.limit){for(a=r;;){if(e=f.cursor,f.in_grouping(w,97,248)){f.cursor=e;break}if(f.cursor=e,e>=f.limit)return;f.cursor++}for(;!f.out_grouping(w,97,248);){if(f.cursor>=f.limit)return;f.cursor++}d=f.cursor,d<a&&(d=a)}}function n(){var e,r;if(f.cursor>=d&&(r=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,e=f.find_among_b(c,32),f.limit_backward=r,e))switch(f.bra=f.cursor,e){case 1:f.slice_del();break;case 2:f.in_grouping_b(p,97,229)&&f.slice_del()}}function t(){var e,r=f.limit-f.cursor;f.cursor>=d&&(e=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,f.find_among_b(l,4)?(f.bra=f.cursor,f.limit_backward=e,f.cursor=f.limit-r,f.cursor>f.limit_backward&&(f.cursor--,f.bra=f.cursor,f.slice_del())):f.limit_backward=e)}function s(){var e,r,i,n=f.limit-f.cursor;if(f.ket=f.cursor,f.eq_s_b(2,"st")&&(f.bra=f.cursor,f.eq_s_b(2,"ig")&&f.slice_del()),f.cursor=f.limit-n,f.cursor>=d&&(r=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,e=f.find_among_b(m,5),f.limit_backward=r,e))switch(f.bra=f.cursor,e){case 1:f.slice_del(),i=f.limit-f.cursor,t(),f.cursor=f.limit-i;break;case 2:f.slice_from("løs")}}function o(){var e;f.cursor>=d&&(e=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,f.out_grouping_b(w,97,248)?(f.bra=f.cursor,u=f.slice_to(u),f.limit_backward=e,f.eq_v_b(u)&&f.slice_del()):f.limit_backward=e)}var a,d,u,c=[new r("hed",-1,1),new r("ethed",0,1),new r("ered",-1,1),new r("e",-1,1),new r("erede",3,1),new r("ende",3,1),new r("erende",5,1),new r("ene",3,1),new r("erne",3,1),new r("ere",3,1),new r("en",-1,1),new r("heden",10,1),new r("eren",10,1),new r("er",-1,1),new r("heder",13,1),new r("erer",13,1),new r("s",-1,2),new r("heds",16,1),new r("es",16,1),new r("endes",18,1),new r("erendes",19,1),new r("enes",18,1),new r("ernes",18,1),new r("eres",18,1),new r("ens",16,1),new r("hedens",24,1),new r("erens",24,1),new r("ers",16,1),new r("ets",16,1),new r("erets",28,1),new r("et",-1,1),new r("eret",30,1)],l=[new r("gd",-1,-1),new r("dt",-1,-1),new r("gt",-1,-1),new r("kt",-1,-1)],m=[new r("ig",-1,1),new r("lig",0,1),new r("elig",1,1),new r("els",-1,1),new r("løst",-1,2)],w=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,48,0,128],p=[239,254,42,3,0,0,0,0,0,0,0,0,0,0,0,0,16],f=new i;this.setCurrent=function(e){f.setCurrent(e)},this.getCurrent=function(){return f.getCurrent()},this.stem=function(){var r=f.cursor;return e(),f.limit_backward=r,f.cursor=f.limit,n(),f.cursor=f.limit,t(),f.cursor=f.limit,s(),f.cursor=f.limit,o(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}}(),e.Pipeline.registerFunction(e.da.stemmer,"stemmer-da"),e.da.stopWordFilter=e.generateStopWordFilter("ad af alle alt anden at blev blive bliver da de dem den denne der deres det dette dig din disse dog du efter eller en end er et for fra ham han hans har havde have hende hendes her hos hun hvad hvis hvor i ikke ind jeg jer jo kunne man mange med meget men mig min mine mit mod ned noget nogle nu når og også om op os over på selv sig sin sine sit skal skulle som sådan thi til ud under var vi vil ville vor være været".split(" ")),e.Pipeline.registerFunction(e.da.stopWordFilter,"stopWordFilter-da")}});
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.hi=function(){this.pipeline.reset(),this.pipeline.add(e.hi.trimmer,e.hi.stopWordFilter,e.hi.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.hi.stemmer))},e.hi.wordCharacters="ऀ-ःऄ-एऐ-टठ-यर-िी-ॏॐ-य़ॠ-९॰-ॿa-zA-Za-zA-Z0-90-9",e.hi.trimmer=e.trimmerSupport.generateTrimmer(e.hi.wordCharacters),e.Pipeline.registerFunction(e.hi.trimmer,"trimmer-hi"),e.hi.stopWordFilter=e.generateStopWordFilter("अत अपना अपनी अपने अभी अंदर आदि आप इत्यादि इन इनका इन्हीं इन्हें इन्हों इस इसका इसकी इसके इसमें इसी इसे उन उनका उनकी उनके उनको उन्हीं उन्हें उन्हों उस उसके उसी उसे एक एवं एस ऐसे और कई कर करता करते करना करने करें कहते कहा का काफ़ी कि कितना किन्हें किन्हों किया किर किस किसी किसे की कुछ कुल के को कोई कौन कौनसा गया घर जब जहाँ जा जितना जिन जिन्हें जिन्हों जिस जिसे जीधर जैसा जैसे जो तक तब तरह तिन तिन्हें तिन्हों तिस तिसे तो था थी थे दबारा दिया दुसरा दूसरे दो द्वारा न नके नहीं ना निहायत नीचे ने पर पहले पूरा पे फिर बनी बही बहुत बाद बाला बिलकुल भी भीतर मगर मानो मे में यदि यह यहाँ यही या यिह ये रखें रहा रहे ऱ्वासा लिए लिये लेकिन व वग़ैरह वर्ग वह वहाँ वहीं वाले वुह वे वो सकता सकते सबसे सभी साथ साबुत साभ सारा से सो संग ही हुआ हुई हुए है हैं हो होता होती होते होना होने".split(" ")),e.hi.stemmer=function(){return function(e){return"function"==typeof e.update?e.update(function(e){return e}):e}}();var r=e.wordcut;r.init(),e.hi.tokenizer=function(i){if(!arguments.length||null==i||void 0==i)return[];if(Array.isArray(i))return i.map(function(r){return isLunr2?new e.Token(r.toLowerCase()):r.toLowerCase()});var t=i.toString().toLowerCase().replace(/^\s+/,"");return r.cut(t).split("|")},e.Pipeline.registerFunction(e.hi.stemmer,"stemmer-hi"),e.Pipeline.registerFunction(e.hi.stopWordFilter,"stopWordFilter-hi")}});
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r="2"==e.version[0];e.ja=function(){this.pipeline.reset(),this.pipeline.add(e.ja.trimmer,e.ja.stopWordFilter,e.ja.stemmer),r?this.tokenizer=e.ja.tokenizer:(e.tokenizer&&(e.tokenizer=e.ja.tokenizer),this.tokenizerFn&&(this.tokenizerFn=e.ja.tokenizer))};var t=new e.TinySegmenter;e.ja.tokenizer=function(i){var n,o,s,p,a,u,m,l,c,f;if(!arguments.length||null==i||void 0==i)return[];if(Array.isArray(i))return i.map(function(t){return r?new e.Token(t.toLowerCase()):t.toLowerCase()});for(o=i.toString().toLowerCase().replace(/^\s+/,""),n=o.length-1;n>=0;n--)if(/\S/.test(o.charAt(n))){o=o.substring(0,n+1);break}for(a=[],s=o.length,c=0,l=0;c<=s;c++)if(u=o.charAt(c),m=c-l,u.match(/\s/)||c==s){if(m>0)for(p=t.segment(o.slice(l,c)).filter(function(e){return!!e}),f=l,n=0;n<p.length;n++)r?a.push(new e.Token(p[n],{position:[f,p[n].length],index:a.length})):a.push(p[n]),f+=p[n].length;l=c+1}return a},e.ja.stemmer=function(){return function(e){return e}}(),e.Pipeline.registerFunction(e.ja.stemmer,"stemmer-ja"),e.ja.wordCharacters="一二三四五六七八九十百千万億兆一-龠々〆ヵヶぁ-んァ-ヴーア-ン゙a-zA-Za-zA-Z0-90-9",e.ja.trimmer=e.trimmerSupport.generateTrimmer(e.ja.wordCharacters),e.Pipeline.registerFunction(e.ja.trimmer,"trimmer-ja"),e.ja.stopWordFilter=e.generateStopWordFilter("これ それ あれ この その あの ここ そこ あそこ こちら どこ だれ なに なん 何 私 貴方 貴方方 我々 私達 あの人 あのかた 彼女 彼 です あります おります います は が の に を で え から まで より も どの と し それで しかし".split(" ")),e.Pipeline.registerFunction(e.ja.stopWordFilter,"stopWordFilter-ja"),e.jp=e.ja,e.Pipeline.registerFunction(e.jp.stemmer,"stemmer-jp"),e.Pipeline.registerFunction(e.jp.trimmer,"trimmer-jp"),e.Pipeline.registerFunction(e.jp.stopWordFilter,"stopWordFilter-jp")}});
|
||||
@@ -1 +0,0 @@
|
||||
module.exports=require("./lunr.ja");
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
!function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():t()(e.lunr)}(this,function(){return function(e){e.multiLanguage=function(){for(var t=Array.prototype.slice.call(arguments),i=t.join("-"),r="",n=[],s=[],p=0;p<t.length;++p)"en"==t[p]?(r+="\\w",n.unshift(e.stopWordFilter),n.push(e.stemmer),s.push(e.stemmer)):(r+=e[t[p]].wordCharacters,e[t[p]].stopWordFilter&&n.unshift(e[t[p]].stopWordFilter),e[t[p]].stemmer&&(n.push(e[t[p]].stemmer),s.push(e[t[p]].stemmer)));var o=e.trimmerSupport.generateTrimmer(r);return e.Pipeline.registerFunction(o,"lunr-multi-trimmer-"+i),n.unshift(o),function(){this.pipeline.reset(),this.pipeline.add.apply(this.pipeline,n),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add.apply(this.searchPipeline,s))}}}});
|
||||
File diff suppressed because one or more lines are too long
@@ -1,18 +0,0 @@
|
||||
/*!
|
||||
* Lunr languages, `Norwegian` language
|
||||
* https://github.com/MihaiValentin/lunr-languages
|
||||
*
|
||||
* Copyright 2014, Mihai Valentin
|
||||
* http://www.mozilla.org/MPL/
|
||||
*/
|
||||
/*!
|
||||
* based on
|
||||
* Snowball JavaScript Library v0.3
|
||||
* http://code.google.com/p/urim/
|
||||
* http://snowball.tartarus.org/
|
||||
*
|
||||
* Copyright 2010, Oleg Mazko
|
||||
* http://www.mozilla.org/MPL/
|
||||
*/
|
||||
|
||||
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.no=function(){this.pipeline.reset(),this.pipeline.add(e.no.trimmer,e.no.stopWordFilter,e.no.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.no.stemmer))},e.no.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.no.trimmer=e.trimmerSupport.generateTrimmer(e.no.wordCharacters),e.Pipeline.registerFunction(e.no.trimmer,"trimmer-no"),e.no.stemmer=function(){var r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,i=new function(){function e(){var e,r=w.cursor+3;if(a=w.limit,0<=r||r<=w.limit){for(s=r;;){if(e=w.cursor,w.in_grouping(d,97,248)){w.cursor=e;break}if(e>=w.limit)return;w.cursor=e+1}for(;!w.out_grouping(d,97,248);){if(w.cursor>=w.limit)return;w.cursor++}a=w.cursor,a<s&&(a=s)}}function i(){var e,r,n;if(w.cursor>=a&&(r=w.limit_backward,w.limit_backward=a,w.ket=w.cursor,e=w.find_among_b(m,29),w.limit_backward=r,e))switch(w.bra=w.cursor,e){case 1:w.slice_del();break;case 2:n=w.limit-w.cursor,w.in_grouping_b(c,98,122)?w.slice_del():(w.cursor=w.limit-n,w.eq_s_b(1,"k")&&w.out_grouping_b(d,97,248)&&w.slice_del());break;case 3:w.slice_from("er")}}function t(){var e,r=w.limit-w.cursor;w.cursor>=a&&(e=w.limit_backward,w.limit_backward=a,w.ket=w.cursor,w.find_among_b(u,2)?(w.bra=w.cursor,w.limit_backward=e,w.cursor=w.limit-r,w.cursor>w.limit_backward&&(w.cursor--,w.bra=w.cursor,w.slice_del())):w.limit_backward=e)}function o(){var e,r;w.cursor>=a&&(r=w.limit_backward,w.limit_backward=a,w.ket=w.cursor,e=w.find_among_b(l,11),e?(w.bra=w.cursor,w.limit_backward=r,1==e&&w.slice_del()):w.limit_backward=r)}var s,a,m=[new r("a",-1,1),new r("e",-1,1),new r("ede",1,1),new r("ande",1,1),new r("ende",1,1),new r("ane",1,1),new r("ene",1,1),new r("hetene",6,1),new r("erte",1,3),new r("en",-1,1),new r("heten",9,1),new r("ar",-1,1),new r("er",-1,1),new r("heter",12,1),new r("s",-1,2),new r("as",14,1),new r("es",14,1),new r("edes",16,1),new r("endes",16,1),new r("enes",16,1),new r("hetenes",19,1),new r("ens",14,1),new r("hetens",21,1),new r("ers",14,1),new r("ets",14,1),new r("et",-1,1),new r("het",25,1),new r("ert",-1,3),new r("ast",-1,1)],u=[new r("dt",-1,-1),new r("vt",-1,-1)],l=[new r("leg",-1,1),new r("eleg",0,1),new r("ig",-1,1),new r("eig",2,1),new r("lig",2,1),new r("elig",4,1),new r("els",-1,1),new r("lov",-1,1),new r("elov",7,1),new r("slov",7,1),new r("hetslov",9,1)],d=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,48,0,128],c=[119,125,149,1],w=new n;this.setCurrent=function(e){w.setCurrent(e)},this.getCurrent=function(){return w.getCurrent()},this.stem=function(){var r=w.cursor;return e(),w.limit_backward=r,w.cursor=w.limit,i(),w.cursor=w.limit,t(),w.cursor=w.limit,o(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}}(),e.Pipeline.registerFunction(e.no.stemmer,"stemmer-no"),e.no.stopWordFilter=e.generateStopWordFilter("alle at av bare begge ble blei bli blir blitt både båe da de deg dei deim deira deires dem den denne der dere deres det dette di din disse ditt du dykk dykkar då eg ein eit eitt eller elles en enn er et ett etter for fordi fra før ha hadde han hans har hennar henne hennes her hjå ho hoe honom hoss hossen hun hva hvem hver hvilke hvilken hvis hvor hvordan hvorfor i ikke ikkje ikkje ingen ingi inkje inn inni ja jeg kan kom korleis korso kun kunne kva kvar kvarhelst kven kvi kvifor man mange me med medan meg meget mellom men mi min mine mitt mot mykje ned no noe noen noka noko nokon nokor nokre nå når og også om opp oss over på samme seg selv si si sia sidan siden sin sine sitt sjøl skal skulle slik so som som somme somt så sånn til um upp ut uten var vart varte ved vere verte vi vil ville vore vors vort vår være være vært å".split(" ")),e.Pipeline.registerFunction(e.no.stopWordFilter,"stopWordFilter-no")}});
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
!function(r,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():t()(r.lunr)}(this,function(){return function(r){r.stemmerSupport={Among:function(r,t,i,s){if(this.toCharArray=function(r){for(var t=r.length,i=new Array(t),s=0;s<t;s++)i[s]=r.charCodeAt(s);return i},!r&&""!=r||!t&&0!=t||!i)throw"Bad Among initialisation: s:"+r+", substring_i: "+t+", result: "+i;this.s_size=r.length,this.s=this.toCharArray(r),this.substring_i=t,this.result=i,this.method=s},SnowballProgram:function(){var r;return{bra:0,ket:0,limit:0,cursor:0,limit_backward:0,setCurrent:function(t){r=t,this.cursor=0,this.limit=t.length,this.limit_backward=0,this.bra=this.cursor,this.ket=this.limit},getCurrent:function(){var t=r;return r=null,t},in_grouping:function(t,i,s){if(this.cursor<this.limit){var e=r.charCodeAt(this.cursor);if(e<=s&&e>=i&&(e-=i,t[e>>3]&1<<(7&e)))return this.cursor++,!0}return!1},in_grouping_b:function(t,i,s){if(this.cursor>this.limit_backward){var e=r.charCodeAt(this.cursor-1);if(e<=s&&e>=i&&(e-=i,t[e>>3]&1<<(7&e)))return this.cursor--,!0}return!1},out_grouping:function(t,i,s){if(this.cursor<this.limit){var e=r.charCodeAt(this.cursor);if(e>s||e<i)return this.cursor++,!0;if(e-=i,!(t[e>>3]&1<<(7&e)))return this.cursor++,!0}return!1},out_grouping_b:function(t,i,s){if(this.cursor>this.limit_backward){var e=r.charCodeAt(this.cursor-1);if(e>s||e<i)return this.cursor--,!0;if(e-=i,!(t[e>>3]&1<<(7&e)))return this.cursor--,!0}return!1},eq_s:function(t,i){if(this.limit-this.cursor<t)return!1;for(var s=0;s<t;s++)if(r.charCodeAt(this.cursor+s)!=i.charCodeAt(s))return!1;return this.cursor+=t,!0},eq_s_b:function(t,i){if(this.cursor-this.limit_backward<t)return!1;for(var s=0;s<t;s++)if(r.charCodeAt(this.cursor-t+s)!=i.charCodeAt(s))return!1;return this.cursor-=t,!0},find_among:function(t,i){for(var s=0,e=i,n=this.cursor,u=this.limit,o=0,h=0,c=!1;;){for(var a=s+(e-s>>1),f=0,l=o<h?o:h,_=t[a],m=l;m<_.s_size;m++){if(n+l==u){f=-1;break}if(f=r.charCodeAt(n+l)-_.s[m])break;l++}if(f<0?(e=a,h=l):(s=a,o=l),e-s<=1){if(s>0||e==s||c)break;c=!0}}for(;;){var _=t[s];if(o>=_.s_size){if(this.cursor=n+_.s_size,!_.method)return _.result;var b=_.method();if(this.cursor=n+_.s_size,b)return _.result}if((s=_.substring_i)<0)return 0}},find_among_b:function(t,i){for(var s=0,e=i,n=this.cursor,u=this.limit_backward,o=0,h=0,c=!1;;){for(var a=s+(e-s>>1),f=0,l=o<h?o:h,_=t[a],m=_.s_size-1-l;m>=0;m--){if(n-l==u){f=-1;break}if(f=r.charCodeAt(n-1-l)-_.s[m])break;l++}if(f<0?(e=a,h=l):(s=a,o=l),e-s<=1){if(s>0||e==s||c)break;c=!0}}for(;;){var _=t[s];if(o>=_.s_size){if(this.cursor=n-_.s_size,!_.method)return _.result;var b=_.method();if(this.cursor=n-_.s_size,b)return _.result}if((s=_.substring_i)<0)return 0}},replace_s:function(t,i,s){var e=s.length-(i-t),n=r.substring(0,t),u=r.substring(i);return r=n+s+u,this.limit+=e,this.cursor>=i?this.cursor+=e:this.cursor>t&&(this.cursor=t),e},slice_check:function(){if(this.bra<0||this.bra>this.ket||this.ket>this.limit||this.limit>r.length)throw"faulty slice operation"},slice_from:function(r){this.slice_check(),this.replace_s(this.bra,this.ket,r)},slice_del:function(){this.slice_from("")},insert:function(r,t,i){var s=this.replace_s(r,t,i);r<=this.bra&&(this.bra+=s),r<=this.ket&&(this.ket+=s)},slice_to:function(){return this.slice_check(),r.substring(this.bra,this.ket)},eq_v_b:function(r){return this.eq_s_b(r.length,r)}}}},r.trimmerSupport={generateTrimmer:function(r){var t=new RegExp("^[^"+r+"]+"),i=new RegExp("[^"+r+"]+$");return function(r){return"function"==typeof r.update?r.update(function(r){return r.replace(t,"").replace(i,"")}):r.replace(t,"").replace(i,"")}}}}});
|
||||
@@ -1,18 +0,0 @@
|
||||
/*!
|
||||
* Lunr languages, `Swedish` language
|
||||
* https://github.com/MihaiValentin/lunr-languages
|
||||
*
|
||||
* Copyright 2014, Mihai Valentin
|
||||
* http://www.mozilla.org/MPL/
|
||||
*/
|
||||
/*!
|
||||
* based on
|
||||
* Snowball JavaScript Library v0.3
|
||||
* http://code.google.com/p/urim/
|
||||
* http://snowball.tartarus.org/
|
||||
*
|
||||
* Copyright 2010, Oleg Mazko
|
||||
* http://www.mozilla.org/MPL/
|
||||
*/
|
||||
|
||||
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.sv=function(){this.pipeline.reset(),this.pipeline.add(e.sv.trimmer,e.sv.stopWordFilter,e.sv.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.sv.stemmer))},e.sv.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.sv.trimmer=e.trimmerSupport.generateTrimmer(e.sv.wordCharacters),e.Pipeline.registerFunction(e.sv.trimmer,"trimmer-sv"),e.sv.stemmer=function(){var r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,t=new function(){function e(){var e,r=w.cursor+3;if(o=w.limit,0<=r||r<=w.limit){for(a=r;;){if(e=w.cursor,w.in_grouping(l,97,246)){w.cursor=e;break}if(w.cursor=e,w.cursor>=w.limit)return;w.cursor++}for(;!w.out_grouping(l,97,246);){if(w.cursor>=w.limit)return;w.cursor++}o=w.cursor,o<a&&(o=a)}}function t(){var e,r=w.limit_backward;if(w.cursor>=o&&(w.limit_backward=o,w.cursor=w.limit,w.ket=w.cursor,e=w.find_among_b(u,37),w.limit_backward=r,e))switch(w.bra=w.cursor,e){case 1:w.slice_del();break;case 2:w.in_grouping_b(d,98,121)&&w.slice_del()}}function i(){var e=w.limit_backward;w.cursor>=o&&(w.limit_backward=o,w.cursor=w.limit,w.find_among_b(c,7)&&(w.cursor=w.limit,w.ket=w.cursor,w.cursor>w.limit_backward&&(w.bra=--w.cursor,w.slice_del())),w.limit_backward=e)}function s(){var e,r;if(w.cursor>=o){if(r=w.limit_backward,w.limit_backward=o,w.cursor=w.limit,w.ket=w.cursor,e=w.find_among_b(m,5))switch(w.bra=w.cursor,e){case 1:w.slice_del();break;case 2:w.slice_from("lös");break;case 3:w.slice_from("full")}w.limit_backward=r}}var a,o,u=[new r("a",-1,1),new r("arna",0,1),new r("erna",0,1),new r("heterna",2,1),new r("orna",0,1),new r("ad",-1,1),new r("e",-1,1),new r("ade",6,1),new r("ande",6,1),new r("arne",6,1),new r("are",6,1),new r("aste",6,1),new r("en",-1,1),new r("anden",12,1),new r("aren",12,1),new r("heten",12,1),new r("ern",-1,1),new r("ar",-1,1),new r("er",-1,1),new r("heter",18,1),new r("or",-1,1),new r("s",-1,2),new r("as",21,1),new r("arnas",22,1),new r("ernas",22,1),new r("ornas",22,1),new r("es",21,1),new r("ades",26,1),new r("andes",26,1),new r("ens",21,1),new r("arens",29,1),new r("hetens",29,1),new r("erns",21,1),new r("at",-1,1),new r("andet",-1,1),new r("het",-1,1),new r("ast",-1,1)],c=[new r("dd",-1,-1),new r("gd",-1,-1),new r("nn",-1,-1),new r("dt",-1,-1),new r("gt",-1,-1),new r("kt",-1,-1),new r("tt",-1,-1)],m=[new r("ig",-1,1),new r("lig",0,1),new r("els",-1,1),new r("fullt",-1,3),new r("löst",-1,2)],l=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,24,0,32],d=[119,127,149],w=new n;this.setCurrent=function(e){w.setCurrent(e)},this.getCurrent=function(){return w.getCurrent()},this.stem=function(){var r=w.cursor;return e(),w.limit_backward=r,w.cursor=w.limit,t(),w.cursor=w.limit,i(),w.cursor=w.limit,s(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return t.setCurrent(e),t.stem(),t.getCurrent()}):(t.setCurrent(e),t.stem(),t.getCurrent())}}(),e.Pipeline.registerFunction(e.sv.stemmer,"stemmer-sv"),e.sv.stopWordFilter=e.generateStopWordFilter("alla allt att av blev bli blir blivit de dem den denna deras dess dessa det detta dig din dina ditt du där då efter ej eller en er era ert ett från för ha hade han hans har henne hennes hon honom hur här i icke ingen inom inte jag ju kan kunde man med mellan men mig min mina mitt mot mycket ni nu när någon något några och om oss på samma sedan sig sin sina sitta själv skulle som så sådan sådana sådant till under upp ut utan vad var vara varför varit varje vars vart vem vi vid vilka vilkas vilken vilket vår våra vårt än är åt över".split(" ")),e.Pipeline.registerFunction(e.sv.stopWordFilter,"stopWordFilter-sv")}});
|
||||
@@ -1 +0,0 @@
|
||||
!function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():t()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.ta=function(){this.pipeline.reset(),this.pipeline.add(e.ta.trimmer,e.ta.stopWordFilter,e.ta.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.ta.stemmer))},e.ta.wordCharacters="-உஊ-ஏஐ-ஙச-ட-னப-யர-ஹ-ிீ-ொ-ௐ---௩௪-௯௰-௹௺-a-zA-Za-zA-Z0-90-9",e.ta.trimmer=e.trimmerSupport.generateTrimmer(e.ta.wordCharacters),e.Pipeline.registerFunction(e.ta.trimmer,"trimmer-ta"),e.ta.stopWordFilter=e.generateStopWordFilter("அங்கு அங்கே அது அதை அந்த அவர் அவர்கள் அவள் அவன் அவை ஆக ஆகவே ஆகையால் ஆதலால் ஆதலினால் ஆனாலும் ஆனால் இங்கு இங்கே இது இதை இந்த இப்படி இவர் இவர்கள் இவள் இவன் இவை இவ்வளவு உனக்கு உனது உன் உன்னால் எங்கு எங்கே எது எதை எந்த எப்படி எவர் எவர்கள் எவள் எவன் எவை எவ்வளவு எனக்கு எனது எனவே என் என்ன என்னால் ஏது ஏன் தனது தன்னால் தானே தான் நாங்கள் நாம் நான் நீ நீங்கள்".split(" ")),e.ta.stemmer=function(){return function(e){return"function"==typeof e.update?e.update(function(e){return e}):e}}();var t=e.wordcut;t.init(),e.ta.tokenizer=function(r){if(!arguments.length||null==r||void 0==r)return[];if(Array.isArray(r))return r.map(function(t){return isLunr2?new e.Token(t.toLowerCase()):t.toLowerCase()});var i=r.toString().toLowerCase().replace(/^\s+/,"");return t.cut(i).split("|")},e.Pipeline.registerFunction(e.ta.stemmer,"stemmer-ta"),e.Pipeline.registerFunction(e.ta.stopWordFilter,"stopWordFilter-ta")}});
|
||||
@@ -1 +0,0 @@
|
||||
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r="2"==e.version[0];e.th=function(){this.pipeline.reset(),this.pipeline.add(e.th.trimmer),r?this.tokenizer=e.th.tokenizer:(e.tokenizer&&(e.tokenizer=e.th.tokenizer),this.tokenizerFn&&(this.tokenizerFn=e.th.tokenizer))},e.th.wordCharacters="[-]",e.th.trimmer=e.trimmerSupport.generateTrimmer(e.th.wordCharacters),e.Pipeline.registerFunction(e.th.trimmer,"trimmer-th");var t=e.wordcut;t.init(),e.th.tokenizer=function(i){if(!arguments.length||null==i||void 0==i)return[];if(Array.isArray(i))return i.map(function(t){return r?new e.Token(t):t});var n=i.toString().replace(/^\s+/,"");return t.cut(n).split("|")}}});
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.vi=function(){this.pipeline.reset(),this.pipeline.add(e.vi.stopWordFilter,e.vi.trimmer)},e.vi.wordCharacters="[A-Za-ẓ̀͐́͑̉̃̓ÂâÊêÔôĂ-ăĐ-đƠ-ơƯ-ư]",e.vi.trimmer=e.trimmerSupport.generateTrimmer(e.vi.wordCharacters),e.Pipeline.registerFunction(e.vi.trimmer,"trimmer-vi"),e.vi.stopWordFilter=e.generateStopWordFilter("là cái nhưng mà".split(" "))}});
|
||||
@@ -1 +0,0 @@
|
||||
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r(require("@node-rs/jieba")):r()(e.lunr)}(this,function(e){return function(r,t){if(void 0===r)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===r.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var i="2"==r.version[0];r.zh=function(){this.pipeline.reset(),this.pipeline.add(r.zh.trimmer,r.zh.stopWordFilter,r.zh.stemmer),i?this.tokenizer=r.zh.tokenizer:(r.tokenizer&&(r.tokenizer=r.zh.tokenizer),this.tokenizerFn&&(this.tokenizerFn=r.zh.tokenizer))},r.zh.tokenizer=function(n){if(!arguments.length||null==n||void 0==n)return[];if(Array.isArray(n))return n.map(function(e){return i?new r.Token(e.toLowerCase()):e.toLowerCase()});t&&e.load(t);var o=n.toString().trim().toLowerCase(),s=[];e.cut(o,!0).forEach(function(e){s=s.concat(e.split(" "))}),s=s.filter(function(e){return!!e});var u=0;return s.map(function(e,t){if(i){var n=o.indexOf(e,u),s={};return s.position=[n,e.length],s.index=t,u=n,new r.Token(e,s)}return e})},r.zh.wordCharacters="\\w一-龥",r.zh.trimmer=r.trimmerSupport.generateTrimmer(r.zh.wordCharacters),r.Pipeline.registerFunction(r.zh.trimmer,"trimmer-zh"),r.zh.stemmer=function(){return function(e){return e}}(),r.Pipeline.registerFunction(r.zh.stemmer,"stemmer-zh"),r.zh.stopWordFilter=r.generateStopWordFilter("的 一 不 在 人 有 是 为 以 于 上 他 而 后 之 来 及 了 因 下 可 到 由 这 与 也 此 但 并 个 其 已 无 小 我 们 起 最 再 今 去 好 只 又 或 很 亦 某 把 那 你 乃 它 吧 被 比 别 趁 当 从 到 得 打 凡 儿 尔 该 各 给 跟 和 何 还 即 几 既 看 据 距 靠 啦 了 另 么 每 们 嘛 拿 哪 那 您 凭 且 却 让 仍 啥 如 若 使 谁 虽 随 同 所 她 哇 嗡 往 哪 些 向 沿 哟 用 于 咱 则 怎 曾 至 致 着 诸 自".split(" ")),r.Pipeline.registerFunction(r.zh.stopWordFilter,"stopWordFilter-zh")}});
|
||||
@@ -1,206 +0,0 @@
|
||||
/**
|
||||
* export the module via AMD, CommonJS or as a browser global
|
||||
* Export code from https://github.com/umdjs/umd/blob/master/returnExports.js
|
||||
*/
|
||||
;(function (root, factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define(factory)
|
||||
} else if (typeof exports === 'object') {
|
||||
/**
|
||||
* Node. Does not work with strict CommonJS, but
|
||||
* only CommonJS-like environments that support module.exports,
|
||||
* like Node.
|
||||
*/
|
||||
module.exports = factory()
|
||||
} else {
|
||||
// Browser globals (root is window)
|
||||
factory()(root.lunr);
|
||||
}
|
||||
}(this, function () {
|
||||
/**
|
||||
* Just return a value to define the module export.
|
||||
* This example returns an object, but the module
|
||||
* can return a function as the exported value.
|
||||
*/
|
||||
|
||||
return function(lunr) {
|
||||
// TinySegmenter 0.1 -- Super compact Japanese tokenizer in Javascript
|
||||
// (c) 2008 Taku Kudo <taku@chasen.org>
|
||||
// TinySegmenter is freely distributable under the terms of a new BSD licence.
|
||||
// For details, see http://chasen.org/~taku/software/TinySegmenter/LICENCE.txt
|
||||
|
||||
function TinySegmenter() {
|
||||
var patterns = {
|
||||
"[一二三四五六七八九十百千万億兆]":"M",
|
||||
"[一-龠々〆ヵヶ]":"H",
|
||||
"[ぁ-ん]":"I",
|
||||
"[ァ-ヴーア-ン゙ー]":"K",
|
||||
"[a-zA-Za-zA-Z]":"A",
|
||||
"[0-90-9]":"N"
|
||||
}
|
||||
this.chartype_ = [];
|
||||
for (var i in patterns) {
|
||||
var regexp = new RegExp(i);
|
||||
this.chartype_.push([regexp, patterns[i]]);
|
||||
}
|
||||
|
||||
this.BIAS__ = -332
|
||||
this.BC1__ = {"HH":6,"II":2461,"KH":406,"OH":-1378};
|
||||
this.BC2__ = {"AA":-3267,"AI":2744,"AN":-878,"HH":-4070,"HM":-1711,"HN":4012,"HO":3761,"IA":1327,"IH":-1184,"II":-1332,"IK":1721,"IO":5492,"KI":3831,"KK":-8741,"MH":-3132,"MK":3334,"OO":-2920};
|
||||
this.BC3__ = {"HH":996,"HI":626,"HK":-721,"HN":-1307,"HO":-836,"IH":-301,"KK":2762,"MK":1079,"MM":4034,"OA":-1652,"OH":266};
|
||||
this.BP1__ = {"BB":295,"OB":304,"OO":-125,"UB":352};
|
||||
this.BP2__ = {"BO":60,"OO":-1762};
|
||||
this.BQ1__ = {"BHH":1150,"BHM":1521,"BII":-1158,"BIM":886,"BMH":1208,"BNH":449,"BOH":-91,"BOO":-2597,"OHI":451,"OIH":-296,"OKA":1851,"OKH":-1020,"OKK":904,"OOO":2965};
|
||||
this.BQ2__ = {"BHH":118,"BHI":-1159,"BHM":466,"BIH":-919,"BKK":-1720,"BKO":864,"OHH":-1139,"OHM":-181,"OIH":153,"UHI":-1146};
|
||||
this.BQ3__ = {"BHH":-792,"BHI":2664,"BII":-299,"BKI":419,"BMH":937,"BMM":8335,"BNN":998,"BOH":775,"OHH":2174,"OHM":439,"OII":280,"OKH":1798,"OKI":-793,"OKO":-2242,"OMH":-2402,"OOO":11699};
|
||||
this.BQ4__ = {"BHH":-3895,"BIH":3761,"BII":-4654,"BIK":1348,"BKK":-1806,"BMI":-3385,"BOO":-12396,"OAH":926,"OHH":266,"OHK":-2036,"ONN":-973};
|
||||
this.BW1__ = {",と":660,",同":727,"B1あ":1404,"B1同":542,"、と":660,"、同":727,"」と":1682,"あっ":1505,"いう":1743,"いっ":-2055,"いる":672,"うし":-4817,"うん":665,"から":3472,"がら":600,"こう":-790,"こと":2083,"こん":-1262,"さら":-4143,"さん":4573,"した":2641,"して":1104,"すで":-3399,"そこ":1977,"それ":-871,"たち":1122,"ため":601,"った":3463,"つい":-802,"てい":805,"てき":1249,"でき":1127,"です":3445,"では":844,"とい":-4915,"とみ":1922,"どこ":3887,"ない":5713,"なっ":3015,"など":7379,"なん":-1113,"にし":2468,"には":1498,"にも":1671,"に対":-912,"の一":-501,"の中":741,"ませ":2448,"まで":1711,"まま":2600,"まる":-2155,"やむ":-1947,"よっ":-2565,"れた":2369,"れで":-913,"をし":1860,"を見":731,"亡く":-1886,"京都":2558,"取り":-2784,"大き":-2604,"大阪":1497,"平方":-2314,"引き":-1336,"日本":-195,"本当":-2423,"毎日":-2113,"目指":-724,"B1あ":1404,"B1同":542,"」と":1682};
|
||||
this.BW2__ = {"..":-11822,"11":-669,"――":-5730,"−−":-13175,"いう":-1609,"うか":2490,"かし":-1350,"かも":-602,"から":-7194,"かれ":4612,"がい":853,"がら":-3198,"きた":1941,"くな":-1597,"こと":-8392,"この":-4193,"させ":4533,"され":13168,"さん":-3977,"しい":-1819,"しか":-545,"した":5078,"して":972,"しな":939,"その":-3744,"たい":-1253,"たた":-662,"ただ":-3857,"たち":-786,"たと":1224,"たは":-939,"った":4589,"って":1647,"っと":-2094,"てい":6144,"てき":3640,"てく":2551,"ては":-3110,"ても":-3065,"でい":2666,"でき":-1528,"でし":-3828,"です":-4761,"でも":-4203,"とい":1890,"とこ":-1746,"とと":-2279,"との":720,"とみ":5168,"とも":-3941,"ない":-2488,"なが":-1313,"など":-6509,"なの":2614,"なん":3099,"にお":-1615,"にし":2748,"にな":2454,"によ":-7236,"に対":-14943,"に従":-4688,"に関":-11388,"のか":2093,"ので":-7059,"のに":-6041,"のの":-6125,"はい":1073,"はが":-1033,"はず":-2532,"ばれ":1813,"まし":-1316,"まで":-6621,"まれ":5409,"めて":-3153,"もい":2230,"もの":-10713,"らか":-944,"らし":-1611,"らに":-1897,"りし":651,"りま":1620,"れた":4270,"れて":849,"れば":4114,"ろう":6067,"われ":7901,"を通":-11877,"んだ":728,"んな":-4115,"一人":602,"一方":-1375,"一日":970,"一部":-1051,"上が":-4479,"会社":-1116,"出て":2163,"分の":-7758,"同党":970,"同日":-913,"大阪":-2471,"委員":-1250,"少な":-1050,"年度":-8669,"年間":-1626,"府県":-2363,"手権":-1982,"新聞":-4066,"日新":-722,"日本":-7068,"日米":3372,"曜日":-601,"朝鮮":-2355,"本人":-2697,"東京":-1543,"然と":-1384,"社会":-1276,"立て":-990,"第に":-1612,"米国":-4268,"11":-669};
|
||||
this.BW3__ = {"あた":-2194,"あり":719,"ある":3846,"い.":-1185,"い。":-1185,"いい":5308,"いえ":2079,"いく":3029,"いた":2056,"いっ":1883,"いる":5600,"いわ":1527,"うち":1117,"うと":4798,"えと":1454,"か.":2857,"か。":2857,"かけ":-743,"かっ":-4098,"かに":-669,"から":6520,"かり":-2670,"が,":1816,"が、":1816,"がき":-4855,"がけ":-1127,"がっ":-913,"がら":-4977,"がり":-2064,"きた":1645,"けど":1374,"こと":7397,"この":1542,"ころ":-2757,"さい":-714,"さを":976,"し,":1557,"し、":1557,"しい":-3714,"した":3562,"して":1449,"しな":2608,"しま":1200,"す.":-1310,"す。":-1310,"する":6521,"ず,":3426,"ず、":3426,"ずに":841,"そう":428,"た.":8875,"た。":8875,"たい":-594,"たの":812,"たり":-1183,"たる":-853,"だ.":4098,"だ。":4098,"だっ":1004,"った":-4748,"って":300,"てい":6240,"てお":855,"ても":302,"です":1437,"でに":-1482,"では":2295,"とう":-1387,"とし":2266,"との":541,"とも":-3543,"どう":4664,"ない":1796,"なく":-903,"など":2135,"に,":-1021,"に、":-1021,"にし":1771,"にな":1906,"には":2644,"の,":-724,"の、":-724,"の子":-1000,"は,":1337,"は、":1337,"べき":2181,"まし":1113,"ます":6943,"まっ":-1549,"まで":6154,"まれ":-793,"らし":1479,"られ":6820,"るる":3818,"れ,":854,"れ、":854,"れた":1850,"れて":1375,"れば":-3246,"れる":1091,"われ":-605,"んだ":606,"んで":798,"カ月":990,"会議":860,"入り":1232,"大会":2217,"始め":1681,"市":965,"新聞":-5055,"日,":974,"日、":974,"社会":2024,"カ月":990};
|
||||
this.TC1__ = {"AAA":1093,"HHH":1029,"HHM":580,"HII":998,"HOH":-390,"HOM":-331,"IHI":1169,"IOH":-142,"IOI":-1015,"IOM":467,"MMH":187,"OOI":-1832};
|
||||
this.TC2__ = {"HHO":2088,"HII":-1023,"HMM":-1154,"IHI":-1965,"KKH":703,"OII":-2649};
|
||||
this.TC3__ = {"AAA":-294,"HHH":346,"HHI":-341,"HII":-1088,"HIK":731,"HOH":-1486,"IHH":128,"IHI":-3041,"IHO":-1935,"IIH":-825,"IIM":-1035,"IOI":-542,"KHH":-1216,"KKA":491,"KKH":-1217,"KOK":-1009,"MHH":-2694,"MHM":-457,"MHO":123,"MMH":-471,"NNH":-1689,"NNO":662,"OHO":-3393};
|
||||
this.TC4__ = {"HHH":-203,"HHI":1344,"HHK":365,"HHM":-122,"HHN":182,"HHO":669,"HIH":804,"HII":679,"HOH":446,"IHH":695,"IHO":-2324,"IIH":321,"III":1497,"IIO":656,"IOO":54,"KAK":4845,"KKA":3386,"KKK":3065,"MHH":-405,"MHI":201,"MMH":-241,"MMM":661,"MOM":841};
|
||||
this.TQ1__ = {"BHHH":-227,"BHHI":316,"BHIH":-132,"BIHH":60,"BIII":1595,"BNHH":-744,"BOHH":225,"BOOO":-908,"OAKK":482,"OHHH":281,"OHIH":249,"OIHI":200,"OIIH":-68};
|
||||
this.TQ2__ = {"BIHH":-1401,"BIII":-1033,"BKAK":-543,"BOOO":-5591};
|
||||
this.TQ3__ = {"BHHH":478,"BHHM":-1073,"BHIH":222,"BHII":-504,"BIIH":-116,"BIII":-105,"BMHI":-863,"BMHM":-464,"BOMH":620,"OHHH":346,"OHHI":1729,"OHII":997,"OHMH":481,"OIHH":623,"OIIH":1344,"OKAK":2792,"OKHH":587,"OKKA":679,"OOHH":110,"OOII":-685};
|
||||
this.TQ4__ = {"BHHH":-721,"BHHM":-3604,"BHII":-966,"BIIH":-607,"BIII":-2181,"OAAA":-2763,"OAKK":180,"OHHH":-294,"OHHI":2446,"OHHO":480,"OHIH":-1573,"OIHH":1935,"OIHI":-493,"OIIH":626,"OIII":-4007,"OKAK":-8156};
|
||||
this.TW1__ = {"につい":-4681,"東京都":2026};
|
||||
this.TW2__ = {"ある程":-2049,"いった":-1256,"ころが":-2434,"しょう":3873,"その後":-4430,"だって":-1049,"ていた":1833,"として":-4657,"ともに":-4517,"もので":1882,"一気に":-792,"初めて":-1512,"同時に":-8097,"大きな":-1255,"対して":-2721,"社会党":-3216};
|
||||
this.TW3__ = {"いただ":-1734,"してい":1314,"として":-4314,"につい":-5483,"にとっ":-5989,"に当た":-6247,"ので,":-727,"ので、":-727,"のもの":-600,"れから":-3752,"十二月":-2287};
|
||||
this.TW4__ = {"いう.":8576,"いう。":8576,"からな":-2348,"してい":2958,"たが,":1516,"たが、":1516,"ている":1538,"という":1349,"ました":5543,"ません":1097,"ようと":-4258,"よると":5865};
|
||||
this.UC1__ = {"A":484,"K":93,"M":645,"O":-505};
|
||||
this.UC2__ = {"A":819,"H":1059,"I":409,"M":3987,"N":5775,"O":646};
|
||||
this.UC3__ = {"A":-1370,"I":2311};
|
||||
this.UC4__ = {"A":-2643,"H":1809,"I":-1032,"K":-3450,"M":3565,"N":3876,"O":6646};
|
||||
this.UC5__ = {"H":313,"I":-1238,"K":-799,"M":539,"O":-831};
|
||||
this.UC6__ = {"H":-506,"I":-253,"K":87,"M":247,"O":-387};
|
||||
this.UP1__ = {"O":-214};
|
||||
this.UP2__ = {"B":69,"O":935};
|
||||
this.UP3__ = {"B":189};
|
||||
this.UQ1__ = {"BH":21,"BI":-12,"BK":-99,"BN":142,"BO":-56,"OH":-95,"OI":477,"OK":410,"OO":-2422};
|
||||
this.UQ2__ = {"BH":216,"BI":113,"OK":1759};
|
||||
this.UQ3__ = {"BA":-479,"BH":42,"BI":1913,"BK":-7198,"BM":3160,"BN":6427,"BO":14761,"OI":-827,"ON":-3212};
|
||||
this.UW1__ = {",":156,"、":156,"「":-463,"あ":-941,"う":-127,"が":-553,"き":121,"こ":505,"で":-201,"と":-547,"ど":-123,"に":-789,"の":-185,"は":-847,"も":-466,"や":-470,"よ":182,"ら":-292,"り":208,"れ":169,"を":-446,"ん":-137,"・":-135,"主":-402,"京":-268,"区":-912,"午":871,"国":-460,"大":561,"委":729,"市":-411,"日":-141,"理":361,"生":-408,"県":-386,"都":-718,"「":-463,"・":-135};
|
||||
this.UW2__ = {",":-829,"、":-829,"〇":892,"「":-645,"」":3145,"あ":-538,"い":505,"う":134,"お":-502,"か":1454,"が":-856,"く":-412,"こ":1141,"さ":878,"ざ":540,"し":1529,"す":-675,"せ":300,"そ":-1011,"た":188,"だ":1837,"つ":-949,"て":-291,"で":-268,"と":-981,"ど":1273,"な":1063,"に":-1764,"の":130,"は":-409,"ひ":-1273,"べ":1261,"ま":600,"も":-1263,"や":-402,"よ":1639,"り":-579,"る":-694,"れ":571,"を":-2516,"ん":2095,"ア":-587,"カ":306,"キ":568,"ッ":831,"三":-758,"不":-2150,"世":-302,"中":-968,"主":-861,"事":492,"人":-123,"会":978,"保":362,"入":548,"初":-3025,"副":-1566,"北":-3414,"区":-422,"大":-1769,"天":-865,"太":-483,"子":-1519,"学":760,"実":1023,"小":-2009,"市":-813,"年":-1060,"強":1067,"手":-1519,"揺":-1033,"政":1522,"文":-1355,"新":-1682,"日":-1815,"明":-1462,"最":-630,"朝":-1843,"本":-1650,"東":-931,"果":-665,"次":-2378,"民":-180,"気":-1740,"理":752,"発":529,"目":-1584,"相":-242,"県":-1165,"立":-763,"第":810,"米":509,"自":-1353,"行":838,"西":-744,"見":-3874,"調":1010,"議":1198,"込":3041,"開":1758,"間":-1257,"「":-645,"」":3145,"ッ":831,"ア":-587,"カ":306,"キ":568};
|
||||
this.UW3__ = {",":4889,"1":-800,"−":-1723,"、":4889,"々":-2311,"〇":5827,"」":2670,"〓":-3573,"あ":-2696,"い":1006,"う":2342,"え":1983,"お":-4864,"か":-1163,"が":3271,"く":1004,"け":388,"げ":401,"こ":-3552,"ご":-3116,"さ":-1058,"し":-395,"す":584,"せ":3685,"そ":-5228,"た":842,"ち":-521,"っ":-1444,"つ":-1081,"て":6167,"で":2318,"と":1691,"ど":-899,"な":-2788,"に":2745,"の":4056,"は":4555,"ひ":-2171,"ふ":-1798,"へ":1199,"ほ":-5516,"ま":-4384,"み":-120,"め":1205,"も":2323,"や":-788,"よ":-202,"ら":727,"り":649,"る":5905,"れ":2773,"わ":-1207,"を":6620,"ん":-518,"ア":551,"グ":1319,"ス":874,"ッ":-1350,"ト":521,"ム":1109,"ル":1591,"ロ":2201,"ン":278,"・":-3794,"一":-1619,"下":-1759,"世":-2087,"両":3815,"中":653,"主":-758,"予":-1193,"二":974,"人":2742,"今":792,"他":1889,"以":-1368,"低":811,"何":4265,"作":-361,"保":-2439,"元":4858,"党":3593,"全":1574,"公":-3030,"六":755,"共":-1880,"円":5807,"再":3095,"分":457,"初":2475,"別":1129,"前":2286,"副":4437,"力":365,"動":-949,"務":-1872,"化":1327,"北":-1038,"区":4646,"千":-2309,"午":-783,"協":-1006,"口":483,"右":1233,"各":3588,"合":-241,"同":3906,"和":-837,"員":4513,"国":642,"型":1389,"場":1219,"外":-241,"妻":2016,"学":-1356,"安":-423,"実":-1008,"家":1078,"小":-513,"少":-3102,"州":1155,"市":3197,"平":-1804,"年":2416,"広":-1030,"府":1605,"度":1452,"建":-2352,"当":-3885,"得":1905,"思":-1291,"性":1822,"戸":-488,"指":-3973,"政":-2013,"教":-1479,"数":3222,"文":-1489,"新":1764,"日":2099,"旧":5792,"昨":-661,"時":-1248,"曜":-951,"最":-937,"月":4125,"期":360,"李":3094,"村":364,"東":-805,"核":5156,"森":2438,"業":484,"氏":2613,"民":-1694,"決":-1073,"法":1868,"海":-495,"無":979,"物":461,"特":-3850,"生":-273,"用":914,"町":1215,"的":7313,"直":-1835,"省":792,"県":6293,"知":-1528,"私":4231,"税":401,"立":-960,"第":1201,"米":7767,"系":3066,"約":3663,"級":1384,"統":-4229,"総":1163,"線":1255,"者":6457,"能":725,"自":-2869,"英":785,"見":1044,"調":-562,"財":-733,"費":1777,"車":1835,"軍":1375,"込":-1504,"通":-1136,"選":-681,"郎":1026,"郡":4404,"部":1200,"金":2163,"長":421,"開":-1432,"間":1302,"関":-1282,"雨":2009,"電":-1045,"非":2066,"駅":1620,"1":-800,"」":2670,"・":-3794,"ッ":-1350,"ア":551,"グ":1319,"ス":874,"ト":521,"ム":1109,"ル":1591,"ロ":2201,"ン":278};
|
||||
this.UW4__ = {",":3930,".":3508,"―":-4841,"、":3930,"。":3508,"〇":4999,"「":1895,"」":3798,"〓":-5156,"あ":4752,"い":-3435,"う":-640,"え":-2514,"お":2405,"か":530,"が":6006,"き":-4482,"ぎ":-3821,"く":-3788,"け":-4376,"げ":-4734,"こ":2255,"ご":1979,"さ":2864,"し":-843,"じ":-2506,"す":-731,"ず":1251,"せ":181,"そ":4091,"た":5034,"だ":5408,"ち":-3654,"っ":-5882,"つ":-1659,"て":3994,"で":7410,"と":4547,"な":5433,"に":6499,"ぬ":1853,"ね":1413,"の":7396,"は":8578,"ば":1940,"ひ":4249,"び":-4134,"ふ":1345,"へ":6665,"べ":-744,"ほ":1464,"ま":1051,"み":-2082,"む":-882,"め":-5046,"も":4169,"ゃ":-2666,"や":2795,"ょ":-1544,"よ":3351,"ら":-2922,"り":-9726,"る":-14896,"れ":-2613,"ろ":-4570,"わ":-1783,"を":13150,"ん":-2352,"カ":2145,"コ":1789,"セ":1287,"ッ":-724,"ト":-403,"メ":-1635,"ラ":-881,"リ":-541,"ル":-856,"ン":-3637,"・":-4371,"ー":-11870,"一":-2069,"中":2210,"予":782,"事":-190,"井":-1768,"人":1036,"以":544,"会":950,"体":-1286,"作":530,"側":4292,"先":601,"党":-2006,"共":-1212,"内":584,"円":788,"初":1347,"前":1623,"副":3879,"力":-302,"動":-740,"務":-2715,"化":776,"区":4517,"協":1013,"参":1555,"合":-1834,"和":-681,"員":-910,"器":-851,"回":1500,"国":-619,"園":-1200,"地":866,"場":-1410,"塁":-2094,"士":-1413,"多":1067,"大":571,"子":-4802,"学":-1397,"定":-1057,"寺":-809,"小":1910,"屋":-1328,"山":-1500,"島":-2056,"川":-2667,"市":2771,"年":374,"庁":-4556,"後":456,"性":553,"感":916,"所":-1566,"支":856,"改":787,"政":2182,"教":704,"文":522,"方":-856,"日":1798,"時":1829,"最":845,"月":-9066,"木":-485,"来":-442,"校":-360,"業":-1043,"氏":5388,"民":-2716,"気":-910,"沢":-939,"済":-543,"物":-735,"率":672,"球":-1267,"生":-1286,"産":-1101,"田":-2900,"町":1826,"的":2586,"目":922,"省":-3485,"県":2997,"空":-867,"立":-2112,"第":788,"米":2937,"系":786,"約":2171,"経":1146,"統":-1169,"総":940,"線":-994,"署":749,"者":2145,"能":-730,"般":-852,"行":-792,"規":792,"警":-1184,"議":-244,"谷":-1000,"賞":730,"車":-1481,"軍":1158,"輪":-1433,"込":-3370,"近":929,"道":-1291,"選":2596,"郎":-4866,"都":1192,"野":-1100,"銀":-2213,"長":357,"間":-2344,"院":-2297,"際":-2604,"電":-878,"領":-1659,"題":-792,"館":-1984,"首":1749,"高":2120,"「":1895,"」":3798,"・":-4371,"ッ":-724,"ー":-11870,"カ":2145,"コ":1789,"セ":1287,"ト":-403,"メ":-1635,"ラ":-881,"リ":-541,"ル":-856,"ン":-3637};
|
||||
this.UW5__ = {",":465,".":-299,"1":-514,"E2":-32768,"]":-2762,"、":465,"。":-299,"「":363,"あ":1655,"い":331,"う":-503,"え":1199,"お":527,"か":647,"が":-421,"き":1624,"ぎ":1971,"く":312,"げ":-983,"さ":-1537,"し":-1371,"す":-852,"だ":-1186,"ち":1093,"っ":52,"つ":921,"て":-18,"で":-850,"と":-127,"ど":1682,"な":-787,"に":-1224,"の":-635,"は":-578,"べ":1001,"み":502,"め":865,"ゃ":3350,"ょ":854,"り":-208,"る":429,"れ":504,"わ":419,"を":-1264,"ん":327,"イ":241,"ル":451,"ン":-343,"中":-871,"京":722,"会":-1153,"党":-654,"務":3519,"区":-901,"告":848,"員":2104,"大":-1296,"学":-548,"定":1785,"嵐":-1304,"市":-2991,"席":921,"年":1763,"思":872,"所":-814,"挙":1618,"新":-1682,"日":218,"月":-4353,"査":932,"格":1356,"機":-1508,"氏":-1347,"田":240,"町":-3912,"的":-3149,"相":1319,"省":-1052,"県":-4003,"研":-997,"社":-278,"空":-813,"統":1955,"者":-2233,"表":663,"語":-1073,"議":1219,"選":-1018,"郎":-368,"長":786,"間":1191,"題":2368,"館":-689,"1":-514,"E2":-32768,"「":363,"イ":241,"ル":451,"ン":-343};
|
||||
this.UW6__ = {",":227,".":808,"1":-270,"E1":306,"、":227,"。":808,"あ":-307,"う":189,"か":241,"が":-73,"く":-121,"こ":-200,"じ":1782,"す":383,"た":-428,"っ":573,"て":-1014,"で":101,"と":-105,"な":-253,"に":-149,"の":-417,"は":-236,"も":-206,"り":187,"る":-135,"を":195,"ル":-673,"ン":-496,"一":-277,"中":201,"件":-800,"会":624,"前":302,"区":1792,"員":-1212,"委":798,"学":-960,"市":887,"広":-695,"後":535,"業":-697,"相":753,"社":-507,"福":974,"空":-822,"者":1811,"連":463,"郎":1082,"1":-270,"E1":306,"ル":-673,"ン":-496};
|
||||
|
||||
return this;
|
||||
}
|
||||
TinySegmenter.prototype.ctype_ = function(str) {
|
||||
for (var i in this.chartype_) {
|
||||
if (str.match(this.chartype_[i][0])) {
|
||||
return this.chartype_[i][1];
|
||||
}
|
||||
}
|
||||
return "O";
|
||||
}
|
||||
|
||||
TinySegmenter.prototype.ts_ = function(v) {
|
||||
if (v) { return v; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
TinySegmenter.prototype.segment = function(input) {
|
||||
if (input == null || input == undefined || input == "") {
|
||||
return [];
|
||||
}
|
||||
var result = [];
|
||||
var seg = ["B3","B2","B1"];
|
||||
var ctype = ["O","O","O"];
|
||||
var o = input.split("");
|
||||
for (i = 0; i < o.length; ++i) {
|
||||
seg.push(o[i]);
|
||||
ctype.push(this.ctype_(o[i]))
|
||||
}
|
||||
seg.push("E1");
|
||||
seg.push("E2");
|
||||
seg.push("E3");
|
||||
ctype.push("O");
|
||||
ctype.push("O");
|
||||
ctype.push("O");
|
||||
var word = seg[3];
|
||||
var p1 = "U";
|
||||
var p2 = "U";
|
||||
var p3 = "U";
|
||||
for (var i = 4; i < seg.length - 3; ++i) {
|
||||
var score = this.BIAS__;
|
||||
var w1 = seg[i-3];
|
||||
var w2 = seg[i-2];
|
||||
var w3 = seg[i-1];
|
||||
var w4 = seg[i];
|
||||
var w5 = seg[i+1];
|
||||
var w6 = seg[i+2];
|
||||
var c1 = ctype[i-3];
|
||||
var c2 = ctype[i-2];
|
||||
var c3 = ctype[i-1];
|
||||
var c4 = ctype[i];
|
||||
var c5 = ctype[i+1];
|
||||
var c6 = ctype[i+2];
|
||||
score += this.ts_(this.UP1__[p1]);
|
||||
score += this.ts_(this.UP2__[p2]);
|
||||
score += this.ts_(this.UP3__[p3]);
|
||||
score += this.ts_(this.BP1__[p1 + p2]);
|
||||
score += this.ts_(this.BP2__[p2 + p3]);
|
||||
score += this.ts_(this.UW1__[w1]);
|
||||
score += this.ts_(this.UW2__[w2]);
|
||||
score += this.ts_(this.UW3__[w3]);
|
||||
score += this.ts_(this.UW4__[w4]);
|
||||
score += this.ts_(this.UW5__[w5]);
|
||||
score += this.ts_(this.UW6__[w6]);
|
||||
score += this.ts_(this.BW1__[w2 + w3]);
|
||||
score += this.ts_(this.BW2__[w3 + w4]);
|
||||
score += this.ts_(this.BW3__[w4 + w5]);
|
||||
score += this.ts_(this.TW1__[w1 + w2 + w3]);
|
||||
score += this.ts_(this.TW2__[w2 + w3 + w4]);
|
||||
score += this.ts_(this.TW3__[w3 + w4 + w5]);
|
||||
score += this.ts_(this.TW4__[w4 + w5 + w6]);
|
||||
score += this.ts_(this.UC1__[c1]);
|
||||
score += this.ts_(this.UC2__[c2]);
|
||||
score += this.ts_(this.UC3__[c3]);
|
||||
score += this.ts_(this.UC4__[c4]);
|
||||
score += this.ts_(this.UC5__[c5]);
|
||||
score += this.ts_(this.UC6__[c6]);
|
||||
score += this.ts_(this.BC1__[c2 + c3]);
|
||||
score += this.ts_(this.BC2__[c3 + c4]);
|
||||
score += this.ts_(this.BC3__[c4 + c5]);
|
||||
score += this.ts_(this.TC1__[c1 + c2 + c3]);
|
||||
score += this.ts_(this.TC2__[c2 + c3 + c4]);
|
||||
score += this.ts_(this.TC3__[c3 + c4 + c5]);
|
||||
score += this.ts_(this.TC4__[c4 + c5 + c6]);
|
||||
// score += this.ts_(this.TC5__[c4 + c5 + c6]);
|
||||
score += this.ts_(this.UQ1__[p1 + c1]);
|
||||
score += this.ts_(this.UQ2__[p2 + c2]);
|
||||
score += this.ts_(this.UQ3__[p3 + c3]);
|
||||
score += this.ts_(this.BQ1__[p2 + c2 + c3]);
|
||||
score += this.ts_(this.BQ2__[p2 + c3 + c4]);
|
||||
score += this.ts_(this.BQ3__[p3 + c2 + c3]);
|
||||
score += this.ts_(this.BQ4__[p3 + c3 + c4]);
|
||||
score += this.ts_(this.TQ1__[p2 + c1 + c2 + c3]);
|
||||
score += this.ts_(this.TQ2__[p2 + c2 + c3 + c4]);
|
||||
score += this.ts_(this.TQ3__[p3 + c1 + c2 + c3]);
|
||||
score += this.ts_(this.TQ4__[p3 + c2 + c3 + c4]);
|
||||
var p = "O";
|
||||
if (score > 0) {
|
||||
result.push(word);
|
||||
word = "";
|
||||
p = "B";
|
||||
}
|
||||
p1 = p2;
|
||||
p2 = p3;
|
||||
p3 = p;
|
||||
word += seg[i];
|
||||
}
|
||||
result.push(word);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
lunr.TinySegmenter = TinySegmenter;
|
||||
};
|
||||
|
||||
}));
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["src/assets/stylesheets/palette/_scheme.scss","../../../src/assets/stylesheets/palette.scss","src/assets/stylesheets/palette/_accent.scss","src/assets/stylesheets/palette/_primary.scss","src/assets/stylesheets/utilities/_break.scss"],"names":[],"mappings":"AA2BA,cAGE,6BAKE,YAAA,CAGA,mDAAA,CACA,6DAAA,CACA,+DAAA,CACA,gEAAA,CACA,mDAAA,CACA,6DAAA,CACA,+DAAA,CACA,gEAAA,CAGA,gDAAA,CACA,gDAAA,CAGA,4BAAA,CACA,iCAAA,CACA,kCAAA,CACA,mCAAA,CACA,mCAAA,CACA,kCAAA,CACA,iCAAA,CACA,+CAAA,CACA,6DAAA,CACA,gEAAA,CACA,4DAAA,CACA,4DAAA,CACA,6DAAA,CAGA,6CAAA,CAGA,+CAAA,CAGA,iCAAA,CAGA,uDAAA,CACA,6DAAA,CACA,2DAAA,CAGA,yDAAA,CAGA,mDAAA,CACA,mDAAA,CAGA,qDAAA,CACA,wDAAA,CAGA,0DAAA,CAKA,8DAAA,CAKA,0DCxDF,CD6DE,kHAEE,YC3DJ,CD+DE,gHAEE,eC7DJ,CDoFE,yDACE,4BClFJ,CDiFE,2DACE,4BC/EJ,CD8EE,gEACE,4BC5EJ,CD2EE,2DACE,4BCzEJ,CDwEE,yDACE,4BCtEJ,CDqEE,0DACE,4BCnEJ,CDkEE,gEACE,4BChEJ,CD+DE,0DACE,4BC7DJ,CD4DE,2OACE,4BCjDJ,CDwDA,+FAGE,iCCtDF,CACF,CCjDE,2BACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCD6CN,CCvDE,4BACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCDoDN,CC9DE,8BACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCD2DN,CCrEE,mCACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCDkEN,CC5EE,8BACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCDyEN,CCnFE,4BACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCDgFN,CC1FE,kCACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCDuFN,CCjGE,4BACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCD8FN,CCxGE,4BACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCDqGN,CC/GE,6BACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCD4GN,CCtHE,mCACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCDmHN,CC7HE,4BACE,4BAAA,CACA,2CAAA,CAIE,8BAAA,CACA,qCD6HN,CCpIE,8BACE,4BAAA,CACA,2CAAA,CAIE,8BAAA,CACA,qCDoIN,CC3IE,6BACE,yBAAA,CACA,2CAAA,CAIE,8BAAA,CACA,qCD2IN,CClJE,8BACE,4BAAA,CACA,2CAAA,CAIE,8BAAA,CACA,qCDkJN,CCzJE,mCACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCDsJN,CE3JE,4BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFwJN,CEnKE,6BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFgKN,CE3KE,+BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFwKN,CEnLE,oCACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFgLN,CE3LE,+BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFwLN,CEnME,6BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFgMN,CE3ME,mCACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFwMN,CEnNE,6BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFgNN,CE3NE,6BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFwNN,CEnOE,8BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFgON,CE3OE,oCACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFwON,CEnPE,6BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAIE,+BAAA,CACA,sCFmPN,CE3PE,+BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAIE,+BAAA,CACA,sCF2PN,CEnQE,8BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAIE,+BAAA,CACA,sCFmQN,CE3QE,+BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAIE,+BAAA,CACA,sCF2QN,CEnRE,oCACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFgRN,CE3RE,8BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFwRN,CEnSE,6BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCAAA,CAKA,4BF4RN,CE5SE,kCACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCAAA,CAKA,4BFqSN,CEtRE,sEACE,4BFyRJ,CE1RE,+DACE,4BF6RJ,CE9RE,iEACE,4BFiSJ,CElSE,gEACE,4BFqSJ,CEtSE,iEACE,4BFySJ,CEhSA,8BACE,0BAAA,CACA,sCAAA,CACA,qCAAA,CACA,+BAAA,CACA,sCAAA,CAGA,4BFiSF,CE9RE,yCACE,+BFgSJ,CE7RI,kDAEE,0CAAA,CACA,sCAAA,CAFA,UFiSN,CG7MI,mCD1EA,+CACE,0BF0RJ,CEvRI,qDACE,0BFyRN,CEpRE,iEACE,eFsRJ,CACF,CGxNI,sCDvDA,uCACE,oCFkRJ,CACF,CEzQA,8BACE,0BAAA,CACA,sCAAA,CACA,gCAAA,CACA,0BAAA,CACA,sCAAA,CAGA,4BF0QF,CEvQE,yCACE,+BFyQJ,CEtQI,kDAEE,0CAAA,CACA,sCAAA,CAFA,UF0QN,CEnQE,yCACE,qBFqQJ,CG9NI,wCDhCA,8CACE,0BFiQJ,CACF,CGtPI,mCDJA,+CACE,0BF6PJ,CE1PI,qDACE,0BF4PN,CACF,CG3OI,wCDTA,iFACE,qBFuPJ,CACF,CGnQI,sCDmBA,uCACE,qBFmPJ,CACF","file":"palette.css"}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.0 KiB |
@@ -1,443 +0,0 @@
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en" class="no-js">
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="next" href="sophia_syntax/">
|
||||
|
||||
<link rel="icon" href="favicon.png">
|
||||
<meta name="generator" content="mkdocs-1.4.2, mkdocs-material-9.0.9">
|
||||
|
||||
|
||||
|
||||
<title>æternity Sophia Language</title>
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="assets/stylesheets/main.0d440cfe.min.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="assets/stylesheets/palette.2505c338.min.css">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
|
||||
<style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
|
||||
|
||||
|
||||
|
||||
<script>__md_scope=new URL(".",location),__md_hash=e=>[...e].reduce((e,_)=>(e<<5)-e+_.charCodeAt(0),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<body dir="ltr" data-md-color-scheme="default" data-md-color-primary="pink" data-md-color-accent="pink">
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&"object"==typeof palette.color)for(var key of Object.keys(palette.color))document.body.setAttribute("data-md-color-"+key,palette.color[key])</script>
|
||||
|
||||
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
|
||||
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
|
||||
<label class="md-overlay" for="__drawer"></label>
|
||||
<div data-md-component="skip">
|
||||
|
||||
|
||||
<a href="#introduction" class="md-skip">
|
||||
Skip to content
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<div data-md-component="announce">
|
||||
|
||||
</div>
|
||||
|
||||
<div data-md-component="outdated" hidden>
|
||||
|
||||
<aside class="md-banner md-banner--warning">
|
||||
<div class="md-banner__inner md-grid md-typeset">
|
||||
|
||||
You're not viewing the latest version.
|
||||
<a href="../.">
|
||||
<strong>Click here to go to latest.</strong>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<script>var el=document.querySelector("[data-md-component=outdated]"),outdated=__md_get("__outdated",sessionStorage);!0===outdated&&el&&(el.hidden=!1)</script>
|
||||
</aside>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="." title="æternity Sophia Language" class="md-header__button md-logo" aria-label="æternity Sophia Language" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54Z"/></svg>
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3V6m0 5h18v2H3v-2m0 5h18v2H3v-2Z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
æternity Sophia Language
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Introduction
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
|
||||
|
||||
<input class="md-option" data-md-color-media="" data-md-color-scheme="default" data-md-color-primary="pink" data-md-color-accent="pink" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_1">
|
||||
|
||||
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_2" hidden>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="m17.75 4.09-2.53 1.94.91 3.06-2.63-1.81-2.63 1.81.91-3.06-2.53-1.94L12.44 4l1.06-3 1.06 3 3.19.09m3.5 6.91-1.64 1.25.59 1.98-1.7-1.17-1.7 1.17.59-1.98L15.75 11l2.06-.05L18.5 9l.69 1.95 2.06.05m-2.28 4.95c.83-.08 1.72 1.1 1.19 1.85-.32.45-.66.87-1.08 1.27C15.17 23 8.84 23 4.94 19.07c-3.91-3.9-3.91-10.24 0-14.14.4-.4.82-.76 1.27-1.08.75-.53 1.93.36 1.85 1.19-.27 2.86.69 5.83 2.89 8.02a9.96 9.96 0 0 0 8.02 2.89m-1.64 2.02a12.08 12.08 0 0 1-7.8-3.47c-2.17-2.19-3.33-5-3.49-7.82-2.81 3.14-2.7 7.96.31 10.98 3.02 3.01 7.84 3.12 10.98.31Z"/></svg>
|
||||
</label>
|
||||
|
||||
|
||||
|
||||
<input class="md-option" data-md-color-media="" data-md-color-scheme="slate" data-md-color-primary="pink" data-md-color-accent="pink" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_2">
|
||||
|
||||
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_1" hidden>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 7a5 5 0 0 1 5 5 5 5 0 0 1-5 5 5 5 0 0 1-5-5 5 5 0 0 1 5-5m0 2a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3m0-7 2.39 3.42C13.65 5.15 12.84 5 12 5c-.84 0-1.65.15-2.39.42L12 2M3.34 7l4.16-.35A7.2 7.2 0 0 0 5.94 8.5c-.44.74-.69 1.5-.83 2.29L3.34 7m.02 10 1.76-3.77a7.131 7.131 0 0 0 2.38 4.14L3.36 17M20.65 7l-1.77 3.79a7.023 7.023 0 0 0-2.38-4.15l4.15.36m-.01 10-4.14.36c.59-.51 1.12-1.14 1.54-1.86.42-.73.69-1.5.83-2.29L20.64 17M12 22l-2.41-3.44c.74.27 1.55.44 2.41.44.82 0 1.63-.17 2.37-.44L12 22Z"/></svg>
|
||||
</label>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
|
||||
<label class="md-search__icon md-icon" for="__search">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"/></svg>
|
||||
</label>
|
||||
<nav class="md-search__options" aria-label="Search">
|
||||
|
||||
<a href="javascript:void(0)" class="md-search__icon md-icon" title="Share" aria-label="Share" data-clipboard data-clipboard-text="" data-md-component="search-share" tabindex="-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7 0-.24-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.15c-.05.21-.08.43-.08.66 0 1.61 1.31 2.91 2.92 2.91 1.61 0 2.92-1.3 2.92-2.91A2.92 2.92 0 0 0 18 16.08Z"/></svg>
|
||||
</a>
|
||||
|
||||
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41Z"/></svg>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<div class="md-search__suggest" data-md-component="search-suggest"></div>
|
||||
|
||||
</form>
|
||||
<div class="md-search__output">
|
||||
<div class="md-search__scrollwrap" data-md-scrollfix>
|
||||
<div class="md-search-result" data-md-component="search-result">
|
||||
<div class="md-search-result__meta">
|
||||
Initializing search
|
||||
</div>
|
||||
<ol class="md-search-result__list" role="presentation"></ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://github.com/aeternity/aesophia" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
GitHub
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
</header>
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
|
||||
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
|
||||
<div class="md-sidebar__scrollwrap">
|
||||
<div class="md-sidebar__inner">
|
||||
|
||||
|
||||
|
||||
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
|
||||
<label class="md-nav__title" for="__drawer">
|
||||
<a href="." title="æternity Sophia Language" class="md-nav__button md-logo" aria-label="æternity Sophia Language" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54Z"/></svg>
|
||||
|
||||
</a>
|
||||
æternity Sophia Language
|
||||
</label>
|
||||
|
||||
<div class="md-nav__source">
|
||||
<a href="https://github.com/aeternity/aesophia" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
GitHub
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ul class="md-nav__list" data-md-scrollfix>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item md-nav__item--active">
|
||||
|
||||
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="." class="md-nav__link md-nav__link--active">
|
||||
Introduction
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="sophia_syntax/" class="md-nav__link">
|
||||
Syntax
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="sophia_features/" class="md-nav__link">
|
||||
Features
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="sophia_stdlib/" class="md-nav__link">
|
||||
Standard library
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="sophia_examples/" class="md-nav__link">
|
||||
Contract examples
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="CHANGELOG/" class="md-nav__link">
|
||||
Changelog
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
|
||||
<div class="md-sidebar__scrollwrap">
|
||||
<div class="md-sidebar__inner">
|
||||
|
||||
|
||||
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-content" data-md-component="content">
|
||||
<article class="md-content__inner md-typeset">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h1 id="introduction">Introduction</h1>
|
||||
<p>Sophia is a functional language designed for smart contract development. It is strongly typed and has
|
||||
restricted mutable state.</p>
|
||||
<p>Sophia is customized for smart contracts, which can be published
|
||||
to a blockchain. Thus some features of conventional
|
||||
languages, such as floating point arithmetic, are not present in Sophia, and
|
||||
some <a href="https://aeternity.com">æternity blockchain</a> specific primitives, constructions and types have been added.</p>
|
||||
<div class="admonition note">
|
||||
<p class="admonition-title">Note</p>
|
||||
<ul>
|
||||
<li>For rapid prototyping of smart contracts check out <a href="https://studio.aepps.com/">AEstudio</a>!</li>
|
||||
<li>For playing around and diving deeper into the language itself check out the <a href="https://repl.aeternity.io/">REPL</a>!</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</article>
|
||||
</div>
|
||||
|
||||
|
||||
<script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var tab,labels=set.querySelector(".tabbed-labels");for(tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script>
|
||||
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<footer class="md-footer">
|
||||
|
||||
<div class="md-footer-meta md-typeset">
|
||||
<div class="md-footer-meta__inner md-grid">
|
||||
<div class="md-copyright">
|
||||
|
||||
|
||||
Made with
|
||||
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
|
||||
Material for MkDocs
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
<div class="md-dialog" data-md-component="dialog">
|
||||
<div class="md-dialog__inner md-typeset"></div>
|
||||
</div>
|
||||
|
||||
<script id="__config" type="application/json">{"base": ".", "features": ["content.tabs.link", "search.highlight", "search.share", "search.suggest"], "search": "assets/javascripts/workers/search.db81ec45.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": {"provider": "mike"}}</script>
|
||||
|
||||
|
||||
<script src="assets/javascripts/bundle.a00a7c5e.min.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because one or more lines are too long
@@ -1,48 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>None</loc>
|
||||
<lastmod>2023-11-27</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
</url>
|
||||
<url>
|
||||
<loc>None</loc>
|
||||
<lastmod>2023-11-27</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
</url>
|
||||
<url>
|
||||
<loc>None</loc>
|
||||
<lastmod>2023-11-27</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
</url>
|
||||
<url>
|
||||
<loc>None</loc>
|
||||
<lastmod>2023-11-27</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
</url>
|
||||
<url>
|
||||
<loc>None</loc>
|
||||
<lastmod>2023-11-27</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
</url>
|
||||
<url>
|
||||
<loc>None</loc>
|
||||
<lastmod>2023-11-27</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
</url>
|
||||
<url>
|
||||
<loc>None</loc>
|
||||
<lastmod>2023-11-27</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
</url>
|
||||
<url>
|
||||
<loc>None</loc>
|
||||
<lastmod>2023-11-27</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
</url>
|
||||
<url>
|
||||
<loc>None</loc>
|
||||
<lastmod>2023-11-27</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
</url>
|
||||
</urlset>
|
||||
Binary file not shown.
@@ -1,411 +0,0 @@
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en" class="no-js">
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="icon" href="../favicon.png">
|
||||
<meta name="generator" content="mkdocs-1.4.2, mkdocs-material-9.0.9">
|
||||
|
||||
|
||||
|
||||
<title>Sophia - æternity Sophia Language</title>
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../assets/stylesheets/main.0d440cfe.min.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../assets/stylesheets/palette.2505c338.min.css">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
|
||||
<style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
|
||||
|
||||
|
||||
|
||||
<script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce((e,_)=>(e<<5)-e+_.charCodeAt(0),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<body dir="ltr" data-md-color-scheme="default" data-md-color-primary="pink" data-md-color-accent="pink">
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&"object"==typeof palette.color)for(var key of Object.keys(palette.color))document.body.setAttribute("data-md-color-"+key,palette.color[key])</script>
|
||||
|
||||
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
|
||||
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
|
||||
<label class="md-overlay" for="__drawer"></label>
|
||||
<div data-md-component="skip">
|
||||
|
||||
</div>
|
||||
<div data-md-component="announce">
|
||||
|
||||
</div>
|
||||
|
||||
<div data-md-component="outdated" hidden>
|
||||
|
||||
<aside class="md-banner md-banner--warning">
|
||||
<div class="md-banner__inner md-grid md-typeset">
|
||||
|
||||
You're not viewing the latest version.
|
||||
<a href="../..">
|
||||
<strong>Click here to go to latest.</strong>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<script>var el=document.querySelector("[data-md-component=outdated]"),outdated=__md_get("__outdated",sessionStorage);!0===outdated&&el&&(el.hidden=!1)</script>
|
||||
</aside>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href=".." title="æternity Sophia Language" class="md-header__button md-logo" aria-label="æternity Sophia Language" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54Z"/></svg>
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3V6m0 5h18v2H3v-2m0 5h18v2H3v-2Z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
æternity Sophia Language
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Sophia
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
|
||||
|
||||
<input class="md-option" data-md-color-media="" data-md-color-scheme="default" data-md-color-primary="pink" data-md-color-accent="pink" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_1">
|
||||
|
||||
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_2" hidden>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="m17.75 4.09-2.53 1.94.91 3.06-2.63-1.81-2.63 1.81.91-3.06-2.53-1.94L12.44 4l1.06-3 1.06 3 3.19.09m3.5 6.91-1.64 1.25.59 1.98-1.7-1.17-1.7 1.17.59-1.98L15.75 11l2.06-.05L18.5 9l.69 1.95 2.06.05m-2.28 4.95c.83-.08 1.72 1.1 1.19 1.85-.32.45-.66.87-1.08 1.27C15.17 23 8.84 23 4.94 19.07c-3.91-3.9-3.91-10.24 0-14.14.4-.4.82-.76 1.27-1.08.75-.53 1.93.36 1.85 1.19-.27 2.86.69 5.83 2.89 8.02a9.96 9.96 0 0 0 8.02 2.89m-1.64 2.02a12.08 12.08 0 0 1-7.8-3.47c-2.17-2.19-3.33-5-3.49-7.82-2.81 3.14-2.7 7.96.31 10.98 3.02 3.01 7.84 3.12 10.98.31Z"/></svg>
|
||||
</label>
|
||||
|
||||
|
||||
|
||||
<input class="md-option" data-md-color-media="" data-md-color-scheme="slate" data-md-color-primary="pink" data-md-color-accent="pink" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_2">
|
||||
|
||||
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_1" hidden>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 7a5 5 0 0 1 5 5 5 5 0 0 1-5 5 5 5 0 0 1-5-5 5 5 0 0 1 5-5m0 2a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3m0-7 2.39 3.42C13.65 5.15 12.84 5 12 5c-.84 0-1.65.15-2.39.42L12 2M3.34 7l4.16-.35A7.2 7.2 0 0 0 5.94 8.5c-.44.74-.69 1.5-.83 2.29L3.34 7m.02 10 1.76-3.77a7.131 7.131 0 0 0 2.38 4.14L3.36 17M20.65 7l-1.77 3.79a7.023 7.023 0 0 0-2.38-4.15l4.15.36m-.01 10-4.14.36c.59-.51 1.12-1.14 1.54-1.86.42-.73.69-1.5.83-2.29L20.64 17M12 22l-2.41-3.44c.74.27 1.55.44 2.41.44.82 0 1.63-.17 2.37-.44L12 22Z"/></svg>
|
||||
</label>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
|
||||
<label class="md-search__icon md-icon" for="__search">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"/></svg>
|
||||
</label>
|
||||
<nav class="md-search__options" aria-label="Search">
|
||||
|
||||
<a href="javascript:void(0)" class="md-search__icon md-icon" title="Share" aria-label="Share" data-clipboard data-clipboard-text="" data-md-component="search-share" tabindex="-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7 0-.24-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.15c-.05.21-.08.43-.08.66 0 1.61 1.31 2.91 2.92 2.91 1.61 0 2.92-1.3 2.92-2.91A2.92 2.92 0 0 0 18 16.08Z"/></svg>
|
||||
</a>
|
||||
|
||||
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41Z"/></svg>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<div class="md-search__suggest" data-md-component="search-suggest"></div>
|
||||
|
||||
</form>
|
||||
<div class="md-search__output">
|
||||
<div class="md-search__scrollwrap" data-md-scrollfix>
|
||||
<div class="md-search-result" data-md-component="search-result">
|
||||
<div class="md-search-result__meta">
|
||||
Initializing search
|
||||
</div>
|
||||
<ol class="md-search-result__list" role="presentation"></ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://github.com/aeternity/aesophia" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
GitHub
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
</header>
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
|
||||
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
|
||||
<div class="md-sidebar__scrollwrap">
|
||||
<div class="md-sidebar__inner">
|
||||
|
||||
|
||||
|
||||
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
|
||||
<label class="md-nav__title" for="__drawer">
|
||||
<a href=".." title="æternity Sophia Language" class="md-nav__button md-logo" aria-label="æternity Sophia Language" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54Z"/></svg>
|
||||
|
||||
</a>
|
||||
æternity Sophia Language
|
||||
</label>
|
||||
|
||||
<div class="md-nav__source">
|
||||
<a href="https://github.com/aeternity/aesophia" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
GitHub
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ul class="md-nav__list" data-md-scrollfix>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href=".." class="md-nav__link">
|
||||
Introduction
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../sophia_syntax/" class="md-nav__link">
|
||||
Syntax
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../sophia_features/" class="md-nav__link">
|
||||
Features
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../sophia_stdlib/" class="md-nav__link">
|
||||
Standard library
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../sophia_examples/" class="md-nav__link">
|
||||
Contract examples
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../CHANGELOG/" class="md-nav__link">
|
||||
Changelog
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
|
||||
<div class="md-sidebar__scrollwrap">
|
||||
<div class="md-sidebar__inner">
|
||||
|
||||
|
||||
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
|
||||
|
||||
|
||||
|
||||
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-content" data-md-component="content">
|
||||
<article class="md-content__inner md-typeset">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>This file has been moved <a href="../sophia_features/">here</a></p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</article>
|
||||
</div>
|
||||
|
||||
|
||||
<script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var tab,labels=set.querySelector(".tabbed-labels");for(tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script>
|
||||
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<footer class="md-footer">
|
||||
|
||||
<div class="md-footer-meta md-typeset">
|
||||
<div class="md-footer-meta__inner md-grid">
|
||||
<div class="md-copyright">
|
||||
|
||||
|
||||
Made with
|
||||
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
|
||||
Material for MkDocs
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
<div class="md-dialog" data-md-component="dialog">
|
||||
<div class="md-dialog__inner md-typeset"></div>
|
||||
</div>
|
||||
|
||||
<script id="__config" type="application/json">{"base": "..", "features": ["content.tabs.link", "search.highlight", "search.share", "search.suggest"], "search": "../assets/javascripts/workers/search.db81ec45.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": {"provider": "mike"}}</script>
|
||||
|
||||
|
||||
<script src="../assets/javascripts/bundle.a00a7c5e.min.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,564 +0,0 @@
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en" class="no-js">
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="prev" href="../sophia_stdlib/">
|
||||
|
||||
|
||||
<link rel="next" href="../CHANGELOG/">
|
||||
|
||||
<link rel="icon" href="../favicon.png">
|
||||
<meta name="generator" content="mkdocs-1.4.2, mkdocs-material-9.0.9">
|
||||
|
||||
|
||||
|
||||
<title>Contract examples - æternity Sophia Language</title>
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../assets/stylesheets/main.0d440cfe.min.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../assets/stylesheets/palette.2505c338.min.css">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
|
||||
<style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
|
||||
|
||||
|
||||
|
||||
<script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce((e,_)=>(e<<5)-e+_.charCodeAt(0),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<body dir="ltr" data-md-color-scheme="default" data-md-color-primary="pink" data-md-color-accent="pink">
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&"object"==typeof palette.color)for(var key of Object.keys(palette.color))document.body.setAttribute("data-md-color-"+key,palette.color[key])</script>
|
||||
|
||||
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
|
||||
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
|
||||
<label class="md-overlay" for="__drawer"></label>
|
||||
<div data-md-component="skip">
|
||||
|
||||
|
||||
<a href="#contract-examples" class="md-skip">
|
||||
Skip to content
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<div data-md-component="announce">
|
||||
|
||||
</div>
|
||||
|
||||
<div data-md-component="outdated" hidden>
|
||||
|
||||
<aside class="md-banner md-banner--warning">
|
||||
<div class="md-banner__inner md-grid md-typeset">
|
||||
|
||||
You're not viewing the latest version.
|
||||
<a href="../..">
|
||||
<strong>Click here to go to latest.</strong>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<script>var el=document.querySelector("[data-md-component=outdated]"),outdated=__md_get("__outdated",sessionStorage);!0===outdated&&el&&(el.hidden=!1)</script>
|
||||
</aside>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href=".." title="æternity Sophia Language" class="md-header__button md-logo" aria-label="æternity Sophia Language" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54Z"/></svg>
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3V6m0 5h18v2H3v-2m0 5h18v2H3v-2Z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
æternity Sophia Language
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Contract examples
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
|
||||
|
||||
<input class="md-option" data-md-color-media="" data-md-color-scheme="default" data-md-color-primary="pink" data-md-color-accent="pink" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_1">
|
||||
|
||||
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_2" hidden>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="m17.75 4.09-2.53 1.94.91 3.06-2.63-1.81-2.63 1.81.91-3.06-2.53-1.94L12.44 4l1.06-3 1.06 3 3.19.09m3.5 6.91-1.64 1.25.59 1.98-1.7-1.17-1.7 1.17.59-1.98L15.75 11l2.06-.05L18.5 9l.69 1.95 2.06.05m-2.28 4.95c.83-.08 1.72 1.1 1.19 1.85-.32.45-.66.87-1.08 1.27C15.17 23 8.84 23 4.94 19.07c-3.91-3.9-3.91-10.24 0-14.14.4-.4.82-.76 1.27-1.08.75-.53 1.93.36 1.85 1.19-.27 2.86.69 5.83 2.89 8.02a9.96 9.96 0 0 0 8.02 2.89m-1.64 2.02a12.08 12.08 0 0 1-7.8-3.47c-2.17-2.19-3.33-5-3.49-7.82-2.81 3.14-2.7 7.96.31 10.98 3.02 3.01 7.84 3.12 10.98.31Z"/></svg>
|
||||
</label>
|
||||
|
||||
|
||||
|
||||
<input class="md-option" data-md-color-media="" data-md-color-scheme="slate" data-md-color-primary="pink" data-md-color-accent="pink" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_2">
|
||||
|
||||
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_1" hidden>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 7a5 5 0 0 1 5 5 5 5 0 0 1-5 5 5 5 0 0 1-5-5 5 5 0 0 1 5-5m0 2a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3m0-7 2.39 3.42C13.65 5.15 12.84 5 12 5c-.84 0-1.65.15-2.39.42L12 2M3.34 7l4.16-.35A7.2 7.2 0 0 0 5.94 8.5c-.44.74-.69 1.5-.83 2.29L3.34 7m.02 10 1.76-3.77a7.131 7.131 0 0 0 2.38 4.14L3.36 17M20.65 7l-1.77 3.79a7.023 7.023 0 0 0-2.38-4.15l4.15.36m-.01 10-4.14.36c.59-.51 1.12-1.14 1.54-1.86.42-.73.69-1.5.83-2.29L20.64 17M12 22l-2.41-3.44c.74.27 1.55.44 2.41.44.82 0 1.63-.17 2.37-.44L12 22Z"/></svg>
|
||||
</label>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
|
||||
<label class="md-search__icon md-icon" for="__search">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"/></svg>
|
||||
</label>
|
||||
<nav class="md-search__options" aria-label="Search">
|
||||
|
||||
<a href="javascript:void(0)" class="md-search__icon md-icon" title="Share" aria-label="Share" data-clipboard data-clipboard-text="" data-md-component="search-share" tabindex="-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7 0-.24-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.15c-.05.21-.08.43-.08.66 0 1.61 1.31 2.91 2.92 2.91 1.61 0 2.92-1.3 2.92-2.91A2.92 2.92 0 0 0 18 16.08Z"/></svg>
|
||||
</a>
|
||||
|
||||
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41Z"/></svg>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<div class="md-search__suggest" data-md-component="search-suggest"></div>
|
||||
|
||||
</form>
|
||||
<div class="md-search__output">
|
||||
<div class="md-search__scrollwrap" data-md-scrollfix>
|
||||
<div class="md-search-result" data-md-component="search-result">
|
||||
<div class="md-search-result__meta">
|
||||
Initializing search
|
||||
</div>
|
||||
<ol class="md-search-result__list" role="presentation"></ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://github.com/aeternity/aesophia" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
GitHub
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
</header>
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
|
||||
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
|
||||
<div class="md-sidebar__scrollwrap">
|
||||
<div class="md-sidebar__inner">
|
||||
|
||||
|
||||
|
||||
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
|
||||
<label class="md-nav__title" for="__drawer">
|
||||
<a href=".." title="æternity Sophia Language" class="md-nav__button md-logo" aria-label="æternity Sophia Language" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54Z"/></svg>
|
||||
|
||||
</a>
|
||||
æternity Sophia Language
|
||||
</label>
|
||||
|
||||
<div class="md-nav__source">
|
||||
<a href="https://github.com/aeternity/aesophia" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
GitHub
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ul class="md-nav__list" data-md-scrollfix>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href=".." class="md-nav__link">
|
||||
Introduction
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../sophia_syntax/" class="md-nav__link">
|
||||
Syntax
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../sophia_features/" class="md-nav__link">
|
||||
Features
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../sophia_stdlib/" class="md-nav__link">
|
||||
Standard library
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item md-nav__item--active">
|
||||
|
||||
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-nav__link md-nav__link--active" for="__toc">
|
||||
Contract examples
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
</label>
|
||||
|
||||
<a href="./" class="md-nav__link md-nav__link--active">
|
||||
Contract examples
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-nav__title" for="__toc">
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
Table of contents
|
||||
</label>
|
||||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#crowdfunding" class="md-nav__link">
|
||||
Crowdfunding
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#repositories" class="md-nav__link">
|
||||
Repositories
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
</nav>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../CHANGELOG/" class="md-nav__link">
|
||||
Changelog
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
|
||||
<div class="md-sidebar__scrollwrap">
|
||||
<div class="md-sidebar__inner">
|
||||
|
||||
|
||||
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-nav__title" for="__toc">
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
Table of contents
|
||||
</label>
|
||||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#crowdfunding" class="md-nav__link">
|
||||
Crowdfunding
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#repositories" class="md-nav__link">
|
||||
Repositories
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-content" data-md-component="content">
|
||||
<article class="md-content__inner md-typeset">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h1 id="contract-examples">Contract examples</h1>
|
||||
<h2 id="crowdfunding">Crowdfunding</h2>
|
||||
<div class="highlight"><pre><span></span><code><span class="cm">/*</span>
|
||||
<span class="cm"> * A simple crowd-funding example</span>
|
||||
<span class="cm"> */</span>
|
||||
<span class="k">contract</span><span class="w"> </span><span class="nf">FundMe</span><span class="w"> </span><span class="ow">=</span>
|
||||
|
||||
<span class="w"> </span><span class="k">record</span><span class="w"> </span><span class="n">spend_args</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">recipient</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">address</span><span class="p">,</span>
|
||||
<span class="w"> </span><span class="n">amount</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="p">}</span>
|
||||
|
||||
<span class="w"> </span><span class="k">record</span><span class="w"> </span><span class="nb">state</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">contributions</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">map</span><span class="p">(</span><span class="kt">address</span><span class="p">,</span><span class="w"> </span><span class="kt">int</span><span class="p">),</span>
|
||||
<span class="w"> </span><span class="n">total</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">int</span><span class="p">,</span>
|
||||
<span class="w"> </span><span class="n">beneficiary</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">address</span><span class="p">,</span>
|
||||
<span class="w"> </span><span class="n">deadline</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">int</span><span class="p">,</span>
|
||||
<span class="w"> </span><span class="n">goal</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="p">}</span>
|
||||
|
||||
<span class="w"> </span><span class="k">stateful</span><span class="w"> </span><span class="k">function</span><span class="w"> </span><span class="n">spend</span><span class="p">(</span><span class="n">args</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="n">spend_args</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span>
|
||||
<span class="w"> </span><span class="nc">Chain</span><span class="p">.</span><span class="n">spend</span><span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">recipient</span><span class="p">,</span><span class="w"> </span><span class="n">args</span><span class="p">.</span><span class="n">amount</span><span class="p">)</span>
|
||||
|
||||
<span class="w"> </span><span class="k">entrypoint</span><span class="w"> </span><span class="n">init</span><span class="p">(</span><span class="n">beneficiary</span><span class="p">,</span><span class="w"> </span><span class="n">deadline</span><span class="p">,</span><span class="w"> </span><span class="n">goal</span><span class="p">)</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="nb">state</span><span class="w"> </span><span class="ow">=</span>
|
||||
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">contributions</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">{},</span>
|
||||
<span class="w"> </span><span class="n">beneficiary</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">beneficiary</span><span class="p">,</span>
|
||||
<span class="w"> </span><span class="n">deadline</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">deadline</span><span class="p">,</span>
|
||||
<span class="w"> </span><span class="n">total</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span>
|
||||
<span class="w"> </span><span class="n">goal</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">goal</span><span class="w"> </span><span class="p">}</span>
|
||||
|
||||
<span class="w"> </span><span class="k">function</span><span class="w"> </span><span class="n">is_contributor</span><span class="p">(</span><span class="n">addr</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span>
|
||||
<span class="w"> </span><span class="nc">Map</span><span class="p">.</span><span class="n">member</span><span class="p">(</span><span class="n">addr</span><span class="p">,</span><span class="w"> </span><span class="nb">state</span><span class="p">.</span><span class="n">contributions</span><span class="p">)</span>
|
||||
|
||||
<span class="w"> </span><span class="k">stateful</span><span class="w"> </span><span class="k">entrypoint</span><span class="w"> </span><span class="n">contribute</span><span class="p">()</span><span class="w"> </span><span class="ow">=</span>
|
||||
<span class="w"> </span><span class="k">if</span><span class="p">(</span><span class="nc">Chain</span><span class="p">.</span><span class="n">block_height</span><span class="w"> </span><span class="ow">>=</span><span class="w"> </span><span class="nb">state</span><span class="p">.</span><span class="n">deadline</span><span class="p">)</span>
|
||||
<span class="w"> </span><span class="n">spend</span><span class="p">({</span><span class="w"> </span><span class="n">recipient</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nc">Call</span><span class="p">.</span><span class="n">caller</span><span class="p">,</span><span class="w"> </span><span class="n">amount</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nc">Call</span><span class="p">.</span><span class="n">value</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="c1">// Refund money</span>
|
||||
<span class="w"> </span><span class="kc">false</span>
|
||||
<span class="w"> </span><span class="k">else</span>
|
||||
<span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">amount</span><span class="w"> </span><span class="ow">=</span>
|
||||
<span class="w"> </span><span class="k">switch</span><span class="p">(</span><span class="nc">Map</span><span class="p">.</span><span class="n">lookup</span><span class="p">(</span><span class="nc">Call</span><span class="p">.</span><span class="n">caller</span><span class="p">,</span><span class="w"> </span><span class="nb">state</span><span class="p">.</span><span class="n">contributions</span><span class="p">))</span>
|
||||
<span class="w"> </span><span class="nf">None</span><span class="w"> </span><span class="ow">=></span><span class="w"> </span><span class="nc">Call</span><span class="p">.</span><span class="n">value</span>
|
||||
<span class="w"> </span><span class="nf">Some</span><span class="p">(</span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="ow">=></span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="ow">+</span><span class="w"> </span><span class="nc">Call</span><span class="p">.</span><span class="n">value</span>
|
||||
<span class="w"> </span><span class="nb">put</span><span class="p">(</span><span class="nb">state</span><span class="p">{</span><span class="w"> </span><span class="n">contributions</span><span class="p">[</span><span class="nc">Call</span><span class="p">.</span><span class="n">caller</span><span class="p">]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">amount</span><span class="p">,</span>
|
||||
<span class="w"> </span><span class="n">total</span><span class="w"> </span><span class="ow">@</span><span class="w"> </span><span class="n">tot</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">tot</span><span class="w"> </span><span class="ow">+</span><span class="w"> </span><span class="nc">Call</span><span class="p">.</span><span class="n">value</span><span class="w"> </span><span class="p">})</span>
|
||||
<span class="w"> </span><span class="kc">true</span>
|
||||
|
||||
<span class="w"> </span><span class="k">stateful</span><span class="w"> </span><span class="k">entrypoint</span><span class="w"> </span><span class="n">withdraw</span><span class="p">()</span><span class="w"> </span><span class="ow">=</span>
|
||||
<span class="w"> </span><span class="k">if</span><span class="p">(</span><span class="nc">Chain</span><span class="p">.</span><span class="n">block_height</span><span class="w"> </span><span class="ow"><</span><span class="w"> </span><span class="nb">state</span><span class="p">.</span><span class="n">deadline</span><span class="p">)</span>
|
||||
<span class="w"> </span><span class="nb">abort</span><span class="p">(</span><span class="s2">"Cannot withdraw before deadline"</span><span class="p">)</span>
|
||||
<span class="w"> </span><span class="k">if</span><span class="p">(</span><span class="nc">Call</span><span class="p">.</span><span class="n">caller</span><span class="w"> </span><span class="ow">==</span><span class="w"> </span><span class="nb">state</span><span class="p">.</span><span class="n">beneficiary</span><span class="p">)</span>
|
||||
<span class="w"> </span><span class="n">withdraw_beneficiary</span><span class="p">()</span>
|
||||
<span class="w"> </span><span class="k">elif</span><span class="p">(</span><span class="n">is_contributor</span><span class="p">(</span><span class="nc">Call</span><span class="p">.</span><span class="n">caller</span><span class="p">))</span>
|
||||
<span class="w"> </span><span class="n">withdraw_contributor</span><span class="p">()</span>
|
||||
<span class="w"> </span><span class="k">else</span>
|
||||
<span class="w"> </span><span class="nb">abort</span><span class="p">(</span><span class="s2">"Not a contributor or beneficiary"</span><span class="p">)</span>
|
||||
|
||||
<span class="w"> </span><span class="k">stateful</span><span class="w"> </span><span class="k">function</span><span class="w"> </span><span class="n">withdraw_beneficiary</span><span class="p">()</span><span class="w"> </span><span class="ow">=</span>
|
||||
<span class="w"> </span><span class="nb">require</span><span class="p">(</span><span class="nb">state</span><span class="p">.</span><span class="n">total</span><span class="w"> </span><span class="ow">>=</span><span class="w"> </span><span class="nb">state</span><span class="p">.</span><span class="n">goal</span><span class="p">,</span><span class="w"> </span><span class="s2">"Project was not funded"</span><span class="p">)</span>
|
||||
<span class="w"> </span><span class="n">spend</span><span class="p">({</span><span class="n">recipient</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">state</span><span class="p">.</span><span class="n">beneficiary</span><span class="p">,</span>
|
||||
<span class="w"> </span><span class="n">amount</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nc">Contract</span><span class="p">.</span><span class="n">balance</span><span class="w"> </span><span class="p">})</span>
|
||||
|
||||
<span class="w"> </span><span class="k">stateful</span><span class="w"> </span><span class="k">function</span><span class="w"> </span><span class="n">withdraw_contributor</span><span class="p">()</span><span class="w"> </span><span class="ow">=</span>
|
||||
<span class="w"> </span><span class="k">if</span><span class="p">(</span><span class="nb">state</span><span class="p">.</span><span class="n">total</span><span class="w"> </span><span class="ow">>=</span><span class="w"> </span><span class="nb">state</span><span class="p">.</span><span class="n">goal</span><span class="p">)</span>
|
||||
<span class="w"> </span><span class="nb">abort</span><span class="p">(</span><span class="s2">"Project was funded"</span><span class="p">)</span>
|
||||
<span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nc">Call</span><span class="p">.</span><span class="n">caller</span>
|
||||
<span class="w"> </span><span class="n">spend</span><span class="p">({</span><span class="n">recipient</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">to</span><span class="p">,</span>
|
||||
<span class="w"> </span><span class="n">amount</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">state</span><span class="p">.</span><span class="n">contributions</span><span class="p">[</span><span class="n">to</span><span class="p">]})</span>
|
||||
<span class="w"> </span><span class="nb">put</span><span class="p">(</span><span class="nb">state</span><span class="p">{</span><span class="w"> </span><span class="n">contributions</span><span class="w"> </span><span class="ow">@</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nc">Map</span><span class="p">.</span><span class="n">delete</span><span class="p">(</span><span class="n">to</span><span class="p">,</span><span class="w"> </span><span class="n">c</span><span class="p">)</span><span class="w"> </span><span class="p">})</span>
|
||||
</code></pre></div>
|
||||
<h2 id="repositories">Repositories</h2>
|
||||
<p>This is a list with repositories that include smart contracts written in Sophia:</p>
|
||||
<ul>
|
||||
<li><a href="https://github.com/aeternity/aepp-sophia-examples">aepp-sophia-examples</a><ul>
|
||||
<li>A repository that contains lots of different examples. The functionality of these examples is - to some extent - also covered by tests written in JavaScript.</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</article>
|
||||
</div>
|
||||
|
||||
|
||||
<script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var tab,labels=set.querySelector(".tabbed-labels");for(tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script>
|
||||
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<footer class="md-footer">
|
||||
|
||||
<div class="md-footer-meta md-typeset">
|
||||
<div class="md-footer-meta__inner md-grid">
|
||||
<div class="md-copyright">
|
||||
|
||||
|
||||
Made with
|
||||
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
|
||||
Material for MkDocs
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
<div class="md-dialog" data-md-component="dialog">
|
||||
<div class="md-dialog__inner md-typeset"></div>
|
||||
</div>
|
||||
|
||||
<script id="__config" type="application/json">{"base": "..", "features": ["content.tabs.link", "search.highlight", "search.share", "search.suggest"], "search": "../assets/javascripts/workers/search.db81ec45.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": {"provider": "mike"}}</script>
|
||||
|
||||
|
||||
<script src="../assets/javascripts/bundle.a00a7c5e.min.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,983 +0,0 @@
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en" class="no-js">
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="prev" href="..">
|
||||
|
||||
|
||||
<link rel="next" href="../sophia_features/">
|
||||
|
||||
<link rel="icon" href="../favicon.png">
|
||||
<meta name="generator" content="mkdocs-1.4.2, mkdocs-material-9.0.9">
|
||||
|
||||
|
||||
|
||||
<title>Syntax - æternity Sophia Language</title>
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../assets/stylesheets/main.0d440cfe.min.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../assets/stylesheets/palette.2505c338.min.css">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
|
||||
<style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
|
||||
|
||||
|
||||
|
||||
<script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce((e,_)=>(e<<5)-e+_.charCodeAt(0),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<body dir="ltr" data-md-color-scheme="default" data-md-color-primary="pink" data-md-color-accent="pink">
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&"object"==typeof palette.color)for(var key of Object.keys(palette.color))document.body.setAttribute("data-md-color-"+key,palette.color[key])</script>
|
||||
|
||||
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
|
||||
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
|
||||
<label class="md-overlay" for="__drawer"></label>
|
||||
<div data-md-component="skip">
|
||||
|
||||
|
||||
<a href="#syntax" class="md-skip">
|
||||
Skip to content
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<div data-md-component="announce">
|
||||
|
||||
</div>
|
||||
|
||||
<div data-md-component="outdated" hidden>
|
||||
|
||||
<aside class="md-banner md-banner--warning">
|
||||
<div class="md-banner__inner md-grid md-typeset">
|
||||
|
||||
You're not viewing the latest version.
|
||||
<a href="../..">
|
||||
<strong>Click here to go to latest.</strong>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<script>var el=document.querySelector("[data-md-component=outdated]"),outdated=__md_get("__outdated",sessionStorage);!0===outdated&&el&&(el.hidden=!1)</script>
|
||||
</aside>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href=".." title="æternity Sophia Language" class="md-header__button md-logo" aria-label="æternity Sophia Language" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54Z"/></svg>
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3V6m0 5h18v2H3v-2m0 5h18v2H3v-2Z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
æternity Sophia Language
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Syntax
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
|
||||
|
||||
<input class="md-option" data-md-color-media="" data-md-color-scheme="default" data-md-color-primary="pink" data-md-color-accent="pink" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_1">
|
||||
|
||||
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_2" hidden>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="m17.75 4.09-2.53 1.94.91 3.06-2.63-1.81-2.63 1.81.91-3.06-2.53-1.94L12.44 4l1.06-3 1.06 3 3.19.09m3.5 6.91-1.64 1.25.59 1.98-1.7-1.17-1.7 1.17.59-1.98L15.75 11l2.06-.05L18.5 9l.69 1.95 2.06.05m-2.28 4.95c.83-.08 1.72 1.1 1.19 1.85-.32.45-.66.87-1.08 1.27C15.17 23 8.84 23 4.94 19.07c-3.91-3.9-3.91-10.24 0-14.14.4-.4.82-.76 1.27-1.08.75-.53 1.93.36 1.85 1.19-.27 2.86.69 5.83 2.89 8.02a9.96 9.96 0 0 0 8.02 2.89m-1.64 2.02a12.08 12.08 0 0 1-7.8-3.47c-2.17-2.19-3.33-5-3.49-7.82-2.81 3.14-2.7 7.96.31 10.98 3.02 3.01 7.84 3.12 10.98.31Z"/></svg>
|
||||
</label>
|
||||
|
||||
|
||||
|
||||
<input class="md-option" data-md-color-media="" data-md-color-scheme="slate" data-md-color-primary="pink" data-md-color-accent="pink" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_2">
|
||||
|
||||
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_1" hidden>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 7a5 5 0 0 1 5 5 5 5 0 0 1-5 5 5 5 0 0 1-5-5 5 5 0 0 1 5-5m0 2a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3m0-7 2.39 3.42C13.65 5.15 12.84 5 12 5c-.84 0-1.65.15-2.39.42L12 2M3.34 7l4.16-.35A7.2 7.2 0 0 0 5.94 8.5c-.44.74-.69 1.5-.83 2.29L3.34 7m.02 10 1.76-3.77a7.131 7.131 0 0 0 2.38 4.14L3.36 17M20.65 7l-1.77 3.79a7.023 7.023 0 0 0-2.38-4.15l4.15.36m-.01 10-4.14.36c.59-.51 1.12-1.14 1.54-1.86.42-.73.69-1.5.83-2.29L20.64 17M12 22l-2.41-3.44c.74.27 1.55.44 2.41.44.82 0 1.63-.17 2.37-.44L12 22Z"/></svg>
|
||||
</label>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
|
||||
<label class="md-search__icon md-icon" for="__search">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"/></svg>
|
||||
</label>
|
||||
<nav class="md-search__options" aria-label="Search">
|
||||
|
||||
<a href="javascript:void(0)" class="md-search__icon md-icon" title="Share" aria-label="Share" data-clipboard data-clipboard-text="" data-md-component="search-share" tabindex="-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7 0-.24-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.15c-.05.21-.08.43-.08.66 0 1.61 1.31 2.91 2.92 2.91 1.61 0 2.92-1.3 2.92-2.91A2.92 2.92 0 0 0 18 16.08Z"/></svg>
|
||||
</a>
|
||||
|
||||
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41Z"/></svg>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<div class="md-search__suggest" data-md-component="search-suggest"></div>
|
||||
|
||||
</form>
|
||||
<div class="md-search__output">
|
||||
<div class="md-search__scrollwrap" data-md-scrollfix>
|
||||
<div class="md-search-result" data-md-component="search-result">
|
||||
<div class="md-search-result__meta">
|
||||
Initializing search
|
||||
</div>
|
||||
<ol class="md-search-result__list" role="presentation"></ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="md-header__source">
|
||||
<a href="https://github.com/aeternity/aesophia" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
GitHub
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
</header>
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
|
||||
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
|
||||
<div class="md-sidebar__scrollwrap">
|
||||
<div class="md-sidebar__inner">
|
||||
|
||||
|
||||
|
||||
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
|
||||
<label class="md-nav__title" for="__drawer">
|
||||
<a href=".." title="æternity Sophia Language" class="md-nav__button md-logo" aria-label="æternity Sophia Language" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54Z"/></svg>
|
||||
|
||||
</a>
|
||||
æternity Sophia Language
|
||||
</label>
|
||||
|
||||
<div class="md-nav__source">
|
||||
<a href="https://github.com/aeternity/aesophia" title="Go to repository" class="md-source" data-md-component="source">
|
||||
<div class="md-source__icon md-icon">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
|
||||
</div>
|
||||
<div class="md-source__repository">
|
||||
GitHub
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ul class="md-nav__list" data-md-scrollfix>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href=".." class="md-nav__link">
|
||||
Introduction
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item md-nav__item--active">
|
||||
|
||||
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-nav__link md-nav__link--active" for="__toc">
|
||||
Syntax
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
</label>
|
||||
|
||||
<a href="./" class="md-nav__link md-nav__link--active">
|
||||
Syntax
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-nav__title" for="__toc">
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
Table of contents
|
||||
</label>
|
||||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#lexical-syntax" class="md-nav__link">
|
||||
Lexical syntax
|
||||
</a>
|
||||
|
||||
<nav class="md-nav" aria-label="Lexical syntax">
|
||||
<ul class="md-nav__list">
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#comments" class="md-nav__link">
|
||||
Comments
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#keywords" class="md-nav__link">
|
||||
Keywords
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#tokens" class="md-nav__link">
|
||||
Tokens
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#layout-blocks" class="md-nav__link">
|
||||
Layout blocks
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#notation" class="md-nav__link">
|
||||
Notation
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#declarations" class="md-nav__link">
|
||||
Declarations
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#types" class="md-nav__link">
|
||||
Types
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#statements" class="md-nav__link">
|
||||
Statements
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#expressions" class="md-nav__link">
|
||||
Expressions
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#operators-types" class="md-nav__link">
|
||||
Operators types
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#operator-precedence" class="md-nav__link">
|
||||
Operator precedence
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
</nav>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../sophia_features/" class="md-nav__link">
|
||||
Features
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../sophia_stdlib/" class="md-nav__link">
|
||||
Standard library
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../sophia_examples/" class="md-nav__link">
|
||||
Contract examples
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../CHANGELOG/" class="md-nav__link">
|
||||
Changelog
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
|
||||
<div class="md-sidebar__scrollwrap">
|
||||
<div class="md-sidebar__inner">
|
||||
|
||||
|
||||
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-nav__title" for="__toc">
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
Table of contents
|
||||
</label>
|
||||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#lexical-syntax" class="md-nav__link">
|
||||
Lexical syntax
|
||||
</a>
|
||||
|
||||
<nav class="md-nav" aria-label="Lexical syntax">
|
||||
<ul class="md-nav__list">
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#comments" class="md-nav__link">
|
||||
Comments
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#keywords" class="md-nav__link">
|
||||
Keywords
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#tokens" class="md-nav__link">
|
||||
Tokens
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#layout-blocks" class="md-nav__link">
|
||||
Layout blocks
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#notation" class="md-nav__link">
|
||||
Notation
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#declarations" class="md-nav__link">
|
||||
Declarations
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#types" class="md-nav__link">
|
||||
Types
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#statements" class="md-nav__link">
|
||||
Statements
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#expressions" class="md-nav__link">
|
||||
Expressions
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#operators-types" class="md-nav__link">
|
||||
Operators types
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#operator-precedence" class="md-nav__link">
|
||||
Operator precedence
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-content" data-md-component="content">
|
||||
<article class="md-content__inner md-typeset">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h1 id="syntax">Syntax</h1>
|
||||
<h2 id="lexical-syntax">Lexical syntax</h2>
|
||||
<h3 id="comments">Comments</h3>
|
||||
<p>Single line comments start with <code>//</code> and block comments are enclosed in <code>/*</code>
|
||||
and <code>*/</code> and can be nested.</p>
|
||||
<h3 id="keywords">Keywords</h3>
|
||||
<div class="highlight"><pre><span></span><code>contract include let switch type record datatype if elif else function
|
||||
stateful payable true false mod public entrypoint private indexed namespace
|
||||
interface main using as for hiding
|
||||
</code></pre></div>
|
||||
<h3 id="tokens">Tokens</h3>
|
||||
<ul>
|
||||
<li><code>Id = [a-z_][A-Za-z0-9_']*</code> identifiers start with a lower case letter.</li>
|
||||
<li><code>Con = [A-Z][A-Za-z0-9_']*</code> constructors start with an upper case letter.</li>
|
||||
<li><code>QId = (Con\.)+Id</code> qualified identifiers (e.g. <code>Map.member</code>)</li>
|
||||
<li><code>QCon = (Con\.)+Con</code> qualified constructor</li>
|
||||
<li><code>TVar = 'Id</code> type variable (e.g <code>'a</code>, <code>'b</code>)</li>
|
||||
<li><code>Int = [0-9]+(_[0-9]+)*|0x[0-9A-Fa-f]+(_[0-9A-Fa-f]+)*</code> integer literal with optional <code>_</code> separators</li>
|
||||
<li><code>Bytes = #[0-9A-Fa-f]+(_[0-9A-Fa-f]+)*</code> byte array literal with optional <code>_</code> separators</li>
|
||||
<li><code>String</code> string literal enclosed in <code>"</code> with escape character <code>\</code></li>
|
||||
<li><code>Char</code> character literal enclosed in <code>'</code> with escape character <code>\</code></li>
|
||||
<li><code>AccountAddress</code> base58-encoded 32 byte account pubkey with <code>ak_</code> prefix</li>
|
||||
<li><code>ContractAddress</code> base58-encoded 32 byte contract address with <code>ct_</code> prefix</li>
|
||||
<li><code>OracleAddress</code> base58-encoded 32 byte oracle address with <code>ok_</code> prefix</li>
|
||||
<li><code>OracleQueryId</code> base58-encoded 32 byte oracle query id with <code>oq_</code> prefix</li>
|
||||
</ul>
|
||||
<p>Valid string escape codes are</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Escape</th>
|
||||
<th>ASCII</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>\b</code></td>
|
||||
<td>8</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>\t</code></td>
|
||||
<td>9</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>\n</code></td>
|
||||
<td>10</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>\v</code></td>
|
||||
<td>11</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>\f</code></td>
|
||||
<td>12</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>\r</code></td>
|
||||
<td>13</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>\e</code></td>
|
||||
<td>27</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>\xHexDigits</code></td>
|
||||
<td><em>HexDigits</em></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>See the <a href="https://github.com/aeternity/protocol/blob/master/node/api/api_encoding.md">identifier encoding scheme</a> for the
|
||||
details on the base58 literals.</p>
|
||||
<h2 id="layout-blocks">Layout blocks</h2>
|
||||
<p>Sophia uses Python-style layout rules to group declarations and statements. A
|
||||
layout block with more than one element must start on a separate line and be
|
||||
indented more than the currently enclosing layout block. Blocks with a single
|
||||
element can be written on the same line as the previous token.</p>
|
||||
<p>Each element of the block must share the same indentation and no part of an
|
||||
element may be indented less than the indentation of the block. For instance</p>
|
||||
<div class="highlight"><pre><span></span><code><span class="k">contract</span><span class="w"> </span><span class="nf">Layout</span><span class="w"> </span><span class="ow">=</span>
|
||||
<span class="w"> </span><span class="k">function</span><span class="w"> </span><span class="n">foo</span><span class="p">()</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="c1">// no layout</span>
|
||||
<span class="w"> </span><span class="k">function</span><span class="w"> </span><span class="n">bar</span><span class="p">()</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="c1">// layout block starts on next line</span>
|
||||
<span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">foo</span><span class="p">()</span><span class="w"> </span><span class="c1">// indented more than 2 spaces</span>
|
||||
<span class="w"> </span><span class="n">x</span>
|
||||
<span class="w"> </span><span class="ow">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="c1">// the '+' is indented more than the 'x'</span>
|
||||
</code></pre></div>
|
||||
<h2 id="notation">Notation</h2>
|
||||
<p>In describing the syntax below, we use the following conventions:</p>
|
||||
<ul>
|
||||
<li>Upper-case identifiers denote non-terminals (like <code>Expr</code>) or terminals with
|
||||
some associated value (like <code>Id</code>).</li>
|
||||
<li>Keywords and symbols are enclosed in single quotes: <code>'let'</code> or <code>'='</code>.</li>
|
||||
<li>Choices are separated by vertical bars: <code>|</code>.</li>
|
||||
<li>Optional elements are enclosed in <code>[</code> square brackets <code>]</code>.</li>
|
||||
<li><code>(</code> Parentheses <code>)</code> are used for grouping.</li>
|
||||
<li>Zero or more repetitions are denoted by a postfix <code>*</code>, and one or more
|
||||
repetitions by a <code>+</code>.</li>
|
||||
<li><code>Block(X)</code> denotes a layout block of <code>X</code>s.</li>
|
||||
<li><code>Sep(X, S)</code> is short for <code>[X (S X)*]</code>, i.e. a possibly empty sequence of <code>X</code>s
|
||||
separated by <code>S</code>s.</li>
|
||||
<li><code>Sep1(X, S)</code> is short for <code>X (S X)*</code>, i.e. same as <code>Sep</code>, but must not be empty.</li>
|
||||
</ul>
|
||||
<h2 id="declarations">Declarations</h2>
|
||||
<p>A Sophia file consists of a sequence of <em>declarations</em> in a layout block.</p>
|
||||
<div class="highlight"><pre><span></span><code><span class="n">File</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="n">Block</span><span class="p">(</span><span class="n">TopDecl</span><span class="p">)</span>
|
||||
|
||||
<span class="n">TopDecl</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="p">[</span><span class="err">'</span><span class="n">payable</span><span class="err">'</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="err">'</span><span class="n">main</span><span class="err">'</span><span class="p">]</span><span class="w"> </span><span class="err">'</span><span class="n">contract</span><span class="err">'</span><span class="w"> </span><span class="n">Con</span><span class="w"> </span><span class="p">[</span><span class="n">Implement</span><span class="p">]</span><span class="w"> </span><span class="sc">'='</span><span class="w"> </span><span class="n">Block</span><span class="p">(</span><span class="n">Decl</span><span class="p">)</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="n">contract</span><span class="sc">' '</span><span class="n">interface</span><span class="err">'</span><span class="w"> </span><span class="n">Con</span><span class="w"> </span><span class="p">[</span><span class="n">Implement</span><span class="p">]</span><span class="w"> </span><span class="sc">'='</span><span class="w"> </span><span class="n">Block</span><span class="p">(</span><span class="n">Decl</span><span class="p">)</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="n">namespace</span><span class="err">'</span><span class="w"> </span><span class="n">Con</span><span class="w"> </span><span class="sc">'='</span><span class="w"> </span><span class="n">Block</span><span class="p">(</span><span class="n">Decl</span><span class="p">)</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'@</span><span class="n">compiler</span><span class="err">'</span><span class="w"> </span><span class="n">PragmaOp</span><span class="w"> </span><span class="n">Version</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="n">include</span><span class="err">'</span><span class="w"> </span><span class="n">String</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Using</span>
|
||||
|
||||
<span class="n">Implement</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="sc">':'</span><span class="w"> </span><span class="n">Sep1</span><span class="p">(</span><span class="n">Con</span><span class="p">,</span><span class="w"> </span><span class="sc">','</span><span class="p">)</span>
|
||||
|
||||
<span class="n">Decl</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="err">'</span><span class="n">type</span><span class="err">'</span><span class="w"> </span><span class="n">Id</span><span class="w"> </span><span class="p">[</span><span class="sc">'('</span><span class="w"> </span><span class="n">TVar</span><span class="o">*</span><span class="w"> </span><span class="sc">')'</span><span class="p">]</span><span class="w"> </span><span class="sc">'='</span><span class="w"> </span><span class="n">TypeAlias</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="n">record</span><span class="err">'</span><span class="w"> </span><span class="n">Id</span><span class="w"> </span><span class="p">[</span><span class="sc">'('</span><span class="w"> </span><span class="n">TVar</span><span class="o">*</span><span class="w"> </span><span class="sc">')'</span><span class="p">]</span><span class="w"> </span><span class="sc">'='</span><span class="w"> </span><span class="n">RecordType</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="n">datatype</span><span class="err">'</span><span class="w"> </span><span class="n">Id</span><span class="w"> </span><span class="p">[</span><span class="sc">'('</span><span class="w"> </span><span class="n">TVar</span><span class="o">*</span><span class="w"> </span><span class="sc">')'</span><span class="p">]</span><span class="w"> </span><span class="sc">'='</span><span class="w"> </span><span class="n">DataType</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="n">let</span><span class="err">'</span><span class="w"> </span><span class="n">Id</span><span class="w"> </span><span class="p">[</span><span class="sc">':'</span><span class="w"> </span><span class="n">Type</span><span class="p">]</span><span class="w"> </span><span class="sc">'='</span><span class="w"> </span><span class="n">Expr</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="p">(</span><span class="n">EModifier</span><span class="o">*</span><span class="w"> </span><span class="err">'</span><span class="n">entrypoint</span><span class="err">'</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">FModifier</span><span class="o">*</span><span class="w"> </span><span class="err">'</span><span class="n">function</span><span class="err">'</span><span class="p">)</span><span class="w"> </span><span class="n">Block</span><span class="p">(</span><span class="n">FunDecl</span><span class="p">)</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Using</span>
|
||||
|
||||
<span class="n">FunDecl</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="n">Id</span><span class="w"> </span><span class="sc">':'</span><span class="w"> </span><span class="n">Type</span><span class="w"> </span><span class="c1">// Type signature</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Id</span><span class="w"> </span><span class="n">Args</span><span class="w"> </span><span class="p">[</span><span class="sc">':'</span><span class="w"> </span><span class="n">Type</span><span class="p">]</span><span class="w"> </span><span class="sc">'='</span><span class="w"> </span><span class="n">Block</span><span class="p">(</span><span class="n">Stmt</span><span class="p">)</span><span class="w"> </span><span class="c1">// Definition</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Id</span><span class="w"> </span><span class="n">Args</span><span class="w"> </span><span class="p">[</span><span class="sc">':'</span><span class="w"> </span><span class="n">Type</span><span class="p">]</span><span class="w"> </span><span class="n">Block</span><span class="p">(</span><span class="n">GuardedDef</span><span class="p">)</span><span class="w"> </span><span class="c1">// Guarded definitions</span>
|
||||
|
||||
<span class="n">GuardedDef</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="sc">'|'</span><span class="w"> </span><span class="n">Sep1</span><span class="p">(</span><span class="n">Expr</span><span class="p">,</span><span class="w"> </span><span class="sc">','</span><span class="p">)</span><span class="w"> </span><span class="sc">'='</span><span class="w"> </span><span class="n">Block</span><span class="p">(</span><span class="n">Stmt</span><span class="p">)</span>
|
||||
|
||||
<span class="n">Using</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="err">'</span><span class="n">using</span><span class="err">'</span><span class="w"> </span><span class="n">Con</span><span class="w"> </span><span class="p">[</span><span class="err">'</span><span class="n">as</span><span class="err">'</span><span class="w"> </span><span class="n">Con</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">UsingParts</span><span class="p">]</span>
|
||||
<span class="n">UsingParts</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="err">'</span><span class="k">for</span><span class="sc">' '</span><span class="p">[</span><span class="err">'</span><span class="w"> </span><span class="n">Sep1</span><span class="p">(</span><span class="n">Id</span><span class="p">,</span><span class="w"> </span><span class="sc">','</span><span class="p">)</span><span class="w"> </span><span class="sc">']'</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="n">hiding</span><span class="sc">' '</span><span class="p">[</span><span class="err">'</span><span class="w"> </span><span class="n">Sep1</span><span class="p">(</span><span class="n">Id</span><span class="p">,</span><span class="w"> </span><span class="sc">','</span><span class="p">)</span><span class="w"> </span><span class="sc">']'</span>
|
||||
|
||||
<span class="n">PragmaOp</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="sc">'<'</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="o">=<</span><span class="err">'</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="o">==</span><span class="err">'</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="o">>=</span><span class="err">'</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="sc">'>'</span>
|
||||
<span class="n">Version</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="n">Sep1</span><span class="p">(</span><span class="n">Int</span><span class="p">,</span><span class="w"> </span><span class="sc">'.'</span><span class="p">)</span>
|
||||
|
||||
<span class="n">EModifier</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="err">'</span><span class="n">payable</span><span class="err">'</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="n">stateful</span><span class="err">'</span>
|
||||
<span class="n">FModifier</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="err">'</span><span class="n">stateful</span><span class="err">'</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="n">private</span><span class="err">'</span>
|
||||
|
||||
<span class="n">Args</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="sc">'('</span><span class="w"> </span><span class="n">Sep</span><span class="p">(</span><span class="n">Pattern</span><span class="p">,</span><span class="w"> </span><span class="sc">','</span><span class="p">)</span><span class="w"> </span><span class="sc">')'</span>
|
||||
</code></pre></div>
|
||||
<p>Contract declarations must appear at the top-level.</p>
|
||||
<p>For example,
|
||||
<div class="highlight"><pre><span></span><code><span class="k">contract</span><span class="w"> </span><span class="nf">Test</span><span class="w"> </span><span class="ow">=</span>
|
||||
<span class="w"> </span><span class="k">type</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">int</span>
|
||||
<span class="w"> </span><span class="k">entrypoint</span><span class="w"> </span><span class="n">add</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="n">t</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">+</span><span class="w"> </span><span class="n">y</span>
|
||||
</code></pre></div></p>
|
||||
<p>There are three forms of type declarations: type aliases (declared with the
|
||||
<code>type</code> keyword), record type definitions (<code>record</code>) and data type definitions
|
||||
(<code>datatype</code>):</p>
|
||||
<div class="highlight"><pre><span></span><code><span class="n">TypeAlias</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="n">Type</span>
|
||||
<span class="n">RecordType</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="sc">'{'</span><span class="w"> </span><span class="n">Sep</span><span class="p">(</span><span class="n">FieldType</span><span class="p">,</span><span class="w"> </span><span class="sc">','</span><span class="p">)</span><span class="w"> </span><span class="sc">'}'</span>
|
||||
<span class="n">DataType</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="n">Sep1</span><span class="p">(</span><span class="n">ConDecl</span><span class="p">,</span><span class="w"> </span><span class="sc">'|'</span><span class="p">)</span>
|
||||
|
||||
<span class="n">FieldType</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="n">Id</span><span class="w"> </span><span class="sc">':'</span><span class="w"> </span><span class="n">Type</span>
|
||||
<span class="n">ConDecl</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="n">Con</span><span class="w"> </span><span class="p">[</span><span class="sc">'('</span><span class="w"> </span><span class="n">Sep1</span><span class="p">(</span><span class="n">Type</span><span class="p">,</span><span class="w"> </span><span class="sc">','</span><span class="p">)</span><span class="w"> </span><span class="sc">')'</span><span class="p">]</span>
|
||||
</code></pre></div>
|
||||
<p>For example,
|
||||
<div class="highlight"><pre><span></span><code><span class="k">record</span><span class="w"> </span><span class="n">point</span><span class="p">(</span><span class="nv">'a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">{</span><span class="n">x</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="nv">'a</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="nv">'a</span><span class="p">}</span>
|
||||
<span class="k">datatype</span><span class="w"> </span><span class="n">shape</span><span class="p">(</span><span class="nv">'a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">Circle</span><span class="p">(</span><span class="n">point</span><span class="p">(</span><span class="nv">'a</span><span class="p">),</span><span class="w"> </span><span class="nv">'a</span><span class="p">)</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span><span class="nf">Rect</span><span class="p">(</span><span class="n">point</span><span class="p">(</span><span class="nv">'a</span><span class="p">),</span><span class="w"> </span><span class="n">point</span><span class="p">(</span><span class="nv">'a</span><span class="p">))</span>
|
||||
<span class="k">type</span><span class="w"> </span><span class="n">int_shape</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">shape</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span>
|
||||
</code></pre></div></p>
|
||||
<h2 id="types">Types</h2>
|
||||
<div class="highlight"><pre><span></span><code><span class="n">Type</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="n">Domain</span><span class="w"> </span><span class="err">'</span><span class="o">=></span><span class="err">'</span><span class="w"> </span><span class="n">Type</span><span class="w"> </span><span class="c1">// Function type</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Type</span><span class="w"> </span><span class="sc">'('</span><span class="w"> </span><span class="n">Sep</span><span class="p">(</span><span class="n">Type</span><span class="p">,</span><span class="w"> </span><span class="sc">','</span><span class="p">)</span><span class="w"> </span><span class="sc">')'</span><span class="w"> </span><span class="c1">// Type application</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="sc">'('</span><span class="w"> </span><span class="n">Type</span><span class="w"> </span><span class="sc">')'</span><span class="w"> </span><span class="c1">// Parens</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="n">unit</span><span class="err">'</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Sep</span><span class="p">(</span><span class="n">Type</span><span class="p">,</span><span class="w"> </span><span class="sc">'*'</span><span class="p">)</span><span class="w"> </span><span class="c1">// Tuples</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Id</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">QId</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">TVar</span>
|
||||
|
||||
<span class="n">Domain</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="n">Type</span><span class="w"> </span><span class="c1">// Single argument</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="sc">'('</span><span class="w"> </span><span class="n">Sep</span><span class="p">(</span><span class="n">Type</span><span class="p">,</span><span class="w"> </span><span class="sc">','</span><span class="p">)</span><span class="w"> </span><span class="sc">')'</span><span class="w"> </span><span class="c1">// Multiple arguments</span>
|
||||
</code></pre></div>
|
||||
<p>The function type arrow associates to the right.</p>
|
||||
<p>Example,
|
||||
<div class="highlight"><pre><span></span><code><span class="nv">'a</span><span class="w"> </span><span class="ow">=></span><span class="w"> </span><span class="kt">list</span><span class="p">(</span><span class="nv">'a</span><span class="p">)</span><span class="w"> </span><span class="ow">=></span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="ow">*</span><span class="w"> </span><span class="kt">list</span><span class="p">(</span><span class="nv">'a</span><span class="p">))</span>
|
||||
</code></pre></div></p>
|
||||
<h2 id="statements">Statements</h2>
|
||||
<p>Function bodies are blocks of <em>statements</em>, where a statement is one of the following</p>
|
||||
<div class="highlight"><pre><span></span><code><span class="n">Stmt</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="err">'</span><span class="k">switch</span><span class="sc">' '</span><span class="p">(</span><span class="err">'</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="sc">')'</span><span class="w"> </span><span class="n">Block</span><span class="p">(</span><span class="n">Case</span><span class="p">)</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="k">if</span><span class="sc">' '</span><span class="p">(</span><span class="err">'</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="sc">')'</span><span class="w"> </span><span class="n">Block</span><span class="p">(</span><span class="n">Stmt</span><span class="p">)</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="n">elif</span><span class="sc">' '</span><span class="p">(</span><span class="err">'</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="sc">')'</span><span class="w"> </span><span class="n">Block</span><span class="p">(</span><span class="n">Stmt</span><span class="p">)</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="k">else</span><span class="err">'</span><span class="w"> </span><span class="n">Block</span><span class="p">(</span><span class="n">Stmt</span><span class="p">)</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="n">let</span><span class="err">'</span><span class="w"> </span><span class="n">LetDef</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Using</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Expr</span>
|
||||
|
||||
<span class="n">LetDef</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="n">Id</span><span class="w"> </span><span class="n">Args</span><span class="w"> </span><span class="p">[</span><span class="sc">':'</span><span class="w"> </span><span class="n">Type</span><span class="p">]</span><span class="w"> </span><span class="sc">'='</span><span class="w"> </span><span class="n">Block</span><span class="p">(</span><span class="n">Stmt</span><span class="p">)</span><span class="w"> </span><span class="c1">// Function definition</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Pattern</span><span class="w"> </span><span class="sc">'='</span><span class="w"> </span><span class="n">Block</span><span class="p">(</span><span class="n">Stmt</span><span class="p">)</span><span class="w"> </span><span class="c1">// Value definition</span>
|
||||
|
||||
<span class="n">Case</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="n">Pattern</span><span class="w"> </span><span class="err">'</span><span class="o">=></span><span class="err">'</span><span class="w"> </span><span class="n">Block</span><span class="p">(</span><span class="n">Stmt</span><span class="p">)</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Pattern</span><span class="w"> </span><span class="n">Block</span><span class="p">(</span><span class="n">GuardedCase</span><span class="p">)</span>
|
||||
|
||||
<span class="n">GuardedCase</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="sc">'|'</span><span class="w"> </span><span class="n">Sep1</span><span class="p">(</span><span class="n">Expr</span><span class="p">,</span><span class="w"> </span><span class="sc">','</span><span class="p">)</span><span class="w"> </span><span class="err">'</span><span class="o">=></span><span class="err">'</span><span class="w"> </span><span class="n">Block</span><span class="p">(</span><span class="n">Stmt</span><span class="p">)</span>
|
||||
|
||||
<span class="n">Pattern</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="n">Expr</span>
|
||||
</code></pre></div>
|
||||
<p><code>if</code> statements can be followed by zero or more <code>elif</code> statements and an optional final <code>else</code> statement. For example,</p>
|
||||
<div class="highlight"><pre><span></span><code><span class="k">let</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">4</span>
|
||||
<span class="k">switch</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">))</span>
|
||||
<span class="w"> </span><span class="nf">None</span><span class="w"> </span><span class="ow">=></span><span class="w"> </span><span class="mi">0</span>
|
||||
<span class="w"> </span><span class="nf">Some</span><span class="p">(</span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="ow">=></span>
|
||||
<span class="w"> </span><span class="k">if</span><span class="p">(</span><span class="n">y</span><span class="w"> </span><span class="ow">></span><span class="w"> </span><span class="mi">10</span><span class="p">)</span>
|
||||
<span class="w"> </span><span class="s2">"too big"</span>
|
||||
<span class="w"> </span><span class="k">elif</span><span class="p">(</span><span class="n">y</span><span class="w"> </span><span class="ow"><</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span>
|
||||
<span class="w"> </span><span class="s2">"too small"</span>
|
||||
<span class="w"> </span><span class="k">else</span>
|
||||
<span class="w"> </span><span class="s2">"just right"</span>
|
||||
</code></pre></div>
|
||||
<h2 id="expressions">Expressions</h2>
|
||||
<div class="highlight"><pre><span></span><code><span class="n">Expr</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="sc">'('</span><span class="w"> </span><span class="n">LamArgs</span><span class="w"> </span><span class="sc">')'</span><span class="w"> </span><span class="err">'</span><span class="o">=></span><span class="err">'</span><span class="w"> </span><span class="n">Block</span><span class="p">(</span><span class="n">Stmt</span><span class="p">)</span><span class="w"> </span><span class="c1">// Anonymous function (x) => x + 1</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="sc">'('</span><span class="w"> </span><span class="n">BinOp</span><span class="w"> </span><span class="sc">')'</span><span class="w"> </span><span class="c1">// Operator lambda (+)</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="k">if</span><span class="sc">' '</span><span class="p">(</span><span class="err">'</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="sc">')'</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="err">'</span><span class="k">else</span><span class="err">'</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="c1">// If expression if(x < y) y else x</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="sc">':'</span><span class="w"> </span><span class="n">Type</span><span class="w"> </span><span class="c1">// Type annotation 5 : int</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">BinOp</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="c1">// Binary operator x + y</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">UnOp</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="c1">// Unary operator ! b</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="sc">'('</span><span class="w"> </span><span class="n">Sep</span><span class="p">(</span><span class="n">Expr</span><span class="p">,</span><span class="w"> </span><span class="sc">','</span><span class="p">)</span><span class="w"> </span><span class="sc">')'</span><span class="w"> </span><span class="c1">// Application f(x, y)</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="sc">'.'</span><span class="w"> </span><span class="n">Id</span><span class="w"> </span><span class="c1">// Projection state.x</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="sc">'['</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="sc">']'</span><span class="w"> </span><span class="c1">// Map lookup map[key]</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="sc">'{'</span><span class="w"> </span><span class="n">Sep</span><span class="p">(</span><span class="n">FieldUpdate</span><span class="p">,</span><span class="w"> </span><span class="sc">','</span><span class="p">)</span><span class="w"> </span><span class="sc">'}'</span><span class="w"> </span><span class="c1">// Record or map update r{ fld[key].x = y }</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="sc">'['</span><span class="w"> </span><span class="n">Sep</span><span class="p">(</span><span class="n">Expr</span><span class="p">,</span><span class="w"> </span><span class="sc">','</span><span class="p">)</span><span class="w"> </span><span class="sc">']'</span><span class="w"> </span><span class="c1">// List [1, 2, 3]</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="sc">'['</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="sc">'|'</span><span class="w"> </span><span class="n">Sep</span><span class="p">(</span><span class="n">Generator</span><span class="p">,</span><span class="w"> </span><span class="sc">','</span><span class="p">)</span><span class="w"> </span><span class="sc">']'</span>
|
||||
<span class="w"> </span><span class="c1">// List comprehension [k | x <- [1], if (f(x)), let k = x+1]</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="sc">'['</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="err">'</span><span class="p">..</span><span class="err">'</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="sc">']'</span><span class="w"> </span><span class="c1">// List range [1..n]</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="sc">'{'</span><span class="w"> </span><span class="n">Sep</span><span class="p">(</span><span class="n">FieldUpdate</span><span class="p">,</span><span class="w"> </span><span class="sc">','</span><span class="p">)</span><span class="w"> </span><span class="sc">'}'</span><span class="w"> </span><span class="c1">// Record or map value {x = 0, y = 1}, {[key] = val}</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="sc">'('</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="sc">')'</span><span class="w"> </span><span class="c1">// Parens (1 + 2) * 3</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="sc">'('</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="sc">'='</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="sc">')'</span><span class="w"> </span><span class="c1">// Assign pattern (y = x::_)</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Id</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Con</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">QId</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">QCon</span><span class="w"> </span><span class="c1">// Identifiers x, None, Map.member, AELib.Token</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Int</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Bytes</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Char</span><span class="w"> </span><span class="c1">// Literals 123, 0xff, #00abc123, "foo", '%'</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">AccountAddress</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">ContractAddress</span><span class="w"> </span><span class="c1">// Chain identifiers</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">OracleAddress</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">OracleQueryId</span><span class="w"> </span><span class="c1">// Chain identifiers</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="o">???</span><span class="err">'</span><span class="w"> </span><span class="c1">// Hole expression 1 + ???</span>
|
||||
|
||||
<span class="n">Generator</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="n">Pattern</span><span class="w"> </span><span class="err">'</span><span class="o"><-</span><span class="err">'</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="c1">// Generator</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="k">if</span><span class="sc">' '</span><span class="p">(</span><span class="err">'</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="sc">')'</span><span class="w"> </span><span class="c1">// Guard</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">LetDef</span><span class="w"> </span><span class="c1">// Definition</span>
|
||||
|
||||
<span class="n">LamArgs</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="sc">'('</span><span class="w"> </span><span class="n">Sep</span><span class="p">(</span><span class="n">LamArg</span><span class="p">,</span><span class="w"> </span><span class="sc">','</span><span class="p">)</span><span class="w"> </span><span class="sc">')'</span>
|
||||
<span class="n">LamArg</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="n">Id</span><span class="w"> </span><span class="p">[</span><span class="sc">':'</span><span class="w"> </span><span class="n">Type</span><span class="p">]</span>
|
||||
|
||||
<span class="n">FieldUpdate</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="n">Path</span><span class="w"> </span><span class="sc">'='</span><span class="w"> </span><span class="n">Expr</span>
|
||||
<span class="n">Path</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="n">Id</span><span class="w"> </span><span class="c1">// Record field</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="sc">'['</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="sc">']'</span><span class="w"> </span><span class="c1">// Map key</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Path</span><span class="w"> </span><span class="sc">'.'</span><span class="w"> </span><span class="n">Id</span><span class="w"> </span><span class="c1">// Nested record field</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Path</span><span class="w"> </span><span class="sc">'['</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="sc">']'</span><span class="w"> </span><span class="c1">// Nested map key</span>
|
||||
|
||||
<span class="n">BinOp</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="err">'</span><span class="o">||</span><span class="err">'</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="o">&&</span><span class="err">'</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="sc">'<'</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="sc">'>'</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="o">=<</span><span class="err">'</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="o">>=</span><span class="err">'</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="o">==</span><span class="err">'</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="o">!=</span><span class="err">'</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="o">::</span><span class="err">'</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="o">++</span><span class="err">'</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="sc">'+'</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="sc">'-'</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="sc">'*'</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="sc">'/'</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="n">mod</span><span class="err">'</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="sc">'^'</span>
|
||||
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">'</span><span class="o">|></span><span class="err">'</span>
|
||||
<span class="n">UnOp</span><span class="w"> </span><span class="o">::=</span><span class="w"> </span><span class="sc">'-'</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="sc">'!'</span>
|
||||
</code></pre></div>
|
||||
<h2 id="operators-types">Operators types</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Operators</th>
|
||||
<th>Type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>-</code> <code>+</code> <code>*</code> <code>/</code> <code>mod</code> <code>^</code></td>
|
||||
<td>arithmetic operators</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>!</code> <code>&&</code> <code>||</code></td>
|
||||
<td>logical operators</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>==</code> <code>!=</code> <code><</code> <code>></code> <code>=<</code> <code>>=</code></td>
|
||||
<td>comparison operators</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>::</code> <code>++</code></td>
|
||||
<td>list operators</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>|></code></td>
|
||||
<td>functional operators</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2 id="operator-precedence">Operator precedence</h2>
|
||||
<p>In order of highest to lowest precedence.</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Operators</th>
|
||||
<th>Associativity</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>!</code></td>
|
||||
<td>right</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>^</code></td>
|
||||
<td>left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>*</code> <code>/</code> <code>mod</code></td>
|
||||
<td>left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>-</code> (unary)</td>
|
||||
<td>right</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>+</code> <code>-</code></td>
|
||||
<td>left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>::</code> <code>++</code></td>
|
||||
<td>right</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code><</code> <code>></code> <code>=<</code> <code>>=</code> <code>==</code> <code>!=</code></td>
|
||||
<td>none</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>&&</code></td>
|
||||
<td>right</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>||</code></td>
|
||||
<td>right</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>|></code></td>
|
||||
<td>left</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</article>
|
||||
</div>
|
||||
|
||||
|
||||
<script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var tab,labels=set.querySelector(".tabbed-labels");for(tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script>
|
||||
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<footer class="md-footer">
|
||||
|
||||
<div class="md-footer-meta md-typeset">
|
||||
<div class="md-footer-meta__inner md-grid">
|
||||
<div class="md-copyright">
|
||||
|
||||
|
||||
Made with
|
||||
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
|
||||
Material for MkDocs
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
<div class="md-dialog" data-md-component="dialog">
|
||||
<div class="md-dialog__inner md-typeset"></div>
|
||||
</div>
|
||||
|
||||
<script id="__config" type="application/json">{"base": "..", "features": ["content.tabs.link", "search.highlight", "search.share", "search.suggest"], "search": "../assets/javascripts/workers/search.db81ec45.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": {"provider": "mike"}}</script>
|
||||
|
||||
|
||||
<script src="../assets/javascripts/bundle.a00a7c5e.min.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,39 @@
|
||||
%% -*- mode: erlang; indent-tabs-mode: nil -*-
|
||||
|
||||
{erl_opts, [debug_info]}.
|
||||
|
||||
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git",
|
||||
{ref, "e8253b0"}}}
|
||||
, {getopt, "1.0.1"}
|
||||
, {jsx, {git, "https://github.com/talentdeficit/jsx.git",
|
||||
{tag, "2.8.0"}}}
|
||||
]}.
|
||||
|
||||
{escript_incl_apps, [aesophia, aebytecode, getopt]}.
|
||||
{escript_main_app, aesophia}.
|
||||
{escript_name, aesophia}.
|
||||
{escript_emu_args, "%%! \n"}.
|
||||
{provider_hooks, [{post, [{compile, escriptize}]}]}.
|
||||
|
||||
{post_hooks, [{"(linux|darwin|solaris|freebsd|netbsd|openbsd)",
|
||||
escriptize,
|
||||
"cp \"$REBAR_BUILD_DIR/bin/aesophia\" ./aesophia"},
|
||||
{"win32",
|
||||
escriptize,
|
||||
"robocopy \"%REBAR_BUILD_DIR%/bin/\" ./ aesophia* "
|
||||
"/njs /njh /nfl /ndl & exit /b 0"} % silence things
|
||||
]}.
|
||||
|
||||
{dialyzer, [
|
||||
{warnings, [unknown]},
|
||||
{plt_apps, all_deps},
|
||||
{base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]}
|
||||
]}.
|
||||
|
||||
{relx, [{release, {aesophia, "2.1.0"},
|
||||
[aesophia, aebytecode, getopt]},
|
||||
|
||||
{dev_mode, true},
|
||||
{include_erts, false},
|
||||
|
||||
{extended_start_script, true}]}.
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
{"1.1.0",
|
||||
[{<<"aebytecode">>,
|
||||
{git,"https://github.com/aeternity/aebytecode.git",
|
||||
{ref,"e8253b09709f1595d8bd6a1756a0ce93185c6518"}},
|
||||
0},
|
||||
{<<"aeserialization">>,
|
||||
{git,"https://github.com/aeternity/aeserialization.git",
|
||||
{ref,"6dce265753af4e651f77746e77ea125145c85dd3"}},
|
||||
1},
|
||||
{<<"base58">>,
|
||||
{git,"https://github.com/aeternity/erl-base58.git",
|
||||
{ref,"60a335668a60328a29f9731b67c4a0e9e3d50ab6"}},
|
||||
2},
|
||||
{<<"eblake2">>,{pkg,<<"eblake2">>,<<"1.0.0">>},1},
|
||||
{<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},0},
|
||||
{<<"jsx">>,
|
||||
{git,"https://github.com/talentdeficit/jsx.git",
|
||||
{ref,"3074d4865b3385a050badf7828ad31490d860df5"}},
|
||||
0}]}.
|
||||
[
|
||||
{pkg_hash,[
|
||||
{<<"eblake2">>, <<"EC8AD20E438AAB3F2E8D5D118C366A0754219195F8A0F536587440F8F9BCF2EF">>},
|
||||
{<<"getopt">>, <<"C73A9FA687B217F2FF79F68A3B637711BB1936E712B521D8CE466B29CBF7808A">>}]}
|
||||
].
|
||||
@@ -0,0 +1,572 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @author Robert Virding
|
||||
%%% @copyright (C) 2019, Aeternity Anstalt
|
||||
%%% @doc
|
||||
%%% ACI interface
|
||||
%%% @end
|
||||
%%% Created : 12 Jan 2019
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(aeso_aci).
|
||||
|
||||
%% Old deprecated interface.
|
||||
-export([encode/1,encode/2,decode/1]).
|
||||
|
||||
-export([encode_contract/1,encode_contract/2,decode_contract/1]).
|
||||
-export([encode_func/1,encode_type/1,encode_arg/1,
|
||||
encode_stmt/1,encode_expr/1]).
|
||||
|
||||
%% Define records for the various typed syntactic forms. These make
|
||||
%% the code easier but don't seem to exist elsewhere. Unfortunately
|
||||
%% sometimes the same typename is used with different fields.
|
||||
|
||||
%% Top-level
|
||||
-record(contract, {ann,con,decls}).
|
||||
%% -record(namespace, {ann,con,decls}).
|
||||
-record(letfun, {ann,id,args,type,body}).
|
||||
-record(type_def, {ann,id,vars,typedef}).
|
||||
|
||||
%% Types
|
||||
-record(app_t, {ann,id,fields}).
|
||||
-record(tuple_t, {ann,args}).
|
||||
-record(bytes_t, {ann,len}).
|
||||
-record(record_t, {fields}).
|
||||
-record(field_t, {ann,id,type}).
|
||||
-record(alias_t, {type}).
|
||||
-record(variant_t, {cons}).
|
||||
-record(constr_t, {ann,con,args}).
|
||||
-record(fun_t, {ann,named,args,type}).
|
||||
|
||||
%% Tokens
|
||||
-record(arg, {ann,id,type}).
|
||||
-record(id, {ann,name}).
|
||||
-record(con, {ann,name}).
|
||||
-record(qid, {ann,names}).
|
||||
-record(qcon, {ann,names}).
|
||||
-record(tvar, {ann,name}).
|
||||
|
||||
%% Statements
|
||||
-record(block, {ann,body}).
|
||||
-record('if', {ann,test,then,else}). %Both statement and expression
|
||||
-record(letval, {ann,pat,type,exp}).
|
||||
-record(switch, {ann,arg,cases}).
|
||||
-record('case', {ann,pat,body}).
|
||||
|
||||
%% Expressions
|
||||
-record(bool, {ann,bool}).
|
||||
-record(int, {ann,value}).
|
||||
-record(string, {ann,bin}).
|
||||
-record(bytes, {ann,bin}).
|
||||
-record(tuple, {ann,args}).
|
||||
-record(list, {ann,args}).
|
||||
-record(record, {ann,fields}). %Create a record
|
||||
-record(field, {ann,name,value}). %A record field
|
||||
-record(proj, {ann,value}). %?
|
||||
-record(map, {ann,fields}). %Create a map
|
||||
-record(map_get, {ann,field}).
|
||||
-record(lam, {ann,args,body}).
|
||||
-record(app, {ann,func,args}).
|
||||
-record(typed, {ann,expr,type}).
|
||||
|
||||
%% The old deprecated interface.
|
||||
|
||||
encode(C) -> encode_contract(C).
|
||||
encode(C, Os) -> encode_contract(C, Os).
|
||||
decode(J) -> decode_contract(J).
|
||||
|
||||
%% encode_contract(ContractString) -> {ok,JSON} | {error,String}.
|
||||
%% encode_contract(ContractString, Options) -> {ok,JSON} | {error,String}.
|
||||
%% Build a JSON structure with lists and tuples, not maps, as this
|
||||
%% allows us to order the fields in the contructed JSON string.
|
||||
|
||||
encode_contract(ContractString) ->
|
||||
encode_contract(ContractString, []).
|
||||
encode_contract(ContractString, Options) when is_binary(ContractString) ->
|
||||
encode_contract(binary_to_list(ContractString), Options);
|
||||
encode_contract(ContractString, Options) ->
|
||||
try
|
||||
Ast = parse(ContractString, Options),
|
||||
%% io:format("Ast\n~p\n", [Ast]),
|
||||
%% aeso_ast:pp(Ast),
|
||||
TypedAst = aeso_ast_infer_types:infer(Ast, Options),
|
||||
%% io:format("Typed ast\n~p\n", [TypedAst]),
|
||||
%% aeso_ast:pp_typed(TypedAst),
|
||||
%% We find and look at the last contract.
|
||||
Contract = lists:last(TypedAst),
|
||||
Cname = contract_name(Contract),
|
||||
Tdefs = do_encode_contract_typedefs(sort_decls(contract_types(Contract))),
|
||||
Fdefs = [ do_encode_func(F) || F <- sort_decls(contract_funcs(Contract)),
|
||||
not is_private_func(F) ],
|
||||
Jmap = [{<<"contract">>,
|
||||
[{<<"name">>, do_encode_name(Cname)}] ++
|
||||
Tdefs ++
|
||||
[{<<"functions">>, Fdefs}]}],
|
||||
%% io:format("~p\n", [Jmap]),
|
||||
{ok,jsx:encode(Jmap)}
|
||||
catch
|
||||
%% The compiler errors.
|
||||
error:{parse_errors, Errors} ->
|
||||
{error, join_errors("Parse errors", Errors, fun(E) -> E end)};
|
||||
error:{type_errors, Errors} ->
|
||||
{error, join_errors("Type errors", Errors, fun(E) -> E end)};
|
||||
error:{code_errors, Errors} ->
|
||||
{error, join_errors("Code errors", Errors,
|
||||
fun (E) -> io_lib:format("~p", [E]) end)}
|
||||
%% General programming errors in the compiler just signal error.
|
||||
end.
|
||||
|
||||
join_errors(Prefix, Errors, Pfun) ->
|
||||
Ess = [ Pfun(E) || E <- Errors ],
|
||||
list_to_binary(string:join([Prefix|Ess], "\n")).
|
||||
|
||||
%% do_encode_contract_typedefs(TypeDefs) -> [JSON].
|
||||
%% Return a list of typedefs and state and event if they occur.
|
||||
|
||||
do_encode_contract_typedefs(Tdefs) ->
|
||||
Fun = fun(T, {Ts,Ss,Es}) ->
|
||||
%% Only one state and event.
|
||||
case typedef_name(T) of
|
||||
"state" -> {Ts,[do_encode_state_typedef(T)],Es};
|
||||
"event" -> {Ts,Ss,[do_encode_event_typedef(T)]};
|
||||
_Name -> {Ts ++ [do_encode_typedef(T)],Ss,Es}
|
||||
end
|
||||
end,
|
||||
{Ts,Ss,Es} = lists:foldl(Fun, {[],[],[]}, Tdefs),
|
||||
Ss ++ [{<<"type_defs">>, Ts}] ++ Es.
|
||||
|
||||
%% do_encode_state_typedef(StateTdef) -> JSON.
|
||||
%% do_encode_event_typedef(EventTdef) -> JSON.
|
||||
|
||||
do_encode_state_typedef(State) ->
|
||||
Def = typedef_def(State),
|
||||
{<<"state">>,do_encode_alias(Def)}.
|
||||
|
||||
do_encode_event_typedef(State) ->
|
||||
Def = typedef_def(State),
|
||||
{<<"event">>,do_encode_alias(Def)}.
|
||||
|
||||
%% encode_func(TypedAST) -> JSON.
|
||||
%% Encode a function AST into a JSON structure.
|
||||
|
||||
encode_func(AST) ->
|
||||
jsx:encode(do_encode_func(AST)).
|
||||
|
||||
%% do_encode_func(Function) -> JSONmap
|
||||
%% Encode a function definition. Currently we are only interested in
|
||||
%% the interface and type.
|
||||
|
||||
do_encode_func(Fdef) ->
|
||||
Name = function_name(Fdef),
|
||||
Args = function_args(Fdef),
|
||||
Type = function_type(Fdef),
|
||||
[{<<"name">>, do_encode_name(Name)},
|
||||
{<<"arguments">>, do_encode_args(Args)},
|
||||
{<<"returns">>, do_encode_type(Type)},
|
||||
{<<"stateful">>, is_stateful_func(Fdef)}].
|
||||
|
||||
%% encode_arg(TypedAST) -> JSON.
|
||||
%% Encode an argument AST into a JSON structure.
|
||||
|
||||
encode_arg(AST) ->
|
||||
jsx:encode(do_encode_arg(AST)).
|
||||
|
||||
%% do_encode_args(ArgASTs) -> [JSONmap].
|
||||
%% do_encode_arg(ArgAST) -> JSONmap.
|
||||
|
||||
do_encode_args(Args) ->
|
||||
[ do_encode_arg(A) || A <- Args ].
|
||||
|
||||
do_encode_arg(#arg{id=Id,type=T}) ->
|
||||
[{<<"name">>,do_encode_type(Id)},
|
||||
{<<"type">>,do_encode_type(T)}].
|
||||
|
||||
%% encode_type(TypedAST) -> JSON.
|
||||
%% Encode a type AST into a JSON structure.
|
||||
|
||||
encode_type(AST) ->
|
||||
jsx:encode(do_encode_type(AST)).
|
||||
|
||||
%% do_encode_types([TypeAST]) -> [JSONmap].
|
||||
%% do_encode_type(TypeAST) -> JsonMap.
|
||||
|
||||
do_encode_types(Types) ->
|
||||
[ do_encode_type(T) || T <- Types ].
|
||||
|
||||
do_encode_type(#tvar{name=N}) -> do_encode_name(N);
|
||||
do_encode_type(#id{name=N}) -> do_encode_name(N);
|
||||
do_encode_type(#con{name=N}) -> do_encode_name(N);
|
||||
do_encode_type(#qid{names=Ns}) ->
|
||||
do_encode_name(lists:join(".", Ns));
|
||||
do_encode_type(#qcon{names=Ns}) ->
|
||||
do_encode_name(lists:join(".", Ns)); %?
|
||||
do_encode_type(#tuple_t{args=As}) ->
|
||||
Eas = do_encode_types(As),
|
||||
[{<<"tuple">>,Eas}];
|
||||
do_encode_type(#bytes_t{len=Len}) ->
|
||||
{<<"bytes">>,Len};
|
||||
do_encode_type(#record_t{fields=Fs}) ->
|
||||
Efs = do_encode_type_rec_fields(Fs),
|
||||
[{<<"record">>,Efs}];
|
||||
%% Special case lists and maps as they are built-in types.
|
||||
do_encode_type(#app_t{id=#id{name="list"},fields=[F]}) ->
|
||||
Ef = do_encode_type(F),
|
||||
[{<<"list">>,Ef}];
|
||||
do_encode_type(#app_t{id=#id{name="map"},fields=Fs}) ->
|
||||
Ef = do_encode_type_mapo_field(Fs),
|
||||
[{<<"map">>,Ef}];
|
||||
%% Other applications.
|
||||
do_encode_type(#app_t{id=Id,fields=Fs}) ->
|
||||
Name = do_encode_type(Id),
|
||||
Efs = do_encode_types(Fs),
|
||||
[{Name,Efs}];
|
||||
do_encode_type(#variant_t{cons=Cs}) ->
|
||||
Ecs = do_encode_types(Cs),
|
||||
[{<<"variant">>,Ecs}];
|
||||
do_encode_type(#constr_t{con=C,args=As}) ->
|
||||
Ec = do_encode_type(C),
|
||||
Eas = do_encode_types(As),
|
||||
[{Ec,Eas}];
|
||||
do_encode_type(#fun_t{args=As,type=T}) ->
|
||||
Eas = do_encode_types(As),
|
||||
Et = do_encode_type(T),
|
||||
[{<<"function">>,[{<<"arguments">>,Eas},{<<"returns">>,Et}]}].
|
||||
|
||||
do_encode_name(Name) ->
|
||||
list_to_binary(Name).
|
||||
|
||||
%% do_encode_type_rec_fields(Fields) -> [JSONmap].
|
||||
%% do_encode_type_rec_field(Field) -> JSONmap.
|
||||
%% Encode a record field type.
|
||||
|
||||
do_encode_type_rec_fields(Fs) ->
|
||||
[ do_encode_type_rec_field(F) || F <- Fs ].
|
||||
|
||||
do_encode_type_rec_field(#field_t{id=Id,type=T}) ->
|
||||
[{<<"name">>,do_encode_type(Id)},
|
||||
{<<"type">>,do_encode_type(T)}].
|
||||
|
||||
%% do_encode_type_mapo_field(Field) -> JSONmap.
|
||||
%% Two fields for one map type.
|
||||
|
||||
do_encode_type_mapo_field([K,V]) ->
|
||||
[{<<"key">>,do_encode_type(K)},
|
||||
{<<"value">>,do_encode_type(V)}].
|
||||
|
||||
%% do_encode_typedef(TypeDefAST) -> JSON.
|
||||
|
||||
do_encode_typedef(Type) ->
|
||||
Name = typedef_name(Type),
|
||||
Vars = typedef_vars(Type),
|
||||
Def = typedef_def(Type),
|
||||
[{<<"name">>, do_encode_name(Name)},
|
||||
{<<"vars">>, do_encode_tvars(Vars)},
|
||||
{<<"typedef">>, do_encode_alias(Def)}].
|
||||
|
||||
do_encode_tvars(Vars) ->
|
||||
[ do_encode_tvar(V) || V <- Vars ].
|
||||
|
||||
do_encode_tvar(#tvar{name=N}) ->
|
||||
[{<<"name">>, do_encode_name(N)}].
|
||||
|
||||
do_encode_alias(#alias_t{type=T}) ->
|
||||
do_encode_type(T);
|
||||
do_encode_alias(A) -> do_encode_type(A).
|
||||
|
||||
%% encode_stmt(StmtAST) -> JSON.
|
||||
%% Encode a statement AST into a JSON structure.
|
||||
|
||||
encode_stmt(AST) ->
|
||||
jsx:encode(do_encode_stmt(AST)).
|
||||
|
||||
%% do_encode_stmt(StmtAST) -> JSONmap.
|
||||
|
||||
do_encode_stmt(#typed{expr=E}) -> %Ignore the type
|
||||
do_encode_stmt(E);
|
||||
do_encode_stmt(#block{body=Body}) ->
|
||||
Eblock = [ do_encode_stmt(B) || B <- Body ],
|
||||
[{<<"block">>,Eblock}];
|
||||
do_encode_stmt(#'if'{test=Test,then=Then,else=Else}) ->
|
||||
%% This is both a statement and en expression.
|
||||
Etest = do_encode_expr(Test),
|
||||
Ethen = do_encode_stmt(Then),
|
||||
Eelse = do_encode_stmt(Else),
|
||||
[{<<"if">>,[{<<"test">>,Etest},{<<"then">>,Ethen},{<<"else">>,Eelse}]}];
|
||||
do_encode_stmt(#letval{pat=Pat,exp=Exp}) ->
|
||||
Epat = do_encode_expr(Pat),
|
||||
Eexp = do_encode_expr(Exp),
|
||||
[{<<"let">>,[{<<"pattern">>,Epat},{<<"expression">>,Eexp}]}];
|
||||
do_encode_stmt(#switch{arg=Arg,cases=Cases}) ->
|
||||
Earg = do_encode_expr(Arg),
|
||||
Ecases = [ do_encode_stmt_case(Case) || Case <- Cases ],
|
||||
[{<<"switch">>,[{<<"arg">>,Earg},{<<"cases">>,Ecases}]}];
|
||||
do_encode_stmt(E) ->
|
||||
do_encode_expr(E).
|
||||
|
||||
do_encode_stmt_case(#'case'{pat=Pat,body=Body}) ->
|
||||
Epat = do_encode_expr(Pat), %Patterns are expessions
|
||||
Ebody = do_encode_stmt(Body),
|
||||
[{<<"pattern">>,Epat},{<<"body">>,Ebody}].
|
||||
|
||||
%% encode_expr(ExprAST) -> JSON.
|
||||
%% Encode an expression AST into a JSON structure.
|
||||
|
||||
encode_expr(AST) ->
|
||||
jsx:encode(do_encode_expr(AST)).
|
||||
|
||||
%% do_encode_exprs(ExprASTs) -> [JSONmap].
|
||||
%% do_encode_expr(ExprAST) -> JSONmap.
|
||||
|
||||
do_encode_exprs(Es) ->
|
||||
[ do_encode_expr(E) || E <- Es ].
|
||||
|
||||
do_encode_expr(#typed{expr=E}) -> %Ignore the type
|
||||
do_encode_expr(E);
|
||||
do_encode_expr(#id{name=N}) -> do_encode_name(N);
|
||||
do_encode_expr(#con{name=N}) -> do_encode_name(N);
|
||||
do_encode_expr(#qid{names=Ns}) ->
|
||||
do_encode_name(lists:join(".", Ns));
|
||||
do_encode_expr(#qcon{names=Ns}) ->
|
||||
do_encode_name(lists:join(".", Ns)); %?
|
||||
do_encode_expr(#bool{bool=B}) -> B;
|
||||
do_encode_expr(#int{value=V}) -> V;
|
||||
do_encode_expr(#string{bin=B}) ->
|
||||
[{<<"string">>,B}];
|
||||
do_encode_expr(#bytes{bin=B}) -> B;
|
||||
do_encode_expr(#tuple{args=As}) ->
|
||||
Eas = do_encode_exprs(As),
|
||||
[{<<"tuple">>,Eas}];
|
||||
do_encode_expr(#list{args=As}) ->
|
||||
Eas = do_encode_exprs(As),
|
||||
[{<<"list">>,Eas}];
|
||||
do_encode_expr(#record{fields=Fs}) -> %Create a record
|
||||
Efs = do_encode_expr_rec_fields(Fs),
|
||||
[{<<"create_record">>,Efs}];
|
||||
do_encode_expr({record,_Ann,Rec,Fs}) -> %Update a record
|
||||
Erec = do_encode_expr(Rec),
|
||||
Efs = do_encode_expr_rec_fields(Fs),
|
||||
[{<<"update_record">>,[Erec,Efs]}];
|
||||
do_encode_expr(#lam{args=As,body=B}) ->
|
||||
Eas = do_encode_args(As),
|
||||
Eb = do_encode_stmt(B),
|
||||
[{<<"function">>,[{<<"arguments">>,Eas},{<<"body">>,Eb}]}];
|
||||
do_encode_expr(#map{fields=Fs}) -> %Create a map
|
||||
Efs = do_encode_expr_map_fields(Fs),
|
||||
[{<<"create_map">>,Efs}];
|
||||
do_encode_expr({map,_Ann,Map,Fs}) -> %Update a map
|
||||
Emap = do_encode_expr(Map),
|
||||
Efs = do_encode_expr_map_fields(Fs),
|
||||
[{<<"update_map">>,[Emap,Efs]}];
|
||||
do_encode_expr(#map_get{field=F}) ->
|
||||
do_encode_expr(F);
|
||||
do_encode_expr(#proj{value=V}) ->
|
||||
do_encode_expr(V);
|
||||
do_encode_expr(#app{func=F,args=As}) ->
|
||||
Ef = do_encode_expr(F),
|
||||
Eas = do_encode_exprs(As),
|
||||
[{<<"apply">>,[{<<"function">>,Ef},
|
||||
{<<"arguments">>,Eas}]}];
|
||||
do_encode_expr(#'if'{test=Test,then=Then,else=Else}) ->
|
||||
%% This is both a statement and en expression.
|
||||
Etest = do_encode_expr(Test),
|
||||
Ethen = do_encode_expr(Then),
|
||||
Eelse = do_encode_expr(Else),
|
||||
[{<<"if">>,[{<<"test">>,Etest},{<<"then">>,Ethen},{<<"else">>,Eelse}]}];
|
||||
do_encode_expr({Op,_Ann}) ->
|
||||
list_to_binary(atom_to_list(Op)).
|
||||
|
||||
%% do_encode_expr_rec_fields(Fields) -> [JSON].
|
||||
%% do_encode_expr_rec_field(Field) -> JSON.
|
||||
%% Encode a record field expression.
|
||||
|
||||
do_encode_expr_rec_fields(Fs) ->
|
||||
[ do_encode_expr_rec_field(F) || F <- Fs ].
|
||||
|
||||
do_encode_expr_rec_field(#field{name=[N],value=V}) ->
|
||||
[{<<"name">>,do_encode_expr(N)},
|
||||
{<<"value">>,do_encode_expr(V)}].
|
||||
|
||||
%% do_encode_expr_map_fields(Fields) -> [JSON].
|
||||
%% do_encode_expr_map_field(Field) -> JSON.
|
||||
%% Encode a map field expression.
|
||||
|
||||
do_encode_expr_map_fields(Fs) ->
|
||||
[ do_encode_expr_map_field(F) || F <- Fs ].
|
||||
|
||||
do_encode_expr_map_field({K,V}) ->
|
||||
[{<<"key">>,do_encode_expr(K)},
|
||||
{<<"value">>,do_encode_expr(V)}];
|
||||
do_encode_expr_map_field(#field{name=[K],value=V}) ->
|
||||
[{<<"key">>,do_encode_expr(K)},
|
||||
{<<"value">>,do_encode_expr(V)}].
|
||||
|
||||
%% decode_contract(JSON) -> ContractString.
|
||||
%% Decode a JSON string and generate a suitable contract string which
|
||||
%% can be included in a contract definition. We decode into a map
|
||||
%% here as this is easier to work with and order is not important.
|
||||
|
||||
decode_contract(Json) ->
|
||||
Map = jsx:decode(Json, [return_maps]),
|
||||
%% io:format("~p\n", [Map]),
|
||||
#{<<"contract">> := C} = Map,
|
||||
#{<<"name">> := Name, <<"type_defs">> := Ts, <<"functions">> := Fs} = C,
|
||||
CS = ["contract"," ",io_lib:format("~s", [Name])," =\n",
|
||||
do_decode_tdefs(Ts),
|
||||
do_decode_funcs(Fs)],
|
||||
list_to_binary(CS).
|
||||
|
||||
do_decode_funcs(Fs) -> [ do_decode_func(F) || F <- Fs ].
|
||||
|
||||
do_decode_func(#{<<"name">> := <<"init">>}) -> [];
|
||||
do_decode_func(#{<<"name">> := Name,<<"arguments">> := As,<<"returns">> := T}) ->
|
||||
[" function"," ",io_lib:format("~s", [Name])," : ",
|
||||
do_decode_args(As)," => ",do_decode_type(T),$\n].
|
||||
|
||||
do_decode_args(As) ->
|
||||
Das = [ do_decode_arg(A) || A <- As ],
|
||||
[$(,lists:join(", ", Das),$)].
|
||||
|
||||
do_decode_arg(#{<<"type">> := T}) -> do_decode_type(T).
|
||||
|
||||
do_decode_types(Ets) ->
|
||||
[ do_decode_type(Et) || Et <- Ets ].
|
||||
|
||||
do_decode_type(#{<<"tuple">> := Ets}) ->
|
||||
Ts = do_decode_types(Ets),
|
||||
[$(,lists:join(", ", Ts),$)];
|
||||
do_decode_type(#{<<"record">> := Efs}) ->
|
||||
Fs = do_decode_type_rec_fields(Efs),
|
||||
[${,lists:join(", ", Fs),$}];
|
||||
do_decode_type(#{<<"list">> := Et}) ->
|
||||
T = do_decode_type(Et),
|
||||
["list",$(,T,$)];
|
||||
do_decode_type(#{<<"map">> := Et}) ->
|
||||
T = do_decode_type_map(Et),
|
||||
["map",$(,T,$)];
|
||||
do_decode_type(#{<<"variant">> := Ets}) ->
|
||||
Ts = do_decode_types(Ets),
|
||||
lists:join(" | ", Ts);
|
||||
do_decode_type(Econs) when is_map(Econs) -> %General constructor
|
||||
%% io:format("~p\n", [Econs]),
|
||||
[{Ec,Ets}] = maps:to_list(Econs),
|
||||
C = do_decode_name(Ec),
|
||||
Ts = do_decode_types(Ets),
|
||||
[C,$(,lists:join(", ", Ts),$)];
|
||||
do_decode_type(T) -> %Just raw names.
|
||||
do_decode_name(T).
|
||||
|
||||
do_decode_name(En) ->
|
||||
binary_to_list(En).
|
||||
|
||||
do_decode_type_rec_fields(Efs) ->
|
||||
[ do_decode_type_rec_field(Ef) || Ef <- Efs ].
|
||||
|
||||
do_decode_type_rec_field(#{<<"name">> := En,<<"type">> := Et}) ->
|
||||
Name = do_decode_name(En),
|
||||
Type = do_decode_type(Et),
|
||||
[Name," : ",Type].
|
||||
|
||||
do_decode_type_map(#{<<"key">> := Ek,<<"value">> := Ev}) ->
|
||||
Key = do_decode_type(Ek),
|
||||
Value = do_decode_type(Ev),
|
||||
[Key,", ",Value].
|
||||
|
||||
%% do_decode_tdefs(Json) -> [TypeString].
|
||||
%% Here we are only interested in the type definitions and ignore the
|
||||
%% aliases. We find them as they always have variants.
|
||||
|
||||
do_decode_tdefs(Ts) -> [ do_decode_tdef(T) ||
|
||||
#{<<"typedef">> := #{<<"variant">> := _}} = T <- Ts
|
||||
].
|
||||
|
||||
do_decode_tdef(#{<<"name">> := Name,<<"vars">> := Vs,<<"typedef">> := T}) ->
|
||||
[" datatype"," ",do_decode_name(Name),do_decode_tvars(Vs),
|
||||
" = ",do_decode_type(T),$\n].
|
||||
|
||||
do_decode_tvars([]) -> []; %No tvars, no parentheses
|
||||
do_decode_tvars(Vs) ->
|
||||
Dvs = [ do_decode_tvar(V) || V <- Vs ],
|
||||
[$(,lists:join(", ", Dvs),$)].
|
||||
|
||||
do_decode_tvar(#{<<"name">> := N}) -> io_lib:format("~s", [N]).
|
||||
|
||||
%% #contract{Ann, Con, [Declarations]}.
|
||||
|
||||
contract_name(#contract{con=#con{name=N}}) -> N.
|
||||
|
||||
contract_funcs(#contract{decls=Decls}) ->
|
||||
[ D || D <- Decls, is_record(D, letfun) ].
|
||||
|
||||
contract_types(#contract{decls=Decls}) ->
|
||||
[ D || D <- Decls, is_record(D, type_def) ].
|
||||
|
||||
%% To keep dialyzer happy and quiet.
|
||||
%% namespace_name(#namespace{con=#con{name=N}}) -> N.
|
||||
%%
|
||||
%% namespace_funcs(#namespace{decls=Decls}) ->
|
||||
%% [ D || D <- Decls, is_record(D, letfun) ].
|
||||
%%
|
||||
%% namespace_types(#namespace{decls=Decls}) ->
|
||||
%% [ D || D <- Decls, is_record(D, type_def) ].
|
||||
|
||||
sort_decls(Ds) ->
|
||||
Sort = fun (D1, D2) ->
|
||||
aeso_syntax:get_ann(line, D1, 0) =<
|
||||
aeso_syntax:get_ann(line, D2, 0)
|
||||
end,
|
||||
lists:sort(Sort, Ds).
|
||||
|
||||
%% #letfun{Ann, Id, [Arg], Type, Typedef}.
|
||||
|
||||
function_name(#letfun{id=#id{name=N}}) -> N.
|
||||
|
||||
function_args(#letfun{args=Args}) -> Args.
|
||||
|
||||
function_type(#letfun{type=Type}) -> Type.
|
||||
|
||||
is_private_func(#letfun{ann=A}) -> aeso_syntax:get_ann(private, A, false).
|
||||
|
||||
is_stateful_func(#letfun{ann=A}) -> aeso_syntax:get_ann(stateful, A, false).
|
||||
|
||||
%% #type_def{Ann, Id, [Var], Typedef}.
|
||||
|
||||
typedef_name(#type_def{id=#id{name=N}}) -> N.
|
||||
|
||||
typedef_vars(#type_def{vars=Vars}) -> Vars.
|
||||
|
||||
typedef_def(#type_def{typedef=Def}) -> Def.
|
||||
|
||||
%% parse(ContractString, Options) -> {ok,AST}.
|
||||
%% Signal errors, the sophia compiler way. Sigh!
|
||||
|
||||
parse(Text, Options) ->
|
||||
%% Try and return something sensible here!
|
||||
case aeso_parser:string(Text, Options) of
|
||||
%% Yay, it worked!
|
||||
{ok, Contract} -> Contract;
|
||||
%% Scan errors.
|
||||
{error, {Pos, scan_error}} ->
|
||||
parse_error(Pos, "scan error");
|
||||
{error, {Pos, scan_error_no_state}} ->
|
||||
parse_error(Pos, "scan error");
|
||||
%% Parse errors.
|
||||
{error, {Pos, parse_error, Error}} ->
|
||||
parse_error(Pos, Error);
|
||||
{error, {Pos, ambiguous_parse, As}} ->
|
||||
ErrorString = io_lib:format("Ambiguous ~p", [As]),
|
||||
parse_error(Pos, ErrorString);
|
||||
%% Include error
|
||||
{error, {Pos, include_error, File}} ->
|
||||
parse_error(Pos, io_lib:format("could not find include file '~s'", [File]))
|
||||
end.
|
||||
|
||||
parse_error(Pos, ErrorString) ->
|
||||
%% io:format("Error ~p ~p\n", [Pos,ErrorString]),
|
||||
Error = io_lib:format("~s: ~s", [pos_error(Pos), ErrorString]),
|
||||
error({parse_errors, [Error]}).
|
||||
|
||||
pos_error({Line, Pos}) ->
|
||||
io_lib:format("line ~p, column ~p", [Line, Pos]);
|
||||
pos_error({no_file, Line, Pos}) ->
|
||||
pos_error({Line, Pos});
|
||||
pos_error({File, Line, Pos}) ->
|
||||
io_lib:format("file ~s, line ~p, column ~p", [File, Line, Pos]).
|
||||
@@ -0,0 +1,27 @@
|
||||
-module(aeso_ast).
|
||||
|
||||
-export([int/2,
|
||||
line/1,
|
||||
pp/1,
|
||||
pp_typed/1,
|
||||
symbol/2,
|
||||
symbol_name/1
|
||||
]).
|
||||
|
||||
|
||||
symbol(Line, Chars) -> {symbol, Line, Chars}.
|
||||
int(Line, Int) -> {'Int', Line, Int}.
|
||||
|
||||
line({symbol, Line, _}) -> Line.
|
||||
|
||||
symbol_name({symbol, _, Name}) -> Name.
|
||||
|
||||
pp(Ast) ->
|
||||
String = prettypr:format(aeso_pretty:decls(Ast, [])),
|
||||
io:format("Ast:\n~s\n", [String]).
|
||||
|
||||
pp_typed(TypedAst) ->
|
||||
%% io:format("Typed tree:\n~p\n",[TypedAst]),
|
||||
String = prettypr:format(aeso_pretty:decls(TypedAst, [show_generated])),
|
||||
io:format("Type ast:\n~s\n",[String]).
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,846 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @author Happi (Erik Stenman)
|
||||
%%% @copyright (C) 2017, Aeternity Anstalt
|
||||
%%% @doc
|
||||
%%% Compiler from Aeterinty Sophia language to the Aeternity VM, aevm.
|
||||
%%% @end
|
||||
%%% Created : 21 Dec 2017
|
||||
%%%
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeso_ast_to_icode).
|
||||
|
||||
-export([ast_typerep/1, ast_typerep/2, type_value/1,
|
||||
convert_typed/2, prim_call/5]).
|
||||
|
||||
-include_lib("aebytecode/include/aeb_opcodes.hrl").
|
||||
-include("aeso_icode.hrl").
|
||||
|
||||
-spec convert_typed(aeso_syntax:ast(), list()) -> aeso_icode:icode().
|
||||
convert_typed(TypedTree, Options) ->
|
||||
Name = case lists:last(TypedTree) of
|
||||
{contract, _, {con, _, Con}, _} -> Con;
|
||||
_ -> gen_error(last_declaration_must_be_contract)
|
||||
end,
|
||||
Icode = code(TypedTree, aeso_icode:set_name(Name, aeso_icode:new(Options))),
|
||||
deadcode_elimination(Icode).
|
||||
|
||||
code([{contract, _Attribs, Con, Code}|Rest], Icode) ->
|
||||
NewIcode = contract_to_icode(Code, aeso_icode:set_namespace(Con, Icode)),
|
||||
code(Rest, NewIcode);
|
||||
code([{namespace, _Ann, Name, Code}|Rest], Icode) ->
|
||||
%% TODO: nested namespaces
|
||||
NewIcode = contract_to_icode(Code, aeso_icode:set_namespace(Name, Icode)),
|
||||
code(Rest, NewIcode);
|
||||
code([], Icode) ->
|
||||
add_default_init_function(add_builtins(Icode)).
|
||||
|
||||
%% Generate error on correct format.
|
||||
|
||||
gen_error(Error) ->
|
||||
error({code_errors, [Error]}).
|
||||
|
||||
%% Create default init function (only if state is unit).
|
||||
add_default_init_function(Icode = #{functions := Funs, state_type := State}) ->
|
||||
{_, _, QInit} = aeso_icode:qualify({id, [], "init"}, Icode),
|
||||
case lists:keymember(QInit, 1, Funs) of
|
||||
true -> Icode;
|
||||
false when State /= {tuple, []} ->
|
||||
gen_error(missing_init_function);
|
||||
false ->
|
||||
Type = {tuple, [typerep, {tuple, []}]},
|
||||
Value = #tuple{ cpts = [type_value({tuple, []}), {tuple, []}] },
|
||||
DefaultInit = {QInit, [], [], Value, Type},
|
||||
Icode#{ functions => [DefaultInit | Funs] }
|
||||
end.
|
||||
|
||||
-spec contract_to_icode(aeso_syntax:ast(), aeso_icode:icode()) ->
|
||||
aeso_icode:icode().
|
||||
contract_to_icode([{namespace, _, Name, Defs} | Rest], Icode) ->
|
||||
NS = aeso_icode:get_namespace(Icode),
|
||||
Icode1 = contract_to_icode(Defs, aeso_icode:enter_namespace(Name, Icode)),
|
||||
contract_to_icode(Rest, aeso_icode:set_namespace(NS, Icode1));
|
||||
contract_to_icode([{type_def, _Attrib, Id = {id, _, Name}, Args, Def} | Rest],
|
||||
Icode = #{ types := Types, constructors := Constructors }) ->
|
||||
TypeDef = make_type_def(Args, Def, Icode),
|
||||
NewConstructors =
|
||||
case Def of
|
||||
{variant_t, Cons} ->
|
||||
Tags = lists:seq(0, length(Cons) - 1),
|
||||
GetName = fun({constr_t, _, C, _}) -> C end,
|
||||
QName = fun(Con) -> {_, _, Xs} = aeso_icode:qualify(GetName(Con), Icode), Xs end,
|
||||
maps:from_list([ {QName(Con), Tag} || {Tag, Con} <- lists:zip(Tags, Cons) ]);
|
||||
_ -> #{}
|
||||
end,
|
||||
{_, _, TName} = aeso_icode:qualify(Id, Icode),
|
||||
Icode1 = Icode#{ types := Types#{ TName => TypeDef },
|
||||
constructors := maps:merge(Constructors, NewConstructors) },
|
||||
Icode2 = case Name of
|
||||
"state" when Args == [] -> Icode1#{ state_type => ast_typerep(Def, Icode) };
|
||||
"state" -> gen_error(state_type_cannot_be_parameterized);
|
||||
"event" when Args == [] -> Icode1#{ event_type => Def };
|
||||
"event" -> gen_error(event_type_cannot_be_parameterized);
|
||||
_ -> Icode1
|
||||
end,
|
||||
contract_to_icode(Rest, Icode2);
|
||||
contract_to_icode([{letfun, Attrib, Name, Args, _What, Body={typed,_,_,T}}|Rest], Icode) ->
|
||||
FunAttrs = [ stateful || proplists:get_value(stateful, Attrib, false) ] ++
|
||||
[ private || is_private(Attrib, Icode) ],
|
||||
%% TODO: Handle types
|
||||
FunName = ast_id(Name),
|
||||
%% TODO: push funname to env
|
||||
FunArgs = ast_args(Args, [], Icode),
|
||||
%% TODO: push args to env
|
||||
{FunBody, TypeRep} =
|
||||
case FunName of
|
||||
"init" ->
|
||||
%% Pair the initial state with a typerep for the state (TODO: until we have the state type in some contract metadata)
|
||||
#{ state_type := StateType } = Icode,
|
||||
{#tuple{ cpts = [type_value(StateType), ast_body(Body, Icode)] },
|
||||
{tuple, [typerep, ast_typerep(T, Icode)]}};
|
||||
_ -> {ast_body(Body, Icode), ast_typerep(T, Icode)}
|
||||
end,
|
||||
QName = aeso_icode:qualify(Name, Icode),
|
||||
NewIcode = ast_fun_to_icode(ast_id(QName), FunAttrs, FunArgs, FunBody, TypeRep, Icode),
|
||||
contract_to_icode(Rest, NewIcode);
|
||||
contract_to_icode([{letrec,_,Defs}|Rest], Icode) ->
|
||||
%% OBS! This code ignores the letrec structure of the source,
|
||||
%% because the back end treats ALL declarations as recursive! We
|
||||
%% need to decide whether to (a) modify the back end to respect
|
||||
%% the letrec structure, or (b) (preferably) modify the front end
|
||||
%% just to parse a list of (mutually recursive) definitions.
|
||||
contract_to_icode(Defs++Rest, Icode);
|
||||
contract_to_icode([], Icode) -> Icode;
|
||||
contract_to_icode([{fun_decl, _, _, _} | Code], Icode) ->
|
||||
contract_to_icode(Code, Icode);
|
||||
contract_to_icode([Decl | Code], Icode) ->
|
||||
io:format("Unhandled declaration: ~p\n", [Decl]),
|
||||
contract_to_icode(Code, Icode).
|
||||
|
||||
ast_id({id, _, Id}) -> Id;
|
||||
ast_id({qid, _, Id}) -> Id.
|
||||
|
||||
ast_args([{arg, _, Name, Type}|Rest], Acc, Icode) ->
|
||||
ast_args(Rest, [{ast_id(Name), ast_type(Type, Icode)}| Acc], Icode);
|
||||
ast_args([], Acc, _Icode) -> lists:reverse(Acc).
|
||||
|
||||
ast_type(T, Icode) ->
|
||||
ast_typerep(T, Icode).
|
||||
|
||||
-define(id_app(Fun, Args, ArgTypes, OutType),
|
||||
{app, _, {typed, _, {id, _, Fun}, {fun_t, _, _, ArgTypes, OutType}}, Args}).
|
||||
|
||||
-define(qid_app(Fun, Args, ArgTypes, OutType),
|
||||
{app, _, {typed, _, {qid, _, Fun}, {fun_t, _, _, ArgTypes, OutType}}, Args}).
|
||||
|
||||
-define(oracle_t(Q, R), {app_t, _, {id, _, "oracle"}, [Q, R]}).
|
||||
-define(query_t(Q, R), {app_t, _, {id, _, "oracle_query"}, [Q, R]}).
|
||||
-define(option_t(A), {app_t, _, {id, _, "option"}, [A]}).
|
||||
-define(map_t(K, V), {app_t, _, {id, _, "map"}, [K, V]}).
|
||||
|
||||
ast_body(?qid_app(["Chain","spend"], [To, Amount], _, _), Icode) ->
|
||||
prim_call(?PRIM_CALL_SPEND, ast_body(Amount, Icode), [ast_body(To, Icode)], [word], {tuple, []});
|
||||
|
||||
ast_body(?qid_app([Con, "Chain", "event"], [Event], _, _), Icode = #{ contract_name := Con }) ->
|
||||
aeso_builtins:check_event_type(Icode),
|
||||
builtin_call({event, maps:get(event_type, Icode)}, [ast_body(Event, Icode)]);
|
||||
|
||||
%% Chain environment
|
||||
ast_body(?qid_app(["Chain", "balance"], [Address], _, _), Icode) ->
|
||||
#prim_balance{ address = ast_body(Address, Icode) };
|
||||
ast_body(?qid_app(["Chain", "block_hash"], [Height], _, _), Icode) ->
|
||||
#prim_block_hash{ height = ast_body(Height, Icode) };
|
||||
ast_body(?qid_app(["Call", "gas_left"], [], _, _), _Icode) ->
|
||||
prim_gas_left;
|
||||
ast_body({qid, _, ["Contract", "address"]}, _Icode) -> prim_contract_address;
|
||||
ast_body({qid, _, ["Contract", "balance"]}, _Icode) -> #prim_balance{ address = prim_contract_address };
|
||||
ast_body({qid, _, ["Call", "origin"]}, _Icode) -> prim_call_origin;
|
||||
ast_body({qid, _, ["Call", "caller"]}, _Icode) -> prim_caller;
|
||||
ast_body({qid, _, ["Call", "value"]}, _Icode) -> prim_call_value;
|
||||
ast_body({qid, _, ["Call", "gas_price"]}, _Icode) -> prim_gas_price;
|
||||
ast_body({qid, _, ["Chain", "coinbase"]}, _Icode) -> prim_coinbase;
|
||||
ast_body({qid, _, ["Chain", "timestamp"]}, _Icode) -> prim_timestamp;
|
||||
ast_body({qid, _, ["Chain", "block_height"]}, _Icode) -> prim_block_height;
|
||||
ast_body({qid, _, ["Chain", "difficulty"]}, _Icode) -> prim_difficulty;
|
||||
ast_body({qid, _, ["Chain", "gas_limit"]}, _Icode) -> prim_gas_limit;
|
||||
%% TODO: eta expand!
|
||||
ast_body({qid, _, ["Chain", "balance"]}, _Icode) ->
|
||||
gen_error({underapplied_primitive, 'Chain.balance'});
|
||||
ast_body({qid, _, ["Chain", "block_hash"]}, _Icode) ->
|
||||
gen_error({underapplied_primitive, 'Chain.block_hash'});
|
||||
ast_body({qid, _, ["Chain", "spend"]}, _Icode) ->
|
||||
gen_error({underapplied_primitive, 'Chain.spend'});
|
||||
|
||||
%% State
|
||||
ast_body({qid, _, [Con, "state"]}, #{ contract_name := Con }) -> prim_state;
|
||||
ast_body(?qid_app([Con, "put"], [NewState], _, _), Icode = #{ contract_name := Con }) ->
|
||||
#prim_put{ state = ast_body(NewState, Icode) };
|
||||
ast_body({qid, _, [Con, "put"]}, #{ contract_name := Con }) ->
|
||||
gen_error({underapplied_primitive, put}); %% TODO: eta
|
||||
|
||||
%% Abort
|
||||
ast_body(?id_app("abort", [String], _, _), Icode) ->
|
||||
#funcall{ function = #var_ref{ name = {builtin, abort} },
|
||||
args = [ast_body(String, Icode)] };
|
||||
|
||||
%% Authentication
|
||||
ast_body({qid, _, ["Auth", "tx_hash"]}, _Icode) ->
|
||||
prim_call(?PRIM_CALL_AUTH_TX_HASH, #integer{value = 0},
|
||||
[], [], aeso_icode:option_typerep(word));
|
||||
|
||||
%% Oracles
|
||||
ast_body(?qid_app(["Oracle", "register"], Args, _, ?oracle_t(QType, RType)), Icode) ->
|
||||
{Sign, [Acct, QFee, TTL]} = get_signature_arg(Args),
|
||||
prim_call(?PRIM_CALL_ORACLE_REGISTER, #integer{value = 0},
|
||||
[ast_body(Acct, Icode), ast_body(Sign, Icode), ast_body(QFee, Icode), ast_body(TTL, Icode),
|
||||
ast_type_value(QType, Icode), ast_type_value(RType, Icode)],
|
||||
[word, sign_t(), word, ttl_t(Icode), typerep, typerep], word);
|
||||
|
||||
ast_body(?qid_app(["Oracle", "query_fee"], [Oracle], _, _), Icode) ->
|
||||
prim_call(?PRIM_CALL_ORACLE_QUERY_FEE, #integer{value = 0},
|
||||
[ast_body(Oracle, Icode)], [word], word);
|
||||
|
||||
ast_body(?qid_app(["Oracle", "query"], [Oracle, Q, QFee, QTTL, RTTL], [_, QType, _, _, _], _), Icode) ->
|
||||
prim_call(?PRIM_CALL_ORACLE_QUERY, ast_body(QFee, Icode),
|
||||
[ast_body(Oracle, Icode), ast_body(Q, Icode), ast_body(QTTL, Icode), ast_body(RTTL, Icode)],
|
||||
[word, ast_type(QType, Icode), ttl_t(Icode), ttl_t(Icode)], word);
|
||||
|
||||
ast_body(?qid_app(["Oracle", "extend"], Args, _, _), Icode) ->
|
||||
{Sign, [Oracle, TTL]} = get_signature_arg(Args),
|
||||
prim_call(?PRIM_CALL_ORACLE_EXTEND, #integer{value = 0},
|
||||
[ast_body(Oracle, Icode), ast_body(Sign, Icode), ast_body(TTL, Icode)],
|
||||
[word, sign_t(), ttl_t(Icode)], {tuple, []});
|
||||
|
||||
ast_body(?qid_app(["Oracle", "respond"], Args, [_, _, RType], _), Icode) ->
|
||||
{Sign, [Oracle, Query, R]} = get_signature_arg(Args),
|
||||
prim_call(?PRIM_CALL_ORACLE_RESPOND, #integer{value = 0},
|
||||
[ast_body(Oracle, Icode), ast_body(Query, Icode), ast_body(Sign, Icode), ast_body(R, Icode)],
|
||||
[word, word, sign_t(), ast_type(RType, Icode)], {tuple, []});
|
||||
|
||||
ast_body(?qid_app(["Oracle", "get_question"], [Oracle, Q], [_, ?query_t(QType, _)], _), Icode) ->
|
||||
prim_call(?PRIM_CALL_ORACLE_GET_QUESTION, #integer{value = 0},
|
||||
[ast_body(Oracle, Icode), ast_body(Q, Icode)], [word, word], ast_type(QType, Icode));
|
||||
|
||||
ast_body(?qid_app(["Oracle", "get_answer"], [Oracle, Q], [_, ?query_t(_, RType)], _), Icode) ->
|
||||
prim_call(?PRIM_CALL_ORACLE_GET_ANSWER, #integer{value = 0},
|
||||
[ast_body(Oracle, Icode), ast_body(Q, Icode)], [word, word], aeso_icode:option_typerep(ast_type(RType, Icode)));
|
||||
|
||||
ast_body({qid, _, ["Oracle", "register"]}, _Icode) -> gen_error({underapplied_primitive, 'Oracle.register'});
|
||||
ast_body({qid, _, ["Oracle", "query"]}, _Icode) -> gen_error({underapplied_primitive, 'Oracle.query'});
|
||||
ast_body({qid, _, ["Oracle", "extend"]}, _Icode) -> gen_error({underapplied_primitive, 'Oracle.extend'});
|
||||
ast_body({qid, _, ["Oracle", "respond"]}, _Icode) -> gen_error({underapplied_primitive, 'Oracle.respond'});
|
||||
ast_body({qid, _, ["Oracle", "query_fee"]}, _Icode) -> gen_error({underapplied_primitive, 'Oracle.query_fee'});
|
||||
ast_body({qid, _, ["Oracle", "get_answer"]}, _Icode) -> gen_error({underapplied_primitive, 'Oracle.get_answer'});
|
||||
ast_body({qid, _, ["Oracle", "get_question"]}, _Icode) -> gen_error({underapplied_primitive, 'Oracle.get_question'});
|
||||
|
||||
%% Name service
|
||||
ast_body(?qid_app(["AENS", "resolve"], [Name, Key], _, ?option_t(Type)), Icode) ->
|
||||
case is_monomorphic(Type) of
|
||||
true ->
|
||||
case ast_type(Type, Icode) of
|
||||
T when T == word; T == string -> ok;
|
||||
_ -> gen_error({invalid_result_type, 'AENS.resolve', Type})
|
||||
end,
|
||||
prim_call(?PRIM_CALL_AENS_RESOLVE, #integer{value = 0},
|
||||
[ast_body(Name, Icode), ast_body(Key, Icode), ast_type_value(Type, Icode)],
|
||||
[string, string, typerep], aeso_icode:option_typerep(ast_type(Type, Icode)));
|
||||
false ->
|
||||
gen_error({unresolved_result_type, 'AENS.resolve', Type})
|
||||
end;
|
||||
|
||||
ast_body(?qid_app(["AENS", "preclaim"], Args, _, _), Icode) ->
|
||||
{Sign, [Addr, CHash]} = get_signature_arg(Args),
|
||||
prim_call(?PRIM_CALL_AENS_PRECLAIM, #integer{value = 0},
|
||||
[ast_body(Addr, Icode), ast_body(CHash, Icode), ast_body(Sign, Icode)],
|
||||
[word, word, sign_t()], {tuple, []});
|
||||
|
||||
ast_body(?qid_app(["AENS", "claim"], Args, _, _), Icode) ->
|
||||
{Sign, [Addr, Name, Salt]} = get_signature_arg(Args),
|
||||
prim_call(?PRIM_CALL_AENS_CLAIM, #integer{value = 0},
|
||||
[ast_body(Addr, Icode), ast_body(Name, Icode), ast_body(Salt, Icode), ast_body(Sign, Icode)],
|
||||
[word, string, word, sign_t()], {tuple, []});
|
||||
|
||||
ast_body(?qid_app(["AENS", "transfer"], Args, _, _), Icode) ->
|
||||
{Sign, [FromAddr, ToAddr, NameHash]} = get_signature_arg(Args),
|
||||
prim_call(?PRIM_CALL_AENS_TRANSFER, #integer{value = 0},
|
||||
[ast_body(FromAddr, Icode), ast_body(ToAddr, Icode), ast_body(NameHash, Icode), ast_body(Sign, Icode)],
|
||||
[word, word, word, sign_t()], {tuple, []});
|
||||
|
||||
ast_body(?qid_app(["AENS", "revoke"], Args, _, _), Icode) ->
|
||||
{Sign, [Addr, NameHash]} = get_signature_arg(Args),
|
||||
prim_call(?PRIM_CALL_AENS_REVOKE, #integer{value = 0},
|
||||
[ast_body(Addr, Icode), ast_body(NameHash, Icode), ast_body(Sign, Icode)],
|
||||
[word, word, sign_t()], {tuple, []});
|
||||
|
||||
ast_body({qid, _, ["AENS", "resolve"]}, _Icode) -> gen_error({underapplied_primitive, 'AENS.resolve'});
|
||||
ast_body({qid, _, ["AENS", "preclaim"]}, _Icode) -> gen_error({underapplied_primitive, 'AENS.preclaim'});
|
||||
ast_body({qid, _, ["AENS", "claim"]}, _Icode) -> gen_error({underapplied_primitive, 'AENS.claim'});
|
||||
ast_body({qid, _, ["AENS", "transfer"]}, _Icode) -> gen_error({underapplied_primitive, 'AENS.transfer'});
|
||||
ast_body({qid, _, ["AENS", "revoke"]}, _Icode) -> gen_error({underapplied_primitive, 'AENS.revoke'});
|
||||
|
||||
%% Maps
|
||||
|
||||
%% -- map lookup m[k]
|
||||
ast_body({map_get, _, Map, Key}, Icode) ->
|
||||
{_, ValType} = check_monomorphic_map(Map, Icode),
|
||||
Fun = {map_get, ast_typerep(ValType, Icode)},
|
||||
builtin_call(Fun, [ast_body(Map, Icode), ast_body(Key, Icode)]);
|
||||
%% -- map lookup_default m[k = v]
|
||||
ast_body({map_get, _, Map, Key, Val}, Icode) ->
|
||||
{_, ValType} = check_monomorphic_map(Map, Icode),
|
||||
Fun = {map_lookup_default, ast_typerep(ValType, Icode)},
|
||||
builtin_call(Fun, [ast_body(Map, Icode), ast_body(Key, Icode), ast_body(Val, Icode)]);
|
||||
|
||||
%% -- lookup functions
|
||||
ast_body(?qid_app(["Map", "lookup"], [Key, Map], _, _), Icode) ->
|
||||
map_get(Key, Map, Icode);
|
||||
ast_body(?qid_app(["Map", "lookup_default"], [Key, Map, Val], _, _), Icode) ->
|
||||
{_, ValType} = check_monomorphic_map(Map, Icode),
|
||||
Fun = {map_lookup_default, ast_typerep(ValType, Icode)},
|
||||
builtin_call(Fun, [ast_body(Map, Icode), ast_body(Key, Icode), ast_body(Val, Icode)]);
|
||||
ast_body(?qid_app(["Map", "member"], [Key, Map], _, _), Icode) ->
|
||||
builtin_call(map_member, [ast_body(Map, Icode), ast_body(Key, Icode)]);
|
||||
ast_body(?qid_app(["Map", "size"], [Map], _, _), Icode) ->
|
||||
builtin_call(map_size, [ast_body(Map, Icode)]);
|
||||
ast_body(?qid_app(["Map", "delete"], [Key, Map], _, _), Icode) ->
|
||||
map_del(Key, Map, Icode);
|
||||
|
||||
%% -- map conversion to/from list
|
||||
ast_body(App = ?qid_app(["Map", "from_list"], [List], _, MapType), Icode) ->
|
||||
Ann = aeso_syntax:get_ann(App),
|
||||
{KeyType, ValType} = check_monomorphic_map(Ann, MapType, Icode),
|
||||
builtin_call(map_from_list, [ast_body(List, Icode), map_empty(KeyType, ValType, Icode)]);
|
||||
|
||||
ast_body(?qid_app(["Map", "to_list"], [Map], _, _), Icode) ->
|
||||
map_tolist(Map, Icode);
|
||||
|
||||
ast_body({qid, _, ["Map", "from_list"]}, _Icode) -> gen_error({underapplied_primitive, 'Map.from_list'});
|
||||
%% ast_body({qid, _, ["Map", "to_list"]}, _Icode) -> gen_error({underapplied_primitive, 'Map.to_list'});
|
||||
ast_body({qid, _, ["Map", "lookup"]}, _Icode) -> gen_error({underapplied_primitive, 'Map.lookup'});
|
||||
ast_body({qid, _, ["Map", "lookup_default"]}, _Icode) -> gen_error({underapplied_primitive, 'Map.lookup_default'});
|
||||
ast_body({qid, _, ["Map", "member"]}, _Icode) -> gen_error({underapplied_primitive, 'Map.member'});
|
||||
|
||||
%% -- map construction { k1 = v1, k2 = v2 }
|
||||
ast_body({typed, Ann, {map, _, KVs}, MapType}, Icode) ->
|
||||
{KeyType, ValType} = check_monomorphic_map(Ann, MapType, Icode),
|
||||
lists:foldr(fun({K, V}, Map) ->
|
||||
builtin_call(map_put, [Map, ast_body(K, Icode), ast_body(V, Icode)])
|
||||
end, map_empty(KeyType, ValType, Icode), KVs);
|
||||
|
||||
%% -- map update m { [k] = v } or m { [k] @ x = f(x) } or m { [k = v] @ x = f(x) }
|
||||
ast_body({map, _, Map, []}, Icode) -> ast_body(Map, Icode);
|
||||
ast_body({map, _, Map, [Upd]}, Icode) ->
|
||||
case Upd of
|
||||
{field, _, [{map_get, _, Key}], Val} ->
|
||||
map_put(Key, Val, Map, Icode);
|
||||
{field_upd, _, [{map_get, _, Key}], ValFun} ->
|
||||
map_upd(Key, ValFun, Map, Icode);
|
||||
{field_upd, _, [{map_get, _, Key, Val}], ValFun} ->
|
||||
map_upd(Key, Val, ValFun, Map, Icode)
|
||||
end;
|
||||
ast_body({map, Ann, Map, [Upd | Upds]}, Icode) ->
|
||||
ast_body({map, Ann, {map, Ann, Map, [Upd]}, Upds}, Icode);
|
||||
|
||||
%% Crypto
|
||||
ast_body(?qid_app(["Crypto", "ecverify"], [Msg, PK, Sig], _, _), Icode) ->
|
||||
prim_call(?PRIM_CALL_CRYPTO_ECVERIFY, #integer{value = 0},
|
||||
[ast_body(Msg, Icode), ast_body(PK, Icode), ast_body(Sig, Icode)],
|
||||
[word, word, sign_t()], word);
|
||||
|
||||
ast_body(?qid_app(["Crypto", "ecverify_secp256k1"], [Msg, PK, Sig], _, _), Icode) ->
|
||||
prim_call(?PRIM_CALL_CRYPTO_ECVERIFY_SECP256K1, #integer{value = 0},
|
||||
[ast_body(Msg, Icode), ast_body(PK, Icode), ast_body(Sig, Icode)],
|
||||
[bytes_t(32), bytes_t(64), bytes_t(64)], word);
|
||||
|
||||
ast_body(?qid_app(["Crypto", "sha3"], [Term], [Type], _), Icode) ->
|
||||
generic_hash_primop(?PRIM_CALL_CRYPTO_SHA3, Term, Type, Icode);
|
||||
ast_body(?qid_app(["Crypto", "sha256"], [Term], [Type], _), Icode) ->
|
||||
generic_hash_primop(?PRIM_CALL_CRYPTO_SHA256, Term, Type, Icode);
|
||||
ast_body(?qid_app(["Crypto", "blake2b"], [Term], [Type], _), Icode) ->
|
||||
generic_hash_primop(?PRIM_CALL_CRYPTO_BLAKE2B, Term, Type, Icode);
|
||||
ast_body(?qid_app(["String", "sha256"], [String], _, _), Icode) ->
|
||||
string_hash_primop(?PRIM_CALL_CRYPTO_SHA256_STRING, String, Icode);
|
||||
ast_body(?qid_app(["String", "blake2b"], [String], _, _), Icode) ->
|
||||
string_hash_primop(?PRIM_CALL_CRYPTO_BLAKE2B_STRING, String, Icode);
|
||||
|
||||
%% Strings
|
||||
%% -- String length
|
||||
ast_body(?qid_app(["String", "length"], [String], _, _), Icode) ->
|
||||
#funcall{ function = #var_ref{ name = {builtin, string_length} },
|
||||
args = [ast_body(String, Icode)] };
|
||||
|
||||
%% -- String concat
|
||||
ast_body(?qid_app(["String", "concat"], [String1, String2], _, _), Icode) ->
|
||||
#funcall{ function = #var_ref{ name = {builtin, string_concat} },
|
||||
args = [ast_body(String1, Icode), ast_body(String2, Icode)] };
|
||||
|
||||
%% -- String hash (sha3)
|
||||
ast_body(?qid_app(["String", "sha3"], [String], _, _), Icode) ->
|
||||
#unop{ op = 'sha3', rand = ast_body(String, Icode) };
|
||||
|
||||
%% -- Bits
|
||||
ast_body(?qid_app(["Bits", Fun], Args, _, _), Icode)
|
||||
when Fun == "test"; Fun == "set"; Fun == "clear";
|
||||
Fun == "union"; Fun == "intersection"; Fun == "difference" ->
|
||||
C = fun(N) when is_integer(N) -> #integer{ value = N };
|
||||
(X) -> X end,
|
||||
Bin = fun(O) -> fun(A, B) -> #binop{ op = O, left = C(A), right = C(B) } end end,
|
||||
And = Bin('band'),
|
||||
Or = Bin('bor'),
|
||||
Bsl = fun(A, B) -> (Bin('bsl'))(B, A) end, %% flipped arguments
|
||||
Bsr = fun(A, B) -> (Bin('bsr'))(B, A) end,
|
||||
Neg = fun(A) -> #unop{ op = 'bnot', rand = C(A) } end,
|
||||
case [Fun | [ ast_body(Arg, Icode) || Arg <- Args ]] of
|
||||
["test", Bits, Ix] -> And(Bsr(Bits, Ix), 1);
|
||||
["set", Bits, Ix] -> Or(Bits, Bsl(1, Ix));
|
||||
["clear", Bits, Ix] -> And(Bits, Neg(Bsl(1, Ix)));
|
||||
["union", A, B] -> Or(A, B);
|
||||
["intersection", A, B] -> And(A, B);
|
||||
["difference", A, B] -> And(A, Neg(And(A, B)))
|
||||
end;
|
||||
ast_body({qid, _, ["Bits", "none"]}, _Icode) ->
|
||||
#integer{ value = 0 };
|
||||
ast_body({qid, _, ["Bits", "all"]}, _Icode) ->
|
||||
#integer{ value = 1 bsl 256 - 1 };
|
||||
ast_body(?qid_app(["Bits", "sum"], [Bits], _, _), Icode) ->
|
||||
builtin_call(popcount, [ast_body(Bits, Icode), #integer{ value = 0 }]);
|
||||
|
||||
%% -- Conversion
|
||||
ast_body(?qid_app(["Int", "to_str"], [Int], _, _), Icode) ->
|
||||
builtin_call(int_to_str, [ast_body(Int, Icode)]);
|
||||
|
||||
ast_body(?qid_app(["Address", "to_str"], [Addr], _, _), Icode) ->
|
||||
builtin_call(addr_to_str, [ast_body(Addr, Icode)]);
|
||||
|
||||
%% Other terms
|
||||
ast_body({id, _, Name}, _Icode) ->
|
||||
#var_ref{name = Name};
|
||||
ast_body({qid, _, Name}, _Icode) ->
|
||||
#var_ref{name = Name};
|
||||
ast_body({bool, _, Bool}, _Icode) -> %BOOL as ints
|
||||
Value = if Bool -> 1 ; true -> 0 end,
|
||||
#integer{value = Value};
|
||||
ast_body({int, _, Value}, _Icode) ->
|
||||
#integer{value = Value};
|
||||
ast_body({bytes, _, Bin}, _Icode) ->
|
||||
case aeb_memory:binary_to_words(Bin) of
|
||||
[Word] -> #integer{value = Word};
|
||||
Words -> #tuple{cpts = [#integer{value = W} || W <- Words]}
|
||||
end;
|
||||
ast_body({Key, _, Bin}, _Icode) when Key == account_pubkey;
|
||||
Key == contract_pubkey;
|
||||
Key == oracle_pubkey;
|
||||
Key == oracle_query_id ->
|
||||
<<Value:32/unit:8>> = Bin,
|
||||
#integer{value = Value};
|
||||
ast_body({string,_,Bin}, _Icode) ->
|
||||
Cpts = [size(Bin) | aeb_memory:binary_to_words(Bin)],
|
||||
#tuple{cpts = [#integer{value=X} || X <- Cpts]};
|
||||
ast_body({tuple,_,Args}, Icode) ->
|
||||
#tuple{cpts = [ast_body(A, Icode) || A <- Args]};
|
||||
ast_body({list,_,Args}, Icode) ->
|
||||
#list{elems = [ast_body(A, Icode) || A <- Args]};
|
||||
%% Typed contract calls
|
||||
ast_body({proj, _, {typed, _, Addr, {con, _, _}}, {id, _, "address"}}, Icode) ->
|
||||
ast_body(Addr, Icode); %% Values of contract types _are_ addresses.
|
||||
ast_body({app, _, {typed, _, {proj, _, {typed, _, Addr, {con, _, Contract}}, {id, _, FunName}},
|
||||
{fun_t, _, NamedT, ArgsT, OutT}}, Args0}, Icode) ->
|
||||
NamedArgs = [Arg || Arg = {named_arg, _, _, _} <- Args0],
|
||||
Args = Args0 -- NamedArgs,
|
||||
ArgOpts = [ {Name, ast_body(Value, Icode)} || {named_arg, _, {id, _, Name}, Value} <- NamedArgs ],
|
||||
Defaults = [ {Name, ast_body(Default, Icode)} || {named_arg_t, _, {id, _, Name}, _, Default} <- NamedT ],
|
||||
%% TODO: eta expand
|
||||
length(Args) /= length(ArgsT) andalso
|
||||
gen_error({underapplied_contract_call,
|
||||
string:join([Contract, FunName], ".")}),
|
||||
ArgsI = [ ast_body(Arg, Icode) || Arg <- Args ],
|
||||
ArgType = ast_typerep({tuple_t, [], ArgsT}),
|
||||
Gas = proplists:get_value("gas", ArgOpts ++ Defaults),
|
||||
Value = proplists:get_value("value", ArgOpts ++ Defaults),
|
||||
OutType = ast_typerep(OutT, Icode),
|
||||
<<TypeHash:256>> = aeb_abi:function_type_hash(list_to_binary(FunName), ArgType, OutType),
|
||||
%% The function is represented by its type hash (which includes the name)
|
||||
Fun = #integer{value = TypeHash},
|
||||
#prim_call_contract{
|
||||
address = ast_body(Addr, Icode),
|
||||
gas = Gas,
|
||||
value = Value,
|
||||
arg = #tuple{cpts = [Fun, #tuple{ cpts = ArgsI }]},
|
||||
%% The type check is implicitly done by using the type hash as the
|
||||
%% entrypoint on the callee side.
|
||||
type_hash= #integer{value = 0}
|
||||
};
|
||||
ast_body({proj, _, {typed, _, _, {con, _, Contract}}, {id, _, FunName}}, _Icode) ->
|
||||
gen_error({underapplied_contract_call,
|
||||
string:join([Contract, FunName], ".")});
|
||||
|
||||
ast_body({con, _, Name}, Icode) ->
|
||||
Tag = aeso_icode:get_constructor_tag([Name], Icode),
|
||||
#tuple{cpts = [#integer{value = Tag}]};
|
||||
ast_body({qcon, _, Name}, Icode) ->
|
||||
Tag = aeso_icode:get_constructor_tag(Name, Icode),
|
||||
#tuple{cpts = [#integer{value = Tag}]};
|
||||
ast_body({app, _, {typed, _, {con, _, Name}, _}, Args}, Icode) ->
|
||||
Tag = aeso_icode:get_constructor_tag([Name], Icode),
|
||||
#tuple{cpts = [#integer{value = Tag} | [ ast_body(Arg, Icode) || Arg <- Args ]]};
|
||||
ast_body({app, _, {typed, _, {qcon, _, Name}, _}, Args}, Icode) ->
|
||||
Tag = aeso_icode:get_constructor_tag(Name, Icode),
|
||||
#tuple{cpts = [#integer{value = Tag} | [ ast_body(Arg, Icode) || Arg <- Args ]]};
|
||||
ast_body({app,As,Fun,Args}, Icode) ->
|
||||
case aeso_syntax:get_ann(format, As) of
|
||||
infix ->
|
||||
{Op, _} = Fun,
|
||||
[A, B] = Args,
|
||||
ast_binop(Op, As, A, B, Icode);
|
||||
prefix ->
|
||||
{Op, _} = Fun,
|
||||
[A] = Args,
|
||||
#unop{op = Op, rand = ast_body(A, Icode)};
|
||||
_ ->
|
||||
#funcall{function=ast_body(Fun, Icode),
|
||||
args=[ast_body(A, Icode) || A <- Args]}
|
||||
end;
|
||||
ast_body({'if',_,Dec,Then,Else}, Icode) ->
|
||||
#ifte{decision = ast_body(Dec, Icode)
|
||||
,then = ast_body(Then, Icode)
|
||||
,else = ast_body(Else, Icode)};
|
||||
ast_body({switch,_,A,Cases}, Icode) ->
|
||||
%% let's assume the parser has already ensured that only valid
|
||||
%% patterns appear in cases.
|
||||
#switch{expr=ast_body(A, Icode),
|
||||
cases=[{ast_body(Pat, Icode),ast_body(Body, Icode)}
|
||||
|| {'case',_,Pat,Body} <- Cases]};
|
||||
ast_body({block,As,[{letval,_,Pat,_,E}|Rest]}, Icode) ->
|
||||
#switch{expr=ast_body(E, Icode),
|
||||
cases=[{ast_body(Pat, Icode),ast_body({block,As,Rest}, Icode)}]};
|
||||
ast_body({block,_,[]}, _Icode) ->
|
||||
#tuple{cpts=[]};
|
||||
ast_body({block,_,[E]}, Icode) ->
|
||||
ast_body(E, Icode);
|
||||
ast_body({block,As,[E|Rest]}, Icode) ->
|
||||
#switch{expr=ast_body(E, Icode),
|
||||
cases=[{#var_ref{name="_"},ast_body({block,As,Rest}, Icode)}]};
|
||||
ast_body({lam,_,Args,Body}, Icode) ->
|
||||
#lambda{args=[#arg{name = ast_id(P), type = ast_type(T, Icode)} || {arg,_,P,T} <- Args],
|
||||
body=ast_body(Body, Icode)};
|
||||
ast_body({typed,_,{record,Attrs,Fields},{record_t,DefFields}}, Icode) ->
|
||||
%% Compile as a tuple with the fields in the order they appear in the definition.
|
||||
NamedField = fun({field, _, [{proj, _, {id, _, Name}}], E}) -> {Name, E} end,
|
||||
NamedFields = lists:map(NamedField, Fields),
|
||||
#tuple{cpts =
|
||||
[case proplists:get_value(Name, NamedFields) of
|
||||
undefined ->
|
||||
Line = aeso_syntax:get_ann(line, Attrs),
|
||||
#missing_field{format = "Missing field in record: ~s (on line ~p)\n",
|
||||
args = [Name,Line]};
|
||||
E ->
|
||||
ast_body(E, Icode)
|
||||
end
|
||||
|| {field_t,_,{id,_,Name},_} <- DefFields]};
|
||||
ast_body({typed,_,{record,Attrs,_Fields},T}, _Icode) ->
|
||||
gen_error({record_has_bad_type,Attrs,T});
|
||||
ast_body({proj,_,{typed,_,Record,{record_t,Fields}},{id,_,FieldName}}, Icode) ->
|
||||
[Index] = [I
|
||||
|| {I,{field_t,_,{id,_,Name},_}} <-
|
||||
lists:zip(lists:seq(1,length(Fields)),Fields),
|
||||
Name==FieldName],
|
||||
#binop{op = '!', left = #integer{value = 32*(Index-1)}, right = ast_body(Record, Icode)};
|
||||
ast_body({record, Attrs, {typed, _, Record, RecType={record_t, Fields}}, Update}, Icode) ->
|
||||
UpdatedName = fun({field, _, [{proj, _, {id, _, Name}}], _}) -> Name;
|
||||
({field_upd, _, [{proj, _, {id, _, Name}}], _}) -> Name
|
||||
end,
|
||||
UpdatedNames = lists:map(UpdatedName, Update),
|
||||
Rec = {typed, Attrs, {id, Attrs, "_record"}, RecType},
|
||||
CompileUpdate =
|
||||
fun(Fld={field, _, _, _}) -> Fld;
|
||||
({field_upd, Ann, LV=[{proj, Ann1, P}], Fun}) ->
|
||||
{field, Ann, LV, {app, Ann, Fun, [{proj, Ann1, Rec, P}]}}
|
||||
end,
|
||||
|
||||
#switch{expr=ast_body(Record, Icode),
|
||||
cases=[{#var_ref{name = "_record"},
|
||||
ast_body({typed, Attrs,
|
||||
{record, Attrs,
|
||||
lists:map(CompileUpdate, Update) ++
|
||||
[{field, Attrs, [{proj, Attrs, {id, Attrs, Name}}],
|
||||
{proj, Attrs, Rec, {id, Attrs, Name}}}
|
||||
|| {field_t, _, {id, _, Name}, _} <- Fields,
|
||||
not lists:member(Name, UpdatedNames)]},
|
||||
RecType}, Icode)}
|
||||
]};
|
||||
ast_body({typed, _, Body, _}, Icode) ->
|
||||
ast_body(Body, Icode).
|
||||
|
||||
ast_binop(Op, Ann, {typed, _, A, Type}, B, Icode)
|
||||
when Op == '=='; Op == '!=';
|
||||
Op == '<'; Op == '>';
|
||||
Op == '<='; Op == '=<'; Op == '>=' ->
|
||||
Monomorphic = is_monomorphic(Type),
|
||||
case ast_typerep(Type, Icode) of
|
||||
_ when not Monomorphic ->
|
||||
gen_error({cant_compare_polymorphic_type, Ann, Op, Type});
|
||||
word -> #binop{op = Op, left = ast_body(A, Icode), right = ast_body(B, Icode)};
|
||||
string ->
|
||||
Neg = case Op of
|
||||
'==' -> fun(X) -> X end;
|
||||
'!=' -> fun(X) -> #unop{ op = '!', rand = X } end;
|
||||
_ -> gen_error({cant_compare, Ann, Op, Type})
|
||||
end,
|
||||
Neg(#funcall{ function = #var_ref{name = {builtin, str_equal}},
|
||||
args = [ast_body(A, Icode), ast_body(B, Icode)] });
|
||||
_ -> gen_error({cant_compare, Ann, Op, Type})
|
||||
end;
|
||||
ast_binop('++', _, A, B, Icode) ->
|
||||
#funcall{ function = #var_ref{ name = {builtin, list_concat} },
|
||||
args = [ast_body(A, Icode), ast_body(B, Icode)] };
|
||||
ast_binop(Op, _, A, B, Icode) ->
|
||||
#binop{op = Op, left = ast_body(A, Icode), right = ast_body(B, Icode)}.
|
||||
|
||||
check_monomorphic_map({typed, Ann, _, MapType}, Icode) ->
|
||||
check_monomorphic_map(Ann, MapType, Icode).
|
||||
|
||||
check_monomorphic_map(Ann, Type = ?map_t(KeyType, ValType), Icode) ->
|
||||
case is_monomorphic(KeyType) of
|
||||
true ->
|
||||
case has_maps(ast_type(KeyType, Icode)) of
|
||||
false -> {KeyType, ValType};
|
||||
true -> gen_error({cant_use_map_as_map_keys, Ann, Type})
|
||||
end;
|
||||
false -> gen_error({cant_compile_map_with_polymorphic_keys, Ann, Type})
|
||||
end.
|
||||
|
||||
map_empty(KeyType, ValType, Icode) ->
|
||||
prim_call(?PRIM_CALL_MAP_EMPTY, #integer{value = 0},
|
||||
[ast_type_value(KeyType, Icode),
|
||||
ast_type_value(ValType, Icode)],
|
||||
[typerep, typerep], word).
|
||||
|
||||
map_get(Key, Map = {typed, Ann, _, MapType}, Icode) ->
|
||||
{_KeyType, ValType} = check_monomorphic_map(Ann, MapType, Icode),
|
||||
builtin_call({map_lookup, ast_type(ValType, Icode)}, [ast_body(Map, Icode), ast_body(Key, Icode)]).
|
||||
|
||||
map_put(Key, Val, Map, Icode) ->
|
||||
builtin_call(map_put, [ast_body(Map, Icode), ast_body(Key, Icode), ast_body(Val, Icode)]).
|
||||
|
||||
map_del(Key, Map, Icode) ->
|
||||
prim_call(?PRIM_CALL_MAP_DELETE, #integer{value = 0},
|
||||
[ast_body(Map, Icode), ast_body(Key, Icode)],
|
||||
[word, word], word).
|
||||
|
||||
map_tolist(Map, Icode) ->
|
||||
{KeyType, ValType} = check_monomorphic_map(Map, Icode),
|
||||
prim_call(?PRIM_CALL_MAP_TOLIST, #integer{value = 0},
|
||||
[ast_body(Map, Icode)],
|
||||
[word], {list, {tuple, [ast_type(KeyType, Icode), ast_type(ValType, Icode)]}}).
|
||||
|
||||
map_upd(Key, ValFun, Map = {typed, Ann, _, MapType}, Icode) ->
|
||||
{_, ValType} = check_monomorphic_map(Ann, MapType, Icode),
|
||||
FunName = {map_upd, ast_type(ValType, Icode)},
|
||||
Args = [ast_body(Map, Icode), ast_body(Key, Icode), ast_body(ValFun, Icode)],
|
||||
builtin_call(FunName, Args).
|
||||
|
||||
map_upd(Key, Default, ValFun, Map = {typed, Ann, _, MapType}, Icode) ->
|
||||
{_, ValType} = check_monomorphic_map(Ann, MapType, Icode),
|
||||
FunName = {map_upd_default, ast_type(ValType, Icode)},
|
||||
Args = [ast_body(Map, Icode), ast_body(Key, Icode), ast_body(Default, Icode), ast_body(ValFun, Icode)],
|
||||
builtin_call(FunName, Args).
|
||||
|
||||
is_monomorphic({tvar, _, _}) -> false;
|
||||
is_monomorphic([H|T]) ->
|
||||
is_monomorphic(H) andalso is_monomorphic(T);
|
||||
is_monomorphic(T) when is_tuple(T) ->
|
||||
is_monomorphic(tuple_to_list(T));
|
||||
is_monomorphic(_) -> true.
|
||||
|
||||
%% Implemented as a contract call to the contract with address 0.
|
||||
prim_call(Prim, Amount, Args, ArgTypes, OutType) ->
|
||||
TypeHash =
|
||||
case aeb_primops:op_needs_type_check(Prim) of
|
||||
true ->
|
||||
PrimBin = binary:encode_unsigned(Prim),
|
||||
ArgType = {tuple, ArgTypes},
|
||||
<<TH:256>> = aeb_abi:function_type_hash(PrimBin, ArgType, OutType),
|
||||
TH;
|
||||
false ->
|
||||
0
|
||||
end,
|
||||
#prim_call_contract{ gas = prim_gas_left,
|
||||
address = #integer{ value = ?PRIM_CALLS_CONTRACT },
|
||||
value = Amount,
|
||||
arg = #tuple{cpts = [#integer{ value = Prim }| Args]},
|
||||
type_hash= #integer{value = TypeHash}
|
||||
}.
|
||||
|
||||
generic_hash_primop(PrimOp, Term, Type, Icode) ->
|
||||
ArgType = ast_type(Type, Icode),
|
||||
TypeValue = type_value(ArgType),
|
||||
prim_call(PrimOp, #integer{value = 0},
|
||||
[TypeValue, ast_body(Term, Icode)],
|
||||
[typerep, ArgType], word).
|
||||
|
||||
string_hash_primop(PrimOp, String, Icode) ->
|
||||
prim_call(PrimOp, #integer{value = 0}, [ast_body(String, Icode)], [string], word).
|
||||
|
||||
make_type_def(Args, Def, Icode = #{ type_vars := TypeEnv }) ->
|
||||
TVars = [ X || {tvar, _, X} <- Args ],
|
||||
fun(Types) ->
|
||||
TypeEnv1 = maps:from_list(lists:zip(TVars, Types)),
|
||||
ast_typerep(Def, Icode#{ type_vars := maps:merge(TypeEnv, TypeEnv1) })
|
||||
end.
|
||||
|
||||
-spec ast_typerep(aeso_syntax:type()) -> aeb_aevm_data:type().
|
||||
ast_typerep(Type) -> ast_typerep(Type, aeso_icode:new([])).
|
||||
|
||||
ast_typerep({id, _, Name}, Icode) ->
|
||||
lookup_type_id(Name, [], Icode);
|
||||
ast_typerep({qid, _, Name}, Icode) ->
|
||||
lookup_type_id(Name, [], Icode);
|
||||
ast_typerep({con, _, _}, _) ->
|
||||
word; %% Contract type
|
||||
ast_typerep({bytes_t, _, Len}, _) ->
|
||||
{bytes, Len};
|
||||
ast_typerep({app_t, _, {id, _, Name}, Args}, Icode) ->
|
||||
ArgReps = [ ast_typerep(Arg, Icode) || Arg <- Args ],
|
||||
lookup_type_id(Name, ArgReps, Icode);
|
||||
ast_typerep({tvar,_,A}, #{ type_vars := TypeVars }) ->
|
||||
case maps:get(A, TypeVars, undefined) of
|
||||
undefined -> word; %% We serialize type variables just as addresses in the originating VM.
|
||||
Type -> Type
|
||||
end;
|
||||
ast_typerep({tuple_t,_,Cpts}, Icode) ->
|
||||
{tuple, [ast_typerep(C, Icode) || C<-Cpts]};
|
||||
ast_typerep({record_t,Fields}, Icode) ->
|
||||
{tuple, [ begin
|
||||
{field_t, _, _, T} = Field,
|
||||
ast_typerep(T, Icode)
|
||||
end || Field <- Fields]};
|
||||
ast_typerep({fun_t,_,_,_,_}, _Icode) ->
|
||||
function;
|
||||
ast_typerep({alias_t, T}, Icode) -> ast_typerep(T, Icode);
|
||||
ast_typerep({variant_t, Cons}, Icode) ->
|
||||
{variant, [ begin
|
||||
{constr_t, _, _, Args} = Con,
|
||||
[ ast_typerep(Arg, Icode) || Arg <- Args ]
|
||||
end || Con <- Cons ]}.
|
||||
|
||||
ttl_t(Icode) ->
|
||||
ast_typerep({qid, [], ["Chain", "ttl"]}, Icode).
|
||||
|
||||
sign_t() -> bytes_t(64).
|
||||
bytes_t(Len) -> {bytes, Len}.
|
||||
|
||||
get_signature_arg(Args0) ->
|
||||
NamedArgs = [Arg || Arg = {named_arg, _, _, _} <- Args0],
|
||||
Args = Args0 -- NamedArgs,
|
||||
|
||||
DefaultVal = {tuple, [], [{int, [], 0}, {int, [], 0}]},
|
||||
Sig =
|
||||
case NamedArgs of
|
||||
[] -> DefaultVal;
|
||||
[{named_arg, _, _, Val}] -> Val
|
||||
end,
|
||||
{Sig, Args}.
|
||||
|
||||
lookup_type_id(Name, Args, #{ types := Types }) ->
|
||||
case maps:get(Name, Types, undefined) of
|
||||
undefined -> gen_error({undefined_type, Name});
|
||||
TDef -> TDef(Args)
|
||||
end.
|
||||
|
||||
ast_type_value(T, Icode) ->
|
||||
type_value(ast_type(T, Icode)).
|
||||
|
||||
type_value(word) ->
|
||||
#tuple{ cpts = [#integer{ value = ?TYPEREP_WORD_TAG }] };
|
||||
type_value(string) ->
|
||||
#tuple{ cpts = [#integer{ value = ?TYPEREP_STRING_TAG }] };
|
||||
type_value(typerep) ->
|
||||
#tuple{ cpts = [#integer{ value = ?TYPEREP_TYPEREP_TAG }] };
|
||||
type_value({list, A}) ->
|
||||
#tuple{ cpts = [#integer{ value = ?TYPEREP_LIST_TAG }, type_value(A)] };
|
||||
type_value({tuple, As}) ->
|
||||
#tuple{ cpts = [#integer{ value = ?TYPEREP_TUPLE_TAG },
|
||||
#list{ elems = [ type_value(A) || A <- As ] }] };
|
||||
type_value({bytes, Len}) ->
|
||||
#tuple{ cpts = [#integer{ value = ?TYPEREP_BYTES_TAG }, #integer{ value = Len }] };
|
||||
type_value({variant, Cs}) ->
|
||||
#tuple{ cpts = [#integer{ value = ?TYPEREP_VARIANT_TAG },
|
||||
#list{ elems = [ #list{ elems = [ type_value(A) || A <- As ] } || As <- Cs ] }] };
|
||||
type_value({map, K, V}) ->
|
||||
#tuple{ cpts = [#integer{ value = ?TYPEREP_MAP_TAG },
|
||||
type_value(K), type_value(V)] }.
|
||||
|
||||
%% As abort is a built-in in the future it will be illegal to for
|
||||
%% users to define abort. For the time being strip away all user
|
||||
%% defined abort functions.
|
||||
|
||||
ast_fun_to_icode("abort", _Atts, _Args, _Body, _TypeRep, Icode) ->
|
||||
%% Strip away all user defined abort functions.
|
||||
Icode;
|
||||
ast_fun_to_icode(Name, Attrs, Args, Body, TypeRep, #{functions := Funs} = Icode) ->
|
||||
NewFuns = [{Name, Attrs, Args, Body, TypeRep}| Funs],
|
||||
aeso_icode:set_functions(NewFuns, Icode).
|
||||
|
||||
has_maps({map, _, _}) -> true;
|
||||
has_maps(word) -> false;
|
||||
has_maps(string) -> false;
|
||||
has_maps(typerep) -> false;
|
||||
has_maps({list, T}) -> has_maps(T);
|
||||
has_maps({tuple, Ts}) -> lists:any(fun has_maps/1, Ts);
|
||||
has_maps({variant, Cs}) -> lists:any(fun has_maps/1, lists:append(Cs)).
|
||||
|
||||
%% A function is private if marked 'private' or 'internal', or if it's not
|
||||
%% defined in the main contract name space. (NOTE: changes when we introduce
|
||||
%% inheritance).
|
||||
is_private(Ann, #{ contract_name := MainContract } = Icode) ->
|
||||
{_, _, CurrentNamespace} = aeso_icode:get_namespace(Icode),
|
||||
proplists:get_value(private, Ann, false) orelse
|
||||
proplists:get_value(internal, Ann, false) orelse
|
||||
MainContract /= CurrentNamespace.
|
||||
|
||||
%% -------------------------------------------------------------------
|
||||
%% Builtins
|
||||
%% -------------------------------------------------------------------
|
||||
|
||||
builtin_call(Builtin, Args) ->
|
||||
#funcall{ function = #var_ref{ name = {builtin, Builtin} },
|
||||
args = Args }.
|
||||
|
||||
add_builtins(Icode = #{functions := Funs}) ->
|
||||
Builtins = aeso_builtins:used_builtins(Funs),
|
||||
Icode#{functions := [ aeso_builtins:builtin_function(B) || B <- Builtins ] ++ Funs}.
|
||||
|
||||
|
||||
%% -------------------------------------------------------------------
|
||||
%% Deadcode elimination
|
||||
%% -------------------------------------------------------------------
|
||||
|
||||
deadcode_elimination(Icode = #{ functions := Funs }) ->
|
||||
PublicNames = [ Name || {Name, Ann, _, _, _} <- Funs, not lists:member(private, Ann) ],
|
||||
ArgsToPat = fun(Args) -> [ #var_ref{ name = X } || {X, _} <- Args ] end,
|
||||
Defs = maps:from_list([ {Name, {binder, ArgsToPat(Args), Body}} || {Name, _, Args, Body, _} <- Funs ]),
|
||||
UsedNames = chase_names(Defs, PublicNames, #{}),
|
||||
UsedFuns = [ Def || Def = {Name, _, _, _, _} <- Funs, maps:is_key(Name, UsedNames) ],
|
||||
Icode#{ functions := UsedFuns }.
|
||||
|
||||
chase_names(_Defs, [], Used) -> Used;
|
||||
chase_names(Defs, [X | Xs], Used) ->
|
||||
%% can happen when compiling __call contracts
|
||||
case maps:is_key(X, Used) orelse not maps:is_key(X, Defs) of
|
||||
true -> chase_names(Defs, Xs, Used); %% already chased
|
||||
false ->
|
||||
Def = maps:get(X, Defs),
|
||||
Vars = maps:keys(free_vars(Def)),
|
||||
chase_names(Defs, Vars ++ Xs, Used#{ X => true })
|
||||
end.
|
||||
|
||||
free_vars(#var_ref{ name = X }) -> #{ X => true };
|
||||
free_vars(#arg{ name = X }) -> #{ X => true };
|
||||
free_vars({binder, Pat, Body}) ->
|
||||
maps:without(maps:keys(free_vars(Pat)), free_vars(Body));
|
||||
free_vars(#switch{ expr = E, cases = Cases }) ->
|
||||
free_vars([E | [{binder, P, B} || {P, B} <- Cases]]);
|
||||
free_vars(#lambda{ args = Xs, body = E }) ->
|
||||
free_vars({binder, Xs, E});
|
||||
free_vars(T) when is_tuple(T) -> free_vars(tuple_to_list(T));
|
||||
free_vars([H | T]) -> maps:merge(free_vars(H), free_vars(T));
|
||||
free_vars(_) -> #{}.
|
||||
@@ -0,0 +1,500 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @copyright (C) 2018, Aeternity Anstalt
|
||||
%%% @doc
|
||||
%%% Compiler builtin functions for Aeterinty Sophia language.
|
||||
%%% @end
|
||||
%%% Created : 20 Dec 2018
|
||||
%%%
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(aeso_builtins).
|
||||
|
||||
-export([ builtin_function/1
|
||||
, check_event_type/1
|
||||
, used_builtins/1 ]).
|
||||
|
||||
-import(aeso_ast_to_icode, [prim_call/5]).
|
||||
|
||||
-include_lib("aebytecode/include/aeb_opcodes.hrl").
|
||||
-include("aeso_icode.hrl").
|
||||
|
||||
used_builtins(#funcall{ function = #var_ref{ name = {builtin, Builtin} }, args = Args }) ->
|
||||
lists:umerge(dep_closure([Builtin]), used_builtins(Args));
|
||||
used_builtins([H|T]) ->
|
||||
lists:umerge(used_builtins(H), used_builtins(T));
|
||||
used_builtins(T) when is_tuple(T) ->
|
||||
used_builtins(tuple_to_list(T));
|
||||
used_builtins(M) when is_map(M) ->
|
||||
used_builtins(maps:to_list(M));
|
||||
used_builtins(_) -> [].
|
||||
|
||||
builtin_deps(Builtin) ->
|
||||
lists:usort(builtin_deps1(Builtin)).
|
||||
|
||||
builtin_deps1({map_lookup_default, Type}) -> [{map_lookup, Type}];
|
||||
builtin_deps1({map_get, Type}) -> [{map_lookup, Type}];
|
||||
builtin_deps1(map_member) -> [{map_lookup, word}];
|
||||
builtin_deps1({map_upd, Type}) -> [{map_get, Type}, map_put];
|
||||
builtin_deps1({map_upd_default, Type}) -> [{map_lookup_default, Type}, map_put];
|
||||
builtin_deps1(map_from_list) -> [map_put];
|
||||
builtin_deps1(str_equal) -> [str_equal_p];
|
||||
builtin_deps1(string_concat) -> [string_concat_inner1, string_copy, string_shift_copy];
|
||||
builtin_deps1(int_to_str) -> [{baseX_int, 10}];
|
||||
builtin_deps1(addr_to_str) -> [{baseX_int, 58}];
|
||||
builtin_deps1({baseX_int, X}) -> [{baseX_int_pad, X}];
|
||||
builtin_deps1({baseX_int_pad, X}) -> [{baseX_int_encode, X}];
|
||||
builtin_deps1({baseX_int_encode, X}) -> [{baseX_int_encode_, X}, {baseX_tab, X}, {baseX_digits, X}];
|
||||
builtin_deps1(string_reverse) -> [string_reverse_];
|
||||
builtin_deps1(_) -> [].
|
||||
|
||||
dep_closure(Deps) ->
|
||||
case lists:umerge(lists:map(fun builtin_deps/1, Deps)) of
|
||||
[] -> Deps;
|
||||
Deps1 -> lists:umerge(Deps, dep_closure(Deps1))
|
||||
end.
|
||||
|
||||
%% Helper functions/macros
|
||||
v(X) when is_atom(X) -> v(atom_to_list(X));
|
||||
v(X) when is_list(X) -> #var_ref{name = X}.
|
||||
|
||||
option_none() -> {tuple, [{integer, 0}]}.
|
||||
option_some(X) -> {tuple, [{integer, 1}, X]}.
|
||||
|
||||
-define(call(Fun, Args), #funcall{ function = #var_ref{ name = {builtin, Fun} }, args = Args }).
|
||||
-define(I(X), {integer, X}).
|
||||
-define(V(X), v(X)).
|
||||
-define(A(Op), aeb_opcodes:mnemonic(Op)).
|
||||
-define(LET(Var, Expr, Body), {switch, Expr, [{v(Var), Body}]}).
|
||||
-define(DEREF(Var, Ptr, Body), {switch, v(Ptr), [{{tuple, [v(Var)]}, Body}]}).
|
||||
-define(NXT(Ptr), op('+', Ptr, 32)).
|
||||
-define(NEG(A), op('/', A, {unop, '-', {integer, 1}})).
|
||||
-define(BYTE(Ix, Word), op('byte', Ix, Word)).
|
||||
|
||||
-define(EQ(A, B), op('==', A, B)).
|
||||
-define(LT(A, B), op('<', A, B)).
|
||||
-define(GT(A, B), op('>', A, B)).
|
||||
-define(ADD(A, B), op('+', A, B)).
|
||||
-define(SUB(A, B), op('-', A, B)).
|
||||
-define(MUL(A, B), op('*', A, B)).
|
||||
-define(DIV(A, B), op('div', A, B)).
|
||||
-define(MOD(A, B), op('mod', A, B)).
|
||||
-define(EXP(A, B), op('^', A, B)).
|
||||
-define(AND(A, B), op('&&', A, B)).
|
||||
|
||||
%% Bit shift operations takes their arguments backwards!?
|
||||
-define(BSL(X, B), op('bsl', ?MUL(B, 8), X)).
|
||||
-define(BSR(X, B), op('bsr', ?MUL(B, 8), X)).
|
||||
|
||||
op(Op, A, B) -> {binop, Op, operand(A), operand(B)}.
|
||||
|
||||
operand(A) when is_atom(A) -> v(A);
|
||||
operand(I) when is_integer(I) -> {integer, I};
|
||||
operand(T) -> T.
|
||||
|
||||
str_to_icode(String) when is_list(String) ->
|
||||
str_to_icode(list_to_binary(String));
|
||||
str_to_icode(BinStr) ->
|
||||
Cpts = [size(BinStr) | aeb_memory:binary_to_words(BinStr)],
|
||||
#tuple{ cpts = [ #integer{value = X} || X <- Cpts ] }.
|
||||
|
||||
check_event_type(Icode) ->
|
||||
case maps:get(event_type, Icode) of
|
||||
{variant_t, Cons} ->
|
||||
check_event_type(Cons, Icode);
|
||||
_ ->
|
||||
error({event_should_be_variant_type})
|
||||
end.
|
||||
|
||||
check_event_type(Evts, Icode) ->
|
||||
[ check_event_type(Name, Ix, T, Icode)
|
||||
|| {constr_t, Ann, {con, _, Name}, Types} <- Evts,
|
||||
{Ix, T} <- lists:zip(aeso_syntax:get_ann(indices, Ann), Types) ].
|
||||
|
||||
check_event_type(EvtName, Ix, Type, Icode) ->
|
||||
VMType =
|
||||
try
|
||||
aeso_ast_to_icode:ast_typerep(Type, Icode)
|
||||
catch _:_ ->
|
||||
error({EvtName, could_not_resolve_type, Type})
|
||||
end,
|
||||
case {Ix, VMType} of
|
||||
{indexed, word} -> ok;
|
||||
{notindexed, string} -> ok;
|
||||
{indexed, _} -> error({EvtName, indexed_field_should_be_word, is, VMType});
|
||||
{notindexed, _} -> error({EvtName, payload_should_be_string, is, VMType})
|
||||
end.
|
||||
|
||||
bfun(B, {IArgs, IExpr, IRet}) ->
|
||||
{{builtin, B}, [private], IArgs, IExpr, IRet}.
|
||||
|
||||
builtin_function(BF) ->
|
||||
case BF of
|
||||
{event, EventT} -> bfun(BF, builtin_event(EventT));
|
||||
abort -> bfun(BF, builtin_abort());
|
||||
{map_lookup, Type} -> bfun(BF, builtin_map_lookup(Type));
|
||||
map_put -> bfun(BF, builtin_map_put());
|
||||
map_delete -> bfun(BF, builtin_map_delete());
|
||||
map_size -> bfun(BF, builtin_map_size());
|
||||
{map_get, Type} -> bfun(BF, builtin_map_get(Type));
|
||||
{map_lookup_default, Type} -> bfun(BF, builtin_map_lookup_default(Type));
|
||||
map_member -> bfun(BF, builtin_map_member());
|
||||
{map_upd, Type} -> bfun(BF, builtin_map_upd(Type));
|
||||
{map_upd_default, Type} -> bfun(BF, builtin_map_upd_default(Type));
|
||||
map_from_list -> bfun(BF, builtin_map_from_list());
|
||||
list_concat -> bfun(BF, builtin_list_concat());
|
||||
string_length -> bfun(BF, builtin_string_length());
|
||||
string_concat -> bfun(BF, builtin_string_concat());
|
||||
string_concat_inner1 -> bfun(BF, builtin_string_concat_inner1());
|
||||
string_copy -> bfun(BF, builtin_string_copy());
|
||||
string_shift_copy -> bfun(BF, builtin_string_shift_copy());
|
||||
str_equal_p -> bfun(BF, builtin_str_equal_p());
|
||||
str_equal -> bfun(BF, builtin_str_equal());
|
||||
popcount -> bfun(BF, builtin_popcount());
|
||||
int_to_str -> bfun(BF, builtin_int_to_str());
|
||||
addr_to_str -> bfun(BF, builtin_addr_to_str());
|
||||
{baseX_int, X} -> bfun(BF, builtin_baseX_int(X));
|
||||
{baseX_digits, X} -> bfun(BF, builtin_baseX_digits(X));
|
||||
{baseX_tab, X} -> bfun(BF, builtin_baseX_tab(X));
|
||||
{baseX_int_pad, X} -> bfun(BF, builtin_baseX_int_pad(X));
|
||||
{baseX_int_encode, X} -> bfun(BF, builtin_baseX_int_encode(X));
|
||||
{baseX_int_encode_, X} -> bfun(BF, builtin_baseX_int_encode_(X));
|
||||
string_reverse -> bfun(BF, builtin_string_reverse());
|
||||
string_reverse_ -> bfun(BF, builtin_string_reverse_())
|
||||
end.
|
||||
|
||||
%% Event primitive (dependent on Event type)
|
||||
%%
|
||||
%% We need to switch on the event and prepare the correct #event for icode_to_asm
|
||||
%% NOTE: we assume all errors are already checked!
|
||||
builtin_event(EventT) ->
|
||||
A = fun(X) -> aeb_opcodes:mnemonic(X) end,
|
||||
VIx = fun(Ix) -> v(lists:concat(["v", Ix])) end,
|
||||
ArgPats = fun(Ts) -> [ VIx(Ix) || Ix <- lists:seq(0, length(Ts) - 1) ] end,
|
||||
Payload = %% Should put data ptr, length on stack.
|
||||
fun([]) -> {inline_asm, [A(?PUSH1), 0, A(?PUSH1), 0]};
|
||||
([V]) -> {seq, [V, {inline_asm, [A(?DUP1), A(?MLOAD), %% length, ptr
|
||||
A(?SWAP1), A(?PUSH1), 32, A(?ADD)]}]} %% ptr+32, length
|
||||
end,
|
||||
Clause =
|
||||
fun(_Tag, {con, _, Con}, IxTypes) ->
|
||||
Types = [ T || {_Ix, T} <- IxTypes ],
|
||||
Indexed = [ Var || {Var, {indexed, _Type}} <- lists:zip(ArgPats(Types), IxTypes) ],
|
||||
EvtIndex = {unop, 'sha3', str_to_icode(Con)},
|
||||
{event, lists:reverse(Indexed) ++ [EvtIndex], Payload(ArgPats(Types) -- Indexed)}
|
||||
end,
|
||||
Pat = fun(Tag, Types) -> {tuple, [{integer, Tag} | ArgPats(Types)]} end,
|
||||
|
||||
{variant_t, Cons} = EventT,
|
||||
Tags = lists:seq(0, length(Cons) - 1),
|
||||
|
||||
{[{"e", event}],
|
||||
{switch, v(e),
|
||||
[{Pat(Tag, Types), Clause(Tag, Con, lists:zip(aeso_syntax:get_ann(indices, Ann), Types))}
|
||||
|| {Tag, {constr_t, Ann, Con, Types}} <- lists:zip(Tags, Cons) ]},
|
||||
{tuple, []}}.
|
||||
|
||||
%% Abort primitive.
|
||||
builtin_abort() ->
|
||||
A = fun(X) -> aeb_opcodes:mnemonic(X) end,
|
||||
{[{"s", string}],
|
||||
{inline_asm, [A(?PUSH1),0, %% Push a dummy 0 for the first arg
|
||||
A(?REVERT)]}, %% Stack: 0,Ptr
|
||||
{tuple,[]}}.
|
||||
|
||||
%% Map primitives
|
||||
builtin_map_lookup(Type) ->
|
||||
Ret = aeso_icode:option_typerep(Type),
|
||||
{[{"m", word}, {"k", word}],
|
||||
prim_call(?PRIM_CALL_MAP_GET, #integer{value = 0},
|
||||
[#var_ref{name = "m"}, #var_ref{name = "k"}],
|
||||
[word, word], Ret),
|
||||
Ret}.
|
||||
|
||||
builtin_map_put() ->
|
||||
%% We don't need the types for put.
|
||||
{[{"m", word}, {"k", word}, {"v", word}],
|
||||
prim_call(?PRIM_CALL_MAP_PUT, #integer{value = 0},
|
||||
[v(m), v(k), v(v)], [word, word, word], word),
|
||||
word}.
|
||||
|
||||
builtin_map_delete() ->
|
||||
{[{"m", word}, {"k", word}],
|
||||
prim_call(?PRIM_CALL_MAP_DELETE, #integer{value = 0},
|
||||
[v(m), v(k)], [word, word], word),
|
||||
word}.
|
||||
|
||||
builtin_map_size() ->
|
||||
{[{"m", word}],
|
||||
prim_call(?PRIM_CALL_MAP_SIZE, #integer{value = 0},
|
||||
[v(m)], [word], word),
|
||||
word}.
|
||||
|
||||
%% Map builtins
|
||||
builtin_map_get(Type) ->
|
||||
%% function map_get(m, k) =
|
||||
%% switch(map_lookup(m, k))
|
||||
%% Some(v) => v
|
||||
{[{"m", word}, {"k", word}],
|
||||
{switch, ?call({map_lookup, Type}, [v(m), v(k)]), [{option_some(v(v)), v(v)}]},
|
||||
Type}.
|
||||
|
||||
builtin_map_lookup_default(Type) ->
|
||||
%% function map_lookup_default(m, k, default) =
|
||||
%% switch(map_lookup(m, k))
|
||||
%% None => default
|
||||
%% Some(v) => v
|
||||
{[{"m", word}, {"k", word}, {"default", Type}],
|
||||
{switch, ?call({map_lookup, Type}, [v(m), v(k)]),
|
||||
[{option_none(), v(default)},
|
||||
{option_some(v(v)), v(v)}]},
|
||||
Type}.
|
||||
|
||||
builtin_map_member() ->
|
||||
%% function map_member(m, k) : bool =
|
||||
%% switch(Map.lookup(m, k))
|
||||
%% None => false
|
||||
%% _ => true
|
||||
{[{"m", word}, {"k", word}],
|
||||
{switch, ?call({map_lookup, word}, [v(m), v(k)]),
|
||||
[{option_none(), {integer, 0}},
|
||||
{{var_ref, "_"}, {integer, 1}}]},
|
||||
word}.
|
||||
|
||||
builtin_map_upd(Type) ->
|
||||
%% function map_upd(map, key, fun) =
|
||||
%% map_put(map, key, fun(map_get(map, key)))
|
||||
{[{"map", word}, {"key", word}, {"valfun", word}],
|
||||
?call(map_put,
|
||||
[v(map), v(key),
|
||||
#funcall{ function = v(valfun),
|
||||
args = [?call({map_get, Type}, [v(map), v(key)])] }]),
|
||||
word}.
|
||||
|
||||
builtin_map_upd_default(Type) ->
|
||||
%% function map_upd(map, key, val, fun) =
|
||||
%% map_put(map, key, fun(map_lookup_default(map, key, val)))
|
||||
{[{"map", word}, {"key", word}, {"val", word}, {"valfun", word}],
|
||||
?call(map_put,
|
||||
[v(map), v(key),
|
||||
#funcall{ function = v(valfun),
|
||||
args = [?call({map_lookup_default, Type}, [v(map), v(key), v(val)])] }]),
|
||||
word}.
|
||||
|
||||
builtin_map_from_list() ->
|
||||
%% function map_from_list(xs, acc) =
|
||||
%% switch(xs)
|
||||
%% [] => acc
|
||||
%% (k, v) :: xs => map_from_list(xs, acc { [k] = v })
|
||||
{[{"xs", {list, {tuple, [word, word]}}}, {"acc", word}],
|
||||
{switch, v(xs),
|
||||
[{{list, []}, v(acc)},
|
||||
{{binop, '::', {tuple, [v(k), v(v)]}, v(ys)},
|
||||
?call(map_from_list,
|
||||
[v(ys), ?call(map_put, [v(acc), v(k), v(v)])])}]},
|
||||
word}.
|
||||
|
||||
%% list_concat
|
||||
%%
|
||||
%% Concatenates two lists.
|
||||
builtin_list_concat() ->
|
||||
{[{"l1", {list, word}}, {"l2", {list, word}}],
|
||||
{switch, v(l1),
|
||||
[{{list, []}, v(l2)},
|
||||
{{binop, '::', v(hd), v(tl)},
|
||||
{binop, '::', v(hd), ?call(list_concat, [v(tl), v(l2)])}}
|
||||
]
|
||||
},
|
||||
word}.
|
||||
|
||||
builtin_string_length() ->
|
||||
%% function length(str) =
|
||||
%% switch(str)
|
||||
%% {n} -> n // (ab)use the representation
|
||||
{[{"s", string}],
|
||||
?DEREF(n, s, ?V(n)),
|
||||
word}.
|
||||
|
||||
%% str_concat - concatenate two strings
|
||||
%%
|
||||
%% Unless the second string is the empty string, a new string is created at the
|
||||
%% top of the Heap and the address to it is returned. The tricky bit is when
|
||||
%% the words from the second string has to be shifted to fit next to the first
|
||||
%% string.
|
||||
builtin_string_concat() ->
|
||||
{[{"s1", string}, {"s2", string}],
|
||||
?DEREF(n1, s1,
|
||||
?DEREF(n2, s2,
|
||||
{ifte, ?EQ(n1, 0),
|
||||
?V(s2), %% First string is empty return second string
|
||||
{ifte, ?EQ(n2, 0),
|
||||
?V(s1), %% Second string is empty return first string
|
||||
?LET(ret, {inline_asm, [?A(?MSIZE)]},
|
||||
{seq, [?ADD(n1, n2), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]}, %% Store total len
|
||||
?call(string_concat_inner1, [?V(n1), ?NXT(s1), ?V(n2), ?NXT(s2)]),
|
||||
{inline_asm, [?A(?POP)]}, %% Discard fun ret val
|
||||
?V(ret) %% Put the actual return value
|
||||
]})}
|
||||
}
|
||||
)),
|
||||
word}.
|
||||
|
||||
builtin_string_concat_inner1() ->
|
||||
%% Copy all whole words from the first string, and set up for word fusion
|
||||
%% Special case when the length of the first string is divisible by 32.
|
||||
{[{"n1", word}, {"p1", pointer}, {"n2", word}, {"p2", pointer}],
|
||||
?LET(w1, ?call(string_copy, [?V(n1), ?V(p1)]),
|
||||
?LET(nx, ?MOD(n1, 32),
|
||||
{ifte, ?EQ(nx, 0),
|
||||
?LET(w2, ?call(string_copy, [?V(n2), ?V(p2)]),
|
||||
{seq, [?V(w2), {inline_asm, [?A(?MSIZE), ?A(?MSTORE), ?A(?MSIZE)]}]}),
|
||||
?call(string_shift_copy, [?V(nx), ?V(w1), ?V(n2), ?V(p2)])
|
||||
})),
|
||||
word}.
|
||||
|
||||
builtin_string_copy() ->
|
||||
{[{"n", word}, {"p", pointer}],
|
||||
?DEREF(w, p,
|
||||
{ifte, ?GT(n, 31),
|
||||
{seq, [?V(w), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
|
||||
?call(string_copy, [?SUB(n, 32), ?NXT(p)])]},
|
||||
?V(w)
|
||||
}),
|
||||
word}.
|
||||
|
||||
builtin_string_shift_copy() ->
|
||||
{[{"off", word}, {"dst", word}, {"n", word}, {"p", pointer}],
|
||||
?DEREF(w, p,
|
||||
{seq, [?ADD(dst, ?BSR(w, off)), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
|
||||
{ifte, ?GT(n, ?SUB(32, off)),
|
||||
?call(string_shift_copy, [?V(off), ?BSL(w, ?SUB(32, off)), ?SUB(n, 32), ?NXT(p)]),
|
||||
{inline_asm, [?A(?MSIZE)]}}]
|
||||
}),
|
||||
word}.
|
||||
|
||||
builtin_str_equal_p() ->
|
||||
%% function str_equal_p(n, p1, p2) =
|
||||
%% if(n =< 0) true
|
||||
%% else
|
||||
%% let w1 = *p1
|
||||
%% let w2 = *p2
|
||||
%% w1 == w2 && str_equal_p(n - 32, p1 + 32, p2 + 32)
|
||||
{[{"n", word}, {"p1", pointer}, {"p2", pointer}],
|
||||
{ifte, ?LT(n, 1),
|
||||
?I(1),
|
||||
?DEREF(w1, p1,
|
||||
?DEREF(w2, p2,
|
||||
?AND(?EQ(w1, w2),
|
||||
?call(str_equal_p, [?SUB(n, 32), ?NXT(p1), ?NXT(p2)]))))},
|
||||
word}.
|
||||
|
||||
builtin_str_equal() ->
|
||||
%% function str_equal(s1, s2) =
|
||||
%% let n1 = length(s1)
|
||||
%% let n2 = length(s2)
|
||||
%% n1 == n2 && str_equal_p(n1, s1 + 32, s2 + 32)
|
||||
{[{"s1", string}, {"s2", string}],
|
||||
?DEREF(n1, s1,
|
||||
?DEREF(n2, s2,
|
||||
?AND(?EQ(n1, n2), ?call(str_equal_p, [?V(n1), ?NXT(s1), ?NXT(s2)]))
|
||||
)),
|
||||
word}.
|
||||
|
||||
%% Count the number of 1s in a bit field.
|
||||
builtin_popcount() ->
|
||||
%% function popcount(bits, acc) =
|
||||
%% if (bits == 0) acc
|
||||
%% else popcount(bits bsr 1, acc + bits band 1)
|
||||
{[{"bits", word}, {"acc", word}],
|
||||
{ifte, ?EQ(bits, 0),
|
||||
?V(acc),
|
||||
?call(popcount, [op('bsr', 1, bits), ?ADD(acc, op('band', bits, 1))])
|
||||
}, word}.
|
||||
|
||||
builtin_int_to_str() ->
|
||||
{[{"i", word}], ?call({baseX_int, 10}, [?V(i)]), word}.
|
||||
|
||||
builtin_baseX_tab(_X = 10) ->
|
||||
{[{"ix", word}], ?ADD($0, ix), word};
|
||||
builtin_baseX_tab(_X = 58) ->
|
||||
<<Fst32:256>> = <<"123456789ABCDEFGHJKLMNPQRSTUVWXY">>,
|
||||
<<Lst26:256>> = <<"Zabcdefghijkmnopqrstuvwxyz", 0:48>>,
|
||||
{[{"ix", word}],
|
||||
{ifte, ?LT(ix, 32),
|
||||
?BYTE(ix, Fst32),
|
||||
?BYTE(?SUB(ix, 32), Lst26)
|
||||
},
|
||||
word}.
|
||||
|
||||
builtin_baseX_int(X) ->
|
||||
{[{"w", word}],
|
||||
?LET(ret, {inline_asm, [?A(?MSIZE)]},
|
||||
{seq, [?call({baseX_int_pad, X}, [?V(w), ?I(0), ?I(0)]), {inline_asm, [?A(?POP)]}, ?V(ret)]}),
|
||||
word}.
|
||||
|
||||
builtin_baseX_int_pad(X = 10) ->
|
||||
{[{"src", word}, {"ix", word}, {"dst", word}],
|
||||
{ifte, ?LT(src, 0),
|
||||
?call({baseX_int_encode, X}, [?NEG(src), ?I(1), ?BSL($-, 31)]),
|
||||
?call({baseX_int_encode, X}, [?V(src), ?V(ix), ?V(dst)])},
|
||||
word};
|
||||
builtin_baseX_int_pad(X = 58) ->
|
||||
{[{"src", word}, {"ix", word}, {"dst", word}],
|
||||
{ifte, ?GT(?ADD(?DIV(ix, 31), ?BYTE(ix, src)), 0),
|
||||
?call({baseX_int_encode, X}, [?V(src), ?V(ix), ?V(dst)]),
|
||||
?call({baseX_int_pad, X}, [?V(src), ?ADD(ix, 1), ?ADD(dst, ?BSL($1, ?SUB(31, ix)))])},
|
||||
word}.
|
||||
|
||||
builtin_baseX_int_encode(X) ->
|
||||
{[{"src", word}, {"ix", word}, {"dst", word}],
|
||||
?LET(n, ?call({baseX_digits, X}, [?V(src), ?I(0)]),
|
||||
{seq, [?ADD(n, ?ADD(ix, 1)), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
|
||||
?call({baseX_int_encode_, X}, [?V(src), ?V(dst), ?EXP(X, n), ?V(ix)])]}),
|
||||
word}.
|
||||
|
||||
builtin_baseX_int_encode_(X) ->
|
||||
{[{"src", word}, {"dst", word}, {"fac", word}, {"ix", word}],
|
||||
{ifte, ?EQ(fac, 0),
|
||||
{seq, [?V(dst), {inline_asm, [?A(?MSIZE), ?A(?MSTORE), ?A(?MSIZE)]}]},
|
||||
{ifte, ?EQ(ix, 32),
|
||||
%% We've filled a word, write it and start on new word
|
||||
{seq, [?V(dst), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
|
||||
?call({baseX_int_encode_, X}, [?V(src), ?I(0), ?V(fac), ?I(0)])]},
|
||||
?call({baseX_int_encode_, X},
|
||||
[?MOD(src, fac), ?ADD(dst, ?BSL(?call({baseX_tab, X}, [?DIV(src, fac)]), ?SUB(31, ix))),
|
||||
?DIV(fac, X), ?ADD(ix, 1)])}
|
||||
},
|
||||
word}.
|
||||
|
||||
builtin_baseX_digits(X) ->
|
||||
{[{"x0", word}, {"dgts", word}],
|
||||
?LET(x1, ?DIV(x0, X),
|
||||
{ifte, ?EQ(x1, 0), ?V(dgts), ?call({baseX_digits, X}, [?V(x1), ?ADD(dgts, 1)])}),
|
||||
word}.
|
||||
|
||||
builtin_string_reverse() ->
|
||||
{[{"s", string}],
|
||||
?DEREF(n, s,
|
||||
?LET(ret, {inline_asm, [?A(?MSIZE)]},
|
||||
{seq, [?V(n), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
|
||||
?call(string_reverse_, [?NXT(s), ?I(0), ?I(31), ?SUB(?V(n), 1)]),
|
||||
{inline_asm, [?A(?POP)]}, ?V(ret)]})),
|
||||
word}.
|
||||
|
||||
builtin_string_reverse_() ->
|
||||
{[{"p", pointer}, {"x", word}, {"i1", word}, {"i2", word}],
|
||||
{ifte, ?LT(i2, 0),
|
||||
{seq, [?V(x), {inline_asm, [?A(?MSIZE), ?A(?MSTORE), ?A(?MSIZE)]}]},
|
||||
?LET(p1, ?ADD(p, ?MUL(?DIV(i2, 32), 32)),
|
||||
?DEREF(w, p1,
|
||||
?LET(b, ?BYTE(?MOD(i2, 32), w),
|
||||
{ifte, ?LT(i1, 0),
|
||||
{seq, [?V(x), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
|
||||
?call(string_reverse_,
|
||||
[?V(p), ?BSL(b, 31), ?I(30), ?SUB(i2, 1)])]},
|
||||
?call(string_reverse_,
|
||||
[?V(p), ?ADD(x, ?BSL(b, i1)), ?SUB(i1, 1), ?SUB(i2, 1)])})))},
|
||||
word}.
|
||||
|
||||
builtin_addr_to_str() ->
|
||||
{[{"a", word}], ?call({baseX_int, 58}, [?V(a)]), word}.
|
||||
|
||||
@@ -0,0 +1,523 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @author Happi (Erik Stenman)
|
||||
%%% @copyright (C) 2017, Aeternity Anstalt
|
||||
%%% @doc
|
||||
%%% Compiler from Aeterinty Sophia language to the Aeternity VM, aevm.
|
||||
%%% @end
|
||||
%%% Created : 12 Dec 2017
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeso_compiler).
|
||||
|
||||
-export([ file/1
|
||||
, file/2
|
||||
, from_string/2
|
||||
, check_call/4
|
||||
, create_calldata/3
|
||||
, version/0
|
||||
, sophia_type_to_typerep/1
|
||||
, to_sophia_value/4
|
||||
, to_sophia_value/5
|
||||
, decode_calldata/3
|
||||
]).
|
||||
|
||||
-include_lib("aebytecode/include/aeb_opcodes.hrl").
|
||||
-include("aeso_icode.hrl").
|
||||
|
||||
|
||||
-type option() :: pp_sophia_code
|
||||
| pp_ast
|
||||
| pp_types
|
||||
| pp_typed_ast
|
||||
| pp_icode
|
||||
| pp_assembler
|
||||
| pp_bytecode
|
||||
| {include, {file_system, [string()]} |
|
||||
{explicit_files, #{string() => binary()}}}
|
||||
| {src_file, string()}.
|
||||
-type options() :: [option()].
|
||||
|
||||
-export_type([ option/0
|
||||
, options/0
|
||||
]).
|
||||
|
||||
-spec version() -> {ok, binary()} | {error, term()}.
|
||||
version() ->
|
||||
case lists:keyfind(aesophia, 1, application:loaded_applications()) of
|
||||
false ->
|
||||
case application:load(aesophia) of
|
||||
ok ->
|
||||
case application:get_key(aesophia, vsn) of
|
||||
{ok, VsnString} ->
|
||||
{ok, list_to_binary(VsnString)};
|
||||
undefined ->
|
||||
{error, failed_to_load_aesophia}
|
||||
end;
|
||||
Err = {error, _} ->
|
||||
Err
|
||||
end;
|
||||
{_App, _Des, VsnString} ->
|
||||
{ok, list_to_binary(VsnString)}
|
||||
end.
|
||||
|
||||
-spec file(string()) -> {ok, map()} | {error, binary()}.
|
||||
file(Filename) ->
|
||||
Dir = filename:dirname(Filename),
|
||||
{ok, Cwd} = file:get_cwd(),
|
||||
file(Filename, [{include, {file_system, [Cwd, Dir]}}]).
|
||||
|
||||
-spec file(string(), options()) -> {ok, map()} | {error, binary()}.
|
||||
file(File, Options) ->
|
||||
case read_contract(File) of
|
||||
{ok, Bin} -> from_string(Bin, [{src_file, File} | Options]);
|
||||
{error, Error} ->
|
||||
ErrorString = [File,": ",file:format_error(Error)],
|
||||
{error, join_errors("File errors", [ErrorString], fun(E) -> E end)}
|
||||
end.
|
||||
|
||||
-spec from_string(binary() | string(), options()) -> {ok, map()} | {error, binary()}.
|
||||
from_string(ContractBin, Options) when is_binary(ContractBin) ->
|
||||
from_string(binary_to_list(ContractBin), Options);
|
||||
from_string(ContractString, Options) ->
|
||||
try
|
||||
#{icode := Icode} = string_to_icode(ContractString, Options),
|
||||
TypeInfo = extract_type_info(Icode),
|
||||
Assembler = assemble(Icode, Options),
|
||||
pp_assembler(Assembler, Options),
|
||||
ByteCodeList = to_bytecode(Assembler, Options),
|
||||
ByteCode = << << B:8 >> || B <- ByteCodeList >>,
|
||||
pp_bytecode(ByteCode, Options),
|
||||
{ok, Version} = version(),
|
||||
{ok, #{byte_code => ByteCode,
|
||||
compiler_version => Version,
|
||||
contract_source => ContractString,
|
||||
type_info => TypeInfo
|
||||
}}
|
||||
catch
|
||||
%% The compiler errors.
|
||||
error:{parse_errors, Errors} ->
|
||||
{error, join_errors("Parse errors", Errors, fun(E) -> E end)};
|
||||
error:{type_errors, Errors} ->
|
||||
{error, join_errors("Type errors", Errors, fun(E) -> E end)};
|
||||
error:{code_errors, Errors} ->
|
||||
{error, join_errors("Code errors", Errors,
|
||||
fun (E) -> io_lib:format("~p", [E]) end)}
|
||||
%% General programming errors in the compiler just signal error.
|
||||
end.
|
||||
|
||||
-spec string_to_icode(string(), [option()]) -> map().
|
||||
string_to_icode(ContractString, Options) ->
|
||||
Ast = parse(ContractString, Options),
|
||||
pp_sophia_code(Ast, Options),
|
||||
pp_ast(Ast, Options),
|
||||
{TypeEnv, TypedAst} = aeso_ast_infer_types:infer(Ast, [return_env]),
|
||||
pp_typed_ast(TypedAst, Options),
|
||||
Icode = ast_to_icode(TypedAst, Options),
|
||||
pp_icode(Icode, Options),
|
||||
#{ typed_ast => TypedAst,
|
||||
type_env => TypeEnv,
|
||||
icode => Icode }.
|
||||
|
||||
join_errors(Prefix, Errors, Pfun) ->
|
||||
Ess = [ Pfun(E) || E <- Errors ],
|
||||
list_to_binary(string:join([Prefix|Ess], "\n")).
|
||||
|
||||
-define(CALL_NAME, "__call").
|
||||
-define(DECODE_NAME, "__decode").
|
||||
|
||||
%% Takes a string containing a contract with a declaration/prototype of a
|
||||
%% function (foo, say) and adds function __call() = foo(args) calling this
|
||||
%% function. Returns the name of the called functions, typereps and Erlang
|
||||
%% terms for the arguments.
|
||||
%% NOTE: Special treatment for "init" since it might be implicit and has
|
||||
%% a special return type (typerep, T)
|
||||
-spec check_call(string(), string(), [string()], options()) -> {ok, string(), {[Type], Type}, [term()]} | {error, term()}
|
||||
when Type :: term().
|
||||
check_call(Source, "init" = FunName, Args, Options) ->
|
||||
PatchFun = fun(T) -> {tuple, [typerep, T]} end,
|
||||
case check_call(Source, FunName, Args, Options, PatchFun) of
|
||||
Err = {error, _} when Args == [] ->
|
||||
%% Try with default init-function
|
||||
case check_call(insert_init_function(Source, Options), FunName, Args, Options, PatchFun) of
|
||||
{error, _} -> Err; %% The first error is most likely better...
|
||||
Res -> Res
|
||||
end;
|
||||
Res ->
|
||||
Res
|
||||
end;
|
||||
check_call(Source, FunName, Args, Options) ->
|
||||
PatchFun = fun(T) -> T end,
|
||||
check_call(Source, FunName, Args, Options, PatchFun).
|
||||
|
||||
check_call(ContractString0, FunName, Args, Options, PatchFun) ->
|
||||
try
|
||||
%% First check the contract without the __call function
|
||||
#{} = string_to_icode(ContractString0, Options),
|
||||
ContractString = insert_call_function(ContractString0, FunName, Args, Options),
|
||||
#{typed_ast := TypedAst,
|
||||
icode := Icode} = string_to_icode(ContractString, Options),
|
||||
{ok, {FunName, {fun_t, _, _, ArgTypes, RetType}}} = get_call_type(TypedAst),
|
||||
ArgVMTypes = [ aeso_ast_to_icode:ast_typerep(T, Icode) || T <- ArgTypes ],
|
||||
RetVMType = case RetType of
|
||||
{id, _, "_"} -> any;
|
||||
_ -> aeso_ast_to_icode:ast_typerep(RetType, Icode)
|
||||
end,
|
||||
#{ functions := Funs } = Icode,
|
||||
ArgIcode = get_arg_icode(Funs),
|
||||
ArgTerms = [ icode_to_term(T, Arg) ||
|
||||
{T, Arg} <- lists:zip(ArgVMTypes, ArgIcode) ],
|
||||
{ok, FunName, {ArgVMTypes, PatchFun(RetVMType)}, ArgTerms}
|
||||
catch
|
||||
error:{parse_errors, Errors} ->
|
||||
{error, join_errors("Parse errors", Errors, fun (E) -> E end)};
|
||||
error:{type_errors, Errors} ->
|
||||
{error, join_errors("Type errors", Errors, fun (E) -> E end)};
|
||||
error:{badmatch, {error, missing_call_function}} ->
|
||||
{error, join_errors("Type errors", ["missing __call function"],
|
||||
fun (E) -> E end)};
|
||||
throw:Error -> %Don't ask
|
||||
{error, join_errors("Code errors", [Error],
|
||||
fun (E) -> io_lib:format("~p", [E]) end)}
|
||||
end.
|
||||
|
||||
%% Add the __call function to a contract.
|
||||
-spec insert_call_function(string(), string(), [string()], options()) -> string().
|
||||
insert_call_function(Code, FunName, Args, Options) ->
|
||||
Ast = parse(Code, Options),
|
||||
Ind = last_contract_indent(Ast),
|
||||
lists:flatten(
|
||||
[ Code,
|
||||
"\n\n",
|
||||
lists:duplicate(Ind, " "),
|
||||
"function __call() = ", FunName, "(", string:join(Args, ","), ")\n"
|
||||
]).
|
||||
|
||||
-spec insert_init_function(string(), options()) -> string().
|
||||
insert_init_function(Code, Options) ->
|
||||
Ast = parse(Code, Options),
|
||||
Ind = last_contract_indent(Ast),
|
||||
lists:flatten(
|
||||
[ Code,
|
||||
"\n\n",
|
||||
lists:duplicate(Ind, " "), "function init() = ()\n"
|
||||
]).
|
||||
|
||||
last_contract_indent(Decls) ->
|
||||
case lists:last(Decls) of
|
||||
{_, _, _, [Decl | _]} -> aeso_syntax:get_ann(col, Decl, 1) - 1;
|
||||
_ -> 0
|
||||
end.
|
||||
|
||||
-spec to_sophia_value(string(), string(), ok | error | revert, aeb_aevm_data:data()) ->
|
||||
{ok, aeso_syntax:expr()} | {error, term()}.
|
||||
to_sophia_value(ContractString, Fun, ResType, Data) ->
|
||||
to_sophia_value(ContractString, Fun, ResType, Data, []).
|
||||
|
||||
-spec to_sophia_value(string(), string(), ok | error | revert, binary(), options()) ->
|
||||
{ok, aeso_syntax:expr()} | {error, term()}.
|
||||
to_sophia_value(_, _, error, Err, _Options) ->
|
||||
{ok, {app, [], {id, [], "error"}, [{string, [], Err}]}};
|
||||
to_sophia_value(_, _, revert, Data, _Options) ->
|
||||
case aeb_heap:from_binary(string, Data) of
|
||||
{ok, Err} -> {ok, {app, [], {id, [], "abort"}, [{string, [], Err}]}};
|
||||
{error, _} = Err -> Err
|
||||
end;
|
||||
to_sophia_value(ContractString, FunName, ok, Data, Options) ->
|
||||
try
|
||||
#{ typed_ast := TypedAst,
|
||||
type_env := TypeEnv,
|
||||
icode := Icode } = string_to_icode(ContractString, Options),
|
||||
{ok, _, Type0} = get_decode_type(FunName, TypedAst),
|
||||
Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]),
|
||||
VmType = aeso_ast_to_icode:ast_typerep(Type, Icode),
|
||||
case aeb_heap:from_binary(VmType, Data) of
|
||||
{ok, VmValue} ->
|
||||
try
|
||||
{ok, translate_vm_value(VmType, Type, VmValue)}
|
||||
catch throw:cannot_translate_to_sophia ->
|
||||
Type0Str = prettypr:format(aeso_pretty:type(Type0)),
|
||||
{error, join_errors("Translation error", [lists:flatten(io_lib:format("Cannot translate VM value ~p\n of type ~p\n to Sophia type ~s\n",
|
||||
[Data, VmType, Type0Str]))],
|
||||
fun (E) -> E end)}
|
||||
end;
|
||||
{error, _Err} ->
|
||||
{error, join_errors("Decode errors", [lists:flatten(io_lib:format("Failed to decode binary at type ~p", [VmType]))],
|
||||
fun(E) -> E end)}
|
||||
end
|
||||
catch
|
||||
error:{parse_errors, Errors} ->
|
||||
{error, join_errors("Parse errors", Errors, fun (E) -> E end)};
|
||||
error:{type_errors, Errors} ->
|
||||
{error, join_errors("Type errors", Errors, fun (E) -> E end)};
|
||||
error:{badmatch, {error, missing_function}} ->
|
||||
{error, join_errors("Type errors", ["no function: '" ++ FunName ++ "'"],
|
||||
fun (E) -> E end)};
|
||||
throw:Error -> %Don't ask
|
||||
{error, join_errors("Code errors", [Error],
|
||||
fun (E) -> io_lib:format("~p", [E]) end)}
|
||||
end.
|
||||
|
||||
address_literal(Type, N) -> {Type, [], <<N:256>>}.
|
||||
|
||||
%% TODO: somewhere else
|
||||
-spec translate_vm_value(aeb_aevm_data:type(), aeso_syntax:type(), aeb_aevm_data:data()) -> aeso_syntax:expr().
|
||||
translate_vm_value(word, {id, _, "address"}, N) -> address_literal(account_pubkey, N);
|
||||
translate_vm_value(word, {app_t, _, {id, _, "oracle"}, _}, N) -> address_literal(oracle_pubkey, N);
|
||||
translate_vm_value(word, {app_t, _, {id, _, "oracle_query"}, _}, N) -> address_literal(oracle_query_id, N);
|
||||
translate_vm_value(word, {con, _, _Name}, N) -> address_literal(contract_pubkey, N);
|
||||
translate_vm_value(word, {id, _, "int"}, N) -> {int, [], N};
|
||||
translate_vm_value(word, {id, _, "bits"}, N) -> error({todo, bits, N});
|
||||
translate_vm_value(word, {id, _, "bool"}, N) -> {bool, [], N /= 0};
|
||||
translate_vm_value({bytes, Len}, {bytes_t, _, Len}, Val) when Len =< 32 ->
|
||||
{bytes, [], <<Val:Len/unit:8>>};
|
||||
translate_vm_value({bytes, Len}, {bytes_t, _, Len}, Val) ->
|
||||
{bytes, [], binary:part(<< <<W:32/unit:8>> || W <- tuple_to_list(Val) >>, 0, Len)};
|
||||
translate_vm_value(string, {id, _, "string"}, S) -> {string, [], S};
|
||||
translate_vm_value({list, VmType}, {app_t, _, {id, _, "list"}, [Type]}, List) ->
|
||||
{list, [], [translate_vm_value(VmType, Type, X) || X <- List]};
|
||||
translate_vm_value({option, VmType}, {app_t, _, {id, _, "option"}, [Type]}, Val) ->
|
||||
case Val of
|
||||
none -> {con, [], "None"};
|
||||
{some, X} -> {app, [], {con, [], "Some"}, [translate_vm_value(VmType, Type, X)]}
|
||||
end;
|
||||
translate_vm_value({variant, [[], [VmType]]}, {app_t, _, {id, _, "option"}, [Type]}, Val) ->
|
||||
case Val of
|
||||
{variant, 0, []} -> {con, [], "None"};
|
||||
{variant, 1, [X]} -> {app, [], {con, [], "Some"}, [translate_vm_value(VmType, Type, X)]}
|
||||
end;
|
||||
translate_vm_value({tuple, VmTypes}, {tuple_t, _, Types}, Val)
|
||||
when length(VmTypes) == length(Types),
|
||||
length(VmTypes) == tuple_size(Val) ->
|
||||
{tuple, [], [translate_vm_value(VmType, Type, X)
|
||||
|| {VmType, Type, X} <- lists:zip3(VmTypes, Types, tuple_to_list(Val))]};
|
||||
translate_vm_value({tuple, VmTypes}, {record_t, Fields}, Val)
|
||||
when length(VmTypes) == length(Fields),
|
||||
length(VmTypes) == tuple_size(Val) ->
|
||||
{record, [], [ {field, [], [{proj, [], FName}], translate_vm_value(VmType, FType, X)}
|
||||
|| {VmType, {field_t, _, FName, FType}, X} <- lists:zip3(VmTypes, Fields, tuple_to_list(Val)) ]};
|
||||
translate_vm_value({map, VmKeyType, VmValType}, {app_t, _, {id, _, "map"}, [KeyType, ValType]}, Map)
|
||||
when is_map(Map) ->
|
||||
{map, [], [ {translate_vm_value(VmKeyType, KeyType, Key),
|
||||
translate_vm_value(VmValType, ValType, Val)}
|
||||
|| {Key, Val} <- maps:to_list(Map) ]};
|
||||
translate_vm_value({variant, VmCons}, {variant_t, Cons}, {variant, Tag, Args})
|
||||
when length(VmCons) == length(Cons),
|
||||
length(VmCons) > Tag ->
|
||||
VmTypes = lists:nth(Tag + 1, VmCons),
|
||||
ConType = lists:nth(Tag + 1, Cons),
|
||||
translate_vm_value(VmTypes, ConType, Args);
|
||||
translate_vm_value(VmTypes, {constr_t, _, Con, Types}, Args)
|
||||
when length(VmTypes) == length(Types),
|
||||
length(VmTypes) == length(Args) ->
|
||||
{app, [], Con, [ translate_vm_value(VmType, Type, Arg)
|
||||
|| {VmType, Type, Arg} <- lists:zip3(VmTypes, Types, Args) ]};
|
||||
translate_vm_value(_VmType, _Type, _Data) ->
|
||||
throw(cannot_translate_to_sophia).
|
||||
|
||||
-spec create_calldata(string(), string(), [string()]) ->
|
||||
{ok, binary(), aeb_aevm_data:type(), aeb_aevm_data:type()}
|
||||
| {error, term()}.
|
||||
create_calldata(Code, Fun, Args) ->
|
||||
case check_call(Code, Fun, Args, []) of
|
||||
{ok, FunName, {ArgTypes, RetType}, VMArgs} ->
|
||||
aeb_abi:create_calldata(FunName, VMArgs, ArgTypes, RetType);
|
||||
{error, _} = Err -> Err
|
||||
end.
|
||||
|
||||
-spec decode_calldata(string(), string(), binary()) ->
|
||||
{ok, [aeso_syntax:type()], [aeso_syntax:expr()]}
|
||||
| {error, term()}.
|
||||
decode_calldata(ContractString, FunName, Calldata) ->
|
||||
try
|
||||
#{ typed_ast := TypedAst,
|
||||
type_env := TypeEnv,
|
||||
icode := Icode } = string_to_icode(ContractString, []),
|
||||
{ok, Args, _} = get_decode_type(FunName, TypedAst),
|
||||
DropArg = fun({arg, _, _, T}) -> T; (T) -> T end,
|
||||
ArgTypes = lists:map(DropArg, Args),
|
||||
Type0 = {tuple_t, [], ArgTypes},
|
||||
Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]),
|
||||
VmType = aeso_ast_to_icode:ast_typerep(Type, Icode),
|
||||
case aeb_heap:from_binary({tuple, [word, VmType]}, Calldata) of
|
||||
{ok, {_, VmValue}} ->
|
||||
try
|
||||
{tuple, [], Values} = translate_vm_value(VmType, Type, VmValue),
|
||||
{ok, ArgTypes, Values}
|
||||
catch throw:cannot_translate_to_sophia ->
|
||||
Type0Str = prettypr:format(aeso_pretty:type(Type0)),
|
||||
{error, join_errors("Translation error", [lists:flatten(io_lib:format("Cannot translate VM value ~p\n of type ~p\n to Sophia type ~s\n",
|
||||
[VmValue, VmType, Type0Str]))],
|
||||
fun (E) -> E end)}
|
||||
end;
|
||||
{error, _Err} ->
|
||||
{error, join_errors("Decode errors", [lists:flatten(io_lib:format("Failed to decode binary at type ~p", [VmType]))],
|
||||
fun(E) -> E end)}
|
||||
end
|
||||
catch
|
||||
error:{parse_errors, Errors} ->
|
||||
{error, join_errors("Parse errors", Errors, fun (E) -> E end)};
|
||||
error:{type_errors, Errors} ->
|
||||
{error, join_errors("Type errors", Errors, fun (E) -> E end)};
|
||||
error:{badmatch, {error, missing_function}} ->
|
||||
{error, join_errors("Type errors", ["no function: '" ++ FunName ++ "'"],
|
||||
fun (E) -> E end)};
|
||||
throw:Error -> %Don't ask
|
||||
{error, join_errors("Code errors", [Error],
|
||||
fun (E) -> io_lib:format("~p", [E]) end)}
|
||||
end.
|
||||
|
||||
|
||||
get_arg_icode(Funs) ->
|
||||
case [ Args || {[_, ?CALL_NAME], _, _, {funcall, _, Args}, _} <- Funs ] of
|
||||
[Args] -> Args;
|
||||
[] -> error({missing_call_function, Funs})
|
||||
end.
|
||||
|
||||
get_call_type([{contract, _, _, Defs}]) ->
|
||||
case [ {lists:last(QFunName), FunType}
|
||||
|| {letfun, _, {id, _, ?CALL_NAME}, [], _Ret,
|
||||
{typed, _,
|
||||
{app, _,
|
||||
{typed, _, {qid, _, QFunName}, FunType}, _}, _}} <- Defs ] of
|
||||
[Call] -> {ok, Call};
|
||||
[] -> {error, missing_call_function}
|
||||
end;
|
||||
get_call_type([_ | Contracts]) ->
|
||||
%% The __call should be in the final contract
|
||||
get_call_type(Contracts).
|
||||
|
||||
get_decode_type(FunName, [{contract, _, _, Defs}]) ->
|
||||
GetType = fun({letfun, _, {id, _, Name}, Args, Ret, _}) when Name == FunName -> [{Args, Ret}];
|
||||
({fun_decl, _, {id, _, Name}, {fun_t, _, _, Args, Ret}}) when Name == FunName -> [{Args, Ret}];
|
||||
(_) -> [] end,
|
||||
case lists:flatmap(GetType, Defs) of
|
||||
[{Args, Ret}] -> {ok, Args, Ret};
|
||||
[] -> {error, missing_function}
|
||||
end;
|
||||
get_decode_type(FunName, [_ | Contracts]) ->
|
||||
%% The __decode should be in the final contract
|
||||
get_decode_type(FunName, Contracts).
|
||||
|
||||
%% Translate an icode value (error if not value) to an Erlang term that can be
|
||||
%% consumed by aeb_heap:to_binary().
|
||||
icode_to_term(word, {integer, N}) -> N;
|
||||
icode_to_term(string, {tuple, [{integer, Len} | Words]}) ->
|
||||
<<Str:Len/binary, _/binary>> = << <<W:256>> || {integer, W} <- Words >>,
|
||||
Str;
|
||||
icode_to_term({bytes, Len}, {integer, Value}) when Len =< 32 ->
|
||||
Value;
|
||||
icode_to_term({bytes, Len}, {tuple, Words}) when Len > 32->
|
||||
list_to_tuple([W || {integer, W} <- Words]);
|
||||
icode_to_term({list, T}, {list, Vs}) ->
|
||||
[ icode_to_term(T, V) || V <- Vs ];
|
||||
icode_to_term({tuple, Ts}, {tuple, Vs}) ->
|
||||
list_to_tuple(icodes_to_terms(Ts, Vs));
|
||||
icode_to_term({variant, Cs}, {tuple, [{integer, Tag} | Args]}) ->
|
||||
Ts = lists:nth(Tag + 1, Cs),
|
||||
{variant, Tag, icodes_to_terms(Ts, Args)};
|
||||
icode_to_term(T = {map, KT, VT}, M) ->
|
||||
%% Maps are compiled to builtin and primop calls, so this gets a little hairy
|
||||
case M of
|
||||
{funcall, {var_ref, {builtin, map_put}}, [M1, K, V]} ->
|
||||
Map = icode_to_term(T, M1),
|
||||
Key = icode_to_term(KT, K),
|
||||
Val = icode_to_term(VT, V),
|
||||
Map#{ Key => Val };
|
||||
#prim_call_contract{ address = {integer, 0},
|
||||
arg = {tuple, [{integer, ?PRIM_CALL_MAP_EMPTY}, _, _]} } ->
|
||||
#{};
|
||||
_ -> throw({todo, M})
|
||||
end;
|
||||
icode_to_term(typerep, _) ->
|
||||
throw({todo, typerep});
|
||||
icode_to_term(T, V) ->
|
||||
throw({not_a_value, T, V}).
|
||||
|
||||
icodes_to_terms(Ts, Vs) ->
|
||||
[ icode_to_term(T, V) || {T, V} <- lists:zip(Ts, Vs) ].
|
||||
|
||||
ast_to_icode(TypedAst, Options) ->
|
||||
aeso_ast_to_icode:convert_typed(TypedAst, Options).
|
||||
|
||||
assemble(Icode, Options) ->
|
||||
aeso_icode_to_asm:convert(Icode, Options).
|
||||
|
||||
|
||||
to_bytecode(['COMMENT',_|Rest],_Options) ->
|
||||
to_bytecode(Rest,_Options);
|
||||
to_bytecode([Op|Rest], Options) ->
|
||||
[aeb_opcodes:m_to_op(Op)|to_bytecode(Rest, Options)];
|
||||
to_bytecode([], _) -> [].
|
||||
|
||||
extract_type_info(#{functions := Functions} =_Icode) ->
|
||||
ArgTypesOnly = fun(As) -> [ T || {_, T} <- As ] end,
|
||||
TypeInfo = [aeb_abi:function_type_info(list_to_binary(lists:last(Name)),
|
||||
ArgTypesOnly(Args), TypeRep)
|
||||
|| {Name, Attrs, Args,_Body, TypeRep} <- Functions,
|
||||
not is_tuple(Name),
|
||||
not lists:member(private, Attrs)
|
||||
],
|
||||
lists:sort(TypeInfo).
|
||||
|
||||
pp_sophia_code(C, Opts)-> pp(C, Opts, pp_sophia_code, fun(Code) ->
|
||||
io:format("~s\n", [prettypr:format(aeso_pretty:decls(Code))])
|
||||
end).
|
||||
pp_ast(C, Opts) -> pp(C, Opts, pp_ast, fun aeso_ast:pp/1).
|
||||
pp_typed_ast(C, Opts)-> pp(C, Opts, pp_typed_ast, fun aeso_ast:pp_typed/1).
|
||||
pp_icode(C, Opts) -> pp(C, Opts, pp_icode, fun aeso_icode:pp/1).
|
||||
pp_assembler(C, Opts)-> pp(C, Opts, pp_assembler, fun aeb_asm:pp/1).
|
||||
pp_bytecode(C, Opts) -> pp(C, Opts, pp_bytecode, fun aeb_disassemble:pp/1).
|
||||
|
||||
pp(Code, Options, Option, PPFun) ->
|
||||
case proplists:lookup(Option, Options) of
|
||||
{Option, true} ->
|
||||
PPFun(Code);
|
||||
none ->
|
||||
ok
|
||||
end.
|
||||
|
||||
|
||||
%% -------------------------------------------------------------------
|
||||
%% TODO: Tempoary parser hook below...
|
||||
|
||||
sophia_type_to_typerep(String) ->
|
||||
{ok, Ast} = aeso_parser:type(String),
|
||||
try aeso_ast_to_icode:ast_typerep(Ast) of
|
||||
Type -> {ok, Type}
|
||||
catch _:_ -> {error, bad_type}
|
||||
end.
|
||||
|
||||
parse(Text, Options) ->
|
||||
%% Try and return something sensible here!
|
||||
case aeso_parser:string(Text, Options) of
|
||||
%% Yay, it worked!
|
||||
{ok, Contract} -> Contract;
|
||||
%% Scan errors.
|
||||
{error, {Pos, scan_error}} ->
|
||||
parse_error(Pos, "scan error");
|
||||
{error, {Pos, scan_error_no_state}} ->
|
||||
parse_error(Pos, "scan error");
|
||||
%% Parse errors.
|
||||
{error, {Pos, parse_error, Error}} ->
|
||||
parse_error(Pos, Error);
|
||||
{error, {Pos, ambiguous_parse, As}} ->
|
||||
ErrorString = io_lib:format("Ambiguous ~p", [As]),
|
||||
parse_error(Pos, ErrorString);
|
||||
%% Include error
|
||||
{error, {Pos, include_error, File}} ->
|
||||
parse_error(Pos, io_lib:format("could not find include file '~s'", [File]))
|
||||
end.
|
||||
|
||||
parse_error(Pos, ErrorString) ->
|
||||
Error = io_lib:format("~s: ~s", [pos_error(Pos), ErrorString]),
|
||||
error({parse_errors, [Error]}).
|
||||
|
||||
read_contract(Name) ->
|
||||
file:read_file(Name).
|
||||
|
||||
pos_error({Line, Pos}) ->
|
||||
io_lib:format("line ~p, column ~p", [Line, Pos]);
|
||||
pos_error({no_file, Line, Pos}) ->
|
||||
pos_error({Line, Pos});
|
||||
pos_error({File, Line, Pos}) ->
|
||||
io_lib:format("file ~s, line ~p, column ~p", [File, Line, Pos]).
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
-module(aeso_constants).
|
||||
|
||||
-export([string/1, get_type/1]).
|
||||
|
||||
string(Str) ->
|
||||
case aeso_parser:string("let _ = " ++ Str) of
|
||||
{ok, [{letval, _, _, _, E}]} -> {ok, E};
|
||||
{ok, Other} -> error({internal_error, should_be_letval, Other});
|
||||
Err -> Err
|
||||
end.
|
||||
|
||||
get_type(Str) ->
|
||||
case aeso_parser:string("let _ = " ++ Str) of
|
||||
{ok, [Ast]} ->
|
||||
AstT = aeso_ast_infer_types:infer_constant(Ast),
|
||||
T = ast_to_type(AstT),
|
||||
{ok, T};
|
||||
{ok, Other} -> error({internal_error, should_be_letval, Other});
|
||||
Err -> Err
|
||||
end.
|
||||
|
||||
ast_to_type({id, _, T}) ->
|
||||
T;
|
||||
ast_to_type({tuple_t, _, []}) -> "()";
|
||||
ast_to_type({tuple_t, _, Ts}) ->
|
||||
"(" ++ list_ast_to_type(Ts) ++ ")";
|
||||
ast_to_type({app_t,_, {id, _, "list"}, [T]}) ->
|
||||
lists:flatten("list(" ++ ast_to_type(T) ++ ")");
|
||||
ast_to_type({app_t,_, {id, _, "option"}, [T]}) ->
|
||||
lists:flatten("option(" ++ ast_to_type(T) ++ ")").
|
||||
|
||||
list_ast_to_type([T]) ->
|
||||
ast_to_type(T);
|
||||
list_ast_to_type([T|Ts]) ->
|
||||
ast_to_type(T)
|
||||
++ ", "
|
||||
++ list_ast_to_type(Ts).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @author Happi (Erik Stenman)
|
||||
%%% @copyright (C) 2017, Aeternity Anstalt
|
||||
%%% @doc
|
||||
%%% Intermediate Code for Aeterinty Sophia language.
|
||||
%%% @end
|
||||
%%% Created : 21 Dec 2017
|
||||
%%%
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeso_icode).
|
||||
|
||||
-export([new/1,
|
||||
pp/1,
|
||||
set_name/2,
|
||||
set_namespace/2,
|
||||
enter_namespace/2,
|
||||
get_namespace/1,
|
||||
qualify/2,
|
||||
set_functions/2,
|
||||
map_typerep/2,
|
||||
option_typerep/1,
|
||||
get_constructor_tag/2]).
|
||||
|
||||
-export_type([icode/0]).
|
||||
|
||||
-include("aeso_icode.hrl").
|
||||
|
||||
-type type_def() :: fun(([aeb_aevm_data:type()]) -> aeb_aevm_data:type()).
|
||||
|
||||
-type bindings() :: any().
|
||||
-type fun_dec() :: { string()
|
||||
, [modifier()]
|
||||
, arg_list()
|
||||
, expr()
|
||||
, aeb_aevm_data:type()}.
|
||||
|
||||
-type modifier() :: private | stateful.
|
||||
|
||||
-type type_name() :: string() | [string()].
|
||||
|
||||
-type icode() :: #{ contract_name => string()
|
||||
, functions => [fun_dec()]
|
||||
, namespace => aeso_syntax:con() | aeso_syntax:qcon()
|
||||
, env => [bindings()]
|
||||
, state_type => aeb_aevm_data:type()
|
||||
, event_type => aeb_aevm_data:type()
|
||||
, types => #{ type_name() => type_def() }
|
||||
, type_vars => #{ string() => aeb_aevm_data:type() }
|
||||
, constructors => #{ [string()] => integer() } %% name to tag
|
||||
, options => [any()]
|
||||
}.
|
||||
|
||||
pp(Icode) ->
|
||||
%% TODO: Actually do *Pretty* printing.
|
||||
io:format("~p~n", [Icode]).
|
||||
|
||||
-spec new([any()]) -> icode().
|
||||
new(Options) ->
|
||||
#{ contract_name => ""
|
||||
, functions => []
|
||||
, env => new_env()
|
||||
%% Default to unit type for state and event
|
||||
, state_type => {tuple, []}
|
||||
, event_type => {tuple, []}
|
||||
, types => builtin_types()
|
||||
, type_vars => #{}
|
||||
, constructors => builtin_constructors()
|
||||
, options => Options}.
|
||||
|
||||
builtin_types() ->
|
||||
Word = fun([]) -> word end,
|
||||
#{ "bool" => Word
|
||||
, "int" => Word
|
||||
, "bits" => Word
|
||||
, "string" => fun([]) -> string end
|
||||
, "address" => Word
|
||||
, "hash" => Word
|
||||
, "signature" => fun([]) -> {tuple, [word, word]} end
|
||||
, "oracle" => fun([_, _]) -> word end
|
||||
, "oracle_query" => fun([_, _]) -> word end
|
||||
, "list" => fun([A]) -> {list, A} end
|
||||
, "option" => fun([A]) -> {variant, [[], [A]]} end
|
||||
, "map" => fun([K, V]) -> map_typerep(K, V) end
|
||||
, ["Chain", "ttl"] => fun([]) -> {variant, [[word], [word]]} end
|
||||
}.
|
||||
|
||||
builtin_constructors() ->
|
||||
#{ ["RelativeTTL"] => 0
|
||||
, ["FixedTTL"] => 1
|
||||
, ["None"] => 0
|
||||
, ["Some"] => 1 }.
|
||||
|
||||
map_typerep(K, V) ->
|
||||
{map, K, V}.
|
||||
|
||||
option_typerep(A) ->
|
||||
{variant, [[], [A]]}.
|
||||
|
||||
new_env() ->
|
||||
[].
|
||||
|
||||
-spec set_name(string(), icode()) -> icode().
|
||||
set_name(Name, Icode) ->
|
||||
maps:put(contract_name, Name, Icode).
|
||||
|
||||
-spec set_namespace(aeso_syntax:con() | aeso_syntax:qcon(), icode()) -> icode().
|
||||
set_namespace(NS, Icode) -> Icode#{ namespace => NS }.
|
||||
|
||||
-spec enter_namespace(aeso_syntax:con(), icode()) -> icode().
|
||||
enter_namespace(NS, Icode = #{ namespace := NS1 }) ->
|
||||
Icode#{ namespace => aeso_syntax:qualify(NS1, NS) };
|
||||
enter_namespace(NS, Icode) ->
|
||||
Icode#{ namespace => NS }.
|
||||
|
||||
-spec get_namespace(icode()) -> false | aeso_syntax:con() | aeso_syntax:qcon().
|
||||
get_namespace(Icode) -> maps:get(namespace, Icode, false).
|
||||
|
||||
-spec qualify(aeso_syntax:id() | aeso_syntax:con(), icode()) -> aeso_syntax:id() | aeso_syntax:qid() | aeso_syntax:con() | aeso_syntax:qcon().
|
||||
qualify(X, Icode) ->
|
||||
case get_namespace(Icode) of
|
||||
false -> X;
|
||||
NS -> aeso_syntax:qualify(NS, X)
|
||||
end.
|
||||
|
||||
-spec set_functions([fun_dec()], icode()) -> icode().
|
||||
set_functions(NewFuns, Icode) ->
|
||||
maps:put(functions, NewFuns, Icode).
|
||||
|
||||
-spec get_constructor_tag([string()], icode()) -> integer().
|
||||
get_constructor_tag(Name, #{constructors := Constructors}) ->
|
||||
case maps:get(Name, Constructors, undefined) of
|
||||
undefined -> error({undefined_constructor, Name});
|
||||
Tag -> Tag
|
||||
end.
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
|
||||
-include_lib("aebytecode/include/aeb_typerep_def.hrl").
|
||||
|
||||
-record(arg, {name::string(), type::?Type()}).
|
||||
|
||||
-type expr() :: term().
|
||||
-type arg() :: #arg{name::string(), type::?Type()}.
|
||||
-type arg_list() :: [arg()].
|
||||
|
||||
-record(fun_dec, { name :: string()
|
||||
, args :: arg_list()
|
||||
, body :: expr()}).
|
||||
|
||||
-record(var_ref, { name :: string() | list(string()) | {builtin, atom() | tuple()}}).
|
||||
|
||||
-record(prim_call_contract,
|
||||
{ gas :: expr()
|
||||
, address :: expr()
|
||||
, value :: expr()
|
||||
, arg :: expr()
|
||||
, type_hash:: expr()
|
||||
}).
|
||||
|
||||
-record(prim_balance, { address :: expr() }).
|
||||
-record(prim_block_hash, { height :: expr() }).
|
||||
-record(prim_put, { state :: expr() }).
|
||||
|
||||
-record(integer, {value :: integer()}).
|
||||
|
||||
-record(tuple, {cpts :: [expr()]}).
|
||||
|
||||
-record(list, {elems :: [expr()]}).
|
||||
|
||||
-record(unop, { op :: term()
|
||||
, rand :: expr()}).
|
||||
|
||||
-record(binop, { op :: term()
|
||||
, left :: expr()
|
||||
, right :: expr()}).
|
||||
|
||||
-record(ifte, { decision :: expr()
|
||||
, then :: expr()
|
||||
, else :: expr()}).
|
||||
|
||||
-record(switch, { expr :: expr()
|
||||
, cases :: [{expr(),expr()}]}).
|
||||
|
||||
-record(funcall, { function :: expr()
|
||||
, args :: [expr()]}).
|
||||
|
||||
-record(lambda, { args :: arg_list(),
|
||||
body :: expr()}).
|
||||
|
||||
-record(missing_field, { format :: string()
|
||||
, args :: [term()]}).
|
||||
|
||||
-record(seq, {exprs :: [expr()]}).
|
||||
|
||||
-record(event, {topics :: [expr()], payload :: expr()}).
|
||||
@@ -0,0 +1,981 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @author Happi (Erik Stenman)
|
||||
%%% @copyright (C) 2017, Aeternity Anstalt
|
||||
%%% @doc
|
||||
%%% Translator from Aesophia Icode to Aevm Assebly
|
||||
%%% @end
|
||||
%%% Created : 21 Dec 2017
|
||||
%%%
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeso_icode_to_asm).
|
||||
|
||||
-export([convert/2]).
|
||||
|
||||
-include_lib("aebytecode/include/aeb_opcodes.hrl").
|
||||
-include("aeso_icode.hrl").
|
||||
|
||||
i(Code) -> aeb_opcodes:mnemonic(Code).
|
||||
|
||||
%% We don't track purity or statefulness in the type checker yet.
|
||||
is_stateful({FName, _, _, _, _}) -> lists:last(FName) /= "init".
|
||||
|
||||
is_public({_Name, Attrs, _Args, _Body, _Type}) -> not lists:member(private, Attrs).
|
||||
|
||||
convert(#{ contract_name := _ContractName
|
||||
, state_type := StateType
|
||||
, functions := Functions
|
||||
},
|
||||
_Options) ->
|
||||
%% Create a function dispatcher
|
||||
DispatchFun = {"_main", [], [{"arg", "_"}],
|
||||
{switch, {var_ref, "arg"},
|
||||
[{{tuple, [fun_hash(Fun),
|
||||
{tuple, make_args(Args)}]},
|
||||
icode_seq([ hack_return_address(Fun, length(Args) + 1) ] ++
|
||||
[ {funcall, {var_ref, FName}, make_args(Args)}]
|
||||
)}
|
||||
|| Fun={FName, _, Args, _,_TypeRep} <- Functions, is_public(Fun) ]},
|
||||
word},
|
||||
NewFunctions = Functions ++ [DispatchFun],
|
||||
%% Create a function environment
|
||||
Funs = [{Name, length(Args), make_ref()}
|
||||
|| {Name, _Attrs, Args, _Body, _Type} <- NewFunctions],
|
||||
%% Create dummy code to call the main function with one argument
|
||||
%% taken from the stack
|
||||
StopLabel = make_ref(),
|
||||
StatefulStopLabel = make_ref(),
|
||||
MainFunction = lookup_fun(Funs, "_main"),
|
||||
|
||||
StateTypeValue = aeso_ast_to_icode:type_value(StateType),
|
||||
|
||||
DispatchCode = [%% push two return addresses to stop, one for stateful
|
||||
%% functions and one for non-stateful functions.
|
||||
push_label(StatefulStopLabel),
|
||||
push_label(StopLabel),
|
||||
%% The calldata is already on the stack when we start. Put
|
||||
%% it on top (also reorders StatefulStop and Stop).
|
||||
swap(2),
|
||||
|
||||
jump(MainFunction),
|
||||
jumpdest(StatefulStopLabel),
|
||||
|
||||
%% We need to encode the state type and put it
|
||||
%% underneath the return value.
|
||||
assemble_expr(Funs, [], nontail, StateTypeValue), %% StateT Ret
|
||||
swap(1), %% Ret StateT
|
||||
|
||||
%% We should also change the state value at address 0 to a
|
||||
%% pointer to the state value (to allow 0 to represent an
|
||||
%% unchanged state).
|
||||
i(?MSIZE), %% Ptr
|
||||
push(0), i(?MLOAD), %% Val Ptr
|
||||
i(?MSIZE), i(?MSTORE), %% Ptr Mem[Ptr] := Val
|
||||
push(0), i(?MSTORE), %% Mem[0] := Ptr
|
||||
|
||||
%% The pointer to the return value is on top of
|
||||
%% the stack, but the return instruction takes two
|
||||
%% stack arguments.
|
||||
push(0),
|
||||
i(?RETURN),
|
||||
jumpdest(StopLabel),
|
||||
%% Set state pointer to 0 to indicate that we didn't change state
|
||||
push(0), dup(1), i(?MSTORE),
|
||||
%% Same as StatefulStopLabel above
|
||||
push(0),
|
||||
i(?RETURN)
|
||||
],
|
||||
%% Code is a deep list of instructions, containing labels and
|
||||
%% references to them. Labels take the form {'JUMPDEST', Ref}, and
|
||||
%% references take the form {push_label, Ref}, which is translated
|
||||
%% into a PUSH instruction.
|
||||
Code = [assemble_function(Funs, Name, Args, Body)
|
||||
|| {Name, _, Args, Body, _Type} <- NewFunctions],
|
||||
resolve_references(
|
||||
[%% i(?COMMENT), "CONTRACT: " ++ ContractName,
|
||||
DispatchCode,
|
||||
Code]).
|
||||
|
||||
%% Generate error on correct format.
|
||||
|
||||
gen_error(Error) ->
|
||||
error({code_errors, [Error]}).
|
||||
|
||||
make_args(Args) ->
|
||||
[{var_ref, [I-1 + $a]} || I <- lists:seq(1, length(Args))].
|
||||
|
||||
fun_hash({FName, _, Args, _, TypeRep}) ->
|
||||
ArgType = {tuple, [T || {_, T} <- Args]},
|
||||
<<Hash:256>> = aeb_abi:function_type_hash(list_to_binary(lists:last(FName)), ArgType, TypeRep),
|
||||
{integer, Hash}.
|
||||
|
||||
%% Expects two return addresses below N elements on the stack. Picks the top
|
||||
%% one for stateful functions and the bottom one for non-stateful.
|
||||
hack_return_address(Fun, N) ->
|
||||
case is_stateful(Fun) of
|
||||
true -> {inline_asm, [i(?MSIZE)]};
|
||||
false ->
|
||||
{inline_asm, %% X1 .. XN State NoState
|
||||
[ dup(N + 2) %% NoState X1 .. XN State NoState
|
||||
, swap(N + 1) %% State X1 .. XN NoState NoState
|
||||
]} %% Top of the stack will be discarded.
|
||||
end.
|
||||
|
||||
assemble_function(Funs, Name, Args, Body) ->
|
||||
[jumpdest(lookup_fun(Funs, Name)),
|
||||
assemble_expr(Funs, lists:reverse(Args), tail, Body),
|
||||
%% swap return value and first argument
|
||||
pop_args(length(Args)),
|
||||
swap(1),
|
||||
i(?JUMP)].
|
||||
|
||||
%% {seq, Es} - should be "one" operation in terms of stack content
|
||||
%% i.e. after the `seq` there should be one new element on the stack.
|
||||
assemble_expr(Funs, Stack, Tail, {seq, [E]}) ->
|
||||
assemble_expr(Funs, Stack, Tail, E);
|
||||
assemble_expr(Funs, Stack, Tail, {seq, [E | Es]}) ->
|
||||
[assemble_expr(Funs, Stack, nontail, E),
|
||||
assemble_expr(Funs, Stack, Tail, {seq, Es})];
|
||||
assemble_expr(_Funs, _Stack, _Tail, {inline_asm, Code}) ->
|
||||
Code; %% Unsafe! Code should take care to respect the stack!
|
||||
assemble_expr(Funs, Stack, _TailPosition, {var_ref, Id}) ->
|
||||
case lists:keymember(Id, 1, Stack) of
|
||||
true ->
|
||||
dup(lookup_var(Id, Stack));
|
||||
false ->
|
||||
%% Build a closure
|
||||
%% When a top-level fun is called directly, we do not
|
||||
%% reach this case.
|
||||
Eta = make_ref(),
|
||||
Continue = make_ref(),
|
||||
[i(?MSIZE),
|
||||
push_label(Eta),
|
||||
dup(2),
|
||||
i(?MSTORE),
|
||||
jump(Continue),
|
||||
%% the code of the closure
|
||||
jumpdest(Eta),
|
||||
%% pop the pointer to the function
|
||||
pop(1),
|
||||
jump(lookup_fun(Funs, Id)),
|
||||
jumpdest(Continue)]
|
||||
end;
|
||||
assemble_expr(_, _, _, {missing_field, Format, Args}) ->
|
||||
io:format(Format, Args),
|
||||
gen_error(missing_field);
|
||||
assemble_expr(_Funs, _Stack, _, {integer, N}) ->
|
||||
push(N);
|
||||
assemble_expr(Funs, Stack, _, {tuple, Cpts}) ->
|
||||
%% We build tuples right-to-left, so that the first write to the
|
||||
%% tuple extends the memory size. Because we use ?MSIZE as the
|
||||
%% heap pointer, we must allocate the tuple AFTER computing the
|
||||
%% first element.
|
||||
%% We store elements into the tuple as soon as possible, to avoid
|
||||
%% keeping them for a long time on the stack.
|
||||
case lists:reverse(Cpts) of
|
||||
[] ->
|
||||
i(?MSIZE);
|
||||
[Last|Rest] ->
|
||||
[assemble_expr(Funs, Stack, nontail, Last),
|
||||
%% allocate the tuple memory
|
||||
i(?MSIZE),
|
||||
%% compute address of last word
|
||||
push(32 * (length(Cpts) - 1)), i(?ADD),
|
||||
%% Stack: <last-value> <pointer>
|
||||
%% Write value to memory (allocates the tuple)
|
||||
swap(1), dup(2), i(?MSTORE),
|
||||
%% Stack: pointer to last word written
|
||||
[[%% Update pointer to next word to be written
|
||||
push(32), swap(1), i(?SUB),
|
||||
%% Compute element
|
||||
assemble_expr(Funs, [pointer|Stack], nontail, A),
|
||||
%% Write element to memory
|
||||
dup(2), i(?MSTORE)]
|
||||
%% And we leave a pointer to the last word written on
|
||||
%% the stack
|
||||
|| A <- Rest]]
|
||||
%% The pointer to the entire tuple is on the stack
|
||||
end;
|
||||
assemble_expr(_Funs, _Stack, _, {list, []}) ->
|
||||
%% Use Erik's value of -1 for []
|
||||
[push(0), i(?NOT)];
|
||||
assemble_expr(Funs, Stack, _, {list, [A|B]}) ->
|
||||
assemble_expr(Funs, Stack, nontail, {tuple, [A, {list, B}]});
|
||||
assemble_expr(Funs, Stack, _, {unop, '!', A}) ->
|
||||
case A of
|
||||
{binop, Logical, _, _} when Logical=='&&'; Logical=='||' ->
|
||||
assemble_expr(Funs, Stack, nontail, {ifte, A, {integer, 0}, {integer, 1}});
|
||||
_ ->
|
||||
[assemble_expr(Funs, Stack, nontail, A),
|
||||
i(?ISZERO)
|
||||
]
|
||||
end;
|
||||
assemble_expr(Funs, Stack, _, {event, Topics, Payload}) ->
|
||||
[assemble_exprs(Funs, Stack, Topics ++ [Payload]),
|
||||
case length(Topics) of
|
||||
0 -> i(?LOG0);
|
||||
1 -> i(?LOG1);
|
||||
2 -> i(?LOG2);
|
||||
3 -> i(?LOG3);
|
||||
4 -> i(?LOG4)
|
||||
end, i(?MSIZE)];
|
||||
assemble_expr(Funs, Stack, _, {unop, Op, A}) ->
|
||||
[assemble_expr(Funs, Stack, nontail, A),
|
||||
assemble_prefix(Op)];
|
||||
assemble_expr(Funs, Stack, Tail, {binop, '&&', A, B}) ->
|
||||
assemble_expr(Funs, Stack, Tail, {ifte, A, B, {integer, 0}});
|
||||
assemble_expr(Funs, Stack, Tail, {binop, '||', A, B}) ->
|
||||
assemble_expr(Funs, Stack, Tail, {ifte, A, {integer, 1}, B});
|
||||
assemble_expr(Funs, Stack, Tail, {binop, '::', A, B}) ->
|
||||
%% Take advantage of optimizations in tuple construction.
|
||||
assemble_expr(Funs, Stack, Tail, {tuple, [A, B]});
|
||||
assemble_expr(Funs, Stack, _, {binop, Op, A, B}) ->
|
||||
%% EEVM binary instructions take their first argument from the top
|
||||
%% of the stack, so to get operands on the stack in the right
|
||||
%% order, we evaluate from right to left.
|
||||
[assemble_expr(Funs, Stack, nontail, B),
|
||||
assemble_expr(Funs, [dummy|Stack], nontail, A),
|
||||
assemble_infix(Op)];
|
||||
assemble_expr(Funs, Stack, _, {lambda, Args, Body}) ->
|
||||
Function = make_ref(),
|
||||
FunBody = make_ref(),
|
||||
Continue = make_ref(),
|
||||
NoMatch = make_ref(),
|
||||
FreeVars = free_vars({lambda, Args, Body}),
|
||||
{NewVars, MatchingCode} = assemble_pattern(FunBody, NoMatch, {tuple, [{var_ref, "_"}|FreeVars]}),
|
||||
BodyCode = assemble_expr(Funs, NewVars ++ lists:reverse([ {Arg#arg.name, Arg#arg.type} || Arg <- Args ]), tail, Body),
|
||||
[assemble_expr(Funs, Stack, nontail, {tuple, [{label, Function}|FreeVars]}),
|
||||
jump(Continue), %% will be optimized away
|
||||
jumpdest(Function),
|
||||
%% A pointer to the closure is on the stack
|
||||
MatchingCode,
|
||||
jumpdest(FunBody),
|
||||
BodyCode,
|
||||
pop_args(length(Args)+length(NewVars)),
|
||||
swap(1),
|
||||
i(?JUMP),
|
||||
jumpdest(NoMatch), %% dead code--raise an exception just in case
|
||||
push(0),
|
||||
i(?NOT),
|
||||
i(?MLOAD),
|
||||
i(?STOP),
|
||||
jumpdest(Continue)];
|
||||
assemble_expr(_, _, _, {label, Label}) ->
|
||||
push_label(Label);
|
||||
assemble_expr(Funs, Stack, nontail, {funcall, Fun, Args}) ->
|
||||
Return = make_ref(),
|
||||
%% This is the obvious code:
|
||||
%% [{push_label, Return},
|
||||
%% assemble_exprs(Funs, [return_address|Stack], Args++[Fun]),
|
||||
%% 'JUMP',
|
||||
%% {'JUMPDEST', Return}];
|
||||
%% Its problem is that it stores the return address on the stack
|
||||
%% while the arguments are computed, which is unnecessary. To
|
||||
%% avoid that, we compute the last argument FIRST, and replace it
|
||||
%% with the return address using a SWAP.
|
||||
%%
|
||||
%% assemble_function leaves the code pointer of the function to
|
||||
%% call on top of the stack, and--if the function is not a
|
||||
%% top-level name--a pointer to its tuple of free variables. In
|
||||
%% either case a JUMP is the right way to call it.
|
||||
case Args of
|
||||
[] ->
|
||||
[push_label(Return),
|
||||
assemble_function(Funs, [return_address|Stack], Fun),
|
||||
i(?JUMP),
|
||||
jumpdest(Return)];
|
||||
_ ->
|
||||
{Init, [Last]} = lists:split(length(Args) - 1, Args),
|
||||
[assemble_exprs(Funs, Stack, [Last|Init]),
|
||||
%% Put the return address in the right place, which also
|
||||
%% reorders the args correctly.
|
||||
push_label(Return),
|
||||
swap(length(Args)),
|
||||
assemble_function(Funs, [dummy || _ <- Args] ++ [return_address|Stack], Fun),
|
||||
i(?JUMP),
|
||||
jumpdest(Return)]
|
||||
end;
|
||||
assemble_expr(Funs, Stack, tail, {funcall, Fun, Args}) ->
|
||||
IsTopLevel = is_top_level_fun(Stack, Fun),
|
||||
%% If the fun is not top-level, then it may refer to local
|
||||
%% variables and must be computed before stack shuffling.
|
||||
ArgsAndFun = Args++[Fun || not IsTopLevel],
|
||||
ComputeArgsAndFun = assemble_exprs(Funs, Stack, ArgsAndFun),
|
||||
%% Copy arguments back down the stack to the start of the frame
|
||||
ShuffleSpec = lists:seq(length(ArgsAndFun), 1, -1) ++ [discard || _ <- Stack],
|
||||
Shuffle = shuffle_stack(ShuffleSpec),
|
||||
[ComputeArgsAndFun, Shuffle,
|
||||
if IsTopLevel ->
|
||||
%% still need to compute function
|
||||
assemble_function(Funs, [], Fun);
|
||||
true ->
|
||||
%% need to unpack a closure
|
||||
[dup(1), i(?MLOAD)]
|
||||
end,
|
||||
i(?JUMP)];
|
||||
assemble_expr(Funs, Stack, Tail, {ifte, Decision, Then, Else}) ->
|
||||
%% This compilation scheme introduces a lot of labels and
|
||||
%% jumps. Unnecessary ones are removed later in
|
||||
%% resolve_references.
|
||||
Close = make_ref(),
|
||||
ThenL = make_ref(),
|
||||
ElseL = make_ref(),
|
||||
[assemble_decision(Funs, Stack, Decision, ThenL, ElseL),
|
||||
jumpdest(ElseL),
|
||||
assemble_expr(Funs, Stack, Tail, Else),
|
||||
jump(Close),
|
||||
jumpdest(ThenL),
|
||||
assemble_expr(Funs, Stack, Tail, Then),
|
||||
jumpdest(Close)
|
||||
];
|
||||
assemble_expr(Funs, Stack, Tail, {switch, A, Cases}) ->
|
||||
Close = make_ref(),
|
||||
[assemble_expr(Funs, Stack, nontail, A),
|
||||
assemble_cases(Funs, Stack, Tail, Close, Cases),
|
||||
{'JUMPDEST', Close}];
|
||||
%% State primitives
|
||||
%% (A pointer to) the contract state is stored at address 0.
|
||||
assemble_expr(_Funs, _Stack, _Tail, prim_state) ->
|
||||
[push(0), i(?MLOAD)];
|
||||
assemble_expr(Funs, Stack, _Tail, #prim_put{ state = State }) ->
|
||||
[assemble_expr(Funs, Stack, nontail, State),
|
||||
push(0), i(?MSTORE), %% We need something for the unit value on the stack,
|
||||
i(?MSIZE)]; %% MSIZE is the cheapest instruction.
|
||||
%% Environment primitives
|
||||
assemble_expr(_Funs, _Stack, _Tail, prim_contract_address) ->
|
||||
[i(?ADDRESS)];
|
||||
assemble_expr(_Funs, _Stack, _Tail, prim_call_origin) ->
|
||||
[i(?ORIGIN)];
|
||||
assemble_expr(_Funs, _Stack, _Tail, prim_caller) ->
|
||||
[i(?CALLER)];
|
||||
assemble_expr(_Funs, _Stack, _Tail, prim_call_value) ->
|
||||
[i(?CALLVALUE)];
|
||||
assemble_expr(_Funs, _Stack, _Tail, prim_gas_price) ->
|
||||
[i(?GASPRICE)];
|
||||
assemble_expr(_Funs, _Stack, _Tail, prim_gas_left) ->
|
||||
[i(?GAS)];
|
||||
assemble_expr(_Funs, _Stack, _Tail, prim_coinbase) ->
|
||||
[i(?COINBASE)];
|
||||
assemble_expr(_Funs, _Stack, _Tail, prim_timestamp) ->
|
||||
[i(?TIMESTAMP)];
|
||||
assemble_expr(_Funs, _Stack, _Tail, prim_block_height) ->
|
||||
[i(?NUMBER)];
|
||||
assemble_expr(_Funs, _Stack, _Tail, prim_difficulty) ->
|
||||
[i(?DIFFICULTY)];
|
||||
assemble_expr(_Funs, _Stack, _Tail, prim_gas_limit) ->
|
||||
[i(?GASLIMIT)];
|
||||
assemble_expr(Funs, Stack, _Tail, #prim_balance{ address = Addr }) ->
|
||||
[assemble_expr(Funs, Stack, nontail, Addr),
|
||||
i(?BALANCE)];
|
||||
assemble_expr(Funs, Stack, _Tail, #prim_block_hash{ height = Height }) ->
|
||||
[assemble_expr(Funs, Stack, nontail, Height),
|
||||
i(?BLOCKHASH)];
|
||||
assemble_expr(Funs, Stack, _Tail,
|
||||
#prim_call_contract{ gas = Gas
|
||||
, address = To
|
||||
, value = Value
|
||||
, arg = Arg
|
||||
, type_hash= TypeHash
|
||||
}) ->
|
||||
%% ?CALL takes (from the top)
|
||||
%% Gas, To, Value, Arg, TypeHash, _OOffset,_OSize
|
||||
%% So assemble these in reverse order.
|
||||
[ assemble_exprs(Funs, Stack, [ {integer, 0}, {integer, 0}, TypeHash
|
||||
, Arg, Value, To, Gas ])
|
||||
, i(?CALL)
|
||||
].
|
||||
|
||||
|
||||
assemble_exprs(_Funs, _Stack, []) ->
|
||||
[];
|
||||
assemble_exprs(Funs, Stack, [E|Es]) ->
|
||||
[assemble_expr(Funs, Stack, nontail, E),
|
||||
assemble_exprs(Funs, [dummy|Stack], Es)].
|
||||
|
||||
assemble_decision(Funs, Stack, {binop, '&&', A, B}, Then, Else) ->
|
||||
Label = make_ref(),
|
||||
[assemble_decision(Funs, Stack, A, Label, Else),
|
||||
jumpdest(Label),
|
||||
assemble_decision(Funs, Stack, B, Then, Else)];
|
||||
assemble_decision(Funs, Stack, {binop, '||', A, B}, Then, Else) ->
|
||||
Label = make_ref(),
|
||||
[assemble_decision(Funs, Stack, A, Then, Label),
|
||||
jumpdest(Label),
|
||||
assemble_decision(Funs, Stack, B, Then, Else)];
|
||||
assemble_decision(Funs, Stack, {unop, '!', A}, Then, Else) ->
|
||||
assemble_decision(Funs, Stack, A, Else, Then);
|
||||
assemble_decision(Funs, Stack, {ifte, A, B, C}, Then, Else) ->
|
||||
TrueL = make_ref(),
|
||||
FalseL = make_ref(),
|
||||
[assemble_decision(Funs, Stack, A, TrueL, FalseL),
|
||||
jumpdest(TrueL), assemble_decision(Funs, Stack, B, Then, Else),
|
||||
jumpdest(FalseL), assemble_decision(Funs, Stack, C, Then, Else)];
|
||||
assemble_decision(Funs, Stack, Decision, Then, Else) ->
|
||||
[assemble_expr(Funs, Stack, nontail, Decision),
|
||||
jump_if(Then), jump(Else)].
|
||||
|
||||
%% Entered with value to switch on on top of the stack
|
||||
%% Evaluate selected case, then jump to Close with result on the
|
||||
%% stack.
|
||||
assemble_cases(_Funs, _Stack, _Tail, _Close, []) ->
|
||||
%% No match! What should be do? There's no real way to raise an
|
||||
%% exception, except consuming all the gas.
|
||||
%% There should not be enough gas to do this:
|
||||
[push(1), i(?NOT),
|
||||
i(?MLOAD),
|
||||
%% now stop, so that jump optimizer realizes we will not fall
|
||||
%% through this code.
|
||||
i(?STOP)];
|
||||
assemble_cases(Funs, Stack, Tail, Close, [{Pattern, Body}|Cases]) ->
|
||||
Succeed = make_ref(),
|
||||
Fail = make_ref(),
|
||||
{NewVars, MatchingCode} =
|
||||
assemble_pattern(Succeed, Fail, Pattern),
|
||||
%% In the code that follows, if this is NOT the last case, then we
|
||||
%% save the value being switched on, and discard it on
|
||||
%% success. The code is simpler if this IS the last case.
|
||||
[[dup(1) || Cases /= []], %% save value for next case, if there is one
|
||||
MatchingCode,
|
||||
jumpdest(Succeed),
|
||||
%% Discard saved value, if we saved one
|
||||
[case NewVars of
|
||||
[] ->
|
||||
pop(1);
|
||||
[_] ->
|
||||
%% Special case for peep-hole optimization
|
||||
pop_args(1);
|
||||
_ ->
|
||||
[swap(length(NewVars)), pop(1)]
|
||||
end
|
||||
|| Cases/=[]],
|
||||
assemble_expr(Funs,
|
||||
case Cases of
|
||||
[] -> NewVars;
|
||||
_ -> reorder_vars(NewVars)
|
||||
end
|
||||
++Stack, Tail, Body),
|
||||
%% If the Body makes a tail call, then we will not return
|
||||
%% here--but it doesn't matter, because
|
||||
%% (a) the NewVars will be popped before the tailcall
|
||||
%% (b) the code below will be deleted since it is dead
|
||||
pop_args(length(NewVars)),
|
||||
jump(Close),
|
||||
jumpdest(Fail),
|
||||
assemble_cases(Funs, Stack, Tail, Close, Cases)].
|
||||
|
||||
%% Entered with value to match on top of the stack.
|
||||
%% Generated code removes value, and
|
||||
%% - jumps to Fail if no match, or
|
||||
%% - binds variables, leaves them on the stack, and jumps to Succeed
|
||||
%% Result is a list of variables to add to the stack, and the matching
|
||||
%% code.
|
||||
assemble_pattern(Succeed, Fail, {integer, N}) ->
|
||||
{[], [push(N),
|
||||
i(?EQ),
|
||||
jump_if(Succeed),
|
||||
jump(Fail)]};
|
||||
assemble_pattern(Succeed, _Fail, {var_ref, "_"}) ->
|
||||
{[], [i(?POP), jump(Succeed)]};
|
||||
assemble_pattern(Succeed, Fail, {missing_field, _, _}) ->
|
||||
%% Missing record fields are quite ok in patterns.
|
||||
assemble_pattern(Succeed, Fail, {var_ref, "_"});
|
||||
assemble_pattern(Succeed, _Fail, {var_ref, Id}) ->
|
||||
{[{Id, "_"}], jump(Succeed)};
|
||||
assemble_pattern(Succeed, _Fail, {tuple, []}) ->
|
||||
{[], [pop(1), jump(Succeed)]};
|
||||
assemble_pattern(Succeed, Fail, {tuple, [A]}) ->
|
||||
%% Treat this case specially, because we don't need to save the
|
||||
%% pointer to the tuple.
|
||||
{AVars, ACode} = assemble_pattern(Succeed, Fail, A),
|
||||
{AVars, [i(?MLOAD),
|
||||
ACode]};
|
||||
assemble_pattern(Succeed, Fail, {tuple, [A|B]}) ->
|
||||
%% Entered with the address of the tuple on the top of the
|
||||
%% stack. We will duplicate the address before matching on A.
|
||||
Continue = make_ref(), %% the label for matching B
|
||||
Pop1Fail = make_ref(), %% pop 1 word and goto Fail
|
||||
PopNFail = make_ref(), %% pop length(AVars) words and goto Fail
|
||||
{AVars, ACode} =
|
||||
assemble_pattern(Continue, Pop1Fail, A),
|
||||
{BVars, BCode} =
|
||||
assemble_pattern(Succeed, PopNFail, {tuple, B}),
|
||||
{BVars ++ reorder_vars(AVars),
|
||||
[%% duplicate the pointer so we don't lose it when we match on A
|
||||
dup(1),
|
||||
i(?MLOAD),
|
||||
ACode,
|
||||
jumpdest(Continue),
|
||||
%% Bring the pointer to the top of the stack--this reorders AVars!
|
||||
swap(length(AVars)),
|
||||
push(32),
|
||||
i(?ADD),
|
||||
BCode,
|
||||
case AVars of
|
||||
[] ->
|
||||
[jumpdest(Pop1Fail), pop(1),
|
||||
jumpdest(PopNFail),
|
||||
jump(Fail)];
|
||||
_ ->
|
||||
[{'JUMPDEST', PopNFail}, pop(length(AVars)-1),
|
||||
{'JUMPDEST', Pop1Fail}, pop(1),
|
||||
{push_label, Fail}, 'JUMP']
|
||||
end]};
|
||||
assemble_pattern(Succeed, Fail, {list, []}) ->
|
||||
%% [] is represented by -1.
|
||||
{[], [push(1),
|
||||
i(?ADD),
|
||||
jump_if(Fail),
|
||||
jump(Succeed)]};
|
||||
assemble_pattern(Succeed, Fail, {list, [A|B]}) ->
|
||||
assemble_pattern(Succeed, Fail, {binop, '::', A, {list, B}});
|
||||
assemble_pattern(Succeed, Fail, {binop, '::', A, B}) ->
|
||||
%% Make sure it's not [], then match as tuple.
|
||||
NotNil = make_ref(),
|
||||
{Vars, Code} = assemble_pattern(Succeed, Fail, {tuple, [A, B]}),
|
||||
{Vars, [dup(1), push(1), i(?ADD), %% Check for [] without consuming the value
|
||||
jump_if(NotNil), %% so it's still there when matching the tuple.
|
||||
pop(1), %% It was [] so discard the saved value.
|
||||
jump(Fail),
|
||||
jumpdest(NotNil),
|
||||
Code]}.
|
||||
|
||||
%% When Vars are on the stack, with a value we want to discard
|
||||
%% below them, then we swap the top variable with that value and pop.
|
||||
%% This reorders the variables on the stack, as follows:
|
||||
reorder_vars([]) ->
|
||||
[];
|
||||
reorder_vars([V|Vs]) ->
|
||||
Vs ++ [V].
|
||||
|
||||
assemble_prefix('sha3') -> [i(?DUP1), i(?MLOAD), %% length, ptr
|
||||
i(?SWAP1), push(32), i(?ADD), %% ptr+32, length
|
||||
i(?SHA3)];
|
||||
assemble_prefix('-') -> [push(0), i(?SUB)];
|
||||
assemble_prefix('bnot') -> i(?NOT).
|
||||
|
||||
assemble_infix('+') -> i(?ADD);
|
||||
assemble_infix('-') -> i(?SUB);
|
||||
assemble_infix('*') -> i(?MUL);
|
||||
assemble_infix('/') -> i(?SDIV);
|
||||
assemble_infix('div') -> i(?DIV);
|
||||
assemble_infix('mod') -> i(?MOD);
|
||||
assemble_infix('^') -> i(?EXP);
|
||||
assemble_infix('bor') -> i(?OR);
|
||||
assemble_infix('band') -> i(?AND);
|
||||
assemble_infix('bxor') -> i(?XOR);
|
||||
assemble_infix('bsl') -> i(?SHL);
|
||||
assemble_infix('bsr') -> i(?SHR);
|
||||
assemble_infix('<') -> i(?SLT); %% comparisons are SIGNED
|
||||
assemble_infix('>') -> i(?SGT);
|
||||
assemble_infix('==') -> i(?EQ);
|
||||
assemble_infix('<=') -> [i(?SGT), i(?ISZERO)];
|
||||
assemble_infix('=<') -> [i(?SGT), i(?ISZERO)];
|
||||
assemble_infix('>=') -> [i(?SLT), i(?ISZERO)];
|
||||
assemble_infix('!=') -> [i(?EQ), i(?ISZERO)];
|
||||
assemble_infix('!') -> [i(?ADD), i(?MLOAD)];
|
||||
assemble_infix('byte') -> i(?BYTE).
|
||||
%% assemble_infix('::') -> [i(?MSIZE), write_word(0), write_word(1)].
|
||||
|
||||
%% a function may either refer to a top-level function, in which case
|
||||
%% we fetch the code label from Funs, or it may be a lambda-expression
|
||||
%% (including a top-level function passed as a parameter). In the
|
||||
%% latter case, the function value is a pointer to a tuple of the code
|
||||
%% pointer and the free variables: we keep the pointer and push the
|
||||
%% code pointer onto the stack. In either case, we are ready to enter
|
||||
%% the function with JUMP.
|
||||
assemble_function(Funs, Stack, Fun) ->
|
||||
case is_top_level_fun(Stack, Fun) of
|
||||
true ->
|
||||
{var_ref, Name} = Fun,
|
||||
{push_label, lookup_fun(Funs, Name)};
|
||||
false ->
|
||||
[assemble_expr(Funs, Stack, nontail, Fun),
|
||||
dup(1),
|
||||
i(?MLOAD)]
|
||||
end.
|
||||
|
||||
free_vars(V={var_ref, _}) ->
|
||||
[V];
|
||||
free_vars({switch, E, Cases}) ->
|
||||
lists:umerge(free_vars(E),
|
||||
lists:umerge([free_vars(Body)--free_vars(Pattern)
|
||||
|| {Pattern, Body} <- Cases]));
|
||||
free_vars({lambda, Args, Body}) ->
|
||||
free_vars(Body) -- [{var_ref, Arg#arg.name} || Arg <- Args];
|
||||
free_vars(T) when is_tuple(T) ->
|
||||
free_vars(tuple_to_list(T));
|
||||
free_vars([H|T]) ->
|
||||
lists:umerge(free_vars(H), free_vars(T));
|
||||
free_vars(_) ->
|
||||
[].
|
||||
|
||||
|
||||
|
||||
%% shuffle_stack reorders the stack, for example before a tailcall. It is called
|
||||
%% with a description of the current stack, and how the final stack
|
||||
%% should appear. The argument is a list containing
|
||||
%% a NUMBER for each element that should be kept, the number being
|
||||
%% the position this element should occupy in the final stack
|
||||
%% discard, for elements that can be discarded.
|
||||
%% The positions start at 1, referring to the variable to be placed at
|
||||
%% the bottom of the stack, and ranging up to the size of the final stack.
|
||||
shuffle_stack([]) ->
|
||||
[];
|
||||
shuffle_stack([discard|Stack]) ->
|
||||
[i(?POP) | shuffle_stack(Stack)];
|
||||
shuffle_stack([N|Stack]) ->
|
||||
case length(Stack) + 1 - N of
|
||||
0 ->
|
||||
%% the job should be finished
|
||||
CorrectStack = lists:seq(N - 1, 1, -1),
|
||||
CorrectStack = Stack,
|
||||
[];
|
||||
MoveBy ->
|
||||
{Pref, [_|Suff]} = lists:split(MoveBy - 1, Stack),
|
||||
[swap(MoveBy) | shuffle_stack([lists:nth(MoveBy, Stack) | Pref ++ [N|Suff]])]
|
||||
end.
|
||||
|
||||
|
||||
|
||||
lookup_fun(Funs, Name) ->
|
||||
case [Ref || {Name1, _, Ref} <- Funs,
|
||||
Name == Name1] of
|
||||
[Ref] -> Ref;
|
||||
[] -> gen_error({undefined_function, Name})
|
||||
end.
|
||||
|
||||
is_top_level_fun(Stack, {var_ref, Id}) ->
|
||||
not lists:keymember(Id, 1, Stack);
|
||||
is_top_level_fun(_, _) ->
|
||||
false.
|
||||
|
||||
lookup_var(Id, Stack) ->
|
||||
lookup_var(1, Id, Stack).
|
||||
|
||||
lookup_var(N, Id, [{Id, _Type}|_]) ->
|
||||
N;
|
||||
lookup_var(N, Id, [_|Stack]) ->
|
||||
lookup_var(N + 1, Id, Stack);
|
||||
lookup_var(_, Id, []) ->
|
||||
gen_error({var_not_in_scope, Id}).
|
||||
|
||||
%% Smart instruction generation
|
||||
|
||||
%% TODO: handle references to the stack beyond depth 16. Perhaps the
|
||||
%% best way is to repush variables that will be needed in
|
||||
%% subexpressions before evaluating he subexpression... i.e. fix the
|
||||
%% problem in assemble_expr, rather than here. A fix here would have
|
||||
%% to save the top elements of the stack in memory, duplicate the
|
||||
%% targetted element, and then repush the values from memory.
|
||||
dup(N) when 1 =< N, N =< 16 ->
|
||||
i(?DUP1 + N - 1).
|
||||
|
||||
push(N) ->
|
||||
Bytes = binary:encode_unsigned(N),
|
||||
true = size(Bytes) =< 32,
|
||||
[i(?PUSH1 + size(Bytes) - 1) |
|
||||
binary_to_list(Bytes)].
|
||||
|
||||
%% Pop N values from UNDER the top element of the stack.
|
||||
%% This is a pseudo-instruction so peephole optimization can
|
||||
%% combine pop_args(M), pop_args(N) to pop_args(M+N)
|
||||
pop_args(0) ->
|
||||
[];
|
||||
pop_args(N) ->
|
||||
{pop_args, N}.
|
||||
%% [swap(N), pop(N)].
|
||||
|
||||
pop(N) ->
|
||||
[i(?POP) || _ <- lists:seq(1, N)].
|
||||
|
||||
swap(0) ->
|
||||
%% Doesn't exist, but is logically a no-op.
|
||||
[];
|
||||
swap(N) when 1 =< N, N =< 16 ->
|
||||
i(?SWAP1 + N - 1).
|
||||
|
||||
jumpdest(Label) -> {i(?JUMPDEST), Label}.
|
||||
push_label(Label) -> {push_label, Label}.
|
||||
|
||||
jump(Label) -> [push_label(Label), i(?JUMP)].
|
||||
jump_if(Label) -> [push_label(Label), i(?JUMPI)].
|
||||
|
||||
%% ICode utilities (TODO: move to separate module)
|
||||
|
||||
icode_noname() -> #var_ref{name = "_"}.
|
||||
|
||||
icode_seq([A]) -> A;
|
||||
icode_seq([A | As]) ->
|
||||
icode_seq(A, icode_seq(As)).
|
||||
|
||||
icode_seq(A, B) ->
|
||||
#switch{ expr = A, cases = [{icode_noname(), B}] }.
|
||||
|
||||
%% Stack: <N elements> ADDR
|
||||
%% Write elements at addresses ADDR, ADDR+32, ADDR+64...
|
||||
%% Stack afterwards: ADDR
|
||||
% write_words(N) ->
|
||||
% [write_word(I) || I <- lists:seq(N-1, 0, -1)].
|
||||
|
||||
%% Unused at the moment. Comment out to please dialyzer.
|
||||
%% write_word(I) ->
|
||||
%% [%% Stack: elements e ADDR
|
||||
%% swap(1),
|
||||
%% dup(2),
|
||||
%% %% Stack: elements ADDR e ADDR
|
||||
%% push(32*I),
|
||||
%% i(?ADD),
|
||||
%% %% Stack: elements ADDR e ADDR+32I
|
||||
%% i(?MSTORE)].
|
||||
|
||||
%% Resolve references, and convert code from deep list to flat list.
|
||||
%% List elements are:
|
||||
%% Opcodes
|
||||
%% Byte values
|
||||
%% {'JUMPDEST', Ref} -- assembles to ?JUMPDEST and sets Ref
|
||||
%% {push_label, Ref} -- assembles to ?PUSHN address bytes
|
||||
|
||||
%% For now, we assemble all code addresses as three bytes.
|
||||
|
||||
resolve_references(Code) ->
|
||||
Peephole = peep_hole(lists:flatten(Code)),
|
||||
%% WARNING: Optimizing jumps reorders the code and deletes
|
||||
%% instructions. When debugging the assemble_ functions, it can be
|
||||
%% useful to replace the next line by:
|
||||
%% Instrs = lists:flatten(Code),
|
||||
%% thus disabling the optimization.
|
||||
OptimizedJumps = optimize_jumps(Peephole),
|
||||
Instrs = lists:reverse(peep_hole_backwards(lists:reverse(OptimizedJumps))),
|
||||
Labels = define_labels(0, Instrs),
|
||||
lists:flatten([use_labels(Labels, I) || I <- Instrs]).
|
||||
|
||||
define_labels(Addr, [{'JUMPDEST', Lab}|More]) ->
|
||||
[{Lab, Addr}|define_labels(Addr + 1, More)];
|
||||
define_labels(Addr, [{push_label, _}|More]) ->
|
||||
define_labels(Addr + 4, More);
|
||||
define_labels(Addr, [{pop_args, N}|More]) ->
|
||||
define_labels(Addr + N + 1, More);
|
||||
define_labels(Addr, [_|More]) ->
|
||||
define_labels(Addr + 1, More);
|
||||
define_labels(_, []) ->
|
||||
[].
|
||||
|
||||
use_labels(_, {'JUMPDEST', _}) ->
|
||||
'JUMPDEST';
|
||||
use_labels(Labels, {push_label, Ref}) ->
|
||||
case proplists:get_value(Ref, Labels) of
|
||||
undefined ->
|
||||
gen_error({undefined_label, Ref});
|
||||
Addr when is_integer(Addr) ->
|
||||
[i(?PUSH3),
|
||||
Addr div 65536, (Addr div 256) rem 256, Addr rem 256]
|
||||
end;
|
||||
use_labels(_, {pop_args, N}) ->
|
||||
[swap(N), pop(N)];
|
||||
use_labels(_, I) ->
|
||||
I.
|
||||
|
||||
%% Peep-hole optimization.
|
||||
%% The compilation of conditionals can introduce jumps depending on
|
||||
%% constants 1 and 0. These are removed by peep-hole optimization.
|
||||
|
||||
peep_hole(['PUSH1', 0, {push_label, _}, 'JUMPI'|More]) ->
|
||||
peep_hole(More);
|
||||
peep_hole(['PUSH1', 1, {push_label, Lab}, 'JUMPI'|More]) ->
|
||||
[{push_label, Lab}, 'JUMP'|peep_hole(More)];
|
||||
peep_hole([{pop_args, M}, {pop_args, N}|More]) when M + N =< 16 ->
|
||||
peep_hole([{pop_args, M + N}|More]);
|
||||
peep_hole([I|More]) ->
|
||||
[I|peep_hole(More)];
|
||||
peep_hole([]) ->
|
||||
[].
|
||||
|
||||
%% Peep-hole optimization on reversed instructions lists.
|
||||
|
||||
peep_hole_backwards(Code) ->
|
||||
NewCode = peep_hole_backwards1(Code),
|
||||
if Code == NewCode -> Code;
|
||||
true -> peep_hole_backwards(NewCode)
|
||||
end.
|
||||
|
||||
peep_hole_backwards1(['ADD', 0, 'PUSH1'|Code]) ->
|
||||
peep_hole_backwards1(Code);
|
||||
peep_hole_backwards1(['POP', UnOp|Code]) when UnOp=='MLOAD';UnOp=='ISZERO';UnOp=='NOT' ->
|
||||
peep_hole_backwards1(['POP'|Code]);
|
||||
peep_hole_backwards1(['POP', BinOp|Code]) when
|
||||
%% TODO: more binary operators
|
||||
BinOp=='ADD';BinOp=='SUB';BinOp=='MUL';BinOp=='SDIV' ->
|
||||
peep_hole_backwards1(['POP', 'POP'|Code]);
|
||||
peep_hole_backwards1(['POP', _, 'PUSH1'|Code]) ->
|
||||
peep_hole_backwards1(Code);
|
||||
peep_hole_backwards1([I|Code]) ->
|
||||
[I|peep_hole_backwards1(Code)];
|
||||
peep_hole_backwards1([]) ->
|
||||
[].
|
||||
|
||||
%% Jump optimization:
|
||||
%% Replaces a jump to a jump with a jump to the final destination
|
||||
%% Moves basic blocks to eliminate an unconditional jump to them.
|
||||
|
||||
%% The compilation of conditionals generates a lot of labels and
|
||||
%% jumps, some of them unnecessary. This optimization phase reorders
|
||||
%% code so that as many jumps as possible can be eliminated, and
|
||||
%% replaced by just falling through to the destination label. This
|
||||
%% both optimizes the code generated by conditionals, and converts one
|
||||
%% call of a function into falling through into its code--so it
|
||||
%% reorders code quite aggressively. Function returns are indirect
|
||||
%% jumps, however, and are never optimized away.
|
||||
|
||||
%% IMPORTANT: since execution begins at address zero, then the first
|
||||
%% block of code must never be moved elsewhere. The code below has
|
||||
%% this property, because it processes blocks from left to right, and
|
||||
%% because the first block does not begin with a label, and so can
|
||||
%% never be jumped to--hence no code can be inserted before it.
|
||||
|
||||
%% The optimization works by taking one block of code at a time, and
|
||||
%% then prepending blocks that jump directly to it, and appending
|
||||
%% blocks that it jumps directly to, resulting in a jump-free sequence
|
||||
%% that is as long as possible. To do so, we store blocks in the form
|
||||
%% {OptionalLabel, Body, OptionalJump} which represents the code block
|
||||
%% OptionalLabel++Body++OptionalJump; the optional parts are the empty
|
||||
%% list of instructions if not present. Two blocks can be merged if
|
||||
%% the first ends in an OptionalJump to the OptionalLabel beginning
|
||||
%% the second; the OptionalJump can then be removed (and the
|
||||
%% OptionalLabel if there are no other references to it--this happens
|
||||
%% during dead code elimination.
|
||||
|
||||
%% TODO: the present implementation is QUADRATIC, because we search
|
||||
%% repeatedly for matching blocks to merge with the first one, storing
|
||||
%% the blocks in a list. A near linear time implementation could use
|
||||
%% two ets tables, one keyed on the labels, and the other keyed on the
|
||||
%% final jumps.
|
||||
|
||||
optimize_jumps(Code) ->
|
||||
JJs = jumps_to_jumps(Code),
|
||||
ShortCircuited = [short_circuit_jumps(JJs, Instr) || Instr <- Code],
|
||||
NoDeadCode = eliminate_dead_code(ShortCircuited),
|
||||
MovedCode = merge_blocks(moveable_blocks(NoDeadCode)),
|
||||
%% Moving code may have made some labels superfluous.
|
||||
eliminate_dead_code(MovedCode).
|
||||
|
||||
|
||||
jumps_to_jumps([{'JUMPDEST', Label}, {push_label, Target}, 'JUMP'|More]) ->
|
||||
[{Label, Target}|jumps_to_jumps(More)];
|
||||
jumps_to_jumps([{'JUMPDEST', Label}, {'JUMPDEST', Target}|More]) ->
|
||||
[{Label, Target}|jumps_to_jumps([{'JUMPDEST', Target}|More])];
|
||||
jumps_to_jumps([_|More]) ->
|
||||
jumps_to_jumps(More);
|
||||
jumps_to_jumps([]) ->
|
||||
[].
|
||||
|
||||
short_circuit_jumps(JJs, {push_label, Lab}) ->
|
||||
case proplists:get_value(Lab, JJs) of
|
||||
undefined ->
|
||||
{push_label, Lab};
|
||||
Target ->
|
||||
%% I wonder if this will ever loop infinitely?
|
||||
short_circuit_jumps(JJs, {push_label, Target})
|
||||
end;
|
||||
short_circuit_jumps(_JJs, Instr) ->
|
||||
Instr.
|
||||
|
||||
eliminate_dead_code(Code) ->
|
||||
Jumps = lists:usort([Lab || {push_label, Lab} <- Code]),
|
||||
NewCode = live_code(Jumps, Code),
|
||||
if Code==NewCode ->
|
||||
Code;
|
||||
true ->
|
||||
eliminate_dead_code(NewCode)
|
||||
end.
|
||||
|
||||
live_code(Jumps, ['JUMP'|More]) ->
|
||||
['JUMP'|dead_code(Jumps, More)];
|
||||
live_code(Jumps, ['STOP'|More]) ->
|
||||
['STOP'|dead_code(Jumps, More)];
|
||||
live_code(Jumps, [{'JUMPDEST', Lab}|More]) ->
|
||||
case lists:member(Lab, Jumps) of
|
||||
true ->
|
||||
[{'JUMPDEST', Lab}|live_code(Jumps, More)];
|
||||
false ->
|
||||
live_code(Jumps, More)
|
||||
end;
|
||||
live_code(Jumps, [I|More]) ->
|
||||
[I|live_code(Jumps, More)];
|
||||
live_code(_, []) ->
|
||||
[].
|
||||
|
||||
dead_code(Jumps, [{'JUMPDEST', Lab}|More]) ->
|
||||
case lists:member(Lab, Jumps) of
|
||||
true ->
|
||||
[{'JUMPDEST', Lab}|live_code(Jumps, More)];
|
||||
false ->
|
||||
dead_code(Jumps, More)
|
||||
end;
|
||||
dead_code(Jumps, [_I|More]) ->
|
||||
dead_code(Jumps, More);
|
||||
dead_code(_, []) ->
|
||||
[].
|
||||
|
||||
%% Split the code into "moveable blocks" that control flow only
|
||||
%% reaches via jumps.
|
||||
moveable_blocks([]) ->
|
||||
[];
|
||||
moveable_blocks([I]) ->
|
||||
[[I]];
|
||||
moveable_blocks([Jump|More]) when Jump=='JUMP'; Jump=='STOP' ->
|
||||
[[Jump]|moveable_blocks(More)];
|
||||
moveable_blocks([I|More]) ->
|
||||
[Block|MoreBlocks] = moveable_blocks(More),
|
||||
[[I|Block]|MoreBlocks].
|
||||
|
||||
%% Merge blocks to eliminate jumps where possible.
|
||||
merge_blocks(Blocks) ->
|
||||
BlocksAndTargets = [label_and_jump(B) || B <- Blocks],
|
||||
[I || {Pref, Body, Suff} <- merge_after(BlocksAndTargets),
|
||||
I <- Pref++Body++Suff].
|
||||
|
||||
%% Merge the first block with other blocks that come after it
|
||||
merge_after(All=[{Label, Body, [{push_label, Target}, 'JUMP']}|BlocksAndTargets]) ->
|
||||
case [{B, J} || {[{'JUMPDEST', L}], B, J} <- BlocksAndTargets,
|
||||
L == Target] of
|
||||
[{B, J}|_] ->
|
||||
merge_after([{Label, Body ++ [{'JUMPDEST', Target}] ++ B, J}|
|
||||
lists:delete({[{'JUMPDEST', Target}], B, J},
|
||||
BlocksAndTargets)]);
|
||||
[] ->
|
||||
merge_before(All)
|
||||
end;
|
||||
merge_after(All) ->
|
||||
merge_before(All).
|
||||
|
||||
%% The first block cannot be merged with any blocks that it jumps
|
||||
%% to... but maybe it can be merged with a block that jumps to it!
|
||||
merge_before([Block={[{'JUMPDEST', Label}], Body, Jump}|BlocksAndTargets]) ->
|
||||
case [{L, B, T} || {L, B, [{push_label, T}, 'JUMP']} <- BlocksAndTargets,
|
||||
T == Label] of
|
||||
[{L, B, T}|_] ->
|
||||
merge_before([{L, B ++ [{'JUMPDEST', Label}] ++ Body, Jump}
|
||||
|lists:delete({L, B, [{push_label, T}, 'JUMP']}, BlocksAndTargets)]);
|
||||
_ ->
|
||||
[Block | merge_after(BlocksAndTargets)]
|
||||
end;
|
||||
merge_before([Block|BlocksAndTargets]) ->
|
||||
[Block | merge_after(BlocksAndTargets)];
|
||||
merge_before([]) ->
|
||||
[].
|
||||
|
||||
%% Convert each block to a PREFIX, which is a label or empty, a
|
||||
%% middle, and a SUFFIX which is a JUMP to a label, or empty.
|
||||
label_and_jump(B) ->
|
||||
{Label, B1} = case B of
|
||||
[{'JUMPDEST', L}|More1] ->
|
||||
{[{'JUMPDEST', L}], More1};
|
||||
_ ->
|
||||
{[], B}
|
||||
end,
|
||||
{Target, B2} = case lists:reverse(B1) of
|
||||
['JUMP', {push_label, T}|More2] ->
|
||||
{[{push_label, T}, 'JUMP'], lists:reverse(More2)};
|
||||
_ ->
|
||||
{[], B1}
|
||||
end,
|
||||
{Label, B2, Target}.
|
||||
@@ -0,0 +1,299 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @author Ulf Norell
|
||||
%%% @copyright (C) 2019, Aeternity Anstalt
|
||||
%%% @doc
|
||||
%%% Fate backend for Sophia compiler
|
||||
%%% @end
|
||||
%%% Created : 11 Jan 2019
|
||||
%%%
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeso_icode_to_fate).
|
||||
|
||||
-include("aeso_icode.hrl").
|
||||
|
||||
-export([compile/2]).
|
||||
|
||||
%% -- Preamble ---------------------------------------------------------------
|
||||
|
||||
-define(TODO(What), error({todo, ?FILE, ?LINE, ?FUNCTION_NAME, What})).
|
||||
|
||||
-define(i(__X__), {immediate, __X__}).
|
||||
-define(a, {stack, 0}).
|
||||
|
||||
-record(env, { args = [], stack = [], tailpos = true }).
|
||||
|
||||
%% -- Debugging --------------------------------------------------------------
|
||||
|
||||
%% debug(Options, Fmt) -> debug(Options, Fmt, []).
|
||||
debug(Options, Fmt, Args) ->
|
||||
case proplists:get_value(debug, Options, true) of
|
||||
true -> io:format(Fmt, Args);
|
||||
false -> ok
|
||||
end.
|
||||
|
||||
%% -- Main -------------------------------------------------------------------
|
||||
|
||||
%% @doc Main entry point.
|
||||
compile(ICode, Options) ->
|
||||
#{ contract_name := _ContractName,
|
||||
state_type := _StateType,
|
||||
functions := Functions } = ICode,
|
||||
SFuns = functions_to_scode(Functions, Options),
|
||||
SFuns1 = optimize_scode(SFuns, Options),
|
||||
to_basic_blocks(SFuns1, Options).
|
||||
|
||||
functions_to_scode(Functions, Options) ->
|
||||
maps:from_list(
|
||||
[ {list_to_binary(Name), function_to_scode(Name, Args, Body, Type, Options)}
|
||||
|| {Name, _Ann, Args, Body, Type} <- Functions, Name /= "init" ]). %% TODO: skip init for now
|
||||
|
||||
function_to_scode(Name, Args, Body, Type, Options) ->
|
||||
debug(Options, "Compiling ~p ~p : ~p ->\n ~p\n", [Name, Args, Type, Body]),
|
||||
ArgTypes = [ icode_type_to_fate(T) || {_, T} <- Args ],
|
||||
ResType = icode_type_to_fate(Type),
|
||||
SCode = to_scode(init_env(Args), Body),
|
||||
debug(Options, " scode: ~p\n", [SCode]),
|
||||
{{ArgTypes, ResType}, SCode}.
|
||||
|
||||
%% -- Types ------------------------------------------------------------------
|
||||
|
||||
%% TODO: the Fate types don't seem to be specified anywhere...
|
||||
icode_type_to_fate(word) -> integer;
|
||||
icode_type_to_fate(string) -> string;
|
||||
icode_type_to_fate({tuple, Types}) ->
|
||||
{tuple, lists:map(fun icode_type_to_fate/1, Types)};
|
||||
icode_type_to_fate({list, Type}) ->
|
||||
{list, icode_type_to_fate(Type)};
|
||||
icode_type_to_fate(typerep) -> typerep;
|
||||
icode_type_to_fate(Type) -> ?TODO(Type).
|
||||
|
||||
%% -- Phase I ----------------------------------------------------------------
|
||||
%% Icode to structured assembly
|
||||
|
||||
%% -- Environment functions --
|
||||
|
||||
init_env(Args) ->
|
||||
#env{ args = Args, stack = [], tailpos = true }.
|
||||
|
||||
push_env(Type, Env) ->
|
||||
Env#env{ stack = [{"_", Type} | Env#env.stack] }.
|
||||
|
||||
notail(Env) -> Env#env{ tailpos = false }.
|
||||
|
||||
lookup_var(#env{ args = Args, stack = S }, X) ->
|
||||
case {keyfind_index(X, 1, S), keyfind_index(X, 1, Args)} of
|
||||
{false, false} -> false;
|
||||
{false, Arg} -> {arg, Arg};
|
||||
{Local, _} -> {stack, Local}
|
||||
end.
|
||||
|
||||
%% -- The compiler --
|
||||
|
||||
to_scode(_Env, #integer{ value = N }) ->
|
||||
[aeb_fate_code:push(?i(N))]; %% Doesn't exist (yet), translated by desugaring
|
||||
|
||||
to_scode(Env, #var_ref{name = X}) ->
|
||||
case lookup_var(Env, X) of
|
||||
false -> error({unbound_variable, X, Env});
|
||||
{stack, N} -> [aeb_fate_code:dup(?i(N))];
|
||||
{arg, N} -> [aeb_fate_code:push({arg, N})]
|
||||
end;
|
||||
to_scode(Env, #binop{ op = Op, left = A, right = B }) ->
|
||||
[ to_scode(notail(Env), B)
|
||||
, to_scode(push_env(binop_type_r(Op), Env), A)
|
||||
, binop_to_scode(Op) ];
|
||||
|
||||
to_scode(Env, #ifte{decision = Dec, then = Then, else = Else}) ->
|
||||
[ to_scode(notail(Env), Dec)
|
||||
, {ifte, to_scode(Env, Then), to_scode(Env, Else)} ];
|
||||
|
||||
to_scode(_Env, Icode) -> ?TODO(Icode).
|
||||
|
||||
%% -- Operators --
|
||||
|
||||
binop_types('+') -> {word, word};
|
||||
binop_types('-') -> {word, word};
|
||||
binop_types('==') -> {word, word};
|
||||
binop_types(Op) -> ?TODO(Op).
|
||||
|
||||
%% binop_type_l(Op) -> element(1, binop_types(Op)).
|
||||
binop_type_r(Op) -> element(2, binop_types(Op)).
|
||||
|
||||
binop_to_scode('+') -> add_a_a_a(); %% Optimization introduces other variants
|
||||
binop_to_scode('-') -> sub_a_a_a();
|
||||
binop_to_scode('==') -> eq_a_a_a().
|
||||
% binop_to_scode(Op) -> ?TODO(Op).
|
||||
|
||||
add_a_a_a() -> aeb_fate_code:add(?a, ?a, ?a).
|
||||
sub_a_a_a() -> aeb_fate_code:sub(?a, ?a, ?a).
|
||||
eq_a_a_a() -> aeb_fate_code:eq(?a, ?a, ?a).
|
||||
|
||||
%% -- Phase II ---------------------------------------------------------------
|
||||
%% Optimize
|
||||
|
||||
optimize_scode(Funs, Options) ->
|
||||
maps:map(fun(Name, Def) -> optimize_fun(Funs, Name, Def, Options) end,
|
||||
Funs).
|
||||
|
||||
flatten(Code) -> lists:map(fun flatten_s/1, lists:flatten(Code)).
|
||||
|
||||
flatten_s({ifte, Then, Else}) -> {ifte, flatten(Then), flatten(Else)};
|
||||
flatten_s(I) -> I.
|
||||
|
||||
optimize_fun(_Funs, Name, {{Args, Res}, Code}, Options) ->
|
||||
Code0 = flatten(Code),
|
||||
debug(Options, "Optimizing ~s\n", [Name]),
|
||||
debug(Options, " original : ~p\n", [Code0]),
|
||||
Code1 = simplify(Code0),
|
||||
debug(Options, " simplified: ~p\n", [Code1]),
|
||||
Code2 = desugar(Code1),
|
||||
debug(Options, " desugared : ~p\n", [Code2]),
|
||||
{{Args, Res}, Code2}.
|
||||
|
||||
simplify([]) -> [];
|
||||
simplify([I | Code]) ->
|
||||
simpl_top(simpl_s(I), simplify(Code)).
|
||||
|
||||
simpl_s({ifte, Then, Else}) ->
|
||||
{ifte, simplify(Then), simplify(Else)};
|
||||
simpl_s(I) -> I.
|
||||
|
||||
%% add_i 0 --> nop
|
||||
simpl_top({'ADD', _, ?i(0), _}, Code) -> Code;
|
||||
%% push n, add_a --> add_i n
|
||||
simpl_top({'PUSH', ?a, ?i(N)},
|
||||
[{'ADD', ?a, ?a, ?a} | Code]) ->
|
||||
simpl_top( aeb_fate_code:add(?a, ?i(N), ?a), Code);
|
||||
%% push n, add_i m --> add_i (n + m)
|
||||
simpl_top({'PUSH', ?a, ?i(N)}, [{'ADD', ?a, ?i(M), ?a} | Code]) ->
|
||||
simpl_top(aeb_fate_code:push(?i(N + M)), Code);
|
||||
%% add_i n, add_i m --> add_i (n + m)
|
||||
simpl_top({'ADD', ?a, ?i(N), ?a}, [{'ADD', ?a, ?i(M), ?a} | Code]) ->
|
||||
simpl_top({'ADD', ?a, ?i(N + M), ?a}, Code);
|
||||
|
||||
simpl_top(I, Code) -> [I | Code].
|
||||
|
||||
%% Desugar and specialize
|
||||
desugar({'ADD', ?a, ?i(1), ?a}) -> [aeb_fate_code:inc()];
|
||||
desugar({ifte, Then, Else}) -> [{ifte, desugar(Then), desugar(Else)}];
|
||||
desugar(Code) when is_list(Code) ->
|
||||
lists:flatmap(fun desugar/1, Code);
|
||||
desugar(I) -> [I].
|
||||
|
||||
%% -- Phase III --------------------------------------------------------------
|
||||
%% Constructing basic blocks
|
||||
|
||||
to_basic_blocks(Funs, Options) ->
|
||||
maps:from_list([ {Name, {{Args, Res},
|
||||
bb(Name, Code ++ [aeb_fate_code:return()], Options)}}
|
||||
|| {Name, {{Args, Res}, Code}} <- maps:to_list(Funs) ]).
|
||||
|
||||
bb(Name, Code, Options) ->
|
||||
Blocks0 = blocks(Code),
|
||||
Blocks = optimize_blocks(Blocks0),
|
||||
Labels = maps:from_list([ {Ref, I} || {I, {Ref, _}} <- with_ixs(Blocks) ]),
|
||||
BBs = [ set_labels(Labels, B) || B <- Blocks ],
|
||||
debug(Options, "Final code for ~s:\n ~p\n", [Name, BBs]),
|
||||
maps:from_list(BBs).
|
||||
|
||||
%% -- Break up scode into basic blocks --
|
||||
|
||||
blocks(Code) ->
|
||||
Top = make_ref(),
|
||||
blocks([{Top, Code}], []).
|
||||
|
||||
blocks([], Acc) ->
|
||||
lists:reverse(Acc);
|
||||
blocks([{Ref, Code} | Blocks], Acc) ->
|
||||
block(Ref, Code, [], Blocks, Acc).
|
||||
|
||||
block(Ref, [], CodeAcc, Blocks, BlockAcc) ->
|
||||
blocks(Blocks, [{Ref, lists:reverse(CodeAcc)} | BlockAcc]);
|
||||
block(Ref, [{ifte, Then, Else} | Code], Acc, Blocks, BlockAcc) ->
|
||||
ThenLbl = make_ref(),
|
||||
RestLbl = make_ref(),
|
||||
block(Ref, Else ++ [{jump, RestLbl}],
|
||||
[{jumpif, ThenLbl} | Acc],
|
||||
[{ThenLbl, Then ++ [{jump, RestLbl}]},
|
||||
{RestLbl, Code} | Blocks],
|
||||
BlockAcc);
|
||||
block(Ref, [I | Code], Acc, Blocks, BlockAcc) ->
|
||||
block(Ref, Code, [I | Acc], Blocks, BlockAcc).
|
||||
|
||||
%% -- Reorder, inline, and remove dead blocks --
|
||||
|
||||
optimize_blocks(Blocks) ->
|
||||
%% We need to look at the last instruction a lot, so reverse all blocks.
|
||||
Rev = fun(Bs) -> [ {Ref, lists:reverse(Code)} || {Ref, Code} <- Bs ] end,
|
||||
RBlocks = Rev(Blocks),
|
||||
RBlockMap = maps:from_list(RBlocks),
|
||||
RBlocks1 = reorder_blocks(RBlocks, []),
|
||||
RBlocks2 = [ {Ref, inline_block(RBlockMap, Ref, Code)} || {Ref, Code} <- RBlocks1 ],
|
||||
RBlocks3 = remove_dead_blocks(RBlocks2),
|
||||
Rev(RBlocks3).
|
||||
|
||||
%% Choose the next block based on the final jump.
|
||||
reorder_blocks([], Acc) ->
|
||||
lists:reverse(Acc);
|
||||
reorder_blocks([{Ref, Code} | Blocks], Acc) ->
|
||||
reorder_blocks(Ref, Code, Blocks, Acc).
|
||||
|
||||
reorder_blocks(Ref, Code, Blocks, Acc) ->
|
||||
Acc1 = [{Ref, Code} | Acc],
|
||||
case Code of
|
||||
['RETURN'|_] -> reorder_blocks(Blocks, Acc1);
|
||||
[{'RETURNR', _}|_] -> reorder_blocks(Blocks, Acc1);
|
||||
[{jump, L}|_] ->
|
||||
NotL = fun({L1, _}) -> L1 /= L end,
|
||||
case lists:splitwith(NotL, Blocks) of
|
||||
{Blocks1, [{L, Code1} | Blocks2]} ->
|
||||
reorder_blocks(L, Code1, Blocks1 ++ Blocks2, Acc1);
|
||||
{_, []} -> reorder_blocks(Blocks, Acc1)
|
||||
end
|
||||
end.
|
||||
|
||||
%% Inline short blocks (≤ 2 instructions)
|
||||
inline_block(BlockMap, Ref, [{jump, L} | Code] = Code0) when L /= Ref ->
|
||||
case maps:get(L, BlockMap, nocode) of
|
||||
Dest when length(Dest) < 3 ->
|
||||
%% Remove Ref to avoid infinite loops
|
||||
inline_block(maps:remove(Ref, BlockMap), L, Dest) ++ Code;
|
||||
_ -> Code0
|
||||
end;
|
||||
inline_block(_, _, Code) -> Code.
|
||||
|
||||
%% Remove unused blocks
|
||||
remove_dead_blocks(Blocks = [{Top, _} | _]) ->
|
||||
BlockMap = maps:from_list(Blocks),
|
||||
LiveBlocks = chase_labels([Top], BlockMap, #{}),
|
||||
[ B || B = {L, _} <- Blocks, maps:is_key(L, LiveBlocks) ].
|
||||
|
||||
chase_labels([], _, Live) -> Live;
|
||||
chase_labels([L | Ls], Map, Live) ->
|
||||
Code = maps:get(L, Map),
|
||||
Jump = fun({jump, A}) -> [A || not maps:is_key(A, Live)];
|
||||
({jumpif, A}) -> [A || not maps:is_key(A, Live)];
|
||||
(_) -> [] end,
|
||||
New = lists:flatmap(Jump, Code),
|
||||
chase_labels(New ++ Ls, Map, Live#{ L => true }).
|
||||
|
||||
|
||||
%% -- Translate label refs to indices --
|
||||
|
||||
set_labels(Labels, {Ref, Code}) when is_reference(Ref) ->
|
||||
{maps:get(Ref, Labels), [ set_labels(Labels, I) || I <- Code ]};
|
||||
set_labels(Labels, {jump, Ref}) -> aeb_fate_code:jump(maps:get(Ref, Labels));
|
||||
set_labels(Labels, {jumpif, Ref}) -> aeb_fate_code:jumpif(?a, maps:get(Ref, Labels));
|
||||
set_labels(_, I) -> I.
|
||||
|
||||
%% -- Helpers ----------------------------------------------------------------
|
||||
|
||||
with_ixs(Xs) ->
|
||||
lists:zip(lists:seq(0, length(Xs) - 1), Xs).
|
||||
|
||||
keyfind_index(X, J, Xs) ->
|
||||
case [ I || {I, E} <- with_ixs(Xs), X == element(J, E) ] of
|
||||
[I | _] -> I;
|
||||
[] -> false
|
||||
end.
|
||||
|
||||
@@ -0,0 +1,413 @@
|
||||
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @copyright (C) 2018, Aeternity Anstalt
|
||||
%%% @doc Parser combinators for the Sophia parser. Based on
|
||||
%%% Koen Claessen. 2004. Parallel Parsing Processes. J. Functional
|
||||
%%% Programming 14, 6 (November 2004)
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeso_parse_lib).
|
||||
|
||||
-export([parse/2,
|
||||
return/1, fail/0, fail/1, map/2, bind/2,
|
||||
lazy/1, choice/1, choice/2, tok/1, layout/0,
|
||||
left/2, right/2, between/3, optional/1,
|
||||
many/1, many1/1, sep/2, sep1/2,
|
||||
infixl/2, infixr/2]).
|
||||
|
||||
%% -- Types ------------------------------------------------------------------
|
||||
|
||||
-export_type([parser/1, parser_expr/1, pos/0, token/0, tokens/0]).
|
||||
|
||||
-type pos() :: {string() | no_file, integer(), integer()} | {integer(), integer()}.
|
||||
-type token() :: {atom(), pos(), term()} | {atom(), pos()}.
|
||||
-type tokens() :: [token()].
|
||||
-type error() :: {pos(), string() | no_error}.
|
||||
|
||||
-define(lazy(F), {aeso_parse_lazy, F}).
|
||||
-define(fail(Err), {aeso_parse_fail, Err}).
|
||||
-define(choice(Ps), {aeso_parse_choice, Ps}).
|
||||
-define(bind(P, F), {aeso_parse_bind, P, F}).
|
||||
-define(right(P, Q), {aeso_parse_right, P, Q}).
|
||||
-define(left(P, Q), {aeso_parse_left, P, Q}).
|
||||
-define(map(F, P), {aeso_parse_map, F, P}).
|
||||
-define(layout, aeso_parse_layout).
|
||||
-define(tok(Atom), {aeso_parse_tok, Atom}).
|
||||
-define(return(X), {aeso_parse_return, X}).
|
||||
|
||||
%% Type synonyms since you can't have function types as macro arguments for some reason.
|
||||
-type delayed(A) :: fun(() -> A).
|
||||
-type continuation(A, B) :: fun((A) -> parser(B)).
|
||||
-type function(A, B) :: fun((A) -> B).
|
||||
|
||||
%% The representation of parsers that the user writes. These get compiled down to a lower-level
|
||||
%% representation before parsing (parser1/1).
|
||||
-opaque parser_expr(A)
|
||||
:: ?lazy(delayed(parser(A)))
|
||||
| ?fail(term())
|
||||
| ?choice([parser(A)])
|
||||
| ?bind(parser(B), continuation(B, A))
|
||||
| ?map(function(B, A), parser(B))
|
||||
| ?left(parser(A), parser(A))
|
||||
| ?right(parser(A), parser(A)).
|
||||
|
||||
%% Lists, tuples and maps of parsers are valid parsers. These are applied in left-to-right order and
|
||||
%% a list/tuple/map is built out of the results. For maps only the values (and not the keys) can be
|
||||
%% parsers.
|
||||
-type parser(A) :: parser_expr(A)
|
||||
| maybe_improper_list(parser(_), parser(_))
|
||||
| tuple() %% A = tuple()
|
||||
| term(). %% Interpreted as a parser that returns the term without consuming input
|
||||
|
||||
%% The low level parser representation. This is what's used when doing the
|
||||
%% actual parsing (see parse1/2).
|
||||
-type parser1(A) :: {tok_bind, #{atom() => fun((token()) -> parser1(A))}}
|
||||
%% ^ Consume a token and dispatch on its tag.
|
||||
| {fail, term()}
|
||||
%% ^ Fail with the given error
|
||||
| {return_plus, A, parser1(A)}
|
||||
%% ^ Choice between returning a value and continue parsing
|
||||
| {layout, fun((integer()) -> parser1(A)), parser1(A)}.
|
||||
%% ^ Parse a layout block. If a layout block can be started, it commits to the
|
||||
%% first argument. I.e. no backtracking to the second argument if the first
|
||||
%% fails.
|
||||
|
||||
%% 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).
|
||||
apply_p(?lazy(F), K) -> apply_p(F(), K);
|
||||
apply_p(?fail(Err), _) -> {fail, Err};
|
||||
apply_p(?choice([P | Ps]), K) -> lists:foldl(fun(Q, R) -> choice1(apply_p(Q, K), R) end,
|
||||
apply_p(P, K), Ps);
|
||||
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(?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(?layout, K) -> {layout, K, {fail, {expected, layout_block}}};
|
||||
apply_p(?tok(Atom), K) -> {tok_bind, #{Atom => K}};
|
||||
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(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) ->
|
||||
{Keys, Ps} = lists:unzip(maps:to_list(M)),
|
||||
apply_p(Ps, fun(Vals) -> K(maps:from_list(lists:zip(Keys, Vals))) end);
|
||||
apply_p(X, K) -> K(X).
|
||||
|
||||
%% -- Primitive combinators --------------------------------------------------
|
||||
|
||||
%% @doc Create a delayed parser. Required when building recursive parsers to avoid looping.
|
||||
-spec lazy(fun(() -> parser(A))) -> parser(A).
|
||||
lazy(Delayed) -> ?lazy(Delayed).
|
||||
|
||||
%% @doc A parser that always fails.
|
||||
-spec fail(term()) -> parser(none()).
|
||||
fail(Err) -> ?fail(Err).
|
||||
|
||||
%% @doc Fail with no error message.
|
||||
-spec fail() -> parser(none()).
|
||||
fail() -> fail(no_error).
|
||||
|
||||
%% @doc A choice between two parsers. Succeeds if either parser succeeds.
|
||||
-spec choice(parser(A), parser(A)) -> parser(A).
|
||||
choice(?choice(Ps), ?choice(Qs)) -> ?choice(Ps ++ Qs);
|
||||
choice(?choice(Ps), Q) -> ?choice([Q | Ps]);
|
||||
choice(P, ?choice(Qs)) -> ?choice([P | Qs]);
|
||||
choice(P, Q) -> ?choice([P, Q]).
|
||||
|
||||
%% @doc A choice between a list of parsers. Applies 'choice/2' repeatedly.
|
||||
-spec choice([parser(A)]) -> parser(A).
|
||||
choice([]) -> fail(empty_choice);
|
||||
choice([P]) -> P;
|
||||
choice([P | Ps]) -> choice(P, choice(Ps)).
|
||||
|
||||
%% @doc Parse a single token with the given tag.
|
||||
-spec tok(atom()) -> parser(token()).
|
||||
tok(Atom) -> ?tok(Atom).
|
||||
|
||||
%% @doc Apply two parsers in sequence and return the result from the first one.
|
||||
-spec left(parser(A), parser(_)) -> parser(A).
|
||||
left(P, Q) -> ?left(P, Q).
|
||||
|
||||
%% @doc Apply two parsers in sequence and return the result from the second one.
|
||||
-spec right(parser(_), parser(A)) -> parser(A).
|
||||
right(P, Q) -> ?right(P, Q).
|
||||
|
||||
%% @doc A parser that always succeeds with the given value.
|
||||
-spec return(A) -> parser(A).
|
||||
return(X) -> ?return(X).
|
||||
|
||||
%% @doc Monadic bind. Lets you inspect the result of the first parser before deciding on what to
|
||||
%% parse next.
|
||||
-spec bind(parser(A), fun((A) -> parser(B))) -> parser(B).
|
||||
bind(?return(X), F) -> F(X);
|
||||
bind(P, F) -> ?bind(P, F).
|
||||
|
||||
%% @doc Apply a function to the result of a parser.
|
||||
-spec map(fun((A) -> B), parser(A)) -> parser(B).
|
||||
map(Fun, P) -> ?map(Fun, P).
|
||||
|
||||
%% @doc Parse the start of a layout block. A layout block can start if the next token is not on the
|
||||
%% same line as the previous token and it is indented further than the current layout block (if
|
||||
%% any). The result is the column of the new layout block (i.e. the column of the next token).
|
||||
-spec layout() -> parser(integer()).
|
||||
layout() -> ?layout.
|
||||
|
||||
%% @doc Parse a sequence of tokens using a parser. Fails if the parse is ambiguous.
|
||||
-spec parse(parser(A), tokens()) -> {ok, A} | {error, term()}.
|
||||
parse(P, S) ->
|
||||
case parse1(apply_p(P, fun(X) -> {return_plus, X, {fail, no_error}} end), S) of
|
||||
{[], {Pos, Err}} -> {error, {Pos, parse_error, flatten_error(Err)}};
|
||||
{[A], _} -> {ok, A};
|
||||
{As, _} -> {error, {{1, 1}, ambiguous_parse, As}}
|
||||
end.
|
||||
|
||||
-spec flatten_error(iolist() | no_error) -> string().
|
||||
flatten_error(no_error) -> "Unspecified error";
|
||||
flatten_error(Err) -> lists:flatten(Err).
|
||||
|
||||
%% -- Derived combinators ----------------------------------------------------
|
||||
|
||||
%% @doc Parse zero or more A's.
|
||||
-spec many(parser(A)) -> parser([A]).
|
||||
many(P) -> choice([], many1(P)).
|
||||
|
||||
-dialyzer({nowarn_function, many1/1}). %% Silence improper_list warning.
|
||||
%% @doc Parse one or more A's.
|
||||
-spec many1(parser(A)) -> parser([A]).
|
||||
many1(P) -> [P | lazy(fun() -> many(P) end)].
|
||||
|
||||
%% @doc Parse zero or more A's, separated by Sep.
|
||||
-spec sep(parser(A), parser(_)) -> parser([A]).
|
||||
sep(P, Sep) -> choice([], sep1(P, Sep)).
|
||||
|
||||
-dialyzer({nowarn_function, sep1/2}). %% Silence improper_list warning.
|
||||
%% @doc Parse one or more A's, separated by Sep.
|
||||
-spec sep1(parser(A), parser(_)) -> parser([A]).
|
||||
sep1(P, Sep) -> [P | many(right(Sep, P))].
|
||||
|
||||
%% @doc Parse a left-associative operator. <p>
|
||||
%% <tt>infixl(Elem, Op) ::= Elem | infixl(Elem, Op) Op Elem</tt>
|
||||
%% </p>
|
||||
-spec infixl(parser(A), parser(fun((A, A) -> A))) -> parser(A).
|
||||
infixl(Elem, Op) ->
|
||||
bind(Elem, fun(A) ->
|
||||
bind(many({Op, Elem}), fun(Ops) ->
|
||||
return(build_infixl(A, Ops)) end) end).
|
||||
|
||||
%% @doc Parse a right-associative operator. <p>
|
||||
%% <tt>infixr(Elem, Op) ::= Elem | Elem Op infixl(Elem, Op)</tt>
|
||||
%% </p>
|
||||
-spec infixr(parser(A), parser(fun((A, A) -> A))) -> parser(A).
|
||||
infixr(Elem, Op) ->
|
||||
bind(Elem, fun(A) ->
|
||||
bind(many({Op, Elem}), fun(Ops) ->
|
||||
return(build_infixr(A, Ops)) end) end).
|
||||
|
||||
build_infixl(A, []) -> A;
|
||||
build_infixl(A, [{Op, B} | Ops]) -> build_infixl(Op(A, B), Ops).
|
||||
|
||||
build_infixr(A, []) -> A;
|
||||
build_infixr(A, [{Op, B} | Ops]) -> Op(A, build_infixr(B, Ops)).
|
||||
|
||||
%% @doc Parse an A between two other things (typically brackets of some kind).
|
||||
-spec between(parser(_), parser(A), parser(_)) -> parser(A).
|
||||
between(L, P, R) ->
|
||||
right(L, left(P, R)).
|
||||
|
||||
-spec optional(parser(A)) -> parser(none | {ok, A}).
|
||||
optional(P) -> choice(none, {ok, P}).
|
||||
|
||||
%% -- Internal functions -----------------------------------------------------
|
||||
|
||||
-spec tag(token()) -> atom().
|
||||
tag(T) when is_tuple(T) -> element(1, T).
|
||||
|
||||
-spec pos(token()) -> pos().
|
||||
pos(T) when is_tuple(T) -> element(2, T).
|
||||
|
||||
-spec line(token()) -> integer().
|
||||
line(T) when is_tuple(T) -> element(1, pos(T)).
|
||||
|
||||
-spec col(token()) -> integer().
|
||||
col(T) when is_tuple(T) -> element(2, pos(T)).
|
||||
|
||||
%% Choice on low-level parsers.
|
||||
-spec choice1(parser1(A), parser1(A)) -> parser1(A).
|
||||
|
||||
%% If both parsers want the next token we grab it and merge the continuations.
|
||||
choice1({tok_bind, Map1}, {tok_bind, 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.
|
||||
choice1({fail, E1}, {fail, E2}) -> {fail, add_error(E1, E2)};
|
||||
choice1({fail, _}, Q) -> Q;
|
||||
choice1(P, {fail, _}) -> P;
|
||||
|
||||
%% If either side can deliver a value, then so can the choice.
|
||||
choice1({return_plus, X, P}, Q) -> {return_plus, X, choice1(P, Q)};
|
||||
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
|
||||
%% will commit to a layout block is there is one.
|
||||
choice1({layout, F, P}, {layout, G, Q}) ->
|
||||
{layout, fun(N) -> choice1(F(N), G(N)) end, choice1(P, Q)};
|
||||
choice1({layout, F, P}, Q) -> {layout, F, choice1(P, Q)};
|
||||
choice1(P, {layout, G, Q}) -> {layout, G, choice1(P, Q)}.
|
||||
|
||||
%% Token stream representation. This is the state of the parse function.
|
||||
-record(ts, {layout :: [integer()], %% Column numbers of the current layout blocks.
|
||||
last :: token(), %% The previously consumed token.
|
||||
inserted :: tokens(), %% Inserted layout tokens, consumed before 'tokens'.
|
||||
tokens :: tokens()}). %% The remaining tokens to be parsed.
|
||||
|
||||
%% The initial token stream.
|
||||
ts(S) ->
|
||||
#ts{ layout = [], last = {bof, {0, 0}}, inserted = [], tokens = S }.
|
||||
|
||||
%% The parse function. Parses a token stream returning a list of results and an error message in
|
||||
%% case of failure.
|
||||
-spec parse1(parser1(A), tokens()) -> {[A], term()}.
|
||||
parse1(P, S) ->
|
||||
parse1(P, ts(S), [], no_error).
|
||||
|
||||
%% The main work horse. Returns a list of possible parses and an error message in case parsing
|
||||
%% fails.
|
||||
-spec parse1(parser1(A), #ts{}, [A], term()) -> {[A], error()}.
|
||||
parse1({tok_bind, Map}, Ts, Acc, Err) ->
|
||||
case next_token(Ts) of
|
||||
{T, Ts1} ->
|
||||
case maps:get(tag(T), Map, '$not_found') of
|
||||
'$not_found' ->
|
||||
%% Insert a vclose (if required) on unexpected tokens. This lets you have layout
|
||||
%% blocks inside parens without having to put the closing paren on a separate
|
||||
%% line. Example:
|
||||
%% ((x) =>
|
||||
%% let y = x + 1
|
||||
%% y + y)(4)
|
||||
case maps:get(vclose, Map, '$not_found') of
|
||||
'$not_found' ->
|
||||
{Acc, unexpected_token_error(Ts, T)};
|
||||
F ->
|
||||
VClose = {vclose, pos(T)},
|
||||
Ts2 = pop_layout(VClose, Ts#ts{ last = VClose }),
|
||||
parse1(F(VClose), Ts2, Acc, Err)
|
||||
end;
|
||||
F -> parse1(F(T), Ts1, Acc, Err)
|
||||
end;
|
||||
false ->
|
||||
{Acc, mk_error(Ts, io_lib:format("Unexpected end of file. Expected one of ~p.",
|
||||
[maps:keys(Map)]))}
|
||||
end;
|
||||
parse1({layout, F, P}, Ts, Acc, Err) ->
|
||||
case start_layout(Ts) of
|
||||
{Col, Ts1} -> parse1(F(Col), Ts1, Acc, Err);
|
||||
false -> parse1(P, Ts, Acc, mk_error(Ts, "Expected layout block."))
|
||||
end;
|
||||
parse1({return_plus, X, P}, Ts, Acc, Err) ->
|
||||
case next_token(Ts) of
|
||||
false -> parse1(P, Ts, [X | Acc], Err);
|
||||
{T, _} -> parse1(P, Ts, Acc, unexpected_token_error(Ts, T))
|
||||
end;
|
||||
parse1({fail, Err}, Ts, Acc, Err1) ->
|
||||
Err2 = case next_token(Ts) of
|
||||
{T, _} -> unexpected_token_error(Ts, T);
|
||||
_ -> no_error
|
||||
end,
|
||||
{Acc, add_error(add_error(mk_error(Ts, Err), Err2), Err1)}.
|
||||
|
||||
%% Get the current position of the token stream. This is the position of the next token if any, and
|
||||
%% the line after the last token if at the end of the stream.
|
||||
-spec current_pos(#ts{}) -> pos().
|
||||
current_pos(#ts{ inserted = [T | _] }) -> pos(T);
|
||||
current_pos(#ts{ tokens = [T | _] }) -> pos(T);
|
||||
current_pos(#ts{ last = T }) -> end_pos(pos(T)).
|
||||
|
||||
-spec mk_error(#ts{}, term()) -> error().
|
||||
mk_error(Ts, Err) ->
|
||||
{current_pos(Ts), Err}.
|
||||
|
||||
-spec unexpected_token_error(#ts{}, token()) -> error().
|
||||
unexpected_token_error(Ts, T) ->
|
||||
mk_error(Ts, io_lib:format("Unexpected token ~p", [tag(T)])).
|
||||
|
||||
%% Get the next token from a token stream. Inserts layout tokens if necessary.
|
||||
-spec next_token(#ts{}) -> false | {token(), #ts{}}.
|
||||
next_token(Ts) ->
|
||||
case insert_layout_tokens(Ts) of
|
||||
Ts1 = #ts{ inserted = [L | Ls] } -> {L, pop_layout(L, Ts1#ts{ last = L, inserted = Ls })};
|
||||
Ts1 = #ts{ tokens = [T | S] } -> {T, Ts1#ts{ last = T, tokens = S }};
|
||||
#ts{ inserted = [], tokens = [] } -> false
|
||||
end.
|
||||
|
||||
%% Pop a layout block on an inserted 'vclose' token.
|
||||
-spec pop_layout(token(), #ts{}) -> #ts{}.
|
||||
pop_layout({vclose, _}, Ts = #ts{ layout = [_ | Layout] }) -> Ts#ts{ layout = Layout };
|
||||
pop_layout(_, Ts) -> Ts.
|
||||
|
||||
%% Attempt to start a new layout block. Requires the next token to be on a new line and indented
|
||||
%% more than any existing layout block. Sets the previous token to 'vopen'.
|
||||
-spec start_layout(#ts{}) -> false | {integer(), #ts{}}.
|
||||
start_layout(#ts{ inserted = [_ | _] }) -> false; %% Can't start a layout block before consuming all layout tokens
|
||||
start_layout(#ts{ tokens = [] }) -> false; %% No more tokens
|
||||
start_layout(Ts = #ts{ layout = Layout, last = Last, tokens = [T | _] }) ->
|
||||
Col = col(T),
|
||||
Valid = case Layout of
|
||||
[] -> line(Last) < line(T);
|
||||
[C1 | _] -> line(Last) < line(T) andalso C1 < Col
|
||||
end,
|
||||
Valid andalso {Col, Ts#ts{ layout = [Col | Layout], last = {vopen, pos(T)} }}.
|
||||
|
||||
%% Insert layout tokens. If the next token is on the same line as the current layout block we insert
|
||||
%% a 'vsemi' token. If the next token is indented less, we insert a 'vclose' token.
|
||||
-spec insert_layout_tokens(#ts{}) -> #ts{}.
|
||||
insert_layout_tokens(Ts = #ts{ inserted = [_ | _] }) ->
|
||||
Ts; %% already inserted layout tokens
|
||||
insert_layout_tokens(Ts = #ts{ layout = Layout, last = Last, tokens = S }) ->
|
||||
ToInsert = insert_layout_tokens(Layout, Last, S, []),
|
||||
Ts#ts{ inserted = ToInsert }.
|
||||
|
||||
%% Compute the layout tokens to be inserted.
|
||||
-spec insert_layout_tokens([integer()], token(), tokens(), tokens()) -> tokens().
|
||||
insert_layout_tokens([_ | Layout], Last, [], Acc) ->
|
||||
%% End of the file. Insert vclose tokens for all layout blocks.
|
||||
Vclose = {vclose, end_pos(pos(Last))},
|
||||
insert_layout_tokens(Layout, Last, [], [Vclose | Acc]);
|
||||
insert_layout_tokens([N | Layout1], Last, S = [T | _], Acc) ->
|
||||
Col = col(T),
|
||||
%% Don't insert a vsemi if the previous token was a vopen or a vsemi. The former to avoid a
|
||||
%% vsemi for the first token of the block and the latter to avoid inserting infinite vsemis.
|
||||
AlreadySemi = lists:member(tag(Last), [vsemi, vopen]) andalso col(Last) == N,
|
||||
if Col == N, not AlreadySemi ->
|
||||
lists:reverse([{vsemi, pos(T)} | Acc]);
|
||||
Col < N ->
|
||||
Vclose = {vclose, pos(T)},
|
||||
insert_layout_tokens(Layout1, Vclose, S, [Vclose | Acc]);
|
||||
true ->
|
||||
lists:reverse(Acc)
|
||||
end;
|
||||
insert_layout_tokens([], _Last, _S, Acc) ->
|
||||
lists:reverse(Acc).
|
||||
|
||||
%% The end-of-file position. Beginning of the line after the last token.
|
||||
end_pos({L, _}) -> {L + 1, 1}.
|
||||
|
||||
%% Combine two error messages. Discard no_error's otherwise pick the first error.
|
||||
add_error(no_error, Err) -> Err;
|
||||
add_error({_, no_error}, Err) -> Err;
|
||||
add_error(Err, no_error) -> Err;
|
||||
add_error(Err, {_, no_error}) -> Err;
|
||||
add_error(Err, _Err1) -> Err.
|
||||
|
||||
%% For some unfathomable reason the maps module does not have a merge_with function.
|
||||
-spec merge_with(fun((term(), term()) -> term()), map(), map()) -> map().
|
||||
merge_with(Fun, Map1, Map2) ->
|
||||
case maps:size(Map1) > maps:size(Map2) of
|
||||
true ->
|
||||
lists:foldl(fun({K, R}, M) ->
|
||||
maps:update_with(K, fun(L) -> Fun(L, R) end, R, M)
|
||||
end, Map1, maps:to_list(Map2));
|
||||
false ->
|
||||
lists:foldl(fun({K, L}, M) ->
|
||||
maps:update_with(K, fun(R) -> Fun(L, R) end, L, M)
|
||||
end, Map2, maps:to_list(Map1))
|
||||
end.
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
-define(LET_P(X, P, Q), aeso_parse_lib:bind(P, fun(X) -> Q end)).
|
||||
-define(LAZY_P(P), aeso_parse_lib:lazy(fun() -> P end)).
|
||||
-define(MEMO_P(P), aeso_parse_lib:lazy(aeso_parse_lib:memoised(fun() -> P end))).
|
||||
|
||||
-define(GUARD_P(G, P),
|
||||
case G of
|
||||
true -> P;
|
||||
false -> fail()
|
||||
end).
|
||||
|
||||
-define(RULE(A, Do), map(fun(_1) -> Do end, A )).
|
||||
-define(RULE(A, B, Do), map(fun({_1, _2}) -> Do end, {A, B} )).
|
||||
-define(RULE(A, B, C, Do), map(fun({_1, _2, _3}) -> Do end, {A, B, C} )).
|
||||
-define(RULE(A, B, C, D, Do), map(fun({_1, _2, _3, _4}) -> Do end, {A, B, C, D} )).
|
||||
-define(RULE(A, B, C, D, E, Do), map(fun({_1, _2, _3, _4, _5}) -> Do end, {A, B, C, D, E} )).
|
||||
-define(RULE(A, B, C, D, E, F, Do), map(fun({_1, _2, _3, _4, _5, _6}) -> Do end, {A, B, C, D, E, F})).
|
||||
|
||||
-import(aeso_parse_lib,
|
||||
[tok/1, tok/2, between/3, many/1, many1/1, sep/2, sep1/2,
|
||||
infixl/1, infixr/1, choice/1, choice/2, return/1, layout/0,
|
||||
fail/0, fail/1, map/2, infixl/2, infixr/2, infixl1/2, infixr1/2,
|
||||
left/2, right/2, optional/1]).
|
||||
|
||||
|
||||
@@ -0,0 +1,542 @@
|
||||
%%% File : aeso_parser.erl
|
||||
%%% Author : Ulf Norell
|
||||
%%% Description :
|
||||
%%% Created : 1 Mar 2018 by Ulf Norell
|
||||
-module(aeso_parser).
|
||||
|
||||
-export([string/1,
|
||||
string/2,
|
||||
type/1]).
|
||||
|
||||
-include("aeso_parse_lib.hrl").
|
||||
|
||||
-type parse_result() :: {ok, aeso_syntax:ast()}
|
||||
| {error, {aeso_parse_lib:pos(), atom(), term()}}
|
||||
| {error, {aeso_parse_lib:pos(), atom()}}.
|
||||
|
||||
-spec string(string()) -> parse_result().
|
||||
string(String) ->
|
||||
string(String, []).
|
||||
|
||||
-spec string(string(), aeso_compiler:options()) -> parse_result().
|
||||
string(String, Opts) ->
|
||||
case parse_and_scan(file(), String, Opts) of
|
||||
{ok, AST} ->
|
||||
expand_includes(AST, Opts);
|
||||
Err = {error, _} ->
|
||||
Err
|
||||
end.
|
||||
|
||||
type(String) ->
|
||||
parse_and_scan(type(), String, []).
|
||||
|
||||
parse_and_scan(P, S, Opts) ->
|
||||
set_current_file(proplists:get_value(src_file, Opts, no_file)),
|
||||
case aeso_scan:scan(S) of
|
||||
{ok, Tokens} -> aeso_parse_lib:parse(P, Tokens);
|
||||
Error -> Error
|
||||
end.
|
||||
|
||||
%% -- Parsing rules ----------------------------------------------------------
|
||||
|
||||
file() -> choice([], block(decl())).
|
||||
|
||||
decl() ->
|
||||
?LAZY_P(
|
||||
choice(
|
||||
%% Contract declaration
|
||||
[ ?RULE(keyword(contract), con(), tok('='), maybe_block(decl()), {contract, _1, _2, _4})
|
||||
, ?RULE(keyword(namespace), con(), tok('='), maybe_block(decl()), {namespace, _1, _2, _4})
|
||||
, ?RULE(keyword(include), str(), {include, _2})
|
||||
|
||||
%% Type declarations TODO: format annotation for "type bla" vs "type bla()"
|
||||
, ?RULE(keyword(type), id(), {type_decl, _1, _2, []})
|
||||
, ?RULE(keyword(type), id(), type_vars(), {type_decl, _1, _2, _3})
|
||||
, ?RULE(keyword(type), id(), tok('='), typedef(type), {type_def, _1, _2, [], _4})
|
||||
, ?RULE(keyword(type), id(), type_vars(), tok('='), typedef(type), {type_def, _1, _2, _3, _5})
|
||||
, ?RULE(keyword(record), id(), tok('='), typedef(record), {type_def, _1, _2, [], _4})
|
||||
, ?RULE(keyword(record), id(), type_vars(), tok('='), typedef(record), {type_def, _1, _2, _3, _5})
|
||||
, ?RULE(keyword(datatype), id(), tok('='), typedef(variant), {type_def, _1, _2, [], _4})
|
||||
, ?RULE(keyword(datatype), id(), type_vars(), tok('='), typedef(variant), {type_def, _1, _2, _3, _5})
|
||||
|
||||
%% Function declarations
|
||||
, ?RULE(modifiers(), keyword(function), id(), tok(':'), type(), add_modifiers(_1, {fun_decl, _2, _3, _5}))
|
||||
, ?RULE(modifiers(), keyword(function), fundef(), add_modifiers(_1, set_pos(get_pos(_2), _3)))
|
||||
, ?RULE(keyword('let'), valdef(), set_pos(get_pos(_1), _2))
|
||||
])).
|
||||
|
||||
modifiers() ->
|
||||
many(choice([token(stateful), token(public), token(private), token(internal)])).
|
||||
|
||||
add_modifiers([], Node) -> Node;
|
||||
add_modifiers(Mods = [Tok | _], Node) ->
|
||||
%% Set the position to the position of the first modifier. This is
|
||||
%% important for code transformation tools (like what we do in
|
||||
%% create_calldata) to be able to get the indentation of the declaration.
|
||||
set_pos(get_pos(Tok),
|
||||
lists:foldl(fun({Mod, _}, X) -> set_ann(Mod, true, X) end,
|
||||
Node, Mods)).
|
||||
|
||||
%% -- Type declarations ------------------------------------------------------
|
||||
|
||||
typedef(type) -> ?RULE(type(), {alias_t, _1});
|
||||
typedef(record) -> ?RULE(brace_list(field_type()), {record_t, _1});
|
||||
typedef(variant) -> ?RULE(constructors(), {variant_t, _1}).
|
||||
|
||||
constructors() ->
|
||||
sep1(constructor(), tok('|')).
|
||||
|
||||
constructor() -> %% TODO: format for Con() vs Con
|
||||
choice(?RULE(con(), {constr_t, get_ann(_1), _1, []}),
|
||||
?RULE(con(), con_args(), {constr_t, get_ann(_1), _1, _2})).
|
||||
|
||||
con_args() -> paren_list(con_arg()).
|
||||
type_args() -> paren_list(type()).
|
||||
field_type() -> ?RULE(id(), tok(':'), type(), {field_t, get_ann(_1), _1, _3}).
|
||||
|
||||
con_arg() -> choice(type(), ?RULE(keyword(indexed), type(), set_ann(indexed, true, _2))).
|
||||
|
||||
%% -- Let declarations -------------------------------------------------------
|
||||
|
||||
letdecl() ->
|
||||
choice(
|
||||
?RULE(keyword('let'), letdef(), set_pos(get_pos(_1), _2)),
|
||||
?RULE(keyword('let'), tok(rec), sep1(letdef(), tok('and')), {letrec, _1, _3})).
|
||||
|
||||
letdef() -> choice(valdef(), fundef()).
|
||||
|
||||
valdef() ->
|
||||
choice(
|
||||
?RULE(id(), tok('='), body(), {letval, [], _1, type_wildcard(), _3}),
|
||||
?RULE(id(), tok(':'), type(), tok('='), body(), {letval, [], _1, _3, _5})).
|
||||
|
||||
fundef() ->
|
||||
choice(
|
||||
[ ?RULE(id(), args(), tok('='), body(), {letfun, [], _1, _2, type_wildcard(), _4})
|
||||
, ?RULE(id(), args(), tok(':'), type(), tok('='), body(), {letfun, [], _1, _2, _4, _6})
|
||||
]).
|
||||
|
||||
args() -> paren_list(arg()).
|
||||
|
||||
arg() -> choice(
|
||||
?RULE(id(), {arg, get_ann(_1), _1, type_wildcard()}),
|
||||
?RULE(id(), tok(':'), type(), {arg, get_ann(_1), _1, _3})).
|
||||
|
||||
%% -- Types ------------------------------------------------------------------
|
||||
|
||||
type_vars() -> paren_list(tvar()).
|
||||
|
||||
type() -> ?LAZY_P(type100()).
|
||||
|
||||
type100() -> type200().
|
||||
|
||||
type200() ->
|
||||
?RULE(many({fun_domain(), keyword('=>')}), type300(), fun_t(_1, _2)).
|
||||
|
||||
type300() -> type400().
|
||||
|
||||
type400() ->
|
||||
choice(
|
||||
[?RULE(typeAtom(), optional(type_args()),
|
||||
case _2 of
|
||||
none -> _1;
|
||||
{ok, Args} -> {app_t, get_ann(_1), _1, Args}
|
||||
end),
|
||||
?RULE(id("bytes"), parens(token(int)),
|
||||
{bytes_t, get_ann(_1), element(3, _2)})
|
||||
]).
|
||||
|
||||
typeAtom() ->
|
||||
?LAZY_P(choice(
|
||||
[ id(), token(con), token(qcon), token(qid), tvar()
|
||||
, ?RULE(keyword('('), comma_sep(type()), tok(')'), tuple_t(_1, _2))
|
||||
])).
|
||||
|
||||
fun_domain() -> ?RULE(?LAZY_P(type300()), fun_domain(_1)).
|
||||
|
||||
%% -- Statements -------------------------------------------------------------
|
||||
|
||||
body() ->
|
||||
?LET_P(Stmts, maybe_block(stmt()), block_e(Stmts)).
|
||||
|
||||
stmt() ->
|
||||
?LAZY_P(choice(
|
||||
[ expr()
|
||||
, letdecl()
|
||||
, {switch, keyword(switch), parens(expr()), maybe_block(branch())}
|
||||
, {'if', keyword('if'), parens(expr()), body()}
|
||||
, {elif, keyword(elif), parens(expr()), body()}
|
||||
, {else, keyword(else), body()}
|
||||
])).
|
||||
|
||||
branch() ->
|
||||
?RULE(pattern(), keyword('=>'), body(), {'case', _2, _1, _3}).
|
||||
|
||||
pattern() ->
|
||||
?LET_P(E, expr500(), parse_pattern(E)).
|
||||
|
||||
%% -- Expressions ------------------------------------------------------------
|
||||
|
||||
expr() -> expr100().
|
||||
|
||||
expr100() ->
|
||||
Expr100 = ?LAZY_P(expr100()),
|
||||
Expr200 = ?LAZY_P(expr200()),
|
||||
choice(
|
||||
[ ?RULE(args(), keyword('=>'), body(), {lam, _2, _1, _3}) %% TODO: better location
|
||||
, {'if', keyword('if'), parens(Expr100), Expr200, right(tok(else), Expr100)}
|
||||
, ?RULE(Expr200, optional(right(tok(':'), type())),
|
||||
case _2 of
|
||||
none -> _1;
|
||||
{ok, Type} -> {typed, get_ann(_1), _1, Type}
|
||||
end)
|
||||
]).
|
||||
|
||||
expr200() -> infixr(expr300(), binop('||')).
|
||||
expr300() -> infixr(expr400(), binop('&&')).
|
||||
expr400() -> infix(expr500(), binop(['<', '>', '=<', '>=', '==', '!='])).
|
||||
expr500() -> infixr(expr600(), binop(['::', '++'])).
|
||||
expr600() -> infixl(expr650(), binop(['+', '-'])).
|
||||
expr650() -> ?RULE(many(token('-')), expr700(), prefixes(_1, _2)).
|
||||
expr700() -> infixl(expr750(), binop(['*', '/', mod])).
|
||||
expr750() -> infixl(expr800(), binop(['^'])).
|
||||
expr800() -> ?RULE(many(token('!')), expr900(), prefixes(_1, _2)).
|
||||
expr900() -> ?RULE(exprAtom(), many(elim()), elim(_1, _2)).
|
||||
|
||||
exprAtom() ->
|
||||
?LAZY_P(begin
|
||||
Expr = ?LAZY_P(expr()),
|
||||
choice(
|
||||
[ id_or_addr(), con(), token(qid), token(qcon)
|
||||
, token(bytes), token(string), token(char)
|
||||
, token(int)
|
||||
, ?RULE(token(hex), set_ann(format, hex, setelement(1, _1, int)))
|
||||
, {bool, keyword(true), true}
|
||||
, {bool, keyword(false), false}
|
||||
, ?RULE(brace_list(?LAZY_P(field_assignment())), record(_1))
|
||||
, {list, [], bracket_list(Expr)}
|
||||
, ?RULE(tok('['), Expr, binop('..'), Expr, tok(']'), _3(_2, _4))
|
||||
, ?RULE(keyword('('), comma_sep(Expr), tok(')'), tuple_e(_1, _2))
|
||||
])
|
||||
end).
|
||||
|
||||
arg_expr() ->
|
||||
?LAZY_P(
|
||||
choice([ ?RULE(id(), tok('='), expr(), {named_arg, [], _1, _3})
|
||||
, expr() ])).
|
||||
|
||||
elim() ->
|
||||
?LAZY_P(
|
||||
choice(
|
||||
[ {proj, keyword('.'), id()}
|
||||
, ?RULE(paren_list(arg_expr()), {app, [], _1})
|
||||
, ?RULE(keyword('{'), comma_sep(field_assignment()), tok('}'), {rec_upd, _1, _2})
|
||||
, ?RULE(keyword('['), map_key(), keyword(']'), map_get(_1, _2))
|
||||
])).
|
||||
|
||||
map_get(Ann, {map_key, Key}) -> {map_get, Ann, Key};
|
||||
map_get(Ann, {map_key, Key, Val}) -> {map_get, Ann, Key, Val}.
|
||||
|
||||
map_key() ->
|
||||
?RULE(expr(), optional({tok('='), expr()}), map_key(_1, _2)).
|
||||
|
||||
map_key(Key, none) -> {map_key, Key};
|
||||
map_key(Key, {ok, {_, Val}}) -> {map_key, Key, Val}.
|
||||
|
||||
elim(E, []) -> E;
|
||||
elim(E, [{proj, Ann, P} | Es]) -> elim({proj, Ann, E, P}, Es);
|
||||
elim(E, [{app, Ann, Args} | Es]) -> elim({app, Ann, E, Args}, Es);
|
||||
elim(E, [{rec_upd, Ann, Flds} | Es]) -> elim(record_update(Ann, E, Flds), Es);
|
||||
elim(E, [{map_get, Ann, Key} | Es]) -> elim({map_get, Ann, E, Key}, Es);
|
||||
elim(E, [{map_get, Ann, Key, Val} | Es]) -> elim({map_get, Ann, E, Key, Val}, Es).
|
||||
|
||||
record_update(Ann, E, Flds) ->
|
||||
{record_or_map(Flds), Ann, E, Flds}.
|
||||
|
||||
record([]) -> {map, [], []};
|
||||
record(Fs) ->
|
||||
case record_or_map(Fs) of
|
||||
record -> {record, get_ann(hd(Fs)), Fs};
|
||||
map ->
|
||||
Ann = get_ann(hd(Fs ++ [{empty, []}])), %% TODO: source location for empty maps
|
||||
KV = fun({field, _, [{map_get, _, Key}], Val}) -> {Key, Val};
|
||||
({field, _, LV, Id, _}) ->
|
||||
bad_expr_err("Cannot use '@' in map construction", infix(LV, {op, Ann, '@'}, Id));
|
||||
({field, _, LV, _}) ->
|
||||
bad_expr_err("Cannot use nested fields or keys in map construction", LV) end,
|
||||
{map, Ann, lists:map(KV, Fs)}
|
||||
end.
|
||||
|
||||
record_or_map(Fields) ->
|
||||
Kind = fun(Fld) -> case element(3, Fld) of
|
||||
[{proj, _, _} | _] -> proj;
|
||||
[{map_get, _, _} | _] -> map_get;
|
||||
[{map_get, _, _, _} | _] -> map_get
|
||||
end end,
|
||||
case lists:usort(lists:map(Kind, Fields)) of
|
||||
[proj] -> record;
|
||||
[map_get] -> map;
|
||||
_ ->
|
||||
[{field, Ann, _, _} | _] = Fields,
|
||||
bad_expr_err("Mixed record fields and map keys in", {record, Ann, Fields})
|
||||
end.
|
||||
|
||||
field_assignment() ->
|
||||
?RULE(lvalue(), optional({tok('@'), id()}), tok('='), expr(), field_assignment(get_ann(_3), _1, _2, _4)).
|
||||
|
||||
field_assignment(Ann, LV, none, E) ->
|
||||
{field, Ann, LV, E};
|
||||
field_assignment(Ann, LV, {ok, {_, Id}}, E) ->
|
||||
{field, Ann, LV, Id, E}.
|
||||
|
||||
lvalue() ->
|
||||
?RULE(lvalueAtom(), many(elim()), lvalue(elim(_1, _2))).
|
||||
|
||||
lvalueAtom() ->
|
||||
?LAZY_P(choice([ id()
|
||||
, ?RULE(keyword('['), map_key(), keyword(']'), _2)
|
||||
])).
|
||||
|
||||
lvalue(E) -> lvalue(E, []).
|
||||
|
||||
lvalue(X = {id, Ann, _}, LV) -> [{proj, Ann, X} | LV];
|
||||
lvalue({map_key, K}, LV) -> [{map_get, get_ann(K), K} | LV];
|
||||
lvalue({map_key, K, V}, LV) -> [{map_get, get_ann(K), K, V} | LV];
|
||||
lvalue({proj, Ann, E, P}, LV) -> lvalue(E, [{proj, Ann, P} | LV]);
|
||||
lvalue({map_get, Ann, E, K}, LV) -> lvalue(E, [{map_get, Ann, K} | LV]);
|
||||
lvalue({map_get, Ann, E, K, V}, LV) -> lvalue(E, [{map_get, Ann, K, V} | LV]);
|
||||
lvalue(E, _) -> bad_expr_err("Not a valid lvalue", E).
|
||||
|
||||
infix(E, Op) ->
|
||||
?RULE(E, optional({Op, E}),
|
||||
case _2 of
|
||||
none -> _1;
|
||||
{ok, {F, Arg}} -> F(_1, Arg)
|
||||
end).
|
||||
|
||||
binop(Op) when is_atom(Op) -> binop([Op]);
|
||||
binop(Ops) ->
|
||||
?RULE(choice([ token(Op) || Op <- Ops ]), fun(A, B) -> infix(A, _1, B) end).
|
||||
|
||||
con() -> token(con).
|
||||
id() -> token(id).
|
||||
tvar() -> token(tvar).
|
||||
str() -> token(string).
|
||||
|
||||
token(Tag) ->
|
||||
?RULE(tok(Tag),
|
||||
case _1 of
|
||||
{Tok, {Line, Col}} -> {Tok, pos_ann(Line, Col)};
|
||||
{Tok, {Line, Col}, Val} -> {Tok, pos_ann(Line, Col), Val}
|
||||
end).
|
||||
|
||||
id(Id) ->
|
||||
?LET_P({id, A, X} = Y, id(),
|
||||
if X == Id -> Y;
|
||||
true -> fail({A, "expected 'bytes'"})
|
||||
end).
|
||||
|
||||
id_or_addr() ->
|
||||
?RULE(id(), parse_addr_literal(_1)).
|
||||
|
||||
parse_addr_literal(Id = {id, Ann, Name}) ->
|
||||
case lists:member(lists:sublist(Name, 3), ["ak_", "ok_", "oq_", "ct_"]) of
|
||||
false -> Id;
|
||||
true ->
|
||||
try aeser_api_encoder:decode(list_to_binary(Name)) of
|
||||
{Type, Bin} -> {Type, Ann, Bin}
|
||||
catch _:_ ->
|
||||
Id
|
||||
end
|
||||
end.
|
||||
|
||||
%% -- Helpers ----------------------------------------------------------------
|
||||
|
||||
keyword(K) -> ann(tok(K)).
|
||||
ann(P) -> map(fun get_ann/1, P).
|
||||
|
||||
block(P) ->
|
||||
between(layout(), sep1(P, tok(vsemi)), tok(vclose)).
|
||||
|
||||
maybe_block(P) ->
|
||||
choice(block(P), [P]).
|
||||
|
||||
parens(P) -> between(tok('('), P, tok(')')).
|
||||
braces(P) -> between(tok('{'), P, tok('}')).
|
||||
brackets(P) -> between(tok('['), P, tok(']')).
|
||||
comma_sep(P) -> sep(P, tok(',')).
|
||||
|
||||
paren_list(P) -> parens(comma_sep(P)).
|
||||
brace_list(P) -> braces(comma_sep(P)).
|
||||
bracket_list(P) -> brackets(comma_sep(P)).
|
||||
|
||||
%% -- Annotations ------------------------------------------------------------
|
||||
|
||||
-type ann() :: aeso_syntax:ann().
|
||||
-type ann_line() :: aeso_syntax:ann_line().
|
||||
-type ann_col() :: aeso_syntax:ann_col().
|
||||
|
||||
-spec pos_ann(ann_line(), ann_col()) -> ann().
|
||||
pos_ann(Line, Col) -> [{file, current_file()}, {line, Line}, {col, Col}].
|
||||
|
||||
current_file() ->
|
||||
get('$current_file').
|
||||
|
||||
set_current_file(File) ->
|
||||
put('$current_file', File).
|
||||
|
||||
ann_pos(Ann) ->
|
||||
{proplists:get_value(file, Ann),
|
||||
proplists:get_value(line, Ann),
|
||||
proplists:get_value(col, Ann)}.
|
||||
|
||||
get_ann(Ann) when is_list(Ann) -> Ann;
|
||||
get_ann(Node) ->
|
||||
case element(2, Node) of
|
||||
{Line, Col} when is_integer(Line), is_integer(Col) -> pos_ann(Line, Col);
|
||||
Ann -> Ann
|
||||
end.
|
||||
|
||||
get_ann(Key, Node) ->
|
||||
proplists:get_value(Key, get_ann(Node)).
|
||||
|
||||
set_ann(Key, Val, Node) ->
|
||||
Ann = get_ann(Node),
|
||||
setelement(2, Node, lists:keystore(Key, 1, Ann, {Key, Val})).
|
||||
|
||||
get_pos(Node) ->
|
||||
{current_file(), get_ann(line, Node), get_ann(col, Node)}.
|
||||
|
||||
set_pos({F, L, C}, Node) ->
|
||||
set_ann(file, F, set_ann(line, L, set_ann(col, C, Node))).
|
||||
|
||||
infix(L, Op, R) -> set_ann(format, infix, {app, get_ann(L), Op, [L, R]}).
|
||||
|
||||
prefixes(Ops, E) -> lists:foldr(fun prefix/2, E, Ops).
|
||||
prefix(Op, E) -> set_ann(format, prefix, {app, get_ann(Op), Op, [E]}).
|
||||
|
||||
type_wildcard() ->
|
||||
{id, [{origin, system}], "_"}.
|
||||
|
||||
block_e(Stmts) ->
|
||||
group_ifs(Stmts, []).
|
||||
|
||||
group_ifs([], [Stmt]) -> return(Stmt);
|
||||
group_ifs([], Acc) ->
|
||||
Stmts = [Stmt | _] = lists:reverse(Acc),
|
||||
{block, get_ann(Stmt), Stmts};
|
||||
group_ifs([{'if', Ann, Cond, Then} | Stmts], Acc) ->
|
||||
{Elses, Rest} = else_branches(Stmts, []),
|
||||
group_ifs(Rest, [build_if(Ann, Cond, Then, Elses) | Acc]);
|
||||
group_ifs([{else, Ann, _} | _], _) ->
|
||||
fail({Ann, "No matching 'if' for 'else'"});
|
||||
group_ifs([{elif, Ann, _, _} | _], _) ->
|
||||
fail({Ann, "No matching 'if' for 'elif'"});
|
||||
group_ifs([Stmt | Stmts], Acc) ->
|
||||
group_ifs(Stmts, [Stmt | Acc]).
|
||||
|
||||
build_if(Ann, Cond, Then, [{elif, Ann1, Cond1, Then1} | Elses]) ->
|
||||
{'if', Ann, Cond, Then,
|
||||
set_ann(format, elif, build_if(Ann1, Cond1, Then1, Elses))};
|
||||
build_if(Ann, Cond, Then, [{else, _Ann, Else}]) ->
|
||||
{'if', Ann, Cond, Then, Else};
|
||||
build_if(Ann, Cond, Then, []) ->
|
||||
{'if', Ann, Cond, Then, {unit, [{origin, system}]}}.
|
||||
|
||||
else_branches([Elif = {elif, _, _, _} | Stmts], Acc) ->
|
||||
else_branches(Stmts, [Elif | Acc]);
|
||||
else_branches([Else = {else, _, _} | Stmts], Acc) ->
|
||||
{lists:reverse([Else | Acc]), Stmts};
|
||||
else_branches(Stmts, Acc) ->
|
||||
{lists:reverse(Acc), Stmts}.
|
||||
|
||||
tuple_t(_Ann, [Type]) -> Type; %% Not a tuple
|
||||
tuple_t(Ann, Types) -> {tuple_t, Ann, Types}.
|
||||
|
||||
fun_t(Domains, Type) ->
|
||||
lists:foldr(fun({Dom, Ann}, T) -> {fun_t, Ann, [], Dom, T} end,
|
||||
Type, Domains).
|
||||
|
||||
tuple_e(Ann, []) -> {unit, Ann};
|
||||
tuple_e(_Ann, [Expr]) -> Expr; %% Not a tuple
|
||||
tuple_e(Ann, Exprs) -> {tuple, Ann, Exprs}.
|
||||
|
||||
%% TODO: not nice
|
||||
fun_domain({tuple_t, _, Args}) -> Args;
|
||||
fun_domain(T) -> [T].
|
||||
|
||||
-spec parse_pattern(aeso_syntax:expr()) -> aeso_parse_lib:parser(aeso_syntax:pat()).
|
||||
parse_pattern({app, Ann, Con = {'::', _}, Es}) ->
|
||||
{app, Ann, Con, lists:map(fun parse_pattern/1, Es)};
|
||||
parse_pattern({app, Ann, Con = {con, _, _}, Es}) ->
|
||||
{app, Ann, Con, lists:map(fun parse_pattern/1, Es)};
|
||||
parse_pattern({tuple, Ann, Es}) ->
|
||||
{tuple, Ann, lists:map(fun parse_pattern/1, Es)};
|
||||
parse_pattern({list, Ann, Es}) ->
|
||||
{list, Ann, lists:map(fun parse_pattern/1, Es)};
|
||||
parse_pattern({record, Ann, Fs}) ->
|
||||
{record, Ann, lists:map(fun parse_field_pattern/1, Fs)};
|
||||
parse_pattern(E = {con, _, _}) -> E;
|
||||
parse_pattern(E = {id, _, _}) -> E;
|
||||
parse_pattern(E = {unit, _}) -> E;
|
||||
parse_pattern(E = {int, _, _}) -> E;
|
||||
parse_pattern(E = {bool, _, _}) -> E;
|
||||
parse_pattern(E = {bytes, _, _}) -> E;
|
||||
parse_pattern(E = {string, _, _}) -> E;
|
||||
parse_pattern(E = {char, _, _}) -> E;
|
||||
parse_pattern(E) -> bad_expr_err("Not a valid pattern", E).
|
||||
|
||||
-spec parse_field_pattern(aeso_syntax:field(aeso_syntax:expr())) -> aeso_parse_lib:parser(aeso_syntax:field(aeso_syntax:pat())).
|
||||
parse_field_pattern({field, Ann, F, E}) ->
|
||||
{field, Ann, F, parse_pattern(E)}.
|
||||
|
||||
return_error({no_file, L, C}, Err) ->
|
||||
fail(io_lib:format("~p:~p:\n~s", [L, C, Err]));
|
||||
return_error({F, L, C}, Err) ->
|
||||
fail(io_lib:format("In ~s at ~p:~p:\n~s", [F, L, C, Err])).
|
||||
|
||||
-spec ret_doc_err(ann(), prettypr:document()) -> no_return().
|
||||
ret_doc_err(Ann, Doc) ->
|
||||
return_error(ann_pos(Ann), prettypr:format(Doc)).
|
||||
|
||||
-spec bad_expr_err(string(), aeso_syntax:expr()) -> no_return().
|
||||
bad_expr_err(Reason, E) ->
|
||||
ret_doc_err(get_ann(E),
|
||||
prettypr:sep([prettypr:text(Reason ++ ":"),
|
||||
prettypr:nest(2, aeso_pretty:expr(E))])).
|
||||
|
||||
%% -- Helper functions -------------------------------------------------------
|
||||
expand_includes(AST, Opts) ->
|
||||
expand_includes(AST, [], Opts).
|
||||
|
||||
expand_includes([], Acc, _Opts) ->
|
||||
{ok, lists:reverse(Acc)};
|
||||
expand_includes([{include, S = {string, _, File}} | AST], Acc, Opts) ->
|
||||
case read_file(File, Opts) of
|
||||
{ok, Bin} ->
|
||||
Opts1 = lists:keystore(src_file, 1, Opts, {src_file, File}),
|
||||
case string(binary_to_list(Bin), Opts1) of
|
||||
{ok, AST1} ->
|
||||
expand_includes(AST1 ++ AST, Acc, Opts);
|
||||
Err = {error, _} ->
|
||||
Err
|
||||
end;
|
||||
{error, _} ->
|
||||
{error, {get_pos(S), include_error, File}}
|
||||
end;
|
||||
expand_includes([E | AST], Acc, Opts) ->
|
||||
expand_includes(AST, [E | Acc], Opts).
|
||||
|
||||
read_file(File, Opts) ->
|
||||
case proplists:get_value(include, Opts, {explicit_files, #{}}) of
|
||||
{file_system, Paths} ->
|
||||
CandidateNames = [ filename:join(Dir, File) || Dir <- Paths ],
|
||||
lists:foldr(fun(F, {error, _}) -> file:read_file(F);
|
||||
(_F, OK) -> OK end, {error, not_found}, CandidateNames);
|
||||
{explicit_files, Files} ->
|
||||
case maps:get(binary_to_list(File), Files, not_found) of
|
||||
not_found -> {error, not_found};
|
||||
Src -> {ok, Src}
|
||||
end
|
||||
end.
|
||||
|
||||
@@ -0,0 +1,453 @@
|
||||
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @copyright (C) 2017, Aeternity Anstalt
|
||||
%%% @doc Pretty printer for Sophia.
|
||||
%%%
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeso_pretty).
|
||||
|
||||
-import(prettypr, [text/1, sep/1, above/2, beside/2, nest/2, empty/0]).
|
||||
|
||||
-export([decls/1, decls/2, decl/1, decl/2, expr/1, expr/2, type/1, type/2]).
|
||||
|
||||
-export_type([options/0]).
|
||||
|
||||
-type doc() :: prettypr:document().
|
||||
-type options() :: [{indent, non_neg_integer()} | show_generated].
|
||||
|
||||
%% More options:
|
||||
%% Newline before open curly
|
||||
%% Space before ':'
|
||||
|
||||
%% -- Options ----------------------------------------------------------------
|
||||
|
||||
-define(aeso_pretty_opts, aeso_pretty_opts).
|
||||
|
||||
-spec options() -> options().
|
||||
options() ->
|
||||
case get(?aeso_pretty_opts) of
|
||||
undefined -> [];
|
||||
Opts -> Opts
|
||||
end.
|
||||
|
||||
-spec option(atom(), any()) -> any().
|
||||
option(Key, Default) ->
|
||||
proplists:get_value(Key, options(), Default).
|
||||
|
||||
-spec show_generated() -> boolean().
|
||||
show_generated() -> option(show_generated, false).
|
||||
|
||||
-spec indent() -> non_neg_integer().
|
||||
indent() -> option(indent, 2).
|
||||
|
||||
-spec with_options(options(), fun(() -> A)) -> A.
|
||||
with_options(Options, Fun) ->
|
||||
put(?aeso_pretty_opts, Options),
|
||||
Res = Fun(),
|
||||
erase(?aeso_pretty_opts),
|
||||
Res.
|
||||
|
||||
%% -- Pretty printing helpers ------------------------------------------------
|
||||
|
||||
-spec par([doc()]) -> doc().
|
||||
par(Ds) -> par(Ds, indent()).
|
||||
|
||||
-spec par([doc()], non_neg_integer()) -> doc().
|
||||
par([], _) -> empty();
|
||||
par(Ds, N) -> prettypr:par(Ds, N).
|
||||
|
||||
-spec follow(doc(), doc(), non_neg_integer()) -> doc().
|
||||
follow(A, B, N) ->
|
||||
sep([A, nest(N, B)]).
|
||||
|
||||
-spec follow(doc(), doc()) -> doc().
|
||||
follow(A, B) -> follow(A, B, indent()).
|
||||
|
||||
-spec above([doc()]) -> doc().
|
||||
above([]) -> empty();
|
||||
above([D]) -> D;
|
||||
above([D | Ds]) -> lists:foldl(fun(X, Y) -> above(Y, X) end, D, Ds).
|
||||
|
||||
-spec beside([doc()]) -> doc().
|
||||
beside([]) -> empty();
|
||||
beside([D]) -> D;
|
||||
beside([D | Ds]) -> lists:foldl(fun(X, Y) -> beside(Y, X) end, D, Ds).
|
||||
|
||||
-spec hsep([doc()]) -> doc().
|
||||
hsep(Ds) -> beside(punctuate(text(" "), [ D || D <- Ds, D /= empty() ])).
|
||||
|
||||
-spec hsep(doc(), doc()) -> doc().
|
||||
hsep(D1, D2) -> hsep([D1, D2]).
|
||||
|
||||
-spec punctuate(doc(), [doc()]) -> [doc()].
|
||||
punctuate(_Sep, []) -> [];
|
||||
punctuate(_Sep, [D]) -> [D];
|
||||
punctuate(Sep, [D | Ds]) -> [beside(D, Sep) | punctuate(Sep, Ds)].
|
||||
|
||||
-spec paren(doc()) -> doc().
|
||||
paren(D) -> beside([text("("), D, text(")")]).
|
||||
|
||||
-spec paren(boolean(), doc()) -> doc().
|
||||
paren(false, D) -> D;
|
||||
paren(true, D) -> paren(D).
|
||||
|
||||
-spec indent(doc()) -> doc().
|
||||
indent(D) -> nest(indent(), D).
|
||||
|
||||
%% block(Header, Body) ->
|
||||
%% Header
|
||||
%% Body
|
||||
-spec block(doc(), doc()) -> doc().
|
||||
block(Header, Body) ->
|
||||
sep([ Header, indent(Body) ]).
|
||||
|
||||
-spec comma_brackets(string(), string(), [doc()]) -> doc().
|
||||
comma_brackets(Open, Close, Ds) ->
|
||||
beside([text(Open), par(punctuate(text(","), Ds), 0), text(Close)]).
|
||||
|
||||
-spec tuple([doc()]) -> doc().
|
||||
tuple(Ds) ->
|
||||
comma_brackets("(", ")", Ds).
|
||||
|
||||
-spec list([doc()]) -> doc().
|
||||
list(Ds) ->
|
||||
comma_brackets("[", "]", Ds).
|
||||
|
||||
-spec record([doc()]) -> doc().
|
||||
record(Ds) ->
|
||||
comma_brackets("{", "}", Ds).
|
||||
|
||||
%% equals(A, B) -> A = B
|
||||
-spec equals(doc(), doc()) -> doc().
|
||||
equals(A, B) -> follow(hsep(A, text("=")), B).
|
||||
|
||||
%% typed(A, B) -> A : B.
|
||||
-spec typed(doc(), aeso_syntax:type()) -> doc().
|
||||
typed(A, Type) ->
|
||||
case aeso_syntax:get_ann(origin, Type) == system andalso
|
||||
not show_generated() of
|
||||
true -> A;
|
||||
false -> follow(hsep(A, text(":")), type(Type))
|
||||
end.
|
||||
|
||||
%% -- Exports ----------------------------------------------------------------
|
||||
|
||||
-spec decls([aeso_syntax:decl()], options()) -> doc().
|
||||
decls(Ds, Options) ->
|
||||
with_options(Options, fun() -> decls(Ds) end).
|
||||
|
||||
-spec decls([aeso_syntax:decl()]) -> doc().
|
||||
decls(Ds) -> above([ decl(D) || D <- Ds ]).
|
||||
|
||||
-spec decl(aeso_syntax:decl(), options()) -> doc().
|
||||
decl(D, Options) ->
|
||||
with_options(Options, fun() -> decl(D) end).
|
||||
|
||||
-spec decl(aeso_syntax:decl()) -> doc().
|
||||
decl({contract, _, C, Ds}) ->
|
||||
block(follow(text("contract"), hsep(name(C), text("="))), decls(Ds));
|
||||
decl({namespace, _, C, Ds}) ->
|
||||
block(follow(text("namespace"), hsep(name(C), text("="))), decls(Ds));
|
||||
decl({type_decl, _, T, Vars}) -> typedecl(alias_t, T, Vars);
|
||||
decl({type_def, _, T, Vars, Def}) ->
|
||||
Kind = element(1, Def),
|
||||
equals(typedecl(Kind, T, Vars), typedef(Def));
|
||||
decl({fun_decl, _, F, T}) ->
|
||||
hsep(text("function"), typed(name(F), T));
|
||||
decl(D = {letfun, Attrs, _, _, _, _}) ->
|
||||
Mod = fun({Mod, true}) when Mod == private; Mod == internal; Mod == public; Mod == stateful ->
|
||||
text(atom_to_list(Mod));
|
||||
(_) -> empty() end,
|
||||
hsep(lists:map(Mod, Attrs) ++ [letdecl("function", D)]);
|
||||
decl(D = {letval, _, _, _, _}) -> letdecl("let", D);
|
||||
decl(D = {letrec, _, _}) -> letdecl("let", D).
|
||||
|
||||
-spec expr(aeso_syntax:expr(), options()) -> doc().
|
||||
expr(E, Options) ->
|
||||
with_options(Options, fun() -> expr(E) end).
|
||||
|
||||
-spec expr(aeso_syntax:expr()) -> doc().
|
||||
expr(E) -> expr_p(0, E).
|
||||
|
||||
%% -- Not exported -----------------------------------------------------------
|
||||
|
||||
-spec name(aeso_syntax:id() | aeso_syntax:qid() | aeso_syntax:con() | aeso_syntax:qcon() | aeso_syntax:tvar()) -> doc().
|
||||
name({id, _, Name}) -> text(Name);
|
||||
name({con, _, Name}) -> text(Name);
|
||||
name({qid, _, Names}) -> text(string:join(Names, "."));
|
||||
name({qcon, _, Names}) -> text(string:join(Names, "."));
|
||||
name({tvar, _, Name}) -> text(Name);
|
||||
name({typed, _, Name, _}) -> name(Name).
|
||||
|
||||
-spec letdecl(string(), aeso_syntax:letbind()) -> doc().
|
||||
letdecl(Let, {letval, _, F, T, E}) ->
|
||||
block_expr(0, hsep([text(Let), typed(name(F), T), text("=")]), E);
|
||||
letdecl(Let, {letfun, _, F, Args, T, E}) ->
|
||||
block_expr(0, hsep([text(Let), typed(beside(name(F), args(Args)), T), text("=")]), E);
|
||||
letdecl(Let, {letrec, _, [D | Ds]}) ->
|
||||
hsep(text(Let), above([ letdecl("rec", D) | [ letdecl("and", D1) || D1 <- Ds ] ])).
|
||||
|
||||
-spec args([aeso_syntax:arg()]) -> doc().
|
||||
args(Args) ->
|
||||
tuple(lists:map(fun arg/1, Args)).
|
||||
|
||||
-spec arg(aeso_syntax:arg()) -> doc().
|
||||
arg({arg, _, X, T}) -> typed(name(X), T).
|
||||
|
||||
-spec typedecl(alias_t | record_t | variant_t, aeso_syntax:id(), [aeso_syntax:tvar()]) -> doc().
|
||||
typedecl(Kind, T, Vars) ->
|
||||
KW = case Kind of
|
||||
alias_t -> text("type");
|
||||
record_t -> text("record");
|
||||
variant_t -> text("datatype")
|
||||
end,
|
||||
case Vars of
|
||||
[] -> hsep(KW, name(T));
|
||||
_ -> beside(hsep(KW, name(T)),
|
||||
tuple(lists:map(fun name/1, Vars)))
|
||||
end.
|
||||
|
||||
-spec typedef(aeso_syntax:typedef()) -> doc().
|
||||
typedef({alias_t, Type}) -> type(Type);
|
||||
typedef({record_t, Fields}) ->
|
||||
record(lists:map(fun field_t/1, Fields));
|
||||
typedef({variant_t, Constructors}) ->
|
||||
par(punctuate(text(" |"), lists:map(fun constructor_t/1, Constructors))).
|
||||
|
||||
-spec constructor_t(aeso_syntax:constructor_t()) -> doc().
|
||||
constructor_t({constr_t, _, C, []}) -> name(C);
|
||||
constructor_t({constr_t, _, C, Args}) -> beside(name(C), tuple_type(Args)).
|
||||
|
||||
-spec field_t(aeso_syntax:field_t()) -> doc().
|
||||
field_t({field_t, _, Name, Type}) ->
|
||||
typed(name(Name), Type).
|
||||
|
||||
-spec type(aeso_syntax:type(), options()) -> doc().
|
||||
type(Type, Options) ->
|
||||
with_options(Options, fun() -> type(Type) end).
|
||||
|
||||
-spec type(aeso_syntax:type()) -> doc().
|
||||
type({fun_t, _, Named, Args, Ret}) ->
|
||||
follow(hsep(tuple_type(Named ++ Args), text("=>")), type(Ret));
|
||||
type({app_t, _, Type, []}) ->
|
||||
type(Type);
|
||||
type({app_t, _, Type, Args}) ->
|
||||
beside(type(Type), tuple_type(Args));
|
||||
type({tuple_t, _, Args}) ->
|
||||
tuple_type(Args);
|
||||
type({bytes_t, _, Len}) ->
|
||||
text(lists:concat(["bytes(", Len, ")"]));
|
||||
type({named_arg_t, _, Name, Type, _Default}) ->
|
||||
%% Drop the default value
|
||||
%% follow(hsep(typed(name(Name), Type), text("=")), expr(Default));
|
||||
typed(name(Name), Type);
|
||||
|
||||
type(R = {record_t, _}) -> typedef(R);
|
||||
type(T = {id, _, _}) -> name(T);
|
||||
type(T = {qid, _, _}) -> name(T);
|
||||
type(T = {con, _, _}) -> name(T);
|
||||
type(T = {qcon, _, _}) -> name(T);
|
||||
type(T = {tvar, _, _}) -> name(T).
|
||||
|
||||
-spec tuple_type([aeso_syntax:type()]) -> doc().
|
||||
tuple_type(Args) ->
|
||||
tuple(lists:map(fun type/1, Args)).
|
||||
|
||||
-spec arg_expr(aeso_syntax:arg_expr()) -> doc().
|
||||
arg_expr({named_arg, _, Name, 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}) ->
|
||||
paren(P > 100, follow(hsep(args(Args), text("=>")), expr_p(100, E)));
|
||||
expr_p(P, If = {'if', Ann, Cond, Then, Else}) ->
|
||||
Format = aeso_syntax:get_ann(format, If),
|
||||
if Format == '?:' ->
|
||||
paren(P > 100,
|
||||
follow(expr_p(200, Cond),
|
||||
follow(hsep(text("?"), expr_p(100, Then)),
|
||||
hsep(text(":"), expr_p(100, Else)), 0)));
|
||||
true ->
|
||||
{Elifs, Else1} = get_elifs(Else),
|
||||
above([ stmt_p(Stmt) || Stmt <- [{'if', Ann, Cond, Then} | Elifs] ++ [Else1]])
|
||||
end;
|
||||
expr_p(_P, {switch, _, E, Cases}) ->
|
||||
block(beside(text("switch"), paren(expr(E))),
|
||||
above(lists:map(fun alt/1, Cases)));
|
||||
expr_p(_, {tuple, _, Es}) ->
|
||||
tuple(lists:map(fun expr/1, Es));
|
||||
expr_p(_, {list, _, Es}) ->
|
||||
list(lists:map(fun expr/1, Es));
|
||||
expr_p(_, {record, _, Fs}) ->
|
||||
record(lists:map(fun field/1, Fs));
|
||||
expr_p(_, {map, Ann, KVs}) ->
|
||||
record([ field({field, Ann, [{map_get, [], K}], V}) || {K, V} <- KVs ]);
|
||||
expr_p(P, {map, Ann, E, Flds}) ->
|
||||
expr_p(P, {record, Ann, E, Flds});
|
||||
expr_p(P, {record, Ann, E, Fs}) ->
|
||||
paren(P > 900, hsep(expr_p(900, E), expr({record, Ann, Fs})));
|
||||
expr_p(_, {block, _, Ss}) ->
|
||||
block(empty(), statements(Ss));
|
||||
expr_p(P, {proj, _, E, X}) ->
|
||||
paren(P > 900, beside([expr_p(900, E), text("."), name(X)]));
|
||||
expr_p(P, {map_get, _, E, Key}) ->
|
||||
paren(P > 900, beside([expr_p(900, E), list([expr(Key)])]));
|
||||
expr_p(P, {map_get, Ann, E, Key, Val}) ->
|
||||
paren(P > 900, beside([expr_p(900, E), list([expr(equals(Ann, Key, Val))])]));
|
||||
expr_p(P, {typed, _, E, T}) ->
|
||||
paren(P > 0, typed(expr(E), T));
|
||||
expr_p(P, {assign, _, LV, E}) ->
|
||||
paren(P > 0, equals(expr_p(900, LV), expr(E)));
|
||||
%% -- Operators
|
||||
expr_p(_, {app, _, {'..', _}, [A, B]}) ->
|
||||
list([infix(0, '..', A, B)]);
|
||||
expr_p(P, E = {app, _, F = {Op, _}, Args}) when is_atom(Op) ->
|
||||
case {aeso_syntax:get_ann(format, E), Args} of
|
||||
{infix, [A, B]} -> infix(P, Op, A, B);
|
||||
{prefix, [A]} -> prefix(P, Op, A);
|
||||
_ -> app(P, F, Args)
|
||||
end;
|
||||
expr_p(_, {app, _, C={Tag, _, _}, []}) when Tag == con; Tag == qcon ->
|
||||
expr_p(0, C);
|
||||
expr_p(P, {app, _, F, Args}) ->
|
||||
app(P, F, Args);
|
||||
%% -- Constants
|
||||
expr_p(_, E = {int, _, N}) ->
|
||||
S = case aeso_syntax:get_ann(format, E) of
|
||||
hex -> "0x" ++ integer_to_list(N, 16);
|
||||
_ -> integer_to_list(N)
|
||||
end,
|
||||
text(S);
|
||||
expr_p(_, {bool, _, B}) -> text(atom_to_list(B));
|
||||
expr_p(_, {bytes, _, Bin}) ->
|
||||
Digits = byte_size(Bin),
|
||||
<<N:Digits/unit:8>> = Bin,
|
||||
text(lists:flatten(io_lib:format("#~*.16.0b", [Digits*2, N])));
|
||||
expr_p(_, {hash, _, <<N:512>>}) -> text("#" ++ integer_to_list(N, 16));
|
||||
expr_p(_, {Type, _, Bin})
|
||||
when Type == account_pubkey;
|
||||
Type == contract_pubkey;
|
||||
Type == oracle_pubkey;
|
||||
Type == oracle_query_id ->
|
||||
text(binary_to_list(aeser_api_encoder:encode(Type, Bin)));
|
||||
expr_p(_, {unit, _}) -> text("()");
|
||||
expr_p(_, {string, _, S}) -> term(binary_to_list(S));
|
||||
expr_p(_, {char, _, C}) ->
|
||||
case C of
|
||||
$' -> text("'\\''");
|
||||
$" -> text("'\"'");
|
||||
_ -> S = lists:flatten(io_lib:format("~p", [[C]])),
|
||||
text("'" ++ tl(lists:droplast(S)) ++ "'")
|
||||
end;
|
||||
%% -- Names
|
||||
expr_p(_, E = {id, _, _}) -> name(E);
|
||||
expr_p(_, E = {con, _, _}) -> name(E);
|
||||
expr_p(_, E = {qid, _, _}) -> name(E);
|
||||
expr_p(_, E = {qcon, _, _}) -> name(E);
|
||||
%% -- For error messages
|
||||
expr_p(_, {Op, _}) when is_atom(Op) ->
|
||||
paren(text(atom_to_list(Op)));
|
||||
expr_p(_, {lvalue, _, LV}) -> lvalue(LV).
|
||||
|
||||
stmt_p({'if', _, Cond, Then}) ->
|
||||
block_expr(200, beside(text("if"), paren(expr(Cond))), Then);
|
||||
stmt_p({elif, _, Cond, Then}) ->
|
||||
block_expr(200, beside(text("elif"), paren(expr(Cond))), Then);
|
||||
stmt_p({else, Else}) ->
|
||||
HideGenerated = not show_generated(),
|
||||
case aeso_syntax:get_ann(origin, Else) of
|
||||
system when HideGenerated -> empty();
|
||||
_ -> block_expr(200, text("else"), Else)
|
||||
end.
|
||||
|
||||
-spec bin_prec(aeso_syntax:bin_op()) -> {integer(), integer(), integer()}.
|
||||
bin_prec('..') -> { 0, 0, 0}; %% Always printed inside '[ ]'
|
||||
bin_prec('=') -> { 0, 0, 0}; %% Always printed inside '[ ]'
|
||||
bin_prec('||') -> {200, 300, 200};
|
||||
bin_prec('&&') -> {300, 400, 300};
|
||||
bin_prec('<') -> {400, 500, 500};
|
||||
bin_prec('>') -> {400, 500, 500};
|
||||
bin_prec('=<') -> {400, 500, 500};
|
||||
bin_prec('>=') -> {400, 500, 500};
|
||||
bin_prec('==') -> {400, 500, 500};
|
||||
bin_prec('!=') -> {400, 500, 500};
|
||||
bin_prec('++') -> {500, 600, 500};
|
||||
bin_prec('::') -> {500, 600, 500};
|
||||
bin_prec('+') -> {600, 600, 650};
|
||||
bin_prec('-') -> {600, 600, 650};
|
||||
bin_prec('*') -> {700, 700, 750};
|
||||
bin_prec('/') -> {700, 700, 750};
|
||||
bin_prec(mod) -> {700, 700, 750};
|
||||
bin_prec('^') -> {750, 750, 800}.
|
||||
|
||||
-spec un_prec(aeso_syntax:un_op()) -> {integer(), integer()}.
|
||||
un_prec('-') -> {650, 650};
|
||||
un_prec('!') -> {800, 800}.
|
||||
|
||||
equals(Ann, A, B) ->
|
||||
{app, [{format, infix} | Ann], {'=', Ann}, [A, B]}.
|
||||
|
||||
-spec infix(integer(), aeso_syntax:bin_op(), aeso_syntax:expr(), aeso_syntax:expr()) -> doc().
|
||||
infix(P, Op, A, B) ->
|
||||
{Top, L, R} = bin_prec(Op),
|
||||
paren(P > Top,
|
||||
follow(hsep(expr_p(L, A), text(atom_to_list(Op))),
|
||||
expr_p(R, B))).
|
||||
|
||||
prefix(P, Op, A) ->
|
||||
{Top, Inner} = un_prec(Op),
|
||||
paren(P > Top, hsep(text(atom_to_list(Op)), expr_p(Inner, A))).
|
||||
|
||||
app(P, F, Args) ->
|
||||
paren(P > 900,
|
||||
beside(expr_p(900, F),
|
||||
tuple(lists:map(fun arg_expr/1, Args)))).
|
||||
|
||||
field({field, _, LV, E}) ->
|
||||
follow(hsep(lvalue(LV), text("=")), expr(E));
|
||||
field({field, _, LV, Id, E}) ->
|
||||
follow(hsep([lvalue(LV), text("@"), name(Id), text("=")]), expr(E));
|
||||
field({field_upd, _, LV, Fun}) ->
|
||||
follow(hsep(lvalue(LV), text("~")), expr(Fun)). %% Not valid syntax
|
||||
|
||||
lvalue([E | Es]) ->
|
||||
beside([elim(E) | lists:map(fun elim1/1, Es)]).
|
||||
|
||||
elim({proj, _, X}) -> name(X);
|
||||
elim({map_get, Ann, K}) -> expr_p(0, {list, Ann, [K]});
|
||||
elim({map_get, Ann, K, V}) -> expr_p(0, {list, Ann, [equals(Ann, K, V)]}).
|
||||
|
||||
elim1(Proj={proj, _, _}) -> beside(text("."), elim(Proj));
|
||||
elim1(Get={map_get, _, _}) -> elim(Get);
|
||||
elim1(Get={map_get, _, _, _}) -> elim(Get).
|
||||
|
||||
alt({'case', _, Pat, Body}) ->
|
||||
block_expr(0, hsep(expr_p(500, Pat), text("=>")), Body).
|
||||
|
||||
block_expr(_, Header, {block, _, Ss}) ->
|
||||
block(Header, statements(Ss));
|
||||
block_expr(P, Header, E) ->
|
||||
follow(Header, expr_p(P, E)).
|
||||
|
||||
statements(Stmts) ->
|
||||
above([ statement(S) || S <- Stmts ]).
|
||||
|
||||
statement(S = {letval, _, _, _, _}) -> letdecl("let", S);
|
||||
statement(S = {letfun, _, _, _, _, _}) -> letdecl("let", S);
|
||||
statement(S = {letrec, _, _}) -> letdecl("let", S);
|
||||
statement(E) -> expr(E).
|
||||
|
||||
get_elifs(Expr) -> get_elifs(Expr, []).
|
||||
|
||||
get_elifs(If = {'if', Ann, Cond, Then, Else}, Elifs) ->
|
||||
case aeso_syntax:get_ann(format, If) of
|
||||
elif -> get_elifs(Else, [{elif, Ann, Cond, Then} | Elifs]);
|
||||
_ -> {lists:reverse(Elifs), If}
|
||||
end;
|
||||
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]).
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @copyright (C) 2017, Aeternity Anstalt
|
||||
%%% @doc The Sophia lexer.
|
||||
%%%
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeso_scan).
|
||||
|
||||
-export([scan/1]).
|
||||
|
||||
-import(aeso_scan_lib, [token/1, token/2, symbol/0, skip/0,
|
||||
override/2, push/2, pop/1]).
|
||||
|
||||
lexer() ->
|
||||
DIGIT = "[0-9]",
|
||||
HEXDIGIT = "[0-9a-fA-F]",
|
||||
LOWER = "[a-z_]",
|
||||
UPPER = "[A-Z]",
|
||||
CON = [UPPER, "[a-zA-Z0-9_]*"],
|
||||
INT = [DIGIT, "+"],
|
||||
HEX = ["0x", HEXDIGIT, "+"],
|
||||
BYTES = ["#", HEXDIGIT, "+"],
|
||||
WS = "[\\000-\\ ]+",
|
||||
ID = [LOWER, "[a-zA-Z0-9_']*"],
|
||||
TVAR = ["'", ID],
|
||||
QID = ["(", CON, "\\.)+", ID],
|
||||
QCON = ["(", CON, "\\.)+", CON],
|
||||
OP = "[=!<>+\\-*/:&|?~@^]+",
|
||||
CHAR = "'([^'\\\\]|(\\\\.))'",
|
||||
STRING = "\"([^\"\\\\]|(\\\\.))*\"",
|
||||
|
||||
CommentStart = {"/\\*", push(comment, skip())},
|
||||
CommentRules =
|
||||
[ CommentStart
|
||||
, {"\\*/", pop(skip())}
|
||||
, {"[^/*]+|[/*]", skip()} ],
|
||||
|
||||
Keywords = ["contract", "include", "let", "rec", "switch", "type", "record", "datatype", "if", "elif", "else", "function",
|
||||
"stateful", "true", "false", "and", "mod", "public", "private", "indexed", "internal", "namespace"],
|
||||
KW = string:join(Keywords, "|"),
|
||||
|
||||
Rules =
|
||||
%% Comments and whitespace
|
||||
[ CommentStart
|
||||
, {"//.*", skip()}
|
||||
, {WS, skip()}
|
||||
|
||||
%% Special characters
|
||||
, {"\\.\\.|[,.;()\\[\\]{}]", symbol()}
|
||||
|
||||
%% Literals
|
||||
, {CHAR, token(char, fun parse_char/1)}
|
||||
, {STRING, token(string, fun parse_string/1)}
|
||||
, {HEX, token(hex, fun parse_hex/1)}
|
||||
, {INT, token(int, fun list_to_integer/1)}
|
||||
, {BYTES, token(bytes, fun parse_bytes/1)}
|
||||
|
||||
%% Identifiers (qualified first!)
|
||||
, {QID, token(qid, fun(S) -> string:tokens(S, ".") end)}
|
||||
, {QCON, token(qcon, fun(S) -> string:tokens(S, ".") end)}
|
||||
, {TVAR, token(tvar)}
|
||||
, override({ID, token(id)}, {KW, symbol()}) %% Keywords override identifiers. Need to
|
||||
, {CON, token(con)} %% use override to avoid lexing "lettuce"
|
||||
%% as ['let', {id, "tuce"}].
|
||||
%% Operators
|
||||
, {OP, symbol()}
|
||||
],
|
||||
|
||||
[{code, Rules}, {comment, CommentRules}].
|
||||
|
||||
scan(String) ->
|
||||
Lexer = aeso_scan_lib:compile(lexer()),
|
||||
aeso_scan_lib:string(Lexer, code, String).
|
||||
|
||||
%% -- Helpers ----------------------------------------------------------------
|
||||
|
||||
parse_string([$" | Chars]) ->
|
||||
unescape(Chars).
|
||||
|
||||
parse_char([$', $\\, Code, $']) ->
|
||||
case Code of
|
||||
$' -> $';
|
||||
$\\ -> $\\;
|
||||
$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, []).
|
||||
|
||||
%% TODO: numeric escapes
|
||||
unescape([$"], Acc) ->
|
||||
list_to_binary(lists:reverse(Acc));
|
||||
unescape([$\\, Code | Chars], Acc) ->
|
||||
Ok = fun(C) -> unescape(Chars, [C | Acc]) end,
|
||||
case Code of
|
||||
$" -> Ok($");
|
||||
$\\ -> Ok($\\);
|
||||
$b -> Ok($\b);
|
||||
$e -> Ok($\e);
|
||||
$f -> Ok($\f);
|
||||
$n -> Ok($\n);
|
||||
$r -> Ok($\r);
|
||||
$t -> Ok($\t);
|
||||
$v -> Ok($\v);
|
||||
_ -> error("Bad control sequence: \\" ++ [Code]) %% TODO
|
||||
end;
|
||||
unescape([C | Chars], Acc) ->
|
||||
unescape(Chars, [C | Acc]).
|
||||
|
||||
parse_hex("0x" ++ Chars) -> list_to_integer(Chars, 16).
|
||||
|
||||
parse_bytes("#" ++ Chars) ->
|
||||
N = list_to_integer(Chars, 16),
|
||||
Digits = (length(Chars) + 1) div 2,
|
||||
<<N:Digits/unit:8>>.
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @copyright (C) 2017, Aeternity Anstalt
|
||||
%%% @doc A customisable lexer.
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeso_scan_lib).
|
||||
|
||||
-export([compile/1, string/3,
|
||||
token/1, token/2, symbol/0, skip/0,
|
||||
override/2, push/2, pop/1]).
|
||||
|
||||
-export_type([lexer/0, token_spec/0, token_action/0, token/0, pos/0, regex/0]).
|
||||
|
||||
%% -- Exported types --
|
||||
|
||||
-type regex() :: iodata() | unicode:charlist().
|
||||
-type pos() :: {integer(), integer()}.
|
||||
-type lex_state() :: atom().
|
||||
-type token() :: {atom(), pos(), term()} | {atom(), pos()}.
|
||||
|
||||
-type token_spec() :: {regex(), token_action()}.
|
||||
-opaque token_action() :: fun((string(), pos()) -> {tok_result(), state_change()}).
|
||||
|
||||
-opaque lexer() :: [{lex_state(),
|
||||
fun((string(), pos()) -> {ok, tok_result(), string(), pos()}
|
||||
| end_of_file | error)}].
|
||||
|
||||
%% -- Internal types --
|
||||
-type tok_result() :: {token, token()} | skip.
|
||||
-type state_change() :: none | pop | {push, lex_state()}.
|
||||
|
||||
%% @doc Compile a lexer specification. Takes the regexps for each state and
|
||||
%% combines them into a single big regexp that is then compiled with re:compile/1.
|
||||
%% Note: contrary to lexer generators like leex, we don't have longest match
|
||||
%% semantics (since this isn't supported by re). Use override/2 instead.
|
||||
-spec compile([{lex_state(), [token_spec()]}]) -> lexer().
|
||||
compile(TokenSpecs) ->
|
||||
[{S, compile_spec(Spec)} || {S, Spec} <- TokenSpecs].
|
||||
|
||||
compile_spec(TokenSpecs) ->
|
||||
WithIxs = lists:zip(lists:seq(1, length(TokenSpecs)), TokenSpecs),
|
||||
{ok, Regex} = re:compile(["^(", name(0), string:join([ ["(", name(I), R, ")"] || {I, {R, _}} <- WithIxs ], "|"),")"]),
|
||||
Actions = [ Fun || {_, Fun} <- TokenSpecs ],
|
||||
fun ("", _Pos) -> end_of_file;
|
||||
(S, Pos) ->
|
||||
case re:run(S, Regex, [{capture, all_names}]) of
|
||||
{match, [{0, N} | Capture]} ->
|
||||
Index = 1 + length(lists:takewhile(fun({P, _}) -> P == -1 end, Capture)),
|
||||
Action = lists:nth(Index, Actions),
|
||||
{TokS, Rest} = lists:split(N, S),
|
||||
Tok = Action(TokS, Pos),
|
||||
{ok, Tok, Rest, next_pos(TokS, Pos)};
|
||||
nomatch ->
|
||||
error
|
||||
end
|
||||
end.
|
||||
|
||||
%% @doc Produce a token with the given tag and the matched string as the
|
||||
%% value.
|
||||
-spec token(atom()) -> token_action().
|
||||
token(Tag) ->
|
||||
token(Tag, fun(X) -> X end).
|
||||
|
||||
%% @doc Produce a token with the given tag and the value computed from the
|
||||
%% matched string using the function.
|
||||
-spec token(atom(), fun((string()) -> term())) -> token_action().
|
||||
token(Tag, Fun) ->
|
||||
fun(S, P) -> {{token, {Tag, P, Fun(S)}}, none} end.
|
||||
|
||||
%% @doc Produce a token with the matched string (converted to an atom) as the
|
||||
%% tag and no value.
|
||||
-spec symbol() -> token_action().
|
||||
symbol() ->
|
||||
fun(S, P) -> {{token, {list_to_atom(S), P}}, none} end.
|
||||
|
||||
%% @doc Skip the matched string, producing no token.
|
||||
-spec skip() -> token_action().
|
||||
skip() ->
|
||||
fun(_, _) -> {skip, none} end.
|
||||
|
||||
%% @doc Enter the given state and perform the given action. The argument action
|
||||
%% should not change the state.
|
||||
-spec push(lex_state(), token_action()) -> token_action().
|
||||
push(State, Action) ->
|
||||
fun(S, P) -> {Res, _} = Action(S, P), {Res, {push, State}} end.
|
||||
|
||||
%% @doc Exit from the current state and perform the given action. The argument
|
||||
%% action should not change the state.
|
||||
-spec pop(token_action()) -> token_action().
|
||||
pop(Action) ->
|
||||
fun(S, P) -> {Res, _} = Action(S, P), {Res, pop} end.
|
||||
|
||||
%% @doc Match using the first spec, but if the second spec also matches use
|
||||
%% that one instead. Use this for overlapping tokens (like identifiers and
|
||||
%% keywords), since matching does not have longest-match semantics.
|
||||
-spec override(token_spec(), token_spec()) -> token_spec().
|
||||
override({Re1, Action1}, {Re2, Action2}) ->
|
||||
{ok, Compiled} = re:compile(["^(", Re2, ")$"]),
|
||||
{Re1, fun(S, P) ->
|
||||
case re:run(S, Compiled, [{capture, none}]) of
|
||||
match -> Action2(S, P);
|
||||
nomatch -> Action1(S, P)
|
||||
end end}.
|
||||
|
||||
%% @doc Run a lexer. Takes the starting state and the string to lex.
|
||||
-spec string(lexer(), lex_state(), string()) -> {ok, [token()]} | {error, term()}.
|
||||
string(Lexer, State, String) -> string(Lexer, [State], String, {1, 1}).
|
||||
|
||||
string(Lexer, Stack, String, Pos) ->
|
||||
Lines = string:split(String, "\n", all),
|
||||
string(Lexer, Stack, Lines, Pos, []).
|
||||
|
||||
string(_Lexers, [], [Line | _Rest], Pos, _Acc) ->
|
||||
{error, {{Line,Pos}, scan_error_no_state}};
|
||||
string(_Lexers, _Stack, [], _Pos, Acc) ->
|
||||
{ok, lists:reverse(Acc)};
|
||||
string(Lexers, [State | Stack], [Line | Lines], Pos, Acc) ->
|
||||
Lexer = proplists:get_value(State, Lexers, State),
|
||||
case Lexer(Line, Pos) of
|
||||
{ok, {Res, StateChange}, Line1, Pos1} ->
|
||||
Acc1 = case Res of
|
||||
{token, Tok} -> [Tok | Acc];
|
||||
skip -> Acc
|
||||
end,
|
||||
Stack1 = case StateChange of
|
||||
none -> [State | Stack];
|
||||
pop -> Stack;
|
||||
{push, State1} -> [State1, State | Stack]
|
||||
end,
|
||||
string(Lexers, Stack1, [Line1 | Lines], Pos1, Acc1);
|
||||
end_of_file -> string(Lexers, [State | Stack], Lines, next_pos("\n", Pos), Acc);
|
||||
error -> {error, {{Line,Pos}, scan_error}}
|
||||
end.
|
||||
|
||||
%% -- Internal functions -----------------------------------------------------
|
||||
|
||||
name(I) ->
|
||||
io_lib:format("?<A~3.10.0b>", [I]).
|
||||
|
||||
-define(TAB_SIZE, 8).
|
||||
|
||||
next_pos([], P) -> P;
|
||||
next_pos([$\n | S], {L, _}) -> next_pos(S, {L + 1, 1});
|
||||
next_pos([$\t | S], {L, C}) -> next_pos(S, {L, (C + ?TAB_SIZE - 1) div ?TAB_SIZE * ?TAB_SIZE + 1});
|
||||
next_pos([_ | S], {L, C}) -> next_pos(S, {L, C + 1}).
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @copyright (C) 2017, Aeternity Anstalt
|
||||
%%% @doc Sophia abstract syntax types.
|
||||
%%%
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(aeso_syntax).
|
||||
|
||||
-export([get_ann/1, get_ann/2, get_ann/3, set_ann/2, qualify/2]).
|
||||
|
||||
-export_type([ann_line/0, ann_col/0, ann_origin/0, ann_format/0, ann/0]).
|
||||
-export_type([name/0, id/0, con/0, qid/0, qcon/0, tvar/0, op/0]).
|
||||
-export_type([bin_op/0, un_op/0]).
|
||||
-export_type([decl/0, letbind/0, typedef/0]).
|
||||
-export_type([arg/0, field_t/0, constructor_t/0, named_arg_t/0]).
|
||||
-export_type([type/0, constant/0, expr/0, arg_expr/0, field/1, stmt/0, alt/0, lvalue/0, elim/0, pat/0]).
|
||||
-export_type([ast/0]).
|
||||
|
||||
-type ast() :: [decl()].
|
||||
|
||||
-type ann_line() :: integer().
|
||||
-type ann_col() :: integer().
|
||||
-type ann_origin() :: system | user.
|
||||
-type ann_format() :: '?:' | hex | infix | prefix | elif.
|
||||
|
||||
-type ann() :: [{line, ann_line()} | {col, ann_col()} | {format, ann_format()} | {origin, ann_origin()}].
|
||||
|
||||
-type name() :: string().
|
||||
-type id() :: {id, ann(), name()}.
|
||||
-type con() :: {con, ann(), name()}.
|
||||
-type qid() :: {qid, ann(), [name()]}.
|
||||
-type qcon() :: {qcon, ann(), [name()]}.
|
||||
-type tvar() :: {tvar, ann(), name()}.
|
||||
|
||||
-type decl() :: {contract, ann(), con(), [decl()]}
|
||||
| {namespace, ann(), con(), [decl()]}
|
||||
| {type_decl, ann(), id(), [tvar()]}
|
||||
| {type_def, ann(), id(), [tvar()], typedef()}
|
||||
| {fun_decl, ann(), id(), type()}
|
||||
| letbind().
|
||||
|
||||
-type letbind()
|
||||
:: {letval, ann(), id(), type(), expr()}
|
||||
| {letfun, ann(), id(), [arg()], type(), expr()}
|
||||
| {letrec, ann(), [letbind()]}.
|
||||
|
||||
-type arg() :: {arg, ann(), id(), type()}.
|
||||
|
||||
-type typedef()
|
||||
:: {alias_t, type()}
|
||||
| {record_t, [field_t()]}
|
||||
| {variant_t, [constructor_t()]}.
|
||||
|
||||
-type field_t() :: {field_t, ann(), id(), type()}.
|
||||
|
||||
-type constructor_t() :: {constr_t, ann(), con(), [type()]}.
|
||||
|
||||
-type type() :: {fun_t, ann(), [named_arg_t()], [type()], type()}
|
||||
| {app_t, ann(), type(), [type()]}
|
||||
| {tuple_t, ann(), [type()]}
|
||||
| {bytes_t, ann(), integer()}
|
||||
| id() | qid()
|
||||
| con() | qcon() %% contracts
|
||||
| tvar().
|
||||
|
||||
-type named_arg_t() :: {named_arg_t, ann(), id(), type(), expr()}.
|
||||
|
||||
-type constant()
|
||||
:: {int, ann(), integer()}
|
||||
| {bool, ann(), true | false}
|
||||
| {hash, ann(), binary()}
|
||||
| {account_pubkey, binary()}
|
||||
| {contract_pubkey, binary()}
|
||||
| {oracle_pubkey, binary()}
|
||||
| {oracle_query_id, binary()}
|
||||
| {unit, ann()}
|
||||
| {string, ann(), binary()}
|
||||
| {char, ann(), integer()}.
|
||||
|
||||
-type op() :: bin_op() | un_op().
|
||||
|
||||
-type bin_op() :: '+' | '-' | '*' | '/' | mod | '^'
|
||||
| '++' | '::' | '<' | '>' | '=<' | '>=' | '==' | '!='
|
||||
| '||' | '&&' | '..'.
|
||||
-type un_op() :: '-' | '!'.
|
||||
|
||||
-type expr()
|
||||
:: {lam, ann(), [arg()], expr()}
|
||||
| {'if', ann(), expr(), expr(), expr()}
|
||||
| {switch, ann(), expr(), [alt()]}
|
||||
| {app, ann(), expr(), [arg_expr()]}
|
||||
| {proj, ann(), expr(), id()}
|
||||
| {tuple, ann(), [expr()]}
|
||||
| {list, ann(), [expr()]}
|
||||
| {typed, ann(), expr(), type()}
|
||||
| {record, ann(), [field(expr())]}
|
||||
| {record, ann(), expr(), [field(expr())]} %% record update
|
||||
| {map, ann(), expr(), [field(expr())]} %% map update
|
||||
| {map, ann(), [{expr(), expr()}]}
|
||||
| {map_get, ann(), expr(), expr()}
|
||||
| {map_get, ann(), expr(), expr(), expr()}
|
||||
| {block, ann(), [stmt()]}
|
||||
| {op(), ann()}
|
||||
| id() | qid() | con() | qcon()
|
||||
| constant().
|
||||
|
||||
-type arg_expr() :: expr() | {named_arg, ann(), id(), expr()}.
|
||||
|
||||
%% When lvalue is a projection this is sugar for accessing fields in nested
|
||||
%% records. For instance,
|
||||
%% r { x.y: 5 }
|
||||
%% is the same as
|
||||
%% r { x: r.x { y: 5 } }
|
||||
-type field(E) :: {field, ann(), lvalue(), E}
|
||||
| {field, ann(), lvalue(), id(), E}. %% modifying a field (id is previous value)
|
||||
|
||||
-type stmt() :: letbind()
|
||||
| expr().
|
||||
|
||||
-type alt() :: {'case', ann(), pat(), expr()}.
|
||||
|
||||
-type lvalue() :: nonempty_list(elim()).
|
||||
|
||||
-type elim() :: {proj, ann(), id()}
|
||||
| {map_get, ann(), expr()}
|
||||
| {map_get, ann(), expr(), expr()}.
|
||||
|
||||
-type pat() :: {app, ann(), con() | op(), [pat()]}
|
||||
| {tuple, ann(), [pat()]}
|
||||
| {list, ann(), [pat()]}
|
||||
| {record, ann(), [field(pat())]}
|
||||
| constant()
|
||||
| con()
|
||||
| id().
|
||||
|
||||
get_ann(Node) when is_tuple(Node) -> element(2, Node);
|
||||
get_ann(Ann) when is_list(Ann) -> Ann.
|
||||
|
||||
set_ann(Ann1, Node) when is_tuple(Node) -> setelement(2, Node, Ann1);
|
||||
set_ann(Ann1, Ann) when is_list(Ann) -> Ann1.
|
||||
|
||||
get_ann(Key, Node) ->
|
||||
proplists:get_value(Key, get_ann(Node)).
|
||||
|
||||
get_ann(Key, Node, Default) ->
|
||||
proplists:get_value(Key, get_ann(Node), Default).
|
||||
|
||||
qualify({con, Ann, N}, X) -> qualify({qcon, Ann, [N]}, X);
|
||||
qualify({qcon, _, NS}, {con, Ann, C}) -> {qcon, Ann, NS ++ [C]};
|
||||
qualify({qcon, _, NS}, {id, Ann, X}) -> {qid, Ann, NS ++ [X]}.
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @copyright (C) 2018, Aeternity Anstalt
|
||||
%%% @doc
|
||||
%%% Sophia syntax utilities.
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeso_syntax_utils).
|
||||
|
||||
-export([used_ids/1, used_types/1, used/1]).
|
||||
|
||||
-record(alg, {zero, plus, scoped}).
|
||||
|
||||
-type alg(A) :: #alg{ zero :: A
|
||||
, plus :: fun((A, A) -> A)
|
||||
, scoped :: fun((A, A) -> A) }.
|
||||
|
||||
-type kind() :: decl | type | bind_type | expr | bind_expr.
|
||||
|
||||
-spec fold(alg(A), fun((kind(), _) -> A), kind(), E | [E]) -> A
|
||||
when E :: aeso_syntax:decl()
|
||||
| aeso_syntax:typedef()
|
||||
| aeso_syntax:field_t()
|
||||
| aeso_syntax:constructor_t()
|
||||
| aeso_syntax:type()
|
||||
| aeso_syntax:expr()
|
||||
| aeso_syntax:pat()
|
||||
| aeso_syntax:arg()
|
||||
| aeso_syntax:alt()
|
||||
| aeso_syntax:elim()
|
||||
| aeso_syntax:arg_expr()
|
||||
| aeso_syntax:field(aeso_syntax:expr())
|
||||
| aeso_syntax:stmt().
|
||||
fold(Alg = #alg{zero = Zero, plus = Plus, scoped = Scoped}, Fun, K, X) ->
|
||||
Sum = fun(Xs) -> lists:foldl(Plus, Zero, Xs) end,
|
||||
Same = fun(A) -> fold(Alg, Fun, K, A) end,
|
||||
Decl = fun(D) -> fold(Alg, Fun, decl, D) end,
|
||||
Type = fun(T) -> fold(Alg, Fun, type, T) end,
|
||||
Expr = fun(E) -> fold(Alg, Fun, expr, E) end,
|
||||
BindExpr = fun(P) -> fold(Alg, Fun, bind_expr, P) end,
|
||||
BindType = fun(T) -> fold(Alg, Fun, bind_type, T) end,
|
||||
Top = Fun(K, X),
|
||||
Bound = fun LB ({letval, _, Y, _, _}) -> BindExpr(Y);
|
||||
LB ({letfun, _, F, _, _, _}) -> BindExpr(F);
|
||||
LB ({letrec, _, Ds}) -> Sum(lists:map(LB, Ds));
|
||||
LB (_) -> Zero
|
||||
end,
|
||||
Rec = case X of
|
||||
%% lists (bound things in head scope over tail)
|
||||
[A | As] -> Scoped(Same(A), Same(As));
|
||||
%% decl()
|
||||
{contract, _, _, Ds} -> Decl(Ds);
|
||||
{namespace, _, _, Ds} -> Decl(Ds);
|
||||
{type_decl, _, I, _} -> BindType(I);
|
||||
{type_def, _, I, _, D} -> Plus(BindType(I), Decl(D));
|
||||
{fun_decl, _, _, T} -> Type(T);
|
||||
{letval, _, F, T, E} -> Sum([BindExpr(F), Type(T), Expr(E)]);
|
||||
{letfun, _, F, Xs, T, E} -> Sum([BindExpr(F), Type(T), Scoped(BindExpr(Xs), Expr(E))]);
|
||||
{letrec, _, Ds} -> Plus(Bound(Ds), Decl(Ds));
|
||||
%% typedef()
|
||||
{alias_t, T} -> Type(T);
|
||||
{record_t, Fs} -> Type(Fs);
|
||||
{variant_t, Cs} -> Type(Cs);
|
||||
%% field_t() and constructor_t()
|
||||
{field_t, _, _, T} -> Type(T);
|
||||
{constr_t, _, _, Ts} -> Type(Ts);
|
||||
%% type()
|
||||
{fun_t, _, Named, Args, Ret} -> Type([Named, Args, Ret]);
|
||||
{app_t, _, T, Ts} -> Type([T | Ts]);
|
||||
{tuple_t, _, Ts} -> Type(Ts);
|
||||
%% named_arg_t()
|
||||
{named_arg_t, _, _, T, E} -> Plus(Type(T), Expr(E));
|
||||
%% expr()
|
||||
{lam, _, Args, E} -> Scoped(BindExpr(Args), Expr(E));
|
||||
{'if', _, A, B, C} -> Expr([A, B, C]);
|
||||
{switch, _, E, Alts} -> Expr([E, Alts]);
|
||||
{app, _, A, As} -> Expr([A | As]);
|
||||
{proj, _, E, _} -> Expr(E);
|
||||
{tuple, _, As} -> Expr(As);
|
||||
{list, _, As} -> Expr(As);
|
||||
{typed, _, E, T} -> Plus(Expr(E), Type(T));
|
||||
{record, _, Fs} -> Expr(Fs);
|
||||
{record, _, E, Fs} -> Expr([E | Fs]);
|
||||
{map, _, E, Fs} -> Expr([E | Fs]);
|
||||
{map, _, KVs} -> Sum([Expr([Key, Val]) || {Key, Val} <- KVs]);
|
||||
{map_get, _, A, B} -> Expr([A, B]);
|
||||
{map_get, _, A, B, C} -> Expr([A, B, C]);
|
||||
{block, _, Ss} -> Expr(Ss);
|
||||
%% field()
|
||||
{field, _, LV, E} -> Expr([LV, E]);
|
||||
{field, _, LV, _, E} -> Expr([LV, E]);
|
||||
%% arg()
|
||||
{arg, _, X, T} -> Plus(Expr(X), Type(T));
|
||||
%% alt()
|
||||
{'case', _, P, E} -> Scoped(BindExpr(P), Expr(E));
|
||||
%% elim()
|
||||
{proj, _, _} -> Zero;
|
||||
{map_get, _, E} -> Expr(E);
|
||||
%% arg_expr()
|
||||
{named_arg, _, _, E} -> Expr(E);
|
||||
_ -> Alg#alg.zero
|
||||
end,
|
||||
(Alg#alg.plus)(Top, Rec).
|
||||
|
||||
%% Name dependencies
|
||||
|
||||
used_ids(E) ->
|
||||
[ X || {term, [X]} <- used(E) ].
|
||||
|
||||
used_types(T) ->
|
||||
[ X || {type, [X]} <- used(T) ].
|
||||
|
||||
-type entity() :: {term, [string()]}
|
||||
| {type, [string()]}
|
||||
| {namespace, [string()]}.
|
||||
|
||||
-spec entity_alg() -> alg([entity()]).
|
||||
entity_alg() ->
|
||||
IsBound = fun({K, _}) -> lists:member(K, [bound_term, bound_type]) end,
|
||||
Unbind = fun(bound_term) -> term; (bound_type) -> type end,
|
||||
Scoped = fun(Xs, Ys) ->
|
||||
{Bound, Others} = lists:partition(IsBound, Ys),
|
||||
Bound1 = [ {Unbind(Tag), X} || {Tag, X} <- Bound ],
|
||||
lists:umerge(Xs -- Bound1, Others)
|
||||
end,
|
||||
#alg{ zero = []
|
||||
, plus = fun lists:umerge/2
|
||||
, scoped = Scoped }.
|
||||
|
||||
-spec used(_) -> [entity()].
|
||||
used(D) ->
|
||||
Kind = fun(expr) -> term;
|
||||
(bind_expr) -> bound_term;
|
||||
(type) -> type;
|
||||
(bind_type) -> bound_type
|
||||
end,
|
||||
NS = fun(Xs) -> {namespace, lists:droplast(Xs)} end,
|
||||
NotBound = fun({Tag, _}) -> not lists:member(Tag, [bound_term, bound_type]) end,
|
||||
Xs =
|
||||
fold(entity_alg(),
|
||||
fun(K, {id, _, X}) -> [{Kind(K), [X]}];
|
||||
(K, {qid, _, Xs}) -> [{Kind(K), Xs}, NS(Xs)];
|
||||
(K, {con, _, X}) -> [{Kind(K), [X]}];
|
||||
(K, {qcon, _, Xs}) -> [{Kind(K), Xs}, NS(Xs)];
|
||||
(_, _) -> []
|
||||
end, decl, D),
|
||||
lists:filter(NotBound, Xs).
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @copyright (C) 2018, Aeternity Anstalt
|
||||
%%% @doc
|
||||
%%% Sophia utility functions.
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeso_utils).
|
||||
|
||||
-export([scc/1]).
|
||||
|
||||
-export_type([graph/1]).
|
||||
|
||||
%% -- Topological sort
|
||||
|
||||
-type graph(Node) :: #{Node => [Node]}. %% List of incoming edges (dependencies).
|
||||
|
||||
%% Topologically sorted strongly-connected components of a graph.
|
||||
-spec scc(graph(Node)) -> [{cyclic, [Node]} | {acyclic, Node}].
|
||||
scc(Graph) ->
|
||||
Trees = dfs(Graph, lists:reverse(postorder(dff(reverse_graph(Graph))))),
|
||||
Decode = fun(T) ->
|
||||
case postorder(T) of
|
||||
[I] -> case lists:member(I, maps:get(I, Graph, [])) of
|
||||
true -> {cyclic, [I]};
|
||||
false -> {acyclic, I}
|
||||
end;
|
||||
Is -> {cyclic, Is}
|
||||
end end,
|
||||
lists:map(Decode, Trees).
|
||||
|
||||
%% Depth first spanning forest of a graph.
|
||||
dff(Graph) ->
|
||||
dfs(Graph, maps:keys(Graph)).
|
||||
|
||||
dfs(Graph, Vs) ->
|
||||
{_, Trees} = dfs(Graph, #{}, Vs, []),
|
||||
Trees.
|
||||
|
||||
dfs(_Graph, Visited, [], Trees) -> {Visited, lists:reverse(Trees)};
|
||||
dfs(Graph, Visited, [V | Vs], Trees) ->
|
||||
case maps:is_key(V, Visited) of
|
||||
true -> dfs(Graph, Visited, Vs, Trees);
|
||||
false ->
|
||||
{Visited1, Tree} = dfs1(Graph, Visited#{ V => true }, V),
|
||||
dfs(Graph, Visited1, Vs, [Tree | Trees])
|
||||
end.
|
||||
|
||||
dfs1(Graph, Visited, V) ->
|
||||
Ws = maps:get(V, Graph, []),
|
||||
{Visited1, Trees} = dfs(Graph, Visited, Ws, []),
|
||||
{Visited1, {V, Trees}}.
|
||||
|
||||
%% Post-order traversal of a tree/forest.
|
||||
postorder(Tree = {_, _}) -> postorder([Tree]);
|
||||
postorder(Trees) when is_list(Trees) -> postorder(Trees, []).
|
||||
|
||||
postorder([], Acc) -> Acc;
|
||||
postorder([{V, Trees1} | Trees], Acc) ->
|
||||
postorder(Trees1, [V | postorder(Trees, Acc)]).
|
||||
|
||||
from_edges(Is, Es) ->
|
||||
lists:foldl(fun({I, J}, G) ->
|
||||
maps:update_with(I, fun(Js) -> lists:umerge([J], Js) end, [J], G)
|
||||
end, maps:from_list([ {I, []} || I <- Is ]), Es).
|
||||
|
||||
reverse_graph(G) ->
|
||||
from_edges(maps:keys(G), [ {J, I} || {I, Js} <- maps:to_list(G), J <- Js ]).
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
{application, aesophia,
|
||||
[{description, "Contract Language for aeternity"},
|
||||
{vsn, "2.1.0"},
|
||||
{registered, []},
|
||||
{applications,
|
||||
[kernel,
|
||||
stdlib,
|
||||
jsx,
|
||||
syntax_tools,
|
||||
getopt,
|
||||
aebytecode
|
||||
]},
|
||||
{env,[]},
|
||||
{modules, []},
|
||||
|
||||
{maintainers, []},
|
||||
{licenses, []},
|
||||
{links, []}
|
||||
]}.
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
-module(aesophia).
|
||||
|
||||
-export([main/1]).
|
||||
|
||||
-define(OPT_SPEC,
|
||||
[ {src_file, undefined, undefined, string, "Sophia source code file"}
|
||||
, {version, $V, "version", undefined, "Print compiler version"}
|
||||
, {verbose, $v, "verbose", undefined, "Verbose output"}
|
||||
, {help, $h, "help", undefined, "Show this message"}
|
||||
, {outfile, $o, "out", string, "Output file (experimental)"} ]).
|
||||
|
||||
usage() ->
|
||||
getopt:usage(?OPT_SPEC, "aesophia").
|
||||
|
||||
main(Args) ->
|
||||
case getopt:parse(?OPT_SPEC, Args) of
|
||||
{ok, {Opts, []}} ->
|
||||
case Opts of
|
||||
[version] ->
|
||||
print_vsn();
|
||||
[help] ->
|
||||
usage();
|
||||
_ ->
|
||||
compile(Opts)
|
||||
end;
|
||||
|
||||
{ok, {_, NonOpts}} ->
|
||||
io:format("Can't understand ~p\n\n", [NonOpts]),
|
||||
usage();
|
||||
|
||||
{error, {Reason, Data}} ->
|
||||
io:format("Error: ~s ~p\n\n", [Reason, Data]),
|
||||
usage()
|
||||
end.
|
||||
|
||||
|
||||
compile(Opts) ->
|
||||
case proplists:get_value(src_file, Opts, undefined) of
|
||||
undefined ->
|
||||
io:format("Error: no input source file\n\n"),
|
||||
usage();
|
||||
File ->
|
||||
compile(File, Opts)
|
||||
end.
|
||||
|
||||
compile(File, Opts) ->
|
||||
Verbose = proplists:get_value(verbose, Opts, false),
|
||||
OutFile = proplists:get_value(outfile, Opts, undefined),
|
||||
|
||||
try
|
||||
Res = aeso_compiler:file(File, [pp_ast || Verbose]),
|
||||
write_outfile(OutFile, Res),
|
||||
io:format("\nCompiled successfully!\n")
|
||||
catch
|
||||
%% The compiler errors.
|
||||
error:{type_errors, Errors} ->
|
||||
io:format("\n~s\n", [string:join(["** Type errors\n" | Errors], "\n")]);
|
||||
error:{parse_errors, Errors} ->
|
||||
io:format("\n~s\n", [string:join(["** Parse errors\n" | Errors], "\n")]);
|
||||
error:{code_errors, Errors} ->
|
||||
ErrorStrings = [ io_lib:format("~p", [E]) || E <- Errors ],
|
||||
io:format("\n~s\n", [string:join(["** Code errors\n" | ErrorStrings], "\n")]);
|
||||
%% General programming errors in the compiler.
|
||||
error:Error ->
|
||||
Where = hd(erlang:get_stacktrace()),
|
||||
ErrorString = io_lib:format("Error: ~p in\n ~p", [Error,Where]),
|
||||
io:format("\n~s\n", [ErrorString])
|
||||
end.
|
||||
|
||||
write_outfile(undefined, _) -> ok;
|
||||
write_outfile(Out, ResMap) ->
|
||||
%% Lazy approach
|
||||
file:write_file(Out, term_to_binary(ResMap)),
|
||||
io:format("Output written to: ~s\n", [Out]).
|
||||
|
||||
print_vsn() ->
|
||||
{ok, Vsn} = aeso_compiler:version(),
|
||||
io:format("Compiler version: ~s\n", [Vsn]).
|
||||
@@ -0,0 +1,199 @@
|
||||
-module(aeso_abi_tests).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-compile(export_all).
|
||||
|
||||
-define(SANDBOX(Code), sandbox(fun() -> Code end)).
|
||||
-define(DUMMY_HASH_WORD, 16#123).
|
||||
-define(DUMMY_HASH, <<0:30/unit:8, 127, 119>>). %% 16#123
|
||||
-define(DUMMY_HASH_LIT, "#0000000000000000000000000000000000000000000000000000000000000123").
|
||||
|
||||
sandbox(Code) ->
|
||||
Parent = self(),
|
||||
Tag = make_ref(),
|
||||
{Pid, Ref} = spawn_monitor(fun() -> Parent ! {Tag, Code()} end),
|
||||
receive
|
||||
{Tag, Res} -> erlang:demonitor(Ref, [flush]), {ok, Res};
|
||||
{'DOWN', Ref, process, Pid, Reason} -> {error, Reason}
|
||||
after 100 ->
|
||||
exit(Pid, kill),
|
||||
{error, loop}
|
||||
end.
|
||||
|
||||
malicious_from_binary_test() ->
|
||||
CircularList = from_words([32, 1, 32]), %% Xs = 1 :: Xs
|
||||
{ok, {error, circular_references}} = ?SANDBOX(aeb_heap:from_binary({list, word}, CircularList)),
|
||||
{ok, {error, {binary_too_short, _}}} = ?SANDBOX(aeb_heap:from_binary(word, <<1, 2, 3, 4>>)),
|
||||
ok.
|
||||
|
||||
from_words(Ws) ->
|
||||
<< <<(from_word(W))/binary>> || W <- Ws >>.
|
||||
|
||||
from_word(W) when is_integer(W) ->
|
||||
<<W:256>>;
|
||||
from_word(S) when is_list(S) ->
|
||||
Len = length(S),
|
||||
Bin = <<(list_to_binary(S))/binary, 0:(32 - Len)/unit:8>>,
|
||||
<<Len:256, Bin/binary>>.
|
||||
|
||||
encode_decode_test() ->
|
||||
encode_decode(word, 42),
|
||||
42 = encode_decode(word, 42),
|
||||
-1 = encode_decode(signed_word, -1),
|
||||
<<"Hello world">> = encode_decode(string, <<"Hello world">>),
|
||||
{} = encode_decode({tuple, []}, {}),
|
||||
{42} = encode_decode({tuple, [word]}, {42}),
|
||||
{42, 0} = encode_decode({tuple, [word, word]}, {42, 0}),
|
||||
[] = encode_decode({list, word}, []),
|
||||
[32] = encode_decode({list, word}, [32]),
|
||||
none = encode_decode({option, word}, none),
|
||||
{some, 1} = encode_decode({option, word}, {some, 1}),
|
||||
string = encode_decode(typerep, string),
|
||||
word = encode_decode(typerep, word),
|
||||
{list, word} = encode_decode(typerep, {list, word}),
|
||||
{tuple, [word]} = encode_decode(typerep, {tuple, [word]}),
|
||||
1 = encode_decode(word, 1),
|
||||
0 = encode_decode(word, 0),
|
||||
ok.
|
||||
|
||||
encode_decode_sophia_test() ->
|
||||
Check = fun(Type, Str) -> case {encode_decode_sophia_string(Type, Str), Str} of
|
||||
{X, X} -> ok;
|
||||
Other -> Other
|
||||
end end,
|
||||
ok = Check("int", "42"),
|
||||
ok = Check("bool", "true"),
|
||||
ok = Check("bool", "false"),
|
||||
ok = Check("string", "\"Hello\""),
|
||||
ok = Check("(string, list(int), option(bool))",
|
||||
"(\"Hello\", [1, 2, 3], Some(true))"),
|
||||
ok = Check("variant", "Blue({[\"x\"] = 1})"),
|
||||
ok = Check("r", "{x = (\"foo\", 0), y = Red}"),
|
||||
ok.
|
||||
|
||||
encode_decode_sophia_string(SophiaType, String) ->
|
||||
io:format("String ~p~n", [String]),
|
||||
Code = [ "contract MakeCall =\n"
|
||||
, " type arg_type = ", SophiaType, "\n"
|
||||
, " type an_alias('a) = (string, 'a)\n"
|
||||
, " record r = {x : an_alias(int), y : variant}\n"
|
||||
, " datatype variant = Red | Blue(map(string, int))\n"
|
||||
, " function foo : arg_type => arg_type\n" ],
|
||||
case aeso_compiler:check_call(lists:flatten(Code), "foo", [String], []) of
|
||||
{ok, _, {[Type], _}, [Arg]} ->
|
||||
io:format("Type ~p~n", [Type]),
|
||||
Data = encode(Arg),
|
||||
case aeso_compiler:to_sophia_value(Code, "foo", ok, Data) of
|
||||
{ok, Sophia} ->
|
||||
lists:flatten(io_lib:format("~s", [prettypr:format(aeso_pretty:expr(Sophia))]));
|
||||
{error, Err} ->
|
||||
io:format("~s\n", [Err]),
|
||||
{error, Err}
|
||||
end;
|
||||
{error, Err} ->
|
||||
io:format("~s\n", [Err]),
|
||||
{error, Err}
|
||||
end.
|
||||
|
||||
calldata_test() ->
|
||||
[42, <<"foobar">>] = encode_decode_calldata("foo", ["int", "string"], ["42", "\"foobar\""]),
|
||||
Map = #{ <<"a">> => 4 },
|
||||
[{variant, 1, [Map]}, {{<<"b">>, 5}, {variant, 0, []}}] =
|
||||
encode_decode_calldata("foo", ["variant", "r"], ["Blue({[\"a\"] = 4})", "{x = (\"b\", 5), y = Red}"]),
|
||||
[?DUMMY_HASH_WORD, 16#456] = encode_decode_calldata("foo", ["bytes(32)", "address"],
|
||||
[?DUMMY_HASH_LIT, "ak_1111111111111111111111111111113AFEFpt5"]),
|
||||
[?DUMMY_HASH_WORD, ?DUMMY_HASH_WORD] =
|
||||
encode_decode_calldata("foo", ["bytes(32)", "hash"], [?DUMMY_HASH_LIT, ?DUMMY_HASH_LIT]),
|
||||
|
||||
[119, {0, 0}] = encode_decode_calldata("foo", ["int", "signature"], ["119", [$# | lists:duplicate(128, $0)]]),
|
||||
|
||||
[16#456] = encode_decode_calldata("foo", ["Remote"], ["ct_1111111111111111111111111111113AFEFpt5"]),
|
||||
|
||||
ok.
|
||||
|
||||
calldata_init_test() ->
|
||||
encode_decode_calldata("init", ["int"], ["42"], {tuple, [typerep, word]}),
|
||||
|
||||
Code = parameterized_contract("foo", ["int"]),
|
||||
encode_decode_calldata_(Code, "init", [], {tuple, [typerep, {tuple, []}]}).
|
||||
|
||||
calldata_indent_test() ->
|
||||
Test = fun(Extra) ->
|
||||
encode_decode_calldata_(
|
||||
parameterized_contract(Extra, "foo", ["int"]),
|
||||
"foo", ["42"], word)
|
||||
end,
|
||||
Test(" stateful function bla() = ()"),
|
||||
Test(" type x = int"),
|
||||
Test(" private function bla : int => int"),
|
||||
Test(" public stateful function bla(x : int) =\n"
|
||||
" x + 1"),
|
||||
Test(" stateful private function bla(x : int) : int =\n"
|
||||
" x + 1"),
|
||||
ok.
|
||||
|
||||
parameterized_contract(FunName, Types) ->
|
||||
parameterized_contract([], FunName, Types).
|
||||
|
||||
parameterized_contract(ExtraCode, FunName, Types) ->
|
||||
lists:flatten(
|
||||
["contract Remote =\n"
|
||||
" function bla : () => ()\n\n"
|
||||
"contract Dummy =\n",
|
||||
ExtraCode, "\n",
|
||||
" type an_alias('a) = (string, 'a)\n"
|
||||
" record r = {x : an_alias(int), y : variant}\n"
|
||||
" datatype variant = Red | Blue(map(string, int))\n"
|
||||
" function ", FunName, " : (", string:join(Types, ", "), ") => int\n" ]).
|
||||
|
||||
oracle_test() ->
|
||||
Contract =
|
||||
"contract OracleTest =\n"
|
||||
" function question(o, q : oracle_query(list(string), option(int))) =\n"
|
||||
" Oracle.get_question(o, q)\n",
|
||||
{ok, _, {[word, word], {list, string}}, [16#123, 16#456]} =
|
||||
aeso_compiler:check_call(Contract, "question", ["ok_111111111111111111111111111111ZrdqRz9",
|
||||
"oq_1111111111111111111111111111113AFEFpt5"], []),
|
||||
|
||||
ok.
|
||||
|
||||
permissive_literals_fail_test() ->
|
||||
Contract =
|
||||
"contract OracleTest =\n"
|
||||
" function haxx(o : oracle(list(string), option(int))) =\n"
|
||||
" Chain.spend(o, 1000000)\n",
|
||||
{error, <<"Type errors\nCannot unify", _/binary>>} =
|
||||
aeso_compiler:check_call(Contract, "haxx", ["#123"], []),
|
||||
ok.
|
||||
|
||||
encode_decode_calldata(FunName, Types, Args) ->
|
||||
encode_decode_calldata(FunName, Types, Args, word).
|
||||
|
||||
encode_decode_calldata(FunName, Types, Args, RetType) ->
|
||||
Code = parameterized_contract(FunName, Types),
|
||||
encode_decode_calldata_(Code, FunName, Args, RetType).
|
||||
|
||||
encode_decode_calldata_(Code, FunName, Args, RetVMType) ->
|
||||
{ok, Calldata, CalldataType, RetVMType1} = aeso_compiler:create_calldata(Code, FunName, Args),
|
||||
?assertEqual(RetVMType1, RetVMType),
|
||||
{ok, {_Hash, ArgTuple}} = aeb_heap:from_binary(CalldataType, Calldata),
|
||||
case FunName of
|
||||
"init" ->
|
||||
ok;
|
||||
_ ->
|
||||
{ok, _ArgTypes, ValueASTs} = aeso_compiler:decode_calldata(Code, FunName, Calldata),
|
||||
Values = [ prettypr:format(aeso_pretty:expr(V)) || V <- ValueASTs ],
|
||||
?assertMatch({X, X}, {Args, Values})
|
||||
end,
|
||||
tuple_to_list(ArgTuple).
|
||||
|
||||
encode_decode(T, D) ->
|
||||
?assertEqual(D, decode(T, encode(D))),
|
||||
D.
|
||||
|
||||
encode(D) ->
|
||||
aeb_heap:to_binary(D).
|
||||
|
||||
decode(T,B) ->
|
||||
{ok, D} = aeb_heap:from_binary(T, B),
|
||||
D.
|
||||
@@ -0,0 +1,76 @@
|
||||
-module(aeso_aci_tests).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
|
||||
do_test() ->
|
||||
test_contract(1),
|
||||
test_contract(2),
|
||||
test_contract(3).
|
||||
|
||||
test_contract(N) ->
|
||||
{Contract,MapACI,DecACI} = test_cases(N),
|
||||
{ok,JSON} = aeso_aci:encode(Contract),
|
||||
?assertEqual(MapACI, jsx:decode(JSON, [return_maps])),
|
||||
?assertEqual(DecACI, aeso_aci:decode(JSON)).
|
||||
|
||||
test_cases(1) ->
|
||||
Contract = <<"contract C =\n"
|
||||
" function a(i : int) = i+1\n">>,
|
||||
MapACI = #{<<"contract">> =>
|
||||
#{<<"name">> => <<"C">>,
|
||||
<<"type_defs">> => [],
|
||||
<<"functions">> =>
|
||||
[#{<<"name">> => <<"a">>,
|
||||
<<"arguments">> =>
|
||||
[#{<<"name">> => <<"i">>,
|
||||
<<"type">> => <<"int">>}],
|
||||
<<"returns">> => <<"int">>,
|
||||
<<"stateful">> => false}]}},
|
||||
DecACI = <<"contract C =\n"
|
||||
" function a : (int) => int\n">>,
|
||||
{Contract,MapACI,DecACI};
|
||||
|
||||
test_cases(2) ->
|
||||
Contract = <<"contract C =\n"
|
||||
" type allan = int\n"
|
||||
" function a(i : allan) = i+1\n">>,
|
||||
MapACI = #{<<"contract">> =>
|
||||
#{<<"name">> => <<"C">>,
|
||||
<<"type_defs">> =>
|
||||
[#{<<"name">> => <<"allan">>,
|
||||
<<"typedef">> => <<"int">>,
|
||||
<<"vars">> => []}],
|
||||
<<"functions">> =>
|
||||
[#{<<"arguments">> =>
|
||||
[#{<<"name">> => <<"i">>,
|
||||
<<"type">> => <<"int">>}],
|
||||
<<"name">> => <<"a">>,
|
||||
<<"returns">> => <<"int">>,
|
||||
<<"stateful">> => false}]}},
|
||||
DecACI = <<"contract C =\n"
|
||||
" function a : (int) => int\n">>,
|
||||
{Contract,MapACI,DecACI};
|
||||
test_cases(3) ->
|
||||
Contract = <<"contract C =\n"
|
||||
" datatype bert('a) = Bin('a)\n"
|
||||
" function a(i : bert(string)) = 1\n">>,
|
||||
MapACI = #{<<"contract">> =>
|
||||
#{<<"functions">> =>
|
||||
[#{<<"arguments">> =>
|
||||
[#{<<"name">> => <<"i">>,
|
||||
<<"type">> =>
|
||||
#{<<"C.bert">> => [<<"string">>]}}],
|
||||
<<"name">> => <<"a">>,<<"returns">> => <<"int">>,
|
||||
<<"stateful">> => false}],
|
||||
<<"name">> => <<"C">>,
|
||||
<<"type_defs">> =>
|
||||
[#{<<"name">> => <<"bert">>,
|
||||
<<"typedef">> =>
|
||||
#{<<"variant">> =>
|
||||
[#{<<"Bin">> => [<<"'a">>]}]},
|
||||
<<"vars">> => [#{<<"name">> => <<"'a">>}]}]}},
|
||||
DecACI = <<"contract C =\n"
|
||||
" datatype bert('a) = Bin('a)\n"
|
||||
" function a : (C.bert(string)) => int\n">>,
|
||||
{Contract,MapACI,DecACI}.
|
||||
@@ -0,0 +1,305 @@
|
||||
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @copyright (C) 2018, Aeternity Anstalt
|
||||
%%% @doc Test Sophia language compiler.
|
||||
%%%
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(aeso_compiler_tests).
|
||||
|
||||
-compile([export_all, nowarn_export_all]).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
%% Very simply test compile the given contracts. Only basic checks
|
||||
%% are made on the output, just that it is a binary which indicates
|
||||
%% that the compilation worked.
|
||||
simple_compile_test_() ->
|
||||
[ {"Testing the " ++ ContractName ++ " contract",
|
||||
fun() ->
|
||||
case compile(ContractName) of
|
||||
#{byte_code := ByteCode,
|
||||
contract_source := _,
|
||||
type_info := _} -> ?assertMatch(Code when is_binary(Code), ByteCode);
|
||||
ErrBin ->
|
||||
io:format("\n~s", [ErrBin]),
|
||||
error(ErrBin)
|
||||
end
|
||||
end} || ContractName <- compilable_contracts() ] ++
|
||||
[ {"Testing error messages of " ++ ContractName,
|
||||
fun() ->
|
||||
case compile(ContractName) of
|
||||
<<"Type errors\n", ErrorString/binary>> ->
|
||||
check_errors(lists:sort(ExpectedErrors), ErrorString);
|
||||
<<"Parse errors\n", ErrorString/binary>> ->
|
||||
check_errors(lists:sort(ExpectedErrors), ErrorString)
|
||||
end
|
||||
end} ||
|
||||
{ContractName, ExpectedErrors} <- failing_contracts() ] ++
|
||||
[ {"Testing include with explicit files",
|
||||
fun() ->
|
||||
FileSystem = maps:from_list(
|
||||
[ begin
|
||||
{ok, Bin} = file:read_file(filename:join([aeso_test_utils:contract_path(), File])),
|
||||
{File, Bin}
|
||||
end || File <- ["included.aes", "../contracts/included2.aes"] ]),
|
||||
#{byte_code := Code1} = compile("include", [{include, {explicit_files, FileSystem}}]),
|
||||
#{byte_code := Code2} = compile("include"),
|
||||
?assertMatch(true, Code1 == Code2)
|
||||
end} ] ++
|
||||
[ {"Testing deadcode elimination",
|
||||
fun() ->
|
||||
#{ byte_code := NoDeadCode } = compile("nodeadcode"),
|
||||
#{ byte_code := DeadCode } = compile("deadcode"),
|
||||
SizeNoDeadCode = byte_size(NoDeadCode),
|
||||
SizeDeadCode = byte_size(DeadCode),
|
||||
?assertMatch({_, _, true}, {SizeDeadCode, SizeNoDeadCode, SizeDeadCode + 40 < SizeNoDeadCode}),
|
||||
ok
|
||||
end} ].
|
||||
|
||||
check_errors(Expect, ErrorString) ->
|
||||
%% This removes the final single \n as well.
|
||||
Actual = binary:split(<<ErrorString/binary,$\n>>, <<"\n\n">>, [global,trim]),
|
||||
case {Expect -- Actual, Actual -- Expect} of
|
||||
{[], Extra} -> ?assertMatch({unexpected, []}, {unexpected, Extra});
|
||||
{Missing, []} -> ?assertMatch({missing, []}, {missing, Missing});
|
||||
{Missing, Extra} -> ?assertEqual(Missing, Extra)
|
||||
end.
|
||||
|
||||
compile(Name) ->
|
||||
compile(Name, [{include, {file_system, [aeso_test_utils:contract_path()]}}]).
|
||||
|
||||
compile(Name, Options) ->
|
||||
String = aeso_test_utils:read_contract(Name),
|
||||
case aeso_compiler:from_string(String, [{src_file, Name} | Options]) of
|
||||
{ok, Map} -> Map;
|
||||
{error, ErrorString} -> ErrorString
|
||||
end.
|
||||
|
||||
%% compilable_contracts() -> [ContractName].
|
||||
%% The currently compilable contracts.
|
||||
|
||||
compilable_contracts() ->
|
||||
["complex_types",
|
||||
"counter",
|
||||
"dutch_auction",
|
||||
"environment",
|
||||
"factorial",
|
||||
"fundme",
|
||||
"identity",
|
||||
"maps",
|
||||
"oracles",
|
||||
"remote_call",
|
||||
"simple",
|
||||
"simple_storage",
|
||||
"spend_test",
|
||||
"stack",
|
||||
"test",
|
||||
"builtin_bug",
|
||||
"builtin_map_get_bug",
|
||||
"nodeadcode",
|
||||
"deadcode",
|
||||
"variant_types",
|
||||
"state_handling",
|
||||
"events",
|
||||
"include",
|
||||
"basic_auth",
|
||||
"bitcoin_auth",
|
||||
"address_literals"
|
||||
].
|
||||
|
||||
%% Contracts that should produce type errors
|
||||
|
||||
failing_contracts() ->
|
||||
[ {"name_clash",
|
||||
[<<"Duplicate definitions of abort at\n"
|
||||
" - (builtin location)\n"
|
||||
" - line 14, column 3">>,
|
||||
<<"Duplicate definitions of double_def at\n"
|
||||
" - line 10, column 3\n"
|
||||
" - line 11, column 3">>,
|
||||
<<"Duplicate definitions of double_proto at\n"
|
||||
" - line 4, column 3\n"
|
||||
" - line 5, column 3">>,
|
||||
<<"Duplicate definitions of proto_and_def at\n"
|
||||
" - line 7, column 3\n"
|
||||
" - line 8, column 3">>,
|
||||
<<"Duplicate definitions of put at\n"
|
||||
" - (builtin location)\n"
|
||||
" - line 15, column 3">>,
|
||||
<<"Duplicate definitions of state at\n"
|
||||
" - (builtin location)\n"
|
||||
" - line 16, column 3">>]}
|
||||
, {"type_errors",
|
||||
[<<"Unbound variable zz at line 17, column 21">>,
|
||||
<<"Cannot unify int\n"
|
||||
" and list(int)\n"
|
||||
"when checking the application at line 26, column 9 of\n"
|
||||
" (::) : (int, list(int)) => list(int)\n"
|
||||
"to arguments\n"
|
||||
" x : int\n"
|
||||
" x : int">>,
|
||||
<<"Cannot unify string\n"
|
||||
" and int\n"
|
||||
"when checking the assignment of the field\n"
|
||||
" x : map(string, string) (at line 9, column 46)\n"
|
||||
"to the old value __x and the new value\n"
|
||||
" __x {[\"foo\"] @ x = x + 1} : map(string, int)">>,
|
||||
<<"Cannot unify int\n"
|
||||
" and string\n"
|
||||
"when checking the type of the expression at line 34, column 45\n"
|
||||
" 1 : int\n"
|
||||
"against the expected type\n"
|
||||
" string">>,
|
||||
<<"Cannot unify string\n"
|
||||
" and int\n"
|
||||
"when checking the type of the expression at line 34, column 50\n"
|
||||
" \"bla\" : string\n"
|
||||
"against the expected type\n"
|
||||
" int">>,
|
||||
<<"Cannot unify string\n"
|
||||
" and int\n"
|
||||
"when checking the type of the expression at line 32, column 18\n"
|
||||
" \"x\" : string\n"
|
||||
"against the expected type\n"
|
||||
" int">>,
|
||||
<<"Cannot unify string\n"
|
||||
" and int\n"
|
||||
"when checking the type of the expression at line 11, column 56\n"
|
||||
" \"foo\" : string\n"
|
||||
"against the expected type\n"
|
||||
" int">>,
|
||||
<<"Cannot unify int\n"
|
||||
" and string\n"
|
||||
"when comparing the types of the if-branches\n"
|
||||
" - w : int (at line 38, column 13)\n"
|
||||
" - z : string (at line 39, column 10)">>,
|
||||
<<"Not a record type: string\n"
|
||||
"arising from the projection of the field y (at line 22, column 38)">>,
|
||||
<<"Not a record type: string\n"
|
||||
"arising from an assignment of the field y (at line 21, column 42)">>,
|
||||
<<"Not a record type: string\n"
|
||||
"arising from an assignment of the field y (at line 20, column 38)">>,
|
||||
<<"Not a record type: string\n"
|
||||
"arising from an assignment of the field y (at line 19, column 35)">>,
|
||||
<<"Ambiguous record type with field y (at line 13, column 25) could be one of\n"
|
||||
" - r (at line 4, column 10)\n"
|
||||
" - r' (at line 5, column 10)">>,
|
||||
<<"Repeated name x in pattern\n"
|
||||
" x :: x (at line 26, column 7)">>,
|
||||
<<"No record type with fields y, z (at line 14, column 22)">>,
|
||||
<<"The field z is missing when constructing an element of type r2 (at line 15, column 24)">>,
|
||||
<<"Record type r2 does not have field y (at line 15, column 22)">>]}
|
||||
, {"init_type_error",
|
||||
[<<"Cannot unify string\n"
|
||||
" and map(int, int)\n"
|
||||
"when checking that 'init' returns a value of type 'state' at line 7, column 3">>]}
|
||||
, {"missing_state_type",
|
||||
[<<"Cannot unify string\n"
|
||||
" and ()\n"
|
||||
"when checking that 'init' returns a value of type 'state' at line 5, column 3">>]}
|
||||
, {"missing_fields_in_record_expression",
|
||||
[<<"The field x is missing when constructing an element of type r('a) (at line 7, column 40)">>,
|
||||
<<"The field y is missing when constructing an element of type r(int) (at line 8, column 40)">>,
|
||||
<<"The fields y, z are missing when constructing an element of type r('1) (at line 6, column 40)">>]}
|
||||
, {"namespace_clash",
|
||||
[<<"The contract Call (at line 4, column 10) has the same name as a namespace at (builtin location)">>]}
|
||||
, {"bad_events",
|
||||
[<<"The payload type int (at line 10, column 30) should be string">>,
|
||||
<<"The payload type alias_address (at line 12, column 30) equals address but it should be string">>,
|
||||
<<"The indexed type string (at line 9, column 25) is not a word type">>,
|
||||
<<"The indexed type alias_string (at line 11, column 25) equals string which is not a word type">>]}
|
||||
, {"bad_events2",
|
||||
[<<"The event constructor BadEvent1 (at line 9, column 7) has too many non-indexed values (max 1)">>,
|
||||
<<"The event constructor BadEvent2 (at line 10, column 7) has too many indexed values (max 3)">>,
|
||||
<<"The event constructor BadEvent3 (at line 11, column 7) has too many non-indexed values (max 1)">>,
|
||||
<<"The payload type address (at line 11, column 17) should be string">>,
|
||||
<<"The payload type int (at line 11, column 26) should be string">>]}
|
||||
, {"type_clash",
|
||||
[<<"Cannot unify int\n"
|
||||
" and string\n"
|
||||
"when checking the record projection at line 12, column 40\n"
|
||||
" r.foo : (gas : int, value : int) => Remote.themap\n"
|
||||
"against the expected type\n"
|
||||
" (gas : int, value : int) => map(string, int)">>]}
|
||||
, {"bad_include_and_ns",
|
||||
[<<"Include of 'included.aes' at line 2, column 11\nnot allowed, include only allowed at top level.">>,
|
||||
<<"Nested namespace not allowed\nNamespace 'Foo' at line 3, column 13 not defined at top level.">>]}
|
||||
, {"bad_address_literals",
|
||||
[<<"The type bytes(32) is not a contract type\n"
|
||||
"when checking that the contract literal at line 32, column 5\n"
|
||||
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
|
||||
"has the type\n"
|
||||
" bytes(32)">>,
|
||||
<<"The type oracle(int, bool) is not a contract type\n"
|
||||
"when checking that the contract literal at line 30, column 5\n"
|
||||
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
|
||||
"has the type\n"
|
||||
" oracle(int, bool)">>,
|
||||
<<"The type address is not a contract type\n"
|
||||
"when checking that the contract literal at line 28, column 5\n"
|
||||
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
|
||||
"has the type\n"
|
||||
" address">>,
|
||||
<<"Cannot unify oracle_query('1, '2)\n"
|
||||
" and Remote\n"
|
||||
"when checking the type of the expression at line 25, column 5\n"
|
||||
" oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY :\n"
|
||||
" oracle_query('1, '2)\n"
|
||||
"against the expected type\n"
|
||||
" Remote">>,
|
||||
<<"Cannot unify oracle_query('3, '4)\n"
|
||||
" and bytes(32)\n"
|
||||
"when checking the type of the expression at line 23, column 5\n"
|
||||
" oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY :\n"
|
||||
" oracle_query('3, '4)\n"
|
||||
"against the expected type\n"
|
||||
" bytes(32)">>,
|
||||
<<"Cannot unify oracle_query('5, '6)\n"
|
||||
" and oracle(int, bool)\n"
|
||||
"when checking the type of the expression at line 21, column 5\n"
|
||||
" oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY :\n"
|
||||
" oracle_query('5, '6)\n"
|
||||
"against the expected type\n"
|
||||
" oracle(int, bool)">>,
|
||||
<<"Cannot unify oracle('7, '8)\n"
|
||||
" and Remote\n"
|
||||
"when checking the type of the expression at line 18, column 5\n"
|
||||
" ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 :\n"
|
||||
" oracle('7, '8)\n"
|
||||
"against the expected type\n"
|
||||
" Remote">>,
|
||||
<<"Cannot unify oracle('9, '10)\n"
|
||||
" and bytes(32)\n"
|
||||
"when checking the type of the expression at line 16, column 5\n"
|
||||
" ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 :\n"
|
||||
" oracle('9, '10)\n"
|
||||
"against the expected type\n"
|
||||
" bytes(32)">>,
|
||||
<<"Cannot unify oracle('11, '12)\n"
|
||||
" and oracle_query(int, bool)\n"
|
||||
"when checking the type of the expression at line 14, column 5\n"
|
||||
" ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 :\n"
|
||||
" oracle('11, '12)\n"
|
||||
"against the expected type\n"
|
||||
" oracle_query(int, bool)">>,
|
||||
<<"Cannot unify address\n"
|
||||
" and oracle(int, bool)\n"
|
||||
"when checking the type of the expression at line 11, column 5\n"
|
||||
" ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt : address\n"
|
||||
"against the expected type\n"
|
||||
" oracle(int, bool)">>,
|
||||
<<"Cannot unify address\n"
|
||||
" and Remote\n"
|
||||
"when checking the type of the expression at line 9, column 5\n"
|
||||
" ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt : address\n"
|
||||
"against the expected type\n"
|
||||
" Remote">>,
|
||||
<<"Cannot unify address\n"
|
||||
" and bytes(32)\n"
|
||||
"when checking the type of the expression at line 7, column 5\n"
|
||||
" ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt : address\n"
|
||||
"against the expected type\n"
|
||||
" bytes(32)">>]}
|
||||
].
|
||||
@@ -0,0 +1,22 @@
|
||||
-module(aeso_eunit_SUITE).
|
||||
|
||||
-compile([export_all, nowarn_export_all]).
|
||||
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
|
||||
all() ->
|
||||
[{group, eunit}].
|
||||
|
||||
groups() ->
|
||||
[{eunit, [], [ aeso_scan_tests
|
||||
, aeso_parser_tests
|
||||
, aeso_compiler_tests
|
||||
, aeso_abi_tests
|
||||
, aeso_aci_tests
|
||||
]}].
|
||||
|
||||
aeso_scan_tests(_Config) -> ok = eunit:test(aeso_scan_tests).
|
||||
aeso_parser_tests(_Config) -> ok = eunit:test(aeso_parser_tests).
|
||||
aeso_compiler_tests(_Config) -> ok = eunit:test(aeso_compiler_tests).
|
||||
aeso_abi_tests(_Config) -> ok = eunit:test(aeso_abi_tests).
|
||||
aeso_aci_tests(_Config) -> ok = eunit:test(aeso_aci_tests).
|
||||
@@ -0,0 +1,111 @@
|
||||
-module(aeso_parser_tests).
|
||||
|
||||
-export([parse_contract/1]).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
simple_contracts_test_() ->
|
||||
{foreach,
|
||||
fun() -> ok end,
|
||||
fun(_) -> ok end,
|
||||
[{"Parse a contract with an identity function.",
|
||||
fun() ->
|
||||
Text = "contract Identity =\n"
|
||||
" function id(x) = x\n",
|
||||
?assertMatch(
|
||||
[{contract, _, {con, _, "Identity"},
|
||||
[{letfun, _, {id, _, "id"}, [{arg, _, {id, _, "x"}, {id, _, "_"}}], {id, _, "_"},
|
||||
{id, _, "x"}}]}], parse_string(Text)),
|
||||
ok
|
||||
end},
|
||||
{"Operator precedence test.",
|
||||
fun() ->
|
||||
NoPar = fun NoPar(X) when is_atom(X) -> atom_to_list(X);
|
||||
NoPar({A, Op, B}) -> lists:concat([NoPar(A), " ", Op, " ", NoPar(B)]);
|
||||
NoPar({Op, A}) -> lists:concat([Op, " ", NoPar(A)])
|
||||
end,
|
||||
Par = fun Par(X) when is_atom(X) -> atom_to_list(X);
|
||||
Par({A, Op, B}) -> lists:concat(["(", Par(A), " ", Op, " ", Par(B), ")"]);
|
||||
Par({Op, A}) -> lists:concat(["(", Op, " ", Par(A), ")"])
|
||||
end,
|
||||
Parse = fun(S) ->
|
||||
try remove_line_numbers(parse_expr(S))
|
||||
catch _:_ -> ?assertMatch(ok, {parse_fail, S}) end
|
||||
end,
|
||||
CheckParens = fun(Expr) ->
|
||||
?assertEqual(Parse(NoPar(Expr)), Parse(Par(Expr)))
|
||||
end,
|
||||
LeftAssoc = fun(Op) -> CheckParens({{a, Op, b}, Op, c}) end,
|
||||
RightAssoc = fun(Op) -> CheckParens({a, Op, {b, Op, c}}) end,
|
||||
NonAssoc = fun(Op) ->
|
||||
OpAtom = list_to_atom(Op),
|
||||
?assertError({error, {_, parse_error, _}},
|
||||
parse_expr(NoPar({a, Op, {b, Op, c}}))) end,
|
||||
Stronger = fun(Op1, Op2) ->
|
||||
CheckParens({{a, Op1, b}, Op2, c}),
|
||||
CheckParens({a, Op2, {b, Op1, c}})
|
||||
end,
|
||||
|
||||
Tiers = [["||"], ["&&"], ["==", "!=", "<", ">", "=<", ">="], ["::", "++"],
|
||||
["+", "-"], ["*", "/", "mod"]],
|
||||
|
||||
%% associativity
|
||||
[ RightAssoc(Op) || Op <- ["||", "&&", "::", "++"] ],
|
||||
[ NonAssoc(Op) || Op <- ["==", "!=", "<", ">", "=<", ">="] ],
|
||||
[ LeftAssoc(Op) || Op <- ["+", "-", "*", "/", "mod"] ],
|
||||
|
||||
%% precedence
|
||||
[ Stronger(Op2, Op1) || [T1 , T2 | _] <- tails(Tiers), Op1 <- T1, Op2 <- T2 ],
|
||||
ok
|
||||
end}
|
||||
] ++
|
||||
%% Parse tests of example contracts
|
||||
[ {lists:concat(["Parse the ", Contract, " contract."]),
|
||||
fun() -> roundtrip_contract(Contract) end}
|
||||
|| Contract <- [counter, voting, all_syntax, '05_greeter', aeproof, multi_sig, simple_storage, withdrawal, fundme, dutch_auction] ]
|
||||
}.
|
||||
|
||||
parse_contract(Name) ->
|
||||
parse_string(aeso_test_utils:read_contract(Name)).
|
||||
|
||||
roundtrip_contract(Name) ->
|
||||
round_trip(aeso_test_utils:read_contract(Name)).
|
||||
|
||||
parse_string(Text) ->
|
||||
case aeso_parser:string(Text) of
|
||||
{ok, Contract} -> Contract;
|
||||
Err -> error(Err)
|
||||
end.
|
||||
|
||||
parse_expr(Text) ->
|
||||
[{letval, _, _, _, Expr}] =
|
||||
parse_string("let _ = " ++ Text),
|
||||
Expr.
|
||||
|
||||
round_trip(Text) ->
|
||||
Contract = parse_string(Text),
|
||||
Text1 = prettypr:format(aeso_pretty:decls(Contract)),
|
||||
Contract1 = parse_string(Text1),
|
||||
NoSrcLoc = remove_line_numbers(Contract),
|
||||
NoSrcLoc1 = remove_line_numbers(Contract1),
|
||||
?assertMatch(NoSrcLoc, diff(NoSrcLoc, NoSrcLoc1)).
|
||||
|
||||
remove_line_numbers({line, _L}) -> {line, 0};
|
||||
remove_line_numbers({col, _C}) -> {col, 0};
|
||||
remove_line_numbers([H|T]) ->
|
||||
[remove_line_numbers(H) | remove_line_numbers(T)];
|
||||
remove_line_numbers(T) when is_tuple(T) ->
|
||||
list_to_tuple(remove_line_numbers(tuple_to_list(T)));
|
||||
remove_line_numbers(M) when is_map(M) ->
|
||||
maps:from_list(remove_line_numbers(maps:to_list(M)));
|
||||
remove_line_numbers(X) -> X.
|
||||
|
||||
diff(X, X) -> X;
|
||||
diff([H | T], [H1 | T1]) ->
|
||||
[diff(H, H1) | diff(T, T1)];
|
||||
diff(T, T1) when tuple_size(T) == tuple_size(T1) ->
|
||||
list_to_tuple(diff(tuple_to_list(T), tuple_to_list(T1)));
|
||||
diff(X, Y) -> {X, '/=', Y}.
|
||||
|
||||
tails(Zs) -> lists:foldr(fun(X, [Xs|Xss]) -> [[X|Xs], Xs | Xss] end, [[]], Zs).
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user