Skip to content

Instantly share code, notes, and snippets.

@The-Force
Last active December 12, 2015 09:19
Show Gist options
  • Save The-Force/671a27f5d78f42aba246 to your computer and use it in GitHub Desktop.
Save The-Force/671a27f5d78f42aba246 to your computer and use it in GitHub Desktop.
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