Overview of C-Flat

This is an informal specification of C-flat, a working subset of C for use in a compiler class. C-flat includes expressions, basic control flow, recursive functions, and strict type checking. It is object-compatible with ordinary C and thus can take advantage of the standard C library, at least when using limited types. Note the emphasis on informal: the only precise specification of a language is the compiler itself, and it is your job to write the compiler! If there is anything unclear or ambiguous about this page, just ask, and we will clarify.

Tokens

Just as in C, identifiers (i.e. variable and function names) may contain letters, numbers, and underscores. Identifiers must begin with a letter or an underscore. These are examples of valid identifiers: i x mystr fog123 BigLongName55

The following strings are C-flat keywords and may not be used as identifiers: boolean char else false if int return string true void while

Whitespace is not significant in C-flat. Both C-style and C++-style comments are valid in C-flat:

/* A C-style comment */
a=5; // A C++ style comment

C-flat has four types: integers, characters, strings, and booleans. Each variable must be declared individually. Sample declarations are:

int    x = 123;
char   c = 'q';
string s = "hello world\n";
boolean b = false;

As you can see, integer, character, and string declarations are just like C. The boolean type is new; it may only take the values true or false, much as in C++ or Java. C-flat does not have arrays, pointers, or structs.

Note that constant values are similar to C: an integer is a string of digits, a single character is single quoted and strings are double quoted. Two backslash codes are defined: \n indicates a linefeed (ASCII 10) and \\ indicates a backslash.

Expressions

C-flat has many of the arithmetic operators found in C, with the same meaning and level of precedence:
( ) (highest)
grouping
- unary ops
* / % multiplication
+ - addition
< <= >= == != comparison
= assignment
(lowest)
C-flat is strictly typed: it does not have implicit or explicit data conversions. You cannot perform many of the fast-and-loose conversions found in C. For example, the following constructs are illegal:
int x=65;
char y='A';
if(x>y) ... // error: x and y are of different types!

int f=0;
if(f) ...      // error: f is not a boolean!

void writechar( char c );
int a=65;
writechar(a);  // error: a is not a char!
However, the following constructs are legal:
boolean b;
int x=3, y=5;
b = x<y;     // ok: the expression x<y is boolean

int f=0;
if(f==0) ...    // ok: f==0 is a boolean expression

char c='a';
if(c=='a') ...  // ok: c and 'a' are both chars

Declarations and Statements

In C-flat, you may declare global variables with optional constant initializers, function prototypes, and function definitions. Within functions, you may declare local variables with optional initialization expressions. Scoping rules are identical to C. As in C, you should define a main function, but it should take no arguments. Function definitions may not be nested.

Within functions, basic statements may be arithmetic expressions, return statements, if and if-else statements, while loops, or code within inner { } groups. C-flat does not have switch statements, for-loops, or do-while loops.

Here is an example that pulls it all together:

int x=35;

void write_string( string s );

void fib( int x )
{
	if(x<2) {
		return 1;
	} else {
		return fib(x-1)+fib(x-2);
	}
}

int main()
{
	int i=0;
	while(i<10) {
		write_string("hello fib!");
		i=i+1;
	}
	return fib(x);
}

External Functions and Libraries

C-flat programs are stored in files ending in .cflat and are compiled into assembly .s files via the command cflat. The assembly code must be assembled with the as command and then linked against the C library with the system linker. Here is the full sequence of steps to build a program stored in pgm.cflat into an executable called pgm:
cflat pgm.cflat pgm.s
as pgm.s -o pgm.o
gcc pgm.o -lc -o pgm
C-flat programs may link against routines written in other languages. For example, suppose that you would like to have a function that writes an integer to the console. You could implement in in traditional C like so:
/* Traditional C program in helper.c */

#include <stdio.h>

void write_integer( int x )
{
	printf("%d\n",x);
}
Now, a C-flat program may reference this external function by simply giving the appropriate prototype:
/* C-flat program in pgm.cflat */

void write_integer( int x );

int main()
{
	write_integer(8675309);
}
To build all the pieces together, you must compile and assemble the c-flat as before:
cflat pgm.cflat pgm.s
as pgm.s -o pgm.o
Then build the C helper module and link both modules together into the final program:
gcc helper.c -o helper.o -c
gcc pgm.o helper.o -o pgm