Example #1
0
 def is_nonterminal(cls,
                    node: TreeNode,
                    expected: NonTerminalType = None) -> bool:
     if expected is None:
         return not node.is_terminal()
     else:
         return not node.is_terminal() and expected.is_same(node.name)
Example #2
0
    def compile_while_statement(self, context: Context, root: TreeNode):
        it = root.get_iterator()

        label_count = context.next_label_count(Keyword.WHILE)
        exp_label = f"WHILE_EXP{label_count}"
        end_label = f"WHILE_END{label_count}"

        self.writer.write_label(exp_label)
        _ = Helper.eat_keyword(it, Keyword.WHILE)

        # expression
        _ = Helper.eat_symbol(it, "(")
        expression = Helper.eat_nonterminal(it, NonTerminalType.EXPRESSION)
        self.compile_expression(context, expression)
        _ = Helper.eat_symbol(it, ")")

        # check end condition
        self.writer.write_arithmetic(ArithmeticCommand.NOT)
        self.writer.write_if(end_label)

        # statements
        _ = Helper.eat_symbol(it, "{")
        statements = Helper.eat_nonterminal(it, NonTerminalType.STATEMETNS)
        self.compile_statements(context, statements)
        # loop top
        self.writer.write_goto(exp_label)
        _ = Helper.eat_symbol(it, "}")

        self.writer.write_label(end_label)
Example #3
0
    def compile_class(self, root: TreeNode):
        if not Helper.is_nonterminal(root, NonTerminalType.CLASS):
            raise SyntaxError(f"expect 'class' but got '{root.name}'")

        context = Context()

        it = root.get_iterator()
        # should be 'class' keyword
        _ = Helper.eat_keyword(it, Keyword.CLASS)
        # class name
        node = Helper.eat_identifier(it)
        context["class"] = node.token
        context["nfields"] = 0
        # {
        _ = Helper.eat_symbol(it, "{")

        # TODO static
        # TODO constructor
        # TODO method

        while it.has_next():
            node = Helper.eat(it)

            # class variables
            if Helper.is_nonterminal(node, NonTerminalType.CLASS_VAR_DEC):
                context["nfields"] += Helper.count_fields(node)
                self.compile_class_var_decl(context, node)
                continue

            if Helper.is_symbol(node, "}"):
                break
            # print(node.name)
            self.compile_subroutine(context, node)
Example #4
0
    def compile_subroutine_body(self, context: Context, root: TreeNode):
        it = root.get_iterator()
        _ = Helper.eat_symbol(it, "{")

        # variables
        variables = []
        nlocals = 0
        while it.has_next():
            node = Helper.eat(it)
            if not NonTerminalType.VAR_DEC.is_same(node.name):
                break
            variables.append(node)
            nlocals += Helper.count_variables(node)

        self.writer.write_functions(context["function_name"], nlocals)

        if context["function_type"] == Keyword.CONSTRUCTOR:
            nfields = context["nfields"]
            self.writer.write_push(Segment.CONSTANT, nfields)
            self.writer.write_call("Memory.alloc", 1)
            self.writer.write_pop(Segment.POINTER, 0)
        elif context["function_type"] == Keyword.METHOD:
            # push the first arugment = this to pointer 0
            self.writer.write_push(Segment.ARGUMENT, 0)
            self.writer.write_pop(Segment.POINTER, 0)

        for var in variables:
            self.compile_var_decl(context, var)

        self.compile_statements(context, node)
        _ = Helper.eat_symbol(it)
Example #5
0
 def compile_expression(self, context: Context, root: TreeNode):
     """term (op term)*
     x
     x + y
     x + (y * z)
     """
     it = root.get_iterator()
     term = Helper.eat(it)
     if not it.has_next():
         self.compile_term(context, term)
     else:
         symbol = Helper.eat_symbol(it)
         other = Helper.eat(it)
         self.compile_term(context, term)
         self.compile_term(context, other)
         # handle operation
         if symbol.token == "*":
             self.writer.write_call("Math.multiply", 2)
         elif symbol.token == "/":
             self.writer.write_call("Math.divide", 2)
         else:
             # to avoid confusion with neg
             if symbol.token == "-":
                 cmd = ArithmeticCommand.SUB
             else:
                 cmd = ArithmeticCommand.from_symbol(symbol.token)
             self.writer.write_arithmetic(cmd)
Example #6
0
 def count_fields(cls, node: TreeNode) -> int:
     if NonTerminalType.CLASS_VAR_DEC.is_same(node.name):
         it = node.get_iterator()
         k = Helper.eat_keyword(it)
         if Helper.is_keyword(k, Keyword.FIELD):
             return cls.count_variables(node)
         else:
             return 0
     else:
         raise ValueError
Example #7
0
 def compile_class_var_decl(self, context: Context, root: TreeNode):
     it = root.get_iterator()
     kwd = Helper.eat_keyword(it)
     if Helper.is_keyword(kwd, Keyword.FIELD):
         self.process_variable_decl(root, context.global_symbols,
                                    SymbolKind.FIELD)
     elif Helper.is_keyword(kwd, Keyword.STATIC):
         self.process_variable_decl(root, context.global_symbols,
                                    SymbolKind.STATIC)
     else:
         raise ValueError
Example #8
0
    def process_variable_decl(self, root: TreeNode, table: SymbolTable,
                              kind: SymbolKind):
        it = root.get_iterator()
        _ = Helper.eat_keyword(it)
        type = Helper.eat(it).token
        # name
        while it.has_next():
            name = Helper.eat_identifier(it).token
            table.define(name, type, kind)

            symbol = Helper.eat_symbol(it)
            if symbol.token == ";":
                break
Example #9
0
 def compile_parameter_list(self, context, root: TreeNode):
     """define arguments in local symbol table
     int x, int y
     """
     it = root.get_iterator()
     while it.has_next():
         type = Helper.eat(it).token
         name = Helper.eat(it).token
         context.local_symbols.define(name, type, SymbolKind.ARG)
         if it.has_next():
             _ = Helper.eat_symbol(it, ",")
         else:
             break
Example #10
0
    def compile_let_statement(self, context: Context, root: TreeNode):
        it = root.get_iterator()
        _ = Helper.eat_keyword(it, Keyword.LET)
        # variable
        identifier = Helper.eat_identifier(it)
        name = identifier.token
        symbol = context.lookup_symbol(name)
        index = symbol.index
        segment = Helper.symbol_kind_to_segment(symbol.kind)

        # can be = or []
        # a[i] = ...
        # a =
        # array
        # push base_addr
        # push index_expr
        # add
        # pop pointer 1  (THAT)
        if Helper.is_symbol(Helper.eat_symbol(it), "["):
            # push the variable
            index_expr = Helper.eat_nonterminal(it, NonTerminalType.EXPRESSION)
            self.compile_expression(context, index_expr)
            self.compile_variable(context, identifier)
            _ = Helper.eat_symbol(it, "]")
            _ = Helper.eat_symbol(it, "=")
            self.writer.write_arithmetic(ArithmeticCommand.ADD)

            # rhs
            expression = Helper.eat_nonterminal(it, NonTerminalType.EXPRESSION)
            self.compile_expression(context, expression)

            # stack (bottom)
            # | addr of a[i] |
            # | rhs          |
            #   (top)

            # *(a+i) = rhs

            # copy the top to (result of rhs) to temp0
            self.writer.write_pop(Segment.TEMP, 0)
            # set THAT ot address of a[i]
            self.writer.write_pop(Segment.POINTER, 1)
            # pop rhs stored in temp
            self.writer.write_push(Segment.TEMP, 0)
            self.writer.write_pop(Segment.THAT, 0)
        else:
            # rhs
            expression = Helper.eat_nonterminal(it, NonTerminalType.EXPRESSION)
            self.compile_expression(context, expression)
            # pop the stack top to the variable
            self.writer.write_pop(segment, index)
Example #11
0
 def compile_statements(self, context: Context, root: TreeNode):
     for statement in root.loop_children():
         if NonTerminalType.DO_STATEMENT.is_same(statement.name):
             self.compile_do_statement(context, statement)
         elif NonTerminalType.RETURN_STATEMENT.is_same(statement.name):
             self.compile_return_statement(context, statement)
         elif NonTerminalType.LET_STATEMENT.is_same(statement.name):
             self.compile_let_statement(context, statement)
         elif NonTerminalType.WHILE_STATEMENT.is_same(statement.name):
             self.compile_while_statement(context, statement)
         elif NonTerminalType.IF_STATEMENT.is_same(statement.name):
             self.compile_if_statement(context, statement)
         else:
             print(statement.name)
