Storage entities represent all objects that are stored in memory during execution. Céu supports variables, vectors, events (external and internal), and pools as entity classes.
var int v; // "v" is a variable of type "int" var byte buf; // "buf" is a vector with at most 9 values of type "byte" input none&& A; // "A" is an input event that carries values of type "none&&" event bool e; // "e" is an internal event that carries values of type "bool" pool Anim anims; // "anims" is a dynamic "pool" of instances of type "Anim"
A declaration binds the identifier with a memory location that holds values of the associated type.
Storage entities have lexical scope, i.e., they are visible only in the block in which they are declared.
The lifetime of entities, which is the period between allocation and deallocation in memory, is also limited to the scope of the enclosing block. However, individual elements inside vector and pool entities have dynamic lifetime, but which never outlive the scope of the declaration.
A variable in Céu holds a value of a declared type that may vary during program execution. The value of a variable can be read in expressions or written in assignments. The current value of a variable is preserved until the next assignment, during its whole lifetime.
var int v = _; // empty initializaton par/and do v = 1; // write access with v = 2; // write access end escape v; // read access (yields 2)
A vector in Céu is a dynamic and contiguous collection of variables of the same type.
var byte buf = [1,2,3]; // write access buf = buf .. ; // write access escape buf; // read access (yields 2)
TODO: ring buffers
Events account for the reactive nature of Céu.
Programs manipulate events through the
await halts the running trail until the specified event occurs.
An event occurrence is broadcast to the whole program and awakes trails
awaiting that event to resume execution.
Unlike all other entity classes, the value of an event is ephemeral and does
not persist after a reaction terminates.
For this reason, an event identifier is not a variable: values can only
be communicated through
A declaration includes the type of value the occurring
Note: none is a valid type for signal-only events with no associated values.
input none I; // "I" is an input event that carries no values output int O; // "O" is an output event that carries values of type "int" event int e; // "e" is an internal event that carries values of type "int" par/and do await I; // awakes when "I" occurs emit e(10); // broadcasts "e" passing 10, awakes the "await" below with var int v = await e; // awaits "e" assigning the received value to "v" emit O(v); // emits "O" back to the environment passing "v" end
As described in Internal Reactions, Céu supports external and internal events with different behavior.
External events are used as interfaces between programs and devices from the real world:
- input events represent input devices such as a sensor, button, mouse, etc.
- output events represent output devices such as a LED, motor, screen, etc.
The availability of external events depends on the environment in use.
emit output events and
await input events.
Internal events, unlike external events, do not represent real devices and are defined by the programmer. Internal events serve as signalling and communication mechanisms among trails in a program.
await internal events.
A pool is a dynamic container to hold running code abstractions.
A pool declaration specifies the type of the
abstraction and maximum number of concurrent instances (possibly unlimited).
Individual elements of pools can only be accessed through
New elements are created with
spawn and are
removed automatically when the code execution terminates.
code/await Anim (none) => none do // defines the "Anim" code abstraction <...> // body of "Anim" end pool Anim as; // declares an unlimited container for "Anim" instances loop i in [1->10] do spawn Anim() in as; // creates 10 instances of "Anim" into "as" end
When a pool declaration goes out of scope, all running code abstractions are automatically aborted.
A location (aka l-value) is a path to a memory position holding a value.
The list that follows summarizes all valid locations:
- storage entity: variable, vector, internal event (but not external), or pool
- native expression or symbol
- data field
- vector index
- vector length
- pointer dereferencing
- option unwrapping
Locations appear in assignments, event manipulation, iterators, and expressions. Locations are detailed in Locations and Expressions.
emit e(1); // "e" is an internal event _UDR = 10; // "_UDR" is a native symbol person.age = 70; // "age" is a field of "person" vec = $vec; // "vec" is a vector index $vec = 1; // "$vec" is a vector length *ptr = 1; // "ptr" is a pointer to a variable a! = 1; // "a" is of an option type
Céu supports aliases and pointers as references to entities, aka strong and weak references, respectively.
An alias is an alternate view for an entity: after the entity and alias are bounded, they are indistinguishable.
A pointer is a value that is the address of an entity, providing indirect access to it.
As an analogy with a person's identity, a family nickname referring to a person is an alias; a job position referring to a person is a pointer.
Céu support aliases to all storage entity classes, except external events and pointer types. Céu also supports option variable aliases which are aliases that may be bounded or not.
An alias is declared by suffixing the entity class with the modifier
& and is acquired by prefixing an entity identifier with the operator
var int v = 0; var& int a = &v; // "a" is an alias to "v" ... a = 1; // "a" and "v" are indistinguishable _printf("%d\n", v); // prints 1
An option variable alias, declared as
var&?, serves two purposes:
- Map a native resource to Céu.
The alias is acquired by prefixing the associated
native call with the operator
&. Since the allocation may fail, the alias may remain unbounded.
- Hold the result of a
spawninvocation. Since the allocation may fail, the alias may remain unbounded.
Accesses to option variable aliases must always use option checking or unwrapping.
TODO: or implicit assert with & declarations
var&? _FILE f = &_fopen(<...>) finalize with _fclose(f); end; if f? then <...> // "f" is assigned else <...> // "f" is not assigned end
var&? My_Code my_code = spawn My_Code(); if my_code? then <...> // "spawn" succeeded else <...> // "spawn" failed end
A pointer is declared by suffixing the type with the modifier
&& and is acquired by prefixing an entity with the operator
Applying the operator
* to a pointer provides indirect access to its
var int v = 0; var int&& p = &&v; // "p" holds a pointer to "v" ... *p = 1; // "p" provides indirect access to "v" _printf("%d\n", v); // prints 1
The following restrictions apply to pointers in Céu:
- No support for pointers to events, vectors, or pools (only variables).
- A pointer is only accessible between its declaration and the next yielding statement.