class Parser(object): def __init__(self, filename): self._lexer = Lexer(filename) # Lexical analyzer instance self._lexer.analyze() # Let the lexer do it's thing self._command_type = None # The type of command we're parsing now self._arg1 = None # The first arg of the current command self._arg2 = None # The second arg of the current command self._token = None self._lexeme = None # VM IL command dispatch table # Commands can have an arity of 0, 1, or 2 self._commands = {k:self._nullary for k in ('add', 'sub', 'neg', 'eq', 'gt', 'lt', 'and', 'or', 'not', 'return')} self._commands.update({k:self._unary for k in ('label', 'goto', 'if-goto')}) self._commands.update({k:self._binary for k in ('push', 'pop', 'function', 'call')}) def _get_next_token(self): """Populates the current token and lexeme with the next from the lexer """ self._token, self._lexeme = self._lexer.get_next_token() def _peek_next_token(self): """Returns the next token without removing it from the input """ return self._lexer.peek_next_token() def has_more_commands(self): """Returns True is there are more tokens in the input """ return self._lexer.has_more_tokens() def _parse_command(self, func): """Simple helper method for the command dispatch table """ func() def _nullary(self): self._command_type = self._token if self._command_type == VmToken.ARITHMETIC: self._arg1 = self._lexeme def _unary(self): self._command_type = self._token self._get_next_token() self._arg1 = self._lexeme def _binary(self): self._unary() self._get_next_token() self._arg2 = self._lexeme def advance(self): """Gets the next command """ try: self._arg1 = None self._arg2 = None self._get_next_token() self._parse_command(self._commands[self._lexeme]) except Exception as ex: self._command_type = VmToken.ERROR self._symbol = None print(str(ex)) @property def command_type(self): """Returns the current command type as a VmToken """ return self._command_type @property def arg1(self): """Returns the first argument of the current command, or the operator in the case of arithmetic commands """ return self._arg1 @property def arg2(self): """Returns the second argument of the current command """ return self._arg2
class Interpreter: COMPARISON_OPERATORS = (TokenType.LESS, TokenType.GREATHER, TokenType.EQUAL, TokenType.NOT_EQUAL, TokenType.LESS_EQ, TokenType.GREATHER_EQ) def __init__(self, lexer, state, memory): self.lexer = lexer self.current_token = self.lexer.get_next_token() self.state = state self.memory = memory self.activeVars = [] self.lib = Library() def error(self): raise Exception('Greska u parsiranju') def eat(self, type): if self.current_token.type == type: self.current_token = self.lexer.get_next_token() else: self.error() def is_callable(self, word): return True if word in Library.RESERVED_METHOD_WORDS and self.current_token.type == TokenType.LPAREN else False def resolve_function(self, word): self.eat(TokenType.LPAREN) first_argument = self.current_token.value self.eat(TokenType.STRING) res = getattr(self.lib, word)(first_argument) self.eat(TokenType.RPAREN) return res def factor(self): token = self.current_token if token.type == TokenType.STRING: word = token.value self.eat(TokenType.STRING) # print(self.current_token.value) if self.is_callable(word): return self.resolve_function(word) # it's var if self.current_token.type == TokenType.ASSIGN: self.eat(TokenType.ASSIGN) result = self.expr() self.memory[word] = result return result return self.memory[word] elif token.type == TokenType.INTEGER: self.eat(TokenType.INTEGER) # print("FACTOR: " + str(token.value)) return token.value elif token.type == TokenType.LPAREN: self.eat(TokenType.LPAREN) result = self.bool() self.eat(TokenType.RPAREN) return result def term(self): result = self.factor() # print("TERM got result: " + str(result)) # print("TERM curr token: " + str(self.current_token.value)) while self.current_token.type in (TokenType.MUL, TokenType.DIV): token = self.current_token # print("TERM: " + str(token.value)) if token.type == TokenType.MUL: self.eat(TokenType.MUL) result = result * self.factor() elif token.type == TokenType.DIV: self.eat(TokenType.DIV) result = result // self.factor() else: self.error() return result def expr(self): result = self.term() # print("EXPR got result: " + str(result)) # print("EXPR curr token: " + str(self.current_token.value)) while self.current_token.type in (TokenType.PLUS, TokenType.MINUS): token = self.current_token # print("EXPR: " + str(token.value)) if token.type == TokenType.PLUS: self.eat(TokenType.PLUS) result = result + self.term() elif token.type == TokenType.MINUS: self.eat(TokenType.MINUS) result = result - self.term() else: self.error() return result def bool(self): result = True left = self.expr() if self.current_token.type in Interpreter.COMPARISON_OPERATORS: right = None while self.current_token.type in Interpreter.COMPARISON_OPERATORS: if self.current_token.type == TokenType.LESS: self.eat(TokenType.LESS) right = self.expr() if not (left < right): result = False left = right elif self.current_token.type == TokenType.GREATHER: self.eat(TokenType.GREATHER) right = self.expr() if not (left > right): result = False left = right elif self.current_token.type == TokenType.EQUAL: self.eat(TokenType.EQUAL) right = self.expr() if not (left == right): result = False left = right elif self.current_token.type == TokenType.NOT_EQUAL: self.eat(TokenType.NOT_EQUAL) right = self.expr() if not (left != right): result = False left = right elif self.current_token.type == TokenType.LESS_EQ: self.eat(TokenType.LESS_EQ) right = self.expr() if not (left <= right): result = False left = right elif self.current_token.type == TokenType.GREATHER_EQ: self.eat(TokenType.GREATHER_EQ) right = self.expr() if not (left >= right): result = False left = right return result else: return left def evaluate(self): if self.state == "POSTFIX": self.postfix_to_infix(self.lexer.text) elif self.state == "PREFIX": self.prefix_to_infix(self.lexer.text) return self.bool() def postfix_to_infix(self, exp): output = [] self.lexer = Lexer(exp) self.current_token = self.lexer.get_next_token() while True: if self.current_token.type == TokenType.EOF: break if self.current_token.type == TokenType.STRING: word = self.current_token.value self.eat(TokenType.STRING) if word in Library.RESERVED_METHOD_WORDS: try: self.eat(TokenType.LPAREN) word += "(" + self.current_token.value + ")" self.eat(TokenType.STRING) self.eat(TokenType.RPAREN) except Exception: pass output.append(word) # print(output) elif self.current_token.type == TokenType.INTEGER: output.append(self.current_token.value) self.eat(TokenType.INTEGER) else: operand1 = str(output.pop()) try: operand2 = str(output.pop()) operator = str(self.current_token.value) self.current_token = self.lexer.get_next_token() expression = '(' + operand2 + operator + operand1 + ')' output.append(expression) except IndexError: if operand1.isalnum(): output.append(operand1) # print(output) self.lexer = Lexer(output[0]) self.current_token = self.lexer.get_next_token() # print(self.lexer.text) def rev_input(self, text): self.lexer = Lexer(text) self.current_token = self.lexer.get_next_token() rev_text = '' while self.current_token.type is not TokenType.EOF: word = self.current_token.value # print("Word je " + str(word)) if self.current_token.type is TokenType.STRING: # print('String je') self.eat(TokenType.STRING) # print("Curr token je " + str(self.current_token.value)) if word in Library.RESERVED_METHOD_WORDS: # print("Rez rec") try: self.eat(TokenType.LPAREN) word += "(" + self.current_token.value + ")" self.eat(TokenType.STRING) self.eat(TokenType.RPAREN) except Exception: pass # print("Appendujem " + str(word)) rev_text = ' ' + str(word) + rev_text else: # print("Appendujem " + str(word)) rev_text = ' ' + str(word) + rev_text self.current_token = self.lexer.get_next_token() # print(rev_text) return rev_text def prefix_to_infix(self, pre_exp): stack = [] rev_input = self.rev_input(pre_exp) # print("Reversed: " + rev_input) self.lexer = Lexer(rev_input) self.current_token = self.lexer.get_next_token() while True: if self.current_token.type is TokenType.EOF: break if self.current_token.type is TokenType.STRING: word = self.current_token.value self.eat(TokenType.STRING) if word in Library.RESERVED_METHOD_WORDS: try: self.eat(TokenType.LPAREN) word += "(" + self.current_token.value + ")" self.eat(TokenType.STRING) self.eat(TokenType.RPAREN) except Exception: pass stack.append(word) # print(output) elif self.current_token.type is TokenType.INTEGER: stack.append(self.current_token.value) self.eat(TokenType.INTEGER) else: op1 = stack.pop() op2 = stack.pop() temp = "(" + str(op1) + str( self.current_token.value) + str(op2) + ")" self.current_token = self.lexer.get_next_token() stack.append(temp) k = stack.pop() # print(k) self.lexer = Lexer(k) self.current_token = self.lexer.get_next_token()
class Parser(object): def __init__(self, filename): self._lexer = Lexer(filename) # Lexical analyzer instance self._lexer.analyze() # Let the lexer do it's thing self._command_type = None # The type of command we're parsing now self._symbol = None # The current a- or l-command symbol self._dest = None # The current c-command dest field self._comp = None # The current c-command comp field self._jump = None # The current c-command jump field @property def _next_token(self): return self._lexer.get_next_token() def _peek_next_token(self): return self._lexer.peek_next_token() def _parse_a_command(self): """Parses commands of the form @symbol or @number """ token, lexeme = self._next_token if token == HackToken.IDENTIFIER or token == HackToken.NUMBER: self._command_type = CommandType.A_COMMAND self._symbol = lexeme else: raise Exception( "Invalid input '{}'; expected identifier or number.".format( lexeme)) def _parse_l_command(self): """Parses label commands of the form (symbol) """ token, lexeme = self._next_token if token == HackToken.IDENTIFIER: self._command_type = CommandType.L_COMMAND self._symbol = lexeme # Consume and ignore the next token # Well, mostly ignore -- it's useful for error checking token, lexeme = self._next_token if token != HackToken.OP_RPAREN: raise Exception( "Invalid input '{}'; expected ')'.".format(lexeme)) else: raise Exception( "Invalid input '{}'; expected identifier.".format(lexeme)) def _parse_dest(self, token, lexeme): """Sets the dest part of the c-command, if there is one """ t, _ = self._peek_next_token() if t == HackToken.OP_ASSIGN: # This token is the dest; consume the '=' and return the next token self._next_token self._dest = lexeme return self._next_token else: # This is not the dest; return it back to the caller self._dest = None return Token(token, lexeme) def _parse_comp(self, token, lexeme): """Sets the comp part of the c-command; this is required """ self._comp = lexeme if token == HackToken.OP_NOT or token == HackToken.OP_MINUS: # Unary not or negation _, l = self._next_token self._comp += l # concatenate the two lexemes elif token == HackToken.NUMBER or token == HackToken.IDENTIFIER: t, l = self._peek_next_token() if t in [ HackToken.OP_AND, HackToken.OP_OR, HackToken.OP_PLUS, HackToken.OP_MINUS ]: # We've got a binary operator; use it and get the other operand _, l = self._next_token _, ll = self._next_token self._comp += (l + ll) else: raise Exception("Invalid input '{}'.".format(lexeme)) def _parse_jump(self): """Sets the jump part of the c-command, if it exists """ t, _ = self._peek_next_token() if t == HackToken.OP_SEMICOLON: # Consume the semicolon; next token should be the jump value # TODO: Add error checking here self._next_token _, l = self._next_token self._jump = l else: # No jump self._jump = None def _parse_c_command(self, token, lexeme): """Parses commands of the following forms: dest=comp;jump dest=comp comp;jump comp """ self._command_type = CommandType.C_COMMAND comp_tok, comp_val = self._parse_dest(token, lexeme) self._parse_comp(comp_tok, comp_val) self._parse_jump() def has_more_commands(self): return self._lexer.has_more_tokens() def advance(self): try: token, lexeme = self._next_token if token == HackToken.OP_ADDR: self._parse_a_command() elif token == HackToken.OP_LPAREN: self._parse_l_command() elif token == HackToken.EOF: self._command_type = None self._symbol = None else: self._parse_c_command(token, lexeme) except Exception as ex: self._command_type = CommandType.ERROR self._symbol = None print(str(ex)) @property def command_type(self): return self._command_type @property def symbol(self): return self._symbol @property def dest(self): return self._dest or "null" @property def comp(self): return self._comp @property def jump(self): return self._jump or "null"