In this article you will learn about:
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.
#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.
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.
# 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;
# 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.
*/
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.
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 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 |
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: { :=, ::, +=, -= ... }.
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.
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.
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.
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.
...
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;
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...
## 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;
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.
#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
...
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.
// 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.
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.
# 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;
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.
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.
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.
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;
One expression can span multiple lines.
# 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;
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 | |
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 |
constructor | add | del | pop |
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 |
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 |
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 |
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 |
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 |
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 |
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.
Symbol | Description |
---|---|
**...** | Single line separator |
/*...*/ | Outline comments | Expression comments |
+-...-+ | Block comments | Boxed comments |
"/.../g" | Regular expression search pattern |
Symbol | Description | (_,_,_) | List literal | parameters | arguments |
---|---|
[_,_,_] | Array literal for (Vector | Matrix) |
{_,_,_} | Ordinal | DataSet | HashMap | Class / Object |
Symbol | Description | '...' | Symbol = Unicode UTF8 codepoint (4 Bytes). |
---|---|
"..." | Variable capacity UTF8 String literal. |
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:
"_" '_' (...) [...] {...} #(...)
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. |
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 |
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) |
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 |
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 |
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 |
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 |
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