From f17c278f81b8efcb300eec97b43459f37245e048 Mon Sep 17 00:00:00 2001 From: Chris <Christopher.Happe@uni-duesseldorf.de> Date: Mon, 23 Nov 2020 09:40:26 +0100 Subject: [PATCH] =?UTF-8?q?Interpreter=20f=C3=BCr=20GOTO-Programme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kapitel-8/Interpreter/gotointerpreter.py | 281 ++++++++++++++++++ 1 file changed, 281 insertions(+) create mode 100644 info4/kapitel-8/Interpreter/gotointerpreter.py diff --git a/info4/kapitel-8/Interpreter/gotointerpreter.py b/info4/kapitel-8/Interpreter/gotointerpreter.py new file mode 100644 index 0000000..30ff6e0 --- /dev/null +++ b/info4/kapitel-8/Interpreter/gotointerpreter.py @@ -0,0 +1,281 @@ +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) + if marker_number not in self.marker_to_position.keys(): + self.error_handler.handle_error('GOTO zu nicht vorhandener Markierung') + self.lex.current_position = self.marker_to_position.get(marker_number) + self.error_handler.line_number = self.marker_to_line.get(marker_number) + 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) + if marker_number not in self.marker_to_position.keys(): + self.error_handler.handle_error('GOTO zu nicht vorhandener Markierung') + self.lex.current_position = saved_position + self.error_handler.line_number = saved_line + 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: + return -1 + + +def interpret(program, timeout=60): + interpreter = GOTOInterpreter(timeout) + return interpreter.interpret(program) -- GitLab