Sage-Code Laboratory
index<--

Eve Syntax

Eve's syntax draws inspiration from a variety of programming languages. We leverage short English keywords without resorting to abbreviations. Notably, languages like Ada, Ruby, Fortran, and Python have influenced Eve's design. Unlike some languages, Eve doesn't rely on curly braces for code blocks. We believe you'll find Eve enjoyable to learn.

Page bookmarks

In this article you will learn about:



Design Strategy

Eve aims to strike a balance between a verbose, explicit syntax for learning purposes and a concise, intuitive syntax for ease of use. The goal is to design a syntax that is both descriptive and readable for beginners, while also being concise and "looking right" for experienced users.

Hello World

One of the best ways to get started with a new programming language is through the classic "Hello World" example. In Eve, printing "Hello World!" to the console is straightforward and highlights some key aspects of the language's syntax:.

Example:

#demo: hello world
driver hello:

process:
  ** main process:
  print "Hello World!";
return;

In example above we have a script, the file name must be hello.eve. It consist of driver with name "hello". Inside we have a single main process that print to console "Hello World!". This is basicly a small Eve program.

Syntax Elements

To better understand Eve's syntax, let's examine a another program and identify its key syntactic elements and punctuation symbols. While we will describe the role of each symbol and keyword, you don't need to memorize them all at once. We'll create detailed explanations for the language's semantics later on.

Example 1:


# Eve programs start with a "driver"
driver syntax_elements(a, b: Integer):

** define two global lambda expressions
global
  set add = (p1, p2: Integer) => (p1 + p2);
  set sub = (p1, p2: Integer) => (p1 - p2);

process:
  ** the process use arguments -a -b
  print "param1: {a}, param2: {b}";

  ** define local variables
  new v1, v2 :Integer;
  
  let v1 := add(4, 5);
  let v2 := sub(5, 3);

  ** print the result
  print ("result:", v1); -- 9
  print ("result:", v2); -- 2
return;

Punctuation:

ments">Comments

Examples 2:


# boxed comments in Eve language
+--------------------------------------------
| Eve boxed comments can be used on top     |
| You can create a box using this new style |
--------------------------------------------+
driver comment_demo:

# line comments in Eve language
----------------------------------------------
-- Single line comments are inspired from Ada 
-- You can use these comments at end of lines 
----------------------------------------------
process:
  ** this comment style is inspired from "md"
  new a = 0, b = 1 :Integer;

  ** next demo use end of line comments:
  let a := a + b * -- end of line comments can
          (a - 1); -- be useful in expressions
return;

# Final Notes:
/*

-- Eve also has C legacy style comments. These are
used for notes or outline large blocks of code.

-- Eve block comments can be nested over boxed 
comments. So you can comment out large sections
of code that already have some comments.
*/

Notes:

Identifiers

The name of identifiers in Eve can have a length of 30 characters. A name starts with lowercase letters (a..z) or capital letters (A..Z) followed by one or more characters or numbers. No special characters or spaces are permitted in the name except underscore ("_"). An identifier can contain underscore and can start with underscore but it can not end with underscore and can not use two consecutive underscores.

These are valid identifiers

_
x, y, z
a1, a2, a3
thisIsOK
this_is_ok
_this_is_valid

These are invalid identifiers

1st
not_valid_
_not_valid_

To summarize, Eve identifiers follow a conventional naming style similar to many programming languages, with some additional restrictions on the use of underscores at the beginning, end, and consecutive positions within the identifier name.

Prefix & Sigil

Identifier prefix is used to declare a variable, but is not required when using the variable. A sigil is part of variable name and it must be used when you use the variable.

* vararg parameter prefix, this simbol is not part of the identifier.
@ receiver parameter prefix, this simbol is not part of the identifier.
_ protected member sigil, this simbol is not part of the identifier.
$ system state/variable sigil, this simbol is part of the identifier.

So in summary, Eve uses specific prefixes like * and @ to denote special parameter types, and sigils like _ and $ as part of variable names to indicate their scope and accessibility (protected or system-wide). This convention helps differentiate various types of identifiers and variables within the Eve programming language.

System Variables

System variables start with sigil: "$", that is making a "system state". System states are static and public, shared in a a process.

variable Description
$object current instance of a class, current object
$result default result of a routine when a name is not specified
$error current exception object of type Exception

Notes:

Globals

In any Eve script you can define global variables. These are primitive values or references to objects. A global variable is represented by an identifier and value. Type is optional hint. Global variables do not use a sigil.

Global variables can be declared using "set" statement. Initial value is establish with operator "=" follow by a type hint. Globals can be modified during the program execution using "let" statement with modifier operators like: { :=, ::, +=, -= ... }.

Globals example:

