Eve Syntax

Eve uses short English keywords without abbreviations, explicit block terminators and a semicolon at the end of every statement. Blocks are not delimited by braces. Ada, Ruby, Fortran and Python influenced the design.

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, or repeat for 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 lineMeaning
# titleTitle of an aspect or a module.
## subtitleSubtitle.
*****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

FormWhere it startsEndsSpans linesNestsUse
#indentation 0 onlyend of linenonotitle
##indentation 0 onlyend of linenonosubtitle
**anywhere: indentation 0, indented, or after codeend of linenonoline and end-of-line notes
(** ... **)inside an expression**)yesnoexpression comment
/* ... */anywherethe first */yesnoblock comment, notes, disabled code

Notes:

  • Block comments do not nest: in /* a /* b */ c */ the comment ends after b. 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: Point and point are two names.
  • It cannot end with an underscore or contain two consecutive underscores.
  • It cannot contain -, which is the minus operator: a-b means a - 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.

SymbolKindMeaning
*prefixvararg parameter: *args
@prefixinput/output parameter or named result: @result
$sigilsystem-wide variable: $error
!suffixa 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";.

VariableTypeDescription
Runtime objects
$errorErrorthe 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()Callthe calls that led to the error
$trace()Errorthe errors and warnings of the process
$resultresult typethe result of a function that declares no result name; a list when the function has several results
Environment and folders
$NAMEStringany environment variable of the operating system, for example $PATH or $HOME
$EVE_DIRStringthe folder of the Eve runtime
$EVE_LIBStringthe folder of the installed libraries, searched by from … use
$EVE_ASPStringthe folder of the aspects of a primary aspect; by default asp, then the project root, then lib
$EVE_OUTStringthe output folder of the log files, "out" by default
$MY_DIRStringthe folder of the running aspect
Settings
$epsilonRealthe precision of comparisons between real numbers
$timeoutIntegerseconds 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)
$coresIntegerthe size of the worker pool of parallel aspects, the number of hardware cores by default, at most 16 (not in version 1)
$max_parallelIntegerthe number of aspects one parallel group of the primary aspect may start, 8 by default (not in version 1)
Constants
$err_nameIntegererror codes, for example $err_expect = 2, $err_assert = 3, $err_raise = 4 (see Exceptions)
$wrn_nameIntegerwarning 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

SymbolDescription
(_,_,_)List literal | parameters | arguments
[_,_,_]Array literal (vector or matrix)
{_,_,_}Ordinal | DataSet | DataMap | Object

String Delimiters

SymbolDescription
'...'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).

EscapeMeaning
\\backslash
\"double quote
\'single quote
\{ \}the brace itself
&name;HTML character reference, for example α
\nline feed (LF)
\rcarriage return (CR)
\thorizontal tab
\0NUL 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

SymbolDescription
!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

SymbolDescription
**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

SymbolDescription
*Multiplication | string replication
/Division | path concatenation
%Remainder
+Addition | string concatenation
-Subtraction | negation | set difference
^Power (** is a comment)

Relation Operators

SymbolDescription
==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.

SymbolMeaning
:=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.

SymbolDescription
ifConditional (ternary) operator
isSame object (compares locations)
is notDifferent objects (compares locations)
asQuick conversion with format constants, for date and time
eqEquivalence ≡: same value and type
not eqNot equivalent: different value or type
inBelongs to (∈)
not inDoes not belong to (∉)
notNegation
andLogical AND
orLogical OR
xorLogical 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.

LevelOperators
1. () [] (member, call, index)
2unary -, not
3^ (right to left)
4* / %
5+ -
6.. +-
7<< >>
8&&
9||
10== <> < > <= >= =~ is in eq
11and
12xor
13or
14if … else (ternary)

Table of Truth

ABA and BA or BA xor Bnot A
TrueTrueTrueTrueFalseFalse
TrueFalseFalseTrueTrueFalse
FalseTrueFalseTrueTrueTrue
FalseFalseFalseFalseFalseTrue

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.

PhaseKeywords listed in
1 Core Languagethis page
2 Eve Code TopologyScripts
3 Functional ProgrammingFunctions
4 Object Oriented FeaturesClasses
5 Enterprise & System FeaturesProcessing
6 Internet Featuresno keywords: the features are library modules
7 Implementation & ToolsCompiler

Script Structure

The words that every aspect uses.

KeywordMeaning
aspectheader of a script that runs: aspect name is … end name;
exclusive, concurrent, primarythe 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
processthe executable part of an aspect: process main is … return;; main is the entry point, the other processes are private
isends a declaration header (procedure p is); also the operators x is T and a is b
returncloses a process, function, method, constructor or destructor; it carries no value
endcloses 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);.

KeywordMeaning
newcreate 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
letexecute 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
setcreate a constant: set pi = 3.14 :Real;, or a class property
defdeclare 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.

KeywordMeaning
ifif c do … [else if c do …] [else …] done;; also the statement suffix break if c; and the ternary a if c else b
elsethe alternative branch of an if, of a ternary expression, or of a while loop
dostarts the executable region of a job, a loop, an if branch or a when path
donecloses an if, match, job, while or for: done; or done label;
whileloop tested at the start: while c do … done;; also the test of repeat while c;
forvisits 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;
repeatcloses 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];
whena path of a match: when v1, v2 do
oneoption of match: only the first matching path runs (the default)
alloption of match: every matching path runs
otherwhen other do: the path taken when no other when matched

Statements

Statements that check a condition or change the normal flow.

KeywordMeaning
breakleave a loop and continue after its closer: break [label] [if c];
skipgo 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];
passthe empty statement: do nothing
expectcheck a condition; a failed expect is an error with code 2
assertcheck a condition; a failed assert is an error with code 3: the process stops unless it is recovered
overend the whole process at once with code 0, from any depth: over;
exitleave 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
panicend the whole application at once with code 1

Operator Keywords

KeywordMeaning
andlogical AND
orlogical OR
xorlogical exclusive OR
notlogical NOT; also is not, not in, not eq
ischecks a type, x is Integer, or the identity of two references, a is b
inmembership, x in collection; also the source of for and of a builder
eqequivalence: the same value and the same type
asformats 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, done or repeat (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.