Céu is a language for structured synchronous reactive programming. Céu is a project of LabLua, the same lab that developed the elegant and tiny Lua language. Lua means "moon" in Portuguese, and Céu means "sky".
Just a few days ago (March 18, 2018) a new version of Céu was released, version 0.30. I had used earlier versions successfully for embedded programming, so I thought I would try to see what the new version looks and feels like.
I started from a Ubuntu 14.04 LTS image, and cloned the Céu repository:
git clone https://github.com/fsantanna/ceu.git # git was not installed
sudo apt-get install git # okay, git is now installed
git clone https://github.com/fsantanna/ceu.git
Then I built it, which meant installing make, lua, and libreadline.
ls
cd ceu/
ls
make
sudo apt-get install make
make
sudo apt-get install lua # lua comes in several versons on Ubuntu
sudo apt-get install lua-5.3 # lua-5.3 is not available by default in Ubuntu 14.04
cd ~
curl -R -O http://www.lua.org/ftp/lua-5.3.4.tar.gz # just get the source
tar zxf lua-5.3.4.tar.gz
cd lua-5.3.4
make linux test
sudo apt-get install gcc
make linux test
apt-cache search readline
sudo apt-get install libreadline libreadline-dev
sudo apt-get install libreadline6 libreadline6-dev
make linux test
echo 'PATH=$PATH:~/lua-5.3.4/src' >> ~/.bashrc
lua
source ~/.bashrc
lua
cd ~/ceu
make
cd ~/lua-5.3.4/src/
ln -s lua lua5.3
ls
ls -la
cd ~/ceu
make
(After I got this working, I realized that I probably didn't need lua-5.3 specifically, and I could have installed an earlier version of lua using apt-get. Oh well.)
Next I put the ceu
executable on my PATH and tried to write some Céu code.
echo 'PATH=$PATH:~/ceu/src/lua' >> ~/.bashrc
emacs hello.ceu
This the Céu code that I eventually ended up with:
native/nohold _printf;
native/pre do
##include <stdio.h> // include the relevant header for "printf"
end
input int I;
output int O;
// _printf("Boot reaction\n");
// This is probably bad style - leaning on the order of execution to communicate "side band" information from one trail to another.
var bool unhandled = true;
par/or do
loop do
await I;
// _printf("Control flow always reaches here immediately after an I event.\n");
unhandled = true;
end
with
par/and do
var int a;
loop do
a = await I;
if a % 3 == 0 then
break;
end
end
// _printf("Control flow only reaches here after an I event containing a multiple of 3.\n");
emit O(-1);
unhandled = false;
with
var int b;
loop do
b = await I;
if b % 5 == 0 then
break;
end
end
// _printf("Control flow only reaches here after an I event containing a multiple of 5.\n");
emit O(-2);
unhandled = false;
end
with
loop do
var int c;
loop do
c = await I;
if unhandled then
break;
end
end
// _printf("Control flow only reaches here after an I event was not handled by the 3 or 5 previous trails.\n");
emit O(c);
end
with
loop do
await I;
// _printf("Control flow reaches here after an I event.\n");
emit O(-3);
end
end
emit O(-3);
escape 0;
It's a variant on FizzBuzz. Céu programs are always embedded in an environment, which sends events into the program
(here, just the I
event), and implements events that the Céu program sends in response. Here, I
events, which have
integer payloads, are sent into the program, and the program sends back O
events, which also have integer payloads.
One of the primary syntactic elements of Céu programming is par
-
which comes in three flavors, par
, par/and
, and par/or
.
The par/and
runs multiple blocks in parallel, and the entire par/and
block is over when each of the subblocks have finished.
This is similar to &
in Cardelli and Pike's Squeak language, or ||
in Esterel. (BTW, both of those languages are now dead - I don't consider them viable competitors to Céu.)
In contrast par/or
runs only until one of the subblocks has finished; the others are terminated.
The par
construct never finishes, regardless of what its subblocks do, which is more useful than it seems.
Now that we have some Céu code, the executable can compile it into C code. I had to install lpeg in order to run the compiler.
ceu --ceu --ceu-input=hello.ceu
apt-cache search lpeg
sudo apt-get install lua-lpeg
ceu --ceu --ceu-input=hello.ceu
cd ~
wget http://www.inf.puc-rio.br/~roberto/lpeg/lpeg-1.0.1.tar.gz
tar -xvzf lpeg-1.0.1.tar.gz
cd lpeg-1.0.1/
make
emacs makefile
make LUADIR=~/lua-5.3.4/src
cd ~/learn_ceu_030/
cp ~/lpeg-1.0.1/lpeg.so .
ceu --ceu --ceu-input=hello.ceu
The next step is to write an environment. The environment is a piece of C code that plumbs the input events in (and the output events out of) the Céu code. You may be able to use an environment someone else wrote, like an "Arduino environment" (if you have an Arduino) or a "libuv environment" (if you want to interact with the world like node.js interacts with the world), or a "SDL environment" (if you want to play with graphics). Almost any serious use of Céu is going to involve writing a custom environment, and writing a little custom environment is really the best way to learn how Céu works.
The environment comprises two files, types.h
, which you may not ever need to modify, and main.c
, which has all the good stuff. This is the main.c
that I ended up with:
// Johnicholas mostly just copied this out of the Ceu manual.
#include "types.h" // as illustrated above in "Types"
#include <stdio.h>
int ceu_is_running; // detects program termination
// The signature is slightly different if you are compiling without trace turned on.
// The manual has a ceu_callback_main with trace turned on, a four-argument function.
// We want a ceu_callback_main without trace turned on, a three-argument function.
int ceu_callback_main (int cmd, tceu_callback_val p1, tceu_callback_val p2)
{
int is_handled = 0;
switch (cmd) {
case CEU_CALLBACK_TERMINATING:
ceu_is_running = 0;
is_handled = 1;
break;
case CEU_CALLBACK_OUTPUT:
if (p1.num == CEU_OUTPUT_O) {
int val = *((int*)p2.ptr);
if (val == -1) {
printf("Fizz");
} else if (val == -2) {
printf("Buzz");
} else if (val == -3) {
printf("\n");
} else {
printf("%d", val);
}
is_handled = 1;
}
break;
}
// The meaning of a ceu callback return code is "I handled this".
// It is used in order to have a bunch of callbacks each specializing in a different
// subset of output events. Then the Ceu runtime, in order to send a particular output event,
// will walk through the callbacks up until one of them says "I handled this".
// In this case, this is the only callback function, and so in some sense it doesn't
// matter what we return? But returning "is_handled" works for more complicated cases,
// too.
return is_handled;
}
int main (int argc, char* argv[])
{
ceu_is_running = 1;
tceu_callback cb = { &ceu_callback_main, NULL };
ceu_start(&cb, argc, argv);
while (ceu_is_running) {
int v;
if (scanf("%d", &v) == 1) {
ceu_input(CEU_INPUT_I, &v);
}
ceu_input(CEU_INPUT__ASYNC, NULL);
}
ceu_stop();
}
Basically, control flow enters main, and after a little setup, settles into an event loop, poll()
ing or select()
ing or whatever, and sending those events into the hello.ceu code with ceu_input()
. In response to each of these events,
the hello.ceu code calls the callback zero or more times, indicating what output events it wants to send, and the callback interprets the output event codes and payloads, routing them appropriately.
The ceu
executable can combine the three files, the one generated previously from the .ceu code, and the two environment files, to make a .c file.
ceu --env --env-ceu=hello.c --env-types=types.h --env-main=main.c --output=hello_plus_env.c
Finally, we can use gcc to compile the hello_plus_env code into an executable that we can run.
gcc -o hello hello_plus_env.c
This version of FizzBuzz reads numbers that the user types on standard in, and repeats them back to the user. But it responds to the first multiple of 3 that the user types by saying "Fizz", and responds to the first multiple of 5 that the user types by saying "Buzz". Once it has responded both "Fizz" and "Buzz", it shuts down; that's because a par/and
was used to combine the Fizz block and the Buzz block.
One flaw in this code is that the environment is clearly switching on whether the payload is -1, -2, -3, or some other number.
This kind of magic number is not good; it is a code smell. Ideally, they would instead be their own output events, with "none" payload. Maybe you, dear reader, would like to modify the environment to instead have FIZZ
, BUZZ
, NEWLINE
output events - if that sounds interesting, fork this.
I have not even started to touch on Céu's more sophisticated and interesting features, nor the interesting new features in v0.30, nor on how to write a C++ class that delegates to Céu (which I think is one of the most interesting ways to integrate Céu with a large existing C++ codebase). I hope to continue blogging about how to use Céu effectively, though. Please contact me, or the ceu-lang group, if you would like to talk about it!
Pages that I opened during this project:
- Inbox – johnicholas.hines@gmail.com
- The world's leading software development platform · GitHub
- Agile Games 2018 - Mob Programming Conference
- Céu: The Programming Language
- fsantanna/ceu: The Programming Language Céu
- ceu-v0.30.pdf
- Aloan - One Dance For Destiny - YouTube
- Compute Engine - learn ceu 030
- Cloud Shell - learn ceu 030
- install lua5.3 ubuntu14.04 - Google Search
- software installation - Installing lua5.2 vs. lua5.3 on Ubuntu 16.10 - Ask Ubuntu
- lua-5.3 install ubuntu - Google Search
- Lua: download
- install libreadline-dev - Google Search
- bashrc vs profile - Google Search
- bash - How to correctly add a path to PATH? - Unix & Linux Stack Exchange
- how many teats does a giraffe have - Google Search
- emacs exit - Google Search
- LPeg - Parsing Expression Grammars For Lua
- install library ubuntu - Google Search
- emacs indent-rigidly arrow keys - Google Search
- EmacsWiki: Indenting Text
- Rectangles - GNU Emacs Manual
- capitalization - Is the convention for naming make files to use a capital 'm', such as Makefile? - Stack Overflow
- What scanf function in c returns? - Stack Overflow
- src refspec doesn't match any - Google Search
- repository - Git error: src refspec master does not match any - Stack Overflow
- Céu: The Programming Language
- The world's leading software development platform · GitHub