Commit 3047fa72 authored by Christopher Happe's avatar Christopher Happe
Browse files

Feature: Homeomorphic Embedding für Interpreter

parent 98d1fdca
import lexer import lexer
from loopinterpreter import ErrorHandler from loopinterpreter import ErrorHandler
from whileinterpreter import Timeout from whileinterpreter import Timeout
from whileinterpreter import State
import re import re
import operator import operator
...@@ -126,6 +127,13 @@ class GOTOInterpreter: ...@@ -126,6 +127,13 @@ class GOTOInterpreter:
self.error_handler.handle_error('GOTO zu nicht vorhandener Markierung') self.error_handler.handle_error('GOTO zu nicht vorhandener Markierung')
self.lex.current_position = self.marker_to_position.get(marker_number) self.lex.current_position = self.marker_to_position.get(marker_number)
self.error_handler.line_number = self.marker_to_line.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() return self.next_token()
def verify_goto(self, goto_token): def verify_goto(self, goto_token):
...@@ -261,12 +269,14 @@ class GOTOInterpreter: ...@@ -261,12 +269,14 @@ class GOTOInterpreter:
current_token = self.verify_halt(current_token) current_token = self.verify_halt(current_token)
return current_token return current_token
def interpret(self, program, values=None): def interpret(self, program, values=None, test_homeomorphic_embedding=False):
try: try:
with Timeout(self.timeout): with Timeout(self.timeout):
self.halted = False self.halted = False
self.lex = lexer.Lexer(self.regex_to_token, program) self.lex = lexer.Lexer(self.regex_to_token, program)
self.error_handler = ErrorHandler(program, self) self.error_handler = ErrorHandler(program, self)
self.state_list = []
self.test_homeomorphic_embedding = test_homeomorphic_embedding
self.values=values self.values=values
if values is None: if values is None:
self.values = {} self.values = {}
...@@ -284,7 +294,7 @@ class GOTOInterpreter: ...@@ -284,7 +294,7 @@ class GOTOInterpreter:
return -1 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. """Funktion zum Ausführen eines GOTO-Programms.
:param program: GOTO-Programm als String 'M1: A1; M2: A2; ... Mn: An;' :param program: GOTO-Programm als String 'M1: A1; M2: A2; ... Mn: An;'
:param value_list: Array von Integern ([v1, ..., vn]). :param value_list: Array von Integern ([v1, ..., vn]).
...@@ -292,6 +302,7 @@ def interpret(program, value_list=None, timeout=60): ...@@ -292,6 +302,7 @@ def interpret(program, value_list=None, timeout=60):
:param timeout: Zeit nach der die Ausführung eines Programms pausiert wird. :param timeout: Zeit nach der die Ausführung eines Programms pausiert wird.
Gibt eine Möglichkeit zum Abbrechen bei einer Endlosschleife. Gibt eine Möglichkeit zum Abbrechen bei einer Endlosschleife.
Ein Wert von 0 deaktiviert den Timeout. 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. :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;')""" :usage interpret('M1: x0 := x0 + 1; M2: IF x0 = 10 THEN GOTO M4; M3: GOTO M1; M4: HALT;')"""
interpreter = GOTOInterpreter(timeout) interpreter = GOTOInterpreter(timeout)
...@@ -302,7 +313,7 @@ def interpret(program, value_list=None, timeout=60): ...@@ -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: if not isinstance(value, int) or value < 0 or not int(value) == value:
raise ValueError("Variablen können nur natürliche Zahlen zugewiesen bekommen.") raise ValueError("Variablen können nur natürliche Zahlen zugewiesen bekommen.")
values.update({'x' + str(index + 1): value}) values.update({'x' + str(index + 1): value})
return interpreter.interpret(program, values) return interpreter.interpret(program, values, debug)
if __name__ == '__main__': if __name__ == '__main__':
......
...@@ -31,6 +31,9 @@ class ErrorHandler: ...@@ -31,6 +31,9 @@ class ErrorHandler:
if user_input.lower() == 'exit': if user_input.lower() == 'exit':
raise KeyboardInterrupt raise KeyboardInterrupt
def handle_endless_loop(self):
self.handle_error("Mögliche Endlosschleife gefunden.")
class LOOPInterpreter: class LOOPInterpreter:
def __init__(self): def __init__(self):
......
...@@ -206,3 +206,11 @@ class GOTOInterpreterTest(TestCase): ...@@ -206,3 +206,11 @@ class GOTOInterpreterTest(TestCase):
interpret('M1: x0:=1;', [0.2]) interpret('M1: x0:=1;', [0.2])
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
interpret('M1: x0:=4;', ['EINS']) 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)
...@@ -133,3 +133,10 @@ class WHILEInterpreterTest(LOOPInterpreterTest): ...@@ -133,3 +133,10 @@ class WHILEInterpreterTest(LOOPInterpreterTest):
interpret('x0:=1', [0.2]) interpret('x0:=1', [0.2])
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
interpret('x0:=4', ['EINS']) 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)
...@@ -3,7 +3,7 @@ from loopinterpreter import LOOPInterpreter, ErrorHandler ...@@ -3,7 +3,7 @@ from loopinterpreter import LOOPInterpreter, ErrorHandler
import re import re
import signal import signal
from IPython.display import clear_output from IPython.display import clear_output
from copy import deepcopy
class Timeout: class Timeout:
def __init__(self, time): def __init__(self, time):
...@@ -29,6 +29,25 @@ Möchten sie abbrechen? [J,n]:''') ...@@ -29,6 +29,25 @@ Möchten sie abbrechen? [J,n]:''')
except EOFError: except EOFError:
pass 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): class WHILEInterpreter(LOOPInterpreter):
def __init__(self, timeout=60): def __init__(self, timeout=60):
...@@ -112,6 +131,13 @@ class WHILEInterpreter(LOOPInterpreter): ...@@ -112,6 +131,13 @@ class WHILEInterpreter(LOOPInterpreter):
end_found = True end_found = True
while not while_value == 0: 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.lex.current_position = saved_position
self.error_handler.line_number = saved_line self.error_handler.line_number = saved_line
end_found = False end_found = False
...@@ -159,11 +185,13 @@ class WHILEInterpreter(LOOPInterpreter): ...@@ -159,11 +185,13 @@ class WHILEInterpreter(LOOPInterpreter):
return self.next_token() return self.next_token()
def interpret(self, program, values=None): def interpret(self, program, values=None, test_homeomorphic_embedding=False):
try: try:
with Timeout(self.timeout): with Timeout(self.timeout):
self.lex = lexer.Lexer(self.regex_to_token, program) self.lex = lexer.Lexer(self.regex_to_token, program)
self.error_handler = ErrorHandler(program, self) self.error_handler = ErrorHandler(program, self)
self.test_homeomorphic_embedding = test_homeomorphic_embedding
self.state_list = []
self.values = values self.values = values
if values is None: if values is None:
self.values = {} self.values = {}
...@@ -187,7 +215,7 @@ class WHILEInterpreter(LOOPInterpreter): ...@@ -187,7 +215,7 @@ class WHILEInterpreter(LOOPInterpreter):
return -1 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. """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 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]). :param value_list: Array von Integern ([v1, ..., vn]).
...@@ -195,6 +223,7 @@ def interpret(program, value_list=None, timeout=60): ...@@ -195,6 +223,7 @@ def interpret(program, value_list=None, timeout=60):
:param timeout: Zeit nach der die Ausführung eines Programms pausiert wird. :param timeout: Zeit nach der die Ausführung eines Programms pausiert wird.
Gibt eine Möglichkeit zum Abbrechen bei einer Endlosschleife. Gibt eine Möglichkeit zum Abbrechen bei einer Endlosschleife.
Ein Wert von 0 deaktiviert den Timeout. 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. :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')""" :usage interpret('x1:=10; x2:=8; x0:=x2+0; WHILE x1 /=0 DO x0:=x0+1; x1:=x1-1 END')"""
interpreter = WHILEInterpreter(timeout) interpreter = WHILEInterpreter(timeout)
...@@ -205,7 +234,7 @@ def interpret(program, value_list=None, timeout=60): ...@@ -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: if not isinstance(value, int) or value < 0 or not int(value) == value:
raise ValueError("Variablen können nur natürliche Zahlen zugewiesen bekommen.") raise ValueError("Variablen können nur natürliche Zahlen zugewiesen bekommen.")
values.update({'x' + str(index+1): value}) values.update({'x' + str(index+1): value})
return interpreter.interpret(program, values) return interpreter.interpret(program, values, debug)
if __name__ == '__main__': if __name__ == '__main__':
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment