Céu 0.8 - Reference Manual

1

Introduction

Céu is a programming language for reactive applications and intends to offer a higher-level and safer alternative to C. The two main peculiarities of Céu are the synchronous execution model and the use of organisms as abstractions.

Reactive applications interact in real time and continuously with external stimuli from the environment. They represent a wide range of software areas and platforms: from games in powerful desktops, "apps" in capable smart phones, to the emerging internet of things in constrained embedded systems.

Céu supports concurrent lines of execution---known as trails---that react continuously to input events from the environment. Waiting for an event halts the running trail until that event occurs. The environment broadcasts an occurring event to all active trails, which share a single global time reference (the event itself). The synchronous concurrency model of Céu greatly diverges from conventional multithreading (e.g. pthreads and Java threads) and the actor model (e.g.
erlang and Go). On the one hand, trails can share variables in a deterministic and seamless way (e.g. no need for locks or semaphores). On the other hand, there is no real parallelism (e.g. multi-core execution) in the standard synchronous operation mode of the language. Céu is a language for real-time concurrency with complex control specifications, but not for algorithm-intensive or distributed applications.

Céu integrates well with C, being possible to define and call C functions from within Céu programs.

Céu is free software.

1.1

Synchronous execution model

Céu is grounded on a precise definition of logical time as a discrete sequence of external input events: a sequence because only a single input event is handled at a logical time; discrete because reactions to events are guaranteed to execute in bounded time (here the human notion of time, see Bounded execution).

The execution model for Céu programs is as follows:

  1. The program initiates the boot reaction from the first line of code in a single trail.
  2. Active trails, one after another, execute until they await or terminate. This step is named a reaction chain, and always runs in bounded time.
  3. The program goes idle and the environment takes control.
  4. On the occurrence of a new external input event, the environment awakes all trails awaiting that event. It then goes to step 2.

The synchronous execution model of Céu is based on the hypothesis that internal reactions run infinitely faster in comparison to the rate of external events. An internal reaction is the set of computations that execute when an external event occurs. Conceptually, a program takes no time on step 2 and is always idle on step 3. In practice, if a new external input event occurs while a reaction chain is running (step 2), it is enqueued to run in the next reaction. When multiple trails are active at a logical time (i.e. awaking on the same event), Céu schedules them in the order they appear in the program text. This policy is somewhat arbitrary, but provides a priority scheme for trails, and also ensures deterministic and reproducible execution for programs. Note that, at any time, at most one trail is executing. Trails are created with parallel compositions.

The program and diagram below illustrate the behavior of the scheduler of Céu:

 1:  input void A, B, C;
 2:  par/and do           // A, B, and C are external input events
 3:      // trail 1
 4:      <...>            // <...> represents non-awaiting statements
 5:      await A;
 6:      <...>
 7:  with
 8:      // trail 2
 9:      <...>
10:      await B;
11:      <...>
12:  with
13:      // trail 3
14:      <...>
15:      await A;
16:      <...>
17:      await B;
18:      par/and do
19:          // trail 3
20:          <...>
21:      with
22:          // trail 4
23:          <...>
24:      end
25:  end

The program starts in the boot reaction and splits in three trails (a par/and rejoins after all trails terminate). Following the order of declaration for the trails, they are scheduled as follows (t0 in the diagram):

As no other trails are pending, the reaction chain terminates and the scheduler remains idle until the event A occurs (t1 in the diagram):

During this reaction, new instances of events A, B, and C occur (t1 in the diagram) and are enqueued to be handled in the reactions that follow. As A happened first, it is used in the next reaction. However, no trails are awaiting it, so an empty reaction chain takes place (t2 in the diagram). The next reaction dequeues the event B (t3 in the diagram):

With all trails terminated, the program also terminates and does not react to the pending event C. Note that each step in the logical time line (t0, t1, etc.) is identified by the event it handles. Inside a reaction, trails only react to that identifying event (or remain suspended).

1.1.1

Parallel compositions and abortion

The use of trails in parallel allows programs to wait for multiple events at the same time. Céu supports three kinds of parallel compositions differing in how they rejoin and proceed to the statement in sequence:

  1. a par/and rejoins after all trails in parallel terminate;
  2. a par/or rejoins after any trail in parallel terminates;
  3. a par never rejoins (even if all trails terminate).

The termination of a trail inside a par/or aborts the other trails in parallel, which must be necessarily awaiting (from rule 2 of Execution model). Before aborting, a trail has a last opportunity to execute all active finalization statements.

As mentioned in the introduction and emphasized in the execution model, trails inside parallel compositions do execute with real parallelism. It is more accurate to think of parallel compositions as trails awaiting in parallel, given that conceptually trails are always awaiting.

1.1.2

Bounded execution

Reaction chains should run in bounded time to guarantee that programs are responsive and can handle upcoming input events from the environment. For any loop statement in a program, Céu requires that every possible path inside its body contains at least one await or break statement, thus avoiding tight loops (i.e., unbounded loops that do not await).

In the example below, the if true branch may never execute, resulting in a tight loop (which the compiler complains about):

loop do
    if <cond> then
        break;
    end
end

For time-consuming algorithms that require unrestricted loops (e.g., cryptography, image processing), Céu provides Asynchronous execution.

1.1.3

Deterministic execution

TODO (deterministic scheduler + optional static analysis)

1.1.4

Internal reactions

Céu provides inter-trail communication through internal events. Trails use the await and emit operations to manipulate internal events, i.e., a trail that emits an event can awake trails previously awaiting the same event.

An emit starts a new internal reaction in the program:

  1. On an emit, the scheduler saves the statement following it to execute later.
  2. All trails awaiting the emitted event awake and execute (like rule 2 for external reactions).
  3. The emitting trail resumes execution on the saved statement.

If an awaking trail emits another internal event, a new internal reaction starts. The scheduler uses a stack policy (first in, last out) for saved continuation statements from rule 1.

Example:

1:  par/and do
2:      await e;
3:      emit f;
4:  with
5:      await f;
6:  with
7:      ...
8:      emit e;
9:  end

The emit e in trail-3 (line 8) starts an internal reaction that awakes the await e in trail-1 (line 2). Then, the emit f (line 3) starts another internal reaction that awakes the await f in trail-2 (line 5). Trail-2 terminates and the emit f resumes in trail-1. Trail-1 terminates and the emit e resumes in trail-3. Trail-3 terminates. Finally, the par/and rejoins (all trails have terminated) and the program terminates.

1.2

