Compilation¶
The compiler converts an input program in Céu to an output in C, which is further embedded in an environment satisfying a C API, which is finally compiled to an executable:
Command Line¶
The single command ceu
is used for all compilation phases:
Usage: ceu [<options>] <file>...
Options:
--help display this help, then exit
--version display version information, then exit
--pre Preprocessor Phase: preprocess Céu into Céu
--pre-exe=FILE preprocessor executable
--pre-args=ARGS preprocessor arguments
--pre-input=FILE input file to compile (Céu source)
--pre-output=FILE output file to generate (Céu source)
--ceu Céu Phase: compiles Céu into C
--ceu-input=FILE input file to compile (Céu source)
--ceu-output=FILE output source file to generate (C source)
--ceu-line-directives=BOOL insert `#line´ directives in the C output
--ceu-features-lua=BOOL enable `lua´ support
--ceu-features-thread=BOOL enable `async/thread´ support
--ceu-features-isr=BOOL enable `async/isr´ support
--ceu-err-unused=OPT effect for unused identifier: error|warning|pass
--ceu-err-unused-native=OPT unused native identifier
--ceu-err-unused-code=OPT unused code identifier
--ceu-err-uninitialized=OPT effect for uninitialized variable: error|warning|pass
--env Environment Phase: packs all C files together
--env-types=FILE header file with type declarations (C source)
--env-threads=FILE header file with thread declarations (C source)
--env-ceu=FILE output file from Céu phase (C source)
--env-main=FILE source file with main function (C source)
--env-output=FILE output file to generate (C source)
--cc C Compiler Phase: compiles C into binary
--cc-exe=FILE C compiler executable
--cc-args=ARGS compiler arguments
--cc-input=FILE input file to compile (C source)
--cc-output=FILE output file to generate (binary)
All phases are optional. To enable a phase, the associated prefix must be enabled. If two consecutive phases are enabled, the output of the preceding and the input of the succeeding phases can be omitted.
Examples:
# Preprocess "user.ceu", and converts the output to "user.c"
$ ceu --pre --pre-input="user.ceu" --ceu --ceu-output="user.c"
# Packs "user.c", "types.h", and "main.c", compiling them to "app.out"
$ ceu --env --env-ceu=user.c --env-types=types.h --env-main=main.c \
--cc --cc-output=app.out
C API¶
The environment phase of the compiler packs the converted Céu program and additional files in the order as follows:
- type declarations (option
--env-types
) - thread declarations (option
--env-threads
, optional) - a callback prototype (fixed, see below)
- Céu program (option
--env-ceu
, auto generated) - main program (option
--env-main
)
The Céu program uses standardized types and calls, which must be previously
mapped from the host environment in steps 1-3
.
The main program depends on declarations from the Céu program.
Types¶
The type declarations must map the types of the host environment to all primitive types of Céu.
Example:
#include <stdint.h>
#include <sys/types.h>
typedef unsigned char bool;
typedef unsigned char byte;
typedef unsigned int uint;
typedef ssize_t ssize;
typedef size_t usize;
typedef int8_t s8;
typedef int16_t s16;
typedef int32_t s32;
typedef int64_t s64;
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef float f32;
typedef double f64;
Threads¶
If the user program uses threads and the option
--ceu-features-thread
is set, the host environment must provide declarations
for types and functions expected by Céu.
Example:
#include <pthread.h>
#include <unistd.h>
#define CEU_THREADS_T pthread_t
#define CEU_THREADS_MUTEX_T pthread_mutex_t
#define CEU_THREADS_CREATE(t,f,p) pthread_create(t,NULL,f,p)
#define CEU_THREADS_CANCEL(t) ceu_dbg_assert(pthread_cancel(t)==0)
#define CEU_THREADS_JOIN_TRY(t) 0
#define CEU_THREADS_JOIN(t) ceu_dbg_assert(pthread_join(t,NULL)==0)
#define CEU_THREADS_MUTEX_LOCK(m) ceu_dbg_assert(pthread_mutex_lock(m)==0)
#define CEU_THREADS_MUTEX_UNLOCK(m) ceu_dbg_assert(pthread_mutex_unlock(m)==0)
#define CEU_THREADS_SLEEP(us) usleep(us)
#define CEU_THREADS_PROTOTYPE(f,p) void* f (p)
#define CEU_THREADS_RETURN(v) return v
TODO: describe them
Céu¶
The converted program generates types and constants required by the main program.
External Events¶
For each external input and output event
<ID>
defined in Céu, the compiler generates corresponding declarations as
follows:
- An enumeration item
CEU_INPUT_<ID>
that univocally identifies the event. - A
define
macro_CEU_INPUT_<ID>_
. - A struct type
tceu_input_<ID>
with fields corresponding to the types in of the event payload.
Example:
Céu program:
input (int,u8&&) MY_EVT;
Converted program:
enum {
...
CEU_INPUT_MY_EVT,
...
};
#define _CEU_INPUT_MY_EVT_
typedef struct tceu_input_MY_EVT {
int _1;
u8* _2;
} tceu_input_MY_EVT;
Data¶
The global CEU_APP
of type tceu_app
holds all program memory and runtime
information:
typedef struct tceu_app {
bool end_ok; /* if the program terminated */
int end_val; /* final value of the program */
bool async_pending; /* if there is a pending "async" to execute */
...
tceu_code_mem_ROOT root; /* all Céu program memory */
} tceu_app;
static tceu_app CEU_APP;
The struct tceu_code_mem_ROOT
holds the whole memory of the Céu program.
The identifiers for global variables are preserved, making them directly
accessible.
Example:
var int x = 10;
typedef struct tceu_code_mem_ROOT {
...
int x;
} tceu_code_mem_ROOT;
Main¶
The main program provides the entry point for the host platform (i.e., the
main
function), implementing the event loop that senses the world and
notifies the Céu program about changes.
The main program interfaces with the Céu program in both directions:
- Through direct calls, in the direction
main -> Céu
, typically when new input is available. - Through callbacks, in the direction
Céu -> main
, typically when new output is available.
Calls¶
The functions that follow are called by the main program to command the execution of Céu programs:
-
void ceu_start (void)
Initializes and starts the program. Should be called once.
-
void ceu_stop (void)
Finalizes the program. Should be called once.
-
void ceu_input (tceu_nevt evt_id, void* evt_params)
Notifies the program about an input
evt_id
with a payloadevt_params
. Should be called whenever the event loop senses a change. The call toceu_input(CEU_INPUT__ASYNC, NULL)
makes asynchronous blocks to execute a step. -
int ceu_loop (void)
Implements a simple loop encapsulating
ceu_start
,ceu_input
, andceu_stop
. On each loop iteration, make aCEU_CALLBACK_STEP
callback and generates aCEU_INPUT__ASYNC
input. Should be called once. Returns the final value of the program.
Callbacks¶
The Céu program makes callbacks to the main program in specific situations:
tceu_callback_ret ceu_callback (int cmd, tceu_callback_arg p1, tceu_callback_arg p2);
enum {
CEU_CALLBACK_START, /* once in the beginning of `ceu_start` */
CEU_CALLBACK_STOP, /* once in the end of `ceu_stop` */
CEU_CALLBACK_STEP, /* on every iteration of `ceu_loop` */
CEU_CALLBACK_ABORT, /* whenever an error occurs */
CEU_CALLBACK_LOG, /* on error and debugging messages */
CEU_CALLBACK_TERMINATING, /* once after executing the last statement */
CEU_CALLBACK_ASYNC_PENDING, /* whenever there's a pending "async" block */
CEU_CALLBACK_THREAD_TERMINATING, /* whenever a thread terminates */
CEU_CALLBACK_ISR_ENABLE, /* whenever interrupts should be enabled/disabled */
CEU_CALLBACK_ISR_ATTACH, /* whenever an "async/isr" starts */
CEU_CALLBACK_ISR_DETACH, /* whenever an "async/isr" is aborted */
CEU_CALLBACK_ISR_EMIT, /* whenever an "async/isr" emits an innput */
CEU_CALLBACK_WCLOCK_MIN, /* whenever a next minimum timer is required */
CEU_CALLBACK_WCLOCK_DT, /* whenever the elapsed time is requested */
CEU_CALLBACK_OUTPUT, /* whenever an output is emitted */
CEU_CALLBACK_REALLOC, /* whenever memory is allocated/deallocated */
};
TODO: payloads
The main program must implement the ceu_callback
prototype above to handle
the enumerated commands.
Example¶
Suppose the environment supports the events that follow:
input int I;
output int O;
The main.c
implements an event loop to sense occurrences of I
and a
callback handler for occurrences of O
:
#include "types.h" // as illustrated above in "Types"
int ceu_is_running; // detects program termination
tceu_callback_ret ceu_callback (int cmd, tceu_callback_arg p1, tceu_callback_arg p2) {
tceu_callback_ret ret = { .is_handled=1 };
switch (cmd) {
case CEU_CALLBACK_TERMINATING:
ceu_is_running = 0;
break;
case CEU_CALLBACK_OUTPUT:
if (p1.num == CEU_OUTPUT_O) {
printf("output O has been emitted with %d\n", p2.num);
}
break;
default:
ret.is_handled = 0;
}
return ret;
}
int main (void) {
ceu_is_running = 1;
ceu_start();
while (ceu_is_running) {
if detects(CEU_INPUT_A) {
int v = <...>;
ceu_input(CEU_INPUT_A, &v);
}
ceu_input(CEU_INPUT__ASYNC, NULL);
}
ceu_stop();
}