Brainf**k while waiting for a flight
Warning: NSFW language.
Brainfuck is a Turing-complete programming language consisting of eight commands, each of which is represented as a single character.
> Increment the pointer. < Decrement the pointer. + Increment the cell at the pointer. - Decrement the cell at the pointer. . Output the ASCII value of the cell at the pointer. , Input a byte and store it in the cell at the pointer. [ Jump forward past the matching ] if the cell at the current pointer is zero. ] Jump backward to the matching [ unless the cell at the current pointer is zero.
Having arrived almost 3 hours early to JFK, flying back to Cincinnati, I spent the time coding up a Python script which inputs a string and outputs a Brainfuck source code which, when run with a Brainfuck interpreter, outputs said string. So for example:
to_brainfuck "Hello, World!"
Will output:
++++++++++[>+++>++++++>++++++++>++++++++++>+++++++++++<<<<<-]>>++++++++ ++++.>>+.+++++++..>+.<<<<++++++++++++++.------------.>>>>++++++++.---- ----.+++.<.--------.<<<+.-----------------------.
The horror above is what Brainfuck source code looks like. When you run the above code with a Brainfuck interpreter, it will print "Hello, world!".
Brainfuck interpreters and compilers can be found here. Ubuntu has a Brainfuck interpreter called bf.
Probably not the best code I wrote, could use some honing. Still, it served the purpose of killing a couple of hours.
#!/usr/bin/env python import sys class bf: def __init__(self,format_bf=True): """ Initiate brainfuck code string. Pointers are initiated to the following values, with their ascii equivalents shown ptr0 = 10 ptr0 is used as a loop counter ptr1 = 30 ptr2 = 60 @ ptr3 = 80 #P ptr4 = 100 #d ptr5 = 110 #n """ self.bf_code = '' self.bf_code += "++++++++++[>+++>++++++>++++++++>++++++++++>+++++++++++<<<<<-]" # Index: cell number. Value: cell value. self.ptrs = {1: 30, 2: 60, 3: 80, 4: 100, 5:110} self.ptr_idx = 0 # which pointer is being used self.format_bf = format_bf # Format the bf code. Default True. def string_bf(self,instring): # Accepts a string. Outputs bf code which prints that string # when run with a bf interpreter for c in instring: self.bf_code += self.to_bf(c) # add a newline self.bf_code += self.to_bf(chr(10)) if self.format_bf: self.bf_code = self._format_bf_code(self.bf_code) return self.bf_code def _format_bf_code(self,bf_code): # Format the bf source code to 70 chars / line outstr = '' for i,c in enumerate(bf_code): if i % 70 == 0 and i > 0: outstr += '%s\n' % c else: outstr += c return outstr def to_bf(self,c): # accept a character c, generate the bf code to print that # character. # increment / decrement the data pointer if c < '@': ptr_target = 1 elif c >= '@' and c < 'P': ptr_target = 2 elif c >= 'P' and c < 'd': ptr_target = 3 elif c >= 'd' and c <'n': ptr_target = 4 else: ptr_target = 5 ptr_inc_str = self.increment_ptr(ptr_target) # Now increment / decrement the value which the pointer points ascii_target = ord(c) ascii_val = self.ptrs[self.ptr_idx] inc_val, inc_val_str = self.increment_val(ascii_val, ascii_target) self.ptrs[self.ptr_idx] += inc_val return ptr_inc_str+inc_val_str+'.' def increment_val(self,ascii_val, ascii_target): inc_val = ascii_target - ascii_val if inc_val < 0: inc_val_str = '-'*abs(inc_val) elif inc_val > 0: inc_val_str = '+'*abs(inc_val) else: inc_val_str = '' return inc_val, inc_val_str def increment_ptr(self,ptr_target): ptr_inc = ptr_target - self.ptr_idx if ptr_inc < 0: ptr_str = '<'*abs(ptr_inc) elif ptr_inc > 0: ptr_str = '>'*ptr_inc else: ptr_str = '' self.ptr_idx += ptr_inc return ptr_str if __name__ == '__main__': my_bf = bf() if sys.argv[1] == '-f': intext = file(sys.argv[2]).read() else: intext = sys.argv[1] o = my_bf.string_bf(intext) sys.stdout.write("%s\n" % o)
To run:
chmod +x bf_string.py ./bf_string.py "Brainfork is awesome!" > mycode.bf # generate Brainfuck code into mycode.bf bf mycode.bf # The brainfuck interpreter bf Brainfork is awesome!
And mycode.bf will contain:
++++++++++[>+++>++++++>++++++++>++++++++++>+++++++++++<<<<<-]>>++++++.> >>++++.<<+++++++++++++++++.>+++++.>----.<---.>+.+++.<+++++.<<<++.>>>-- .>+.<<<<.>>.>>++++.<----.>----.----.<++++++++.--------.<<<+.---------- -------------.
You can also run it with the -f option, where the input string will be read from a file.
UPDATE: and here is a brainfuck interpreter, written in Python.
UPDATE II: Following Vincent's comment, here is a fixed version of the interpreter. This time it should work with nested loops. Thanks Vincent.
#!/usr/bin/env python import sys class BfInterpreter: def __init__(self,inpath): self.iptr = 0 self.cells =[0] self.cmdptr = 0 self.infile = file(inpath) self.bfcode = self.infile.read() self.cloop_stack = [] # location of current startloop in bf code self.ploop_stack = [] # which ptr is current loopcounter self.loop_ended = False # Indicates if a loop counter just # reached zero def inc_ptr(self): self.iptr += 1 if self.iptr > len(self.cells) - 1: self.cells.append(0) def dec_ptr(self): self.iptr -= 1 if self.iptr <= -1: raise ValueError,"negative pointer" def inc_cell(self): self.cells[self.iptr] += 1 def dec_cell(self): self.cells[self.iptr] -= 1 # Check if this is a loop counter if self.ploop_stack: if self.ploop_stack[-1] == self.iptr and \ self.cells[self.iptr] == 0: self.loop_ended = True def start_loop(self): self.cloop_stack.append(self.cmdptr) self.ploop_stack.append(self.iptr) def end_loop(self): if self.cells[self.iptr] > 0: if not self.cloop_stack: raise ValueError,"no startloop character found" else: self.cmdptr = self.cloop_stack[-1] elif self.cells[self.iptr] == 0 and self.loop_ended: self.loop_ended = False self.cloop_stack.pop() self.ploop_stack.pop() def putc(self): sys.stdout.write("%s" % chr(self.cells[self.iptr])) def getc(self): self.cells[self.iptr] = ord(sys.stdin.read(1)) def run_bf(self): self.cmdptr = -1 while True: self.cmdptr += 1 if self.cmdptr >= len(self.bfcode): break cmd = self.bfcode[self.cmdptr] # print cmd, if cmd == '>': self.inc_ptr() elif cmd == '<': self.dec_ptr() elif cmd == '+': self.inc_cell() elif cmd == '-': self.dec_cell() elif cmd == '[': self.start_loop() elif cmd == ']': self.end_loop() elif cmd == '.': self.putc() elif cmd == ',': self.getc() if __name__ == '__main__': bf = BfInterpreter(sys.argv[1]) bf.run_bf()
To run, download the file, name it (say, pybf) and then:
chmod +x pybf ./pybf bf_source_code_file.bf
You might want to use a stack to keep track of loops, with start_loop pushing the current position to the stack and end_loop popping the latest start_loop position from the stack, otherwise your code won’t work for embedded loops.
Oh wow, nice catch. Will try to fix soon.
So why did you decide to do this more complicated thing with multiple pointers, rather than just having one cell which you incremented/decremented to the required value and then outputted? Was it just to keep the resulting brainfuck shorter?
@David. Yes, to keep it shorter. I basically have 4 pointers pointing to 4 most often used area in ascii table: whitespace, numbers, lowercase and uppercase. If a character to print is in one of those areas, the value at the proper pointer is incremented and printed.