Organisms as abstractions

Céu uses an abstraction mechanism that reconciles data and control state into the single concept of an organism. Organisms provide an object-like interface (data state) as well as multiple lines of execution (control state).

A class of organisms is composed of an interface and a single execution body. The interface exposes public variables, methods, and internal events, like in object oriented programming. The body can contain any valid code in Céu (including parallel compositions) and starts on organism instantiation, executing in parallel with the program. Organism instantiation can be either static or dynamic.

The example below (in the right) blinks two LEDs in parallel with different frequencies. Each blinking LED is a static instance organism of the Blink class:


 1:  class Blink with
 2:      var int led;
 3:      var int freq;
 4:  do
 5:      loop do
 6:          _on(this.led);
 7:          await (this.freq)s;
 8:          _off(this.led);
 9:          await (this.freq/2)s;
10:      end
11:  end
12:
13:  var Blink b1 with
14:       this.led  = 0;
15:       this.freq = 2;
16:  end
17:
18:  var Blink b2 with
19:       this.led  = 1;
20:       this.freq = 4;
21:  end
22:
23:  await 1min;

 1:  var _Blink b1 with
 2:      this.led  = 0;
 3:      this.freq = 2;
 4:  end
 5:
 6:  var _Blink b2 with
 7:      this.led  = 1;
 8:      this.freq = 4;
 9:  end
10:
11:  par/or do
12:      // body of b1
13:      loop do
14:          _on(b1.led);
15:          await (b1.freq)s;
16:          _off(b1.led);
17:          await (b1.freq)s;
18:      end
19:      await FOREVER;
20:  with
21:      // body of b2
22:      loop do
23:          _on(b2.led);
24:          await (b2.freq)s;
25:          _off(b2.led);
26:          await (b2.freq)s;
27:      end
28:      await FOREVER;
29:  with
30:      await 1min;
31:  end

The Blink class (lines 1-11) exposes the led and freq fields, which correspond to the LED port and blinking frequency to be configured for each instance. The application creates two instances, specifying the fields in the constructors (lines 13-16 and 18-21). A constructor starts the instance body to execute in parallel with the application. When reaching the await 1min (line 23), each instance already has its body switching between _on() and _off() every freq milliseconds (lines 5-10).

The code in the left is semantically equivalent to the one in the right, which expands the organisms bodies (lines 13-18 and 22-27) in a par/or with the rest of the application (await 1min, in line 30). Note the await FOREVER statements (lines 19 and 28) that avoid the organisms bodies to terminate the par/or. The _Blink type corresponds to a simple datatype without execution body (i.e., conventional structs or records or objects).

See also Organism declarations, Class and Interface declarations, and Dynamic execution.

2

Lexical rules

2.1

Keywords

Keywords in Céu are reserved names that cannot be used as identifiers (e.g., variable and class names):


        and         async       atomic      await       bool

        break       byte        call        call/rec    char

        class       continue    do          else        else/if

        emit        end         escape      event       every

        f32         f64         false       finalize    float

        FOREVER     free        function    global      if

        in          input       input/output    int     interface

        isr         loop        native      not         nothing

        null        or          outer       output      output/input

        par         par/and     par/or      pause/if    pool

        return      s16         s32         s64         s8

        sizeof      spawn       sync        then        this

        thread      true        u16         u32         u64

        u8          uint        until       var         void

        watching    with        word

        @const      @hold       @nohold     @plain      @pure

        @rec        @safe

2.2

Identifiers

Céu uses identifiers to refer to variables, internal events, external events, classes/interfaces, and native symbols.

ID      ::= <a-z, A-Z, 0-9, _> +
ID_var  ::= ID    // beginning with a lowercase letter (variables and internal events)
ID_ext  ::= ID    // all in uppercase, not beginning with a digit (external events)
ID_cls  ::= ID    // beginning with an uppercase letter (classes)
ID_nat  ::= ID    // beginning with an underscore (native symbols)

Examples:

var int a;                    // "a" is a variable
emit e;                       // "e" is an internal event
await E;                      // "E" is an external input event
var T t;                      // "T" is a class
_printf("hello world!\n");    // "_printf" is a native symbol

2.3

Literals

2.3.1

Booleans

Boolean types have the values true and false.

2.3.2

Integers

Integer values can be written in different bases and also as ASCII characters:

Examples:

// all following are equal to the decimal 127
v = 127;
v = 0777;
v = 0x7F;

// newline ASCII character = decimal 10
c = '\n';

2.3.3

Floats

TODO (like C)

2.3.4

Null pointer

The null literal represents null pointers.

2.3.5

Strings

A sequence of characters surrounded by " is converted into a null-terminated string, just like in C:

Example:

_printf("Hello World!\n");

2.4

Comments

Céu provides C-style comments.

Single-line comments begin with // and run to end of the line.

Multi-line comments use /* and */ as delimiters. Multi-line comments can be nested by using a different number of * as delimiters.

Examples:

var int a;    // this is a single-line comment

/** comments a block that contains comments

var int a;
/* this is a nested multi-line comment
a = 1;
*/

**/

3

Types

Céu is statically typed, requiring all variables and events to be declared before they are used.

A type is composed of an identifier with an optional modifier:

Type ::= ID_type ( {`*´} | `&´ | `[´ `]´ | `[´ NUM `]´ )

A type identifier can be a native identifier, a class identifier, or one of the primitive types:

ID_type ::= ( ID_nat | ID_cls |
              bool  | byte  | char  | f32   | f64   |
              float | int   | s16   | s32   | s64   |
              s8    | u16   | u32   | u64   | u8    |
              uint  | void  | word )

Examples:

var u8 v;       // "v" is of 8-bit unsigned integer type
var _rect r;    // "r" is of external native type "rect"
var char* buf;  // "buf" is a pointer to a "char"
var T t;        // "t" is an organism of class "T"

3.1

Primitive types

Céu has the following primitive types:

    void               // void type
    word               // type with the size of platform dependent word
    bool               // boolean type
    char               // char type
    byte               // 1-byte type
    int      uint      // platform dependent signed and unsigned integer
    s8       u8        // signed and unsigned  8-bit integer
    s16      u16       // signed and unsigned 16-bit integer
    s32      u32       // signed and unsigned 32-bit integer
    s64      u64       // signed and unsigned 64-bit integer
    float              // platform dependent float
    f32      f64       // 32-bit and 64-bit floats

See also the literals for these types.

3.2

Native types

Types defined externally in C can be prefixed by _ to be used in Céu programs.

