def visit(self, node: cil.AbortNode):
        type_name_address = self.get_address('typename')

        self.add_inst(
            mips.CommentNode("<abort>"),
            # "Abort called from class "
            mips.LINode(v0, 4),
            mips.LANode(a0, "s2"),
            mips.SysCallNode(),
        )

        self.visit(cil.TypeNameNode('typename', 'self'))

        self.add_inst(
            # Typename
            mips.LWNode(a0, (type_name_address, fp)),
            mips.LWNode(a0, (4, a0)),
            mips.LINode(v0, 4),
            mips.SysCallNode(),

            # \n
            mips.LINode(v0, 4),
            mips.LANode(a0, 's3'),
            mips.SysCallNode(),

            # Abort
            mips.LINode(v0, 10).with_comm("Finish program execution"),
            mips.SysCallNode(),
            mips.CommentNode("</abort>"))
 def visit(self, node: cil.CaseMatchRuntimeErrorNode):
     self.add_inst(
         mips.CommentNode(f"<case-match-runtime-error>"),
         mips.LINode(v0, 4),
         mips.LANode(a0, 's4'),
         mips.SysCallNode(),
         mips.LINode(v0, 10),
         mips.SysCallNode(),
         mips.CommentNode(f"</case-match-runtime-error>"),
     )
    def visit(self, node: cil.ReadStringNode):
        self.add_inst(mips.CommentNode(f"<readstring:{node.dest}>"))

        address = self.get_address(node.dest)

        self.add_inst(mips.LINode(a0, 512), mips.JALNode('malloc'),
                      mips.MoveNode(t2, v0))
        self.add_inst(
            mips.MoveNode(a0, t2),
            mips.LINode(a1, 512),
            mips.LINode(v0, 8),
            mips.SysCallNode(),
        )
        self.visit(cil.StaticCallNode('String__init', node.dest))
        self.add_inst(mips.LWNode(t0, (address, fp)), mips.SWNode(t2, 4, t0))

        # Remove eol
        self.add_inst(mips.MoveNode(a0, t2), mips.JALNode('remove_eol'))

        self.add_inst(mips.CommentNode(f"</readstring:{node.dest}>"))
    def visit(self, node: cil.GotoIfNode):
        condition = self.get_address(node.condition)

        self.add_inst(
            mips.CommentNode(f"<gotoif:{node.condition}-{node.label}>"),
            mips.LWNode(t0, (condition, fp)),
            mips.LWNode(t0, (4, t0)),
            mips.LINode(t1, 1),
            mips.BEQNode(t0, t1, node.label),
            mips.CommentNode(f"</gotoif:{node.condition}-{node.label}>"),
        )
    def visit(self, node: cil.ReadIntNode):
        address = self.get_address(node.dest)

        self.add_inst(mips.CommentNode(f"<readint:{node.dest}>"),
                      mips.LINode(v0, 5), mips.SysCallNode(),
                      mips.MoveNode(t2, v0))

        self.visit(cil.StaticCallNode("Int__init", node.dest))

        self.add_inst(mips.LWNode(t1, (address, fp)), mips.SWNode(t2, 4, t1),
                      mips.LWNode(v0, (address, fp)),
                      mips.CommentNode(f"</readint:{node.dest}>"))
    def visit(self, node: cil.PrintStringNode):
        address = self.get_address(node.addr)

        self.add_inst(
            mips.CommentNode(f"<printstring:{node.addr}>"),
            mips.LWNode(t0, (address, fp)),
            mips.ADDUNode(a0, t0, 4),
            mips.LINode(v0, 4),
            mips.LWNode(a0, (0, a0)),
            mips.SysCallNode(),
            mips.CommentNode(f"</printstring:{node.addr}>"),
        )
    def visit(self, node: cil.ReturnNode):
        if node.value is not None:
            if isinstance(node.value, int):
                load_to_v0 = mips.LINode(v0, node.value)
            else:
                dest_offset = self.get_address(node.value)
                load_to_v0 = mips.LWNode(v0, (dest_offset, fp))

            self.add_inst(
                mips.CommentNode(f"<return:{node.value}>"),
                load_to_v0,
                mips.CommentNode(f"</return:{node.value}>"),
            )
    def visit(self, node: cil.AssignNode):
        dest = self.get_address(node.dest)

        if isinstance(node.source, int):
            load_inst = mips.LINode(t0, node.source)
        elif node.source == "void":
            load_inst = mips.LANode(t0, "void")
        else:
            src = self.get_address(node.source)
            load_inst = mips.LWNode(t0, (src, fp))

        self.add_inst(
            mips.CommentNode(f"<assignode:{node.dest}-{node.source}>"),
            load_inst,
            mips.SWNode(t0, dest, fp),
            mips.CommentNode(f"</assignode:{node.dest}-{node.source}>"),
        )
    def visit(self, node: cil.SetAttrNode):
        if node.value == 'void':
            load_value_inst = mips.LWNode(t0, 'void')
        elif isinstance(node.value, int):
            load_value_inst = mips.LINode(t0, node.value)
        else:
            value_address = self.get_address(node.value)
            load_value_inst = mips.LWNode(t0, (value_address, fp))

        instance = self.get_address(node.instance)

        self.add_inst(
            mips.CommentNode(
                f"<setattribute:{node.attr.name}-{node.instance}>"),
            # sum 1 to attr index because at offset 0 is the type pointer
            load_value_inst,
            mips.LWNode(t1, (instance, fp)),
            mips.SWNode(t0, 4 * (node.attr.index + 1), t1).with_comm(
                f"Set attr '{node.attr}' of {node.instance} = {node.value}"),
            mips.CommentNode(
                f"</setattribute:{node.attr.name}-{node.instance}>"),
        )
    def visit(self, node: cil.FunctionNode):
        params = [x.name for x in node.params]
        local_vars = [x.name for x in node.local_vars]

        local_vars_size = len(local_vars) * dw

        self.cur_function = mips.FunctionNode(node.name, params, local_vars)
        self.functions[node.name] = self.cur_function

        # Push local vars
        push_instructions = (mips.push_register_instructions(ra) +
                             mips.push_register_instructions(fp) +
                             [mips.ADDINode(fp, sp, 8)] +
                             [mips.ADDINode(sp, sp, -local_vars_size)])

        self.add_inst(
            mips.CommentNode(f"<function:{node.name}>"),
            *push_instructions,
        )

        for instruction in node.instructions:
            self.visit(instruction)

        # Pop local vars
        pop_instructions = ([mips.ADDINode(sp, sp, local_vars_size)] +
                            mips.pop_register_instructions(fp) +
                            mips.pop_register_instructions(ra))

        return_instructions = ([mips.LINode(v0, 10),
                                mips.SysCallNode()] if self.cur_function.name
                               == "main" else [mips.JRNode(ra)])

        self.add_inst(
            *pop_instructions,
            *return_instructions,
            mips.CommentNode(f"</function:{node.name}>"),
        )
 def visit(self, node: cil.ExprVoidRuntimeErrorNode):
     self.add_inst(mips.CommentNode(f"<expr-void-runtime-error>"),
                   mips.LINode(v0, 4), mips.LANode(a0, 's5'),
                   mips.SysCallNode(), mips.LINode(v0, 10),
                   mips.SysCallNode(),
                   mips.CommentNode(f"</expr-void-runtime-error>"))