From 3047fa72a6e05233b37a305c6ccb6524b55c8618 Mon Sep 17 00:00:00 2001
From: Chris <Christopher.Happe@uni-duesseldorf.de>
Date: Mon, 21 Dec 2020 09:29:05 +0100
Subject: [PATCH] =?UTF-8?q?Feature:=20Homeomorphic=20Embedding=20f=C3=BCr?=
 =?UTF-8?q?=20Interpreter?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../kapitel-8/Interpreter/gotointerpreter.py  | 17 +++++++--
 .../kapitel-8/Interpreter/loopinterpreter.py  |  3 ++
 .../Interpreter/test_goto_interpreter.py      |  8 ++++
 .../Interpreter/test_while_interpreter.py     |  7 ++++
 .../kapitel-8/Interpreter/whileinterpreter.py | 37 +++++++++++++++++--
 5 files changed, 65 insertions(+), 7 deletions(-)

diff --git a/info4/kapitel-8/Interpreter/gotointerpreter.py b/info4/kapitel-8/Interpreter/gotointerpreter.py
index f8cbb86..5ef4fb6 100644
--- a/info4/kapitel-8/Interpreter/gotointerpreter.py
+++ b/info4/kapitel-8/Interpreter/gotointerpreter.py
@@ -1,6 +1,7 @@
 import lexer
 from loopinterpreter import ErrorHandler
 from whileinterpreter import Timeout
+from whileinterpreter import State
 import re
 import operator
 
@@ -126,6 +127,13 @@ class GOTOInterpreter:
             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)
+        if self.test_homeomorphic_embedding:
+            new_state = State(self.lex.current_position, self.values)
+            for state in self.state_list:
+
+                if state.less_or_equal(new_state):
+                    self.error_handler.handle_endless_loop()
+            self.state_list.append(new_state)
         return self.next_token()
 
     def verify_goto(self, goto_token):
@@ -261,12 +269,14 @@ class GOTOInterpreter:
             current_token = self.verify_halt(current_token)
         return current_token
 
-    def interpret(self, program, values=None):
+    def interpret(self, program, values=None, test_homeomorphic_embedding=False):
         try:
             with Timeout(self.timeout):
                 self.halted = False
                 self.lex = lexer.Lexer(self.regex_to_token, program)
                 self.error_handler = ErrorHandler(program, self)
+                self.state_list = []
+                self.test_homeomorphic_embedding = test_homeomorphic_embedding
                 self.values=values
                 if values is None:
                     self.values = {}
@@ -284,7 +294,7 @@ class GOTOInterpreter:
             return -1
 
 
-def interpret(program, value_list=None, timeout=60):
+def interpret(program, value_list=None, timeout=60, debug=False):
     """Funktion zum Ausführen eines GOTO-Programms.
     :param program: GOTO-Programm als String 'M1: A1; M2: A2; ... Mn: An;'
     :param value_list: Array von Integern ([v1, ..., vn]).
@@ -292,6 +302,7 @@ def interpret(program, value_list=None, timeout=60):
     :param timeout: Zeit nach der die Ausführung eines Programms pausiert wird.
                     Gibt eine Möglichkeit zum Abbrechen bei einer Endlosschleife.
                     Ein Wert von 0 deaktiviert den Timeout.
+    :param debug Boolean der angibt ob mit Homeomorphic Embedding auf mögliche Endlosschleifen geprüft werden soll.
     :returns integer: Gibt bei Abbruch -1 und sonst den Wert von x0 nach dem GOTO-Programm zurück.
     :usage interpret('M1: x0 := x0 + 1; M2: IF x0 = 10 THEN GOTO M4; M3: GOTO M1; M4: HALT;')"""
     interpreter = GOTOInterpreter(timeout)
@@ -302,7 +313,7 @@ def interpret(program, value_list=None, timeout=60):
             if not isinstance(value, int) or value < 0 or not int(value) == value:
                 raise ValueError("Variablen können nur natürliche Zahlen zugewiesen bekommen.")
             values.update({'x' + str(index + 1): value})
-    return interpreter.interpret(program, values)
+    return interpreter.interpret(program, values, debug)
 
 
 if __name__ == '__main__':
diff --git a/info4/kapitel-8/Interpreter/loopinterpreter.py b/info4/kapitel-8/Interpreter/loopinterpreter.py
index 69b8b3e..68823fe 100644
--- a/info4/kapitel-8/Interpreter/loopinterpreter.py
+++ b/info4/kapitel-8/Interpreter/loopinterpreter.py
@@ -31,6 +31,9 @@ class ErrorHandler:
         if user_input.lower() == 'exit':
             raise KeyboardInterrupt
 
+    def handle_endless_loop(self):
+        self.handle_error("Mögliche Endlosschleife gefunden.")
+
 
 class LOOPInterpreter:
     def __init__(self):
diff --git a/info4/kapitel-8/Interpreter/test_goto_interpreter.py b/info4/kapitel-8/Interpreter/test_goto_interpreter.py
index 1f09335..8c11924 100644
--- a/info4/kapitel-8/Interpreter/test_goto_interpreter.py
+++ b/info4/kapitel-8/Interpreter/test_goto_interpreter.py
@@ -206,3 +206,11 @@ class GOTOInterpreterTest(TestCase):
             interpret('M1: x0:=1;', [0.2])
         with self.assertRaises(ValueError):
             interpret('M1: x0:=4;', ['EINS'])
+
+    def test_homeomorphic_embedding(self):
+        with self.assertRaises(SyntaxError):
+            interpret('M1: x1:=1; M2: IF x1 = 0 THEN GOTO M5;'
+                      'M3: x1:=x1+1; M4: GOTO M2; M5: HALT;', timeout=0, debug=True)
+        with self.assertRaises(SyntaxError):
+            interpret('M1: x1:=100; M2: IF x1 = 0 THEN GOTO M5;'
+                      'M3: x1:=x1+1; M4: GOTO M2; M5: HALT;', timeout=0, debug=True)
diff --git a/info4/kapitel-8/Interpreter/test_while_interpreter.py b/info4/kapitel-8/Interpreter/test_while_interpreter.py
index 01fbedf..3fab6da 100644
--- a/info4/kapitel-8/Interpreter/test_while_interpreter.py
+++ b/info4/kapitel-8/Interpreter/test_while_interpreter.py
@@ -133,3 +133,10 @@ class WHILEInterpreterTest(LOOPInterpreterTest):
             interpret('x0:=1', [0.2])
         with self.assertRaises(ValueError):
             interpret('x0:=4', ['EINS'])
+
+    def test_homeomorphic_embedding(self):
+        with self.assertRaises(SyntaxError):
+            interpret('x1:=1; WHILE x1 != 0 DO x1:=x1+1 END', timeout=0, debug=True)
+        with self.assertRaises(SyntaxError):
+            interpret('''x1:=100; WHILE x1 != 0 DO x1:=x1-1 END;
+                         x1 = 1; WHILE x1 != 0 DO x1:=x1+1 END''', timeout=0, debug=True)
diff --git a/info4/kapitel-8/Interpreter/whileinterpreter.py b/info4/kapitel-8/Interpreter/whileinterpreter.py
index 407d5e5..d5121ca 100644
--- a/info4/kapitel-8/Interpreter/whileinterpreter.py
+++ b/info4/kapitel-8/Interpreter/whileinterpreter.py
@@ -3,7 +3,7 @@ from loopinterpreter import LOOPInterpreter, ErrorHandler
 import re
 import signal
 from IPython.display import clear_output
-
+from copy import deepcopy
 
 class Timeout:
     def __init__(self, time):
@@ -29,6 +29,25 @@ Möchten sie abbrechen? [J,n]:''')
         except EOFError:
             pass
 
+class State:
+    def __init__(self, linenumber, values):
+        self.values = deepcopy(values)
+        self.linenumber = linenumber
+
+    def less_or_equal(self, other):
+        if self.linenumber > other.linenumber:
+            return False
+        values1 = self.values
+        values2 = other.values
+        different_keys = [k for k in values1 if values1.get(k) != values2.get(k)] +\
+                         [k for k in values2 if values1.get(k) is None]
+
+        for key in different_keys:
+            if values1.get(key) > 0 and (values2.get(key) is None or values2.get(key) < values1.get(key)):
+                return False
+
+        return True
+
 
 class WHILEInterpreter(LOOPInterpreter):
     def __init__(self, timeout=60):
@@ -112,6 +131,13 @@ class WHILEInterpreter(LOOPInterpreter):
                     end_found = True
 
         while not while_value == 0:
+            if self.test_homeomorphic_embedding:
+                new_state = State(self.lex.current_position, self.values)
+                for state in self.state_list:
+                    if state.less_or_equal(new_state):
+                        self.error_handler.handle_endless_loop()
+                self.state_list.append(new_state)
+
             self.lex.current_position = saved_position
             self.error_handler.line_number = saved_line
             end_found = False
@@ -159,11 +185,13 @@ class WHILEInterpreter(LOOPInterpreter):
 
         return self.next_token()
 
-    def interpret(self, program, values=None):
+    def interpret(self, program, values=None, test_homeomorphic_embedding=False):
         try:
             with Timeout(self.timeout):
                 self.lex = lexer.Lexer(self.regex_to_token, program)
                 self.error_handler = ErrorHandler(program, self)
+                self.test_homeomorphic_embedding = test_homeomorphic_embedding
+                self.state_list = []
                 self.values = values
                 if values is None:
                     self.values = {}
@@ -187,7 +215,7 @@ class WHILEInterpreter(LOOPInterpreter):
             return -1
 
 
-def interpret(program, value_list=None, timeout=60):
+def interpret(program, value_list=None, timeout=60, debug=False):
     """Funktion zum Ausführen eines WHILE-Programms.
     :param program: WHILE-Programm als String 'x1:=10; x2:=8; x0:=x2+0; WHILE x1 /=0 DO x0:=x0+1; x1:=x1-1 END'
     :param value_list: Array von Integern ([v1, ..., vn]).
@@ -195,6 +223,7 @@ def interpret(program, value_list=None, timeout=60):
     :param timeout: Zeit nach der die Ausführung eines Programms pausiert wird.
                     Gibt eine Möglichkeit zum Abbrechen bei einer Endlosschleife.
                     Ein Wert von 0 deaktiviert den Timeout.
+    :param debug: Boolean der angibt ob mit Homeomorphic Embedding auf mögliche Endlosschleifen geprüft werden soll.
     :returns integer: Gibt bei Abbruch -1 und sonst den Wert von x0 nach dem WHILE-Programm zurück.
     :usage interpret('x1:=10; x2:=8; x0:=x2+0; WHILE x1 /=0 DO x0:=x0+1; x1:=x1-1 END')"""
     interpreter = WHILEInterpreter(timeout)
@@ -205,7 +234,7 @@ def interpret(program, value_list=None, timeout=60):
             if not isinstance(value, int) or value < 0 or not int(value) == value:
                 raise ValueError("Variablen können nur natürliche Zahlen zugewiesen bekommen.")
             values.update({'x' + str(index+1): value})
-    return interpreter.interpret(program, values)
+    return interpreter.interpret(program, values, debug)
 
 
 if __name__ == '__main__':
-- 
GitLab