whileinterpreter.py 8.72 KB
Newer Older
1
2
import lexer
from loopinterpreter import LOOPInterpreter, ErrorHandler
3
import re
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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
29
30
31


class WHILEInterpreter(LOOPInterpreter):
32
    def __init__(self, timeout=60):
33
34
35
36
        super().__init__()
        self.regex_to_token = [(re.compile(r'\d+'), 'NUMBER'),
                               (re.compile(r'x\d+'), 'IDENTIFIER'),
                               (re.compile(r'\+'), 'PLUS'),
37
                               (re.compile(r'[−-]'), 'MINUS'),
38
                               (re.compile(r':=|≔'), 'ALLOCATION'),
39
                               (re.compile(r'/=|≠|!='), 'NOTEQUALS'),
40
41
42
43
44
45
                               (re.compile(r'LOOP'), 'LOOP'),
                               (re.compile(r'WHILE'), 'WHILE'),
                               (re.compile(r'DO'), 'DO'),
                               (re.compile(r'END'), 'END'),
                               (re.compile(r';'), 'SEMICOLON'),
                               (re.compile(r'BREAK'), 'BREAK'),
46
                               (re.compile(r'\s+', re.MULTILINE), 'WHITESPACE'),
47
                               (re.compile(r'[^\n]*'), 'UNKNOWN')]
48
        self.timeout = timeout
49
50

    def process_program(self, forbidden_identifiers, current_token):
51
        if current_token is None or current_token.k not in ['IDENTIFIER', 'LOOP', 'WHILE']:
52
53
            self.error_handler.handle_error('Keine passende Anweisung gefunden\n' +
                                            'Erwartet: IDENTIFIER (x0, x1, ...), LOOP oder WHILE')
54
55
56
57
58
59
60
61
62
        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)
        elif current_token.k == 'WHILE':
            current_token = self.process_while(forbidden_identifiers, current_token)
        return current_token

    def verify_program(self, forbidden_identifiers, current_token):
63
        if current_token is None or current_token.k not in ['IDENTIFIER', 'LOOP', 'WHILE']:
64
65
            self.error_handler.handle_error('Keine passende Anweisung gefunden\n' +
                                            'Erwartet: IDENTIFIER (x0, x1, ...), LOOP oder WHILE')
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
        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)
        elif current_token.k == 'WHILE':
            current_token = self.verify_while(forbidden_identifiers, current_token)
        return current_token

    def process_while(self, forbidden_identifiers, current_token):
        identifier_token = self.next_nonempty_token('WHILE', 'IDENTIFIER (x0, x1, ...)')
        if not identifier_token.k == 'IDENTIFIER':
            self.error_handler.handle_error('IDENTIFIER in WHILE 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.')
81
        if not self.next_nonempty_token('WHILE', 'UNGLEICH').k == 'NOTEQUALS':
82
83
            self.error_handler.handle_error('UNGLEICH in WHILE erwartet.')

84
        zero_token = self.next_nonempty_token('WHILE', '0')
85
86
87
88
89
        if not zero_token.k == 'NUMBER':
            self.error_handler.handle_error('0 in WHILE erwartet.')
        if not int(zero_token.v) == 0:
            self.error_handler.handle_error('0 in WHILE erwartet.')

90
        if not self.next_nonempty_token('WHILE', 'DO').k == 'DO':
91
92
            self.error_handler.handle_error('DO in WHILE erwartet.')

93
94
95
96
97
98
99
100
101
102
103
104
105
        if identifier_token.v in self.values:
            while_value = int(self.values.get(identifier_token.v))
        else:
            while_value = 0

        saved_position = self.lex.current_position
        saved_line = self.error_handler.line_number

        if while_value == 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']:
106
                    self.error_handler.handle_error('SEMICOLON oder END in WHILE erwartet.')
107
108
109
110
111
112
113
114
115
116
117
118
                elif token.k == 'SEMICOLON':
                    continue
                elif token.k == 'END':
                    end_found = True

        while not while_value == 0:
            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']:
119
                    self.error_handler.handle_error('SEMICOLON oder END in WHILE erwartet.')
120
121
122
123
                elif token.k == 'SEMICOLON':
                    continue
                elif token.k == 'END':
                    end_found = True
124
125

            while_value = int(self.values.get(identifier_token.v))
126
127
128
129

        return self.next_token()

    def verify_while(self, forbidden_identifiers, current_token):
130
        identifier_token = self.next_nonempty_token('WHILE', 'IDENTIFIER')
131
132
133
        if not identifier_token.k == 'IDENTIFIER':
            self.error_handler.handle_error('IDENTIFIER in WHILE erwartet.')
        if identifier_token.v in forbidden_identifiers:
134
135
136
            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('WHILE', 'UNGLEICH').k == 'NOTEQUALS':
137
138
            self.error_handler.handle_error('UNGLEICH in WHILE erwartet.')

139
        zero_token = self.next_nonempty_token('WHILE', '0')
140
141
142
143
144
        if not zero_token.k == 'NUMBER':
            self.error_handler.handle_error('0 in WHILE erwartet.')
        if not int(zero_token.v) == 0:
            self.error_handler.handle_error('0 in WHILE erwartet.')

145
        if not self.next_nonempty_token('WHILE', 'DO').k == 'DO':
146
147
            self.error_handler.handle_error('DO in WHILE erwartet.')

148
149
150
151
        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']:
152
                self.error_handler.handle_error('SEMICOLON oder END in WHILE erwartet.')
153
154
155
156
157
158
159
            elif token.k == 'SEMICOLON':
                continue
            elif token.k == 'END':
                end_found = True

        return self.next_token()

160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
    def interpret(self, program):
        try:
            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:
181
182
            print('Die Ausführung des Programms wurde unterbrochen.\n' +
                  'Daher ist der Rückgabewert des Programms nicht definiert.')
183
184
185
186
187
            return -1


def interpret(program, timeout=60):
    interpreter = WHILEInterpreter(timeout)
188
    return interpreter.interpret(program)