Skip to content

Instantly share code, notes, and snippets.

@julienc91
Created June 1, 2020 17:58
Show Gist options
  • Save julienc91/270b85f0e7ff057a61f393847a1ea5b4 to your computer and use it in GitHub Desktop.
Save julienc91/270b85f0e7ff057a61f393847a1ea5b4 to your computer and use it in GitHub Desktop.
A Brainfuck/Ook/Spoon interpreter in python
#!/usr/bin/env python3
import sys
from collections import defaultdict
class AbstractInterpreter:
"""
This is a very basic Brainfuck interpreter
"""
separator = ""
def __init__(self, code):
self.instructions = []
self.instructions_index = 0
self.cursor = 0
self.memory = defaultdict(int)
self.parse_code(code)
@property
def current_instruction(self):
return self.instructions[self.instructions_index]
@property
def current_value(self):
return self.memory[self.cursor]
@current_value.setter
def current_value(self, value):
self.memory[self.cursor] = value
@property
def mapping(self):
raise NotImplementedError
@property
def start_condition_instruction(self):
for k, v in self.mapping.items():
if v == self.start_condition:
return k
@property
def end_condition_instruction(self):
for k, v in self.mapping.items():
if v == self.end_condition:
return k
def parse_code(self, code):
self.instructions = []
instruction = ""
i = 0
while i < len(code):
c = code[i]
instruction += c
if instruction in self.mapping:
self.instructions.append(instruction)
instruction = ""
if self.separator:
try:
if code[i + 1:i + 1 + len(self.separator)] == self.separator:
i += len(self.separator)
except IndexError:
pass
i += 1
def dispatch(self, instruction):
"""
Call the function associated to the current instruction
"""
self.mapping[instruction]()
def increment(self):
self.current_value += 1
self.instructions_index += 1
def decrement(self):
self.current_value -= 1
self.instructions_index += 1
def increment_cursor(self):
self.cursor += 1
self.instructions_index += 1
def decrement_cursor(self):
self.cursor -= 1
self.instructions_index += 1
def read(self):
res = chr(self.current_value)
self.instructions_index += 1
sys.stdout.write(res)
return res
def write(self):
c = sys.stdin.read(1)
self.current_value = ord(c)
self.instructions_index += 1
def start_condition(self):
self.instructions_index += 1
if self.current_value != 0:
return
# find the related "]" and place the cursor just after
count = 0
while count >= 0:
instruction = self.current_instruction
if instruction == self.start_condition_instruction:
count += 1
elif instruction == self.end_condition_instruction:
count -= 1
self.instructions_index += 1
def end_condition(self):
if self.current_value == 0:
self.instructions_index += 1
return
# find the related "[" and place the cursor just after
count = 0
while count >= 0:
self.instructions_index -= 1
instruction = self.current_instruction
if instruction == self.end_condition_instruction:
count += 1
elif instruction == self.start_condition_instruction:
count -= 1
self.instructions_index += 1
def run(self):
while True:
try:
instruction = self.current_instruction
except IndexError:
return
try:
self.dispatch(instruction)
except Exception as e:
print(self.instructions_index, instruction)
raise e
sys.stderr.write("RuntimeError: {}\n".format(e))
sys.exit(2)
class BrainfuckInterpreter(AbstractInterpreter):
@property
def mapping(self):
return {
">": self.increment_cursor,
"<": self.decrement_cursor,
"+": self.increment,
"-": self.decrement,
".": self.read,
",": self.write,
"[": self.start_condition,
"]": self.end_condition
}
class OokInterpreter(AbstractInterpreter):
separator = " "
@property
def mapping(self):
return {
"Ook. Ook?": self.increment_cursor,
"Ook? Ook.": self.decrement_cursor,
"Ook. Ook.": self.increment,
"Ook! Ook!": self.decrement,
"Ook! Ook.": self.read,
"Ook. Ook!": self.write,
"Ook! Ook?": self.start_condition,
"Ook? Ook!": self.end_condition,
"Ook? Ook?": self.terminate
}
def terminate(self):
self.instructions_index = len(self.instructions)
class SpoonInterpreter(AbstractInterpreter):
@property
def mapping(self):
return {
"010": self.increment_cursor,
"011": self.decrement_cursor,
"1": self.increment,
"000": self.decrement,
"001010": self.read,
"0010110": self.write,
"00100": self.start_condition,
"0011": self.end_condition,
"00101110": self.read_all,
"00101111": self.terminate
}
def terminate(self):
self.instructions_index = len(self.instructions)
def read_all(self):
save_cursor = self.cursor
self.cursor = 0
value = ""
while self.cursor in self.memory:
value += self.read()
self.cursor = save_cursor
return value
if __name__ == "__main__":
hello_world = (
"++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++"
".<<+++++++++++++++.>.+++.------.--------.>+.>."
)
interpreter = BrainfuckInterpreter(hello_world)
interpreter.run()
hello_world = (
"Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. "
"Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook. Ook? Ook. Ook. "
"Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. "
"Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. "
"Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook. Ook. Ook. "
"Ook. Ook. Ook. Ook. Ook? Ook. Ook. Ook? Ook. Ook? Ook. Ook? Ook. "
"Ook? Ook. Ook! Ook! Ook? Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook! "
"Ook. Ook. Ook? Ook. Ook. Ook! Ook. Ook. Ook. Ook. Ook. Ook. Ook. "
"Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook! Ook. Ook. "
"Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook. Ook? Ook. Ook. Ook. Ook. "
"Ook! Ook. Ook? Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. "
"Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. "
"Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook. "
"Ook? Ook! Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook! Ook! "
"Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook. Ook! "
"Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! "
"Ook! Ook! Ook! Ook. Ook. Ook? Ook. Ook. Ook! Ook. Ook. Ook? Ook! "
"Ook. Ook? Ook?"
)
interpreter = OokInterpreter(hello_world)
interpreter.run()
hello_world = (
"11111111110010001011111110101111111111010111010101101101101100000"
"11010110010100101001010111111100101000101011100101001011001010011"
"01111111111111111100101001000101011100101000000000000000000000101"
"00000000000000000000000000010100101001010010001010"
)
interpreter = SpoonInterpreter(hello_world)
interpreter.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment