docs: restructuring & introduction of mkdocs with versioning provided by mike

This commit is contained in:
marc0olo 2021-07-27 16:02:06 +02:00
parent 3029bf31cb
commit 1744ec43e6
14 changed files with 672 additions and 619 deletions

23
.github/workflows/docs-develop.yml vendored Normal file
View File

@ -0,0 +1,23 @@
name: Publish development docs
on:
push:
branches: ['master']
jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/setup-python@v2
with:
python-version: 3.8
- uses: actions/cache@v2
with:
path: ~/.cache/pip3
key: ${{ runner.os }}-pip-${{ hashFiles('docs/requirements.txt') }}
- run: pip3 install -r docs/requirements.txt
- run: git config --global user.email "github-action@users.noreply.github.com"
- run: git config --global user.name "GitHub Action"
- run: mike deploy --push master

24
.github/workflows/docs-release.yml vendored Normal file
View File

@ -0,0 +1,24 @@
name: Publish release docs
on:
release:
types: [released]
jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/setup-python@v2
with:
python-version: 3.8
- uses: actions/cache@v2
with:
path: ~/.cache/pip3
key: ${{ runner.os }}-pip-${{ hashFiles('docs/requirements.txt') }}
- run: pip3 install -r docs/requirements.txt
- run: git config --global user.email "github-action@users.noreply.github.com"
- run: git config --global user.name "GitHub Action"
- run: echo "RELEASE_VERSION=${GITHUB_REF:10}" >> $GITHUB_ENV
- run: mike deploy --push --update-aliases $RELEASE_VERSION latest

View File

