Manual-v0.4

From Céupédia
Jump to: navigation, search


Céu is a synchronous reactive language intended to offer a higher-level and safer alternative to C. Reactive programs are mostly guided by the environment, which issues input events into the program that must be promptly acknowledged.

Céu supports multiple lines of execution, known as trails, which are allowed to share variables in a safe and seamless way (e.g. no need for locks or semaphores). The synchronous concurrency model of Céu greatly diverges from conventional multithreading (e.g. pthreads) and the actor model (e.g. erlang): In Céu, trails execute synchronized in reaction to a single input event at a time, hence, it is impossible to have trails reacting to different events. The disciplined step-by-step execution in Céu enables a static analysis that guarantees at compile time that programs are deterministic.

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

Céu has a small memory footprint and is suitable for constrained embedded systems, such as Arduino and TinyOS.

Céu is free software.

For a gentle introduction about Céu, refer to the interactive tutorial and Videos.

See also the complete Syntax of Céu for further reference.


1 Lexical Rules

1.1 Keywords

Follows the list of reserved keywords in Céu:

   and            async          await          break          C              constant       continue
   
   deterministic  do             else           else/if        emit           end            event
   
   every          finally        FOREVER        if             input          int            loop
   
   nohold         not            nothing        null           or             output         par
   
   par/and        par/or         pause/if       pure           return         s8             s16
   
   s32            s64            sizeof         then           until          var            void
   
   with           u8             u16            u32            u64

1.2 Identifiers

Céu uses identifiers to refer to external events, internal events, variables, C symbols, and types:

   ID      ::= <a-z, A-Z, 0-9, _> +
   ID_ext  ::= ID    /* all in uppercase, not beginning with a digit */
   ID_int  ::= ID    /* beginning with a lowercase letter            */
   ID_var  ::= ID    /* beginning with a lowercase letter            */
   ID_c    ::= ID    /* beginning with an underscore                 */
   ID_type ::= ( ID_c | void | int
              | s8 | s16 | s32 | s64
              | u8 | u16 | u32 | u64 ) (`*´ *)

Examples:

   var int a;                    // `int´ is a type, `a´ is a variable
   await A;                      // `A´ is an external input event
   emit e;                       // `e´ is an internal event
   _printf("hello world!\n");    // `_printf´ is a C symbol

1.3 Literals

1.3.1 Integers

Céu supports integer values written in different bases and also as ASCII characters:

  • Decimals are written as is.
  • Octals are prefixed with 0.
  • Hexadecimals are prefixed with 0x.
  • ASCII characters and escape sequences are surrounded by apostrophes.

Examples:

   // all following are equal to the decimal 127
   v = 127;
   v = 0777;
   v = 0x7F;
   
  // newline ASCII character = decimal 10
   c = '\n';

1.3.2 Strings

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

Example:

   _printf("Hello World!\n");

1.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;
   */
   
   **/


2 Types

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

See also section #Declarations.


Examples:

   var u8 v;             /* `v´ is of 8-bit unsigned integer type */
   var _message_t msg;   /* `msg´ is of external C type `message_t´ */
   var _char* buf;       /* `buf´ is a pointer to external C type `char´ */

2.1 Native types

Céu supports the following native types:

   void               /* void type for signal-only events */
   int                /* platform dependent 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 */

2.2 C (external) types

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

The size of an external type must be explicitly declared.

Example:

   C _char = 1;      /* declares the external C type `_char´ of 1 byte */

2.3 Pointers

Any type, being it native or external, can be postfixed with `*´ to become a pointer to that type.


3 Statements

3.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.


3.1.1 do-end

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

   Do ::= do Block end

Example:

   var int i = 1;
   do
       var int i = 2;
       _printf("i = %d\n", i);   // prints 2
   end
   _printf("i = %d\n", i);       // prints 1

3.1.2 nothing

The statement nothing has no effect on the program.

They can be used to circumvent the requirement that blocks must contain at least one statement:

   loop do
       ...
       if cond then
           nothing;
       else
           break;
       end
   end


3.2 Declarations

3.2.1 Variables

The syntax for the definition of variables in Céu is as follows:

   Dcl_var ::= var ID_type (`[´ NUM `]´)? ID_var (`=´ SetExp)? ( `,´ ID_var (`=´ SetExp)? )*

A variable must have an associated type and can be optionally initialized.

Variables are only visible inside the block they are defined.

Céu supports one-dimensional vectors, which are declared by suffixing the variable type (instead of its name) with the vector length surrounded by `[´ and `]´. The first index of a vector is zero.

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

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

