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

Running C0 Programs

There are two ways to run C0 programs. We can use the coin interactive interpreter, which evaluates expressions or executes statements after a program has been loaded, and cc0, a compiler which produces an executable binary. The two share a lot of infrastructure.

If you are a student at Carnegie Mellon University, you should familiarize yourself with the Andrew environment and refer to the instructions for running C0 on Andrew on how to set up your environment.

coin

coin is an interactive interpreter which is useful for small explorations and testing. Once you have set up your environment, you should be able to invoke coin from the Unix shell, whose prompt with indicate with %. If you just want to test expressions, you need to give it no arguments, but its utility is limited since you cannot defined any new functions.

% coin
Coin 0.2.6 "Penny" (r2087, Fri May 27 12:10:21 EDT 2011)
Type `#help' for help or `#quit' to exit.
--> 4 * 13;
52 (int)
--> #quit
Goodbye!

Coin's prompt is --> to accept a statement, and ... when it has not yet parsed a complete statement and expects more input. Coin exploits the fact that an expression, by itself, counts as a statement in C0. For example:

--> 4 *
... 23;
92 (int)
--> 

More typically, you have written a file or several files with functions you'd like to test. You should supply the names of these files when you invoke coin. They will be processed in the order they are given, which is important because functions must be declared or defined before they are used. Here is a simple example of an exponential function which is not a built-in operator in C0.

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

In order to experiment with this function, we pass the filename to 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)
--> 

When your file contains an error that can be detected when the file is loaded, such as incorrect syntax, wrong types, undefined functions, etc., coin will refuse to start up and print an error message. This is because incorrect code cannot be executed. For example, we have omitted the semicolon ; from the first statement.

/* file exp-err.c0 */

int exp(int b, int e)
//@requires e >= 0;
{
  if (e == 0)
    return 1
  else
    return b * exp(b, e-1);
}
% coin exp-err.c0
exp-err.c0:8.3-8.3:error:Malformed expression
Unable to load files, exiting...
%

The error message here point at line 8, column 3. Counting lines in the source (or using an editor such as Emacs and commands such as M-x goto-line) leads us to the line

 else

This is the place where the parser realizes that something is wrong, because else cannot appear inside an expression, and the expression whose value we return started as 1 on the previous line. The error messages given by coin are the same as the ones issued by the cc0 compiler since they share the so-called front end of the C0 language implementation.

We can see a list of all function with the #functions coin directive, and #help to see a list of all directives.

% 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.
--> #functions
int exp(int b, int e);
--> #help
Coin - the C0 interpreter.

Being in the Coin interpreter is like being inside of a C0 function. You can
declare and assign to variables (for instance, by typing `int x = 5;' and
then `x++;' or `x /= 2;') and you can see the value of variables and
expressions (for instance, by typing `true || false;').

Remember: every statement or expression must end with a semicolon `;'.

The following special "pragmas" allow you to pass commands to Coin.
  #functions       - List all available functions
  #help            - Display this message
  #locals          - Display the contents of all declared local variables
  #structs         - List the fields of all available structs
  #quit            - Exit the interpreter
  #reload          - Reload libraries and files; clear local variables

--> 

Generally, it is best to run coin with the -d flag which checks adherence to all Contracts while running your programs. We strongly recommend this as the default, since it helps you test your programs and find bugs early. For example, the exponential function does not work correctly when the exponent is negative because the result may not be an integer. In the code we accounted for this with the contract //@requires e >= 0;.

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

When you invoke coin (or the cc0 compiler), providing the -d flag will ensure that whenever the exp function is called, it is checked that the argument e is indeed greater or equal to 0. Otherwise, an error message is issued.

% coin -d 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);
exp.c0:2.4-2.19: @requires annotation failed

Last position: exp.c0:1-7.26
               exp from <stdio>:1.1-1.9
--> 

If you forgot the -d flag, contracts are not checked and spurious computation may result. In this example, we can see that a negative exponent will lead to a succession of calls to exp with larger and larger negative numbers, in effect never terminating. This masks the fact that the error is in the call, and not in the program.

% 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);
^C
KeyboardInterrupt
Last position: exp.c0:7.12-7.26
               exp from exp.c0:7.16-7.26
                  (and 456972 similar)
               exp from <stdio>:1.1-1.9
--> 

Here we typed control-c on the keyboard (holding down the "control" key while hitting "c") in order to interrupt the execution. We see that we have made 456972 recursive called to exp when execution was stopped.

coin has several options which may change somewhat over time. To see the current set of options when you invoke coin, try

% coin -h
Coin 0.2.6 "Penny" (r2087, Fri May 27 12:10:21 EDT 2011)
Usage: /afs/andrew.cmu.edu/course/15/122/bin/coin.exe [OPTONS_AND_SOURCEFILES...]
  -v        --verbose        Give verbose status and error messages
  -h        --help           Give short usage message and exit
  -d        --dyn-check      Check contracts dynamically
  -l <lib>  --library=<lib>  Include the library <lib>
  -L <dir>                   Add <dir> to the search path for libraries
  -V        --version        Print version number and compile info
  -t        --trace          Trace execution of interpreted code
            --print-codes    Print out the internal language's representation
%

coin is a tool in progress, so some of these options may at present be of limited utility.

cc0

Like coin, the cc0 compiler also takes a list of C0 source files as an argument. However, instead of allowing the user to interactively explore the function defined in these files, it either issues an error message or produces an executable file, by default called a.out. This file can then be invoked from the shell (where you invoke coin or cc0). This executable is much more efficient than running the coin interpreter because it is expressed directly in machine language, so it is probably the more common form of testing your programs. Your solutions to programming assignments will also be compiled with cc0.

If we reconsider the file exp.c0 from the coin examples above, it is not clear what is actually supposed to be executed because the file just contains a function. To solve this, the cc0 compiler expects a special function main to be present in the files that it compiles and will run that function. Let's make a file to test the exponential function.

/* file exp-test.c0 */

#use <conio>

int main() {
  assert(exp(0,0) == 1);
  assert(exp(0,1) == 0);
  assert(exp(1,0) == 1);
  assert(exp(1,17) == 1);
  assert(exp(2,7) == 128);
  assert(exp(-2,7) == -128);
  assert(exp(3,4) == 81);
  assert(exp(-3,4) == 81);
  assert(exp(2,30) == (1<<30)); /* exp(2,k) == (1 << k) */
  print("All tests passed!\n");
  return 0;
}

We use the library conio (with #use <conio>) so we can print the success message near the end. The function main takes no arguments and asserts that a number of equalities must all be true, using the assert statement. Execution will abort, if any of them is false. Then we print a success message and return 0, which by convention is a status code indicating success. When we compile this, we must load both the implementation of the exp function, and the file above with the main function. We pass the -d flag to make sure that contracts are checked during execution. You should always do this unless low-level efficience or timing information is important for a particular run.

% cc0 -d exp.c0 exp-test.c0
% ./a.out
All tests passed!
0
%

The first line produces only the file a.out, which we then call in the second line. If we create an error in this file (here, erroneously thinking that 2<<k computes 2k when it really gives you 2k-1

/* file exp-test-err.c0 */

#use <conio>

int main() {
  assert(exp(0,0) == 1);
  assert(exp(0,1) == 0);
  assert(exp(1,0) == 1);
  assert(exp(1,17) == 1);
  assert(exp(2,7) == 128);
  assert(exp(-2,7) == -128);
  assert(exp(3,4) == 81);
  assert(exp(-3,4) == 81);
  assert(exp(2,30) == (2<<30));	/* oops, should be 1 << 30 */
  print("All tests passed!\n");
  return 0;
}

then the file will still compile (since syntax and types are all correct), but executing the result will yield an error message.

% cc0 -d exp.c0 exp-test-err.c0
% ./a.out
exp-test-err.c0:14.3-14.29: assert failed
Abort (core dumped)
%

And, indeed, line 15 is the line with the failing assertion.

cc0, like coin, takes a number of arguments besides the -d switch already mentioned. You can see the currently allowed options using the -h or --help option. A useful shortcut, for example, is -x that will immediately execute the a.out file instead of requiring you invoke it yourself.

% cc0 -h
C0 reference compiler (cc0) revision 2085 (built Mon May 16 17:32:22 EDT 2011)
Usage: /afs/andrew.cmu.edu/course/15/122/bin/cc0 [OPTION...] SOURCEFILE
where OPTION is
  -v         --verbose         Give verbose status and error messages
  -h         --help            Give short usage message and exit
  -d         --dyn-check       Check contracts dynamically
  -l <lib>   --library=<lib>   Include the library <lib>
  -L <dir>                     Add <dir> to the search path for libraries
  -V         --version         Print version number and compile info
             --dump-ast        Pretty print the program's abstract syntax tree
  -s         --save-files      Save the .c and .h files produced by the compiler
  -r <rt>    --runtime=<rt>    Select a runtime (default "c0rt")
  -O <opt>   --optimize=<opt>  Optimize to level <opt> (default 0, >0 may be unsound)
  -b         --bytecode        Generate bytecode instead of executable
  -o <file>  --output=<file>   Place the executable output into <file>
  -n         --no-log          Disable logging for this compile
  -x         --exec            Execute compiled file

Generally, you should avoid specifying libraries on the command line with -l; include them in the source files with the #use <lib> directive at the beginning of the file that requires a library. The cc0 compiler (and also coin) will not reload libraries even if they appear multiple times.