EVE Virtual Machine

EVE is the machine that runs Eve programs: a virtual machine that compiles a script in memory, runs it, manages its memory and hands the exit code back to the operating system. In this tutorial, "EVE" is the machine, "eve" is the language and the command, and "Eve" starts a sentence. This page describes the machine as the language sees it; how to build one is on the Compiler page.

Running a script

The machine is one program, eve. The official implementation is written in Zig, in the folder evevm/ of the repository, and built to bin/eve.exe. Eve 0.1 does one thing: it runs a script.


eve script.eve                 ** run a driver or a free script
eve script.eve 5 -p x 10       ** values for the parameters of main
eve script.eve -h              ** help, made from the ** @param comments above main
  • A short parameter name is written -p value, a long one --param value. A value is any Eve literal: it is parsed and converted to data;
  • The comments above main of the form ** @param x: "description" are the help text. Without them, -h lists the parameters;
  • The machine returns the exit code of the process to the operating system: 0 for return and over, 1 for panic, 2 for a failed expect, 4 for raise or a run-time error, 5 for an unexpected stop (see Processing).

What happens when a script runs

  1. Load: the machine reads the script and the modules it imports, searching them in $EVE_LIB and the folders of the project;
  2. Compile: the scripts are compiled in memory, to a tree in version 0.1. The compiler stops with an error before anything runs when it finds a mistake, for example a concurrent aspect that calls a function that is not thread safe;
  3. Initialize: every imported module is initialized once (initialize);
  4. Run: the process main of the driver runs, and applies or starts aspects;
  5. Finish: the modules are finalized in reverse order, the memory is released and the machine returns the exit code.

How the machine manages memory

A program never frees memory itself: EVE does it while the program runs. Every value that needs memory (a string, a list, a map, an object, a function) counts the places that hold it: variables, elements of collections, attributes, closures. This is reference counting. When the count of a value falls to zero, nobody can use it any more and its memory is given back.

The memory is given back at a safe point: at the end of a statement, at the end of each pass of a loop, and at done. A loop that makes a string and a list at each pass keeps only the last ones; a million passes need the memory of one.


for i in (1..1000000) do
  new line := "item {i}";        ** the previous line is freed at the end of the pass
  new parts := (i, i + 1, i + 2);
  let total += parts.length();
done;

Reference counting cannot free a cycle: a function that closes over the scope that holds it counts that scope, and the scope counts the function. A tracing collector is the backup: it finds what the program can still reach and frees the rest. It runs at the done of a job, of a parallel group and of a process, and when the program has made many values since its last run. While the aspects of a parallel group run, nothing is freed; the memory they left is given back at done.

  • EVE_MEMSTATS=1 eve script.eve prints, at the end, how many values were made, how many were freed by counting and by tracing, and how many are left;
  • The tests of the machine run with EVE_GC_VERIFY=1, which checks every free against a trace.

Configuration

EVE virtual machine can load system environment variables from a configuration file. These are stored as "$key = value" pairs, where the value is a valid Eve literal. Some system constants can be derived from environment variables using the smart concatenation operator "/".

A configuration file have extension .cfg. One application can run with different configuration file. Application documentation must contain description of all variables required by the application.

A good practice is to provide a configuration template . The template file may contain # comments and $key = value pairs. Values can be numeric, string, symbol, date, time or data literals: [],(),{}. Eve application will automatically parse configuration file to read values for the system variables.

System variables of the machine

The machine provides the system variables, which start with $. The folders below say where it looks for things; the full register, with types and status, is in spec/semantics/variables.md.

VariableMeaningDefault
$EVE_LIBfolder of the installed libraries, searched by from … use$EVE_DIR/lib
$EVE_ASPfolder of the aspects of a driverasp, then the project root, then lib
$EVE_OUToutput folder of the log filesout
$coressize of the worker pool for parallel aspectsnumber of cores, at most 16
$max_parallelaspects one parallel group may start8
$timeoutseconds a task waits on a channel60

Modes of the machine

Planned, not in 0.1: VM parameters, service mode, console REPL and exclusive mode are planned for a later version (about 0.9). Eve 0.1 only runs a script: eve script.eve.

Eve scripts are executed using a virtual machine. You can start the virtual machine as a service or as console application. In console you can run only one driver script at a time. In service mode you can run multiple drivers in parallel. Each driver is independent, it has its own scope (memory location) and can't communicate with other drivers executed by EVE in the same time.

Configuration:  Eve services using a general configuration file: eve.cfg. This file contains default settings for EVE machine. System variables are shared. A copy of EVE system variables is send to every new driver instance.

Configuration settings are loaded over the default values and have priority. In a process, you can temporary change the settings with an assignment. These changes do not propagate back into the configuration file.

Memory allocation: EVE service is in charge of allocating memory for each process. There is no shared memory between processes. When a process is terminated the memory is cleaned up of garbage and the machine continue to run until all processes are finished.

To execute a driver there are these ways:

  1. Using the "eve" system command: eve script.eve,
  2. Planned: using console REPL commands: parse, debug and execute (see manual/usage.md),
  3. Planned: EVE virtual machine can be run as a daemon,
  4. Planned: you can control EVE virtual machine using command: eve with parameters

Exclusive Mode

Planned, not in 0.1. When eve is executed, it can be started with parameter: -x or --exclusive. This is a signal that only one session can run at a time. In this mode a second driver is refused while any other driver is active. Asynchronous methods called inside a driver are not affected. You can use this more especially for debugging or tuning purpose.

Tools around the machine

  • eve --doc makes the documentation of a library from its ** comments and signatures;
  • The test runner script/runtest.py runs the conformance tests on any implementation (see Compiler);
  • The machine can also run remotely and serve requests: see Server.

Read next: Algorithms