Commit f17c278f authored by Christopher Happe's avatar Christopher Happe
Browse files

Interpreter für GOTO-Programme

parent b469f216
import lexer
from loopinterpreter import ErrorHandler
from whileinterpreter import Timeout
import re
import operator
class GOTOInterpreter:
def __init__(self, timeout=60):
self.values = {}
self.lex = None
self.error_handler = None
self.regex_to_token = [(re.compile(r'\d+'), 'NUMBER'),
(re.compile(r'x\d+'), 'IDENTIFIER'),
(re.compile(r'M\d+'), 'MARKER'),
(re.compile(r'\+'), 'PLUS'),
(re.compile(r'-'), 'MINUS'),
(re.compile(r':=|≔'), 'ALLOCATION'),
(re.compile(r'='), 'EQUALS'),
(re.compile(r'/=|≠|!='), 'NOTEQUALS'),
(re.compile(r'IF'), 'IF'),
(re.compile(r'THEN'), 'THEN'),
(re.compile(r'GOTO'), 'GOTO'),
(re.compile(r'HALT'), 'HALT'),
(re.compile(r';'), 'SEMICOLON'),
(re.compile(r':'), 'COLON'),
(re.compile(r'BREAK'), 'BREAK'),
(re.compile(r'\s+', re.MULTILINE), 'WHITESPACE'),
(re.compile(r'[^\n]*'), 'UNKNOWN')]
self.marker_to_position = {}
self.marker_to_line = {}
self.timeout = timeout
self.halted = False
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 process_assignment(self, identifier_token_1):
identifier_1 = identifier_token_1.v
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})
if not self.next_nonempty_token("Zuweisung", "SEMICOLON").k == 'SEMICOLON':
self.error_handler.handle_error("SEMICOLON zum Abschluss einer Zuweisung erwartet.")
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 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})
if not self.next_nonempty_token("Zuweisung", "SEMICOLON").k == 'SEMICOLON':
self.error_handler.handle_error("SEMICOLON zum Abschluss einer Zuweisung erwartet.")
return self.next_token()
def verify_assignment(self, identifier_token_1):
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':
if not self.next_nonempty_token("Zuweisung", "SEMICOLON").k == 'SEMICOLON':
self.error_handler.handle_error("SEMICOLON zum Abschluss einer Zuweisung erwartet.")
return self.next_token()
if not identifier_token_2.k == 'IDENTIFIER':
self.error_handler.handle_error("IDENTIFIER in Zuweisung erwartet.")
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.")
if not self.next_nonempty_token("Zuweisung", "SEMICOLON").k == 'SEMICOLON':
self.error_handler.handle_error("SEMICOLON zum Abschluss einer Zuweisung erwartet.")
return self.next_token()
def process_goto(self, goto_token):
marker_token = self.next_nonempty_token('GOTO', 'MARKER (M1, M2, ...)')
if not marker_token.k == 'MARKER':
self.error_handler.handle_error('MARKER (M1, M2, ...) in GOTO erwartet.')
if not self.next_nonempty_token('GOTO', 'SEMICOLON').k == 'SEMICOLON':
self.error_handler.handle_error('SEMICOLON zum Abschluss eines GOTO erwartet')
marker_number = int(marker_token.v[1:])
current_token = self.next_token()
while current_token is not None and max(self.marker_to_position.keys()) < int(marker_number):
current_token = self.verify_line(current_token)
if marker_number not in self.marker_to_position.keys():
self.error_handler.handle_error('GOTO zu nicht vorhandener Markierung')
self.lex.current_position = self.marker_to_position.get(marker_number)
self.error_handler.line_number = self.marker_to_line.get(marker_number)
return self.next_token()
def verify_goto(self, goto_token):
marker_token = self.next_nonempty_token('GOTO', 'MARKER (M1, M2, ...)')
if not marker_token.k == 'MARKER':
self.error_handler.handle_error('MARKER (M1, M2, ...) in GOTO erwartet.')
if not self.next_nonempty_token('GOTO', 'SEMICOLON').k == 'SEMICOLON':
self.error_handler.handle_error('SEMICOLON zum Abschluss eines GOTO erwartet')
marker_number = int(marker_token.v[1:])
saved_position = self.lex.current_position
saved_line = self.error_handler.line_number
current_token = self.next_token()
while current_token is not None and max(self.marker_to_position.keys()) < int(marker_number):
current_token = self.verify_line(current_token)
if marker_number not in self.marker_to_position.keys():
self.error_handler.handle_error('GOTO zu nicht vorhandener Markierung')
self.lex.current_position = saved_position
self.error_handler.line_number = saved_line
return self.next_token()
def process_if(self, if_token):
identifier_token = self.next_nonempty_token('IF', 'IDENTIFIER')
if not identifier_token.k == 'IDENTIFIER':
self.error_handler.handle_error('IDENTIFIER in IF erwartet')
if not self.next_nonempty_token('IF', '=').k == 'EQUALS':
self.error_handler.handle_error('= in IF erwartet')
number_token = self.next_nonempty_token('IF', 'NUMBER')
if not number_token.k == 'NUMBER':
self.error_handler.handle_error('NUMBER in IF erwartet')
if not self.next_nonempty_token('IF', 'THEN').k == 'THEN':
self.error_handler.handle_error('THEN in IF erwartet')
current_token = self.next_nonempty_token('IF', 'GOTO')
if not current_token.k == 'GOTO':
self.error_handler.handle_error('GOTO in IF erwartet')
if identifier_token.v in self.values.keys():
identifier_value = self.values.get(identifier_token.v)
else:
identifier_value = 0
if identifier_value == int(number_token.v):
return self.process_goto(current_token)
else:
return self.verify_goto(current_token)
def verify_if(self, if_token):
if not self.next_nonempty_token('IF', 'IDENTIFIER').k == 'IDENTIFIER':
self.error_handler.handle_error('IDENTIFIER in IF erwartet')
if not self.next_nonempty_token('IF', '=').k == 'EQUALS':
self.error_handler.handle_error('= in IF erwartet')
if not self.next_nonempty_token('IF', 'NUMBER').k == 'NUMBER':
self.error_handler.handle_error('NUMBER in IF erwartet')
if not self.next_nonempty_token('IF', 'THEN').k == 'THEN':
self.error_handler.handle_error('THEN in IF erwartet')
current_token = self.next_nonempty_token('IF', 'GOTO')
if not current_token.k == 'GOTO':
self.error_handler.handle_error('GOTO in IF erwartet')
return self.verify_goto(current_token)
def process_halt(self, halt_token):
if not self.next_nonempty_token('HALT', 'SEMICOLON').k == 'SEMICOLON':
self.error_handler.handle_error('SEMICOLON zum Abschluss eines HALT erwartet')
self.halted = True
current_token = self.next_token()
while current_token is not None:
current_token = self.verify_line(current_token)
return current_token
def verify_halt(self, halt_token):
if not self.next_nonempty_token('HALT', 'SEMICOLON').k == 'SEMICOLON':
self.error_handler.handle_error('SEMICOLON zum Abschluss eines HALT erwartet')
return self.next_token()
def next_marker_number(self):
if self.marker_to_position.keys():
return max(self.marker_to_position.keys()) + 1
else:
return 1
def process_line(self, current_token):
if current_token is None or not current_token.k == 'MARKER':
self.error_handler.handle_error('Keine passende Anweisung gefunden\n' +
'Erwartet: MARKER (M1, M2, ...)')
marker_number = int(current_token.v[1:])
if not (marker_number == self.next_marker_number() or
(marker_number in self.marker_to_position.keys() and
current_token.p == self.marker_to_position.get(marker_number))):
self.error_handler.handle_error('Die Nummern der Anweisungen starten bei 1 und steigen fortlaufend.')
self.marker_to_position.update({marker_number: current_token.p})
self.marker_to_line.update({marker_number: self.error_handler.line_number})
colon_token = self.next_nonempty_token('LINE', 'COLON')
if not colon_token.k == 'COLON':
self.error_handler.handle_error('DOPPELPUNKT nach MARKER (M1, M2, ...) erwartet.')
current_token = self.next_nonempty_token('LINE', 'IDENTIFIER (x0, x1, ...), IF, GOTO oder HALT')
if current_token.k not in ['IDENTIFIER', 'IF', 'GOTO', 'HALT']:
self.error_handler.handle_error('Keine passende Anweisung gefunden\n' +
'Erwartet: IDENTIFIER (x0, x1, ...), IF, GOTO oder HALT')
elif current_token.k == 'IDENTIFIER':
current_token = self.process_assignment(current_token)
elif current_token.k == 'GOTO':
current_token = self.process_goto(current_token)
elif current_token.k == 'IF':
current_token = self.process_if(current_token)
elif current_token.k == 'HALT':
current_token = self.process_halt(current_token)
return current_token
def verify_line(self, current_token):
if current_token is None or not current_token.k == 'MARKER':
self.error_handler.handle_error('Keine passende Anweisung gefunden\n' +
'Erwartet: MARKER (M1, M2, ...)')
marker_number = int(current_token.v[1:])
if not (marker_number == self.next_marker_number() or
(marker_number in self.marker_to_position.keys() and
current_token.p == self.marker_to_position.get(marker_number))):
self.error_handler.handle_error('Die Nummern der Anweisungen starten bei 1 und steigen fortlaufend.')
self.marker_to_position.update({marker_number: current_token.p})
self.marker_to_line.update({marker_number: self.error_handler.line_number})
if not self.next_nonempty_token('LINE', 'COLON').k == 'COLON':
self.error_handler.handle_error('DOPPELPUNKT nach MARKER (M1, M2, ...) erwartet.')
current_token = self.next_nonempty_token('LINE', 'IDENTIFIER (x0, x1, ...), IF, GOTO oder HALT')
if current_token.k not in ['IDENTIFIER', 'IF', 'GOTO', 'HALT']:
self.error_handler.handle_error('Keine passende Anweisung gefunden\n' +
'Erwartet: IDENTIFIER (x0, x1, ...), IF, GOTO oder HALT')
elif current_token.k == 'IDENTIFIER':
current_token = self.verify_assignment(current_token)
elif current_token.k == 'GOTO':
current_token = self.verify_goto(current_token)
elif current_token.k == 'IF':
current_token = self.verify_if(current_token)
elif current_token.k == 'HALT':
current_token = self.verify_halt(current_token)
return current_token
def interpret(self, program):
try:
with Timeout(self.timeout):
self.halted = False
self.lex = lexer.Lexer(self.regex_to_token, program)
self.error_handler = ErrorHandler(program, self)
self.values = {}
current_token = self.next_token()
while current_token is not None:
current_token = self.process_line(current_token)
if not self.halted:
self.error_handler.handle_error('Ende des Programms ohne HALT erreicht.')
if 'x0' in self.values:
return self.values.get('x0')
return 0
except KeyboardInterrupt:
return -1
def interpret(program, timeout=60):
interpreter = GOTOInterpreter(timeout)
return interpreter.interpret(program)
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment