Eve Scripts
Keywords of this phase
The keywords that phase 2, Eve Code Topology, adds to the core language (the core keywords are in Syntax). Each one is explained on the page in the last column.
| Keyword | Meaning | Explained in |
|---|---|---|
aspect | header of a script that a driver applies: aspect name is | Aspects |
module | header of a library script; end name; closes it | Modules |
from | starts an import: from lib use (a, b); | Modules |
use | lists the imported names of an import | Modules |
export | lists the members a module makes public: export (Point, square); | Modules |
initialize | initialization region of a module | Modules |
finalize | final region of a process: runs when the process ends normally, by exit or by abort | Scripts |
procedure | declares a procedure: a subprogram outside a class that does work and returns no result; it is called as a statement | Subprograms |
apply | runs an aspect and waits for it: apply report(10, @sum); | Aspects |
Projects
A project is a folder with a specific structure. It contains one or more applications that can run independent of each other on the same computer or a group of computers.
project structure
Next project contains two applications: server/agent and show folders where you should put your code (src+lib) and documentation (doc). This is a recommendation but not a hard rule.
$pro_home
project
|-- agent
| |-- aspect1.eve
| |-- aspect2.eve
| ...
|-- server
| |-- aspect1.eve
| |--- aspect2.eve
| ...
|-- library
| |-- module1
| | |-- module_script1.eve
| | |-- module_script2.eve
| | ...
| |-- module2
| ...
|
|-- documents
| |-- readme.md
| |-- index.html
| ...
|-- agent.eve
|-- server.eve
Free scripts
Not every program needs a driver. A free script is a file whose first line starts with #! (the shebang, for example #!/usr/bin/env eve). It holds sequential statements, one after another, and it needs no driver, no process, no return; and no end name;: the script ends at the end of the file. Only a file that starts with #! can be a free script; the same text without the first line is an error.
#!/usr/bin/env eve
** a free script: statements in sequence, it ends at the end of the file
new total = 0 :Integer;
for i in (1..10) do
let total += i;
done;
print "total: {total}";
Rules
- A free script has control statements:
if,loop,for,while, and alsoexpect,printand calls of library functions; - It has no declarations of subprograms: no functions, methods, classes or generators. To define them, write a driver, a module or an Eve macro;
- It has no jobs: jobs,
recoverandfinalizebelong to processes (see Processing), so an error that nobody handles ends the script, with the exit code of table in Processing; - It can import modules with
from … useand apply aspects; - It is the shortest way to a small tool, a test or an experiment, and the first step to a driver: when the script grows, move the statements into
process main is … return;of a driver. A#!file that declares a driver is no longer a free script but an Eve macro, which keeps the driver, its aspects and their shared declarations in the same file.
Which kind of script?
| Script | Starts with | Has a process | Can be run | Defines globals |
|---|---|---|---|---|
| free script | #! | no | yes, eve script.eve | its own variables |
| driver | driver name is | yes, main | yes | yes |
| aspect | aspect name is | yes, main | applied by a driver | no |
| Eve macro | #! or # title, then declarations | yes, main of its one driver | yes, eve macro.eve | yes, shared by its driver and its aspects |
| module | module name is | no, initialize and finalize | imported | no, one private state |
Scripts
Eve define several kind of scripts that together makes a "project suite". Some scripts are reusable but some are specific to a single project. Here are all the possible scripts:
- Driver, created with keyword: "driver",
- Aspect, created with keyword: "aspect",
- Module, created with keyword: "module",
Notes: One project can contain one or many drivers. Each driver is an independent script. A driver has a single executable process, main. A driver control the execution of aspects. An aspect is encapsulated and contains its own process, also named main. An aspect can receive parameters from the driver. An aspect or driver can "import" modules and can use classes or functions defined in these modules.
Features:
- A driver can import one or more modules;
- A driver applies aspects in a sequence, each one an independent piece of the work;
- A driver or aspect define a single executable process,
main; - An aspect or driver can import modules;
- Only a driver defines global states; every driver has its own global scope;
- An aspect has no public members; a module lists its public members in
export (…), and they are used with the dot operator.
Regions
A driver, aspect or module has a single scope. There are no import, alias, constant or global regions: imports (from … use), aliases (def), constants (set), variables (new), classes, functions, methods and processes are declarations in the same scope, in any order. Indentation is mandatory: everything between the header and end name; is indented by exactly 2 spaces, and the compiler reports an error when the indentation is wrong. Only the header, the closing end and the comments before the header start at column 0. A process has its own regions, recover and finalize, aligned with the process keyword. The canonical skeleton of a driver is below, of an aspect in Aspects and of a module in Modules.
Driver skeleton:
An Eve script begins with one of these keywords: {"driver", "aspect", "module"}. Script name is the same as the file name without the extension. A driver or aspect has no parameter list in its header: the parameters belong to its process, process main(parameters) is. The process is indented inside the driver or aspect. A module has no process and no parameters.
** purpose of the driver
driver driver_name is
** system variables and constants
set $sys_con = value;
set LIMIT = 10 :Integer;
** import modules
from $path/library_name use (*);
** alias: qualifier suppression
def AliasName = library_name.MemberName;
** constants
set NAME = Value :Type;
** classes, functions and global variables
class Person = {name:String, age:Integer} <: Object;
set expression_name = (params) => (expression);
new shared = value1 :user_type;
** main process
process main is
new my_var = value :Type; ** private to the process
** executable region
...
recover
** error handling area: retry, resume or abort
...
finalize
** release locked resources, consume the job report
...
return;
end driver_name;
Members
- a script can define members, visible in process;
- a script can import public members from modules;
- a driver can "set" system variables and constants using "$" sigil;
Note: All declarations sit directly under the script header, indented by 2 spaces. They belong to the global scope, which is bound to the process scope.
Note: Only a driver defines global states. In its global scope you can set constants and system variables, and define global variables, classes, functions and methods. Aspects and modules define public and private members instead (see Module Import).
The keyword "set" creates a constant; the keyword "new" creates a variable. Global constants and variables are static: they are created on the heap and have a permanent nature.
System Variables
System variables use prefix "$". These are defined and maintained by EVE virtual engine. Values for these variables can be read automatically by EVE engine at startup. Can be loaded from a configuration file (*.cfg). New variables can be defined by user for each script.
New system variables can be defined with the keyword set directly after the script header (or after the #! line), with 2-space indentation. These are constants that belong to the global scope, the same scope used by the driver.
Note:
- System variables can be defined in a configuration file,
- OS environment variables are visible as system variables: the variable NAME is read as $NAME,
- System variables are usually defined in core EVE modules,
- System variables can be defined or overwritten in program modules,
The system variables that Eve provides, with their types, are listed in one table: System Variables in the Syntax page.
Global Constants
The name of a constant is not enforced by the compiler; the tutorial only recommends it. By convention, constants use capital letters, and the next symbols in a constant name can be numbers or underscore but no special or Unicode character.
Example
** define global scope
set PI = 3.14 :Real; ** global constant
...
new var = 0 :Integer; ** global variable
...
Notes:
- You can create new global constants and variables;
- Prefix "$" for system constants to avoid scope qualifier;
- OS environment variables are visible as $NAME;
- System constants are capitalized and also begin with "$";
Example
driver test is
** driver context constant
set E = 2.718281828 :Real; ** Euler's number
** driver context: class and variable
class Person = {name:String, age:Integer} <: Object;
new x = 0 :Integer; ** global state
...
Notes:
- Global constants are created using keyword "set", global variables using keyword "new",
- All global states are bound to the process scope,
- We are using operator "=" for initialization,
- Operator ":=" executes an expression and can be used in the global scope, for example a string concatenation,
- Constants are by convention using uppercase letters, the compiler does not check it,
- $user_path is a shared variable defined by the user;
- spaces in script-names are not supported you must use: "_";
- aliases are created with "def" from public members of other modules;
- a path is a string, built with the smart concatenation operator /
Importing Modules
The import is used to include public members from modules:
Syntax:
driver script_name is
** define global system variable
set $user_path := root_path/relative_path;
from $user_path use (module_name,...); ** specific modules
from $user_path use (*); ** find all modules
...
** create alias for some of the hidden members
def new_name = module_name.member_name;
process main is
...
return;
end script_name;
Notes:
- A path is a string. The operator / is a "smart concatenation": between strings it joins path segments and it becomes \ on Windows;
- An alias is created with "def Name = library_name.MemberName;". An alias of a parameterized class, like ClassName[parameters], is not supported;
Process
A process is the executable region of a script, mandatory for drivers and aspects. A driver and an aspect have the same shape: declarations, then one process, process main is … return;, and the script closes with end script_name;. main is the only process name and the entry point. An aspect is run with apply aspect_name(args);: the caller waits.
A process can receive parameters, input and output (process name(n: Integer, @total: Integer) is). A process returns no result: its outputs are the parameters marked with "@". It can end with an error instead: the exit code of panic, raise, expect or assert is captured by the driver. The arguments are passed in a list, apply reports(10, @sum);. The caller waits, so the outputs are ready on the next statement. Only the driver defines a global scope: its process reads and changes the driver globals. An aspect has its own state and no globals: it receives data by parameters. The scope of an imported module is bound to the scope of the driver or aspect that imports it.
While modules don't use the 'process' keyword, they have an initialization region that serves a similar purpose. This initialization region is executed first when a process starts. For modules imported by a driver, the initialization runs within the driver's main process. When imported by an aspect, it runs in the process of that aspect, at each apply.
Process context
Every process has a local context. In this context you can define variables, and instantiate objects but you can't define classes, methods or functions. Process local members can't be shared but can be send as input/output parameters to subprograms or aspects.
Process Termination
Process can be early terminated using: "over" or "panic". This is a way to release all locked resources and terminate the process. The statement "over" ends the process it is in with code 0. The statement "panic" is an unhandled exception: it ends the whole application with code 1, not only the process; "panic 0" is not allowed and there is no "panic N". A failed "expect" always ends with code 2, and "raise" ends with an error code greater than 0.
Example:
Next code sequence is terminated after 11 iterations:
# terminate a program with: over
driver test_over is
process main is
loop
new i = 0 :Integer;
do
write "."; wait 10;
over if i > 10;
let i += 1;
repeat;
return;
end test_over;
Read next: Drivers