3.2.2 Events

3.2.2.1 External events

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

External input events are used for interacting with input devices, such as mouse, sensors, etc.

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

The declaration of an input event is as follows:

   Dcl_ext ::= input ID_type ID_ext (`,´ ID_ext)*

External output events are used for interacting with output devices, such as leds, motors, etc.

The declaration of an output event is as follows:

   Dcl_ext ::= output ID_type ID_ext (`,´ ID_ext)*

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. serial interfaces, radio transceivers), the underlying platform must provide different events for each functionality.

The type on the declarations refer to the value the declared event transports between the environment and the application (and vice-versa).

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 MyEvent;    // `MyEvent´ is an input event that carry no values (i.e., it is a signal-only event)
   output int  A,B;       // `A´ and `B´ are integer output events

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

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

3.2.2.2 Internal events

Internal events are used as a communication mechanism among trails through the await and emit statements.

The declaration of an internal event is as follows:


   Dcl_int ::= event ID_type ID_int (`,´ ID_int)*

In contrast with external events, an input event is always used 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.

3.2.3 C (external)

Any existing type, variable, or function in C can be predeclared to be available for use in Céu programs:

   Dcl_c   ::= C (pure|constant|nohold)? C_list
   C_list  ::= (C_type|C_func|C_var) (`,´ (C_type|C_func|C_var))*
   C_type  ::= ID_c `=´ NUM
   C_func  ::= ID_c `(´ `)´
   C_var   ::= ID_c

A declaration is a list of C symbols, all prefixed with `_´, referring to available static globals in the underlying C platform.

A type must include its size in bytes. Functions and variables are distinguished by the `()´ that follows function declarations.

After being declared, the symbols can be used in the program (keeping the prefix `_´).

An "opaque type" can be declared as having zero bytes. An opaque type is never instantiated, but always used as a pointer to be manipulated by external functions

Given that the Céu compiler has no information about external symbols, by default, C functions are considered to be impure (i.e. performs side-effects), and C variables to point to any memory location. For this reason, concurrent accesses to external symbols are always considered non-deterministic.

To change the default behavior, a C declaration may be prefixed with an annotation:

  • constant states that the declared variables are actually constants (e.g. a #define).
  • pure states that the declared functions have no side effects.
  • nohold states that the declared functions do not hold references to pointers passed as parameters.

Annotations are discussed in more depth in sections do-end and TODO(determinism).

Examples:

   C constant _NULL;                  /* `_NULL´ is a constant */
   C _char=1, _FILE=0;                /* `_char´ is a 1-byte type, while `_FILE´ is "opaque" */
   C pure     _abs(), _pow();         /* `_abs´ and `_pow´ are pure functions */
   C nohold   _fprintf(), _sprintf(); /* these functions receive pointers but do not hold references to them */


Céu also supports C blocks to define new global types, variables, and functions using the C syntax:

   C_block ::= C do <code_with_C_syntax> end


Example:

   C _assert(), _inc();
   C do
       #include <assert.h>
       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 C block must be delimited with a custom multi-line comment to avoid confusing the parser:

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

3.2.4 Deterministic annotations

A C function or variable (either from Céu or C) may be declared as deterministic with a set of other functions or variables:

   Dcl_det ::= deterministic ID with ID (`,´ ID)*

Example:

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

Deterministic annotations are discussed in more depth in section TODO(determinism).


3.3 Assignments

Céu supports three kinds of assignments:

   Set ::= Exp `=´ ( Exp                /* simple assignment */
                   | <await_stmt>       /* await  assignment */
                   | <block_stmt> )     /* block  assignment */

The expression on the left side must be assignable.

3.3.1 Simple assignment

The simpler form of assignment uses expressions as values.

Example:

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

3.3.2 Await assignment

Céu also supports assignments from await statements, as the section #Await statements shows.

3.3.3 Block assignment

A whole block can be used as an assignment value by returning from it.

Example:

  // assigns 1 to the variable `a´
  var int a =
      do
          return 1;
      end

Every possible path inside the block is supposed to reach a return statement whose expression becomes the final value of the assignment. Otherwise, the block halts and never terminates.

The following statements can be used in block assignments: if-then-else, loop, every, par, do-end, and async.

3.3.3.1 return

A return statement is used to escape the deepest block being assigned to a variable. The return value is then assigned to the respective variable.

Example:

   a = loop do              // a=1, when `cond´ is satisfied
           await cond;
           if cond then
               return 1;    // `loop´ is the deepest assignment block
           end
           ...
       end

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

Therefore, a program such as

   return 1;

should read as

   var int $ret =
       do
           return 1;
       end;


3.4 C calls

Céu has no support for function definitions, but functions defined in C can be called from Céu:

   Call ::=  Exp `(´ ExpList `)´

Expressions that evaluate to C functions can also be called.

Examples:

   _printf("Hello World!\n");
   ptr:f();


3.5 Event manipulation

The fundamental concept in Céu, accounting for its reactive nature, is that of events.

Events are manipulated through the await and emit statements.

Waiting for an event halts the running trail until that event occurs.

The occurrence of an event is broadcast to all awaiting trails at the same time.

3.5.1 Await statements

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

   Await ::= (Exp `=´)? ( await (ID_ext|ID_int)
                        | await (WCLOCKK|WCLOCKE)
                        ) (`until´ Exp)?
         |  await FOREVER

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
   
   var int x = await B;        // `x´ is set to the value carries by `B´
   x = await B until x > 10;   // only awakes when x > 10

An optional assignment captures the value the await evaluates to.

The optional clause until Exp keeps the trail sleeping until the tested expression is true.

3.5.1.1 Await event

For await statements referring to an event identifier, the running trail halts until that event occurs. When the event occurs, the statement resumes and evaluates to the triggered value.

   input int A;
   var int v = await A;       // `v´ is assigned the value of next occurrence of `A´

3.5.1.2 Await wall-clock

For await statements referring to wall-clock time (i.e., time measured in minutes, milliseconds, etc.), the running trail halts until the referred time elapses.

WCLOCKK and WCLOCKE are described as follows:

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

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

After the referred time elapses, the await statement 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.

Refer to the section #Environment for information about storage types for wall-clock time.

3.5.1.3 Await FOREVER

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

3.5.2 Emit statements

The emit statement triggers the referred wall-clock time, external event, or internal event, awaking all trails waiting for that event (time).

   Emit ::= emit ID_ext (`(´ ExpList? `)´)?
         |  emit ID_int (`=´ Exp)?
         |  emit (WCLOCKK|WCLOCKE)

3.5.2.1 Emit event

  • External events:
For external events, the call syntax is obligatory if the emitted event requires parameters.
An emit on an output event returns immediately a status code for the action that will run asynchronously with the program.
Both the required parameters, status code, and asynchronous action are platform dependent. The status code is always of type int.
Example:
   output int Send;
   if not emit Send(1) then
      return 0;
   end
Input events can only be emitted inside asyncs for the simulation of programs.
  • Internal events:
For internal events, the assignment expression is obligatory for non-void events.
The emit on an internal event suspends and resumes only after all the corresponding awaiting statements react to it. See also section #Execution model for a precise description.
Internal emits have no return status and cannot be used as expressions.

3.5.2.2 Emit time

The WCLOCKK and WCLOCKE parameters refer to wall-clock time, as described in section #Await statements.

Just like for input events, wall-clock time can only be emitted inside asyncs.


3.6 Flow control

3.6.1 if–then–else

Céu provides an if–then–else statement as follows:

   If ::= if Exp then
              Block
          (else/if Exp then
              Block)*
          (else
              Block)?
          end

The if–then–else statement executes the block following then if the condition expression evaluates to a non-zero value. Otherwise, it retries the process with the (optional) else/if alternatives. Finally, it they all fail, the block following the (optional) else is executed.

Note: the condition is not required to be surrounded by parenthesis.

3.6.2 loop

The loop statement continuously executes the block on its body until it reaches its specified limit or a break statement.

   Loop ::= loop (ID_var (`,´ Exp)?)? do Block end

The optional variable is automatically declared and initialized with zero. Its visibility is restricted to the loop body. The variable is read-only, but is automatically incremented after each iteration of the loop.

The optional limiting expression is evaluated once, before the loop starts. If no limiting expression is specified, the loop runs forever.

Céu requires that each possible path in a loop body contains at least one await or break statement, thus ensuring that loops never run in unbounded time. A non-awaiting loop (i.e. a tight loop) raises a warning at compile time.

3.6.2.1 every

