Execute Brain****/Python
Implementation of a Brainfuck interpreter in Python.
Execute Brain****/Python is an implementation of Brainf***.
Other implementations of Brainf***.
Execute Brain****/Python is part of RCBF. You may find other members of RCBF at Category:RCBF.
Usage
prompt > python bfi.py -h usage: bfi.py [-h] [-e EOF] [-w WRAP] [-B BOUND] infile positional arguments: infile options: -h, --help show this help message and exit -e, --eof EOF Configure the EOF type; EOF is unchanged (default):zero:negative_one -w, --wrap WRAP Configure cell wrapping size. WRAP is an int -B, --bound BOUND Limits tape length to BOUND (BOUND is an int). Default: unbounded
Code
from __future__ import annotations
import sys
from collections import defaultdict
def mod(a, b):
r = abs(a % b)
if a < 0: r = -r
return r
class Brainfuck:
"""A Brainf*** interpreter."""
def __init__(self, source: str, eof:str, wrap:str, bound:str):
self.source = source
self.eof = eof
self.wrap = int(wrap)
self.bound = int(bound)
self.jump_map = self._jump_map(source)
@staticmethod
def compile(source: str) -> Brainfuck:
"""Preprocess Brainf*** source code."""
return Brainfuck(source)
@staticmethod
def execute(source: str, eof: str, wrap: str, bound: str) -> None:
"""Compile and run Brainf*** source code."""
Brainfuck(source, eof, wrap, bound).run()
def run(self) -> None:
"""Run the interpreter."""
tape: defaultdict[int, int] = defaultdict(int)
cell = 0
instruction_pointer = 0
while instruction_pointer < len(self.source):
op = self.source[instruction_pointer]
if op == ">":
cell += 1
elif op == "<":
cell -= 1
elif op == "+":
tape[cell] += 1
elif op == "-":
tape[cell] -= 1
elif op == ",":
i = sys.stdin.read(1)
if self.eof == 'unchanged':
if i != '':
tape[cell] = ord(i)
elif self.eof == 'zero':
if i == '':
tape[cell] = 0
else:
tape[cell] = ord(i)
elif self.eof == 'negative_one':
if i == '':
tape[cell] = -1
else:
tape[cell] = ord(i)
elif op == ".":
sys.stdout.write(chr(tape[cell]))
elif op == '#':
print(cell, end=': [')
for i in range(cell-9,cell+1):
print(tape[i],end=' ')
print(']')
elif (op == "[" and not tape[cell]) or (op == "]" and tape[cell]):
instruction_pointer = self.jump_map[instruction_pointer]
if self.wrap != 0:
if tape[cell] < 0:
tape[cell] += self.wrap
tape[cell] = mod(tape[cell], self.wrap)
instruction_pointer += 1
if self.bound != 0:
if cell < 0:
cell += self.bound
cell = mod(cell, self.bound)
def _jump_map(self, source: str) -> dict[int, int]:
"""Precalculate loop start and end indexes."""
indexes: dict[int, int] = {}
stack: list[int] = []
for i, op in enumerate(source):
if op == "[":
stack.append(i)
if op == "]":
if not stack:
source = source[:i]
break
index = stack.pop()
indexes[i], indexes[index] = index, i
if stack:
raise SyntaxError("unclosed loops at {}".format(stack))
return indexes
if __name__ == "__main__":
import argparse
# Pass `-` on the command line to read from the standard input stream.
parser = argparse.ArgumentParser()
parser.add_argument("infile", type=argparse.FileType("r"))
parser.add_argument("-e", "--eof", default="unchanged",
help="Configure the EOF type; EOF is unchanged "
"(default):zero:negative_one")
parser.add_argument("-w", "--wrap", default="0",
help="Configure cell wrapping size. WRAP is an int")
parser.add_argument("-B", "--bound", default="0",
help="Limits tape length to BOUND (BOUND is an int). "
"Default: unbounded")
args = parser.parse_args()
eoftype = args.eof
Brainfuck.execute(args.infile.read(), eoftype, args.wrap,
args.bound)
Sample run (NYYRIKKI's interpreter):
prompt > python bfi.py -w 256 nbfi.b ++++++++++[>+>+++>++++>+++++++>++++++++>+++++++++>++++++++++>+++++++++++>++++++++++++<<<<<<<<<-]>>>>++.>>>+.+++++++..>+.<<<<<<++.>>>>>>>-.<.+++.<.--------.<<<<<+.<+++.---.: Hello world!
A program which I used to test tape pointer wrapping:
[ 0 0 0 0 0 64
^ ]
++[->++<]>[->++<]>[->++<]>[->++<]>[->++<]> #
<<<<<< #.
<<<<<< .
- Output:
prompt > python bfi.py -B 6 test_bound.b 5: [0 0 0 0 0 0 0 0 0 64 ] 5: [0 0 0 0 0 0 0 0 0 64 ] @@
Testing right bound:
[ 0 0 0 0 0 64
^ ]
++[->++<]>[->++<]>[->++<]>[->++<]>[->++<]> #
>>>>>> #.
>>>>>> .
- Output:
Same as above.
Wrapping test:
-- #+++ #
< - >#
- Output:
prompt> python bfi.py -w 256 test.b 0: [0 0 0 0 0 0 0 0 0 254 ] 0: [0 0 0 0 0 0 0 0 0 1 ] 0: [0 0 0 0 0 0 0 0 255 1 ]