diff --git a/info4/kapitel-8/Interpreter/test_while_interpreter.py b/info4/kapitel-8/Interpreter/test_while_interpreter.py index cae730e4a89927db053ab063711ef7309d834362..f326993b202191e4a6fd9c5915f6bb8bf440258f 100644 --- a/info4/kapitel-8/Interpreter/test_while_interpreter.py +++ b/info4/kapitel-8/Interpreter/test_while_interpreter.py @@ -1,5 +1,10 @@ from whileinterpreter import interpret from test_loop_interpreter import LOOPInterpreterTest +from unittest import mock + + +def str_yes(a): + return "J" class WHILEInterpreterTest(LOOPInterpreterTest): @@ -109,3 +114,7 @@ class WHILEInterpreterTest(LOOPInterpreterTest): interpret('x1:=2;; x0:=3') with self.assertRaises(SyntaxError): interpret('WHILE x1 != 0 DO x0:=2;;x2:=1 END') + + @mock.patch('whileinterpreter.input', side_effect=str_yes) + def test_infinite_loop(self, input): + self.assertEqual(interpret('x1:=100000; WHILE x1 != 0 DO x0:=1; x1:=x1-1 END', 1), -1) diff --git a/info4/kapitel-8/Interpreter/whileinterpreter.py b/info4/kapitel-8/Interpreter/whileinterpreter.py index cbddb54d9df5470fe74a068d2e0ca923cd1a1ca7..1c401de6f784c06324911c72ff00bb68f05b27ee 100644 --- a/info4/kapitel-8/Interpreter/whileinterpreter.py +++ b/info4/kapitel-8/Interpreter/whileinterpreter.py @@ -1,9 +1,35 @@ -from loopinterpreter import LOOPInterpreter +import lexer +from loopinterpreter import LOOPInterpreter, ErrorHandler import re +import signal + + +class Timeout: + def __init__(self, time): + self.time = time + + def __enter__(self): + if self.time > 0: + signal.signal(signal.SIGALRM, self.interrupt) + signal.alarm(self.time) + + def __exit__(self, exc_type, exc_value, exc_traceback): + signal.alarm(0) + + def interrupt(self, sig_num, stack_frame): + try: + abort = input('''Die Funktion rechnet relativ lange. +Vielleicht liegt eine Endlosschleife vor. +Möchten sie abbrechen? [J,n]:''') + if abort.upper() in ['J', 'JA', 'Y', 'YES', '']: + raise KeyboardInterrupt + signal.alarm(self.time) + except EOFError: + pass class WHILEInterpreter(LOOPInterpreter): - def __init__(self): + def __init__(self, timeout=60): super().__init__() self.regex_to_token = [(re.compile(r'\d+'), 'NUMBER'), (re.compile(r'x\d+'), 'IDENTIFIER'), @@ -19,11 +45,12 @@ class WHILEInterpreter(LOOPInterpreter): (re.compile(r'BREAK'), 'BREAK'), (re.compile(r'\s+', re.MULTILINE), 'WHITESPACE'), (re.compile(r'[^\n]*'), 'UNKNOWN')] + self.timeout = timeout def process_program(self, forbidden_identifiers, current_token): if current_token is None or current_token.k not in ['IDENTIFIER', 'LOOP', 'WHILE']: - self.error_handler.handle_error("Keine passende Anweisung gefunden\n" + - "Erwartet: IDENTIFIER (x0, x1, ...), LOOP oder WHILE") + self.error_handler.handle_error('Keine passende Anweisung gefunden\n' + + 'Erwartet: IDENTIFIER (x0, x1, ...), LOOP oder WHILE') elif current_token.k == 'IDENTIFIER': current_token = self.process_assignment(forbidden_identifiers, current_token) elif current_token.k == 'LOOP': @@ -34,8 +61,8 @@ class WHILEInterpreter(LOOPInterpreter): def verify_program(self, forbidden_identifiers, current_token): if current_token is None or current_token.k not in ['IDENTIFIER', 'LOOP', 'WHILE']: - self.error_handler.handle_error("Keine passende Anweisung gefunden\n" + - "Erwartet: IDENTIFIER (x0, x1, ...), LOOP oder WHILE") + self.error_handler.handle_error('Keine passende Anweisung gefunden\n' + + 'Erwartet: IDENTIFIER (x0, x1, ...), LOOP oder WHILE') elif current_token.k == 'IDENTIFIER': current_token = self.verify_assignment(forbidden_identifiers, current_token) elif current_token.k == 'LOOP': @@ -51,16 +78,16 @@ class WHILEInterpreter(LOOPInterpreter): if identifier_token.v in forbidden_identifiers: self.error_handler.handle_error('Identifier ' + identifier_token.v + ' ist bereits in Loop vorhanden und darf nicht verwendet werden.') - if not self.next_nonempty_token("WHILE", "UNGLEICH").k == 'NOTEQUALS': + if not self.next_nonempty_token('WHILE', 'UNGLEICH').k == 'NOTEQUALS': self.error_handler.handle_error('UNGLEICH in WHILE erwartet.') - zero_token = self.next_nonempty_token("WHILE", "0") + zero_token = self.next_nonempty_token('WHILE', '0') if not zero_token.k == 'NUMBER': self.error_handler.handle_error('0 in WHILE erwartet.') if not int(zero_token.v) == 0: self.error_handler.handle_error('0 in WHILE erwartet.') - if not self.next_nonempty_token("WHILE", "DO").k == 'DO': + if not self.next_nonempty_token('WHILE', 'DO').k == 'DO': self.error_handler.handle_error('DO in WHILE erwartet.') if identifier_token.v in self.values: @@ -76,7 +103,7 @@ class WHILEInterpreter(LOOPInterpreter): while not end_found: token = self.verify_program(forbidden_identifiers, self.next_token()) if token is None or token.k not in ['SEMICOLON', 'END']: - self.error_handler.handle_error("SEMICOLON oder END in WHILE erwartet.") + self.error_handler.handle_error('SEMICOLON oder END in WHILE erwartet.') elif token.k == 'SEMICOLON': continue elif token.k == 'END': @@ -89,7 +116,7 @@ class WHILEInterpreter(LOOPInterpreter): while not end_found: token = self.process_program(forbidden_identifiers, self.next_token()) if token is None or token.k not in ['SEMICOLON', 'END']: - self.error_handler.handle_error("SEMICOLON oder END in WHILE erwartet.") + self.error_handler.handle_error('SEMICOLON oder END in WHILE erwartet.') elif token.k == 'SEMICOLON': continue elif token.k == 'END': @@ -102,29 +129,29 @@ class WHILEInterpreter(LOOPInterpreter): return self.next_token() def verify_while(self, forbidden_identifiers, current_token): - identifier_token = self.next_nonempty_token("WHILE", "IDENTIFIER") + identifier_token = self.next_nonempty_token('WHILE', 'IDENTIFIER') if not identifier_token.k == 'IDENTIFIER': self.error_handler.handle_error('IDENTIFIER in WHILE erwartet.') if identifier_token.v in forbidden_identifiers: - self.error_handler.handle_error("Identifier " + identifier_token.v + - " ist bereits in Loop vorhanden und darf nicht verwendet werden.") - if not self.next_nonempty_token("WHILE", "UNGLEICH").k == 'NOTEQUALS': + self.error_handler.handle_error('Identifier ' + identifier_token.v + + ' ist bereits in Loop vorhanden und darf nicht verwendet werden.') + if not self.next_nonempty_token('WHILE', 'UNGLEICH').k == 'NOTEQUALS': self.error_handler.handle_error('UNGLEICH in WHILE erwartet.') - zero_token = self.next_nonempty_token("WHILE", "0") + zero_token = self.next_nonempty_token('WHILE', '0') if not zero_token.k == 'NUMBER': self.error_handler.handle_error('0 in WHILE erwartet.') if not int(zero_token.v) == 0: self.error_handler.handle_error('0 in WHILE erwartet.') - if not self.next_nonempty_token("WHILE", "DO").k == 'DO': + if not self.next_nonempty_token('WHILE', 'DO').k == 'DO': self.error_handler.handle_error('DO in WHILE erwartet.') end_found = False while not end_found: token = self.verify_program(forbidden_identifiers, self.next_token()) if token is None or token.k not in ['SEMICOLON', 'END']: - self.error_handler.handle_error("SEMICOLON oder END in WHILE erwartet.") + self.error_handler.handle_error('SEMICOLON oder END in WHILE erwartet.') elif token.k == 'SEMICOLON': continue elif token.k == 'END': @@ -132,7 +159,30 @@ class WHILEInterpreter(LOOPInterpreter): return self.next_token() - -def interpret(program): - interpreter = WHILEInterpreter() + def interpret(self, program): + try: + with Timeout(self.timeout): + self.lex = lexer.Lexer(self.regex_to_token, program) + self.error_handler = ErrorHandler(program, self) + self.values = {} + forbidden_identifiers = [] + current_token = self.next_token() + while current_token is not None: + current_token = self.process_program(forbidden_identifiers, current_token) + if current_token is not None: + if not current_token.k == 'SEMICOLON': + self.error_handler.handle_error('Semicolon erwartet') + current_token = self.next_token() + if current_token is None: + self.error_handler.handle_error('Semikolons werden nur zur Trennung und nicht zum ' + + 'Abschluss von Programmen verwendet') + if 'x0' in self.values: + return self.values.get('x0') + return 0 + except KeyboardInterrupt: + return -1 + + +def interpret(program, timeout=60): + interpreter = WHILEInterpreter(timeout) return interpreter.interpret(program)