Example:

var _message_t msg;      // "message_t" is a C type defined in an external library

Native types support annotations which provide additional information to the compiler.

3.3

Class and Interface types

TODO (brief description)

See Classes and Interfaces.

3.4

Type modifiers

Types can be suffixed with the following modifiers: *, &, [], and [N].

3.4.1

Pointers

TODO (like C)

3.4.2

References

TODO (more or less like C++)

3.4.3

Buffer pointers

TODO (more or less like pointers)

3.4.4

Vectors

One-dimensional vectors are declared by suffixing the variable type with the vector length surrounded by [ and ]. The first index of a vector is zero.

Example:

var int[2] v;       // declares a vector "v" of 2 integers

Note: currently, Céu has no syntax for initializing vectors.

4

Statements

4.1

Blocks

A block is a sequence of statements separated by semicolons (;):

Block ::= { Stmt `;´ }

Note: statements terminated with the end keyword do not require a terminating semicolon.

A block creates a new scope for variables, which are only visible for statements inside the block.

Compound statements (e.g. if-then-else) create new blocks and can be nested for an arbitrary level.

4.1.1

do-end

A block can be explicitly created with the do-end statement:

Do ::= do Block end

4.2

Nothing

nothing is a innocuous statement:

Nothing ::= nothing

4.3

Declarations

4.3.1

Variables

The syntax for the definition of variables is as follows:

Dcl_var ::= var Type ID_var [`=´ SetExp] { `,´ ID_var [`=´ SetExp] }

A variable must have an associated type and can be optionally initialized (see Assignments).

Variables are only visible inside the block they are defined.

Examples:

var int a=0, b=3;   // declares and initializes integer variables "a" and "b"
var int[2] v;       // declares a vector "v" of size 2
4.3.1.1

Organisms

An organism is a variable whose type is the identifier of a class declaration. An optional constructor can initialize the organism fields:

Dcl_org ::= var Type ID_var [ with
              Block
            end ]

Example:

class T with
    var int v;
do
    <body-of-T>
end
var T t with       // "t" is an organism of class "T"
    this.v = 0;    // whose field "v" is initialized to "0"
end

After the declaration, the body of an organism starts to execute in parallel with the rest of the application. The table below shows the equivalent expansion of an organism declaration to a par/or composition containing the class body:

<code-pre-declaration>
var T t with
    <code-constructor-of-t>
end;
<code-pos-declaration>
<code-pre-declaration>
par/or do
    <code-constructor-of-t>
    <code-body-of-class-T>
    await FOREVER;
with
    <code-pos-declaration>
end

Given that an organism is a variable, the block it is declared restricts its life. In the expansion, the par/or makes the organism to go out of scope when &lt;code-pos-declaration&gt; terminates.

TODO (assumes code-pos-declaration closes the block exactly on the end) TODO (vectors of organisms: copy the declaration N times)

4.3.1.1.1
Constructors

Inside constructors the expression this refers to the new organism, while the expression outer refers to the organism creating the new organism:

class U with
    var int v;
do
    ...
end

class T with
    var int v;
do
    var U u with
        this.v = outer.v;   // "this" is of class "U", "outer" is of class "T"
    end;
end

4.3.2

Events

See also Event handling.

4.3.2.1

External events

External events are used as interfaces between programs and devices from the real world:

Being reactive, programs in Céu have input events as their sole entry points through await statements.

An external event is either of type input or output, never being both at the same time. For devices that perform input and output (e.g. radio transceivers), the underlying platform must provide different events for each functionality.

The declaration of input and output events is as follows:

Dcl_ext ::= input (Type|TypeList) ID_ext { `,´ ID_ext }
         |  output Type ID_ext { `,´ ID_ext }

TypeList ::= `(´ Type { `,´ Type } `)´

Events communicate values between the environment and the application (and vice-versa). The declaration includes the type of the value, which can be also a list of types when the event communicates multiple values.

Note: void is a valid type for signal-only events.

The visibility of external events is always global, regardless of the block they are declared.

Examples:

input void A,B;      // "A" and "B" are input events carrying no values
output int MY_EVT;   // "MY_EVT" is an output event carrying integer values

The availability of external events depends on the platform in use. Therefore, external declarations just make pre-existing events visible to a program.

Refer to Environment for information about interfacing with external events in the platform level.

4.3.2.1.1
Requests

TODO (emit + await)

4.3.2.2

Internal events

Internal events have the same purpose of external events, but for communication within trails in a program.

The declaration of internal events is as follows:

Dcl_int ::= event (Type|TypeList) ID_var { `,´ ID_var }

In contrast with external events, an internal event is for input and output at the same time.

Internal events cannot be of a vector type.

Note: void is a valid type for signal-only internal events.

4.3.3

Functions

4.3.3.1

Internal functions

TODO (like functions in any language)

