Skip to content
Snippets Groups Projects
Select Git revision
  • 9f4866f432f68527b1b7f3876d2770dc3780846f
  • master default protected
  • release/1.1.4
  • release/1.1.3
  • release/1.1.1
  • 1.4.2
  • 1.4.1
  • 1.4.0
  • 1.3.0
  • 1.2.1
  • 1.2.0
  • 1.1.5
  • 1.1.4
  • 1.1.3
  • 1.1.1
  • 1.1.0
  • 1.0.9
  • 1.0.8
  • 1.0.7
  • v1.0.5
  • 1.0.5
21 results

RecursiveFunction.tla

Blame
  • 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)