Try Sophia, test your contracts, check “what if”, calculate a factorial. Completely offline, not depending on any remote (readonly sync may be included later).
If you are not familiar with Sophia, check its documentation first.
REPL is launched as an Erlang node. To use it one needs to start up the server and connect to it
using provided CLI client or via another erlang node. The server will be hosted on
localhost
under aerepl
name and aerepl_cookie
cookie.
Clone the repo:
git clone https://github.com/aeternity/aerepl.git
Build the project:
cd aerepl make
Launch the server (needs to be evaluated only once in the project directory)
./aerepl_server start
Launch the client
./aerepl
To shut the server down:
./aerepl_server stop
You can type any arbitrary Sophia expression to evaluate it, for instance
AESO> 2 + 2 4
Beside that, the REPL supports other functionally via commands that are prepended by :
.
The default command is :eval
, which is executed when no other command was chosen, for instance
AESO> :eval 2 + 2 4
Command | Description | Example |
eval | Evaluates an expression and prints the result. Can be used to insert toplevel declarations. | :eval 2 + 2 |
type | Queries expression for its type | :type 2 + 2 |
include | Includes a file. One can use regular syntax for includes as well. | :include List.aes |
uninclude | Unregisters all included files. May refuse to do so if some objects require some include. | :uninclude |
reinclude | Reloads all included files | :reinclude |
cd | Changes the working directory | :cd src |
ls | Lists unhidden contents of the current directory. Does not accept flags | :ls |
pwd | Displays the current working directory | :pwd |
set | Sets REPL’s internal variables or the current state. See this for more details | :set state 10 |
load | Loads a file and evaluates all of its contents line by line | :load script.repl |
deploy | Loads a contract from file and deploys it on a virtual blockchain exposing its interface for the REPL. See this. | :deploy C.aes as c |
rm | Removes a registered name along with all functions that depend on it | :rm x |
It is not required to type the entire command in every case – a prefix is just enough as long as it is not ambiguous.
For example, at the moment of writing :t 3
is equivalent to :type 3
. If a valid command is a proper prefix of another
one, it will take the prtiority when it is fully inserted.
The user can input let-definitions as they would do it inside a function body. The variable bindings are static, meaning that each successive redefinition will shadow the old ones instead of replacing them in previous contexts.
AESO> let x = 1 AESO> let y = x AESO> let (x, z) = (0, 200) AESO> x 0 AESO> y 1
Functions can be defined using the regular toplevel syntax. They can be both stateful
and payable
(see in-repl state and repl account).
It is usually better to define functions instead of entrypoints, because the latter one have bigger restrictions on their types.
Each function uses its own separate namespace, making them safe from redefinitions of other values they depend on. See the example
AESO> let x = 10 AESO> function f() = x AESO> function g() = f() AESO> let x = 99 AESO> g() 10 AESO> function f() = x AESO> g() 10 AESO> f() 99
If the user removes any value that some function depends on or redefines a state
type while some function is using it,
the function will need to be removed.
The REPL will inform about all implicit removals and ask the user if they are okay with that.
REPL allows type definitions in the same manner as contract/namespace toplevel.
Note that due to shadowing it will keep their definitions in separate namespaces,
which will result in possibly suspicious type
query outputs.
AESO> datatype d = D AESO> D : d D AESO> let x = D AESO> datatype d = D | E AESO> :type D TYPEDEF_2.d AESO> :type x TYPEDEF_0.d AESO> x == D REPL:0:0:Cannot unify TYPEDEF_0.d and TYPEDEF_2.dThe REPL can keep track of custom contracts by giving a possibility to compile and deploy them on its virtual blockchain. The user can make calls to the contracts completely freely as the contract reference is provided via regular variable. The variable name is optional – if not provided, the REPL will automatically choose a free one.
// file File.aes contract Con = type state = int entrypoint init() = 0 entrypoint get() = state stateful entrypoint inc() = put(state + 1) // REPL session AESO> :deploy File.aes c : Con was successfully deployed AESO> c.get() 0 AESO> c.inc() () AESO> c.get() 1
REPL variables can be used to adjust the shell’s behavior. They may be adjusted using :set
commands. Currently supported options are:
Value | Type of next arg | Description |
display-gas | true or false | Toggles display of used gas during evaluations. NOTE: This may not be 100% accurate. Treat it like a hint. |
display-deploy-gas | true or false | Toggles display of used gas during contract deploy. NOTE: This may be very inaccurate. Treat it like a hint. |
call-gas | Positive integer | Sets amount of gas to be provided to every evaluation |
call-value | Positive integer | Sets the amount of tokens to provide with each call. Makes sense mainly when calling tracked contracts |
balance | Positive integer | Sets the balance of the in-REPL account |
state | Sophia expresion | Updates the in-REPL state. See this |
colors | none , default , no-emph | Adjusts color display. none is colorless, default colorful and bold and no-emph is without emphasis |
silent | true or false | Toggles whether REPL should print non-error messages |
display-unit | true or false | If false, will not print () if the evaluation result is unit |
The REPL tracks its own internal state that works similarly to the contracts’ states.
By default its state is set to () : unit
, but it can be changed at any time via :set
command:
AESO> state () AESO> :set state 1 AESO> put(state + 1) () AESO> state 2
Defined functions can query and alter the state (as long as they are stateful
):
AESO> :set state 1 AESO> function f() = state AESO> f() 1 AESO> stateful function g() = put(state + 1) AESO> g() () AESO> f() 2
Changing the state using set
will remove all functions that use state
or put
along with all
others that depend on them.
The REPL has its own balance, which can be refilled by the value
variable and inspected by Chain.balance(Call.origin)
call.
It is mainly used for paying gas fees for internal calls and interacting with tracked contracts.
The newline symbol can be replaced by a semicolon. This is very straightforward and it is a temporary “feature” that will be removed.
It just allows to write quick oneliners like let x = 2;put(x)
. Remember about the indentation!
If you want to insert more complex multiline text you can do it in GHCi’s style using :{
to open and :}
to close your expression
AESO> :{ | let id(x) = x | id(2138) :} 2138
This way you can also write splitted definitions:
AESO> :{ | function | f : int => int | f(0) = 100 | f(x) = x :}
The lines with :{
and :}
symbols cannot contain any other non-white characters.
To run tests one just need to run
make test
Currently the tests cover only scenarios that can be inspected in the test/scenarios/
directory. A scenario file consists of regular queries that one would input to the
repl along with expected answers prepended with //
prefix. For example
2 + 2 // 4
If any repl-questions will appear, the testing suite will choose always the default option.
If you want to skip the error message, you can use the _
wildcard. Errors can be
caught using !error
, but for now error testing is limited only to the fact that
it has occured.
REPL internal errors will result in test failure regardles of this catch.
2 + 2 // _ 2 + "XD" // !error