Most of the examples are also stored on GitHub. However it is possible the one in GitHub to be different from then the one in documentation. This is due to fast evolution of the language and not by intention. We declare globals outside of any process in a "global" region. You can use global keyword multiple times in a module but all globals are in the same memory heap and must be unique regarding the region they are defined in.


...
** define global system states
global
  set s = "test" :String;

  ** two integers
  set a :Integer;
  set b :Integer;

global
  ** one Real variable
  set d = 0.5 :Real;
...

Globals are staticly defined in module scope (global) scope. We use "set" keyword to create a global state. If the state alredy exist, it can be modified using "let" in a process or subprogram. Compiler will signal an error if you "set" a variable two times or if you use "set" in a process or in a subprogram. "set" can't change value of a variable.

Initial Value

The initial value can be establish using the initializer "=" (single equal). This operator can use only data literals but no expressions. It is not able to execute the right operator. That is, the initial value can be a constant literal or an expression but the expression is not evaluated.

Zero Values

When a variable is specified, with no initializer, the variables takes default zero values. These values are different for each data type. For example zero value for Integer = 0 for Real = 0.0 and for String = "" (empty string). Composite data types are initialized to "Empty", that is (), [], {}, all equivalent to "∅" used in set theory.

Cascade Initialization

Operator "=" is used to create an expression. That means you can initialize multiple variables using a single declaration. This is different than assignment ":=" or clone "::" that create a statement and not an expression. Operator "=" create a result, so it can be chained like in Python.

Fragment


...
process:
  new a = b = 1 :Integer;    -- create and initialize a,b 
  new x = y = z = 0.5 :Real; -- create and initialize a,y,z
  ...
return;

Expressions

An expression is a syntax element that can be evaluated using mathematical rules and principles. Expressions are created using identifiers, operators, functions and constant literals. Expressions can be grouped using round paranthesis (). Multiple expressions can be used in a statement.

expressions...

Fragment


## simple expressions
process:
  print 10;
  write "this is a test";
  ** complex expressions can use grouping: ()
  print (10 + 10 + 15); -- numeric expression
  print (10 > 5) or (2 < 3); -- logical expression
  ** list of expressions are enclosed in ()
  print (1, 2, 3); -- expect: 1 2 3
  print (10, 11, 12); -- expect: 10 11 12
  ** using write to: avoid new line and print
  write (1,2);
  write (3,4);
  ** now create a new line and output the text
  print; -- 1234
  ...
return;

Notes:

Assign Expression

In Eve, you can assign the result of an expression to a variable using the assignment operator :=. However, this operator exhibits a behavior that you must understand to avoid unintended side effects. When assigning a non-primitive (boxed) variable as an expression, the operator transfers the value by sharing a reference, rather than copying the value itself. This means that the new variable will reference the same underlying object as the original variable. In contrast, when assigning a primitive (native) type, the operator transfers the actual value, creating a separate copy of the data.

Assign: by reference


#expressions pseudo-code
  ...
  new identifier1 := literal;         -- constant literal
  new identifier2 := expression;      -- execute expression
  new identifier3 := function_name(); -- function call
  new identifier4 := variable;        -- borrow (create a refference or copy a value) 
  new identifier5 := native_var;      -- copy value
  ...

Assign: by value:

To make a deep clone/copy of an Object you must use clone operator that is (::) instead of (:=). This will transfer values of attributes, not references. After cloning, we can modify one value without affecting the original. Making a clone is not possible for native variables, it works only with objects.

Syntax:

// making a clone
  new object :: original;
  expect object is not original;

Note: For native type, the operator "is" do not work at all. Attempting to use "is" or "is not" with a native type will always return false even if the values are equal. For native types you have to use "==" to compare the values. For objects, "==" will execute deep comparison, and will return "True" if all object attributes or data elements have also equal values.

Ternary expression

The ternary operator in Eve is the "if" keyword. This keyword has actually two roles in EVE. It can be used to create a conditional statement or an expression. It can be used to return a value or other depending on one or several logical expressions. In other languages we use ternary operator "?" and ":" but in Eve these two symbols are used for more important other purposes.

Syntax:

# ternary expression
process:
** regular style using a single line of code:
  new variable1 := (value1 if condition else value2);

** alternative style using multiple lines of code:
  new variable2 := (value1 if condition1 else
                    value2 if condition2 else
                    value3);
  ...
return;

Example:

In next example we use a ternary expression as argument.


process:
  ** create a logic variable
  new test := True;
  ...
  expect (True if test else False); -- will pass
  ...
return;
Note: While the traditional ternary operator syntax with "?" and ":" is widely adopted in many programming languages, Eve's approach with the "if" keyword prioritize readability, consistency, and scalability. This design choice aligns with Eve's goals of being a comprehensible and accessible language, especially for beginners.

Statements

One statement can be declarative or imperative. We use both in our language. Therefore Eve is an imperative & declarstive language in the same time. Also Eve has support for OOP and Functional Programming paradigms. This makes Eve a modern hybrid language that can be used by developers for small or large projects.

Single line statement

The most simple statements are definitely single line statements. In next example we demonstrate 5 simple statements. Every statement start with a keyword (new, let, expect) and end with semicolumn ";". First line is not a statement but a declaration. Last keyword "return" is actually the end of declaration and not a statement.

Fragment:

driver assign_demo:
process:
  ** initialization statements
  new a :=  2;
  new b := .0;

  ** execute expression
  let b := a + 0.5;

  ** verification statements
  expect a == 2;
  expect b == 2.5;
return;

Multi-line statements

One expression can span multiple lines.

Example:


# multiple line demo
process:
  ** multi-row expression
  new var := 1
            +2
            +3 + 4 +
             5 + 6;

  ** multi-row literal
  new map := {
      'two' :2,
      'four':4,
      'six' :6
  };
return;

Reserved keywords

Computer was invented in England during WW2, so we prefer English keywords, even though a computer language could be created using keywords from other spoken languages. Eve is using about 100 reserved keywords. In next table we collect all keywords reserved for Eve. Some keywords may not yet be used. If we reserve new keywords they should be added here later. Please signal keywords that are not here to help us improve this inventory.

catch alias alter analyze
append join ascend augment
start method stop by
case class clone close
commit constant create cursor
begin delete descend discard
else end error exit
expect break feature fetch
for from function group
halt if import initialize
insert into item constant
limit loop method object
offset open order one
over package pass print
process raise read record
recover release resume repeat
reset rest result on
retry return rollback routine
global select all skip
step store switch job
then trial try reset
update use labe view
where while with write
call panic any other
resolve add del pop
constructoradd del pop

Script regions

Keyword Description
import define import region
alias define identifier for some imported module members
constant define region for user defined constants
global define a region for global states
process define an executable region for driver and aspect
initialize define initialization region for modules
initialize define initialization region for modules
recover define error protection region after process
finalize define final/tear-down region after process

Semantic keywords

Keyword Description
module define a program file (module-name = file-name)
function declare a function of type Function
class define a composite data type for object oriented programming
constructor begin constructor subprogram for a classes/object factory
feature define a design pattern that can be enabled by a class
augment define an augment for an existing class
method define behavior for user defined data types
release define a object clear region for a class
return terminate a process/function or a class declaration

Blocks of code/sections

Keyword Description
begin used before a block of code to create a local scope
split used to run several aspects asynchronously in a group
join end begin block. used to synchronize several aspects
match multi-path conditional selector
when define a pathway in multi-path selector
other define a default pathway in a selector
cycle start declaration region for a repetitive block
loop unconditional repetitive block
while conditional repetition block
for visitor iteration loop
if start a branch or a fork
else start alternative branch
repeat repeat a block of code in a cycle
done close a control statement or a match selector
job start a scope block with error handlers
try start job block region
catch handle a specified error type or other
resolve conclude a job, cleanup and resolute

Operator keywords

Keyword Description
is check data type | reference identity
in check element is belonging to a list/set
not logical NOT operator
and logical AND operator
or logical OR operator
xor logical XOR operator

Assign keywords

For any kind of assignment or modification you must us a specific keyword. This is why we use keywords like: {set, new, let}. User must read what's going on and will understand much better the code by using keywords.

Keyword Description
new Create local variable or attribute
set Create global variable or property
let Alter existing variable or attribute
pop Can create a new local variable from element of a collection

Interruption statements

Keyword Description
panic create unrecoverable error and stop application, status > 0 (os error)
stop interrupt current cycle and continue after the "repeat" keyword.
raise create exception with error code > 0 (recoverable).
repeat end a repetitive block and continue program.
pass does absolutely nothing, is just an empty statement
retry jump back to the same job and try again.
start start a coroutine in asynchronous mode
suspend intrerupt a coroutine and wait for resume
resume continue execution of a suspended coroutine
yield unused (replaced by suspend/resume/wait)
wait intrerupt the main process for specified time
run execute an aspect in asynchronous mode
call execute a shell command in synchronous mode
halt temporary interrupt the process for debug (breackpoint)
exit interrupt execution of a process or sub-program without raising an error
over interrupt all processes and terminate application

Delimiters

In the syntax description "..." represent content and "_,_,_," represents a sequence of elements. "_" represent one of the elements. In descriptions vertical bar "|" represents second alternative. Some operators can have multiple purposes depending on the context and data types.

Comments

