import lexer from loopinterpreter import ErrorHandler from whileinterpreter import Timeout import re import operator class GOTOInterpreter: def __init__(self, timeout=60): self.values = {} self.lex = None self.error_handler = None self.regex_to_token = [(re.compile(r'\d+'), 'NUMBER'), (re.compile(r'x\d+'), 'IDENTIFIER'), (re.compile(r'M\d+'), 'MARKER'), (re.compile(r'\+'), 'PLUS'), (re.compile(r'[−-]'), 'MINUS'), (re.compile(r':=|≔'), 'ALLOCATION'), (re.compile(r'='), 'EQUALS'), (re.compile(r'/=|≠|!='), 'NOTEQUALS'), (re.compile(r'IF'), 'IF'), (re.compile(r'THEN'), 'THEN'), (re.compile(r'GOTO'), 'GOTO'), (re.compile(r'HALT'), 'HALT'), (re.compile(r';'), 'SEMICOLON'), (re.compile(r':'), 'COLON'), (re.compile(r'BREAK'), 'BREAK'), (re.compile(r'\s+', re.MULTILINE), 'WHITESPACE'), (re.compile(r'[^\n]*'), 'UNKNOWN')] self.marker_to_position = {} self.marker_to_line = {} self.timeout = timeout self.halted = False def next_token(self): new_token = self.lex.next() if new_token is None: return None elif new_token.k == 'BREAK': self.error_handler.handle_break() return self.next_token() elif new_token.k == 'WHITESPACE': if new_token.v.count('\n') > 0: self.error_handler.increase_line(new_token.v.count('\n')) return self.next_token() else: return new_token def next_nonempty_token(self, current_function, expected_token): token = self.next_token() if token is None: self.error_handler.handle_error( 'Frühzeitiges Ende von ' + current_function + '\n' + 'Erwartet: ' + expected_token) return token def process_assignment(self, identifier_token_1): identifier_1 = identifier_token_1.v if not self.next_nonempty_token('Zuweisung', ':=').k == 'ALLOCATION': self.error_handler.handle_error(':= in Zuweisung erwartet.') identifier_token_2 = self.next_nonempty_token('Zuweisung', 'IDENTIFIER (x0, x1, ...) oder NUMBER') if identifier_token_2.k == 'NUMBER': value_1 = int(identifier_token_2.v) self.values.update({identifier_token_1.v: value_1}) if not self.next_nonempty_token('Zuweisung', 'SEMICOLON').k == 'SEMICOLON': self.error_handler.handle_error('SEMICOLON zum Abschluss einer Zuweisung erwartet.') return self.next_token() if not identifier_token_2.k == 'IDENTIFIER': self.error_handler.handle_error('IDENTIFIER in Zuweisung erwartet.') identifier_2 = identifier_token_2.v if identifier_2 in self.values: value_2 = self.values.get(identifier_2) else: value_2 = 0 operator_token = self.next_nonempty_token('Zuweisung', '+ oder -') op = None if operator_token.k == 'PLUS': op = operator.__add__ elif operator_token.k == 'MINUS': op = operator.__sub__ else: self.error_handler.handle_error('+ oder - in Zuweisung erwartet.') number_token = self.next_nonempty_token('Zuweisung', 'NUMBER') if not number_token.k == 'NUMBER': self.error_handler.handle_error('NUMBER in Zuweisung erwartet.') value_1 = max(0, op(value_2, int(number_token.v))) self.values.update({identifier_1: value_1}) if not self.next_nonempty_token('Zuweisung', 'SEMICOLON').k == 'SEMICOLON': self.error_handler.handle_error('SEMICOLON zum Abschluss einer Zuweisung erwartet.') return self.next_token() def verify_assignment(self, identifier_token_1): if not self.next_nonempty_token('Zuweisung', ':=').k == 'ALLOCATION': self.error_handler.handle_error(':= in Zuweisung erwartet.') identifier_token_2 = self.next_nonempty_token('Zuweisung', 'IDENTIFIER (x0, x1, ...) oder NUMBER') if identifier_token_2.k == 'NUMBER': if not self.next_nonempty_token('Zuweisung', 'SEMICOLON').k == 'SEMICOLON': self.error_handler.handle_error('SEMICOLON zum Abschluss einer Zuweisung erwartet.') return self.next_token() if not identifier_token_2.k == 'IDENTIFIER': self.error_handler.handle_error('IDENTIFIER in Zuweisung erwartet.') if self.next_nonempty_token('Zuweisung', '+ oder -').k not in ['PLUS', 'MINUS']: self.error_handler.handle_error('+ oder - in Zuweisung erwartet.') if not self.next_nonempty_token('Zuweisung', 'NUMBER').k == 'NUMBER': self.error_handler.handle_error('NUMBER in Zuweisung erwartet.') if not self.next_nonempty_token('Zuweisung', 'SEMICOLON').k == 'SEMICOLON': self.error_handler.handle_error('SEMICOLON zum Abschluss einer Zuweisung erwartet.') return self.next_token() def process_goto(self, goto_token): marker_token = self.next_nonempty_token('GOTO', 'MARKER (M1, M2, ...)') if not marker_token.k == 'MARKER': self.error_handler.handle_error('MARKER (M1, M2, ...) in GOTO erwartet.') if not self.next_nonempty_token('GOTO', 'SEMICOLON').k == 'SEMICOLON': self.error_handler.handle_error('SEMICOLON zum Abschluss eines GOTO erwartet') marker_number = int(marker_token.v[1:]) current_token = self.next_token() while current_token is not None and max(self.marker_to_position.keys()) < int(marker_number): current_token = self.verify_line(current_token) self.lex.current_position = self.marker_to_position.get(marker_number) self.error_handler.line_number = self.marker_to_line.get(marker_number) if marker_number not in self.marker_to_position.keys(): self.error_handler.handle_error('GOTO zu nicht vorhandener Markierung') return self.next_token() def verify_goto(self, goto_token): marker_token = self.next_nonempty_token('GOTO', 'MARKER (M1, M2, ...)') if not marker_token.k == 'MARKER': self.error_handler.handle_error('MARKER (M1, M2, ...) in GOTO erwartet.') if not self.next_nonempty_token('GOTO', 'SEMICOLON').k == 'SEMICOLON': self.error_handler.handle_error('SEMICOLON zum Abschluss eines GOTO erwartet') marker_number = int(marker_token.v[1:]) saved_position = self.lex.current_position saved_line = self.error_handler.line_number current_token = self.next_token() while current_token is not None and max(self.marker_to_position.keys()) < int(marker_number): current_token = self.verify_line(current_token) self.lex.current_position = saved_position self.error_handler.line_number = saved_line if marker_number not in self.marker_to_position.keys(): self.error_handler.handle_error('GOTO zu nicht vorhandener Markierung') return self.next_token() def process_if(self, if_token): identifier_token = self.next_nonempty_token('IF', 'IDENTIFIER') if not identifier_token.k == 'IDENTIFIER': self.error_handler.handle_error('IDENTIFIER in IF erwartet') if not self.next_nonempty_token('IF', '=').k == 'EQUALS': self.error_handler.handle_error('= in IF erwartet') number_token = self.next_nonempty_token('IF', 'NUMBER') if not number_token.k == 'NUMBER': self.error_handler.handle_error('NUMBER in IF erwartet') if not self.next_nonempty_token('IF', 'THEN').k == 'THEN': self.error_handler.handle_error('THEN in IF erwartet') current_token = self.next_nonempty_token('IF', 'GOTO') if not current_token.k == 'GOTO': self.error_handler.handle_error('GOTO in IF erwartet') if identifier_token.v in self.values.keys(): identifier_value = self.values.get(identifier_token.v) else: identifier_value = 0 if identifier_value == int(number_token.v): return self.process_goto(current_token) else: return self.verify_goto(current_token) def verify_if(self, if_token): if not self.next_nonempty_token('IF', 'IDENTIFIER').k == 'IDENTIFIER': self.error_handler.handle_error('IDENTIFIER in IF erwartet') if not self.next_nonempty_token('IF', '=').k == 'EQUALS': self.error_handler.handle_error('= in IF erwartet') if not self.next_nonempty_token('IF', 'NUMBER').k == 'NUMBER': self.error_handler.handle_error('NUMBER in IF erwartet') if not self.next_nonempty_token('IF', 'THEN').k == 'THEN': self.error_handler.handle_error('THEN in IF erwartet') current_token = self.next_nonempty_token('IF', 'GOTO') if not current_token.k == 'GOTO': self.error_handler.handle_error('GOTO in IF erwartet') return self.verify_goto(current_token) def process_halt(self, halt_token): if not self.next_nonempty_token('HALT', 'SEMICOLON').k == 'SEMICOLON': self.error_handler.handle_error('SEMICOLON zum Abschluss eines HALT erwartet') self.halted = True current_token = self.next_token() while current_token is not None: current_token = self.verify_line(current_token) return current_token def verify_halt(self, halt_token): if not self.next_nonempty_token('HALT', 'SEMICOLON').k == 'SEMICOLON': self.error_handler.handle_error('SEMICOLON zum Abschluss eines HALT erwartet') return self.next_token() def next_marker_number(self): if self.marker_to_position.keys(): return max(self.marker_to_position.keys()) + 1 else: return 1 def process_line(self, current_token): if current_token is None or not current_token.k == 'MARKER': self.error_handler.handle_error('Keine passende Anweisung gefunden\n' + 'Erwartet: MARKER (M1, M2, ...)') marker_number = int(current_token.v[1:]) if not (marker_number == self.next_marker_number() or (marker_number in self.marker_to_position.keys() and current_token.p == self.marker_to_position.get(marker_number))): self.error_handler.handle_error('Die Nummern der Anweisungen starten bei 1 und steigen fortlaufend.') self.marker_to_position.update({marker_number: current_token.p}) self.marker_to_line.update({marker_number: self.error_handler.line_number}) colon_token = self.next_nonempty_token('LINE', 'COLON') if not colon_token.k == 'COLON': self.error_handler.handle_error('DOPPELPUNKT nach MARKER (M1, M2, ...) erwartet.') current_token = self.next_nonempty_token('LINE', 'IDENTIFIER (x0, x1, ...), IF, GOTO oder HALT') if current_token.k not in ['IDENTIFIER', 'IF', 'GOTO', 'HALT']: self.error_handler.handle_error('Keine passende Anweisung gefunden\n' + 'Erwartet: IDENTIFIER (x0, x1, ...), IF, GOTO oder HALT') elif current_token.k == 'IDENTIFIER': current_token = self.process_assignment(current_token) elif current_token.k == 'GOTO': current_token = self.process_goto(current_token) elif current_token.k == 'IF': current_token = self.process_if(current_token) elif current_token.k == 'HALT': current_token = self.process_halt(current_token) return current_token def verify_line(self, current_token): if current_token is None or not current_token.k == 'MARKER': self.error_handler.handle_error('Keine passende Anweisung gefunden\n' + 'Erwartet: MARKER (M1, M2, ...)') marker_number = int(current_token.v[1:]) if not (marker_number == self.next_marker_number() or (marker_number in self.marker_to_position.keys() and current_token.p == self.marker_to_position.get(marker_number))): self.error_handler.handle_error('Die Nummern der Anweisungen starten bei 1 und steigen fortlaufend.') self.marker_to_position.update({marker_number: current_token.p}) self.marker_to_line.update({marker_number: self.error_handler.line_number}) if not self.next_nonempty_token('LINE', 'COLON').k == 'COLON': self.error_handler.handle_error('DOPPELPUNKT nach MARKER (M1, M2, ...) erwartet.') current_token = self.next_nonempty_token('LINE', 'IDENTIFIER (x0, x1, ...), IF, GOTO oder HALT') if current_token.k not in ['IDENTIFIER', 'IF', 'GOTO', 'HALT']: self.error_handler.handle_error('Keine passende Anweisung gefunden\n' + 'Erwartet: IDENTIFIER (x0, x1, ...), IF, GOTO oder HALT') elif current_token.k == 'IDENTIFIER': current_token = self.verify_assignment(current_token) elif current_token.k == 'GOTO': current_token = self.verify_goto(current_token) elif current_token.k == 'IF': current_token = self.verify_if(current_token) elif current_token.k == 'HALT': current_token = self.verify_halt(current_token) return current_token def interpret(self, program): try: with Timeout(self.timeout): self.halted = False self.lex = lexer.Lexer(self.regex_to_token, program) self.error_handler = ErrorHandler(program, self) self.values = {} current_token = self.next_token() while current_token is not None: current_token = self.process_line(current_token) if not self.halted: self.error_handler.handle_error('Ende des Programms ohne HALT erreicht.') if 'x0' in self.values: return self.values.get('x0') return 0 except KeyboardInterrupt: print('Die Ausführung des Programms wurde unterbrochen.\n' + 'Daher ist der Rückgabewert des Programms nicht definiert.') return -1 def interpret(program, timeout=60): interpreter = GOTOInterpreter(timeout) return interpreter.interpret(program)