RISC World

C Interpreter

Nigel Brown & Bob Voisey explain the workings of the C interpreter

We have included the C Interpreter on this issue of RISC World as it nicely compliments the !BASICtoC application. Now you can test the C code generated without having to compile it first. The full instructions for the C Interpreter follow.


!Interpret implements a subset of the C programming language. Unlike traditional C compilers, however, this program interprets the source programs without undergoing an intermediate compilation stage.

Initially developed for use as a script language within our software products (Arcterm, ArcBBS & Sourcerer), !Interpret has now been released as a stand alone interpreter in the hope that somebody might find it useful.

Interpreters have some advantages over compilers:..

  • Easy to use; no need to worry about complex cli switches
  • Fast debugging; the lengthy compilation step is eliminated
  • Compact on disk; no intermediate object files are created

..they also have some disadvantages:

  • Slow execution; interpreters are much slower than compiled code
  • Poor management; it can be harder to keep your sources organised

Because the C programming language was designed for use with a compiler, the syntax does not lend itself to interpretation so readily as that of languages such as BASIC. As a result interpreted C does not generally run as quickly as, for example, BBC Basic. Despite this many programmers may prefer to code in the elegant syntax of C.

About this manual

This document is not a C tutorial guide. Rather, its purpose is to define the subset of C implemented by the interpreter, and to outline the differences between that and regular C. If you have not encountered the C programming language before, !Interpret might be a good program with which to start - however you WILL need a programming guide to fully understand some of the facilities.

Information within this manual is given in good faith, however we cannot guarantee it's accuracy, nor the suitability of !Interpret for any particular task.

If you would like a full "C" Manual then APDL can supply the third edition of the book "C - A Dabhand guide" for the very reasonable (cheap) price of just £4 plus £4 UK post.

The Examples directory

Along with !Interpret you will find an Examples directory. Contained within are a number of sources specifically designed to demonstrate the facilities provided by the interpreter. First time C programmers may find these files useful, since theoretical C and practical C sources are sometimes at variance.

Restrictions of !Interpret

They say you never get something for nothing. In this case you do get quite a lot of C, but certainly not an entire implementation. Specifically, !Interpret does not support:

  • Structures (ie struct {} types)
  • Typedefs (user definable types)
  • Enumerated variables (ie enum {} types)
  • Floating point (ie long, double etc)
  • Miscellaneous constructs (notably do and switch)
  • Multiple source files (including #include directives)

Furthermore, !Interpret handles some C syntax in a slightly different way to regular C. These differences will be discussed in the appropriate sections of this manual.

Although the above limitations are rather depressing news for experienced C coders, the greater part of the language definition is implemented and expressions are evaluated in just the same way as in regular C (with a recursive descent parser).

Running !Interpret

There are two ways to run !Interpret:

Double click the application, whereupon you will be prompted to enter the name of the source file to be interpreted. If the file is not in the current directory you will need to specify a full file path. To assist you, an Obey file called !Here is included in the Examples directory to change the current directory to that location.

Run !Interpret by hand, from the cli or from an Obey file.

PRE> Syntax: !Interpret Eg: !Interpret Examples.MySource

Once a source file has been loaded, execution will commence immediately. To abort execution, hit the Escape key. If an error occurs, execution will terminate and the error will be reported. !Interpret has no equivalent of the BBC Basic interactive command mode.


Blocks of source can be marked as comments, in which case the interpreter completely ignores them. These blocks can be embedded anywhere within the source. The delimiters are @* <.comment> *@.

 @* This is a comment *@
 my_func(int i @* This comment won't be parsed *@)

Experienced C programmers will probably have noticed that these are not the traditional comment delimiters. Unfortunately this change was necessary to avoid a more complex bug within the interpreter. It is anticipated that this problem will be fixed for the next release so that we can return to the much loved /* ... */ syntax.


Each source file must have at least one function - the approximate equivalent of BBC Basic procedures and functions. A function is declared by specifying it's name and a parameter list.

 run_print(char *name, int record)

This syntax differs from regular C in the following ways:

  • i -You must not declare the function type
  • ii- Functions need not be predeclared

Item (i) comes about because an interpreted language need not know in advance the type of data to be passed between functions. If you attempt to declare the function type the interpreter will assume that you're declaring a variable, and will not run your code.

Item (ii) is brought about for similar reasons, the consequence being that you can forward-reference functions without having to predeclare prototypes.

Values are returned by functions in the same was as with regular C.

 run_print(char *name, int record)
 return record;

Global & Local Variables

Variables are declared in the same way as with regular C, either near the top of the source (before any functions) for globals or at the top of a function (before any expressions) for locals. The following types of variable are supported:

  - int     32-bit signed integer
  - int[n]  Array of int, with n elements
  - int *   Pointer to int
  - char    8-bit signed byte (ASCII conversion supported with 'x')
  - char[n] Array of char, with n elements (commonly used for strings)
  - char *  Pointer to char

Pointers to variables are processed in the same way as with regular C. To indirect an expression, the * operator is used:

To specify the address of an expression, the & operator is used:

 int i;
 scanf("%d",&i);  @* Store the value at the int pointed to by &i *@

!Interpret is not as fussy as regular C with regard type casting. In general the int type can be used to represent any 32 bit quantity and the char type, any 8 bit quantity. This is particularly true in the context of library function calls, as explained later.


!Interpret parses expressions in the same was as C. As with compiled C, function elements within an expression may be evaluated in any order and may be nested to any practical depth. The following numerical operators are valid within an expression:

  ++   Auto increment (post or pre indexed)
  --   Auto decrement (post or pre indexed)
   +   Add
   -   Sub
   *   Multiply
   /   Divide
   %   Modulo

The following logical operators are valid within an expression:

  ==   Equality
  !=   Non equality
   <.  Less than
   >   Greater than
  >=   Greater than or equal to
  <.=   Less than or equal to
  &&   Logical AND
  ||   Logical OR
   !   Logical NOT

Expressions can be enclosed within parenthesis to define the order of calculation. For example, the following expression is valid: printf("The result is: %d",(i=get_int(++num_to_get%2)));

Construct Syntax

!Interpret implements the following regular C constructs:

  - if() {} else {}
  - for( ; ; ) {}
  - while() {}

The if() construct has the following format:


If condition is true the first program block will execute, otherwise the second block will run.

The for(;;) construct has the following format:

 for(init_command; condition; loop_command)

The init_command is executed once, then loop_command and the programblock are executed repeatedly until condition is true.

The while() construct has the following format:


The program block will execute repeatedly until condition becomes false.

If the break command is executed within any of the program blocks for the above constructs, execution will jump past the construct to the next piece of code. For example:

   puts("Run until quit..");
 puts("..or until escape is pressed!");

If the return command is executed within any of the program blocks for the above constructs, any nested structures will be unwound correctly and execution will return to the calling function. For example:



Library Functions -----------------

A number of useful functions are built into !Interpret. You can call these like any other function and, where valid, they will return values like any other function. Where necessary, the interpreter performs type casting on your behalf.

It is beyond the scope of this manual to fully document the library, however a brief summary of each function is provided below.

Function                                               Returns
bbc_vdu(int character)                                 none
bbc_vduw(int word)                                     none
bbc_stringprint(char *string_to_print)                 none
bbc_cls()                                              none
bbc_colour(int text_colour)                            none
bbc_tab(int x, int y)                                  none
bbc_plot(int plotcode, int x, int y)                   none
bbc_mode(int mode)                                     none
bbc_move(int x, int y)                                 none
bbc_moveby(int x, int y)                               none
bbc_draw(int x, int y)                                 none
bbc_drawby(int x, int y)                               none
bbc_rectangle(int x, int y, int h, int w)              none
bbc_rectanglefill(int x, int y, int h, int w)          none
bbc_circle(int x, int y, int r)                        none
bbc_circlefill(int x, int y, int r)                    none
bbc_origin(int x, int y)                               none
bbc_gwindow(int x0, y0, x1, y1)                        none
bbc_clg()                                              none
bbc_fill(int x, int y)                                 none
bbc_gcol(int plottype, int col)                        none
bbc_tint(int col, int tint)                            none
bbc_palette(int log, int phys, int r, int g, int b)    none
bbc_point(int x, int y)                                int colour
bbc_vduvars(int *in, int *out)                         none
bbc_modevar(int mode, int varno)                       int modevar
bbc_get()                                              int key
bbc_cursor(int curs_type)                              none
bbc_adval(int buffnum)                                 int val
bbc_getbeat()                                          int beat_val
bbc_getbeats()                                         int cycl_len
bbc_gettempo()                                         int beat_rate
bbc_inkey(int time_num)                                int key
bbc_setbeats(int cycl_len)                             none
bbc_settempo(int beat_rate)                            none
bbc_sound(int chan, int amp, int pitch, int dur)       none
bbc_soundoff()                                         none
bbc_soundon()                                          none
bbc_stereo(int chan, int pos)                          none
bbc_voices(int num_chan)                               none
isalnum(char character)                                int true_or_false
isalpha(char character)                                int true_or_false
iscntrl(char character)                                int true_or_false
isdigit(char character)                                int true_or_false
isgraph(char character)                                int true_or_false
islower(char character)                                int true_or_false
isprint(char character)                                int true_or_false
ispunct(char character)                                int true_or_false
isspace(char character)                                int true_or_false
isupper(char character)                                int true_or_false
isxdigit(char character)                               int true_or_false
tolower(char character)                                char character
toupper(char character)                                char character
remove(char *fname)                                    int success_flag
rename(char *old, char *new)                           int success_flag
tmpfile()                                              int file_handle
tmpname(char *result)                                  char fname
fclose(int file_handle)                                int success_flag
fflush(int file_handle)                                int EOF_flag
fopen(char *fname, char *mode)                         int file_handle
freopen(char *fname, char *mode, int file_handle)      int file_handle
setbuf(int file_handle, char *buf)                     none
setvbuf(int file_handle,char *buf, int mode,int size)  int success_flag
fprintf(int file_handle, char *format, ...)            int num_chars
printf(char *format, ...)                              int num_chars
sprintf(char *string, char *format, ...)               int num_chars
fscanf(int file_handle, char *format, ...)             int num_items
scanf(char *format, ...)                               int num_items
sscanf(char *string, char *format, ...)                int num_items
fgetc(int file_handle)                                 char character
fgets(char *buf, int buf_size, int file_handle)        char *string
fputc(char character, int file_handle)                 int EOF_flag
fputs(char *string, int file_handle)                   int EOF_flag
getc(int file_handle)                                  char character
getchar()                                              char character
gets(char *buf)                                        char *string
putc(char character, int file_handle)                  int EOF_flag
putchar(char character)                                int EOF_flag
puts(char *string)                                     int EOF_flag
ungetc(char character, int file_handle)                int EOF_flag
fread(int *ptr, int size, int num, int file_handle)    int num_read
fwrite(int *ptr, int size, int num, int file_handle)   int num_write
fgetpos(int file_handle, int *pos)                     int success_flag
fseek(int file_handle, int offset, int whence)         int success_flag
fsetpos(int file_handle, int *pos)                     int success_flag
ftell(int file_handle)                                 int file_pos
rewind(int file_handle)                                none
clearerr(int file_handle)                              none
feof(int file_handle)                                  int EOF_flag
ferror(int file_handle)                                int err_flag;
perror(char *string)                                   none
atoi(char *string)                                     int int_value
atol(char *string)                                     int int_value
rand()                                                 int rand_value
srand(int seed)                                        none
malloc(int size)                                       int *ptr
calloc(int num, int size)                              int *ptr
realloc(int *ptr, int size)                            int *ptr
free(int *ptr)                                         none
system(char *command)                                  int success_value
getenv(char *name)                                     char *result
abs(int num)                                           int result
clock()                                                int time
memset(int *ptr, char character, int size)             none
memchr(char *string, char character, int size)         int *ptr
memcmp(int *ptr1, int *ptr2, int size)                 int same_flag
memmove(int *ptr1, int *ptr2, int size)                int *ptr1
memcpy(int *ptr1, int *ptr2, int size)                 int *ptr1
strtok(char *string1, char *string2)                   char *result
strstr(char *string1, char *string2)                   char *result
strspn(char *string1, char *string2)                   int size
strchr(char *string, char character)                   char *result
strpbrk(char *string1, char *string2)                  char *result
strcspn(char *string1, char *string2)                  int size
strrchr(char *string, char character)                  char *result
strcfrm(char *string1, char *string2, int num)         int size
strcoll(char *string1, char *string2)                  int result
strncmp(char *string1, char *string2, int num)         int result
strcmp(char *string1, char *string2)                   int result
strncat(char *string1, char *string2, int num)         char *result
strcat(char *string1, char *string2)                   char *result
strncpy(char *string1, char *string2, int num)         char *result
strcpy(char *string1, char *string2)                   char *result
os_swi(int *regs[])                                    none

For detailed descriptions of these functions refer to the BBC Basic Guide and any basic C tutorial guide.

Nigel Brown & Bob Voisey