Dcl_fun ::= function [@rec] ParList `=>´ Type ID_var
            [ do Block end ]

ParList     ::= `(´ ParListItem [ { `,´ ParListItem } ] `)´
ParListItem ::= [@hold] Type [ID_var]
4.3.3.1.1
return

TODO (like return in any language)

Return ::= return [Exp]
4.3.3.2

External functions

TODO (more or less like dynamically loaded functions)

4.3.3.3

Interrupt service routines

TODO (special/restricted functions)

4.3.4

Classes and Interfaces

A class is a template for creating organisms. It contains an interface and a body common to all instances of the class. The interface connects an organism with the rest of the application, exposing internal variable, events, and methods that other organisms can manipulate directly. The body specifies the behavior of the organism and executes when it is instantiated.

An interface is a template for classes that shares the same interface (as described above, the term interface is overloaded here). The body and methods implementations may vary across classes sharing the same interface.

The declaration of classes and interfaces is as follows:

Dcl_cls ::= class ID_cls with
                Dcls    // interface
            do
                Block   // body
            end

Dcl_ifc ::= interface ID_cls with
                Dcls    // interface
            end

Dcls ::= { (Dcl_var | Dcl_int | Dcl_pool | Dcl_fun | Dcl_imp) `;´ }

Dcl_imp ::= interface ID_cls { `,´ ID_cls }

Dcls is a sequence of variables, events, pools, and functions (methods) declarations. It can also refer other interfaces through a Dcl_imp clause, which copies all declarations from the referred interfaces (similarly to the implements clause of Java).

4.3.5

Pools

A pool is a container for dynamic instances of organisms of the same type:

Dcl_pool ::= pool Type ID_var { `,´ ID_var }

The type has to be a class or interface identifier followed by a vector modifier. For pools of classes, the number inside the vector brackets represents the maximum number of instances supported by the pool. For pools of interfaces, the number represents the maximum number of bytes for all instances (as each instance may have a different size). The number inside the vector modifier brackets is optional, though. In this case, the number of instances in the pool is unbounded.

Examples:

pool T[10]  ts;      // a pool of at most 10 instances of class "T"
pool T[]    ts;      // an unbounded pool of instances of class "T"
pool I[100] is;      // a pool of at most 100 bytes of instances of interface "I"
pool I[]    is;      // an unbounded pool of instances of interface "I"

The life of all organisms inside a pool is restricted to the block it is declared. When the pool goes out of scope, all organism bodies are aborted.

See Dynamic execution for organisms allocation.

4.3.6

Native symbols

Native declarations provide additional information about external C symbols. A declaration is an annotation followed by a list of symbols:

Dcl_nat   ::= native [@pure|@const|@nohold|@plain] Nat_list
Nat_list  ::= (Nat_type|Nat_func|Nat_var) { `,` (Nat_type|Nat_func|Nat_var) }
Nat_type  ::= ID_nat `=´ NUM
Nat_func  ::= ID_nat `(´ `)´
Nat_var   ::= ID_nat

A type declaration may define its size in bytes to help the compiler organizing memory. A type of size 0 is an opaque type and cannot be instantiated as a variable that is not a pointer.

Functions and variables are distinguished by the () that follows function declarations.

Native symbols can have the following annotations:

@plain states that the type is not a pointer to another type. @const states that the variable is actually a constants (e.g. a #define). @pure states that the function has no side effects. @nohold states that the function does not hold pointers passed as parameters.

The static analysis of Céu relies on annotations.

Examples:

native _char=1, _FILE=0;              // "char" is a 1-byte type, while `FILE` is "opaque"
native @plain  _rect;                  // "rect" is not a pointer type
native @const  _NULL;                  // "NULL" is a constant
native @pure   _abs(), _pow();         // "abs" and "pow" are pure functions
native @nohold _fprintf(), _sprintf(); // functions receive pointers but do not hold references to them

4.3.7

Safe annotations

A variable or function can be declared as @safe with a set of other functions or variables:

Dcl_det ::= @safe ID with ID { `,´ ID }

Example:

native _p, _f1(), _f2();
@safe _f1 with _f2;
var int* p;
@safe p with _p;
par do
    _f1(...);    // `f1` is safe with `f2`
    *p = 1;      // `p`  is safe with `_p`
    ...
with
    _f2(...);    // `f2` is safe with `f1`
    *_p = 2;     // `_p` is safe with `p`
    ...
end

See also Static analysis.

4.4

Assignments

Céu supports many kinds of assignments:

Set ::= Exp `=´ SetExp
SetExp ::= Exp | <do-end> | <if-then-else> | <loop>
               | <every>  | <par> | <await> | <emit (output)>
               | <thread> | <spawn> )

The expression on the left side must be assignable.

4.4.1

Simple assignment

The simpler form of assignment uses expressions as values.

Example:

var int a,b;
a = b + 1;

4.4.2

Block assignment

A whole block can be used as an assignment value by escaping from it. The following block statements can be used in assignments: do-end´](#do-end) [if-then-else](#conditional), [loop](#repetition), [every](#every), and [par`.

4.4.2.1

escape

An escape statement escapes the deepest block being assigned to a variable. The expression following it is then assigned to the respective variable:

Escape ::= escape Exp

Every possible path inside the block must reach a escape statement whose expression becomes the final value of the assignment.

Example:

a = loop do              // a=1, when "cond" is satisfied
        ...
        if cond then
            escape 1;    // "loop" is the deepest assignment block
        end
        ...
    end

Every program in Céu contains an implicit do-end surrounding it, assigning to a special integer variable $ret holding the return value for the program execution.

Therefore, a program such as

escape 1;

should read as

var int $ret =
    do
        escape 1;
    end;

4.4.3

Await assignment

See Await statements.

4.4.4

Emit assignment

See Emit statements.

4.4.5

Thread assignment

See Threads.

4.4.6

Spawn assignment

See Dynamic execution.

4.5

Calls

The syntax for function calls is as follows:

Call ::= [ call|call/rec ] Exp * `(´ [ExpList] `)´
ExpList = Exp { `,´ Exp }

The called expression has to evaluate to a internal, external), or native function. The call operator is optional, but recursive functions must use the call/rec operator (see Static analysis).

Examples:

_printf("Hello World!\n");  // calls native "printf"
o.f();                      // calls method "f" of organism "o"
F(1,2,3);                   // calls external function "F"

4.6

Event handling

Events are the most fundamental concept of Céu, accounting for its reactive nature. Programs manipulate events through the await and emit statements. An await halts the running trail until that event occurs. An event occurrence is broadcast to all trails trails awaiting that event, awaking them to resume execution.

Céu supports external and internal events. External events are triggered by the environment, while internal events, by the emit statement. See also Synchronous execution model for the differences between external and internal reactions.

4.6.1

Await statements

The await statement halts the running trail until the referred wall-clock time, input event, or internal event occurs.

Await ::= ( await ID_ext |
            await Exp    |
            await (WCLOCKK|WCLOCKE)
          ) [ until Exp ]
       | await FOREVER

VarList ::= `(´ ID_var  { `,´ ID_var } `)´

WCLOCKK ::= [NUM h] [NUM min] [NUM s] [NUM ms] [NUM us]
WCLOCKE ::= `(´ Exp `)´ (h|min|s|ms|us)

Examples:

await A;                  // awaits the input event `A`
await a;                  // awaits the internal event `a`

await 10min3s5ms100us;    // awaits the specified time
await (t)ms;              // awaits the current value of the variable `t` in milliseconds
    
await FOREVER;            // awaits forever

An await may evaluate to zero or more values which can be captured with the optional assignment syntax.

The optional until clause tests an additional condition required to awake. It can be understood as the expansion below:

loop do
    await <...>;
    if <Exp> then
        break;
    end
end
4.6.1.1

Await event

For await statements with internal or external events, the running trail awakes when the referred event is emitted. The await evaluates to the type of the event.

input int E;
var int v = await E;

event (int,int*) e;
var int  v;
var int* ptr;
(v,ptr) = await e;
4.6.1.2

Await time

For await statements with wall-clock time (i.e., time measured in minutes, milliseconds, etc.), the running trail awakes when the referred time elapses.

A constant time is expressed with a sequence of value/unit-of-time pairs (see WCLOCKK above). An expression time is specified with an expression in parenthesis followed by a single unit of time (see WCLOCKE above).

The await evaluates to the residual delta time (dt) (i.e. elapsed time minus requested time), measured in microseconds:

var int dt = await 30ms;    // if 31ms elapses, then dt=1000

Note: dt is always greater than or equal to 0.

4.6.1.3

Await FOREVER

The await FOREVER halts the running trail forever. It cannot be used in assignments, because it never evaluates to anything.

4.6.2

Emit statements

The emit statement triggers the referred wall-clock time, input event, or internal event, awaking all trails waiting for it.

Emit ::= emit Exp    [ `=>´ (Exp | `(´ ExpList `)´)
      |  emit ID_ext [ `=>´ (Exp | `(´ ExpList `)´)
      |  emit (WCLOCKK|WCLOCKE)
4.6.2.1

Emit event

Emit statements with internal or external events expect parameters that match the event type (unless the event is of type void).

Examples:

output int E;
emit E => 1;

event (int,int) e;
emit e => (1,2);

External input events can only be emitted inside asynchronous blocks.

The emission of internal events start new internal reactions.

TODO (emit output evaluates to "int")

4.6.2.2

Emit time

Emit statements with wall-clock time expect expressions with units of time, as described in Await time.

Like input events, time can only be emitted inside asynchronous blocks.

4.7

Conditional

Conditional flow uses the if-then-else statement:

If ::= if Exp then
           Block
       { else/if Exp then
           Block }
       [ else
           Block ]
       end

The block following then executes if the condition expression after the if evaluates to a non-zero value. Otherwise, the same process holds each else/if alternative. Finally, it they all fail, the block following the else executes.

4.8

Repetition

A loop continuously executes its body block:

Loop ::= loop [ Iterator ] do
             Block
         end
Iterator ::= [`(´ Type `)´] ID_var [in Exp]

A loop terminates when it reaches a break or its (optional) iterator terminates.

4.8.1

break

A break escapes the innermost enclosing loop.

Example:

loop do                   // loop 1
    ...
    loop do               // loop 2
        if <cond-1> then
            break;        // escapes loop 2
        end
    end
    ...
    if <cond-2> then
        break;            // escapes loop 1
    end
    ...
end

4.8.2

Iterators

A loop may specify an iterator that yields a new value on each loop iteration.

4.8.2.1

Incremental index

For iterators in which Exp is empty or is of type int, ID_var is incremented after each loop iteration. ID_var is automatically declared read-only, with visibility restricted to the loop body, and is initialized to zero. The optional Exp limits the number of iterations, and is evaluated once before the loop starts.

Example:

loop i in 10 do
    _printf("i = %d\n", i);     // prints "i = 0" up to "i = 9"
end
4.8.2.2

Pool instances

For iterators in which Exp evaluates to a pool, ID_var´ evaluates to the instances on the pool, one at a time, from the oldest to the newest.ID_var` is automatically declared read-only, with visibility restricted to the loop body.

The optional typecast tries

4.8.3

every

The every statement continuously awaits an event and executes its body:

Every ::= every (Exp|VarList) in (WCLOCKK|WCLOCKE|ID_ext|Exp) do
              Block
          end

An every expands to a loop as illustrated below:

every <attr> in <event> do
    <block>
end
loop do
    <attr> = await <event>
    <block>
end

The body of an every cannot contain an await, ensuring that no occurrences of &lt;event&gt; are ever missed.

4.9

Finalization

The finalize statement postpones the execution of its body to happen when its associated block goes out of scope:

Finalize ::= finalize
                 [Exp `=´ SetExp]
             with
                 Block
             end

The presence of the optional attribution clause determines which block to associate with the finalize:

  1. The enclosing block, if the attribution is absent.
  2. The block of the variable being assigned, if the attribution is present.

Example:


input int A;
par/or do
    var _FILE* f;
    finalize
        f = _fopen("/tmp/test.txt");
    with
        _fclose(f);
    end
    every v in A do
        fwrite(&v, ..., f);
    end
with
    await 1s;
end

The program open f and writes to it on every occurrence of A. The writing trail is aborted after one second, but the finalize safely closes the file, because it is associated to the block that declares f.

The static analysis of Céu enforces the use of finalize for unsafe attributions.

4.10

Parallel compositions

The parallel statements par/and, par/or, and par split the running trail in multiple others:

Pars ::= (par/and|par/or|par) do
               Block
          with
               Block
          { with
               Block }
           end

They differ only on how trails terminate (rejoin).

See Synchronous execution model for a detailed description of parallel execution.

4.10.1

par/and

The par/and statement stands for parallel-and and rejoins when all trails terminate:

4.10.2

par/or

The par/or statement stands for parallel-or and rejoins when any of the trails terminate:

4.10.3

par

The par statement never rejoins and should be used when the trails in parallel are supposed to run forever:

4.10.4

watching

The watching statement aborts its body when its associated event occurs:

Watching ::= watching (WCLOCKK|WCLOCKE|ID_ext|Exp) do
                 Block
             end

A wacthing expands to a par/or as illustrated below:

watching <event> do
    <block>
end
par/or do
    <block>
with
    await <event>
end

TODO (supports org refs)

4.11

pause/if

TODO

Pause ::= pause/if Exp do
              Block
          end

4.12

Dynamic execution

The spawn statement creates instances of organisms dynamically:

Dyn ::= spawn ID_cls [in Exp]
            [ with Constructor end ]

The spawn returns a pointer to the allocated organism, or null in the case of failure.

The optional in clause allows the statement to specify in which pool the organisms will live. If absent, the organism is allocated on an implicit pool in the outermost block of the class the allocation happens.

On allocation, the body of the organism starts to execute in parallel with the rest of the application, just like happens for static organisms. The constructor clause is also the same as for static organisms.

A dynamic organism is also automatically deallocated when its execution body terminates.

See Static analysis for the restrictions on manipulating pointers and references to organisms.

4.13

Asynchronous execution

Asynchronous execution permit that programs execute time consuming computations without interfering with the synchronous side of applications (i.e., everything, except asynchronous statements).

Async ::= async [thread] [RefVarList] do
              Block
          end

RefVarList ::= `(´ [`&´] ID_var { `,´ [`&´] ID_var } `)´

4.13.1

Asynchronous blocks

Asynchronous blocks (async) are the simplest alternative for asynchronous execution.

An async body can contain non-awaiting loops (tight loops), which are disallowed on the synchronous side to ensure that programs remain reactive.

The optional list of variables copies values between the synchronous and asynchronous scopes. With the prefix &, the variable is passed by reference and can be altered from inside the async.

The next example uses an async to execute a time-consuming computation, keeping the synchronous side reactive. In a parallel trail, the program awaits one second to kill the computation if it takes too long:

var int fat;
par/or do
    var int v = ...

    // calculates the factorial of v
    fat = async (v) do
        var int fat = 1;
        loop i in v do   // a tight loop
            // v varies from 0 to (v-1)
            fat = fat * (i+1);
        end
        return fat;
    end;
with
    await 1s;          // watchdog to kill the async if it takes too long
    fat = 0;
end
return fat;

An async has the following restrictions:

  1. Only executes if there are no pending input events.
  2. Yields control on every loop iteration on its body.
  3. Cannot use parallel compositions.
  4. Cannot nest other asyncs.
  5. Cannot await events.
  6. Cannot emit internal events.
4.13.1.1

Simulation

An async is allowed to trigger input events and the passage of time, providing a way to test programs in the language itself:

input int A;

// tests a program with a simulation in parallel
par do

    // original program
    var int v = await A;
    loop i do
        await 10ms;
        _printf("v = %d\n", v+i);
    end

with

    // input simulation
    async do
        emit A=>0;      // initial value for "v"
        emit 1s35ms;    // the loop executes 103 times
    end
    return 0;
end

Every time the async emits an event, it suspends (due to rule 1 of previous section). The example prints the v = <v+i> message exactly 103 times.

4.13.2

Threads

TODO

4.13.2.1

Synchronous blocks

TODO

Sync ::= sync do
             Block
         end

4.14

Native blocks

Native blocks define new types, variables, and functions in C:

Native ::= native do
               <code_in_C>
           end

Example:

native do
    #include 
    int inc (int i) {
        return i+1;
    }
end
_assert(_inc(0) == 1);

If the code in C contains the terminating end keyword of Céu, the native block should be delimited with any matching comments to avoid confusing the parser:

native do
    /*** c code ***/
    char str = "This `end` confuses the parser";
    /*** c code ***/
end

5

Expressions

The syntax for expressions in Céu is as follows:

Exp ::= Prim
     |  Exp (or|and) Exp
     |  Exp (`|´|`^´|`&´) Exp
     |  Exp (`!=´|`==´) Exp
     |  Exp (`<=´|`<´|`>´|`>=´) Exp
     |  Exp (`<<´|`>>´) Exp
     |  Exp (`+´|`-´) Exp
     |  Exp (`*´|`/´|`%´) Exp
     |  not Exp
     |  `&´ Exp
     |  (`-´|`+´) Exp
     |  `~´ Exp
     |  `*´ Exp
     |  `(´ Type `)´ Exp
     |  Exp `(´ [ExpList] `)´ [finalize with Block end]
     |  Exp `[´ Exp `]´
     |  Exp (`.´|`:´) ID

