interpreter.py 9.64 KB
Newer Older
1
import lexer
Chris's avatar
Chris committed
2
import sys
Chris's avatar
Chris committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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'\n', re.MULTILINE), 'LINEBREAK'),
                  (re.compile(r'\s+'), 'WHITESPACE'),
                  (re.compile(r'[^\n]*'), 'UNKNOWN')]

global error_handler, lex, values
Chris's avatar
Chris committed
20
21
22


class ErrorHandler:
Chris's avatar
Chris committed
23
    def __init__(self, program):
Chris's avatar
Chris committed
24
25
        sys.tracebacklimit = 0
        self.program = program
Chris's avatar
Chris committed
26
        self.line_number = 0
27

Chris's avatar
Chris committed
28
29
30
    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
Chris's avatar
Chris committed
31

Chris's avatar
Chris committed
32
33
    def increase_line(self):
        self.line_number += 1
34
35


Chris's avatar
Chris committed
36
37
def process_assignment(value_list, forbidden_identifiers, identifier_token_1):
    identifier_1 = identifier_token_1.v
38
    if identifier_1 in forbidden_identifiers:
Chris's avatar
Chris committed
39
40
        error_handler.handle_error("Identifier " + identifier_1 +
                                   " ist bereits in Loop vorhanden und darf nicht verwendet werden.")
41

Chris's avatar
Chris committed
42
43
    if next_nonempty_token("Zuweisung", ":=") == 'EQUALS':
        error_handler.handle_error(":= in Zuweisung erwartet.")
44

Chris's avatar
Chris committed
45
46
47
48
49
    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
50

Chris's avatar
Chris committed
51
52
53
    if not identifier_token_2.k == 'IDENTIFIER':
        error_handler.handle_error("IDENTIFIER in Zuweisung erwartet.")
    identifier_2 = identifier_token_2.v
54
    if identifier_2 in forbidden_identifiers:
Chris's avatar
Chris committed
55
        error_handler.handle_error("Identifier " + identifier_2 +
Chris's avatar
Chris committed
56
                                   " ist bereits in Loop vorhanden und darf nicht verwendet werden.")
57
58
59
60
61
62

    if identifier_2 in value_list:
        value_2 = value_list.get(identifier_2)
    else:
        value_2 = 0

Chris's avatar
Chris committed
63
64
65
66
67
68
    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__
69
    else:
Chris's avatar
Chris committed
70
71
72
73
74
75
76
77
        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)))
78
    value_list.update({identifier_1: value_1})
Chris's avatar
Chris committed
79
    return next_token(), value_list
80

81

Chris's avatar
Chris committed
82
83
def verify_assignment(forbidden_identifiers, identifier_token_1):
    identifier_1 = identifier_token_1.v
84
    if identifier_1 in forbidden_identifiers:
Chris's avatar
Chris committed
85
86
87
88
89
90
91
92
93
94
95
96
        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
97
    if identifier_2 in forbidden_identifiers:
Chris's avatar
Chris committed
98
        error_handler.handle_error("Identifier " + identifier_2 +
Chris's avatar
Chris committed
99
                                   " ist bereits in Loop vorhanden und darf nicht verwendet werden.")
100

Chris's avatar
Chris committed
101
102
103
104
    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.")
105

Chris's avatar
Chris committed
106
    return next_token()
107
108


Chris's avatar
Chris committed
109
110
def process_loop(value_list, forbidden_identifiers, loop_token):
    identifier_token = next_nonempty_token('LOOP', 'IDENTIFIER (x0, x1, ...)')
111
    if not identifier_token.k == 'IDENTIFIER':
Chris's avatar
Chris committed
112
        error_handler.handle_error('IDENTIFIER in LOOP erwartet.')
113
    if identifier_token.v in forbidden_identifiers:
Chris's avatar
Chris committed
114
115
116
117
        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.')
118

119
120
121
122
    if identifier_token.v in value_list:
        number_of_loops = int(value_list.get(identifier_token.v))
    else:
        number_of_loops = 0
Chris's avatar
Chris committed
123
124
125

    saved_position = lex.current_position
    saved_line = error_handler.line_number
126
    forbidden_identifiers.append(identifier_token.v)
127
128
129

    if number_of_loops == 0:
        end_found = False
Chris's avatar
Chris committed
130

131
        while not end_found:
Chris's avatar
Chris committed
132
133
134
135
            token = verify_program(forbidden_identifiers, next_token())
            if token is None or token.k not in ['SEMICOLON', 'END']:
                error_handler("SEMICOLON oder END in LOOP erwartet.")
            elif token.k == 'SEMICOLON':
136
                continue
Chris's avatar
Chris committed
137
            elif token.k == 'END':
138
139
                end_found = True

140
    for index in range(number_of_loops):
Chris's avatar
Chris committed
141
142
        lex.current_position = saved_position
        error_handler.line_number = saved_line
143
        end_found = False
144
        while not end_found:
Chris's avatar
Chris committed
145
146
147
148
            token, value_list = process_program(value_list, forbidden_identifiers, next_token())
            if token is None or token.k not in ['SEMICOLON', 'END']:
                error_handler("SEMICOLON oder END in LOOP erwartet.")
            elif token.k == 'SEMICOLON':
149
                continue
Chris's avatar
Chris committed
150
            elif token.k == 'END':
151
                end_found = True
152
153

    forbidden_identifiers.remove(identifier_token.v)
Chris's avatar
Chris committed
154
    return next_token(), value_list
155

156

Chris's avatar
Chris committed
157
158
def verify_loop(forbidden_identifiers, loop_token):
    identifier_token = next_nonempty_token("LOOP", "IDENTIFIER")
Chris's avatar
Chris committed
159
    if not identifier_token.k == 'IDENTIFIER':
Chris's avatar
Chris committed
160
        error_handler.handle_error('IDENTIFIER in LOOP erwartet.')
Chris's avatar
Chris committed
161
    if identifier_token.v in forbidden_identifiers:
Chris's avatar
Chris committed
162
163
164
165
        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.')
Chris's avatar
Chris committed
166
167
168
169
170

    forbidden_identifiers.append(identifier_token.v)

    end_found = False
    while not end_found:
Chris's avatar
Chris committed
171
172
173
174
        token = verify_program(forbidden_identifiers, next_token())
        if token is None or token.k not in ['SEMICOLON', 'END']:
            error_handler("SEMICOLON oder END in LOOP erwartet.")
        elif token.k == 'SEMICOLON':
Chris's avatar
Chris committed
175
            continue
Chris's avatar
Chris committed
176
        elif token.k == 'END':
Chris's avatar
Chris committed
177
178
179
            end_found = True

    forbidden_identifiers.remove(identifier_token.v)
Chris's avatar
Chris committed
180
    return next_token()
Chris's avatar
Chris committed
181
182


Chris's avatar
Chris committed
183
def process_program(value_list, forbidden_identifiers, current_token):
Chris's avatar
Chris committed
184
    values = value_list
Chris's avatar
Chris committed
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
    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 == 'LINEBREAK':
        error_handler.increase_line()
        return next_token()
    elif new_token.k == 'WHITESPACE':
        return next_token()
Chris's avatar
Chris committed
215
    else:
Chris's avatar
Chris committed
216
217
218
219
220
221
222
223
        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
Chris's avatar
Chris committed
224
225


226
def interpret(program):
Chris's avatar
Chris committed
227
228
229
    global error_handler, lex
    lex = lexer.Lexer(regex_to_token, program)
    error_handler = ErrorHandler(program)
230
231
    values = {}
    forbidden_identifiers = []
Chris's avatar
Chris committed
232
233
234
235
236
237
238
239
240
241
    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")
242
243
244
    if "x0" in values:
        return values.get("x0")
    return 0