class Interpreter: def __init__(self): self.lines = {} self._program_counter = 0 self._running = False self._stack = [] self._variables = {} self._parser = Parser(self._variables) def interactive(self): while True: line = input('>') if len(line): self.interpret_line(line) def interpret_line(self, line): tokenizer = Tokenizer() tokenizer.parse(line) first_token = tokenizer.getNextToken() if first_token.type == Token.NUMBER: self.lines[int(first_token.value)] = tokenizer.prog[tokenizer.pos:] self.sort_lines() else: self.run_line(line) def run_line(self, line): tokenizer = Tokenizer() tokenizer.parse(line) self.execute_statement(tokenizer) def execute_statement(self, tokenizer): statement = self._parser.match_statement(tokenizer) if statement == 'LET': self.stat_let(tokenizer) elif statement == 'PRINT': self.stat_print(tokenizer) elif statement == 'LIST': self.stat_list() elif statement == 'INPUT': self.stat_input(tokenizer) elif statement == 'IF': self.stat_if(tokenizer) elif statement == 'RUN': self.run_program() elif statement == 'END': self.stat_end() elif statement == 'GOTO': self.stat_goto(tokenizer) elif statement == 'GOSUB': self.stat_gosub(tokenizer) elif statement == 'RETURN': self.stat_return() else: raise Exception('Unrecognised statement: ' + statement.value) def run_program(self): self._program_counter = 0 self._running = True self.sort_lines() statements = [x for x in self.lines.values()] while self._program_counter < len(statements) and self._running: statement = statements[self._program_counter] self.run_line(statement) self._program_counter += 1 def sort_lines(self): self.lines = OrderedDict(sorted(self.lines.items(), key=lambda x: x[0])) def stat_let(self, tokenizer): variable = tokenizer.getNextToken() if variable.type != Token.VARIABLE: raise Exception("Expected a variable") if tokenizer.getNextToken().type != Token.EQUALS: raise Exception('Expected an equals') self._parser._variables[variable.value] = self._parser.match_expression(tokenizer) def stat_print(self, tokenizer): list = self._parser.match_expression_list(tokenizer) print(','.join([str(i) for i in list])) def stat_list(self): for no, line in iter(self.lines.items()): print(no, line,) def stat_input(self, tokenizer): vars = self._parser.match_var_list(tokenizer) for var in vars: self._parser._variables[var] = input("?") def stat_if(self, tokenizer): result = self._parser.match_relop(tokenizer) then = tokenizer.getNextToken() if then.type != Token.COMMAND or then.value != 'THEN': raise Exception('Expected then after relative operator') if result: self.execute_statement(tokenizer) def stat_end(self): self._running = False def stat_goto(self, tokenizer): line_number = self._parser.match_expression(tokenizer) self.sort_lines() line_numbers = [x for x in self.lines.keys()] self._program_counter = line_numbers.index(line_number) - 1 def stat_gosub(self, tokenizer): self._stack.append(self._program_counter) self.stat_goto(tokenizer) def stat_return(self): self._program_counter = self._stack.pop()
class TestParser(TestCase): def setUp(self): self.tokenizer = Tokenizer() self.parser = Parser({}) def test_match_var_list(self): self.tokenizer.parse('A, B, C') self.assertEqual(['A', 'B', 'C'], self.parser.match_var_list(self.tokenizer)) def test_match_relop(self): self.tokenizer.parse('2 > 1') self.assertEqual(1, self.parser.match_relop(self.tokenizer)) self.tokenizer.parse('2 < 1') self.assertEqual(0, self.parser.match_relop(self.tokenizer)) self.tokenizer.parse('2 <= 2') self.assertEqual(1, self.parser.match_relop(self.tokenizer)) self.tokenizer.parse('2 <= 1') self.assertEqual(0, self.parser.match_relop(self.tokenizer)) self.tokenizer.parse('2 >= 2') self.assertEqual(1, self.parser.match_relop(self.tokenizer)) self.tokenizer.parse('2 >= 3') self.assertEqual(0, self.parser.match_relop(self.tokenizer)) self.tokenizer.parse('2 = 2') self.assertEqual(1, self.parser.match_relop(self.tokenizer)) self.tokenizer.parse('2 = 3') self.assertEqual(0, self.parser.match_relop(self.tokenizer)) self.tokenizer.parse('2 <> 3') self.assertEqual(1, self.parser.match_relop(self.tokenizer)) self.tokenizer.parse('2 <> 2') self.assertEqual(0, self.parser.match_relop(self.tokenizer)) self.tokenizer.parse('2 >< 3') self.assertEqual(1, self.parser.match_relop(self.tokenizer)) self.tokenizer.parse('2 >< 2') self.assertEqual(0, self.parser.match_relop(self.tokenizer)) def test_match_expression_list(self): self.tokenizer.parse('2+3+2*2, 1+2, 3, "abcd"') self.assertEqual([9,3,3, 'abcd'], self.parser.match_expression_list(self.tokenizer)) def test_match_expression(self): self.tokenizer.parse('2+3+2*2') self.assertEqual(9, self.parser.match_expression(self.tokenizer)) def test_match_term(self): self.tokenizer.parse('2*3*4') self.assertEqual(24, self.parser.match_term(self.tokenizer)) def test_match_factor(self): self.tokenizer.parse('123') self.assertEqual(123, self.parser.match_factor(self.tokenizer)) self.parser._variables['A'] = 456 self.tokenizer.parse('A') self.assertEqual(456, self.parser.match_factor(self.tokenizer)) def test_match_brackets(self): self.tokenizer.parse('(1 + 2) * (3 + 5)') self.assertEqual(24, self.parser.match_expression(self.tokenizer)) def test_left_associativity(self): self.tokenizer.parse('10-2+2') self.assertEqual(10, self.parser.match_term(self.tokenizer))