The every statement executes its body for every occurrence of the referred event.

   Every ::= every (Exp `=´)? (WCLOCKK|WCLOCKE|ID_ext|ID_int) do Block end

The optional expression evaluates to the value of the occurring event.

The block cannot contain await statements.

The every statement is actually a syntactic sugar that expands to a loop.

As an example, the two code snippets are equivalent:

   every 1s do
       _printf("Hello world!\n");
   end

and

   loop do
       await 1s;
       _printf("Hello world!\n");
   end

3.6.2.2 break

The break statement escapes the innermost enclosing loop or every statement.

Example:

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


3.7 Parallel statements

The parallel statements par/and, par/or, and par split the running trail in multiple others. They differ only on how trails rejoin in the future.

See also section #Execution model for a detailed description of parallel execution.

3.7.1 par/and

The par/and statement stands for parallel-and, meaning that the trails in parallel rejoin only after all of them terminate.

   ParAnd ::= par/and do
                  Block
             (with
                  Block)+
              end

3.7.2 par/or

The par/or statement stands for parallel-or, meaning that the trails in parallel rejoin after any of them terminate.

   ParOr ::= par/or do
                 Block
            (with
                 Block)+
             end

3.7.3 par

The par statement never rejoins and should be used when the trails in parallel are supposed to run forever (e.g. a loop without break).

   Par ::= par do
               Block
          (with
               Block)+
           end


3.8 pause/if

TODO


3.9 Asynchronous blocks

Asynchronous blocks (asyncs) permit that programs in Céu execute time consuming computations without interfering with reactions to input events (referred to as the synchronous side of applications). The syntax for asyncs is as follows:

   Async   ::= async ( `(´ VarList `)´ )? do Block end
   VarList ::= ID_var (`,´ ID_var)*

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 can be used to copy values from the current scope to be used inside the async body. Each identifier that appears on the list is automatically declared as new variable in the async body and initialized with the value of the variable with the same name in the current scope.

The following example executes a long computation inside an async in order to keep the program reactive. In a parallel trail, the program awaits one second to kill the computation if it takes too long:

  var int fat;
  var int Value;
  par/or do
      var int v = await Value;
  
      // calculates the factorial of v
      fat = async (v) do
          var int fat = 1;
          loop i, 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;

The following restrictions are imposed to asyncs:

  • Asyncs only execute when there are no pending reactions to input events.
  • Asyncs are suspended whenever a new input event occurs.
  • Asyncs cannot use parallel compositions.
  • Asyncs cannot nest other asyncs.
  • Asyncs cannot await events.
  • Asyncs cannot emit internal events.
  • Asyncs do not share memory with the synchronous side.

A lower priority for asyncs is fundamental to ensure that input events are handled as fast as possible.

3.9.1 Simulation

As asyncs run detached from the synchronous side, they are allowed to trigger input events and the passage of time, providing a way to test programs in the own language:

  input int A;
  
  // tests a program with a simulation in parallel
  par do
  
      // original program
      var int v = await A;
      loop do
          await 10ms;
          _printf("v = %d\n", v);
          v = v + 1;
      end
  
  with
  
      // input simulation
      async do
          emit A(0);      // initial value for `v'
          emit 1s35ms;    // the loop executes 103 times
      end
      return 0;
  end
  
  // (try online!)

Whenever an async emits an event, it is suspeded due to its low priority compared to synchronous code. The example prints the message exactly 103 times.


4 Expressions

Besides constants, variables, C symbols, output emits, and function calls, Céu supports a wide range of expressions.

Most operators and expressions in Céu follow the same semantics of C.

Note: assignments are not expressions in Céu.

4.1 Arithmetic

The arithmetic operators of Céu are

   +      -      %      *      /      +      -

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

4.2 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).

4.3 Logical

The logical operators of Céu are

   not      and      or

which correspond to not, and, or.

4.4 Bitwise

The bitwise operators of Céu are

   ~      &      |      ^      <<      >>

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

4.5 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.

4.6 Pointers

The operator `*´ dereferences its pointer operand, while the operator `&´ returns a pointer to its operand:

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

The operand to `&´ must be an assignable expression.

4.7 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:

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

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

4.8 Type casting

Céu uses angle brackets for type casting:

  Cast ::= `<´ ID_type `>´

4.9 Sizeof

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

   Sizeof ::= `sizeof´ `<´ ID_type `>´

The expression is evaluated at compile time.

Note: Céu has no support for evaluating the size of expressions.

4.10 Precedence

Céu follows the same precedence of C operators:

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

4.11 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 assignments and 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;


5 Execution model

TODO



6 Organisms

TODO


7 Environment

As a reactive language, Céu depends on an external environment (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, and an implementation may use a polling or interrupt-driven notification mechanism.

The Céu compiler generates a C output with hooks following a standard interface that the target platform should comply.

7.1 C API

The following sections specify the available mechanisms of interaction between the environment and the Céu runtime.

7.1.1 Functions

The following functions should be called by the environment to command the execution of Céu programs:

  • void ceu_go_init ()
Initializes and starts the program.
Should be called by the environment once, to start the Céu program.
  • void ceu_go_event (int id, void* data)
Signals the occurrence of the given event to the Céu runtime.
The first parameter is the identifier of the input event (see constants).
The second parameter is a pointer to the event data, or the actual value if the event is of type int. (TODO: is this portable?)
The program resumes on the trails awaiting the event, whose awaiting expressions return the received data.
  • void ceu_go_async ()
Executes a suspended asynchronous block for a time slice.
The time slice is not specified.
However, the execution is guaranteed to suspend (returning from ceu_go_async) if a periodic call to ceu_out_pending() evaluates to true (see macros).
  • void ceu_go_wclock (s32 dt)
Notifies the Céu runtime about the elapsed wall-clock time.
The function receives the elapsed time in microseconds as dt.
The program resumes on the trails whose wall-clock awaits have expired.

Other functions:

  • int ceu_go_all ()
Combines ceu_go_init with an infinite loop that continuously calls ceu_go_async.
Expects that all events are generated from asynchronous blocks (see simulation).

Note: all functions must execute atomically with respect to each other.

7.1.2 Macros

The following macros can be optionally defined by the environment to customize the behavior of the Céu runtime:

  • ceu_out_pending()
Called to probes the environment for the existence of pending input events.
The expected return value is 1 (true) or 0 (false).
This macro is called from the Céu runtime to interrupt the execution of asynchronous blocks when the environment has a pending input event.
By default ceu_out_pending() translates to (1), meaning that asynchronous blocks execute the least possible time slice before giving control back to the environment.
  • ceu_out_wclock(us)
Called to notify the environment that the next wall-clock await will expire in us microseconds.
This macro must be defined in interrupt-based platforms to remind the environment to call ceu_go_wclock.
The special value CEU_WCLOCK_INACTIVE represents the absence of wall-clock awaits.
Note: us can be sometimes less or equal to zero.
By default this macro is not defined, hence, the environment should call ceu_go_wclock periodically.
  • ceu_out_event(id, len, data)
Called whenever the program emits an output event.
This macro specifies the side effects associated with all output events.
The returned integer becomes the result of the emit in the program.
id is a number that identifies the event (see constants); len is the size of the event type; data is a pointer to the value emitted, or the actual value if the event is of type int. (TODO: is this portable?)
  • ceu_out_event_XXX(data)
Per-event version of ceu_out_event(id,len,data).
XXX is the name of the event.
If this macro is defined for a given event, only this macro is called on the occurrence of that event.

7.1.3 Constants & Defines

The following constants and defines are generated by the Céu compiler to be used by the environment.

  • CEU_WCLOCKS, defined if the program uses wall-clock awaits.
The environment can conditionally compile code for calling system timers and ceu_go_wclock.
  • CEU_ASYNCS, defined if the program uses asynchronous blocks.
The environment can conditionally compile code for calling ceu_go_async.
  • CEU_FUNC_XXX, defined for each called C function.
XXX is the name of the function (e.g. CEU_FUNC_printf).
  • CEU_IN_XXX, defined for each input event.
Every external input event has a unique identifier associated with a number.
The identifier is the name of the event prefixed with CEU_IN_ (e.g. CEU_IN_Button).
  • CEU_OUT_XXX, defined for each input event.
Every external output event has a unique identifier associated with a number.
The identifier is the name of the event prefixed with CEU_OUT_ (e.g. CEU_IN_Led).
As an example, consider the following Céu definitions:
   input int A, B;
   output void C, D;
The compiler may generate the following constants for them:
   #define CEU_IN_A    0;
   #define CEU_IN_B    5;
   #define CEU_OUT_C   0;
   #define CEU_OUT_D   1;
Two input events never have the same associated value. The same holds for output events.
The values between input and output events are unrelated.
Also, the values need not to be continuous.

7.1.4 Types

Céu expects the following scalar types to be defined by the environment: s8, s16, s32, u8, u16, and u32. They correspond to signed and unsigned variations of the referred sizes in bits.

Follows an example of possible definitions for the scalar types:

   typedef long       s32;
   typedef short      s16;
   typedef char       s8;
   typedef unsigned long      u32;
   typedef unsigned short     u16;
   typedef unsigned char      u8;

These types are used internally by the language runtime, and can also be used by programmers in Céu programs. For instance, Céu internally uses a s32 type to represent wall-clock time.

7.2 Compiler

Céu provides a command line source-to-source 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 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.