Symbol Description
**...** Single line separator
/*...*/ Outline comments | Expression comments
+-...-+ Block comments | Boxed comments
"/.../g" Regular expression search pattern

Collections

Symbol Description
(_,_,_) List literal | parameters | arguments
[_,_,_] Array literal for (Vector | Matrix)
{_,_,_} Ordinal | DataSet | HashMap | Class / Object

Strings

Symbol Description
'...' Symbol = Unicode UTF8 codepoint (4 Bytes).
"..." Variable capacity UTF8 String literal.

Operators

Eve us ASCII symbols for operators. One operator can be single character or two characters with no space between tem. Sometimes we use the same character twice for example "==" or ">>". This convention is well known in many other computer languages.

Single symbol:

# , : . ; = ~ ? % ^ * - + / < > & | ! @ $

Two symbols:

== !=  => <= >= :> <: .. !. !~ && || &=

Three symbols:

.|. .&. .+. 

Modifiers:

:: := += -= *= /= %= ^= +> <+ >> << -> <- 

Delimiters:

"_" '_' (...) [...] {...} #(...)

Single symbols

Eve use almost al single special characters found on a standard keyboard, but this is not good enaugh. So like anu other language out there we have used 2 or 3 characters to create New symbols.

Symbol Description
! Negation symbol
? String template find & replace
# Title at begging of line | Digit in string pattern
# Last element index in a collection
$ Sigil for share variable or system variable | constant
@ Iput/output parameter | result prefix
; Statement separator | End of statement
: Define type, routine, class or block | Pair-up/binding operator (a:b)
= Type descriptor, initial value for variables | parameters
~ Regular expression comparison/check
. Decimal separator | Member qualifier
, Enumeration for elements | complex expression
* Variable arguments | Multiplication | All elements
* Usable in import to create a file pattern/search
| Used in set builders and to define variant types
_ Void variable that accept any value and clear memory.

Composite symbols

Eve use two symbols to create supplementary operators.

Symbol Description
** Single line comment
-- End of line comment
.. Slice [n..m] | Domain [n..m] or Range (n..m)
<: Define super-type in a derived type declaration
:> Numeric data conversion or convert a subtype into a supertype.
<< Shift ordered collection to left with n: X := C << n
>> Shift ordered collection to right with n: Y := C >> n
:= Assign value of expression to a variable.
&& Represents intersection of two sets"
|| represents: union of two sets

Numeric operators

Symbol Description
* Numeric multiplication | String replication
/ Numeric division | Path concatenation
% Reminder operator | Scalar operator
+ Numeric addition | String concatenation
- Numeric subtraction | String concatenation | Set difference
^ Power operator. (We can't use ** that is for comments)
~ Regex like operator (regular expression matching)

Relation Operators

Eve use two symbols to create a additional operators.

Symbol Description
== Equal | deep comparison
!= Different value | (deep comparison)
> Greater than value
< Less than value
>= Greater than or equal to values
<= Less than or equal to values

Modifiers

Each modifier is created with pattern "?=", where "?" is one of these: {:, +, -, *, /, %, ^}. Also the symbol "::" is a modifier.

symbol meaning
:= Assign by reference | shallow assignment
:: Assign by copy | clone assignment
+= Increment value
-= Decrement value
*= Multiplication modifier
/= Real division modifier
%= Modulo modifier
^= Power modifier
+> Feed left | insert elements at the beginning or at the left side
<+ Feed right | append element in last position or at the right side
&= Set operator used to add elements into an existing set from another set

Logical operators

These operators are expected logical values T = True, F = False

Symbol Description
if conditional operator
is check element are the same (compare locations)
is not check elements are different. (compare locations)
as quick conversion using format constants for date/time
eq equivalence "≡" (compare value & type)
!eq (not eq) non equivalence "!≡" (compare value & type)
in logic belong to: instead of "∈"
!in (not in) logic not belong to: instead of "!∈"
!(not) logic NOT (negation)
and logic AND (&& = intersection)
or logic OR (}} = union)
xor logic Exclusive OR

Unary operator: not

Table of truth

A B A and B A or B A xor B
True True True True False
True False False True True
False True False True True
False False False False False

Bitwise operators

These operators are working for natural numbers ≥ 0

symbol description
!. bitwise NOT (unary)
.&. bitwise AND
.|. bitwise OR
.+. bitwise XOR
<< shift bits to left
>> shift bits to right

Binary operators

A B A.&.B A.|.B A.+.B
00 00 00 00 00
01 00 00 01 01
11 01 01 11 10
10 11 10 11 01
11 11 11 11 00

Unary operators

A !.A A << 1 A >> 1
0000 1111 0000 0000
1111 0000 1110 0111
0111 1000 1110 0011
0110 1001 1100 0011

Read next: Data Types