Skip to content
Snippets Groups Projects
Commit 5b7589c0 authored by Chris's avatar Chris
Browse files

Timeout für Endlosschleifen in WHILE-Programmen closes #2

parent 52d7c7f6
No related branches found
No related tags found
1 merge request!1Master
from whileinterpreter import interpret from whileinterpreter import interpret
from test_loop_interpreter import LOOPInterpreterTest from test_loop_interpreter import LOOPInterpreterTest
from unittest import mock
def str_yes(a):
return "J"
class WHILEInterpreterTest(LOOPInterpreterTest): class WHILEInterpreterTest(LOOPInterpreterTest):
...@@ -109,3 +114,7 @@ class WHILEInterpreterTest(LOOPInterpreterTest): ...@@ -109,3 +114,7 @@ class WHILEInterpreterTest(LOOPInterpreterTest):
interpret('x1:=2;; x0:=3') interpret('x1:=2;; x0:=3')
with self.assertRaises(SyntaxError): with self.assertRaises(SyntaxError):
interpret('WHILE x1 != 0 DO x0:=2;;x2:=1 END') 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)
from loopinterpreter import LOOPInterpreter import lexer
from loopinterpreter import LOOPInterpreter, ErrorHandler
import re 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): class WHILEInterpreter(LOOPInterpreter):
def __init__(self): def __init__(self, timeout=60):
super().__init__() super().__init__()
self.regex_to_token = [(re.compile(r'\d+'), 'NUMBER'), self.regex_to_token = [(re.compile(r'\d+'), 'NUMBER'),
(re.compile(r'x\d+'), 'IDENTIFIER'), (re.compile(r'x\d+'), 'IDENTIFIER'),
...@@ -19,11 +45,12 @@ class WHILEInterpreter(LOOPInterpreter): ...@@ -19,11 +45,12 @@ class WHILEInterpreter(LOOPInterpreter):
(re.compile(r'BREAK'), 'BREAK'), (re.compile(r'BREAK'), 'BREAK'),
(re.compile(r'\s+', re.MULTILINE), 'WHITESPACE'), (re.compile(r'\s+', re.MULTILINE), 'WHITESPACE'),
(re.compile(r'[^\n]*'), 'UNKNOWN')] (re.compile(r'[^\n]*'), 'UNKNOWN')]
self.timeout = timeout
def process_program(self, forbidden_identifiers, current_token): def process_program(self, forbidden_identifiers, current_token):
if current_token is None or current_token.k not in ['IDENTIFIER', 'LOOP', 'WHILE']: if current_token is None or current_token.k not in ['IDENTIFIER', 'LOOP', 'WHILE']:
self.error_handler.handle_error("Keine passende Anweisung gefunden\n" + self.error_handler.handle_error('Keine passende Anweisung gefunden\n' +
"Erwartet: IDENTIFIER (x0, x1, ...), LOOP oder WHILE") 'Erwartet: IDENTIFIER (x0, x1, ...), LOOP oder WHILE')
elif current_token.k == 'IDENTIFIER': elif current_token.k == 'IDENTIFIER':
current_token = self.process_assignment(forbidden_identifiers, current_token) current_token = self.process_assignment(forbidden_identifiers, current_token)
elif current_token.k == 'LOOP': elif current_token.k == 'LOOP':
...@@ -34,8 +61,8 @@ class WHILEInterpreter(LOOPInterpreter): ...@@ -34,8 +61,8 @@ class WHILEInterpreter(LOOPInterpreter):
def verify_program(self, forbidden_identifiers, current_token): def verify_program(self, forbidden_identifiers, current_token):
if current_token is None or current_token.k not in ['IDENTIFIER', 'LOOP', 'WHILE']: if current_token is None or current_token.k not in ['IDENTIFIER', 'LOOP', 'WHILE']:
self.error_handler.handle_error("Keine passende Anweisung gefunden\n" + self.error_handler.handle_error('Keine passende Anweisung gefunden\n' +
"Erwartet: IDENTIFIER (x0, x1, ...), LOOP oder WHILE") 'Erwartet: IDENTIFIER (x0, x1, ...), LOOP oder WHILE')
elif current_token.k == 'IDENTIFIER': elif current_token.k == 'IDENTIFIER':
current_token = self.verify_assignment(forbidden_identifiers, current_token) current_token = self.verify_assignment(forbidden_identifiers, current_token)
elif current_token.k == 'LOOP': elif current_token.k == 'LOOP':
...@@ -51,16 +78,16 @@ class WHILEInterpreter(LOOPInterpreter): ...@@ -51,16 +78,16 @@ class WHILEInterpreter(LOOPInterpreter):
if identifier_token.v in forbidden_identifiers: if identifier_token.v in forbidden_identifiers:
self.error_handler.handle_error('Identifier ' + identifier_token.v + self.error_handler.handle_error('Identifier ' + identifier_token.v +
' ist bereits in Loop vorhanden und darf nicht verwendet werden.') ' 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.') 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': if not zero_token.k == 'NUMBER':
self.error_handler.handle_error('0 in WHILE erwartet.') self.error_handler.handle_error('0 in WHILE erwartet.')
if not int(zero_token.v) == 0: if not int(zero_token.v) == 0:
self.error_handler.handle_error('0 in WHILE erwartet.') 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.') self.error_handler.handle_error('DO in WHILE erwartet.')
if identifier_token.v in self.values: if identifier_token.v in self.values:
...@@ -76,7 +103,7 @@ class WHILEInterpreter(LOOPInterpreter): ...@@ -76,7 +103,7 @@ class WHILEInterpreter(LOOPInterpreter):
while not end_found: while not end_found:
token = self.verify_program(forbidden_identifiers, self.next_token()) token = self.verify_program(forbidden_identifiers, self.next_token())
if token is None or token.k not in ['SEMICOLON', 'END']: 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': elif token.k == 'SEMICOLON':
continue continue
elif token.k == 'END': elif token.k == 'END':
...@@ -89,7 +116,7 @@ class WHILEInterpreter(LOOPInterpreter): ...@@ -89,7 +116,7 @@ class WHILEInterpreter(LOOPInterpreter):
while not end_found: while not end_found:
token = self.process_program(forbidden_identifiers, self.next_token()) token = self.process_program(forbidden_identifiers, self.next_token())
if token is None or token.k not in ['SEMICOLON', 'END']: 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': elif token.k == 'SEMICOLON':
continue continue
elif token.k == 'END': elif token.k == 'END':
...@@ -102,29 +129,29 @@ class WHILEInterpreter(LOOPInterpreter): ...@@ -102,29 +129,29 @@ class WHILEInterpreter(LOOPInterpreter):
return self.next_token() return self.next_token()
def verify_while(self, forbidden_identifiers, current_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': if not identifier_token.k == 'IDENTIFIER':
self.error_handler.handle_error('IDENTIFIER in WHILE erwartet.') self.error_handler.handle_error('IDENTIFIER in WHILE erwartet.')
if identifier_token.v in forbidden_identifiers: if identifier_token.v in forbidden_identifiers:
self.error_handler.handle_error("Identifier " + identifier_token.v + self.error_handler.handle_error('Identifier ' + identifier_token.v +
" ist bereits in Loop vorhanden und darf nicht verwendet werden.") ' 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.') 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': if not zero_token.k == 'NUMBER':
self.error_handler.handle_error('0 in WHILE erwartet.') self.error_handler.handle_error('0 in WHILE erwartet.')
if not int(zero_token.v) == 0: if not int(zero_token.v) == 0:
self.error_handler.handle_error('0 in WHILE erwartet.') 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.') self.error_handler.handle_error('DO in WHILE erwartet.')
end_found = False end_found = False
while not end_found: while not end_found:
token = self.verify_program(forbidden_identifiers, self.next_token()) token = self.verify_program(forbidden_identifiers, self.next_token())
if token is None or token.k not in ['SEMICOLON', 'END']: 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': elif token.k == 'SEMICOLON':
continue continue
elif token.k == 'END': elif token.k == 'END':
...@@ -132,7 +159,30 @@ class WHILEInterpreter(LOOPInterpreter): ...@@ -132,7 +159,30 @@ class WHILEInterpreter(LOOPInterpreter):
return self.next_token() return self.next_token()
def interpret(self, program):
def interpret(program): try:
interpreter = WHILEInterpreter() 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) return interpreter.interpret(program)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment