Created
June 1, 2020 17:58
-
-
Save julienc91/270b85f0e7ff057a61f393847a1ea5b4 to your computer and use it in GitHub Desktop.
A Brainfuck/Ook/Spoon interpreter in python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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