bf

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:

ascii.b
prints all ASCII characters.
bf2c.b
translates from Brainfuck on stdin to C on stdout.
bottles.b
prints a silly rhyme about bottles of beer.
fib.b
prints the sequence of Fibonacci numbers.
hanoi.b
solves the "Towers of Hanoi" game (in German).
hello.b
is the traditional "Hello world" example.
jabh.b
I'll let you figure out this one for yourself.
numwarp.b
prints an ASCII rendition of a number you enter.
prime.b
prints the primes up to a limit you specify.
random.b
prints a random sequence of bits.
rot13.b
does a ROT13 encoding of your input.
ryanbeer.b
is very similar to the bottles.b example.
triangle.b
prints an ASCII version of Sierpinski's triangle.

Source code

// 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;
}