-
-
Save The-Force/671a27f5d78f42aba246 to your computer and use it in GitHub Desktop.
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
import sys | |
import time | |
def show_halp(): | |
print ('===============================================================================') | |
print ('Python BrainFuck Interpreter by The-Force for MV Coding Katas') | |
print ('') | |
print ('Usage: BrainFuck.py Program_File [Output_File [Input_File]]') | |
print ('') | |
print ('Program_File: file containing the Brainfuck code to be interpreted.') | |
print ('Output_File : file for the program output. If not specified a new file will be created.') | |
print ('Input_File : Optional file containing input for the program.') | |
print ('===============================================================================' ) | |
def get_file_string(file_name): | |
try: | |
file_ = open(file_name, 'r') | |
except IOError: | |
sys.exit('IOERROR: Cant open ' + file_name) | |
else: | |
content = file_.read() | |
file_.close() | |
return content | |
def set_file_string(file_name,content): | |
try: | |
file_ = open(file_name, 'w') | |
except IOError: | |
sys.exit('IOERROR: Cant open ' + file_name) | |
else: | |
file_.write(content) | |
file_.close() | |
class MyError(Exception): | |
def __init__(self, s): | |
self.description = s | |
def __str__(self): | |
return repr(self.description) | |
class Program: | |
def __init__(self, original_code): | |
self.valid = False | |
self.set_code(original_code) | |
def set_code(self,original_code): | |
self.optimized_code = [] | |
self.jump = {} | |
self.op_counter = [] | |
self.original_code = original_code | |
self.preprocess() | |
def preprocess(self): | |
original_code = self.original_code | |
op_alphabet = ['>','<','+','-','.',',','[',']'] | |
op_optimizable = ['>','<','+','-','.',','] | |
op = '' | |
op_last = '' | |
code_ptr = 0 | |
code_size = len(self.original_code) | |
new_ptr = 0 | |
stack = [] | |
op_counter = [] | |
optimized_code = [] | |
jump = {} | |
while code_ptr < code_size: | |
op = original_code[code_ptr] | |
if op in op_alphabet: | |
if op not in op_optimizable: | |
optimized_code.append(ord(op)) | |
op_counter.append(0) | |
op_last = op | |
if op == '[': | |
stack.append(len(optimized_code)-1) | |
elif op == ']': | |
f = stack.pop() | |
jump[f] = len(optimized_code)-1; | |
jump[len(optimized_code)-1] = f; | |
else: | |
if op != op_last: | |
op_last = op | |
new_ptr = len(optimized_code) | |
optimized_code.append(ord(op)) | |
op_counter.append(1) | |
else: | |
op_counter[new_ptr] += 1 | |
code_ptr += 1 | |
if len(stack) > 0: | |
raise MyError('ERROR: Invalid Brainfuck syntax') | |
self.valid = True | |
self.op_counter = op_counter | |
self.optimized_code = optimized_code | |
self.jump = jump | |
def run(self,input_string): | |
if not self.valid: | |
raise MyError('ERROR: Invalid Brainfuck syntax') | |
op_counter = self.op_counter | |
optimized_code = self.optimized_code | |
jump = self.jump | |
value_size = 65536 | |
output_size = 256 | |
array = [0] * 30000 | |
array_size = len(array) | |
array_ptr = 0 | |
input_size = len(input_string) | |
input_ptr = 0 | |
code_size = len(optimized_code) | |
code_ptr = 0 | |
output = [] | |
op = 0 | |
while code_ptr < code_size: | |
op = optimized_code[code_ptr] | |
if op == 62: # > | |
array_ptr += op_counter[code_ptr] | |
if array_ptr >= array_size: | |
array_ptr = array_ptr - array_size | |
elif op == 60: # < | |
array_ptr -= op_counter[code_ptr] | |
if array_ptr < 0: | |
array_ptr = array_size + array_ptr | |
elif op == 43: # + | |
array[array_ptr] += op_counter[code_ptr] | |
if array[array_ptr] >= value_size: | |
array[array_ptr] = array[array_ptr] - value_size | |
elif op == 45: # - | |
array[array_ptr] -= op_counter[code_ptr] | |
if array[array_ptr] < 0: | |
array[array_ptr] = value_size + array[array_ptr] | |
elif op == 93 and array[array_ptr] > 0: # ] | |
code_ptr = jump[code_ptr] | |
elif op == 91 and array[array_ptr] <= 0: # [ | |
code_ptr = jump[code_ptr] | |
elif op == 46: # . | |
for i in range(op_counter[code_ptr]): | |
output.append(chr(array[array_ptr] % output_size)) | |
elif op == 44: # , | |
for i in range(op_counter[code_ptr]): | |
if input_ptr < input_size: | |
array[array_ptr] = ord(input_string[input_ptr]) | |
input_ptr += 1 | |
else: | |
array[array_ptr] = value_size - 1 # 65535 0xFFFF | |
break; | |
code_ptr += 1 | |
return ''.join(output) | |
#============================================================================ | |
start_time = time.time() | |
if len(sys.argv) < 2 or len(sys.argv) > 4: | |
show_halp() | |
sys.exit() | |
program_s = get_file_string(sys.argv[1]) | |
input_s = '' | |
if len(sys.argv) == 4: | |
input_s = get_file_string(sys.argv[3]) | |
try: | |
program = Program(program_s) | |
output_s = program.run(input_s) | |
except MyError as e: | |
sys.exit(e) | |
if len(sys.argv) > 2: | |
set_file_string(sys.argv[2],output_s) | |
else: | |
set_file_string(sys.argv[1] + '.out.txt',output_s) | |
print ('Done in: ' + str(time.time() - start_time) + ' seconds') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment