Roco

Appeared in:
13 Oct 2007
Paradigm:
Typing discipline:
Versions and implementations (Collapse all | Expand all):
Programming language

Roco is an esoteric programming language based on the concept of coroutines.

Roco was created in 2007 by Lode Vandevenne, and his implementation remains a de facto standard (not to mention that it’s the only one available). Its specification is rather simple, and the language itself has no commercial value, so it hasn’t been standardized.

Roco was created to try out some variations of handling coroutines. It is Turing-complete, as Brainfuck can be implemented in it, and thus theoretically capable of executing any real-life task.

Coroutines are the main element of the language; they are named parts of the program which have neither input or output parameters, nor local variables. Each coroutine has its own instruction pointer which stores the current command. By default coroutines loop forever: once the instruction pointer has reached the end of the coroutine, it moves again to its start. When a coroutine is called, its execution starts not from the start, but from the last position of its instruction pointer; this means, for example, that the recursion can’t be implemented using a single coroutine calling itself.

The only data structure in the language is a heap which stores 32-bit integer variables. The variables can be referenced by their index within the heap, which in turn can be stored in a variable (this variable being a pointer).

Language Instructions

Here i and o denote input and output parameters. Input parameters can be either variables or constants, while output parameters can be only variables.

  • co X{} : define a coroutine named X; coroutine body is in brackets.

  • co X; : declare a coroutine named X; implementation must follow later within the same scope.

  • ro : the name of the root coroutine (doesn’t have to be defined explicitly).

  • yi X : yield to coroutine X, don’t change the coroutines stack.

  • ca X : call coroutine X: push its address to the coroutines stack and yield to X.

  • ac : inverse of ca: pop address from the coroutines stack and yield to this coroutine. If the stack is empty, program execution stops.

  • if i : if i is zero, skip next command.

  • set o i : store value of i in variable o.

  • eq/neq/gt/lt o i1 i2 : put 1 in variable o, if i1 equals/not equals/is greater/is less than i2, and put 0 otherwise.

  • inc/dec o : increment/decrement the value in o.

  • add/sub/mul/div/mod/and/or/xor o i1 i2 : execute corresponding operation on i1 and i2 and put the result to o (logic operations are done bitwise).

  • not o i : invert bits of i and put the result to o.

  • cout i : print a character by its ASCII-code.

  • cin o : read a character and put its ASCII-code to o.

  • iout i : print a number.

  • iin o : read a number and put it to o.

Elements of syntax:

Nestable comments /* */
Case-sensitivity yes (only lowercase allowed)
Variable identifier regexp [number] (no variable names)
Function identifier regexp [_a-z][_a-z0-9]*
Variable assignment set varname value
Deep equality eq o a b (put 0 or 1 to variable o)
Deep inequality neq o a b (put 0 or 1 to variable o)
Comparison gt lt
Function definition co functionName {}
Function call ca functionName
If - then if i (skip next instruction if i=0)

Examples:

Hello, World!:

Example for versions Roco 20071014

This code uses only main coroutine; it outputs the message character by character, using their ASCII codes, and stops.

cout 72
cout 101
cout 108
cout 108
cout 111
cout 44
cout 32
cout 87
cout 111
cout 114
cout 108
cout 100
cout 33
ac

Fibonacci numbers:

Example for versions Roco 20071014

This example uses iterative definition of Fibonacci numbers by saving them all in cells [2]..[17]. Cell [0] stores the index of the next number to be calculated, and cell [1] is used as temporary storage. Loops are implemented as coroutines, since by definition coroutines loop until another coroutine is called or execution is interrupted with ac command.

co calc{
/* break the loop when the counter is 2+16, since numbers start with cell 2 */
eq [1] [0] 18
if [1] ac

/* calculate next number and store it to [[0]]*/
sub [1] [0] 1
set [[0]] [[1]]
sub [1] [0] 2
add [[0]] [[0]] [[1]]

/* output */
iout [[0]]
cout 44
cout 32

/* increment counter */
add [0] [0] 1
}

/* initialize with first Fibonacci numbers */
set [0] 4
set [2] 1
set [3] 1

iout [2]
cout 44
cout 32
iout [3]
cout 44
cout 32

ca calc

cout 46
cout 46
cout 46
ac

Factorial:

Example for versions Roco 20071014

This example uses iterative definition of factorial. Cell [0] stores the current number, cell [1] is temporary, and cell [2] stores factorial of current number.

co calc{
/* break the loop when the counter is 17 - the number for which we don't need factorial */
eq [1] [0] 17
if [1] ac

/* output current factorial */
iout [0]
cout 33
cout 32
cout 61
cout 32
iout [2]
cout 13
cout 10

/* calculate next number and store it to [2]*/
add [0] [0] 1
mul [2] [2] [0]
}

/* initialize with 0! = 1 */
set [0] 0
set [2] 1

ca calc

ac

CamelCase:

Example for versions Roco 20071014

The example is commented in detail. Coroutine char reads characters from standard input one by one and checks whether they are letters. Coroutine letter is called for characters which turned out to be letters; it converts them into required case and prints them. Note that not command inverts all bits of the number, so it can’t be used to negate a logical value (which is stored as 0 or 1) — one has to subtract this value from 1.

/* [0] - current character 
   [1] - last character was space?
   rest are temporary variables (used within one iteration only)
*/

co letter{
/* coroutine to process the case of a known letter */
/* if it is uppercase, and last one was letter, change to lowercase */
sub [4] 1 [1]
and [5] [2] [4]
if [5]
    add [0] [0] 32
/* if it is lowercase, and last one was space, change to uppercase */
and [5] [3] [1]
if [5]
    sub [0] [0] 32
/* print the character */
cout [0]    
set [1] 0
ac
}

co char{
/* read next character to [0] */
cin [0]

/* break the loop when the next character is end-of-line (ASCII 10) */
eq [2] [0] 10
if [2] ac

/* check whether this character is a letter at all [2] - uppercase, [3] - lowercase, [4] - at all, [5]-[6] - temporary */
/* uppercase */
gt [5] [0] 64
lt [6] [0] 91
and [2] [5] [6]

/* lowercase */
gt [5] [0] 96
lt [6] [0] 123
and [3] [5] [6]

/* at all */
or [4] [2] [3]
sub [5] 1 [4]

/* if this is not a letter, ONLY change [1] */
if [5]
    set [1] 1
/* otherwise, call the coroutine to handle this */
if [4]
    ca letter
}

/* at the start mark that last character was space */
set [1] 1
ca char

ac