@ -9,9 +9,13 @@ The compiler is currently being used three places
## Documentation
* [Smart Contracts on aeternity Blockchain](https://github.com/aeternity/protocol/blob/master/contracts/contracts.md).
* [Sophia Documentation](docs/sophia.md).
* [Sophia Standard Library](docs/sophia_stdlib.md).
* [Introduction](docs/index.md)
* [Syntax](docs/sophia_syntax.md)
* [Features](docs/sophia_features.md)
* [Standard Library](docs/sophia_stdlib.md)
* [Contract Examples](docs/sophia_examples.md)
Additionally you can check out the [contracts section](https://github.com/aeternity/protocol/blob/master/contracts/contracts.md) of the aeternity blockchain specification.
## Versioning
@ -26,5 +30,5 @@ Versioning should follow the [semantic versioning](https://semver.org/spec/v2.0.
The basic modules for interfacing the compiler:
* [aeso_compiler: the Sophia compiler](./docs/aeso_compiler.md)
* [aeso_aci: the ACI interface](./docs/aeso_aci.md)
* [aeso_compiler: the Sophia compiler](docs/aeso_compiler.md)
* [aeso_aci: the ACI interface](docs/aeso_aci.md)

2
docs/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
__pycache__
CHANGELOG.md

BIN
docs/images/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

12
docs/index.md Normal file
View File

@ -0,0 +1,12 @@
# Introduction
Sophia is a language in the ML family designed for smart contract development. It is strongly typed and has
restricted mutable state.
Sophia is customized for smart contracts, which can be published
to a blockchain. Thus some features of conventional
languages, such as floating point arithmetic, are not present in Sophia, and
some [aeternity blockchain](https://aeternity.com) specific primitives, constructions and types have been added.
!!! Note
- For rapid prototyping of smart contracts check out [AEstudio](https://studio.aepps.com/)!
- For playing around with the language in general check out the [REPL](https://repl.aeternity.io/)!

View File

@ -0,0 +1,8 @@
{% extends "base.html" %}
{% block outdated %}
You're not viewing the latest version.
<a href="{{ '../' ~ base_url }}">
<strong>Click here to go to latest.</strong>
</a>
{% endblock %}

4
docs/python/hooks.py Normal file
View File

@ -0,0 +1,4 @@
import shutil
def pre_build(**kwargs):
shutil.copy('./CHANGELOG.md', './docs')

View File

@ -0,0 +1,4 @@
mkdocs==1.2.1
mkdocs-simple-hooks==0.1.3
mkdocs-material==7.1.9
mike==1.0.1

67
docs/sophia_examples.md Normal file
View File

@ -0,0 +1,67 @@
# Contract Examples
## Crowdfunding
```sophia
/*
* A simple crowd-funding example
*/
contract FundMe =
record spend_args = { recipient : address,
amount : int }
record state = { contributions : map(address, int),
total : int,
beneficiary : address,
deadline : int,
goal : int }
stateful function spend(args : spend_args) =
Chain.spend(args.recipient, args.amount)
entrypoint init(beneficiary, deadline, goal) : state =
{ contributions = {},
beneficiary = beneficiary,
deadline = deadline,
total = 0,
goal = goal }
function is_contributor(addr) =
Map.member(addr, state.contributions)
stateful entrypoint contribute() =
if(Chain.block_height >= state.deadline)
spend({ recipient = Call.caller, amount = Call.value }) // Refund money
false
else
let amount =
switch(Map.lookup(Call.caller, state.contributions))
None => Call.value
Some(n) => n + Call.value
put(state{ contributions[Call.caller] = amount,
total @ tot = tot + Call.value })
true
stateful entrypoint withdraw() =
if(Chain.block_height < state.deadline)
abort("Cannot withdraw before deadline")
if(Call.caller == state.beneficiary)
withdraw_beneficiary()
elif(is_contributor(Call.caller))
withdraw_contributor()
else
abort("Not a contributor or beneficiary")
stateful function withdraw_beneficiary() =
require(state.total >= state.goal, "Project was not funded")
spend({recipient = state.beneficiary,
amount = Contract.balance })
stateful function withdraw_contributor() =
if(state.total >= state.goal)
abort("Project was funded")
let to = Call.caller
spend({recipient = to,
amount = state.contributions[to]})
put(state{ contributions @ c = Map.delete(to, c) })
```

View File

@ -1,76 +1,5 @@
<!-- IMPORTANT: REMEMBER TO UPDATE THE TABLE OF CONTENTS AFTER YOUR EDIT -->
# The Sophia Language
An Æternity BlockChain Language
The Sophia is a language in the ML family. It is strongly typed and has
restricted mutable state.
Sophia is customized for smart contracts, which can be published
to a blockchain (the Æternity BlockChain). Thus some features of conventional
languages, such as floating point arithmetic, are not present in Sophia, and
some blockchain specific primitives, constructions and types have been added.
**Table of Contents**
- [Language Features](#language-features)
- [Contracts](#contracts)
- [Calling other contracts](#calling-other-contracts)
- [Protected contract calls](#protected-contract-calls)
- [Contract factories and child contracts](#contract-factories-and-child-contracts)
- [Mutable state](#mutable-state)
- [Stateful functions](#stateful-functions)
- [Payable](#payable)
- [Payable contracts](#payable-contracts)
- [Payable entrypoints](#payable-entrypoints)
- [Namespaces](#namespaces)
- [Splitting code over multiple files](#splitting-code-over-multiple-files)
- [Standard library](#standard-library)
- [Types](#types)
- [Literals](#literals)
- [Arithmetic](#arithmetic)
- [Bit fields](#bit-fields)
- [Type aliases](#type-aliases)
- [Algebraic data types](#algebraic-data-types)
- [Lists](#lists)
- [Maps and records](#maps-and-records)
- [Constructing maps and records](#constructing-maps-and-records)
- [Accessing values](#accessing-values)
- [Updating a value](#updating-a-value)
- [Map implementation](#map-implementation)
- [Strings](#strings)
- [Chars](#chars)
- [Byte arrays](#byte-arrays)
- [Cryptographic builins](#cryptographic-builins)
- [AEVM note](#aevm-note)
- [Authorization interface](#authorization-interface)
- [Oracle interface](#oracle-interface)
- [Example](#example)
- [Sanity checks](#sanity-checks)
- [AENS interface](#aens-interface)
- [Example](#example)
- [Events](#events)
- [Argument order](#argument-order)
- [Compiler pragmas](#compiler-pragmas)
- [Exceptions](#exceptions)
- [Syntax](#syntax)
- [Lexical syntax](#lexical-syntax)
- [Comments](#comments)
- [Keywords](#keywords)
- [Tokens](#tokens)
- [Layout blocks](#layout-blocks)
- [Notation](#notation)
- [Declarations](#declarations)
- [Types](#types)
- [Statements](#statements)
- [Expressions](#expressions)
- [Operators types](#operators-types)
- [Operator precendences](#operator-precendences)
- [Examples](#examples)
- [Delegation signature](#delegation-signature)
## Language Features
### Contracts
# Features
## Contracts
The main unit of code in Sophia is the *contract*.
@ -85,10 +14,10 @@ The main unit of code in Sophia is the *contract*.
state is initialized to its return value.
The language offers some primitive functions to interact with the blockchain and contracts.
Please refer to the [Chain](sophia_stdlib.md#Chain), [Contract](sophia_stdlib.md#Contract)
and the [Call](sophia_stdlib.md#Call) namespaces in the documentation.
Please refer to the [Chain](sophia_stdlib.md#chain), [Contract](sophia_stdlib.md#contract)
and the [Call](sophia_stdlib.md#call) namespaces in the documentation.
#### Calling other contracts
### Calling other contracts
To call a function in another contract you need the address to an instance of
the contract. The type of the address must be a contract type, which consists
@ -141,7 +70,7 @@ without calling it you can write
Chain.spend(v.address, amount)
```
#### Protected contract calls
### Protected contract calls
If a contract call fails for any reason (for instance, the remote contract
crashes or runs out of gas, or the entrypoint doesn't exist or has the wrong
@ -171,7 +100,7 @@ However, note that errors that would normally consume all the gas in the
transaction still only uses up the gas spent running the contract.
#### Contract factories and child contracts
### Contract factories and child contracts
Since the version 6.0.0 Sophia supports deploying contracts by other
contracts. This can be done in two ways:
@ -187,7 +116,7 @@ While `Chain.clone` requires only a `contract interface` and a living instance
of a given contract on the chain, `Chain.create` needs a full definition of a
to-create contract defined by the standard `contract` syntax, for example
```
```sophia
contract IntHolder =
type state = int
entrypoint init(x) = x
@ -203,7 +132,7 @@ In case of a presence of child contracts (`IntHolder` in this case), the main
contract must be pointed out with the `main` keyword as shown in the example.
### Mutable state
## Mutable state
Sophia does not have arbitrary mutable state, but only a limited form of state
associated with each contract instance.
@ -226,7 +155,7 @@ associated with each contract instance.
To make it convenient to update parts of a deeply nested state Sophia
provides special syntax for map/record updates.
#### Stateful functions
### Stateful functions
Top-level functions and entrypoints must be annotated with the
`stateful` keyword to be allowed to affect the state of the running contract.
@ -261,9 +190,9 @@ A `stateful` annotation *is not* required to
* Issue an event using the `event` function.
* Call another contract with `value = 0`, even if the called function is stateful.
### Payable
## Payable
#### Payable contracts
### Payable contracts
A concrete contract is by default *not* payable. Any attempt at spending to such
a contract (either a `Chain.spend` or a normal spend transaction) will fail. If a
@ -278,7 +207,7 @@ payable contract ExampleContract =
If in doubt, it is possible to check if an address is payable using
`Address.is_payable(addr)`.
#### Payable entrypoints
### Payable entrypoints
A contract entrypoint is by default *not* payable. Any call to such a function
(either a [Remote call](#calling-other-contracts) or a contract call transaction)
@ -296,7 +225,7 @@ payable stateful entrypoint buy(to : address) =
Note: In the Aeternity VM (AEVM) contracts and entrypoints were by default
payable until the Lima release.
### Namespaces
## Namespaces
Code can be split into libraries using the `namespace` construct. Namespaces
can appear at the top-level and can contain type and function definitions, but
@ -304,7 +233,7 @@ not entrypoints. Outside the namespace you can refer to the (non-private) names
by qualifying them with the namespace (`Namespace.name`).
For example,
```
```sophia
namespace Library =
type number = int
function inc(x : number) : number = x + 1
@ -319,22 +248,21 @@ Functions in namespaces have access to the same environment (including the
with the exception of `state`, `put` and `Chain.event` since these are
dependent on the specific state and event types of the contract.
### Splitting code over multiple files
## Splitting code over multiple files
Code from another file can be included in a contract using an `include`
statement. These must appear at the top-level (outside the main contract). The
included file can contain one or more namespaces and abstract contracts. For
example, if the file `library.aes` contains
```
```sophia
namespace Library =
function inc(x) = x + 1
```
you can use it from another file using an `include`:
```
```sophia
include "library.aes"
contract MyContract =
entrypoint plus2(x) = Library.inc(Library.inc(x))
@ -345,7 +273,7 @@ the file, except that error messages will refer to the original source
locations. The language will try to include each file at most one time automatically,
so even cyclic includes should be working without any special tinkering.
### Standard library
## Standard library
Sophia offers [standard library](sophia_stdlib.md) which exposes some
primitive operations and some higher level utilities. The builtin
@ -353,7 +281,7 @@ namespaces like `Chain`, `Contract`, `Map`
are included by default and are supported internally by the compiler.
Others like `List`, `Frac`, `Option` need to be manually included using the
`include` directive. For example
```
```sophia
include "List.aes"
include "Pair.aes"
-- Map is already there!
@ -363,7 +291,7 @@ namespace C =
List.map(Pair.fst, (Map.to_list(m)))
```
### Types
## Types
Sophia has the following types:
| Type | Description | Example |
@ -389,7 +317,7 @@ Sophia has the following types:
| oracle_query('a, 'b) | A specific oracle query | ```Oracle.query(o, q, qfee, qttl, rttl)``` |
| contract | A user defined, typed, contract address | ```function call_remote(r : RemoteContract) = r.fun()``` |
### Literals
## Literals
| Type | Constant/Literal example(s) |
| ---------- | ------------------------------- |
| int | `-1`, `2425`, `4598275923475723498573485768` |
@ -412,7 +340,7 @@ Sophia has the following types:
| oracle_query('a, 'b) | `oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY` |
| contract | `ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ` |
### Arithmetic
## Arithmetic
Sophia integers (`int`) are represented by 256-bit (AEVM) or arbitrary-sized (FATE) signed words and supports the following
arithmetic operations:
@ -431,17 +359,17 @@ fails rather than wrapping around to -2²⁵⁵.
The division and modulo operations also throw an arithmetic error if the
second argument is zero.
### Bit fields
## Bit fields
Sophia integers do not support bit arithmetic. Instead there is a separate
type `bits`. See the standard library [documentation](sophia_stdlib.md#Bits).
type `bits`. See the standard library [documentation](sophia_stdlib.md#bits).
On the AEVM a bit field is represented by a 256-bit word and reading or writing
a bit outside the 0..255 range fails with an `arithmetic_error`. On FATE a bit
field can be of arbitrary size (but it is still represented by the
corresponding integer, so setting very high bits can be expensive).
### Type aliases
## Type aliases
Type aliases can be introduced with the `type` keyword and can be
parameterized. For instance
@ -454,13 +382,13 @@ type string_map('a) = map(string, 'a)
A type alias and its definition can be used interchangeably. Sophia does not support
higher-kinded types, meaning that following type alias is invalid: `type wrap('f, 'a) = 'f('a)`
### Algebraic data types
## Algebraic data types
Sophia supports algebraic data types (variant types) and pattern matching. Data
types are declared by giving a list of constructors with
their respective arguments. For instance,
```
```sophia
datatype one_or_both('a, 'b) = Left('a) | Right('b) | Both('a, 'b)
```
@ -485,7 +413,7 @@ function
*NOTE: Data types cannot currently be recursive.*
### Lists
## Lists
A Sophia list is a dynamically sized, homogenous, immutable, singly
linked list. A list is constructed with the syntax `[1, 2, 3]`. The
@ -519,9 +447,9 @@ Lists can be constructed using the range syntax using special `..` operator:
The ranges are always ascending and have step equal to 1.
Please refer to the [standard library](sophia_stdlib.md#List) for the predefined functionalities.
Please refer to the [standard library](sophia_stdlib.md#list) for the predefined functionalities.
### Maps and records
## Maps and records
A Sophia record type is given by a fixed set of fields with associated,
possibly different, types. For instance
@ -536,9 +464,9 @@ but of a fixed type. The type of maps with keys of type `'k` and values of type
`'v` is written `map('k, 'v)`. The key type can be any type that does not
contain a map or a function type.
Please refer to the [standard library](sophia_stdlib.md#Map) for the predefined functionalities.
Please refer to the [standard library](sophia_stdlib.md#map) for the predefined functionalities.
#### Constructing maps and records
### Constructing maps and records
A value of record type is constructed by giving a value for each of the fields.
For the example above,
@ -553,7 +481,7 @@ Maps are constructed similarly, with keys enclosed in square brackets
```
The empty map is written `{}`.
#### Accessing values
### Accessing values
Record fields access is written `r.f` and map lookup `m[k]`. For instance,
```sophia
@ -564,7 +492,7 @@ Looking up a non-existing key in a map results in contract execution failing. A
default value to return for non-existing keys can be provided using the syntax
`m[k = default]`. See also `Map.member` and `Map.lookup` below.
#### Updating a value
### Updating a value
Record field updates are written `r{f = v}`. This creates a new record value
which is the same as `r`, but with the value of the field `f` replaced by `v`.
@ -592,54 +520,45 @@ an account if `a` is not in the map you can write (given a function `empty_accou
accounts{ [a = empty_account()].history = [] }
```
#### Map implementation
### Map implementation
Internally in the VM maps are implemented as hash maps and support fast lookup
and update. Large maps can be stored in the contract state and the size of the
map does not contribute to the gas costs of a contract call reading or updating
it.
### Strings
## Strings
There is a builtin type `string`, which can be seen as an array of bytes.
Strings can be compared for equality (`==`, `!=`), used as keys in maps and
records, and used in builtin functions `String.length`, `String.concat` and
the hash functions described below.
Please refer to the `String` [library documentation](sophia_stdlib.md#String).
Please refer to the `String` [library documentation](sophia_stdlib.md#string).
### Chars
## Chars
There is a builtin type `char` (the underlying representation being an integer),
mainly used to manipulate strings via `String.to_list`/`String.from_list`.
Characters can also be introduced as character literals (`'x', '+', ...).
Please refer to the `Char` [library documentation](sophia_stdlib.md#Char).
Please refer to the `Char` [library documentation](sophia_stdlib.md#char).
### Byte arrays
## Byte arrays
Byte arrays are fixed size arrays of 8-bit integers. They are described in hexadecimal system,
for example the literal `#cafe` creates a two-element array of bytes `ca` (202) and `fe` (254)
and thus is a value of type `bytes(2)`.
Please refer to the `Bytes` [library documentation](sophia_stdlib.md#Bytes).
Please refer to the `Bytes` [library documentation](sophia_stdlib.md#bytes).
## Cryptographic builtins
### Cryptographic builins
Libraries [Crypto](sophia_stdlib.md#Crypto) and [String](sophia_stdlib.md#String) provide functions to
Libraries [Crypto](sophia_stdlib.md#crypto) and [String](sophia_stdlib.md#string) provide functions to
hash objects, verify signatures etc. The `hash` is a type alias for `bytes(32)`.
#### AEVM note
The hash functions in `String` hash strings interpreted as byte arrays, and
the `Crypto` hash functions accept an element of any (first-order) type. The
result is the hash of the binary encoding of the argument as [described
below](#encoding-sophia-values-as-binaries). Note that this means that for `s :
string`, `String.sha3(s)` and `Crypto.sha3(s)` will give different results on AEVM.
### Authorization interface
## Authorization interface
When a Generalized account is authorized, the authorization function needs
access to the transaction and the transaction hash for the wrapped transaction. (A `GAMetaTx`
@ -647,16 +566,15 @@ wrapping a transaction.) The transaction and the transaction hash is available i
`Auth.tx` and `Auth.tx_hash` respectively, they are *only* available during authentication if invoked by a
normal contract call they return `None`.
### Oracle interface
## Oracle interface
You can attach an oracle to the current contract and you can interact with oracles
through the Oracle interface.
For a full description of how Oracle works see
[Oracles](https://github.com/aeternity/protocol/blob/master/oracles/oracles.md#oracles).
For a functionality documentation refer to the [standard library](sophia_stdlib.md#Oracle).
For a functionality documentation refer to the [standard library](sophia_stdlib.md#oracle).
#### Example
### Example
Example for an oracle answering questions of type `string` with answers of type `int`:
```sophia
@ -709,19 +627,19 @@ contract Oracles =
Oracle.get_answer(o, q)
```
#### Sanity checks
### Sanity checks
When an Oracle literal is passed to a contract, no deep checks are performed.
For extra safety [Oracle.check](sophia_stdlib.md#check) and [Oracle.check_query](sophia_stdlib.md#check_query)
functions are provided.
### AENS interface
## AENS interface
Contracts can interact with the
[Aeternity Naming System](https://github.com/aeternity/protocol/blob/master/AENS.md).
For this purpose the [AENS](sophia_stdlib.md#AENS) library was exposed.
For this purpose the [AENS](sophia_stdlib.md#aens) library was exposed.
#### Example
### Example
In this example we assume that the name `name` already exists, and is owned by
an account with address `addr`. In order to allow a contract `ct` to handle
@ -762,7 +680,7 @@ name:
a Sophia contract lookup or update (bad) legacy pointers, the bad keys are
automatically removed so they will not appear in the pointers map.
### Events
## Events
Sophia contracts log structured messages to an event log in the resulting
blockchain transaction. The event log is quite similar to [Events in
@ -773,7 +691,7 @@ Events are further discussed in the [protocol](https://github.com/aeternity/prot
To use events a contract must declare a datatype `event`, and events are then
logged using the `Chain.event` function:
```
```sophia
datatype event
= Event1(int, int, string)
| Event2(string, address)
@ -806,7 +724,7 @@ will emit one Event of each kind in the example.
Chain.event(Event2("This is not indexed", Contract.address))
```
#### Argument order
### Argument order
It is only possible to have one (1) `string` parameter in the event, but it can
be placed in any position (and its value will end up in the `data` field), i.e.
@ -819,14 +737,14 @@ Chain.event(AnotherEvent("This is not indexed", Contract.address))
```
would yield exactly the same result in the example above!
### Compiler pragmas
## Compiler pragmas
To enforce that a contract is only compiled with specific versions of the
Sophia compiler, you can give one or more `@compiler` pragmas at the
top-level (typically at the beginning) of a file. For instance, to enforce that
a contract is compiled with version 4.3 of the compiler you write
```
```sophia
@compiler >= 4.3
@compiler < 4.4
```
@ -836,11 +754,11 @@ numbers are given as a sequence of non-negative integers separated by dots.
Trailing zeros are ignored, so `4.0.0 == 4`. If a constraint is violated an
error is reported and compilation fails.
### Exceptions
## Exceptions
Contracts can fail with an (uncatchable) exception using the built-in function
```
```sophia
abort(reason : string) : 'a
```
@ -851,342 +769,12 @@ consumes all available gas.
For convenience the following function is also built-in:
```
```sophia
function require(b : bool, err : string) =
if(!b) abort(err)
```
## Syntax
### Lexical syntax
#### Comments
Single line comments start with `//` and block comments are enclosed in `/*`
and `*/` and can be nested.
#### Keywords
```
contract elif else entrypoint false function if import include let mod namespace
private payable stateful switch true type record datatype main interface
```
#### Tokens
- `Id = [a-z_][A-Za-z0-9_']*` identifiers start with a lower case letter.
- `Con = [A-Z][A-Za-z0-9_']*` constructors start with an upper case letter.
- `QId = (Con\.)+Id` qualified identifiers (e.g. `Map.member`)
- `QCon = (Con\.)+Con` qualified constructor
- `TVar = 'Id` type variable (e.g `'a`, `'b`)
- `Int = [0-9]+(_[0-9]+)*|0x[0-9A-Fa-f]+(_[0-9A-Fa-f]+)*` integer literal with optional `_` separators
- `Bytes = #[0-9A-Fa-f]+(_[0-9A-Fa-f]+)*` byte array literal with optional `_` separators
- `String` string literal enclosed in `"` with escape character `\`
- `Char` character literal enclosed in `'` with escape character `\`
- `AccountAddress` base58-encoded 32 byte account pubkey with `ak_` prefix
- `ContractAddress` base58-encoded 32 byte contract address with `ct_` prefix
- `OracleAddress` base58-encoded 32 byte oracle address with `ok_` prefix
- `OracleQueryId` base58-encoded 32 byte oracle query id with `oq_` prefix
Valid string escape codes are
| Escape | ASCII | |
|---------------|-------------|---|
| `\b` | 8 | |
| `\t` | 9 | |
| `\n` | 10 | |
| `\v` | 11 | |
| `\f` | 12 | |
| `\r` | 13 | |
| `\e` | 27 | |
| `\xHexDigits` | *HexDigits* | |
See the [identifier encoding scheme](https://github.com/aeternity/protocol/blob/master/node/api/api_encoding.md) for the
details on the base58 literals.
### Layout blocks
Sophia uses Python-style layout rules to group declarations and statements. A
layout block with more than one element must start on a separate line and be
indented more than the currently enclosing layout block. Blocks with a single
element can be written on the same line as the previous token.
Each element of the block must share the same indentation and no part of an
element may be indented less than the indentation of the block. For instance
```
contract Layout =
function foo() = 0 // no layout
function bar() = // layout block starts on next line
let x = foo() // indented more than 2 spaces
x
+ 1 // the '+' is indented more than the 'x'
```
### Notation
In describing the syntax below, we use the following conventions:
- Upper-case identifiers denote non-terminals (like `Expr`) or terminals with
some associated value (like `Id`).
- Keywords and symbols are enclosed in single quotes: `'let'` or `'='`.
- Choices are separated by vertical bars: `|`.
- Optional elements are enclosed in `[` square brackets `]`.
- `(` Parentheses `)` are used for grouping.
- Zero or more repetitions are denoted by a postfix `*`, and one or more
repetitions by a `+`.
- `Block(X)` denotes a layout block of `X`s.
- `Sep(X, S)` is short for `[X (S X)*]`, i.e. a possibly empty sequence of `X`s
separated by `S`s.
- `Sep1(X, S)` is short for `X (S X)*`, i.e. same as `Sep`, but must not be empty.
### Declarations
A Sophia file consists of a sequence of *declarations* in a layout block.
```c
File ::= Block(TopDecl)
TopDecl ::= ['payable'] 'contract' Con '=' Block(Decl)
| 'namespace' Con '=' Block(Decl)
| '@compiler' PragmaOp Version
| 'include' String
Decl ::= 'type' Id ['(' TVar* ')'] '=' TypeAlias
| 'record' Id ['(' TVar* ')'] '=' RecordType
| 'datatype' Id ['(' TVar* ')'] '=' DataType
| (EModifier* 'entrypoint' | FModifier* 'function') Block(FunDecl)
FunDecl ::= Id ':' Type // Type signature
| Id Args [':' Type] '=' Block(Stmt) // Definition
PragmaOp ::= '<' | '=<' | '==' | '>=' | '>'
Version ::= Sep1(Int, '.')
EModifier ::= 'payable' | 'stateful'
FModifier ::= 'stateful' | 'private'
Args ::= '(' Sep(Pattern, ',') ')'
```
Contract declarations must appear at the top-level.
For example,
```sophia
contract Test =
type t = int
entrypoint add (x : t, y : t) = x + y
```
There are three forms of type declarations: type aliases (declared with the
`type` keyword), record type definitions (`record`) and data type definitions
(`datatype`):
```c
TypeAlias ::= Type
RecordType ::= '{' Sep(FieldType, ',') '}'
DataType ::= Sep1(ConDecl, '|')
FieldType ::= Id ':' Type
ConDecl ::= Con ['(' Sep1(Type, ',') ')']
```
For example,
```
record point('a) = {x : 'a, y : 'a}
datatype shape('a) = Circle(point('a), 'a) | Rect(point('a), point('a))
type int_shape = shape(int)
```
### Types
```c
Type ::= Domain '=>' Type // Function type
| Type '(' Sep(Type, ',') ')' // Type application
| '(' Type ')' // Parens
| 'unit' | Sep(Type, '*') // Tuples
| Id | QId | TVar
Domain ::= Type // Single argument
| '(' Sep(Type, ',') ')' // Multiple arguments
```
The function type arrow associates to the right.
Example,
```
'a => list('a) => (int * list('a))
```
### Statements
Function bodies are blocks of *statements*, where a statement is one of the following
```c
Stmt ::= 'switch' '(' Expr ')' Block(Case)
| 'if' '(' Expr ')' Block(Stmt)
| 'elif' '(' Expr ')' Block(Stmt)
| 'else' Block(Stmt)
| 'let' LetDef
| Expr
LetDef ::= Id Args [':' Type] '=' Block(Stmt) // Function definition
| Pattern '=' Block(Stmt) // Value definition
Case ::= Pattern '=>' Block(Stmt)
Pattern ::= Expr
```
`if` statements can be followed by zero or more `elif` statements and an optional final `else` statement. For example,
```
let x : int = 4
switch(f(x))
None => 0
Some(y) =>
if(y > 10)
"too big"
elif(y < 3)
"too small"
else
"just right"
```
### Expressions
```c
Expr ::= '(' LamArgs ')' '=>' Block(Stmt) // Anonymous function (x) => x + 1
| 'if' '(' Expr ')' Expr 'else' Expr // If expression if(x < y) y else x
| Expr ':' Type // Type annotation 5 : int
| Expr BinOp Expr // Binary operator x + y
| UnOp Expr // Unary operator ! b
| Expr '(' Sep(Expr, ',') ')' // Application f(x, y)
| Expr '.' Id // Projection state.x
| Expr '[' Expr ']' // Map lookup map[key]
| Expr '{' Sep(FieldUpdate, ',') '}' // Record or map update r{ fld[key].x = y }
| '[' Sep(Expr, ',') ']' // List [1, 2, 3]
| '[' Expr '|' Sep(Generator, ',') ']'
// List comprehension [k | x <- [1], if (f(x)), let k = x+1]
| '[' Expr '..' Expr ']' // List range [1..n]
| '{' Sep(FieldUpdate, ',') '}' // Record or map value {x = 0, y = 1}, {[key] = val}
| '(' Expr ')' // Parens (1 + 2) * 3
| Id | Con | QId | QCon // Identifiers x, None, Map.member, AELib.Token
| Int | Bytes | String | Char // Literals 123, 0xff, #00abc123, "foo", '%'
| AccountAddress | ContractAddress // Chain identifiers
| OracleAddress | OracleQueryId // Chain identifiers
Generator ::= Pattern '<-' Expr // Generator
| 'if' '(' Expr ')' // Guard
| LetDef // Definition
LamArgs ::= '(' Sep(LamArg, ',') ')'
LamArg ::= Id [':' Type]
FieldUpdate ::= Path '=' Expr
Path ::= Id // Record field
| '[' Expr ']' // Map key
| Path '.' Id // Nested record field
| Path '[' Expr ']' // Nested map key
BinOp ::= '||' | '&&' | '<' | '>' | '=<' | '>=' | '==' | '!='
| '::' | '++' | '+' | '-' | '*' | '/' | 'mod' | '^'
UnOp ::= '-' | '!'
```
### Operators types
| Operators | Type
| --- | ---
| `-` `+` `*` `/` `mod` `^` | arithmetic operators
| `!` `&&` `\|\|` | logical operators
| `==` `!=` `<` `>` `=<` `>=` | comparison operators
| `::` `++` | list operators
### Operator precendences
In order of highest to lowest precedence.
| Operators | Associativity
| --- | ---
| `!` | right
| `^` | left
| `*` `/` `mod` | left
| `-` (unary) | right
| `+` `-` | left
| `::` `++` | right
| `<` `>` `=<` `>=` `==` `!=` | none
| `&&` | right
| `\|\|` | right
## Examples
```sophia
/*
* A simple crowd-funding example
*/
contract FundMe =
record spend_args = { recipient : address,
amount : int }
record state = { contributions : map(address, int),
total : int,
beneficiary : address,
deadline : int,
goal : int }
stateful function spend(args : spend_args) =
Chain.spend(args.recipient, args.amount)
entrypoint init(beneficiary, deadline, goal) : state =
{ contributions = {},
beneficiary = beneficiary,
deadline = deadline,
total = 0,
goal = goal }
function is_contributor(addr) =
Map.member(addr, state.contributions)
stateful entrypoint contribute() =
if(Chain.block_height >= state.deadline)
spend({ recipient = Call.caller, amount = Call.value }) // Refund money
false
else
let amount =
switch(Map.lookup(Call.caller, state.contributions))
None => Call.value
Some(n) => n + Call.value
put(state{ contributions[Call.caller] = amount,
total @ tot = tot + Call.value })
true
stateful entrypoint withdraw() =
if(Chain.block_height < state.deadline)
abort("Cannot withdraw before deadline")
if(Call.caller == state.beneficiary)
withdraw_beneficiary()
elif(is_contributor(Call.caller))
withdraw_contributor()
else
abort("Not a contributor or beneficiary")
stateful function withdraw_beneficiary() =
require(state.total >= state.goal, "Project was not funded")
spend({recipient = state.beneficiary,
amount = Contract.balance })
stateful function withdraw_contributor() =
if(state.total >= state.goal)
abort("Project was funded")
let to = Call.caller
spend({recipient = to,
amount = state.contributions[to]})
put(state{ contributions @ c = Map.delete(to, c) })
```
### Delegation signature
## Delegation signature
Some chain operations (`Oracle.<operation>` and `AENS.<operation>`) have an
optional delegation signature. This is typically used when a user/accounts

View File

@ -5,26 +5,26 @@ check if the comments in the sources are up to date as well. You may find them
in the priv/stdlib directory. Thanks!
-->
# Standard library
# Standard Library
Sophia language offers standard library that consists of several namespaces. Some of them are already
in the scope and do not need any actions to be used, while the others require some files to be included.
The out-of-the-box namespaces are:
- [Bits](#Bits)
- [Bytes](#Bytes)
- [Char](#Char)
- [Int](#Int)
- [Map](#Map)
- [Address](#Address)
- [Crypto](#Crypto)
- [Auth](#Auth)
- [Oracle](#Oracle)
- [AENS](#AENS)
- [Contract](#Contract)
- [Call](#Call)
- [Chain](#Chain)
- [Bits](#bits)
- [Bytes](#bytes)
- [Char](#char)
- [Int](#int)
- [Map](#map)
- [Address](#address)
- [Crypto](#crypto)
- [Auth](#auth)
- [Oracle](#oracle)
- [AENS](#aens)
- [Contract](#contract)
- [Call](#call)
- [Chain](#chain)
The following ones need to be included as regular files with `.aes` suffix, for example
```
@ -41,11 +41,11 @@ include "List.aes"
- [Frac](#Frac)
- [Set](#set-stdlib)
# Builtin namespaces
## Builtin namespaces
They are available without any explicit includes.
## Bits
### Bits
#### none
```
@ -119,7 +119,7 @@ Bits.difference(a : bits, b : bits) : bits
Each bit is true if and only if it was 1 in `a` and 0 in `b`
## Bytes
### Bytes
#### to_int
```
@ -153,7 +153,7 @@ Bytes.split(a : bytes(m + n)) : bytes(m) * bytes(n)
Splits a byte array at given index
## Char
### Char
#### to_int
```
@ -172,7 +172,7 @@ Char.from_int(i : int) : option(char)
Opposite of [to_int](#to_int). Returns `None` if the integer doesn't correspond to a single (normalized) codepoint.
## Int
### Int
#### to_str
```
@ -182,7 +182,7 @@ Int.to_str : int => string
Casts integer to string using decimal representation
## Map
### Map
#### lookup
`Map.lookup(k : 'k, m : map('k, 'v)) : option('v)`
@ -229,7 +229,7 @@ Turns a list of pairs of form `(key, value)` into a map
## Address
### Address
#### to_str
```
@ -271,7 +271,7 @@ Address.to_contract(a : address) : C
Cast address to contract type C (where `C` is a contract)
## Crypto
### Crypto
#### sha3
```
@ -328,7 +328,7 @@ Crypto.verify_sig_secp256k1(msg : hash, pubkey : bytes(64), sig : bytes(64)) : b
<!-- TODO -->
## Auth
### Auth
#### tx
@ -368,7 +368,7 @@ Auth.tx_hash : option(hash)
Gets the transaction hash during authentication.
## Oracle
### Oracle
#### register
```
@ -380,7 +380,7 @@ Registers new oracle answering questions of type `'a` with answers of type `'b`.
* The `acct` is the address of the oracle to register (can be the same as the contract).
* `signature` is a signature proving that the contract is allowed to register the account -
the `network id` + `account address` + `contract address` (concatenated as byte arrays) is
[signed](./sophia.md#delegation-signature) with the
[signed](./sophia_features.md#delegation-signature) with the
private key of the account, proving you have the private key of the oracle to be. If the
address is the same as the contract `sign` is ignored and can be left out entirely.
* The `qfee` is the minimum query fee to be paid by a user when asking a question of the oracle.
@ -413,7 +413,7 @@ Responds to the question `q` on `o`.
Unless the contract address is the same as the oracle address the `signature`
(which is an optional, named argument)
needs to be provided. Proving that we have the private key of the oracle by
[signing](./sophia.md#delegation-signature)
[signing](./sophia_features.md#delegation-signature)
the `network id` + `oracle query id` + `contract address`
@ -481,7 +481,7 @@ Oracle.check_query(o : oracle('a, 'b), q : oracle_query('a, 'b)) : bool
It returns `true` iff the oracle query exist and has the expected type.
## AENS
### AENS
The following functionality is available for interacting with the Aeternity
Naming System (AENS).
@ -491,15 +491,15 @@ a signature to prove that we are allowed to do AENS operations on behalf of
`owner`. The [signature is tied to a network id](https://github.com/aeternity/protocol/blob/iris/consensus/consensus.md#transaction-signature),
i.e. the signature material should be prefixed by the network id.
### Types
#### Types
#### name
##### name
```
datatype name = Name(address, Chain.ttl, map(string, AENS.pointee))
```
#### pointee
##### pointee
```
datatype pointee = AccountPt(address) | OraclePt(address)
@ -507,9 +507,9 @@ datatype pointee = AccountPt(address) | OraclePt(address)
```
### Functions
#### Functions
#### resolve
##### resolve
```
AENS.resolve(name : string, key : string) : option('a)
```
@ -520,7 +520,7 @@ associated with this name (for instance `"account_pubkey"`). The return type
type checked against this type at run time.
#### lookup
##### lookup
```
AENS.lookup(name : string) : option(AENS.name)
```
@ -534,53 +534,53 @@ let Some(Name(owner, FixedTTL(expiry), ptrs)) = AENS.lookup("example.chain")
```
#### preclaim
##### preclaim
```
AENS.preclaim(owner : address, commitment_hash : hash, <signature : signature>) : unit
```
The [signature](./sophia.md#delegation-signature) should be over
The [signature](./sophia_features.md#delegation-signature) should be over
`network id` + `owner address` + `Contract.address` (concatenated as byte arrays).
#### claim
##### claim
```
AENS.claim(owner : address, name : string, salt : int, name_fee : int, <signature : signature>) : unit
```
The [signature](./sophia.md#delegation-signature) should be over
The [signature](./sophia_features.md#delegation-signature) should be over
`network id` + `owner address` + `name_hash` + `Contract.address`
(concatenated as byte arrays)
using the private key of the `owner` account for signing.
#### transfer
##### transfer
```
AENS.transfer(owner : address, new_owner : address, name : string, <signature : signature>) : unit
```
Transfers name to the new owner.
The [signature](./sophia.md#delegation-signature) should be over
The [signature](./sophia_features.md#delegation-signature) should be over
`network id` + `owner address` + `name_hash` + `Contract.address`
(concatenated as byte arrays)
using the private key of the `owner` account for signing.
#### revoke
##### revoke
```
AENS.revoke(owner : address, name : string, <signature : signature>) : unit
```
Revokes the name to extend the ownership time.
The [signature](./sophia.md#delegation-signature) should be over
The [signature](./sophia_features.md#delegation-signature) should be over
`network id` + `owner address` + `name_hash` + `Contract.address`
(concatenated as byte arrays)
using the private key of the `owner` account for signing.
#### update
##### update
```
AENS.update(owner : address, name : string, expiry : option(Chain.ttl), client_ttl : option(int),
new_ptrs : map(string, AENS.pointee), <signature : signature>) : unit
@ -591,7 +591,7 @@ will not be updated, for example if `None` is passed as `expiry` the expiry
block of the name is not changed.
## Contract
### Contract
Values related to the current contract
@ -619,7 +619,7 @@ Contract.balance : int
Amount of coins in the contract account
## Call
### Call
Values related to the call to the current contract
@ -670,13 +670,13 @@ Call.gas_left() : int
The amount of gas left for the current call.
## Chain
### Chain
Values and functions related to the chain itself and other entities that live on it.
### Types
#### Types
#### tx
##### tx
```
record tx = { paying_for : option(Chain.paying_for_tx)
, ga_metas : list(Chain.ga_meta_tx)
@ -686,18 +686,18 @@ record tx = { paying_for : option(Chain.paying_for_tx)
, tx : Chain.base_tx }
```
#### ga_meta_tx
##### ga_meta_tx
```
datatype ga_meta_tx = GAMetaTx(address, int)
```
#### paying_for_tx
##### paying_for_tx
```
datatype paying_for_tx = PayingForTx(address, int)
```
#### base_tx
##### base_tx
```
datatype base_tx = SpendTx(address, int, string)
| OracleRegisterTx | OracleQueryTx | OracleResponseTx | OracleExtendTx
@ -711,9 +711,9 @@ datatype base_tx = SpendTx(address, int, string)
```
### Functions
#### Functions
#### balance
##### balance
```
Chain.balance(a : address) : int
```
@ -721,7 +721,7 @@ Chain.balance(a : address) : int
The balance of account `a`.
#### block_hash
##### block_hash
```
Chain.block_hash(h : int) : option(bytes(32))
```
@ -734,7 +734,7 @@ allowed height. From FATE VM version 2 (IRIS) it will return the block hash of
the current generation.
#### block_height
##### block_height
```
Chain.block_height : int"
```
@ -742,7 +742,7 @@ Chain.block_height : int"
The height of the current block (i.e. the block in which the current call will be included).
#### coinbase
##### coinbase
```
Chain.coinbase : address
```
@ -750,7 +750,7 @@ Chain.coinbase : address
The address of the account that mined the current block.
#### timestamp
##### timestamp
```
Chain.timestamp : int
```
@ -758,7 +758,7 @@ Chain.timestamp : int
The timestamp of the current block.
#### difficulty
##### difficulty
```
Chain.difficulty : int
```
@ -766,7 +766,7 @@ Chain.difficulty : int
The difficulty of the current block.
#### gas
##### gas
```
Chain.gas_limit : int
```
@ -774,7 +774,7 @@ Chain.gas_limit : int
The gas limit of the current block.
#### bytecode_hash
##### bytecode_hash
```
Chain.bytecode_hash : 'c => option(hash)
```
@ -785,7 +785,7 @@ instantiated with a contract. The charged gas increases linearly to
the size of the serialized bytecode of the deployed contract.
#### create
##### create
```
Chain.create(value : int, ...) => 'c
```
@ -837,7 +837,7 @@ main contract Market =
The typechecker must be certain about the created contract's type, so it is
worth writing it explicitly as shown in the example.
#### clone
##### clone
```
Chain.clone : ( ref : 'c, gas : int, value : int, protected : bool, ...
) => if(protected) option('c) else 'c
@ -895,18 +895,18 @@ implementation of the `init` function does not actually return `state`, but
calls `put` instead. Moreover, FATE prevents even handcrafted calls to `init`.
#### event
##### event
```
Chain.event(e : event) : unit
```
Emits the event. To use this function one needs to define the `event` type as a `datatype` in the contract.
# Includable namespaces
## Includable namespaces
These need to be explicitly included (with `.aes` suffix)
## List
### List
This module contains common operations on lists like constructing, querying, traversing etc.
@ -1253,7 +1253,7 @@ List.enumerate(l : list('a)) : list(int * 'a)
Equivalent to [zip](#zip) with `[0..length(l)]`, but slightly faster.
## Option
### Option
Common operations on `option` types and lists of `option`s.
@ -1438,7 +1438,7 @@ Option.choose_first(l : list(option('a))) : option('a)
Same as [choose](#choose), but chooses from a list insted of two arguments.
## String
### String
Operations on the `string` type. A `string` is a UTF-8 encoded byte array.
@ -1554,7 +1554,7 @@ blake2b(s : string) : hash
Computes the Blake2B hash of the string.
## Func
### Func
Functional combinators.
@ -1684,7 +1684,7 @@ Func.untuplify3(f : 'a * 'b * 'c => 'd) : ('a, 'b, 'c) => 'd
Opposite to [tuplify](#tuplify).
## Pair
### Pair
Common operations on 2-tuples.
@ -1736,7 +1736,7 @@ Pair.swap(t : ('a * 'b)) : ('b * 'a)
Swaps elements.
## Triple
### Triple
#### fst
```
@ -1817,234 +1817,234 @@ Triple.rotl(t : ('a * 'b * 'c)) : ('b * 'c * 'a)
Cyclic rotation of the elements to the left.
## BLS12\_381
### BLS12\_381
### Types
#### Types
#### fp
##### fp
Built-in (Montgomery) integer representation 32 bytes
#### fr
##### fr
Built-in (Montgomery) integer representation 48 bytes
#### fp2
##### fp2
```
record fp2 = { x1 : fp, x2 : fp }`
```
#### g1
##### g1
```
record g1 = { x : fp, y : fp, z : fp }
```
#### g2
##### g2
```
record g2 = { x : fp2, y : fp2, z : fp2 }
```
#### gt
##### gt
```
record gt = { x1 : fp, x2 : fp, x3 : fp, x4 : fp, x5 : fp, x6 : fp, x7 : fp, x8 : fp, x9 : fp, x10 : fp, x11 : fp, x12 : fp }
```
### Functions
#### Functions
#### pairing\_check
##### pairing\_check
```
BLS12_381.pairing_check(xs : list(g1), ys : list(g2)) : bool
```
Pairing check of a list of points, `xs` and `ys` should be of equal length.
#### int_to_fr
##### int_to_fr
```
BLS12_381.int_to_fr(x : int) : fr
```
Convert an integer to an `fr` - a 32 bytes internal (Montgomery) integer representation.
#### int_to_fp
##### int_to_fp
```
BLS12_381.int_to_fp(x : int) : fp
```
Convert an integer to an `fp` - a 48 bytes internal (Montgomery) integer representation.
#### fr_to_int
##### fr_to_int
```
BLS12_381.fr_to_int(x : fr) : int
```
Convert a `fr` value into an integer.
#### fp_to_int
##### fp_to_int
```
BLS12_381.fp_to_int(x : fp) : int
```
Convert a `fp` value into an integer.
#### mk_g1
##### mk_g1
```
BLS12_381.mk_g1(x : int, y : int, z : int) : g1
```
Construct a `g1` point from three integers.
#### mk_g2
##### mk_g2
```
BLS12_381.mk_g2(x1 : int, x2 : int, y1 : int, y2 : int, z1 : int, z2 : int) : g2
```
Construct a `g2` point from six integers.
#### g1_neg
##### g1_neg
```
BLS12_381.g1_neg(p : g1) : g1
```
Negate a `g1` value.
#### g1_norm
##### g1_norm
```
BLS12_381.g1_norm(p : g1) : g1
```
Normalize a `g1` value.
#### g1_valid
##### g1_valid
```
BLS12_381.g1_valid(p : g1) : bool
```
Check that a `g1` value is a group member.
#### g1_is_zero
##### g1_is_zero
```
BLS12_381.g1_is_zero(p : g1) : bool
```
Check if a `g1` value corresponds to the zero value of the group.
#### g1_add
##### g1_add
```
BLS12_381.g1_add(p : g1, q : g1) : g1
```
Add two `g1` values.
#### g1_mul
##### g1_mul
```
BLS12_381.g1_mul(k : fr, p : g1) : g1
```
Scalar multiplication for `g1`.
#### g2_neg
##### g2_neg
```
BLS12_381.g2_neg(p : g2) : g2
```
Negate a `g2` value.
#### g2_norm
##### g2_norm
```
BLS12_381.g2_norm(p : g2) : g2
```
Normalize a `g2` value.
#### g2_valid
##### g2_valid
```
BLS12_381.g2_valid(p : g2) : bool
```
Check that a `g2` value is a group member.
#### g2_is_zero
##### g2_is_zero
```
BLS12_381.g2_is_zero(p : g2) : bool
```
Check if a `g2` value corresponds to the zero value of the group.
#### g2_add
##### g2_add
```
BLS12_381.g2_add(p : g2, q : g2) : g2
```
Add two `g2` values.
#### g2_mul
##### g2_mul
```
BLS12_381.g2_mul(k : fr, p : g2) : g2
```
Scalar multiplication for `g2`.
#### gt_inv
##### gt_inv
```
BLS12_381.gt_inv(p : gt) : gt
```
Invert a `gt` value.
#### gt_add
##### gt_add
```
BLS12_381.gt_add(p : gt, q : gt) : gt
```
Add two `gt` values.
#### gt_mul
##### gt_mul
```
BLS12_381.gt_mul(p : gt, q : gt) : gt
```
Multiply two `gt` values.
#### gt_pow
##### gt_pow
```
BLS12_381.gt_pow(p : gt, k : fr) : gt
```
Calculate exponentiation `p ^ k`.
#### gt_is_one
##### gt_is_one
```
BLS12_381.gt_is_one(p : gt) : bool
```
Compare a `gt` value to the unit value of the Gt group.
#### pairing
##### pairing
```
BLS12_381.pairing(p : g1, q : g2) : gt
```
Compute the pairing of a `g1` value and a `g2` value.
#### miller_loop
##### miller_loop
```
BLS12_381.miller_loop(p : g1, q : g2) : gt
```
Do the Miller loop stage of pairing for `g1` and `g2`.
#### final_exp
##### final_exp
```
BLS12_381.final_exp(p : gt) : gt
```
Perform the final exponentiation step of pairing for a `gt` value.
## Frac
### Frac
This namespace provides operations on rational numbers. A rational number is represented
as a fraction of two integers which are stored internally in the `frac` datatype.
@ -2068,9 +2068,9 @@ language provides checkers to prevent unintended usage of them. Therefore the ty
**will** allow that and the results of such comparison will be unspecified.
You should use [lt](#lt), [geq](#geq), [eq](#eq) etc instead.
### Types
#### Types
#### frac
##### frac
```
datatype frac = Pos(int, int) | Zero | Neg(int, int)
```
@ -2079,194 +2079,194 @@ Internal representation of fractional numbers. First integer encodes the numerat
both must be always positive, as the sign is being handled by the choice of the constructor.
### Functions
#### Functions
#### make_frac
##### make_frac
`Frac.make_frac(n : int, d : int) : frac`
Creates a fraction out of numerator and denominator. Automatically normalizes, so
`make_frac(2, 4)` and `make_frac(1, 2)` will yield same results.
#### num
##### num
`Frac.num(f : frac) : int`
Returns the numerator of a fraction.
#### den
##### den
`Frac.den(f : frac) : int`
Returns the denominator of a fraction.
#### to_pair
##### to_pair
`Frac.to_pair(f : frac) : int * int`
Turns a fraction into a pair of numerator and denominator.
#### sign
##### sign
`Frac.sign(f : frac) : int`
Returns the signum of a fraction, -1, 0, 1 if negative, zero, positive respectively.
#### to_str
##### to_str
`Frac.to_str(f : frac) : string`
Conversion to string. Does not display division by 1 or denominator if equals zero.
#### simplify
##### simplify
`Frac.simplify(f : frac) : frac`
Reduces fraction to normal form if for some reason it is not in it.
#### eq
##### eq
`Frac.eq(a : frac, b : frac) : bool`
Checks if `a` is equal to `b`.
#### neq
##### neq
`Frac.neq(a : frac, b : frac) : bool`
Checks if `a` is not equal to `b`.
#### geq
##### geq
`Frac.geq(a : frac, b : frac) : bool`
Checks if `a` is greater or equal to `b`.
#### leq
##### leq
`Frac.leq(a : frac, b : frac) : bool`
Checks if `a` is lesser or equal to `b`.
#### gt
##### gt
`Frac.gt(a : frac, b : frac) : bool`
Checks if `a` is greater than `b`.
#### lt
##### lt
`Frac.lt(a : frac, b : frac) : bool`
Checks if `a` is lesser than `b`.
#### min
##### min
`Frac.min(a : frac, b : frac) : frac`
Chooses lesser of the two fractions.
#### max
##### max
`Frac.max(a : frac, b : frac) : frac`
Chooses greater of the two fractions.
#### abs
##### abs
`Frac.abs(f : frac) : frac`
Absolute value.
#### from_int
##### from_int
`Frac.from_int(n : int) : frac`
From integer conversion. Effectively `make_frac(n, 1)`.
#### floor
##### floor
`Frac.floor(f : frac) : int`
Rounds a fraction to the nearest lesser or equal integer.
#### ceil
##### ceil
`Frac.ceil(f : frac) : int`
Rounds a fraction to the nearest greater or equal integer.
#### round_to_zero
##### round_to_zero
`Frac.round_to_zero(f : frac) : int`
Rounds a fraction towards zero.
Effectively `ceil` if lesser than zero and `floor` if greater.
#### round_from_zero
##### round_from_zero
`Frac.round_from_zero(f : frac) : int`
Rounds a fraction from zero.
Effectively `ceil` if greater than zero and `floor` if lesser.
#### round
##### round
`Frac.round(f : frac) : int`
Rounds a fraction to a nearest integer. If two integers are in the same distance it
will choose the even one.
#### add
##### add
`Frac.add(a : frac, b : frac) : frac`
Sum of the fractions.
#### neg
##### neg
`Frac.neg(a : frac) : frac`
Negation of the fraction.
#### sub
##### sub
`Frac.sub(a : frac, b : frac) : frac`
Subtraction of two fractions.
#### inv
##### inv
`Frac.inv(a : frac) : frac`
Inverts a fraction. Throws error if `a` is zero.
#### mul
##### mul
`Frac.mul(a : frac, b : frac) : frac`
Multiplication of two fractions.
#### div
##### div
`Frac.div(a : frac, b : frac) : frac`
Division of two fractions.
#### int_exp
##### int_exp
`Frac.int_exp(b : frac, e : int) : frac`
Takes `b` to the power of `e`. The exponent can be a negative value.
#### optimize
##### optimize
`Frac.optimize(f : frac, loss : frac) : frac`
Shrink the internal size of a fraction as much as possible by approximating it to the
point where the error would exceed the `loss` value.
#### is_sane
##### is_sane
`Frac.is_sane(f : frac) : bool`
For debugging. If it ever returns false in a code that doesn't call `frac` constructors or

262
docs/sophia_syntax.md Normal file
View File

@ -0,0 +1,262 @@
# Syntax
## Lexical syntax
### Comments
Single line comments start with `//` and block comments are enclosed in `/*`
and `*/` and can be nested.
### Keywords
```
contract elif else entrypoint false function if import include let mod namespace
private payable stateful switch true type record datatype main interface
```
### Tokens
- `Id = [a-z_][A-Za-z0-9_']*` identifiers start with a lower case letter.
- `Con = [A-Z][A-Za-z0-9_']*` constructors start with an upper case letter.
- `QId = (Con\.)+Id` qualified identifiers (e.g. `Map.member`)
- `QCon = (Con\.)+Con` qualified constructor
- `TVar = 'Id` type variable (e.g `'a`, `'b`)
- `Int = [0-9]+(_[0-9]+)*|0x[0-9A-Fa-f]+(_[0-9A-Fa-f]+)*` integer literal with optional `_` separators
- `Bytes = #[0-9A-Fa-f]+(_[0-9A-Fa-f]+)*` byte array literal with optional `_` separators
- `String` string literal enclosed in `"` with escape character `\`
- `Char` character literal enclosed in `'` with escape character `\`
- `AccountAddress` base58-encoded 32 byte account pubkey with `ak_` prefix
- `ContractAddress` base58-encoded 32 byte contract address with `ct_` prefix
- `OracleAddress` base58-encoded 32 byte oracle address with `ok_` prefix
- `OracleQueryId` base58-encoded 32 byte oracle query id with `oq_` prefix
Valid string escape codes are
| Escape | ASCII | |
|---------------|-------------|---|
| `\b` | 8 | |
| `\t` | 9 | |
| `\n` | 10 | |
| `\v` | 11 | |
| `\f` | 12 | |
| `\r` | 13 | |
| `\e` | 27 | |
| `\xHexDigits` | *HexDigits* | |
See the [identifier encoding scheme](https://github.com/aeternity/protocol/blob/master/node/api/api_encoding.md) for the
details on the base58 literals.
## Layout blocks
Sophia uses Python-style layout rules to group declarations and statements. A
layout block with more than one element must start on a separate line and be
indented more than the currently enclosing layout block. Blocks with a single
element can be written on the same line as the previous token.
Each element of the block must share the same indentation and no part of an
element may be indented less than the indentation of the block. For instance
```sophia
contract Layout =
function foo() = 0 // no layout
function bar() = // layout block starts on next line
let x = foo() // indented more than 2 spaces
x
+ 1 // the '+' is indented more than the 'x'
```
## Notation
In describing the syntax below, we use the following conventions:
- Upper-case identifiers denote non-terminals (like `Expr`) or terminals with
some associated value (like `Id`).
- Keywords and symbols are enclosed in single quotes: `'let'` or `'='`.
- Choices are separated by vertical bars: `|`.
- Optional elements are enclosed in `[` square brackets `]`.
- `(` Parentheses `)` are used for grouping.
- Zero or more repetitions are denoted by a postfix `*`, and one or more
repetitions by a `+`.
- `Block(X)` denotes a layout block of `X`s.
- `Sep(X, S)` is short for `[X (S X)*]`, i.e. a possibly empty sequence of `X`s
separated by `S`s.
- `Sep1(X, S)` is short for `X (S X)*`, i.e. same as `Sep`, but must not be empty.
## Declarations
A Sophia file consists of a sequence of *declarations* in a layout block.
```c
File ::= Block(TopDecl)
TopDecl ::= ['payable'] 'contract' Con '=' Block(Decl)
| 'namespace' Con '=' Block(Decl)
| '@compiler' PragmaOp Version
| 'include' String
Decl ::= 'type' Id ['(' TVar* ')'] '=' TypeAlias
| 'record' Id ['(' TVar* ')'] '=' RecordType
| 'datatype' Id ['(' TVar* ')'] '=' DataType
| (EModifier* 'entrypoint' | FModifier* 'function') Block(FunDecl)
FunDecl ::= Id ':' Type // Type signature
| Id Args [':' Type] '=' Block(Stmt) // Definition
PragmaOp ::= '<' | '=<' | '==' | '>=' | '>'
Version ::= Sep1(Int, '.')
EModifier ::= 'payable' | 'stateful'
FModifier ::= 'stateful' | 'private'
Args ::= '(' Sep(Pattern, ',') ')'
```
Contract declarations must appear at the top-level.
For example,
```sophia
contract Test =
type t = int
entrypoint add (x : t, y : t) = x + y
```
There are three forms of type declarations: type aliases (declared with the
`type` keyword), record type definitions (`record`) and data type definitions
(`datatype`):
```c
TypeAlias ::= Type
RecordType ::= '{' Sep(FieldType, ',') '}'
DataType ::= Sep1(ConDecl, '|')
FieldType ::= Id ':' Type
ConDecl ::= Con ['(' Sep1(Type, ',') ')']
```
For example,
```sophia
record point('a) = {x : 'a, y : 'a}
datatype shape('a) = Circle(point('a), 'a) | Rect(point('a), point('a))
type int_shape = shape(int)
```
## Types
```c
Type ::= Domain '=>' Type // Function type
| Type '(' Sep(Type, ',') ')' // Type application
| '(' Type ')' // Parens
| 'unit' | Sep(Type, '*') // Tuples
| Id | QId | TVar
Domain ::= Type // Single argument
| '(' Sep(Type, ',') ')' // Multiple arguments
```
The function type arrow associates to the right.
Example,
```sophia
'a => list('a) => (int * list('a))
```
## Statements
Function bodies are blocks of *statements*, where a statement is one of the following
```c
Stmt ::= 'switch' '(' Expr ')' Block(Case)
| 'if' '(' Expr ')' Block(Stmt)
| 'elif' '(' Expr ')' Block(Stmt)
| 'else' Block(Stmt)
| 'let' LetDef
| Expr
LetDef ::= Id Args [':' Type] '=' Block(Stmt) // Function definition
| Pattern '=' Block(Stmt) // Value definition
Case ::= Pattern '=>' Block(Stmt)
Pattern ::= Expr
```
`if` statements can be followed by zero or more `elif` statements and an optional final `else` statement. For example,
```sophia
let x : int = 4
switch(f(x))
None => 0
Some(y) =>
if(y > 10)
"too big"
elif(y < 3)
"too small"
else
"just right"
```
## Expressions
```c
Expr ::= '(' LamArgs ')' '=>' Block(Stmt) // Anonymous function (x) => x + 1
| 'if' '(' Expr ')' Expr 'else' Expr // If expression if(x < y) y else x
| Expr ':' Type // Type annotation 5 : int
| Expr BinOp Expr // Binary operator x + y
| UnOp Expr // Unary operator ! b
| Expr '(' Sep(Expr, ',') ')' // Application f(x, y)
| Expr '.' Id // Projection state.x
| Expr '[' Expr ']' // Map lookup map[key]
| Expr '{' Sep(FieldUpdate, ',') '}' // Record or map update r{ fld[key].x = y }
| '[' Sep(Expr, ',') ']' // List [1, 2, 3]
| '[' Expr '|' Sep(Generator, ',') ']'
// List comprehension [k | x <- [1], if (f(x)), let k = x+1]
| '[' Expr '..' Expr ']' // List range [1..n]
| '{' Sep(FieldUpdate, ',') '}' // Record or map value {x = 0, y = 1}, {[key] = val}
| '(' Expr ')' // Parens (1 + 2) * 3
| Id | Con | QId | QCon // Identifiers x, None, Map.member, AELib.Token
| Int | Bytes | String | Char // Literals 123, 0xff, #00abc123, "foo", '%'
| AccountAddress | ContractAddress // Chain identifiers
| OracleAddress | OracleQueryId // Chain identifiers
Generator ::= Pattern '<-' Expr // Generator
| 'if' '(' Expr ')' // Guard
| LetDef // Definition
LamArgs ::= '(' Sep(LamArg, ',') ')'
LamArg ::= Id [':' Type]
FieldUpdate ::= Path '=' Expr
Path ::= Id // Record field
| '[' Expr ']' // Map key
| Path '.' Id // Nested record field
| Path '[' Expr ']' // Nested map key
BinOp ::= '||' | '&&' | '<' | '>' | '=<' | '>=' | '==' | '!='
| '::' | '++' | '+' | '-' | '*' | '/' | 'mod' | '^'
UnOp ::= '-' | '!'
```
## Operators types
| Operators | Type
| --- | ---
| `-` `+` `*` `/` `mod` `^` | arithmetic operators
| `!` `&&` `\|\|` | logical operators
| `==` `!=` `<` `>` `=<` `>=` | comparison operators
| `::` `++` | list operators
## Operator precendences
In order of highest to lowest precedence.
| Operators | Associativity
| --- | ---
| `!` | right
| `^` | left
| `*` `/` `mod` | left
| `-` (unary) | right
| `+` `-` | left
| `::` `++` | right
| `<` `>` `=<` `>=` `==` `!=` | none
| `&&` | right
| `\|\|` | right

55
mkdocs.yml Normal file
View File

@ -0,0 +1,55 @@
site_name: aeternity Sophia Language
plugins:
- search
- mkdocs-simple-hooks:
hooks:
on_pre_build: 'docs.python.hooks:pre_build'
repo_url: 'https://github.com/aeternity/aesophia'
edit_uri: ''
extra:
version:
provider: mike
theme:
favicon: images/favicon.png
name: material
custom_dir: docs/mkdocs-overrides
language: en
palette:
- scheme: default
primary: pink
accent: pink
toggle:
icon: material/weather-night
name: Switch to dark mode
- scheme: slate
primary: pink
accent: pink
toggle:
icon: material/weather-sunny
name: Switch to light mode
features:
- content.tabs.link
- search.highlight
- search.share
- search.suggest
# Don't include MkDocs' JavaScript
include_search_page: false
search_index_only: true
markdown_extensions:
- admonition
- pymdownx.highlight
- pymdownx.superfences
- toc:
toc_depth: 3
nav:
- Introduction: index.md
- Syntax: sophia_syntax.md
- Features: sophia_features.md
- Standard Library: sophia_stdlib.md
- Contract Examples: sophia_examples.md
- Changelog: CHANGELOG.md