Skip to content

Instantly share code, notes, and snippets.

@MineRobber9000
Last active March 13, 2020 06:52
Show Gist options
  • Save MineRobber9000/5875a416750d7a7c20ab840a7b529bf7 to your computer and use it in GitHub Desktop.
Save MineRobber9000/5875a416750d7a7c20ab840a7b529bf7 to your computer and use it in GitHub Desktop.
A brainfuck of a Brainfuck interpreter in Python.

bfbf.py

A brainfuck of a Brainfuck interpreter. Written at 2 in the morning, because who needs sleep? (Answer: me. I didn't even really I named the main class BFInterpeter until it caused an error.)

How the fuck does it work?

So the AST mumbo-jumbo creates a class, BFInterpreter, that has one method: a @classmethod'd method called interpret. This implements the core tenets of a Brainfuck interpreter in Python, only using builtins. It uses the "loop dictionary" method of handling loops, where the interpreter stores where each bracket's corresponding partner is. The fix_missing_locations call makes the node pile compileable.

The remaining code in the module:

  1. Executes the code...
  2. ...imports sys...
  3. ...and replaces the module object with the BFInterpreter class.

That's it! To think, something that simple looks that complicated.

As for how I wrote it, I wrote the code all beforehand, and used ast.parse and ast.dump to turn it into an AST object clusterfuck. In the module, the call to fix_missing_locations is what allows me to just compile the ast objects.

(Small note: I decided not to implement input. Any way of doing it in a reasonable manner would be ugly and also counter-intuitive. Any attempt to use the , character will result in the cell being set to 0 (EOF state).)

"""A brainfuck of a Brainfuck interpreter. Uses AST code. Python 3 only."""
from ast import *
m=Module(body=[ClassDef(name='BFInterpreter', bases=[], keywords=[], body=[FunctionDef(name='interpret', args=arguments(args=[arg(arg='cls', annotation=None), arg(arg='p', annotation=None)], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Assign(targets=[Name(id='loopdict', ctx=Store())], value=Dict(keys=[], values=[])), Assign(targets=[Name(id='s', ctx=Store())], value=List(elts=[], ctx=Load())), For(target=Tuple(elts=[Name(id='i', ctx=Store()), Name(id='c', ctx=Store())], ctx=Store()), iter=Call(func=Name(id='enumerate', ctx=Load()), args=[Name(id='p', ctx=Load())], keywords=[]), body=[If(test=Compare(left=Name(id='c', ctx=Load()), ops=[Eq()], comparators=[Str(s='[')]), body=[Expr(value=Call(func=Attribute(value=Name(id='s', ctx=Load()), attr='append', ctx=Load()), args=[Name(id='i', ctx=Load())], keywords=[]))], orelse=[]), If(test=Compare(left=Name(id='c', ctx=Load()), ops=[Eq()], comparators=[Str(s=']')]), body=[Assign(targets=[Name(id='t', ctx=Store())], value=Call(func=Attribute(value=Name(id='s', ctx=Load()), attr='pop', ctx=Load()), args=[UnaryOp(op=USub(), operand=Num(n=1))], keywords=[])), Assign(targets=[Subscript(value=Name(id='loopdict', ctx=Load()), slice=Index(value=Name(id='t', ctx=Load())), ctx=Store())], value=Name(id='i', ctx=Load())), Assign(targets=[Subscript(value=Name(id='loopdict', ctx=Load()), slice=Index(value=Name(id='i', ctx=Load())), ctx=Store())], value=Name(id='t', ctx=Load()))], orelse=[])], orelse=[]), Assign(targets=[Name(id='tape', ctx=Store())], value=Call(func=Name(id='dict', ctx=Load()), args=[], keywords=[])), Assign(targets=[Name(id='mp', ctx=Store())], value=Num(n=0)), Assign(targets=[Name(id='i', ctx=Store())], value=Num(n=0)), While(test=Compare(left=Name(id='i', ctx=Load()), ops=[Lt()], comparators=[Call(func=Name(id='len', ctx=Load()), args=[Name(id='p', ctx=Load())], keywords=[])]), body=[Assign(targets=[Name(id='c', ctx=Store())], value=Subscript(value=Name(id='p', ctx=Load()), slice=Index(value=Name(id='i', ctx=Load())), ctx=Load())), If(test=Compare(left=Name(id='c', ctx=Load()), ops=[Eq()], comparators=[Str(s='+')]), body=[Assign(targets=[Subscript(value=Name(id='tape', ctx=Load()), slice=Index(value=Name(id='mp', ctx=Load())), ctx=Store())], value=BinOp(left=BinOp(left=Call(func=Attribute(value=Name(id='tape', ctx=Load()), attr='get', ctx=Load()), args=[Name(id='mp', ctx=Load()), Num(n=0)], keywords=[]), op=Add(), right=Num(n=1)), op=BitAnd(), right=Num(n=255)))], orelse=[]), If(test=Compare(left=Name(id='c', ctx=Load()), ops=[Eq()], comparators=[Str(s='-')]), body=[Assign(targets=[Subscript(value=Name(id='tape', ctx=Load()), slice=Index(value=Name(id='mp', ctx=Load())), ctx=Store())], value=BinOp(left=BinOp(left=Call(func=Attribute(value=Name(id='tape', ctx=Load()), attr='get', ctx=Load()), args=[Name(id='mp', ctx=Load()), Num(n=0)], keywords=[]), op=Sub(), right=Num(n=1)), op=BitAnd(), right=Num(n=255)))], orelse=[]), If(test=Compare(left=Name(id='c', ctx=Load()), ops=[Eq()], comparators=[Str(s='>')]), body=[Assign(targets=[Name(id='mp', ctx=Store())], value=BinOp(left=BinOp(left=Name(id='mp', ctx=Load()), op=Add(), right=Num(n=1)), op=Mod(), right=Num(n=30000)))], orelse=[]), If(test=Compare(left=Name(id='c', ctx=Load()), ops=[Eq()], comparators=[Str(s='<')]), body=[Assign(targets=[Name(id='mp', ctx=Store())], value=BinOp(left=BinOp(left=Name(id='mp', ctx=Load()), op=Sub(), right=Num(n=1)), op=Mod(), right=Num(n=30000)))], orelse=[]), If(test=Compare(left=Name(id='c', ctx=Load()), ops=[Eq()], comparators=[Str(s='.')]), body=[Expr(value=Call(func=Name(id='print', ctx=Load()), args=[Call(func=Name(id='chr', ctx=Load()), args=[Call(func=Attribute(value=Name(id='tape', ctx=Load()), attr='get', ctx=Load()), args=[Name(id='mp', ctx=Load()), Num(n=0)], keywords=[])], keywords=[])], keywords=[keyword(arg='end', value=Str(s=''))]))], orelse=[]), If(test=Compare(left=Name(id='c', ctx=Load()), ops=[Eq()], comparators=[Str(s=',')]), body=[Assign(targets=[Subscript(value=Name(id='tape', ctx=Load()), slice=Index(value=Name(id='mp', ctx=Load())), ctx=Store())], value=Num(n=0))], orelse=[]), If(test=BoolOp(op=And(), values=[Compare(left=Name(id='c', ctx=Load()), ops=[Eq()], comparators=[Str(s='[')]), Compare(left=Call(func=Attribute(value=Name(id='tape', ctx=Load()), attr='get', ctx=Load()), args=[Name(id='mp', ctx=Load()), Num(n=0)], keywords=[]), ops=[Eq()], comparators=[Num(n=0)])]), body=[Assign(targets=[Name(id='i', ctx=Store())], value=Subscript(value=Name(id='loopdict', ctx=Load()), slice=Index(value=Name(id='i', ctx=Load())), ctx=Load()))], orelse=[]), If(test=BoolOp(op=And(), values=[Compare(left=Name(id='c', ctx=Load()), ops=[Eq()], comparators=[Str(s=']')]), Compare(left=Call(func=Attribute(value=Name(id='tape', ctx=Load()), attr='get', ctx=Load()), args=[Name(id='mp', ctx=Load()), Num(n=0)], keywords=[]), ops=[NotEq()], comparators=[Num(n=0)])]), body=[Assign(targets=[Name(id='i', ctx=Store())], value=Subscript(value=Name(id='loopdict', ctx=Load()), slice=Index(value=Name(id='i', ctx=Load())), ctx=Load()))], orelse=[]), AugAssign(target=Name(id='i', ctx=Store()), op=Add(), value=Num(n=1))], orelse=[])], decorator_list=[Name(id='classmethod', ctx=Load())], returns=None)], decorator_list=[])])
fix_missing_locations(m)
exec(compile(m,"<ast>","exec"))
import sys
sys.modules[__name__]=BFInterpreter
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment