This commit is contained in:
Rupus Reinefjord 2017-09-07 23:45:50 +02:00
parent adeec90d45
commit 4dfa039c1e
4 changed files with 164 additions and 13 deletions

123
python/bf.py Executable file
View file

@ -0,0 +1,123 @@
#!/usr/bin/python3
import collections
import ctypes
import re
import sys
class Mixin:
def __add__(self, other):
return self.value + other
def __sub__(self, other):
return self.value - other
def __radd__(self, other):
return self.value + other
def __rsub__(self, other):
return self.value - other
def __iadd__(self, other):
self.value += other
return self
def __isub__(self, other):
self.value -= other
return self
def __eq__(self, other):
return self.value == other
def __hash__(self):
return hash(self.value)
class Byte(Mixin, ctypes.c_byte):
pass
class UByte(Mixin, ctypes.c_ubyte):
pass
Int = lambda x: x
TYPE = Int
class Machine:
def __init__(self, program):
self.program = program
cells = {}
self.cells = collections.defaultdict(lambda: TYPE(0), cells)
self.data_pointer = TYPE(0)
self.instruction_pointer = 0
self.command_map = {
'>': self.increment_data_pointer,
'<': self.decrement_data_pointer,
'+': self.increment_cell,
'-': self.decrement_cell,
'.': self.print_cell,
',': self.set_cell,
'[': self.loop_start,
']': self.loop_end
}
def increment_data_pointer(self):
self.data_pointer += 1
def decrement_data_pointer(self):
self.data_pointer -= 1
def increment_cell(self):
self.cells[self.data_pointer] += 1
def decrement_cell(self):
self.cells[self.data_pointer] -= 1
def print_cell(self):
print(chr(self.cells[self.data_pointer]), end='')
def set_cell(self):
self.cells[self.data_pointer] = TYPE(ord(input('=> ')))
def loop_start(self):
if self.cells[self.data_pointer] == 0:
loop_starts = 0
self.instruction_pointer += 1
instruction = self.program[self.instruction_pointer]
while loop_starts > 0 or instruction != ']':
if instruction == '[':
loop_starts += 1
if instruction == ']':
loop_starts -= 1
self.instruction_pointer += 1
instruction = self.program[self.instruction_pointer]
def loop_end(self):
if self.cells[self.data_pointer] != 0:
loop_ends = 0
self.instruction_pointer -= 1
instruction = self.program[self.instruction_pointer]
while loop_ends > 0 or instruction != '[':
if instruction == ']':
loop_ends += 1
if instruction == '[':
loop_ends -= 1
self.instruction_pointer -= 1
instruction = self.program[self.instruction_pointer]
def run(self):
while self.instruction_pointer < len(self.program):
self.command_map[self.program[self.instruction_pointer]]()
self.instruction_pointer += 1
if __name__ == "__main__":
with open(sys.argv[1], 'r') as f:
program = re.findall(f"[{re.escape('<>+-.,[]')}]", f.read())
machine = Machine(program)
machine.run()
print("\nExiting...")

189
python/bf2.py Executable file
View file

@ -0,0 +1,189 @@
#!/usr/bin/python3
import collections
import ctypes
import re
import sys
class Mixin:
"""Mixin for allowing addition, subtraction, and hashing.
ctypes.c_* has add/sub, but only via <type>.value. To make switching
between builtin int and ctypes easy, we add it to the object itself.
They are also not hashable, which is needed for
collections.defaultdict().
"""
def __add__(self, other):
return self.value + other
def __radd__(self, other):
return self.value + other
def __iadd__(self, other):
self.value += other
return self
def __sub__(self, other):
return self.value - other
def __rsub__(self, other):
return self.value - other
def __isub__(self, other):
self.value -= other
return self
def __eq__(self, other):
return self.value == other
def __hash__(self):
return hash(self.value)
class Byte(Mixin, ctypes.c_byte):
pass
class UByte(Mixin, ctypes.c_ubyte):
pass
# Make int callable, syntax-wise works like initializing the ctypes.
Int = lambda x: x
# Set to Byte, UByte, or int.
TYPE = Int
class State:
# Slots increase performance with the drawback of not being able to
# add attributes later, which we do not.
__slots__ = ('cells', 'data_pointer', 'instruction_pointer')
def __init__(self):
# Cells default to 0.
self.cells = collections.defaultdict(lambda: TYPE(0), {})
self.data_pointer = TYPE(0)
self.instruction_pointer = 0
class Token:
def __init__(self, instruction_num, state):
self.instruction_num = instruction_num
self.state = state
def exec(self):
raise NotImplementedError
def __str__(self):
return f"{self.instruction_num}: {self.__class__.__name__}"
class IncDataPointer(Token):
def exec(self):
self.state.data_pointer += 1
class DecDataPointer(Token):
def exec(self):
self.state.data_pointer -= 1
class IncCell(Token):
def exec(self):
self.state.cells[self.state.data_pointer] += 1
class DecCell(Token):
def exec(self):
self.state.cells[self.state.data_pointer] -= 1
class PrintCell(Token):
# Could make Int a real class and add a .value, but I like this hack.
if TYPE is Int:
def exec(self):
print(chr(self.state.cells[self.state.data_pointer]), end='')
else:
def exec(self):
print(chr(self.state.cells[self.state.data_pointer].value), end='')
class SetCell(Token):
def exec(self):
self.state.cells[self.state.data_pointer] = ord(input('=> '))
class Loop(Token):
def __init__(self, *args, **kwargs):
self.other = None
super().__init__(*args, **kwargs)
def __str__(self):
return f"{self.instruction_num}: {self.__class__.__name__}, other: {self.other.instruction_num}"
class LoopStart(Loop):
def exec(self):
if self.state.cells[self.state.data_pointer] == 0:
self.state.instruction_pointer = self.other.instruction_num
class LoopEnd(Loop):
def exec(self):
if self.state.cells[self.state.data_pointer] != 0:
self.state.instruction_pointer = self.other.instruction_num
def parse(program, state):
token_map = {
'>': IncDataPointer,
'<': DecDataPointer,
'+': IncCell,
'-': DecCell,
'.': PrintCell,
',': SetCell,
'[': LoopStart,
']': LoopEnd
}
token_list = []
loops = []
for instruction_num, char in enumerate(program):
token = token_map[char](instruction_num, state)
token_list.append(token)
if char == '[':
loops.append(token)
elif char == ']':
loop_start = loops.pop()
loop_start.other = token
token.other = loop_start
return token_list
def run(state, token_list):
while state.instruction_pointer < len(token_list):
token_list[state.instruction_pointer].exec()
state.instruction_pointer += 1
if __name__ == "__main__":
if len(sys.argv) > 1:
with open(sys.argv[1], 'r') as f:
data = f.read()
else:
data = sys.stdin.read()
program = re.findall(f"[{re.escape('<>+-.,[]')}]", data)
state = State()
token_list = parse(program, state)
run(state, token_list)
print("\nExiting...")