Prim ::= `(´ Exp `)´
      |  sizeof `(´ (Type|Exp) `)´
      |  ID_var | ID_nat
      |  null | NUM | String
      |  global | this | outer
      |  (call | call/rec) Exp

Most operators follow the same semantics of C.

Note: assignments are not expressions in Céu.

5.1

Primary

TODO: global, this, outer,

5.2

Arithmetic

The arithmetic operators of Céu are

    +      -      %      *      /      +      -

which correspond to addition, subtraction, modulo (remainder), multiplication, division, unary-plus, and unary-minus.

5.3

Relational

The relational operators of Céu are

    ==      !=      >      <      >=      <=

which correspond to equal-to, not-equal-to, greater-than, less-than, greater-than-or-equal-to, and less-than-or-equal-to.

Relational expressions evaluate to 1 (true) or 0 (false).

5.4

Logical

The logical operators of Céu are

    not      and      or

5.5

Bitwise

The bitwise operators of Céu are

    ~      &      |      ^      <<      >>

which correspond to not, and, or, xor, left-shift, and right-shift.

5.6

Vector indexing

Céu uses square brackets to index vectors:

Index ::= Exp `[´ Exp `]´

The expression on the left side is expected to evaluate to a vector.

Vector indexes start at zero.

5.7

Pointer referencing and dereferencing

The operator * dereferences its pointer operand, while the operator &amp; returns a pointer to its operand:

Deref ::= `*´ Exp
Ref   ::= `&´ Exp

The operand to &amp; must be an [[#sec.exps.assignable|assignable expression]].

5.8

Fields

5.8.1

Structs

The operators .´ and:´ access the fields of structs.

Dot   ::= Exp `.´ Exp
Colon ::= Exp `:´ Exp

The operator . expects a struct as its left operand, while the operator : expects a reference to a struct.

Example:

native do
    typedef struct {
        int v;
    } mystruct;
end
var _mystruct s;
var _mystruct* p = &s;
s.v = 1;
p:v = 0;

Note: struct must be declared in C, as Céu currently has no support for it.

5.8.2

Organisms

TODO

TODO (index clash)

5.9

Type casting

Céu uses parenthesis for type casting:

Cast ::= `(´ ID_type `)´

5.10

Sizeof

A sizeof expression returns the size of a type or expression, in bytes:

Sizeof ::= sizeof `(´ (Type|Exp) `)´

5.11

Precedence

Céu follows the same precedence of C operators:

    /* lower to higer precedence */
    
    or
        
    and
        
    |
    
    ^
    
    &
    
    !=    ==
    
    <=    >=    <     >
    
    >>    <<
    
    +     -                // binary
    
    *     /     %
    
    not     &
    
    +     -                // unary
    
    <>                     // typecast
    
    ()    []    :    .     // call, index

5.12

Assignable expressions

An assignable expression (also known as an l-value) can be a variable, vector index, pointer dereference, or struct access. L-values are required in [[#sec.stmts.assignments|assignments]] and [[#sec.exps.pointers|references]].

Examples:

var int a;
a = 1;

var int[2] v;
v[0] = 1;

var int* p;
*p = 1;

var _mystruct s;
s.v = 1;

var _mystruct* ps;
ps:v = 1;

6

Static analysis

TODO (introduction)

6.1

Types

TODO (weakly typed, like C)

TODO (index clash)

6.2

Loops

TODO

6.3

Finalization

TODO

TODO (index clash)

6.4

Organisms references

TODO

7

Environment

As a reactive language, Céu depends on an external environment (the host platform) to provide input and output events to programs. The environment is responsible for sensing the world and notifying Céu about changes. The actual events vary from environment to environment, as well as the implementation for the notification mechanism (e.g. polling or interrupt-driven).

7.1

The C API

The final output of the compiler of Céu is a program in C that follows a standard application programming interface. The interface specifies some types, macros, and functions, which the environment has to manipulate in order to guide the execution of the original program in Céu.

The example below illustrates a possible main for a host platform:

#include "_ceu_app.c"

int main (void)
{
    char mem[sizeof(CEU_Main)];
    tceu_app app;
        app.data = &mem;
    ceu_app_init(&app);

    while(app->isAlive) {
        ceu_sys_go(app, CEU_IN__ASYNC,  CEU_EVTP((void*)NULL));
        ceu_sys_go(app, CEU_IN__WCLOCK, CEU_EVTP(<how-much-time-since-previous-iteration>));
        if (occuring(CEU_IN_EVT1)) {
            ceu_sys_go(app, CEU_IN__EVT1, param1);
        }
        ...
        if (occuring(CEU_IN_EVTn)) {
            ceu_sys_go(app, CEU_IN__EVTn, paramN);
        }
    }
    return app->ret;
}

int occurring (int evt_id) {
    <platform dependent>
}

tceu_app is a type that represents an application in Céu. The field app.data expects a pointer to the memory of the application, which has to be previously declared.

TODO

7.1.1

Types

TODO

TODO (index clash)

7.1.2

Functions

TODO

TODO (index clash)

7.1.3

Macros

TODO

7.1.4

Constants and Defines

TODO

7.2

Compiler

Céu provides a command line compiler that generates C code for a given input program. The compiler is independent of the target platform.

The generated C output should be included in the main application, and is supposed to be integrated with the specific platform through the presented [[#sec.env.api|API]].

The command line options for the compiler are as follows:

./ceu <filename>              # Ceu input file, or `-` for stdin

    --output <filename>       # C output file (stdout)

    --defs-file <filename>    # define constants in a separate output file (no)

    --join (--no-join)        # join lines enclosed by /*{-{*/ and /*}-}*/ (join)

    --dfa (--no-dfa)          # perform DFA analysis (no-dfa)
    --dfa-viz (--no-dfa-viz)  # generate DFA graph (no-dfa-viz)

    --m4 (--no-m4)            # preprocess the input with `m4` (no-m4)
    --m4-args                 # preprocess the input with `m4` passing arguments in between `"` (no)

The values in parenthesis show the defaults for the options that are omitted.

8

Errors

8.1

Pointer attributions (11xx)

8.1.1

1101 : wrong operator

Use of the unsafe operator ":=" for non-pointer attributions.

Instead, use =.

Example:

var int v := 1;

>>> ERR [1101] : file.ceu : line 1 : wrong operator

8.1.2

1102 : attribution does not require "finalize"

Use of finalize for non-pointer attributions.

Instead, do not use finalize.

Example:

var int v;
finalize
    v = 1;
with
    <...>
end

>>> ERR [1102] : file.lua : line 3 : attribution does not require "finalize"

8.1.3

1103 : wrong operator

Use of the unsafe operator := for constant pointer attributions.

Instead, use =.

Example:

var int ptr := null;

>>> ERR [1103] : file.ceu : line 1 : wrong operator

8.1.4

1104 : attribution does not require "finalize"

Use of finalize for constant pointer attributions.

Instead, do not use finalize.

Example:

var int ptr;
finalize
    ptr = null;
with
    <...>
end

>>> ERR [1104] : file.lua : line 3 : attribution does not require `finalize´

8.1.5

1105 : destination pointer must be declared with the "[]" buffer modifier

Use of normal pointer * to hold pointer to acquired resource.

Instead, use [].

Example:

var int* ptr = _malloc();

>>> ERR [1105] : file.ceu : line 1 : destination pointer must be declared with the `[]´ buffer modifier

8.1.6

1106 : parameter must be "hold"

Omit @hold annotation for function parameter held in the class or global.

Instead, annotate the parameter declaration with @hold.

Examples:

class T with
    var void* ptr;
    function (void* v)=>void f;
do
    function (void* v)=>void f do
        ptr := v;
    end
end

>>> ERR [1106] : file.ceu : line 6 : parameter must be `hold´

/*****************************************************************************/

native do
    void* V;
end
function (void* v)=>void f do
    _V := v;
end

>>> ERR [1106] : file.ceu : line 5 : parameter must be `hold´

8.1.7

1107 : pointer access across "await"

Access to pointer across an await statement. The pointed data may go out of scope between reactions to events.

Instead, don't do it. :)

(Or check if the pointer is better represented as a buffer pointer ([]).)

Examples:

event int* e;
var int* ptr = await e;
await e;     // while here, what "ptr" points may go out of scope
escape *ptr;

>>> ERR [1107] : file.ceu : line 4 : pointer access across `await´

/*****************************************************************************/

var int* ptr = <...>;
par/and do
    await 1s;   // while here, what "ptr" points may go out of scope
with
    event int* e;
    ptr = await e;
end
escape *ptr;

>>> ERR [1107] : file.ceu : line 8 : pointer access across `await´

8.1.8

1108 : "finalize" inside constructor

Use of finalize inside constructor.

Instead, move it to before the constructor or to inside the class.

Examples:

class T with
    var void* ptr;
do
    <...>
end

var T t with
    finalize
        this.ptr = _malloc(10);
    with
        _free(this.ptr);
    end
end;

>>> ERR [1008] : file.ceu : line 7 : `finalize´ inside constructor

/*****************************************************************************/

class T with
    var void* ptr;
do
    <...>
end

spawn T with
    finalize
        this.ptr = _malloc(10);
    with
        _free(this.ptr);
    end
end;

>>> ERR [1008] : file.ceu : line 7 : `finalize´ inside constructor

8.1.9

1109 : call requires "finalize"

Call missing finalize clause.

Call passes a pointer. Function may hold the pointer indefinitely. Pointed data goes out of scope and yields a dangling pointer.

Instead, finalize the call.

Example:

var char[255] buf;
_enqueue(buf);

>>> ERR [1009] : file.ceu : line 2 : call requires `finalize´'

8.1.10

1110 : invalid "finalize"

Call a function that does not require a finalize.

Instead, don't use it.

Example:

_f() finalize with
        <...>
     end;

>>> ERR [1010] : file.ceu : line 1 : invalid `finalize´

9

Syntax


Block ::= { Stmt `;´ }

Stmt ::= <empty-string>
        |  nothing
        |  escape Exp
        |  return [Exp]
        |  break
        |  continue

    /* Declarations */

        /* variable, organisms, events, and pools */
        | var Type ID_var [`=´ SetExp] { `,´ ID_var [`=´ SetExp] }
        | var Type ID_var with
              Block
          end
        | input (Type|TypeList) ID_ext { `,´ ID_ext }
        | output Type ID_ext { `,´ ID_ext }
        | event (Type|TypeList) ID_var { `,´ ID_var }
        | pool Type ID_var { `,´ ID_var }

        /* functions */
        | function [@rec] ParList `=>´ Type ID_var
              [ `do´ Block `end´ ]
            where
                ParList     ::= `(´ ParListItem [ { `,´ ParListItem } ] `)´
                ParListItem ::= [@hold] Type [ID_var]

        /* classes & interfaces */
        | class ID_cls with
              Dcls
          do
              Block
          end
        | interface ID_cls with
              Dcls
          end
            where
                Dcls    ::= { (<var> | <event> | <pool> | <function> | Dcl_imp) `;´ }
                Dcl_imp ::= interface ID_cls { `,´ ID_cls }

        /* native symbols */
        | native [@pure|@const|@nohold|@plain] Nat_list
            where
                Nat_list  ::= (Nat_type|Nat_func|Nat_var) { `,` (Nat_type|Nat_func|Nat_var) }
                Nat_type  ::= ID_nat `=´ NUM
                Nat_func  ::= ID_nat `(´ `)´
                Nat_var   ::= ID_nat

        /* deterministic annotations */
        | @safe ID with ID { `,´ ID }

    /* Assignments */

        | (Exp|VarList) `=´ SetExp

    /* Function calls */

        | [call|call/rec] Exp * `(´ [ExpList] `)´ ExpList = Exp { `,´ Exp }

    /* Event handling */

        /* await */
        | (
            await ID_ext |
            await Exp    |
            await (WCLOCKK|WCLOCKE)
          ) [ until Exp ]
        | await FOREVER

        /* emit */
        | emit Exp    [ `=>´ (Exp | `(´ ExpList `)´)
        | emit (WCLOCKK|WCLOCKE)
        | emit ID_ext [ `=>´ (Exp | `(´ ExpList `)´)

    /* Dynamic execution */
        | spawn * ID_cls * [in Exp]
              [ with Constructor end ]

    /* Flow control */

        /* explicit block */
        |  do Block end

        /* conditional */
        | if Exp then
              Block
          { else/if Exp then
              Block }
          [ else
              Block ]
          end

        /* loops */
        | loop [ [`(´ Type `)´] ID_var [in Exp] ] do
              Block
          end
        | every (Exp|VarList) in (WCLOCKK|WCLOCKE|ID_ext|Exp) do
              Block
          end

        /* finalization */
        | finalize [Exp `=´ SetExp] with
              Block
          end

        /* parallel compositions */
        | (par/and|par/or|par) do
              Block
          with
              Block
          { with
              Block }
           end
        | watching (WCLOCKK|WCLOCKE|ID_ext|Exp) do
              Block
          end

        /* pause */
        | pause/if Exp do
              Block
          end

        /* asynchronous execution */
        | async [thread] [RefVarList] do
              Block
          end
        | sync do
              Block
          end
            where
                RefVarList ::= `(´ [`&´] ID_var { `,´ [`&´] ID_var } `)´

VarList ::= `(´ ID_var  { `,´ ID_var } `)´
SetExp  ::= Exp | <do-end> | <if-then-else> | <loop>
                | <every>  | <par> | <await> | <emit (output)>
                | <thread> | <spawn> )

WCLOCKK ::= [NUM h] [NUM min] [NUM s] [NUM ms] [NUM us]
WCLOCKE ::= `(´ Exp `)´ (h|min|s|ms|us)

ID      ::= <a-z, A-Z, 0-9, _> +
ID_var  ::= ID    // beginning with a lowercase letter
ID_ext  ::= ID    // all in uppercase, not beginning with a digit
ID_cls  ::= ID    // beginning with an uppercase letter
ID_nat  ::= ID    // beginning with an underscore

Type    ::= ID_type ( {`*´} | `&´ | `[´ `]´ | `[´ NUM `]´ )
ID_type ::= ( ID_nat | ID_cls |
            | bool  | byte  | char  | f32   | f64   |
            | float | int   | s16   | s32   | s64   |
            | s8    | u16   | u32   | u64   | u8    |
            | uint  | void  | word )

Exp ::= Prim
        |  Exp (or|and) Exp
        |  Exp (`|´|`^´|`&´) Exp
        |  Exp (`!=´|`==´) Exp
        |  Exp (`<=´|`<´|`>´|`>=´) Exp
        |  Exp (`<<´|`>>´) Exp
        |  Exp (`+´|`-´) Exp
        |  Exp (`*´|`/´|`%´) Exp
        |  not Exp
        |  `&´ Exp
        |  (`-´|`+´) Exp
        |  `~´ Exp
        |  `*´ Exp
        |  `(´ Type `)´ Exp
        |  Exp `(´ [ExpList] `)´ [finalize with Block end]
        |  Exp `[´ Exp `]´
        |  Exp (`.´|`:´) ID

Prim ::= `(´ Exp `)´
        |  sizeof `(´ (Type|Exp) `)´
        |  ID_var | ID_nat
        |  null | NUM | String
        |  global | this | outer
        |  (call | call/rec) Exp

/* The operators follow the same precedence of C. */

    or              /* lowest priority */
    and
    |
    ^
    &
    !=    ==
    <=    >=    <     >
    >>    <<
    +     -                // binary
    *     /     %
    not     &
    +     -                // unary
    <>                     // typecast
    ()    []    :    .     // call, index

10

License

Céu is distributed under the MIT license reproduced below:

 Copyright (C) 2012 Francisco Sant'Anna
 
 Permission is hereby granted, free of charge, to any person obtaining a copy of
 this software and associated documentation files (the "Software"), to deal in
 the Software without restriction, including without limitation the rights to
 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 of the Software, and to permit persons to whom the Software is furnished to do
 so, subject to the following conditions:
 
 The above copyright notice and this permission notice shall be included in all
 copies or substantial portions of the Software.
 
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.