import sys import re import time class Interpreter: def __init__(self, commands_per_second=1_000_000, debug=False, comma_standart_input=True): self.debug = debug self.commands = ['+', '-', '>', '<', '.', ',', '[', ']'] self.mp = 0 # memory pointer self.pc = 0 # program counter self.data = [0] * 2 ''' chceme mít k dispozici i konec datového pole, (abychom mohli zavolat '<' na 0) -> konec pole budeme mít jako self.data_end a kde jsme, poznáme podle self.is_at_end... pak můžeme podle potřeby zvětšovat pole ze začátku nebo z konce !poznámka autora: dalo by se také udělat s jedním polem, tak že bychom 'posouvali' index 0... to mě ale nenapadlo, když jsem to psal, tak jsem to udělal takhle... ''' self.data_end = [0] * 2 # tail is reversed self.is_at_end = False self.code = "" self.commands_per_second = commands_per_second self.MAX = 2**32 - 1 self.user_input = "" self.user_input_index = 0 self.saved_output = "" self.comma_standart_input = comma_standart_input self.last_step = 0 def our_i32(self, value): if value == -1: return self.MAX if value == self.MAX + 1: return 0 return value def load_code_from_input(self): def expand_numbers(match): count = int(match.group(1)) char = match.group(2) return char * count self.code = "" code_filename = sys.argv[1] with open(code_filename) as code_file: for line in code_file: line = line.strip() line = line.split('//')[0].strip() line = re.sub(r'(\d+)(.)', expand_numbers, line) line = ''.join(filter(lambda c: c in self.commands, line)) self.code += line def load_code_from_string(self, code): def expand_numbers(match): count = int(match.group(1)) char = match.group(2) return char * count self.code = "" code = code.strip() code = re.sub(r'(\d+)(.)', expand_numbers, code) code = ''.join(filter(lambda c: c in self.commands, code)) self.code += code def print_overwrite(self, message, prev_len=[0]): sys.stdout.write('\r' + message + ' ' * max(prev_len[0] - len(message), 0)) sys.stdout.flush() prev_len[0] = len(message) def print_data(self, steps): to_print = "step: " + str(steps) + " " to_print += "mp: " + str(self.mp) + " | " for i in range(len(self.data)): to_print += str(self.data[i]) + " " to_print += "|" to_print += " end: | " for i in range(len(self.data_end)).__reversed__(): to_print += str(self.data_end[i]) + " " to_print += "|" self.print_overwrite(to_print) return to_print def get_value_at_mp(self): if not self.is_at_end: return self.data[self.mp] else: return self.data_end[self.MAX - self.mp] def set_value_at_mp(self, value): if not self.is_at_end: self.data[self.mp] = value else: self.data_end[self.MAX - self.mp] = value def handle_plus(self): self.set_value_at_mp(self.our_i32(self.get_value_at_mp() + 1)) def handle_minus(self): self.set_value_at_mp(self.our_i32(self.get_value_at_mp() - 1)) def handle_greater(self): temp = self.mp self.mp = self.our_i32(self.mp + 1) # kontrola, jesstli jsem ne přeskočil konec if self.mp > self.MAX - len(self.data_end): self.is_at_end = True return if temp > self.mp: self.is_at_end = False # rozšíření datového pole if self.mp >= len(self.data): self.data += [0] * (len(self.data)) def handle_less(self): temp = self.mp self.mp = self.our_i32(self.mp - 1) # kontrola, jestli jsem ne přeskočil začátek if self.mp < len(self.data): self.is_at_end = False return if temp < self.mp: self.is_at_end = True if self.MAX - self.mp >= len(self.data_end): self.data_end += [0] * (len(self.data_end)) def handle_dot(self): sys.stdout.write(chr(self.get_value_at_mp())) sys.stdout.flush() self.saved_output += chr(self.get_value_at_mp()) def handle_comma(self): if self.comma_standart_input: input_char = sys.stdin.read(1) else: if self.user_input_index < len(self.user_input): input_char = self.user_input[self.user_input_index] self.user_input_index += 1 else: input_char = None if not input_char: self.set_value_at_mp(0) else: self.set_value_at_mp(ord(input_char)) def handle_left_bracket(self): if self.data[self.mp] == 0: # jump to the matching ] open_brackets = 1 while open_brackets > 0: self.pc += 1 if self.pc >= len(self.code): raise SyntaxError("Unmatched \"[\"") if self.code[self.pc] == "[": open_brackets += 1 elif self.code[self.pc] == "]": open_brackets -= 1 def handle_right_bracket(self): if self.data[self.mp] != 0: close_brackets = 1 while close_brackets > 0: self.pc -= 1 if self.pc < 0: raise SyntaxError("Unmatched \"]\"") if self.code[self.pc] == "]": close_brackets += 1 elif self.code[self.pc] == "[": close_brackets -= 1 def execute_loaded_code(self): steps = 0 print("|Executing code|") while True: if self.pc >= len(self.code): break com = self.code[self.pc] if com == "+": self.handle_plus() elif com == "-": self.handle_minus() elif com == ">": self.handle_greater() elif com == "<": self.handle_less() elif com == ".": self.handle_dot() elif com == ",": self.handle_comma() elif com == "[": self.handle_left_bracket() elif com == "]": self.handle_right_bracket() # debug print if self.debug: self.print_data(steps) time.sleep(1 / self.commands_per_second) self.pc += 1 steps += 1 self.last_step = steps print("\n|Execution done|") ''' Usage: python interpreter.py < nastavte si DEBUG na True, pokud chcete vidět debugovací výstup, pak ale nebude správně fungovat "."... nastavte si commands_per_second na 2, pokud chcete dva kroky za sekundu ''' DEBUG = True COMMANDS_PER_SECOND = 1000 if __name__ == "__main__": interpreter = Interpreter(commands_per_second=COMMANDS_PER_SECOND, debug=DEBUG) interpreter.load_code_from_input() print("Loaded code:", interpreter.code, "length:", len(interpreter.code)) interpreter.execute_loaded_code()