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" vector byte buf; // "buf" is a vector with at most 9 values of type "byte" input void&& A; // "A" is an input event that carries values of type "void&&" event bool e; // "e" is an internal event that carries values of type "bool" pool Anim anims; // "anims" is a dynamic "pool" for 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 elements of the same type.
vector byte buf = [1,2,3]; // write access buf[$buf+1] = 4; // write access escape buf; // read access (yields 2)
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: void is a valid type for signal-only events.
input void 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 sensor, button, mouse, etc.
- output events represent output devices such as 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 (void) => void do // defines the "Anim" code abstraction <...> // body of "Anim" end pool Anim ms; // declares an unlimited container for "Anim" instances loop i in [0->10[ do spawn Anim() in ms; // creates 10 instances of "Anim" into "ms" end
When a pool declaration goes out of scope, all running code abstractions are automatically aborted.
Locations appear in assignments, event manipulation, iterators, and expressions.
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 (which are storage entities)
- vector index
- vector length
- pointer dereferencing
- option unwrapping
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 variable in "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 remain or become unassigned.
An alias is declared by suffixing the entity class with the modifier
& and is acquired by prefixing an entity 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 a variable
The alias is acquired by prefixing the associated
native call with the operator
&. Since the allocation may fail, the alias may remain unassigned.
- Track the lifetime of a variable.
The alias is acquired by prefixing the associated variable with
&. Since the tracked variable may go out of scope, the alias may become unassigned.
Accesses to option variable aliases must always use option checking or unwrapping.
var&? _FILE f = &_fopen(<...>) finalize with _fclose(f); end; if f? then <...> // "f" is assigned else <...> // "f" is not assigned end
var&? int x; do var int y = 10; x = &y; _printf("%d\n", x!); // prints 10 end _printf("%d\n", x!); // error!
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.