This example is an interpreter for the Brainfuck language. Brainfuck is an extremely lightweight (some might even say stupid) programming language that combines an instruction set of only eight instructions with an array of data bytes that the instructions can modify.
Since an interpreter without example programs to run is largely meaningless, the following are a couple of example Brainfuck programs:
// Arena Brainf*k threaded optimizing interpreter - Copyright 2006 J.L. Bezemer
// You can redistribute this file and/or modify it under
// the terms of the GNU General Public License
// Values are pushed on the stack by push() and popped
// from the stack by pop(). The stackpointer is stored in sp.
stack = mkarray (); // this is the stack
sp = 0; // this is the stack pointer
int pop () {
if (sp > 0) { // if values on the stack
n = (stack [--sp]); // return a value
global ("stack", "sp");
return (n);
} else { // else throw an exception
throw ("Unmatched [");
return (0);
}
}
void push (int n) {
stack [sp++] = n; // push a value on the stack
global ("stack", "sp");
}
// This is a brainf*k compatible input buffer. The interpreter reads one
// character at the time, although the user can fill the entire buffer at
// one go. If the buffer is empty, the user is prompted again.
tib = "";
int key () { // if buffer empty prompt user
if (strlen (tib) == 0) tib = fgets (stdin);
c = left (tib, 1); tib = right (tib, strlen (tib) - 1); global ("tib");
return (ord (c) == () ? 0 : ord (c)); // return a character value
}
// These are the code-, parameter-, data- and symbolsegment
// of the brackf*k interpreter. 'mp' is a pointer to the currently
// accessed memory location
cs = ps = ds = ss = mkarray (0);
mp = 0;
// These are all the brainf*k instructions. The last one was added in order
// to provide a return instruction to the calling program
int m_move (int ip) { mp += ps [ip]; global ("mp"); return (++ip); }
int m_add (int ip) { ds [mp] += ps [ip]; global ("ds"); return (++ip);}
int m_get (int ip) { ds [mp] = key (); global ("ds"); return (++ip); }
int m_put (int ip) { printf ("%s", chr (ds [mp])); return (++ip); }
int p_do (int ip) { return ((ds [mp] & 255) == 0 ? ps [ip] : ++ip); }
int p_loop (int ip) { return (ps [ip]); }
int p_exit (int ip) { exit (0); return (ip); }
// This function compiles an instruction, no questions asked. All
// segments (code, parameter and symbol) are updated
int compile (fn a, string b, int c, int d) {
cs [d] = a;
ss [d] = b;
ps [d] = c;
global ("cs", "ps", "ss");
return (++d);
}
// This function tries to figure out whether the instruction to be
// compiled is actually of the same family as the previous instruction
// that has been compiled. If it is, it just changes the parameter segment
// accordingly, but does not compile a new instruction
int optimize (fn a, string b, int c, int d) {
if ((d) && (ss [d - 1] == b)) ps [d - 1] += c;
else d = compile (a, b, c, d);
global ("ps");
return (d);
}
// These are the instructions of how to compile the brainf*k operators.
// They contain the brainf*k instruction, the symbolic name, the change
// in the parameter segment and the current program counter. Note not
// all brainf*k instructions can be optimized
int do_next (int pc) { return (optimize (m_move, "move", 1, pc)); }
int do_prev (int pc) { return (optimize (m_move, "move", -1, pc)); }
int do_inc (int pc) { return (optimize (m_add, "add", 1, pc)); }
int do_dec (int pc) { return (optimize (m_add, "add", -1, pc)); }
int do_put (int pc) { return (compile (m_put, "put", 0, pc)); }
int do_get (int pc) { return (compile (m_get, "get", 0, pc)); }
int do_do (int pc) { push (pc); return (compile (p_do, "jump0", 0, pc)); }
int do_loop (int pc) {
ps [(addr = pop ())] = pc + 1; // patch backlink address
global ("ps");
return (compile (p_loop, "jump", addr, pc));
}
// This table matches the brainf*k operators to compilation instructions
symbols = mkstruct (
">", do_next,
"<", do_prev,
"+", do_inc,
"-", do_dec,
".", do_put,
",", do_get,
"[", do_do,
"]", do_loop
);
// This one actually compiles a brainf*k program to a serie of Arena calls.
void compile_bf (f) {
pc = 0;
while ((char = fgetc (f)) != ()) { // get a brainf*k keyword
assemble = struct_get (symbols, char);
if (assemble != ()) pc = assemble (pc);
} // compile as long as there is keyword
compile (p_exit, "exit", 0, pc); // compile the exit instruction
if (sp != 0) throw ("Unmatched ]"); // check the stack (all addr compiled)
}
// This function allows you to decompile a compiled brainf*k program
void decompile_bf () {
for (x = 0; ss [x] != "exit"; x++)
printf ("[%4d] %-5s (%d)\n", x, ss [x], ps [x]);
}
// Execute the compiled program
void execute_bf () {
ip = 0; // reset instruction pointer
for (;;) { // do this forever
operator = cs [ip]; // get the next operator
ip = operator (ip); // and execute it
}
}
// This is the main function. It opens the file, compiles the program
// and runs the command
void run (string name, fn do_bf) {
if ((f = fopen (name, "r")) != ()) {
try {
compile_bf (f); // compile the program
fclose (f); // close the file
do_bf (); // then execute it
} catch (e) printf ("%s ", e); // catch any exceptions
}
}
switch (argc) {
case (2): run (argv [1], execute_bf); break;
case (3): run (argv [2], decompile_bf); break;
default : print ("Usage: bf [d] filename.b\n"); break;
}