diff --git a/info4/kapitel-8/Interpreter/interpreter.py b/info4/kapitel-8/Interpreter/interpreter.py index ba38dad1f77e702031bfaab050bdddb168a4ce29..c40f09cffa2f76cac4eb8c770a37c372c0c9069e 100644 --- a/info4/kapitel-8/Interpreter/interpreter.py +++ b/info4/kapitel-8/Interpreter/interpreter.py @@ -1,21 +1,38 @@ import lexer +import sys + + +class ErrorHandler: + def __init__(self, program, tokens): + sys.tracebacklimit = 0 + self.program = program + position_to_line = {} + position = 0 + line = 0 + for token in tokens: + if token.k == 'LINEBREAK': + line += 1 + else: + position_to_line[position] = line + position = position + 1 + self.position_to_line = position_to_line + + def handle_error(self, position, message): + line_number = self.position_to_line[position] + msg = ["Fehler in Zeile " + str(line_number + 1), self.program.split("\n")[line_number], message] + raise SyntaxError("\n".join(msg)) from None -loop_program = '''x1 := 2; -x2 := 3; -x3 := x2 + 0; -LOOP x1 DO - x2 := x3 + 1; - x3 := x2 + 0 -END; -x0 := x2 + 0''' + +global error_handler def process_assignment(token_queue, position, value_list, forbidden_identifiers): identifier_1 = token_queue[position].v if identifier_1 in forbidden_identifiers: - raise Exception("Identifier " + identifier_1 + "ist bereits in Loop vorhanden und darf nicht verwendet werden.") + error_handler.handle_error(position, "Identifier " + identifier_1 + + "ist bereits in Loop vorhanden und darf nicht verwendet werden.") if not token_queue[position + 1].k == 'EQUALS': - raise SyntaxError(":= in Zuweisung erwartet.") + error_handler.handle_error(position + 1, ":= in Zuweisung erwartet.") if identifier_1 in value_list: value_1 = value_list.get(identifier_1) @@ -28,11 +45,12 @@ def process_assignment(token_queue, position, value_list, forbidden_identifiers) return position + 3, value_list if not token_queue[position + 2].k == 'IDENTIFIER': - raise SyntaxError("IDENTIFIER in Zuweisung erwartet.") + error_handler.handle_error(position + 2, "IDENTIFIER in Zuweisung erwartet.") identifier_2 = token_queue[position + 2].v if identifier_2 in forbidden_identifiers: - raise Exception( - "Identifier " + identifier_2 + " ist bereits in Loop vorhanden und darf nicht verwendet werden.") + error_handler.handle_error(position + 2, + "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) @@ -40,13 +58,13 @@ def process_assignment(token_queue, position, value_list, forbidden_identifiers) value_2 = 0 if not token_queue[position + 4].k == 'NUMBER': - raise SyntaxError("NUMBER in Zuweisung erwartet.") + error_handler.handle_error(position + 4, "NUMBER in Zuweisung erwartet.") if token_queue[position + 3].k == 'PLUS': value_1 = value_2 + int(token_queue[position + 4].v) elif token_queue[position + 3].k == 'MINUS': value_1 = max(0, value_2 + token_queue[position + 4].v) else: - raise SyntaxError("PLUS oder MINUS in Zuweisung erwartet.") + error_handler.handle_error(position + 3, "PLUS oder MINUS in Zuweisung erwartet.") value_list.update({identifier_1: value_1}) return position + 5, value_list @@ -54,81 +72,41 @@ def process_assignment(token_queue, position, value_list, forbidden_identifiers) def verify_assignment(token_queue, position, forbidden_identifiers): identifier_1 = token_queue[position].v if identifier_1 in forbidden_identifiers: - raise Exception("Identifier " + identifier_1 + "ist bereits in Loop vorhanden und darf nicht verwendet werden.") + error_handler.handle_error(position, + "Identifier " + identifier_1 + + "ist bereits in Loop vorhanden und darf nicht verwendet werden.") if not token_queue[position + 1].k == 'EQUALS': - raise SyntaxError(":= in Zuweisung erwartet.") + error_handler.handle_error(position + 1, ":= in Zuweisung erwartet.") if token_queue[position + 2].k == 'NUMBER': return position + 3 if not token_queue[position + 2].k == 'IDENTIFIER': - raise SyntaxError("IDENTIFIER in Zuweisung erwartet.") + error_handler.handle_error(position + 2, "IDENTIFIER in Zuweisung erwartet.") identifier_2 = token_queue[position + 2].v if identifier_2 in forbidden_identifiers: - raise Exception( - "Identifier " + identifier_2 + " ist bereits in Loop vorhanden und darf nicht verwendet werden.") + error_handler.handle_error(position + 2, + "Identifier " + identifier_2 + + " ist bereits in Loop vorhanden und darf nicht verwendet werden.") if not token_queue[position + 4].k == 'NUMBER': - raise SyntaxError("NUMBER in Zuweisung erwartet.") + error_handler.handle_error(position + 4, "NUMBER in Zuweisung erwartet.") if not token_queue[position + 3].k in ['PLUS', 'MINUS']: - raise SyntaxError("PLUS oder MINUS in Zuweisung erwartet.") + error_handler.handle_error(position + 3, "PLUS oder MINUS in Zuweisung erwartet.") return position + 5 -def verify_loop(token_queue, position, forbidden_identifiers): - identifier_token = token_queue[position + 1] - if not identifier_token.k == 'IDENTIFIER': - raise SyntaxError('IDENTIFIER in LOOP erwartet.') - if identifier_token.v in forbidden_identifiers: - raise SyntaxError( - "Identifier " + identifier_token.v + "ist bereits in Loop vorhanden und darf nicht verwendet werden.") - if not token_queue[position + 2].k == 'DO': - raise SyntaxError('DO in LOOP erwartet.') - - forbidden_identifiers.append(identifier_token.v) - - end_found = False - while not end_found: - position = verify_program(token_queue, position, forbidden_identifiers) - if token_queue[position].k == 'SEMICOLON': - position = position + 1 - continue - elif token_queue[position].k == 'END': - end_found = True - else: - raise SyntaxError("SEMICOLON oder END erwartet.") - - forbidden_identifiers.remove(identifier_token.v) - return position + 1 - - -def verify_program(token_queue, position, forbidden_identifiers): - current_key = token_queue[position].k - if current_key == 'IDENTIFIER': - try: - current_position = verify_assignment(token_queue, position, forbidden_identifiers) - except IndexError: - raise Exception("Frühzeitiges Ende einer Zuweisung.") - elif current_key == 'LOOP': - try: - current_position = verify_loop(token_queue, position, forbidden_identifiers) - except IndexError: - raise Exception("Frühzeitiges Ende eines LOOPs") - else: - raise SyntaxError("Keine passende Anweisung gefunden") - return current_position - - def process_loop(token_queue, position, value_list, forbidden_identifiers): identifier_token = token_queue[position + 1] if not identifier_token.k == 'IDENTIFIER': - raise SyntaxError('IDENTIFIER in LOOP erwartet.') + error_handler.handle_error(position + 1, 'IDENTIFIER in LOOP erwartet.') if identifier_token.v in forbidden_identifiers: - raise SyntaxError( - "Identifier " + identifier_token.v + "ist bereits in Loop vorhanden und darf nicht verwendet werden.") + error_handler.handle_error(position + 1, + "Identifier " + identifier_token.v + + "ist bereits in Loop vorhanden und darf nicht verwendet werden.") if not token_queue[position + 2].k == 'DO': - raise SyntaxError('DO in LOOP erwartet.') + error_handler.handle_error(position + 2, 'DO in LOOP erwartet.') if identifier_token.v in value_list: number_of_loops = int(value_list.get(identifier_token.v)) @@ -137,7 +115,6 @@ def process_loop(token_queue, position, value_list, forbidden_identifiers): saved_position = position + 3 forbidden_identifiers.append(identifier_token.v) - # TODO increment position without changing values when number of loops = 0 if number_of_loops == 0: end_found = False position = saved_position @@ -149,7 +126,7 @@ def process_loop(token_queue, position, value_list, forbidden_identifiers): elif token_queue[position].k == 'END': end_found = True else: - raise SyntaxError("SEMICOLON oder END erwartet.") + error_handler.handle_error(position, "SEMICOLON oder END erwartet.") for index in range(number_of_loops): position = saved_position @@ -162,45 +139,94 @@ def process_loop(token_queue, position, value_list, forbidden_identifiers): elif token_queue[position].k == 'END': end_found = True else: - raise SyntaxError("SEMICOLON oder END erwartet.") + error_handler.handle_error(position, "SEMICOLON oder END erwartet.") forbidden_identifiers.remove(identifier_token.v) return position + 1, value_list +def verify_loop(token_queue, position, forbidden_identifiers): + identifier_token = token_queue[position + 1] + if not identifier_token.k == 'IDENTIFIER': + error_handler.handle_error(position + 1, 'IDENTIFIER in LOOP erwartet.') + if identifier_token.v in forbidden_identifiers: + error_handler.handle_error(position + 1, + "Identifier " + identifier_token.v + + "ist bereits in Loop vorhanden und darf nicht verwendet werden.") + if not token_queue[position + 2].k == 'DO': + error_handler.handle_error(position + 2, 'DO in LOOP erwartet.') + + forbidden_identifiers.append(identifier_token.v) + + end_found = False + while not end_found: + position = verify_program(token_queue, position, forbidden_identifiers) + if token_queue[position].k == 'SEMICOLON': + position = position + 1 + continue + elif token_queue[position].k == 'END': + end_found = True + else: + error_handler(position, "SEMICOLON oder END in LOOP erwartet.") + + forbidden_identifiers.remove(identifier_token.v) + return position + 1 + + def process_program(token_queue, position, value_list, forbidden_identifiers): + current_position = position + values = value_list current_key = token_queue[position].k if current_key == 'IDENTIFIER': try: current_position, values = process_assignment(token_queue, position, value_list, forbidden_identifiers) except IndexError: - raise Exception("Frühzeitiges Ende einer Zuweisung.") + error_handler.handle_error(current_position, "Frühzeitiges Ende einer Zuweisung.") elif current_key == 'LOOP': try: current_position, values = process_loop(token_queue, position, value_list, forbidden_identifiers) except IndexError: - raise Exception("Frühzeitiges Ende eines LOOPs") + error_handler.handle_error(current_position, "Frühzeitiges Ende eines LOOPs") else: - raise SyntaxError("Keine passende Anweisung gefunden") + error_handler.handle_error(current_position, "Keine passende Anweisung gefunden") return current_position, values +def verify_program(token_queue, position, forbidden_identifiers): + current_key = token_queue[position].k + current_position = position + if current_key == 'IDENTIFIER': + try: + current_position = verify_assignment(token_queue, position, forbidden_identifiers) + except IndexError: + error_handler.handle_error(current_position, "Frühzeitiges Ende einer Zuweisung.") + elif current_key == 'LOOP': + try: + current_position = verify_loop(token_queue, position, forbidden_identifiers) + except IndexError: + error_handler.handle_error(current_position, "Frühzeitiges Ende eines LOOPs") + else: + error_handler.handle_error(current_position, "Keine passende Anweisung gefunden") + return current_position + + def interpret(program): tokens = lexer.tokenize(program) + global error_handler + error_handler = ErrorHandler(program, tokens) + tokens = [token for token in tokens if not token.k == 'LINEBREAK'] current_position = 0 values = {} forbidden_identifiers = [] while current_position < len(tokens): current_position, values = process_program(tokens, current_position, values, forbidden_identifiers) if current_position < len(tokens) and not tokens[current_position].k == 'SEMICOLON': - raise SyntaxError("Semicolon erwartet") + error_handler.handle_error(current_position, "Semicolon erwartet") else: + if current_position == len(tokens) - 1: + error_handler.handle_error(current_position, "Semikolons werden nur zur Trennung und nicht zum " + + "Abschluss von Programmen verwendet") current_position = current_position + 1 if "x0" in values: return values.get("x0") return 0 - - -print(interpret(loop_program)) -print(interpret('''x1:= 2; -LOOP x1 DO x2 := 2 END; x0 := x2 + 1''')) diff --git a/info4/kapitel-8/Interpreter/lexer.py b/info4/kapitel-8/Interpreter/lexer.py index f6eb7a89b19a1cacccd114ec0a3ab7d3cbe9cea7..1cf0024e976543121350d89afd9a22f9baf14be9 100644 --- a/info4/kapitel-8/Interpreter/lexer.py +++ b/info4/kapitel-8/Interpreter/lexer.py @@ -1,5 +1,6 @@ import re + class Token: def __init__(self, key, value): self.k = key @@ -17,6 +18,7 @@ def tokenize(program): (re.compile(r'DO'), 'DO'), (re.compile(r'END'), 'END'), (re.compile(r';'), 'SEMICOLON'), + (re.compile(r'\n', re.MULTILINE), 'LINEBREAK'), (re.compile(r'\s+'), 'WHITESPACE')] current_position = 0 new_position = 0 @@ -29,6 +31,9 @@ def tokenize(program): new_position = match.span()[1] break if current_position == new_position: - raise SyntaxError('Syntax Error in line: ' + str(program.count("\n", 0, current_position) + 1)) + msg = ['Fehler in Zeile : ' + str(program.count("\n", 0, current_position) + 1), + 'Erwartet: xi, :=, NUMBER, LOOP, DO, END, ;', + 'Bekommen :' + re.compile(r'[^\n]*').match(program, current_position).group()] + raise SyntaxError("\n".join(msg)) current_position = new_position return token_queue