import lexer import sys import operator import re 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':=|≔'), 'EQUALS'), (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'\n', re.MULTILINE), 'LINEBREAK'), (re.compile(r'\s+'), 'WHITESPACE'), (re.compile(r'[^\n]*'), 'UNKNOWN')] global error_handler, lex, values class ErrorHandler: def __init__(self, program): sys.tracebacklimit = 0 self.program = program self.line_number = 0 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): self.line_number += 1 def handle_break(self): print("BREAK in Zeile " + str(self.line_number)) print("Aktueller Zustand:") for k,v in 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 def process_assignment(value_list, forbidden_identifiers, identifier_token_1): identifier_1 = identifier_token_1.v if identifier_1 in forbidden_identifiers: error_handler.handle_error("Identifier " + identifier_1 + " ist bereits in Loop vorhanden und darf nicht verwendet werden.") if next_nonempty_token("Zuweisung", ":=") == 'EQUALS': error_handler.handle_error(":= in Zuweisung erwartet.") identifier_token_2 = next_nonempty_token("Zuweisung", "IDENTIFIER (x0, x1, ...) oder NUMBER") if identifier_token_2.k == 'NUMBER': value_1 = int(identifier_token_2.v) value_list.update({identifier_token_1.v: value_1}) return next_token(), value_list if not identifier_token_2.k == 'IDENTIFIER': error_handler.handle_error("IDENTIFIER in Zuweisung erwartet.") identifier_2 = identifier_token_2.v if identifier_2 in forbidden_identifiers: error_handler.handle_error("Identifier " + identifier_2 + " ist bereits in Loop vorhanden und darf nicht verwendet werden.") if identifier_2 in value_list: value_2 = value_list.get(identifier_2) else: value_2 = 0 operator_token = next_nonempty_token("Zuweisung", "+ oder -") op = None if operator_token.k == 'PLUS': op = operator.__add__ elif operator_token.k == 'MINUS': op = operator.__sub__ else: error_handler.handle_error("+ oder - in Zuweisung erwartet.") number_token = next_nonempty_token("Zuweisung", "NUMBER") if not number_token.k == 'NUMBER': error_handler.handle_error("NUMBER in Zuweisung erwartet.") value_1 = max(0, op(value_2, int(number_token.v))) value_list.update({identifier_1: value_1}) return next_token(), value_list def verify_assignment(forbidden_identifiers, identifier_token_1): identifier_1 = identifier_token_1.v if identifier_1 in forbidden_identifiers: error_handler.handle_error("Identifier " + identifier_1 + " ist bereits in Loop vorhanden und darf nicht verwendet werden.") if not next_nonempty_token("Zuweisung", ":=").k == 'EQUALS': error_handler.handle_error(":= in Zuweisung erwartet.") identifier_token_2 = next_nonempty_token("Zuweisung", "IDENTIFIER (x0, x1, ...) oder NUMBER") if identifier_token_2.k == 'NUMBER': return next_token() if not identifier_token_2.k == 'IDENTIFIER': error_handler.handle_error("IDENTIFIER in Zuweisung erwartet.") identifier_2 = identifier_token_2.v if identifier_2 in forbidden_identifiers: error_handler.handle_error("Identifier " + identifier_2 + " ist bereits in Loop vorhanden und darf nicht verwendet werden.") if next_nonempty_token("Zuweisung", "+ oder -").k not in ['PLUS', 'MINUS']: error_handler.handle_error("+ oder - in Zuweisung erwartet.") if not next_nonempty_token("Zuweisung", "NUMBER").k == 'NUMBER': error_handler.handle_error("NUMBER in Zuweisung erwartet.") return next_token() def process_loop(value_list, forbidden_identifiers, loop_token): identifier_token = next_nonempty_token('LOOP', 'IDENTIFIER (x0, x1, ...)') if not identifier_token.k == 'IDENTIFIER': error_handler.handle_error('IDENTIFIER in LOOP erwartet.') if identifier_token.v in forbidden_identifiers: error_handler.handle_error('Identifier ' + identifier_token.v + ' ist bereits in Loop vorhanden und darf nicht verwendet werden.') if not next_nonempty_token("LOOP", "DO").k == 'DO': error_handler.handle_error('DO in LOOP erwartet.') if identifier_token.v in value_list: number_of_loops = int(value_list.get(identifier_token.v)) else: number_of_loops = 0 saved_position = lex.current_position saved_line = error_handler.line_number forbidden_identifiers.append(identifier_token.v) if number_of_loops == 0: end_found = False while not end_found: token = verify_program(forbidden_identifiers, next_token()) if token is None or token.k not in ['SEMICOLON', 'END']: 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): lex.current_position = saved_position error_handler.line_number = saved_line end_found = False while not end_found: token, value_list = process_program(value_list, forbidden_identifiers, next_token()) if token is None or token.k not in ['SEMICOLON', 'END']: 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 next_token(), value_list def verify_loop(forbidden_identifiers, loop_token): identifier_token = next_nonempty_token("LOOP", "IDENTIFIER") if not identifier_token.k == 'IDENTIFIER': error_handler.handle_error('IDENTIFIER in LOOP erwartet.') if identifier_token.v in forbidden_identifiers: error_handler.handle_error("Identifier " + identifier_token.v + " ist bereits in Loop vorhanden und darf nicht verwendet werden.") if not next_nonempty_token("LOOP", "DO").k == 'DO': error_handler.handle_error('DO in LOOP erwartet.') forbidden_identifiers.append(identifier_token.v) end_found = False while not end_found: token = verify_program(forbidden_identifiers, next_token()) if token is None or token.k not in ['SEMICOLON', 'END']: 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 next_token() def process_program(value_list, forbidden_identifiers, current_token): values = value_list if current_token is None or current_token.k not in ['IDENTIFIER', 'LOOP']: error_handler.handle_error("Keine passende Anweisung gefunden\n" + "Erwartet: IDENTIFIER (x0, x1, ...) oder LOOP") elif current_token.k == 'IDENTIFIER': current_token, values = process_assignment(value_list, forbidden_identifiers, current_token) elif current_token.k == 'LOOP': current_token, values = process_loop(value_list, forbidden_identifiers, current_token) return current_token, values def verify_program(forbidden_identifiers, current_token): if current_token is None or current_token.k not in ['IDENTIFIER', 'LOOP']: error_handler.handle_error("Keine passende Anweisung gefunden\n" + "Erwartet: IDENTIFIER (x0, x1, ...) oder LOOP") elif current_token.k == 'IDENTIFIER': current_token = verify_assignment(forbidden_identifiers, current_token) elif current_token.k == 'LOOP': current_token = verify_loop(forbidden_identifiers, current_token) return current_token def next_token(): new_token = lex.next() if new_token is None: return None elif new_token.k == 'BREAK': error_handler.handle_break() return next_token() elif new_token.k == 'LINEBREAK': error_handler.increase_line() return next_token() elif new_token.k == 'WHITESPACE': return next_token() else: return new_token def next_nonempty_token(current_function, expected_token): token = next_token() if token is None: error_handler.handle_error("Frühzeitiges Ende von " + current_function + "\n" + "Erwartet: " + expected_token) return token def interpret(program): try: global error_handler, lex, values lex = lexer.Lexer(regex_to_token, program) error_handler = ErrorHandler(program) values = {} forbidden_identifiers = [] current_token = next_token() while current_token is not None: current_token, values = process_program(values, forbidden_identifiers, current_token) if current_token is not None: if not current_token.k == 'SEMICOLON': error_handler.handle_error("Semicolon erwartet") current_token = next_token() if current_token is None: error_handler.handle_error("Semikolons werden nur zur Trennung und nicht zum " + "Abschluss von Programmen verwendet") if "x0" in values: return values.get("x0") return 0 except KeyboardInterrupt: return -1