C0 | Documentation
About Downloads Tutorial References Courses
C0 Tutorial C0 and other languages Other references

Basic Building Blocks

The basic building blocks of C0 program are types, expressions, functions, statements, contracts, and libraries. We first review them in general before going into more detail on each.

Types

C0 has a very stringent type system. Every variable has a unique type, every function takes arguments of fixed types and returns a result of a fixed type, and (with one exception) every expression of the language has a unique type. This greatly aids writing correct programs and reasoning about them in a sound way, two of the main design goals of C0.

The following are the basic types of C0:

  • int. This is the basic numeric type, with integers in the range from -231 to 231-1. See Ints.
  • bool. This is the type of booleans, containing only truth values true and false. They are used pervasively in conditionals, loops, and annotations. See Booleans.
  • char. This is the type of ASCII characters, written as 'c' for a character c. For details, see Characters.
  • string. This is the type of strings, which are enclosed in double quotes such as "this is a string!". See Strings.
  • t[] (arrays). For any type t, there is a type t[] for arrays whose elements have type t. Arrays must be explicitly allocated with a fixed size; array accesses (reads and writes) are checked to be in bounds. See Arrays.
  • t* (pointers). For any type t, there is a type t* for pointers to a value of type t. Pointers are either null or the result of explicit allocation. Dereferencing a pointer to obtain the stored value will explicitly check that the pointer is not null. See Pointers.
  • struct s (structs). We can declare structs (also known as records) to have multiple fields of arbitrary types. We write struct s for the type of structs with name s. Structs must be explicitly allocated. Functions do not pass structs directly, but only pointers to structs or arrays of structs. See Structs.

Expressions

Expressions in C0 have a unique type. They are evaluated to return a value of the given type; what happens with this value depends on where the expression occurs in a program. In the simplest scenario, we are in the coin interpreter so the value of an expression is simply printed back. For example:

% coin
Coin 0.2.6 "Penny" (r2087, Fri May 27 12:10:21 EDT 2011)
Type `#help' for help or `#quit' to exit.
--> 3+5;
8 (int)
--> 

Here, the first line % coin starts up coin from a shell. The expression we type is 3+5 which must be terminated by a semicolon ';', followed by enter or return. coin responds with the value of the expression (here 8) and gives its type in parentheses (here int). It then prompts the user with --> to indicate that it can now accept another expression. In the examples below, we will omit the invocation of coin from the shell and just start from the prompt.

As a second example we write a boolean expression.

--> 3 == 8;
false (bool)
--> 

Constants

Most types have some built-in constants. The value of a constant is the constant itself. We show a few examples; more can be found under the discussion of the individual types.

--> 42;
42 (int)
--> true;
true (bool)
--> "Hello World!";
"Hello World!" (string)
--> 

Variables

Variables in C0 must be declared explicitly with their type. They can be initialized at the declaration site, or separately by a later assignment. In the example below, we show the two ways variables can be declared and initialized.

--> int x = 42;
x is 42 (int)
--> int y;
--> y = x+1;
y is 43 (int)

Note that the line int y; does not print a value. It is a variable declaration and does not have a value as such. An assignment like y = x+1; is a statement and as such does not have a value either but a so-called effect. The effect here is to assign the value 43 to the variable y, so coin prints y is 43 to indicate this effect.

Notice the difference between the expression 3 == 8 earlier (which has value false and no effect) and the statement y = x+1; (which has no value and the effect to assign y the value 43).

Operators

Besides constants and variables, expressions are constructed with operators and function calls. Operators can be infix (such as addition + in x+1 or less-than < in x < 0) or prefix (such as negation ! in !(x < 5)). The particular menagerie of operators are listed with the types of arguments that they apply to.

Function calls

In addition to operators, C0 expressions can also call functions which are either defined in a C0 library or in a program. A function called g is called as g(e1,...,en), where e1 through en are expressions and the function takes n arguments.

Functions

A simple example of a function is the exponential function, which takes a base b and an exponent e as arguments and computes be. It uses the recurrence

  • b0 = 1
  • be = b * be-1 (for e > 0)

and applies when the exponent is positive (greater or equal to 0). In particular, we define 00 = 1. The definition of the function has to be in a file, here called exp.c0. A file can contain the definitions of multiple functions, although in the example we have only one.

/* file exp.c0 */
int exp(int b, int e)
//@requires e >= 0;                                                                          
{
  if (e	== 0)
    return 1;
  else
    return b * exp(b, e-1);
}

The @requires clause specifies that the exponent e should be greater or equal to 0. This is an example of a precondition for the function, which is a particular case of a contract.

To experiment with this function, we give the file name (exp.c0) as an argument when we invoke coin.

% coin exp.c0
Coin 0.2.6 "Penny" (r2087, Fri May 27 12:10:21 EDT 2011)
Type `#help' for help or `#quit' to exit.
--> exp(2,3);
8 (int)
--> exp(3,2);
9 (int)
--> exp(-2,5);
-32 (int)
--> 

We can examine all functions that are loaded using the #functions directive. You can see a list of all available coin directives with #help.

--> #functions
int exp(int b, int e);
--> 

We do not see the definition of the function, but we see its declaration. We see that the first argument b must be an integer (type int), the second argument e must also an integer, and the value returned will be an integer.

Statements

We give here a short introduction; see Statements for more thorough coverage. The body of a function definition is a statement. As such it is executed for its effect, but a function is supposed to return a value (unless its return type is void). Thus we have a special statement, return e;, which is used to return the value of the expression e from the current function. We see this in the exponential function shown above.

/* file exp.c0 */
int exp(int b, int e)
//@requires e >= 0;                                                                          
{
  if (e	== 0)
    return 1;
  else
    return b * exp(b, e-1);
}

We also see a conditional statement, if (e) s1 else s2. This first evaluates e (which must be of type bool) and then either s1 (if e is true) or s2 (if e is false). s1 and s2 are again statements; in the example, they are both return statements.

Another example of a statement is a while loop, while (e) s which first tests e (which must be of type bool). If e is false it exits, and s is not executed. If e is true, it executes s once and then continues by testing e again.

As an example, here is a version of the exponential function using a loop, which also uses variable declarations, variable assignments, and sequences of statements.

/* file exploop.c0 */
int exploop(int b, int e)
//@requires e >= 0;
{
  int result = 1;
  int i = 0;
  while (i < e) {
    result = result * b;
    i = i+1;
  }
  return result;
}

Note that each time around the loop, result is bi, so when the test fails and i is equal to e, then result must be be.

We also see here the sequencing of statements, { s1 s2 ... sk }, enclosed in curly braces. First, the body of the function itself consists of four statements (two variable declarations, a while loop, and a return statements) and the body of the while loop consists of two statements, both of which are assignment statements. We call such a sequence of statements a block.

An important concept in programming languages is that of variable scope. In C0, variables have so-called lexical scope, which means that variables can be used only after they have been declared, and only in the block that they are declared in. Similarly, variables denoting functions parameters (here: b and e) can only be used inside this function. Both are examples of local variables. Variables declared in different functions never have anything to do with each other, because C0 does not have so-called global variables.

Contracts

Contracts are annotations that explain and specify what the program does. They are dynamically checked when the coin interpreter or the cc0 compiler are invoked with the -d flag. We highly recommend using this flag during normal development to catch errors as early as possible.

All forms of contracts contain a boolean expression e. The contract fails, aborting the program, when e evaluates to false. The expression e should be carefully written so it has no effect, so that the program executes the same way, whether contracts are checked or not.

  • @requires e; for function preconditions. e is checked before the function body executes.
  • @ensures e; for function postconditions. e is checked after the return statement. e can use the special variable \\result to refer to the functions return value.
  • @loop_invariant e; for loops. e is checked on every iteration just before the loop exit test is performed.
  • @assert e; for statements. e is checked just before the following statement.

See Contracts for more information.

Libraries

C0 uses a few predefined libraries that supply some standard functions. The most commonly used ones are conio for input/ouput and string for string manipulation. We import a library lib with #use <lib> at the top of a file. The conio and string libraries are briefly discussed with Strings. For a complete reference to libraries, see the C0 library reference.

The following example function expprint(int b, int e) prints the powers b0, b1, ..., be-1, each on a separate line.

/* file expprint.c0 */
#use <conio>

int expprint(int b, int e)
//@requires e >= 0;                                                                                  
{
  int result = 1;
  int i = 0;
  while (i < e) {
    printint(result); print("\n");
    result = result * b;
    i = i+1;
  }
  return result;
}

Here, print("\n") prints the newline character \n (the only character in the string "\n") which finishes the current line of output. It is an idiosyncrasy of C-like languages that printing may not actually show anything at the terminal until the program outputs a newline character (other characters may be held in a so-called output buffer), so you should make sure to output newlines as necessary, especially when debugging your code.

Here is a sample run of this function in coin.

% coin expprint.c0
Coin 0.2.6 "Penny" (r2087, Fri May 27 12:10:21 EDT 2011)
Type `#help' for help or `#quit' to exit.
--> expprint(-2,3);
1
-2
4
-8 (int)
--> 

The line -8 (int) is the value of the expression, tagged with its type; the preceding three lines are the output from the print statements in the function.