예제 #1
0
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
예제 #2
0
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()
예제 #3
0
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"