Example #1
0
    def _compile_while_statement(self, node: ParseTreeNode):
        while_start_label = Label("while_start")
        end_while_label = Label("while_end")

        self.program.append(while_start_label)

        condition_node = node.get_child("bool")
        self.oreo_to_tac(condition_node)

        # if the condition doesn't hold, leave the loop
        # IfZ a Goto L1;
        # > result=L1 op=IfFalseGoto, arg1=a
        self._add_instruction(
            arg1=condition_node.result,
            op=IF_FALSE_GOTO,
            result_var=end_while_label
        )

        # the condition held, so execute the loop body
        self.oreo_to_tac(node.get_child("compound"))

        # go back to start of loop
        self._add_goto_instruction(while_start_label)

        self.program.append(end_while_label)
Example #2
0
    def _compile_if_statement(self, node: ParseTreeNode):
        condition_node = node.get_child("bool")
        self.oreo_to_tac(condition_node)

        condition_is_false_label = Label('if_false')

        # IfZ a Goto L1;
        # > result=L1 op=IfFalseGoto, arg1=a
        self._add_instruction(
            arg1=condition_node.result,
            op=IF_FALSE_GOTO,
            result_var=condition_is_false_label
        )

        # the if statement was true
        self.oreo_to_tac(node.get_child("compound"))

        if node.has_child("optional_else"):
            end_of_else_block_label = Label('else_end')

            # if condition held, skip the else block
            self._add_goto_instruction(end_of_else_block_label)

            # the else block
            self.program.append(condition_is_false_label)
            self.oreo_to_tac(node.get_child("optional_else"))
            self.program.append(end_of_else_block_label)

        else:
            # there's no else block: if condition doesn't hold, just jump down here
            self.program.append(condition_is_false_label)
Example #3
0
    def oreo_to_tac(self, node: ParseTreeNode):
        if hasattr(node, "result"):
            return  # this node has already been processed, no need to do it again

        if node.is_non_terminal("p"):
            # ignore the top level program declaration, just process the program compound itself
            self.oreo_to_tac(node.get_child("compound"))
            return

        # if statements and while loops are carefully compiled in order, so the right things are in the right labels
        elif node.is_non_terminal("i"):
            self._compile_if_statement(node)
            return

        elif node.is_non_terminal("w"):
            self._compile_while_statement(node)
            return

        for child in node.children:
            self.oreo_to_tac(child)

        if node.is_terminal("NUMBER") or node.is_terminal("STRING"):
            literal = node.content.token.attribute
            if node.is_terminal("NUMBER"):
                literal = int(literal)
            node.result = NodeResult(literal=literal)

        elif node.is_terminal("TRUE") or node.is_terminal("FALSE"):
            bool_literal = TRUE_TAC if node.is_terminal("TRUE") else FALSE_TAC
            node.result = NodeResult(literal=bool_literal)

        elif node.is_terminal("ID"):
            node.result = NodeResult(variable=self._get_variable(node.content.token.attribute, create=True))

        elif node.is_in(["term", "factor", "simple_expr", "compare_expr", "bool"]):
            node.result = self._compile_optional_combiner(node)

        elif node.is_non_terminal("expression"):
            node.result = self._compile_optional_combiner(node, specific_combiners=["and_or_b"])

        elif node.is_non_terminal("a"):
            self._compile_assignment(node.get_child("ID"), node.get_child("expression"))

        elif node.is_non_terminal("v") and node.has_child("var_assign"):
            self._compile_assignment(node.get_child("ID"), node.get_child("var_assign").get_child("expression"))

        elif node.is_non_terminal("pr"):
            node.result = self._compile_print_expression(node)

        if hasattr(node, "result"):
            assert isinstance(node.result, NodeResult)
        else:
            node.result = "NULL RESULT"  # to prevent reprocessing processed nodes which do not have a result
Example #4
0
 def _compile_print_expression(self, node: ParseTreeNode):
     if node.has_child("GET"):
         return self._add_instruction(
             op="GET"
         )
     else:
         self._add_instruction(
             result_var=node.get_child("expression").result,
             op=node.get_a_child(["PRINT", "PRINTLN"]).content.token.name
         )
         return "NULL RESULT"
Example #5
0
def _type_check_function_call(node: ParseTreeNode, procedures,
                              none_return_allowed):
    id_paren = node.get_child("ID_PAREN")
    called_procedure = id_paren.content.token.attribute

    for procedure in procedures:
        if procedure.get_child(
                "ID_PAREN").content.token.attribute == called_procedure:
            if procedure.type == NONE and not none_return_allowed:
                token = id_paren.content.token
                raise ParseError(
                    f"Can't assign to procedure that returns none",
                    token.line_num, token.col_num, token.context_line)

            node.type = procedure.type
            return

    token = id_paren.content.token
    raise ParseError(f"Call to undeclared procedure", token.line_num,
                     token.col_num, token.context_line)
Example #6
0
def _require_child_type(node: ParseTreeNode, child: str, required_type):
    _require_type(node.get_child(child), required_type)
Example #7
0
def _type_check(node: ParseTreeNode, procedures: List[ParseTreeNode]):
    # only type check each node once
    # this is useful because sometimes type checking happens out of order, eg in assignment
    if hasattr(node, "type"):
        return

    # do this first to allow recursive procedures
    if node.is_non_terminal("function_definition"):
        procedures.append(node)

    # type check from the bottom up
    for child in node.children:
        _type_check(child, procedures)

    # there is no nice way to do this because many cases have unique behaviour
    # so sadly the best simple way to do it is a big old branching if statement
    if node.is_non_terminal("function_definition"):
        node.type = node.get_child("function_compound").type

    elif node.is_non_terminal("function_compound"):
        _type_check_function_compound(node)

    elif node.is_non_terminal("return_statement"):
        _type_check_return_statement(node)

    elif node.is_non_terminal("arg_type"):
        node.type = node.children[0].type

    elif node.is_non_terminal("expression"):
        _type_check_expression(node)

    elif node.is_non_terminal("compare_expr"):
        _type_check_compare_expr(node)

    elif node.is_non_terminal("simple_expr"):
        _type_check_simple_expr(node)

    elif node.is_non_terminal("term"):
        _type_check_term(node)

    elif node.is_non_terminal("factor"):
        _type_check_factor(node, procedures)

    elif node.is_non_terminal("bool"):
        _type_check_bool(node)

    elif node.is_non_terminal("var_assign"):
        node.type = node.get_child("expression").type

    elif node.is_non_terminal("comp_e"):
        _require_child_type(node, "expression", NUM)
        node.type = BOOL

    elif node.is_non_terminal("add_sub"):
        _require_child_type(node, "term", NUM)
        node.type = NUM

    elif node.is_non_terminal("mul_div"):
        _require_child_type(node, "factor", NUM)
        node.type = NUM

    elif node.is_terminal("ID"):
        var_type = node.scope.get_var_type(node, procedures)
        node.type = var_type

    elif node.is_terminal("NUMBER") or node.is_terminal("NUM"):
        node.type = NUM

    elif node.is_terminal("TRUE") or node.is_terminal(
            "FALSE") or node.is_terminal("BOOL"):
        node.type = BOOL

    # GET gets a string from the user
    elif node.is_terminal("STRING") or node.is_terminal(
            "STR") or node.is_terminal("GET"):
        node.type = STR
Example #8
0
def type_check(root: ParseTreeNode):
    # do not type check the name of the program, just the body
    _type_check(root.get_child("compound"), [])