#!/usr/bin/env python """Brain on Drugs Interpreter Brain on Drugs is a large superset of Brainfuck, including Brainfork and Braintwisted. The 10 tokens taken directly from those languages: + increment the current value - decrement the current value > move the pointer to the right < move the pointer to the left , read a char from stdin . print a char to stdout [ if the current value is zero, skip to the matching ] ] if the current value is not zero, rewind to the matching [ X swap the tape with the code stream Y fork a new thread The new tokens [may invalidate some brainfuck comments]: * multiply the current value by 2 / divide the current value by 2 [integer divide, unless you import __future__.division] ^ move the pointer up vertically _ move the pointer down vertically @ move the pointer "out" towards you # move the pointer "in" away from you ; read a number from stdin [using raw_input] : print a number to stdout = run a command with os.system; first character in the command is the current value, then everything to the right, then everything starting at the beginning of the row, the last is to the left of the current. nulls are stripped. the return code is stored at the current position. Z exit the program ( start an if-else block; if the current value is 0, move to the matching '|'; otherwise set a flag | the else part of the if-else block ) the end of the if-else block ~ bind a server socket to host, on the port at the current value ! if you've bound a server socket, accept a connection [blocking]; else, connect to the ip found in the 4 nearest places in the xy-plane [starting north, going clockwise], setting the port to the current value. [see example scripts at the bottom of the source.] $ read a char from the client socket % write a char to the client socket """ import os, sys, re, threading, socket try: import cStringIO as StringIO except: import StringIO class BOD_Interpreter: """The parser is a while loop in the __call__ method, which calls the method in actions associated with the char read. The __init__ method strips anything that isn't in actions.keys(). Simulates infinite 3D "tape" with lists of lists of lists of ints by adding cells/lists when shifting out of range. The tape alphabet is consisted of all signed ints, unless you import __future__.division, in which case floats. Cells are initialized to 0. Dimension : pos direction, neg direction x : right, left y : up, down z : out, in """ def right(self): self.px += 1 if self.px >= len(self.tape[self.pz][self.py]): for i in range(len(self.tape)): for j in range(len(self.tape[0])): self.tape[i][j].append(0) def left(self): if self.px == 0: for i in range(len(self.tape)): for j in range(len(self.tape[0])): self.tape[i][j] = [0] + self.tape[i][j] else: self.px -= 1 def up(self): self.py += 1 if self.py >= len(self.tape[self.pz]): for i in range(len(self.tape)): self.tape[i].append([0]*self.px) def down(self): if self.py == 0: for i in range(len(self.tape)): self.tape[i] = [0]*self.px + self.tape[i] else: self.py -= 1 def out(self): self.pz += 1 if self.pz >= len(self.tape): self.tape += [[0]*self.px]*self.py def in(self): if self.pz == 0: self.tape = [[0]*self.px]*self.py + self.tape else: self.pz -= 1 def inc(self): self.tape[self.py][self.px] += 1 def dec(self): self.tape[self.py][self.px] -= 1 def mult(self): self.tape[self.py][self.px] *= 2 def div(self): """if you want floats, say "from __future__ import division" """ self.tape[self.py][self.px] /= 2 def getc(self): """read a character in from stdin. EOF = 0 """ c = sys.stdin.read(1) self.tape[self.pz][self.py][self.px] = (c == '') and 0 ord(c) def putc(self): """write to stdout the character whose ascii value is at the current location.""" sys.stdout.write(chr(self.tape[self.pz][self.py][self.px])) def getn(self): """read using raw_input, try to cast it to an int, set it to the current location.""" i = 0 try: i = int(raw_input()) except: pass self.tape[self.pz][self.py][self.px] = i def putn(self): sys.stdout.write(str(self.tape[self.pz][self.py][self.px])) def start_loop(self): if 0 == self.tape[self.py][self.px]: depth = 1 while depth != 0: c = self.script.read(1) if c == '[': depth += 1 elif c == ']': depth -= 1 elif c == '': break def end_loop(self): if 0 != self.tape[self.py][self.px]: depth = 1 while depth != 0: self.script.seek(-2, 1) # 2 steps back c = self.script.read(1) # 1 step forward if c == '[': depth -= 1 elif c == ']': depth += 1 elif self.script.tell() == 0: break def swap(self): self.script, self.tape[self.pz][self.py] = ( StringIO.StringIO(''.join(map(chr, self.tape[self.pz][self.py]))), list(self.script.getvalue())) if self.p2 != 0: self.script.seek(self.p2) else: self.p2 = self.p def fork(self): newme = BOD_Interpreter(self.txt, sock=self.sock, server_sock=self.server_sock) newme.script.seek(self.script.tell()) newme.tape, newme.px, newme.py, newme.pz = self.tape, (self.px + 1), self.py, self.pz newme.tape[newme.pz][newme.py][newme.px] = 1 self.tape[self.pz][self.py][self.px] = 0 threading.Thread(target=newme).start() def exit(self): """seeks to the end of script, satisfying the while loop end-game condition.""" self.script.seek(len(self.script.getvalue())) def start_ifelse(self): if self.tape[self.p] == 0: depth = self.ifelse_flag = 1 while depth != 0: c = self.script.read(1) if c == '(': depth += 1 elif (c == ')') or ((c == '|') and (depth == 1)): depth -= 1 def ifelse(self): if self.ifelse_flag != 0: depth = 1 while depth != 0: c = self.script.read(1) if c == '(': depth += 1 elif c == ')': depth -= 1 def end_ifelse(self): """place-holder for uniformity; does nothing.""" pass def socket_bind(self): self.server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.server_sock.bind((socket.gethostname(), self.tape[self.pz][self.py][self.px])) def socket_connect_accept(self): if self.server_sock == None: self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.connect(( str(self.tape[self.pz][self.py-1][self.px]) + '.' + str(self.tape[self.pz][self.py][self.px+1]) + '.' + str(self.tape[self.pz][self.py+1][self.px]) + '.' + str(self.tape[self.pz][self.py][self.px-1]), self.tape[self.pz][self.py][self.px] )) else: self.sock = self.server_sock.accept() def socket_read(self): try: self.tape[self.pz][self.py][self.px] = ord(self.sock.read(1)) except Exception, e: print 'socket read error at', script.tell(), '--', e def socket_write(self): try: self.sock.send(chr(self.tape[self.pz][self.py][self.px])) except Exception, e: print 'socket write error at', script.tell(), '--', e def command(self): self.tape[self.pz][self.py][self.px] = os.system(''.join( map(chr, self.tape[self.pz][self.py][self.px:] + self.tape[self.pz][self.py][:self.px])).replace('\0', '')) actions = { '>' : right, '<' : left, '^' : up, '_' : down, '@' : out, # how do you draw a vector pointing out of the board at you? into the board? '#' : in, '+' : inc, '-' : dec, '*' : mult, '/' : div, ',' : getc, '.' : putc, ';' : getn, ':' : putn, '[' : start_loop, ']' : end_loop, 'X' : swap, 'Y' : fork, 'Z' : exit, '(' : start_ifelse, '|' : ifelse, ')' : end_ifelse, '~' : socket_bind, '!' : socket_connect_accept, '$' : socket_read, '%' : socket_write, '=' : command, } def __init__(self, txt='', **kw): """parse txt as if it were a brainfork/braintwist script. self.__dict__ is updated from kw, so read this source for handy keyword arguments. """ self.txt = re.sub('[^%s]' % re.escape(''.join(self.actions.keys())), '', txt) self.script = StringIO.StringIO(self.txt) self.px = self.py = self.pz = self.p2 = 0 # p2 is used while parsing tape[z][y] as script, after an odd number of 'X's, so parsing doesn't restart at 0 self.tape = [[[0]]] # 3d! self.ifelse_flag = 0 self.debug = False self.sock = self.server_sock = None self.__dict__.update(kw) def __call__(self): c = self.script.read(1) while c != '': self.actions.get(c, lambda:None)() if self.debug: print 'tape:', self.tape print 'p:', (self.px, self.py, self.pz) c = self.script.read(1) hello_world = '++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.' print_sexy = '+*******>+++++[<------>-]<.+++.--.++++++++.++++++++++++++.' eval = '++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>,[>,]<[<]X' echo = ',[.,]' copy_0_to_1 = '+++[->+>+<<]>>[-<<+>>]' # use 2 as a buffer fibonacci = '@+***++# store newline at 0 0 1 +>+< initial conditions [ :@.# print number at origin and newline ]' bang_idiom = '([-]|+) test_bang = 'Y(:<+***++.>([-]|+):|:<+***++.>([-]|+):)' print_newline = '+***++.' test_mult_div_getn_putn = ';*:[-];/:' test_mult_div_getn_putn2 = ';>;< read two numbers [[->+>+<<]>>[-<<+>>]-]:' multi_threaded_server = '~[!Y]$[.$]' debug = '''>>>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>,[>,]<[<]<+Y[<++++++++++>[<.>]]>X ''' test_sockets = 'Y(-|+)(+**********+~![$.]|^+*******-_<+>+**********+!^' + print_sexy.replace('.', '%') + ')' # spawns a client thread [which connects to localhost and sends a name] while main binds to host and prints what it recvs if __name__ == "__main__": if len(sys.argv): BOD_Interpreter(open(sys.argv[1]).read())() else: BOD_Interpreter(hello_world, debug=True)()