Eve Syntax
Design Strategy
Eve balances an explicit, verbose syntax that is easy to learn with a concise syntax that is easy to use. Keywords say what a statement does: new creates a variable, let executes an assignment or an expression, done closes a block: new a := 1; let a += 1;.
Hello World
The classic first program prints one line to the console:
# demo: hello world
primary aspect hello is
process main is
** main process
print "Hello World!";
return;
end hello;
The file is hello.eve: an aspect named hello, with one main process that prints "Hello World!". The aspect name and the file name are the same. It is a primary aspect when it is meant to be run by a command.
Syntax Elements
The next program shows most syntax elements. Each one is described later on this page and on the following pages.
# Eve programs start with an aspect
primary aspect syntax_elements is
** define two global lambda expressions
set add = (p1, p2: Integer) => (p1 + p2);
set sub = (p1, p2: Integer) => (p1 - p2);
process main(a, b: Integer) is
** the process uses the arguments a and b
print "param1: {a}, param2: {b}";
** define local variables
new v1, v2 :Integer;
let v1 := add(4, 5);
new v2 := sub(5, 3);
** print the results
print ("result:", v1); ** result:,9
print ("result:", v2); ** result:,2
return;
end syntax_elements;
Punctuation
- Expressions use infix operators, like Java and most languages.
- A comma separates the elements of a list and the arguments of a call.
- A statement usually starts with a keyword and always ends with a semicolon
;. - A declaration header ends with the keyword
is:aspect hello is. - A block ends with its terminator keyword:
return,end,done, orrepeatfor a loop tested at the end. - Regions and nested blocks are indented.
Source Text
Before the parser sees any statement, the lexer reads characters and turns them into tokens: comments, names, literals, operators and delimiters. This section and the next ones describe these lexical rules.
Encoding and Lines
- Source files are UTF-8 text with the extension
.eve. - Lines end with LF or CR+LF; both are valid.
- Save files as UTF-8 without a byte order mark (BOM). A BOM at the start of a file is ignored; removing it is the job of a formatter.
- Names, keywords and operators are ASCII. Unicode is allowed in strings and comments.
- A statement ends with
;, not with the end of the line, so it can span several lines.
File Header
The first line tells what kind of file this is.
| First line | Meaning |
|---|---|
# title | Title of an aspect or a module. |
## subtitle | Subtitle. |
***** | A row of stars (a ** comment), for example above a block comment. |
Comments
Eve comments follow Markdown where possible: # and ## are titles, ** is a note. Block comments use /* ... */, as in most languages.
Comment Forms
| Form | Where it starts | Ends | Spans lines | Nests | Use |
|---|---|---|---|---|---|
# | indentation 0 only | end of line | no | no | title |
## | indentation 0 only | end of line | no | no | subtitle |
** | anywhere: indentation 0, indented, or after code | end of line | no | no | line and end-of-line notes |
(** ... **) | inside an expression | **) | yes | no | expression comment |
/* ... */ | anywhere | the first */ | yes | no | block comment, notes, disabled code |
Notes:
- Block comments do not nest: in
/* a /* b */ c */the comment ends afterb. To comment out code that holds comments, use**lines or expression comments inside it. //,--and boxed+--- ... ---+comments are not Eve.**always starts a comment, so the power operator is^.#is used only for comments:#and##start a line with indentation 0. A#!line is an error: Eve has no free scripts, every program is an aspect. Indentation is significant in Eve, so an indented#is an error.- After the
return;that ends a script, notes can follow in a block comment.
Example
# comments in Eve
/*-----------------------------------------------
A block comment can describe the whole script.
It can span many lines.
-----------------------------------------------*/
primary aspect comment_demo is
process main is
** a note on its own line
new a = 0, b = 1 :Integer;
** an expression comment can sit inside an expression
let a := a + b * (** first factor **) (a - 1); ** end-of-line note
return;
end comment_demo;
/* Final notes:
notes after the end of the script use a block comment.
*/
Identifiers
An identifier names a variable, a constant, a type or a subprogram.
- It starts with a letter (
a..z,A..Z), followed by letters, digits and underscores. - It has at most 42 characters.
- Names are case-sensitive:
Pointandpointare two names. - It cannot end with an underscore or contain two consecutive underscores.
- It cannot contain
-, which is the minus operator:a-bmeansa - b. _alone is the void variable: it accepts any value and discards it.
Valid identifiers
_ x, y, z a1, a2, a3 thisIsOK this_is_ok
Invalid identifiers
1st not_valid_ two__underscores file-name
Prefix and Sigil
A prefix is written in a declaration but is not part of the name: the variable is used without it. A sigil is part of the name and is always written.
| Symbol | Kind | Meaning |
|---|---|---|
* | prefix | vararg parameter: *args |
@ | prefix | input/output parameter or named result: @result |
$ | sigil | system-wide variable: $error |
! | suffix | a stochastic function: its result can differ for the same arguments, random!(), next!(); a method that changes shared state: o.record!() |
Visibility is not a prefix: it is a word in front of a declaration, public, protected or private in a class (see Classes), and the export list in a module (see Modules).
System Variables
System variables start with the sigil $. They are static and public, shared in a process. They need no module qualifier. The environment of the operating system, the configuration file of the primary aspect ($key = value lines) and the library modules provide them; a primary aspect can change some of them with set, for example set $EVE_ASP = "my_folder";.
| Variable | Type | Description |
|---|---|---|
| Runtime objects | ||
$error | Error | the error being handled in recover: $error.message, $error.code, $error.line, $error.job (the label of the failed job); also the error of an aspect, at the apply line |
$stack | ()Call | the calls that led to the error |
$trace | ()Error | the errors and warnings of the process |
$result | result type | the result of a function that declares no result name; a list when the function has several results |
| Environment and folders | ||
$NAME | String | any environment variable of the operating system, for example $PATH or $HOME |
$EVE_DIR | String | the folder of the Eve runtime |
$EVE_LIB | String | the folder of the installed libraries, searched by from … use |
$EVE_ASP | String | the folder of the aspects of a primary aspect; by default asp, then the project root, then lib |
$EVE_OUT | String | the output folder of the log files, "out" by default |
$MY_DIR | String | the folder of the running aspect |
| Settings | ||
$epsilon | Real | the precision of comparisons between real numbers |
$timeout | Integer | seconds a task waits on a channel before a time-out error, 60 by default; an aspect may set its own value, which shadows the primary aspect value (multitasking, not in version 1) |
$cores | Integer | the size of the worker pool of parallel aspects, the number of hardware cores by default, at most 16 (not in version 1) |
$max_parallel | Integer | the number of aspects one parallel group of the primary aspect may start, 8 by default (not in version 1) |
| Constants | ||
$err_name | Integer | error codes, for example $err_expect = 2, $err_assert = 3, $err_raise = 4 (see Exceptions) |
$wrn_name | Integer | warning codes, for example $wrn_truncate = 6 |
Notes:
- The sigil
$avoids collisions with local names. - A system constant usually holds a data literal.
- A system variable can be an object with attributes or a value of a basic type.
Delimiters
In the descriptions below, ... stands for content, _,_,_ for a sequence of elements and | separates alternatives. Some symbols have several uses, depending on the context.
Collection Delimiters
| Symbol | Description |
|---|---|
(_,_,_) | List literal | parameters | arguments |
[_,_,_] | Array literal (vector or matrix) |
{_,_,_} | Ordinal | DataSet | DataMap | Object |
String Delimiters
| Symbol | Description |
|---|---|
'...' | Rune: one Unicode code point, stored in 4 bytes |
"..." | String: UTF-8 text of any length; {name} and {name % format} insert the value of a name; a literal brace is \{ \} |
"""...""" | Text literal on several lines; raw, no escape sequences |
"/.../g" | Regex literal: a string that starts with / keeps its backslashes, with no escapes and no placeholders except \"; it is a String, used as a pattern by =~; the negation is not (s =~ "/…/") |
Escape Sequences
Inside "..." and '...', a backslash starts an escape sequence. Any other character after \ is a lexical error. Two literals have no escapes: the text literal """...""", and the regex literal, a string that starts with /, where only \" is an escape (see Regular Expressions).
| Escape | Meaning |
|---|---|
\\ | backslash |
\" | double quote |
\' | single quote |
\{ \} | the brace itself |
&name; | HTML character reference, for example α |
\n | line feed (LF) |
\r | carriage return (CR) |
\t | horizontal tab |
\0 | NUL character |
{U+H...} | Unicode code point, 1 to 6 hex digits: {U+03B2} is β |
Operators
Eve uses ASCII symbols for operators. An operator is one character or two characters with no space between them. Some use the same character twice, for example == or >>, as in many other languages. Several operators are keywords: and, or, not, is, in.
Single symbols:
, : . ; = ? % ^ * - + / < > & | ! @ $
Two symbols:
== <> => <= >= :> <: .. && || >< =~ +- ..< >.. >..<
Modifiers:
:: := += -= *= /= %= ^= +> <+ >> <<
List arrows:
-> <-
Delimiters:
"_" '_' (...) [...] {...} "{name}" \{ \}
Single Symbols
| Symbol | Description |
|---|---|
! | Suffix of a stochastic function or an unsafe method: name!() |
? | Open end of a range or an array: (0..?), [?]Integer |
# | Title comment at column 0 only |
$ | Sigil of a system variable or constant |
@ | Input/output parameter | result prefix |
; | End of statement |
: | Type hint | label | pair (key:value) |
= | Initial value | named argument |
. | Decimal separator | member qualifier |
, | Separator of elements, parameters and arguments |
* | Multiplication | vararg prefix | all elements | file pattern in import |
| | Set builders | variant types |
_ | Void variable |
Composite Symbols
| Symbol | Description |
|---|---|
** | Comment to the end of the line |
.. | Range (n..m) | slice |
<: | Supertype in a derived type declaration |
:> | Numeric conversion | convert a subtype into a supertype |
=> | Result of a function or lambda expression |
<< | Shift modifier: shifts a value left in place |
>> | Shift modifier: shifts a value right in place |
:= | Assign with type inference |
&& | Intersection of two sets |
|| | Union of two sets |
-> <- | Remove the last or the first element of a list |
=~ | Approximate match: a string with a regular expression (s =~ "/^a/"), a number within a tolerance (x =~ b +- 0.01, or x =~ b with the tolerance $epsilon). == is the exact match |
+- | Range around a value: b +- t is the range (b - t)..(b + t), so x in b +- t also works |
>< | Cartesian product |
Numeric Operators
| Symbol | Description |
|---|---|
* | Multiplication | string replication |
/ | Division | path concatenation |
% | Remainder |
+ | Addition | string concatenation |
- | Subtraction | negation | set difference |
^ | Power (** is a comment) |
Relation Operators
| Symbol | Description |
|---|---|
== | Equal value (deep comparison for objects) |
<> | Different value (deep comparison for objects) |
> | Greater than |
< | Less than |
>= | Greater than or equal to |
<= | Less than or equal to |
Modifier Operators
Most modifiers have the form ?=, where ? is one of : + - * / % ^. ::, +>, <+, << and >> are modifiers too. A modifier changes the variable on its left.
| Symbol | Meaning |
|---|---|
:= | Assign by reference, with type inference |
:: | Assign a deep copy (clone) |
+= | Increment; on a List or Array with a scalar, increment every element |
-= | Decrement; on a List or Array with a scalar, decrement every element |
*= | Multiply |
/= | Divide |
%= | Remainder |
^= | Power |
+> | Insert elements at the beginning (left side) |
<+ | Append elements at the end (right side) |
<< | Shift left in place |
>> | Shift right in place |
Logical Operators
These operators work with logical values: True and False.
| Symbol | Description |
|---|---|
if | Conditional (ternary) operator |
is | Same object (compares locations) |
is not | Different objects (compares locations) |
as | Quick conversion with format constants, for date and time |
eq | Equivalence ≡: same value and type |
not eq | Not equivalent: different value or type |
in | Belongs to (∈) |
not in | Does not belong to (∉) |
not | Negation |
and | Logical AND |
or | Logical OR |
xor | Logical exclusive OR |
Operator Precedence
Operators on a higher row bind tighter. Operators on the same row are evaluated from left to right, except ^, which is evaluated from right to left. Parentheses change the order.
Unary minus first. A minus with no term before it is unary and applies first, before ^: -2^2 is (-2)^2, that is 4. A minus with a term before it is the subtraction: 4 - 2^2 is 0, and 4 - -2^2 is 4 - (-2)^2, also 0. For the negative of a power write -(2^2). Many languages give -2^2 = -4; Eve does not.
| Level | Operators |
|---|---|
| 1 | . () [] (member, call, index) |
| 2 | unary -, not |
| 3 | ^ (right to left) |
| 4 | * / % |
| 5 | + - |
| 6 | .. +- |
| 7 | << >> |
| 8 | && |
| 9 | || |
| 10 | == <> < > <= >= =~ is in eq |
| 11 | and |
| 12 | xor |
| 13 | or |
| 14 | if … else (ternary) |
Table of Truth
| A | B | A and B | A or B | A xor B | not A |
|---|---|---|---|---|---|
| True | True | True | True | False | False |
| True | False | False | True | True | False |
| False | True | False | True | True | True |
| False | False | False | False | False | True |
Keywords
Eve reserves English words as keywords; a keyword cannot be used as an identifier. The tutorial does not list them all in one place: each phase lists its own keywords on one of its first pages, close to the pages that explain them. This page lists the keywords of the core language. Every keyword is reserved from the first version, also the ones that a later phase explains. The complete list for implementers is in the specification, spec/lexical/keywords.json.
| Phase | Keywords listed in |
|---|---|
| 1 Core Language | this page |
| 2 Eve Code Topology | Scripts |
| 3 Functional Programming | Functions |
| 4 Object Oriented Features | Classes |
| 5 Enterprise & System Features | Processing |
| 6 Internet Features | no keywords: the features are library modules |
| 7 Implementation & Tools | Compiler |
Script Structure
The words that every aspect uses.
| Keyword | Meaning |
|---|---|
aspect | header of a script that runs: aspect name is … end name; |
exclusive, concurrent, primary | the kind of an aspect, written before aspect: exclusive is the default: called with apply; concurrent: called with apply or start; primary: run by a command, never called by an aspect |
process | the executable part of an aspect: process main is … return;; main is the entry point, the other processes are private |
is | ends a declaration header (procedure p is); also the operators x is T and a is b |
return | closes a process, function, method, constructor or destructor; it carries no value |
end | closes an aspect, module, class or trait: end name; |
Assign Keywords
Every statement that binds a value starts with a keyword, so the reader sees what it does. set creates a constant and new creates a variable: new a := 0;; new on a name that exists is an error. let executes an assignment or an expression: let a := a + 1;, let a += 2;, let lst -> x;. One let can change several variables in one statement. It creates a missing name only in a capture, with the type inferred from the element: let lst -> x; removes the last element of lst and puts it in x, which is created when it does not exist; in a loop, x is created on the first pass and assigned again on the next ones. let y := 5; on a missing y is an error. When x already holds a native value, the value is overwritten; when it holds a reference, the reference changes and the old object is released at the end of the scope. Writing new before such a name is the explicit form: let lst -> new x;, new x <- lst;. A class called like a function builds an object: new p := Point(1, 2);.
| Keyword | Meaning |
|---|---|
new | create variables or object attributes: new x := 0;, new copy :: original;, new self.x := x; (inside the class only). Inside a statement, new before a name creates that name: let lst -> new x;. new on a name that exists is an error |
let | execute an assignment or an expression: let x := 1;, let x += 1;, let lst -> x;, let f(x);. It may change several names; in a capture (let lst -> x;, let x <- lst;) it creates a missing name by type inference |
set | create a constant: set pi = 3.14 :Real;, or a class property |
def | declare an alias of a name |
Blocks
A block opens with a keyword and closes with done (or repeat for a loop tested at the end). See Control.
| Keyword | Meaning |
|---|---|
if | if c do … [else if c do …] [else …] done;; also the statement suffix break if c; and the ternary a if c else b |
else | the alternative branch of an if, of a ternary expression, or of a while loop |
do | starts the executable region of a job, a loop, an if branch or a when path |
done | closes an if, match, job, while or for: done; or done label; |
while | loop tested at the start: while c do … done;; also the test of repeat while c; |
for | visits a range or a collection: for x in r do … done; |
loop | [label:] loop is the header of a loop (its label and declarations), or a loop tested at the end: loop … do … repeat; |
repeat | closes a loop tested at the end: repeat [label] [while c];; a bare repeat; loops until break |
match | [label:] match s [one | all] multi-path selector, closed by done [label]; |
when | a path of a match: when v1, v2 do |
one | option of match: only the first matching path runs (the default) |
all | option of match: every matching path runs |
other | when other do: the path taken when no other when matched |
Statements
Statements that check a condition or change the normal flow.
| Keyword | Meaning |
|---|---|
break | leave a loop and continue after its closer: break [label] [if c]; |
skip | go to the continuation point of a loop: the next element of a for, the condition of a while, the test of a repeat; skip [label] [if c]; |
pass | the empty statement: do nothing |
expect | check a condition; a failed expect is an error with code 2 |
assert | check a condition; a failed assert is an error with code 3: the process stops unless it is recovered |
over | end the whole process at once with code 0, from any depth: over; |
exit | leave the subprogram or process it is written in, back to the caller; in the main process of a primary aspect the program ends, finalize runs |
panic | end the whole application at once with code 1 |
Operator Keywords
| Keyword | Meaning |
|---|---|
and | logical AND |
or | logical OR |
xor | logical exclusive OR |
not | logical NOT; also is not, not in, not eq |
is | checks a type, x is Integer, or the identity of two references, a is b |
in | membership, x in collection; also the source of for and of a builder |
eq | equivalence: the same value and the same type |
as | formats a value: d as T24 |
Globals and Initialization
Global Variables
A script can define global variables in its scope, outside any process. A global holds a primitive value or a reference to an object. It is declared with set and changed later with an assignment, without keyword. Globals do not use a sigil. All globals share one memory space, so each name is unique.
...
** define global states
set s = "test" :String;
** two integers
set a :Integer;
set b :Integer;
** one Real variable
set d = 0.5 :Real;
...
Globals are static, defined in the module scope. set creates a global state; an assignment changes it in a process or subprogram, with an operator such as :=, ::, += or -=. The compiler reports an error when a variable is created twice with set, or when set is used inside a process or subprogram.
Initial Value
The initializer = is the light assignment: it gives the first value of a variable from a data literal. The type is inferred from the literal, or given after :. = is used only for initial values, in a declaration and for the default of a parameter; a variable is changed with := (an expression) or :: (a clone), never with =. = is not an expression: it returns no value and does not chain.
The assignment := infers the type from the right side, evaluates the whole right side, and is a statement, not an expression. When = is followed by an expression instead of a literal, the compiler warns and treats it as :=.
process main is
new a = 5; ** literal, type inferred: Integer
new b = 5 :Real; ** literal and explicit type
new x := a + 2; ** expression: use :=
let a := 7; ** a change uses :=, never =
return;
Zero Values
A variable declared without a value gets the zero value of its type: 0 for Integer, 0.0 for Real, "" for String. Composite types start empty: (), [] or {}, the empty set ∅. new a, b :Integer; gives both names the value 0. The zero value of every type is in the table of Data Types.
Several Variables
One declaration can initialize several variables. The type after the last value applies to every name: new a = 1, b = 2 :Integer; is the same as new (a = 1, b = 2) :Integer;. A list of names takes one value for all of them, or one value each:
...
process main is
new a = 1, b = 2 :Integer; ** preferred: each name with its value
new (x, y, z) = 0.5; ** one value for all, type inferred: Real
new (m, n) = (1, 2); ** one value each
...
return;
Expressions
An expression is evaluated to a value. It is built from identifiers, literals, operators and function calls, and can be grouped with round parentheses.
- Expressions combine into larger expressions.
- The type of an expression is found by type inference.
- An expression is assigned to a variable with
:=or used as an argument. - Parentheses make the order of operations explicit.
Printing Expressions
print and write are procedures of the system library. Their arguments are a list of expressions, mapped to parameters by position or by name. A procedure has one parameter list only. Optional parameters that follow the vararg parameter must be named in the call: print is declared procedure print(*args, sep := ",", eol := True) and called print (1, 2, 3, sep: " ");. print separates its arguments with sep (default ",") and ends with a new line; write adds no separator and no new line. sep works only between several arguments, not inside a list: a list variable is printed as a list.
## simple expressions
process main is
print 10;
write "this is a test";
print; ** ends the line started by write
** grouping with ()
print (10 + 10 + 15); ** numeric expression
print (10 > 5) or (2 < 3); ** logical expression
** a list of expressions
print (1, 2, 3); ** 1,2,3
print (1, 2, 3, sep: " "); ** 1 2 3
** a list is one argument: sep does not apply
new lst := (1, 2, 3);
print lst; ** (1,2,3)
** write does not add a new line
write (1, 2);
write (3, 4);
print; ** 1234
return;
Assign Expression
The assignment := transfers a native (primitive) value by copying it. For an object, it shares the reference: both variables point to the same object, and a change through one is visible through the other.
** assignment forms
...
new identifier1 := literal; ** constant literal
new identifier2 := expression; ** evaluate the expression
new identifier3 := function_name(); ** function call
new identifier4 := variable; ** object: share the reference
new identifier5 := native_var; ** native value: copy the value
...
To copy an object with all its attributes, use the clone operator ::. After cloning, changing the copy does not change the original. Cloning applies to objects and collections; native values are always copied. There is no clone keyword or method: :: is the only way to clone. A slice follows the same rule: := creates a view over the elements of the base collection, :: creates a new collection with a copy of them.
** making a clone
new object :: original;
expect object is not original;
Note: is compares locations, so on native values it is always false, even when the values are equal. Compare native values with ==. On objects, == is a deep comparison: it is True when all attributes or elements are equal.
Ternary Expression
The conditional expression uses if and else instead of ? and :, which have other uses in Eve. The same if keyword starts a conditional statement.
** ternary expression
process main is
** on one line:
new variable1 := (value1 if condition else value2);
** on several lines:
new variable2 := (value1 if condition1 else
value2 if condition2 else
value3);
...
return;
A ternary expression can be an argument:
process
** create a logic variable
new test := True;
...
expect (True if test else False); ** passes
...
return;
Statements
A statement is declarative or imperative; Eve uses both kinds.
- A statement is usually one line of code.
- A statement can span several lines; it always ends with
;. - A group of statements is a block, indented by the same number of spaces.
- A block ends with its terminator:
return,end,doneorrepeat(see Control Flow).
Single-Line Statements
The next example has five single-line statements, each starting with a keyword: new declares, let modifies, expect verifies. The header line is a declaration, and the final return ends that declaration.
aspect assign_demo is
process main is
** initialization statements
new a := 2;
new b := 0.0;
** evaluate an expression
let b := a + 0.5;
** verification statements
expect a == 2;
expect b == 2.5;
return;
end assign_demo;
Multi-Line Statements
A statement continues on the next line until ;:
- an expression can continue after an operator;
- a literal enclosed in parentheses, brackets, braces or quotes can span lines.
** multiple line demo
process main is
** multi-line expression
new var := 1
+ 2
+ 3 + 4 +
5 + 6;
** multi-line literal
new map := {
'two' :2,
'four':4,
'six' :6
};
return;
Pitfalls
Identity Versus Equality
is compares locations. On native values it is always false; use == to compare values.
Reference Versus Copy
:= shares the reference of an object, so a change through one variable is visible through the other. Use :: to make an independent copy.
Stars and Comments
** always starts a comment, so 2 ** 3 is 2 followed by a comment. The power operator is ^: 2 ^ 3. Block comments do not nest.