Sage-Code Laboratory

EVE Structure

EVE is a scripting language designed to quicly build an automation program that can be managed by a job schedule. Usually you do all the job in one script but sometimes you need to divide a project into parts. Therefore EVE has support for secondary scripts. The main script together with secondary scripts and library modules are stored in a folder structure we will explain below.


Next sections will teach you the structure of EVE projects.


EVE script

EVE enable you to create scripts that can be compiled in memory and can be run in a loop until a finalization condition force the program to terminate. EVE scripts are never compiled into an executable form. Every time a script runs, it create a log file that can be archived and investigate later.

Kind of Scirpts

EVE include several kind of scripts that together makes a "suite" of scripts, called project. Some scripts are reusable to many projects but some are specific to a single project. Here are all the possible scripts:

Notes: One project can contain one or many "driver scripts". A driver script can "import modules". A module is a non executable script that contains utility: constants, data types, functions, and routines.



Modules are source files having extension: *.eve. Module names are using lowercase letters, can contain underscore or digits but no special characters and no Unicode strings. Longer names that use several words can be separate with underscore. The module name can be 30 characters long.



library is a shared folder containing reusable modules.


An "aspect" is a script specific to a particular project. It resolve one aspect of a problem. Aspect files are executable scripts. You can call one aspect from another aspect or from the application driver.

An aspect have parameters and processing region. You can call an aspect by using keyword: "start". An aspect can fail or can pass. You can collect the aspect status to check if the aspect has pass or failed.


driver is the application main aspect. It has the role to lead the application logical thread. When driver execution is over the application give control back to the operating system.



One application can load system constants from a configuration file. These are stored as "$name:value" pairs. Some system constants can be derived from environment variables using concatenation operators "&","+", "/" or "\".

A configuration file have extension .cfg. It can be used by the compiler or by the application. One application can run with different configuration files. Application documentation must contain description of configuration constants.

Sometimes a file template is provided for copy and modify. Template file may contain comments using Bee syntax that you will learn later. Bee application will automatically parse configuration file to read values for: system constants.

Script Regions

A script file is divided into regions using keywords: {import, define, global, class, routine}. In each region you can declare one or single kind of members. Executable statements are enclosed in structures.

Syntax Pattern:

An EVE script start with optional directive: "driver", "aspect", "module". If none is specified, then we consider the file a module. That is a module can not be executed if is not imported in a driver or aspect.

# structure of a driver (pseudocode)
**   Header comments: module purpose    **
[driver | aspect | module] demo: //name of the scripot is "demo.eve"

** global region
    $sys_con = "value"; // system constant
    @sys_var = {1,2,3}; // system variable

** import region
    from $path/library_name use (*);

** qualifier suppression region
    ClassName = library_name.class_name;
    ClassName = class_name{generic_parameters};

** shared constants region
    TypeName NAME = value; // constant (UPPERCASE NAMES)

** function declaration region
    TypeName: name(params) => (expression);

** class declaration region
class ClassName(params) <: Superclass:

** routine declaration region
routine name(params):

** local declaration region
    TypeName x;         // default value = 0
    TypeName y = value; // specify value ≠ 0
   // executable region

Declaration order

Order of regions by default is: {globals, imports, aliases, constants, variables, functions, classes, routines, locals, process}. Routines and classes can alternate. You can define multiple regions of the same type when members depend on each other.


Globals are declared in first module region, with zero space indentation. There is no keyword "global". Global members are recognized by "sigil" $ or @. Therefore we do not need any keywords to declare globals:

Declare Globals

    $identifier := value;
    @identifier := value;

Once you have imported one module that have globals, you can access them without qualifiers. If names colide, the import can fail, you have to hide the global members that colide to be able to import.

System constants

System constants start with prefix "$". Usually are loaded from a configuration file (*.cfg) and do not need to be declared explicit in a constant region. They are known by EVE runtime environment and can be used in all modules.


Several system constants are provided by EVE environment:

System variables

System variables are starting with prefix "@" and are defined by standard library.


Import region

Is used to include members from several other modules into current module:


** define global constant
    $user_path := $root_path/relative_path

    from $user_path use (member_name,...);  // specific members
    from $user_path use (*);                // all public members


Shared constants

Shared constants are declared in constant region, with "." prefix.


    Double .PI = 3.14; // shared constant


Shared variables

Shared variables are declared in variable region:


#declare variables
Double pi = 3.14; //  shared variable



A function is a relation between some input values and output values. A function may or may not make a computation to establish the output. In EVE functions are pure and first class values.


Functions are declared in script regions that start with keyword: "function". A function can be public or private, local or global. Let's analyze the syntax:

#declare functions
    TypeName name(parameters) => (expression);


Function call The call for a function is using name of the function and round brackets () for arguments. The brackets are mandatory even if there are no arguments, otherwise the function is not executed.


#demo function call
driver function_call:

    TypeName: function_name(Type : param = value,...) => (expression);
    TypeName result; //not initialized (require store)    
    ** call with no arguments:
    result := function_name();
    ** call with arguments mapped by position
    result := function_name(value, ...);
    ** call using parameter names and pair-up operator ":"
    result := function_name(parameter:value ...);

Note: Argument value can be anything that translate to a value of expected type:

formal parameters

function call arguments

There is a difference between the parameter and the argument. The parameter is a local variable in the function scope while arguments are values assigned to these parameters with a function call. Arguments can be literals, constants or variables.


# demo function with parameters
driver function_params:

    Integer sum(Integer a, b) => (a + b); 
    print sum(1,2);  // 3
    print sum(2,4);  // 6


A routine is a named block of code that can be executed multiple times. A script can have one or more routines. Driver and aspects, must have one special routine that is called "main". Driver can accept a list of text parameters. Aspect can have other type of parameters inlcuding output parameters.

Syntax Pattern:

Routine with result:

aspect aspect_name(Type parameter, Type .result):
    ... other members
    ** declare local variables
    ** executable region
    if condition then 
    end if;
    result := value; //result parameter
return [result];


Routine name A routine is extending the language with domain specific algorithms. It must have suggestive names so that other developers can understand its purpose. The routines are doing something, therefore the best names for methods are verbs.

Parameters Formal parameters are defined in round brackets () separated by comma. Each parameter must have type and name. Using parameters require several conventions to resolve many requirements. General syntax for parameter name is:


Mandatory parameters do not have initial values but only type. Optional parameters have initial value.


  1. One routine can receive one or more parameters,
  2. Parameters having initial values are optional,
  3. Values used for parameters in routine call are called arguments,
  4. You can assign arguments by position using a list of values,
  5. You can assign arguments ny name using name:value pairs;

Variable list of arguments

One routine can receive multiple arguments of the same type separated by comma into a list.


# print all arguments
driver test_args:

routine test(List[String] *args) 
  print args;

** call routine with variable number of arguments
apply test ('a','b','c'); // use 3 arguments
apply test ('a','b');     // use 2 arguments

** you can use operator "*" to _spread_ collection elements
    Set[Integer] argument;
    argument := {1, 2, 3};
    ** transfer arguments by position using spreading operator (*)
    test (*argument);  

Routine context

Every routine has a local context. In this context a routine can define variables, functions and objects but not other routines nor clases. These members are private, can not be accessed from outside of the routine.

Routine call Routines can be used in call statements. A routine call can be done using "apply routine_name" followed by arguments. For one single argument, or no argument the parentheses are not required.

#these are all valid routine calls
apply routine_name; //call routine without arguments
apply routine_name argument_value;   //call with single argument
apply routine_name (value, value, ...);//call with a list of arguments
apply routine_name (param:value,...);  //call with a argument by name
apply routine_name (value, value, param:value); //mix position with names
apply routine_name (value, *list_args);  //mix position with list
apply routine_name (param:value, *map_args);   //mix names with dictionary

Routine termination A routine end with keyword return; When routine is terminated, program execution will continue with the next statement after the routine call. Keyword exit can terminate a routine early. If the result do not have initial value and routine terminate early the result may be null.


# driver with parameters
driver routine_call(List[String]: *args):

routine test(Integer a): 
    ** print is a system routine
    print 'argument list has # members' ? a; 

    ** number of arguments received:
    Integer c := args.length();
    ** verify condition and exit
    exit if c == 0;  // early interruption  
    apply test(c);   // routine call

Side Effects

A routine can have side-effects:

using side-effects

Next routine: "add_numbers" has 2 side effects:

#test side-effects
driver side_effect:

routine add_numbers():
    **side effects  
    test := p1 + p2; //  first side-effect
    print test;      //  second side-effect

** local variables
    Integer test; 
    Integer p1;   
    Integer p2;   
    p1 := 10;    //  side effect
    p2 := 20;    //  side effect
    add_numbers; //  call routine add_numbers;
    expect (result == 30);

Output Parameters

To avoid shared variables you can use input/output parameters. We mark ourput parameters using symbol "." like parameter is public. Output parameters require a variable as argument, otherwise you will not be able to capture the output value.

#demo output parameters
driver output_params:

routine add(Integer p1 = 0, p2 = 1,  Integer .out):
    out := p1 + p2;

    Integer result;
    ** reference argument require a variable
    apply add(1,2, result);
    print result;     //  expected value 3
    ** negative test
    apply add(1,2,4); //  error, "out" parameter require a variable



Dispatch is a form of subroutine selection by signature. It makes possible multiple subroutines with the same and different parameters. These kind of subroutines are overloaded. Subroutine signature include name, parameter types and result types.

Wikipedia: name mangling


Classes are composite data types. We use a classes to create multiple objects with same structure. Each object is a reference to a location in memory were the object is stored. An object is also called instance of a class.


class name(parameters) <: base_class:
   // definition region
   // constructor region
   // release region

Script Execution

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 start only a one script at a time. In service mode you can start multiple sessions with different startup parameters. Each session is independent and can start one single script.

Configuration:  EVE serviceis using a general configuration file: eve.cfg. This file contains information about all scripts and configuration files. After the service starts this file is parsed and each script that have an entry is start automatically formng a session. For each session, the service will create a different log file.

Memory alocation: EVE service is in charge of allocating memory for one session before the application starts. There is no shared memory between sessions. That is a session is dedicated for a single application. After application is terminated the memory is released.

To start an application there are 2 methods:

  1. Using the system command call with parameters
  2. Using console REPL commands line app and type a "start" command

Running a Driver

eve:> run path/driver_name -c file.cfg -m 2048GB

Driver Execution:

When a driver is executed, all it's components are compiled in memory, then the main subroutine is executed. If a script do not have a "main()", it can not be executed. The is the program entry point. When the "driver.main()" is finalized the program terminates and is removed from memory.

Aspect Execution:

One aspect is executed from "driver main process" or from another aspect process. You can not start an aspect from itself. Recursive or cyclic aspects are not supported. The compiler will detect a recursive aspect and will fail.


Aspects can be resolved in linear mode one after another. Let's consider we have 3 aspect modules: aspect_a, aspect_b, aspect_c. We can execute each aspect using keyword: "solve". This will start aspect in synchronous mode and wait for each aspect to finish before start the next aspect:

** resolve several aspects
routine sync_demo:
    ** solve each aspect immediatly
    solve aspect_a(params);
    solve aspect_b(params);    
    solve aspect_c(params);        


Aspects can be resolved in parallel mode in different processes.

** resolve several aspects
routine async_demo:
    ** enque aspects to be resolved
    defer aspect_a(params);
    defer aspect_b(params);    
    defer aspect_c(params);        
    ** resolve all enqued aspects

Parallel Sessions

One aspect can resolve same problem with different parameters in parallel.

** resolve several aspects
driver defer_demo:

    ** enque same aspect 4 times
    for i in (1..4) loop
        defer aspect_demo(i);        
    end loop;

Module Execution:

The drivers and aspects can load modules in memory. After loading, all public elements of modules can be executed on demand. Before execution the driver can interact with the user to ask for input then call routines, functions and static classes members.

Modules are "singleton" once a module is loaded in memory, it will not load a second time. Its states are unique for a session. EVE is a multi-session system. A script can be run asynchronously in parallel with itself but in different sessions.

Exclusive Mode:

When a script is executed, it can be started with parameter: -e or --exclusive. This is a signal that the script can not be started a second time in a separated session. In this mode a script can connect to a database in admin mode and can lock a file so that no other process will access the file in parallel.

Session Termination:

Script termination can be done using: "exit" or "abort". This is a way to release all locked resources and terminate the application session. Program can end with an error code using "abort N" statement, otherwise it will automaticly return 0 code for the operating system.

When script is terminated, it depends if was started from a console in debug mode or in regular mode. In debug mode the script remains in memory, parsed and ready for debuging. The memory can be investigated using commands.


Next code sequence is terminated after 100 iterations:

** fast forward demo
driver over_demo:

    Integer i = 0;
        write ".";
        wait 100;               
        over if i == 100;
        i += 1;
    end loop;

Read next: Data Types