teaching machines

A C Interpreter

January 17, 2013 by . Filed under code, public.

My programming languages class this semester will begin with a few lectures on shell scripting. As an exercise for myself, I thought I’d write a little C “interpreter” so that I can “directly” run my C source code. The first thing we’ll need is a C source file. Here’s one:

#include <stdio.h>

int main(int argc, char **argv) {
  int a = 7;
  printf("a: %d\n", a);
  return 0;
}

Now, I need to indicate what interpreter should run this C script. I’ll write an interpreter named crun and make sure it’s in a directory on my PATH. I can associate this interpreter with my C script by prepending a shebang line as the first line of my file:

#!/usr/bin/env crun

#include <stdio.h>

int main(int argc, char **argv) {
  int a = 7;
  printf("a: %d\n", a);
  return 0;
}

The crun interpreter is itself just a shell script that silently compiles and runs the C code:

#!/usr/bin/env zsh

src=$1
root=${src:t:r}
exe=/tmp/$root

gcc -o $exe -x c =(sed -ne '2,$ p' $src) && ($exe; rm $exe)

The assignment to root strips the path to the source code file of any leading directory information (:t) and the file extension (:r). This will be the name of our executable, which we put in the /tmp directory. The =() is a zsh operation that abbreviates redirection to a tmp file and a reference to this tmp file. That is, it abbreviates this sequence:

sed -ne '2,$ p' $src > tmpfile
gcc -o $exe -x c tmpfile

The sed command prints out all lines of the source file but the shebang line (lines 2 through the end, or 2,$). Since the output of this is going to an anonymous tmp file, we need to force gcc to treat it as C source code (-x c). We compile. If compilation was successful, we run the executable. Afterwards, we delete the executable—whether the running was successful or not. With command1 && command2, command2 only runs if command1 returned 0. With (command1; command2), command2 always runs.

And for the final reckoning, we make our code executable and run it:

$ chmod u+x print.c
$ ./print.c 
a: 7

Okay, that was silly.