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