Programming Brainfuck...

Posted Fri 12 December 2008 By Dragon  | Category : programming

Tags : brainfuck / bf / python /

 

No, I'm not being vulgar, that is actually the language's name. bf, or brainf*ck if you are really sensitive. I'm going to suspect someone created it on a bet. Basically, the idea was to create the most minimalistic computer language possible that you could still write programs in. If you've ever actually tried writing anything in it, you will find it's aptly named.

How did I wind up inspired to mess with something like this? Er, well, a few days ago, some bf code was embedded in a webcomic I read, and, in the grand tradition of doing things the hard way, I was inspired to write my own bf interpreter in python to see what the code did. (I found out. It prints "Beep!". ) anyway, here is my interpreter:

import sys

class BFInterp(object):
    INST = { '>' :  'incp',
                   '<' : 'decp',
                   '+' : 'inc',
                   '-' : 'dec',
                   '.' : 'output',
                   ',':'input',
                   '[' :'bra',
                   ']': 'ket' }
    def __init__(self, stack_size = 30000, max_val = 256 ):
        self.stacksize = stack_size
        self.maxval = max_val
        self.stack = [0] *  stack_size
        self.debug = False
        self.code = ''

    def reset(self):
        self.cp = 0
        self.pointer = 0

    def framesweep(self):
        self.bramap = {}
        self.ketmap = {}
        last = []
        for i in range(len(self._code)):
            op = self._code[i]
            if op == '[':
                last.append(i)
            if op == ']':
                self.bramap[last.pop()] = i
        if last:
            raise IndexError("Unbalanced []'s ")
        for k, v in self.bramap.items():
            self.ketmap[v] = k

    def get_code(self):
        return self._code

    def set_code(self, val):
        self._code = val
        self.reset()
        self.framesweep()

    code = property(get_code, set_code)

    def get_val(self):
        return self.stack[self.pointer]

    def set_val(self, value):
        self.stack[self.pointer] = value % self.maxval

    value = property(get_val, set_val)

    def incp(self):
        if (self.pointer+1) >= self.stacksize:
            raise ValueError("Pointer Overflow Error!")
        self.pointer += 1

    def decp(self):
        if self.pointer < 1:
            raise ValueError("Pointer Underflow Error!")
        self.pointer -= 1

    def inc(self):
        self.value = self.value + 1

    def dec(self):
        self.value = self.value - 1

    def output(self):
        sys.stdout.write(chr(self.value))

    def input(self):
        self.value = ord(sys.stdin.read(1))

    def bra(self):
        if not self.value:
            self.cp = self.bramap[self.cp]

    def ket(self):
        if self.value:
            self.cp = self.ketmap[self.cp]

    def execute(self, code):
        self.code = code
        self.run()

    def run(self):
        while self.step():
            pass

    def step(self):
        if self.code[self.cp] in self.INST:
            getattr(self, self.INST[self.code[self.cp]])()
        self.cp += 1
        if self.debug:
            sys.stderr.write(str(self) + "\n")
        return self.cp < len(self.code)

    def __str__(self):
        return  "BFInterp state: Inst #%s[%s] p = %s, val = %s" % (self.cp, self.code[self.cp], self.pointer, self.value)

if __name__ == '__main__':
    b = BFInterp()
    b.code = '+++++++++++[>+++[>++>+++>+<<<-]<-]>>.>++..+++++++++++.>.'
    b.run()

And yes, it's quite intelligible for a bf interpreter. There is one in assembly that is 240 bytes long.