Example #12
0
 def compile_expression_list(self, context: Context, root: TreeNode):
     """<expression> (, <expression>)*
     """
     it = root.get_iterator()
     # empty list
     if not it.has_next():
         return
     # at least one expression
     expression = next(it)
     self.compile_expression(context, expression)
     # more expressions
     while it.has_next():
         _ = Helper.eat_symbol(it, ",")
         expression = next(it)
         self.compile_expression(context, expression)
Example #13
0
    def compile_subroutine(self, context: Context, root: TreeNode):
        """
        <function_type> <function_name>
        {
            (<var statement>)*
            <statements>
        }
        """
        # clear context
        context.clear_local_symbols()
        context.clear_label_count()

        it = root.get_iterator()
        # expect 'constructor', 'function' or 'method
        node = Helper.eat_keyword(it)
        function_type = Keyword.from_str(node.token)
        context['function_type'] = function_type

        return_type: TreeNode = Helper.eat(it)
        context["return_type"] = return_type.token

        function_name = f"{context['class']}.{Helper.eat_identifier(it).token}"

        # skip symbol
        _ = Helper.eat_symbol(it, "(")

        # parameters
        parametert_list = Helper.eat_nonterminal(
            it, NonTerminalType.PARAMETER_LIST)
        nargs = Helper.parameter_size(parametert_list)
        self.compile_parameter_list(context, parametert_list)

        # skip symbol
        _ = Helper.eat_symbol(it, ")")

        # 'function' instruction needs the number of local variables.
        # delegate it to compiler of subroutine body.
        context["function_name"] = function_name

        # subroutine body
        body = Helper.eat_nonterminal(it, NonTerminalType.SUBROUTINE_BODY)
        self.compile_subroutine_body(context, body)

        # moved out of function. remove it!
        del context["function_name"]
        del context["function_type"]
        del context["return_type"]
        context.clear_local_symbols()
Example #14
0
 def compile_return_statement(self, context: Context, root: TreeNode):
     it = root.get_iterator()
     _ = Helper.eat_keyword(it)
     if context["return_type"] == "void":
         # push dummy
         self.writer.write_push(Segment.CONSTANT, 0)
     elif context["function_type"] == Keyword.CONSTRUCTOR:
         # expect return this;
         expr = Helper.eat_nonterminal(it, NonTerminalType.EXPRESSION)
         term = Helper.eat_nonterminal(expr.get_iterator(),
                                       NonTerminalType.TERM)
         _ = Helper.eat_keyword(term.get_iterator(), Keyword.THIS)
         self.writer.write_push(Segment.POINTER, 0)
     else:
         expr = Helper.eat_nonterminal(it, NonTerminalType.EXPRESSION)
         self.compile_expression(context, expr)
     self.writer.write_return()
Example #15
0
    def compile_do_statement(self, context: Context, root: TreeNode):
        """calls a function returning nothing
        """
        # need a symbol table here!
        it = root.get_iterator()
        _ = Helper.eat_keyword(it, Keyword.DO)

        # method call
        #   do foo(x, y)
        # static function call
        #   do Output.printInt(123)
        # method call
        #   do p1.dist(p2)
        # in any case, start with identifier
        self.compile_call(context, it)

        # remove dummy return value
        self.writer.write_pop(Segment.TEMP, 0)
Example #16
0
 def compile_term(self, context: Context, root: TreeNode):
     """
     x
     -x
     ((x+y)*2)
     Memory.peek(8000)
     variable
     "abcdefg"
     """
     it = root.get_iterator()
     node = Helper.eat(it)
     # integer constant
     if node.is_terminal() and node.token_type == TokenType.INT_CONST:
         value = int(node.token)
         self.writer.write_push(Segment.CONSTANT, value)
     # string constant
     elif node.is_terminal() and node.token_type == TokenType.STRING_CONST:
         self.compile_string_const(context, node)
     # unary operation
     elif Helper.is_symbol(node, "-"):
         term = Helper.eat_nonterminal(it, NonTerminalType.TERM)
         self.compile_term(context, term)
         self.writer.write_arithmetic(ArithmeticCommand.NEG)
     elif Helper.is_symbol(node, "~"):
         term = Helper.eat_nonterminal(it, NonTerminalType.TERM)
         self.compile_term(context, term)
         self.writer.write_arithmetic(ArithmeticCommand.NOT)
     # boolean
     elif Helper.is_keyword(node, Keyword.TRUE):
         # push -1
         self.writer.write_push(Segment.CONSTANT, 0)
         self.writer.write_arithmetic(ArithmeticCommand.NOT)
     elif Helper.is_keyword(node, Keyword.FALSE):
         self.writer.write_push(Segment.CONSTANT, 0)
     # null
     elif Helper.is_keyword(node, Keyword.NULL):
         # push -1
         self.writer.write_push(Segment.CONSTANT, 0)
     # this
     elif Helper.is_keyword(node, Keyword.THIS):
         self.writer.write_push(Segment.POINTER, 0)
     # expression enclosed by parentheses
     elif Helper.is_symbol(node, "("):
         expression = Helper.eat_nonterminal(it, NonTerminalType.EXPRESSION)
         self.compile_expression(context, expression)
         _ = Helper.eat_symbol(it, ")")
     elif Helper.is_identifier(node):
         if it.has_next():
             # is array?
             nxt = it.current_value()
             if Helper.is_symbol(nxt, "["):
                 # index
                 _ = Helper.eat(it)
                 expr = Helper.eat_nonterminal(it,
                                               NonTerminalType.EXPRESSION)
                 self.compile_expression(context, expr)
                 self.compile_variable(context, node)
                 _ = Helper.eat(it)
                 # address = base address + index
                 self.writer.write_arithmetic(ArithmeticCommand.ADD)
                 # THAT
                 self.writer.write_pop(Segment.POINTER, 1)
                 # push *that
                 self.writer.write_push(Segment.THAT, 0)
             # otherwise should be function call
             else:
                 self.compile_call(context, root.get_iterator())
         # variable
         else:
             self.compile_variable(context, node)
     else:
         print("term not defined: ", root)
Example #17
0
    def compile_if_statement(self, context: Context, root: TreeNode):
        """
        <expr>
        if-goto IFTRUE
        goto IFFLASE
        label IFTRUE
        <true_statements>
        goto IFEND
        label IFFALSE (else)
        <false_statements>
        label IFEND
        """
        it = root.get_iterator()

        # labels
        n = context.next_label_count(Keyword.IF)
        if_true_label = f"IF_TRUE{n}"
        if_false_label = f"IF_FALSE{n}"
        if_end_label = f"IF_END{n}"
        has_else = False

        # if (expr)
        _ = Helper.eat_keyword(it, Keyword.IF)
        _ = Helper.eat_symbol(it, "(")
        expr = Helper.eat_nonterminal(it, NonTerminalType.EXPRESSION)
        _ = Helper.eat_symbol(it, ")")

        # if true statements
        _ = Helper.eat_symbol(it, "{")
        true_statements = Helper.eat_nonterminal(it,
                                                 NonTerminalType.STATEMETNS)
        _ = Helper.eat_symbol(it, "}")

        # else statement (optional)
        if it.has_next():
            has_else = True
            _ = Helper.eat_keyword(it, Keyword.ELSE)
            _ = Helper.eat_symbol(it, "{")
            false_statements = Helper.eat_nonterminal(
                it, NonTerminalType.STATEMETNS)
            _ = Helper.eat_symbol(it, "}")

        self.compile_expression(context, expr)
        if has_else:
            self.writer.write_if(if_true_label)  # if-statement
            self.writer.write_goto(if_false_label)  # else-statement
            # true
            self.writer.write_label(if_true_label)
            self.compile_statements(context, true_statements)
            self.writer.write_goto(if_end_label)
            # false
            self.writer.write_label(if_false_label)
            self.compile_statements(context, false_statements)
            self.writer.write_label(if_end_label)
        else:
            self.writer.write_if(if_true_label)  # if-statement
            self.writer.write_goto(if_false_label)  # else-statement
            # true
            self.writer.write_label(if_true_label)
            self.compile_statements(context, true_statements)
            self.writer.write_label(if_false_label)