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)
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)
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)
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)
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)
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
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
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
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
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)
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)
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)
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()
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()
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)
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)
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)