Select Git revision
loopinterpreter.py
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
loopinterpreter.py 11.68 KiB
import lexer
import sys
import operator
import re
class ErrorHandler:
def __init__(self, program, interpreter):
sys.tracebacklimit = 0
self.program = program
self.line_number = 0
self.interpreter = interpreter
def handle_error(self, message):
msg = ['Fehler in Zeile ' + str(self.line_number + 1),
self.program.split('\n')[self.line_number],
message]
raise SyntaxError('\n'.join(msg)) from None
def increase_line(self, value):
self.line_number += value
def handle_break(self):
print('BREAK in Zeile ' + str(self.line_number))
print('Aktueller Zustand:')
for k, v in self.interpreter.values.items():
print('Variable ' + k + ': ' + str(v))
user_input = input('Drücke ENTER zum Fotfahren oder schreibe EXIT zum Beenden:')
if user_input.lower() == 'exit':
raise KeyboardInterrupt
class LOOPInterpreter:
def __init__(self):
self.regex_to_token = [(re.compile(r'\d+'), 'NUMBER'),
(re.compile(r'x\d+'), 'IDENTIFIER'),
(re.compile(r'\+'), 'PLUS'),
(re.compile(r'[−-]'), 'MINUS'),
(re.compile(r':=|≔'), 'ALLOCATION'),
(re.compile(r'LOOP'), 'LOOP'),
(re.compile(r'DO'), 'DO'),
(re.compile(r'END'), 'END'),
(re.compile(r';'), 'SEMICOLON'),
(re.compile(r'BREAK'), 'BREAK'),
(re.compile(r'\s+', re.MULTILINE), 'WHITESPACE'),
(re.compile(r'[^\n]*'), 'UNKNOWN')]
self.values = {}
self.lex = None
self.error_handler = None
def process_assignment(self, forbidden_identifiers, identifier_token_1):
identifier_1 = identifier_token_1.v
if identifier_1 in forbidden_identifiers:
self.error_handler.handle_error('Identifier ' + identifier_1 +
' ist bereits in Loop vorhanden und darf nicht verwendet werden.')
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})
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 forbidden_identifiers:
self.error_handler.handle_error('Identifier ' + identifier_2 +
' ist bereits in Loop vorhanden und darf nicht verwendet werden.')
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})
return self.next_token()
def verify_assignment(self, forbidden_identifiers, identifier_token_1):
identifier_1 = identifier_token_1.v
if identifier_1 in forbidden_identifiers:
self.error_handler.handle_error('Identifier ' + identifier_1 +
' ist bereits in Loop vorhanden und darf nicht verwendet werden.')
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':
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 forbidden_identifiers:
self.error_handler.handle_error('Identifier ' + identifier_2 +
' ist bereits in Loop vorhanden und darf nicht verwendet werden.')
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.')
return self.next_token()
def process_loop(self, forbidden_identifiers, loop_token):
identifier_token = self.next_nonempty_token('LOOP', 'IDENTIFIER (x0, x1, ...)')
if not identifier_token.k == 'IDENTIFIER':
self.error_handler.handle_error('IDENTIFIER in LOOP 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('LOOP', 'DO').k == 'DO':
self.error_handler.handle_error('DO in LOOP erwartet.')
if identifier_token.v in self.values:
number_of_loops = int(self.values.get(identifier_token.v))
else:
number_of_loops = 0
saved_position = self.lex.current_position
saved_line = self.error_handler.line_number
forbidden_identifiers.append(identifier_token.v)
if number_of_loops == 0:
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 LOOP erwartet.')
elif token.k == 'SEMICOLON':
continue
elif token.k == 'END':
end_found = True
for index in range(number_of_loops):
self.lex.current_position = saved_position
self.error_handler.line_number = saved_line
end_found = False
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 LOOP erwartet.')
elif token.k == 'SEMICOLON':
continue
elif token.k == 'END':
end_found = True
forbidden_identifiers.remove(identifier_token.v)
return self.next_token()
def verify_loop(self, forbidden_identifiers, loop_token):
identifier_token = self.next_nonempty_token('LOOP', 'IDENTIFIER')
if not identifier_token.k == 'IDENTIFIER':
self.error_handler.handle_error('IDENTIFIER in LOOP 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('LOOP', 'DO').k == 'DO':
self.error_handler.handle_error('DO in LOOP erwartet.')
forbidden_identifiers.append(identifier_token.v)
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 LOOP erwartet.')
elif token.k == 'SEMICOLON':
continue
elif token.k == 'END':
end_found = True
forbidden_identifiers.remove(identifier_token.v)
return self.next_token()
def process_program(self, forbidden_identifiers, current_token):
if current_token is None or current_token.k not in ['IDENTIFIER', 'LOOP']:
self.error_handler.handle_error('Keine passende Anweisung gefunden\n' +
'Erwartet: IDENTIFIER (x0, x1, ...) oder LOOP')
elif current_token.k == 'IDENTIFIER':
current_token = self.process_assignment(forbidden_identifiers, current_token)
elif current_token.k == 'LOOP':
current_token = self.process_loop(forbidden_identifiers, current_token)
return current_token
def verify_program(self, forbidden_identifiers, current_token):
if current_token is None or current_token.k not in ['IDENTIFIER', 'LOOP']:
self.error_handler.handle_error('Keine passende Anweisung gefunden\n' +
'Erwartet: IDENTIFIER (x0, x1, ...) oder LOOP')
elif current_token.k == 'IDENTIFIER':
current_token = self.verify_assignment(forbidden_identifiers, current_token)
elif current_token.k == 'LOOP':
current_token = self.verify_loop(forbidden_identifiers, current_token)
return current_token
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 interpret(self, program):
try:
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):
interpreter = LOOPInterpreter()
return interpreter.interpret(program)