示例#1
0
class IdleInterRepr:
    def __init__(self):
        self.__temporals = Temporal()
        self.__operands_stack = Stack()
        self.__operators_stack = Stack()
        self.__quads = []
        self.__temp_quads = []
        self.__jump_stack = Stack()
        self.__func_calls_stack = Stack()
        self.__param_counter_stack = Stack()

    @property
    def quads(self):
        return self.__quads

    def constant_quads(self):
        const_quads = []

        for const_dict in CompilationMemory.CONSTANTS.values():
            for name, var in const_dict.items():
                const_quads.append(
                    (OperationCode.ASSIGN, name, None, var.address))

        return const_quads

    def get_last_var(self):
        """Returns top of operands stack. Expects to be called only when calling function or variable within object."""

        return self.__operands_stack.pop()

    def add_goto_main(self):
        self.quads.append((OperationCode.GOTO, None, None, None))

    def set_main(self, main_func: Func):
        start = list(self.__quads[0])
        start[3] = main_func.func_start
        self.__quads[0] = tuple(start)

    def add_var(self, var):
        self.__operands_stack.push(var)

    def add_access_instance_var(self, class_ref: Variable, obj_var: Variable):

        temp = self.__temporals.next(obj_var.var_type)
        self.__quads.append((OperationCode.ASSIGN, class_ref.address,
                             obj_var.address, temp.address))
        self.__operands_stack.push(temp)

    def array_access(self, var):
        index_var = self.__operands_stack.pop()

        if index_var.var_type != DataType.INT:
            return False

        result = self.__temporals.next(DataType.POINTER)
        result.make_pointer(var.array_type)

        self.__quads.append((OperationCode.ARRINDEXCHECK.to_code(), 0,
                             var.array_size - 1, index_var.address))
        self.__quads.append((OperationCode.ARRACCESS.to_code(), var.address,
                             index_var.address, result.address))
        self.__operands_stack.push(result)
        self.__temporals.free_up_if_temp(index_var)
        return True

    def array_sort(self, var, direction):
        self.__quads.append((OperationCode.ARRSORT.to_code(),
                             (0, var.array_size), direction, var.address))

    def array_find(self, var):
        oper = self.__operands_stack.pop()
        result = self.__temporals.next(DataType.INT)

        if oper.var_type != var.array_type:
            return False

        self.__quads.append(
            (OperationCode.ARRFIND.to_code(), (0, var.array_size),
             (oper.address, var.address), result.address))

        self.__operands_stack.push(result)
        temp_func = Func('temp')
        temp_func.return_type = DataType.INT
        self.__func_calls_stack.push(temp_func)
        return True

    def add_operator(self, operator):
        self.__operators_stack.push(OperationCode(operator))

    def __resolve_oper(self) -> bool:
        right_oper = self.__operands_stack.pop()
        left_oper = self.__operands_stack.pop()
        operator = self.__operators_stack.pop()

        result_type = SemanticCube.check(operator, left_oper.var_type,
                                         right_oper.var_type)

        if result_type == DataType.ERROR:
            return False

        result = self.__temporals.next(result_type)
        self.__quads.append((operator.to_code(), left_oper.address,
                             right_oper.address, result.address))
        self.__operands_stack.push(result)

        self.__temporals.free_up_if_temp(left_oper)
        self.__temporals.free_up_if_temp(right_oper)

        return True

    def check_addsub(self) -> bool:
        if OperationCode.is_add_sub(self.__operators_stack.peek()):
            return self.__resolve_oper()

        return True

    def check_divmult(self) -> bool:
        if OperationCode.is_div_mult(self.__operators_stack.peek()):
            return self.__resolve_oper()

        return True

    def check_relop(self) -> bool:
        if OperationCode.is_relop(self.__operators_stack.peek()):
            return self.__resolve_oper()

        return True

    def open_parenthesis(self):
        self.__operators_stack.push(OperationCode.FAKEBOTTOM)

    def close_parenthesis(self):
        self.__operators_stack.pop()

    def assign(self) -> bool:
        oper = self.__operands_stack.pop()
        var = self.__operands_stack.pop()

        if oper.var_type != var.var_type:
            return False

        self.__temporals.free_up_if_temp(oper)

        self.__quads.append(
            (OperationCode.ASSIGN.to_code(), oper.address, None, var.address))
        return True

    def short_var_decl_assign(self) -> DataType:
        oper = self.__operands_stack.pop()
        var = self.__operands_stack.pop()

        self.__temporals.free_up_if_temp(oper)

        self.__quads.append(
            (OperationCode.ASSIGN.to_code(), oper.address, None, var.address))
        return oper.var_type

    def read(self, read_type):
        result = self.__temporals.next(read_type)

        if read_type == DataType.INT:
            self.__quads.append(
                (OperationCode.READINT.to_code(), None, None, result.address))

        if read_type == DataType.FLOAT:
            self.__quads.append((OperationCode.READFLOAT.to_code(), None, None,
                                 result.address))

        if read_type == DataType.STRING:
            self.__quads.append((OperationCode.READSTRING.to_code(), None,
                                 None, result.address))

        self.__operands_stack.push(result)
        temp_func = Func('temp')
        temp_func.return_type = read_type
        self.__func_calls_stack.push(temp_func)

    def to_string(self):
        oper = self.__operands_stack.pop()
        result = self.__temporals.next(DataType.STRING)

        self.__quads.append((OperationCode.TOSTRING.to_code(), oper.address,
                             None, result.address))

        self.__operands_stack.push(result)
        temp_func = Func('temp')
        temp_func.return_type = DataType.STRING
        self.__func_calls_stack.push(temp_func)

    def print_st(self):
        oper = self.__operands_stack.pop()
        self.__quads.append(
            (OperationCode.PRINT.to_code(), oper.address, None, None))

    def start_while(self):
        self.__jump_stack.push(len(self.__quads))

    def end_while_expr(self) -> bool:
        expr_result = self.__operands_stack.pop()

        if expr_result.var_type != DataType.BOOL:
            return False

        self.__quads.append(
            (OperationCode.GOTOF.to_code(), expr_result.address, None, None))
        self.__jump_stack.push(len(self.__quads) - 1)

        return True

    def end_while(self):
        expr_end = self.__jump_stack.pop()
        while_start = self.__jump_stack.pop()

        # Add GOTO to loop back to while start
        self.__quads.append(
            (OperationCode.GOTO.to_code(), None, None, while_start))

        # Update GOTOF jump address after expression
        if expr_end:
            expr_end_quad = list(self.__quads[expr_end])
            expr_end_quad[3] = len(self.__quads)
            self.__quads[expr_end] = tuple(expr_end_quad)

    def start_for_assign(self):
        # Save current quad list into temporal space and reset quads
        self.__temp_quads = self.__quads
        self.__quads = []

    def end_for_assign(self):
        # Switch quad list with the temporal quads generated from the for assignment
        quads = self.__temp_quads
        self.__temp_quads = self.__quads
        self.__quads = quads

    def end_for_block(self):
        # Add temporal quads from assignment to end of block
        self.__quads.extend(self.__temp_quads)

    def end_if_expr(self) -> bool:
        expr_result = self.__operands_stack.pop()

        if expr_result.var_type != DataType.BOOL:
            return False

        self.__quads.append(
            (OperationCode.GOTOF.to_code(), expr_result.address, None, None))
        false_jumps = Stack()
        false_jumps.push(len(self.__quads) - 1)
        self.__jump_stack.push(false_jumps)

        return True

    def fill_if_end_jumps(self):
        fill_jumps = self.__jump_stack.pop()
        while not fill_jumps.isEmpty():
            expr_end = fill_jumps.pop()
            # Update GOTOF jump address after expression
            expr_end_quad = list(self.__quads[expr_end])
            expr_end_quad[3] = len(self.__quads)
            self.__quads[expr_end] = tuple(expr_end_quad)

    def start_else_ifs(self):
        goto_end_jumps = Stack()
        goto_false_jumps = self.__jump_stack.pop()
        self.__jump_stack.push(goto_end_jumps)
        self.__jump_stack.push(goto_false_jumps)

    def add_else(self):
        self.__quads.append((OperationCode.GOTO.to_code(), None, None, None))
        false_jumps = self.__jump_stack.pop()
        false_jump = false_jumps.pop()

        # Add quad to stack of quads that should jump to the end of if
        goto_end_jumps = self.__jump_stack.pop()
        goto_end_jumps.push(len(self.__quads) - 1)
        self.__jump_stack.push(goto_end_jumps)

        # Update GOTOF jump address from previous if / else if
        expr_false_jump = list(self.__quads[false_jump])
        expr_false_jump[3] = len(self.__quads)
        self.__quads[false_jump] = tuple(expr_false_jump)

    def add_func_era(self, func_called: Func, obj_var: Variable = None):
        if obj_var != None:
            # If call belongs to object, add object reference to change instance contexts
            self.__quads.append((OperationCode.ERA, func_called.func_start,
                                 obj_var.address, None))
        elif func_called.name == func_called.return_type:
            temp = self.__temporals.next(func_called.return_type)
            self.__quads.append((OperationCode.ERA, func_called.func_start,
                                 temp.address, None))
            self.__operands_stack.push(temp)
        elif func_called.name == "__default_constructor__":
            temp = self.__temporals.next(func_called.return_type)
            self.__operands_stack.push(temp)
        else:
            self.__quads.append(
                (OperationCode.ERA, func_called.func_start, None, None))

        self.__func_calls_stack.push(func_called)
        self.__param_counter_stack.push(0)

    def add_func_param(self):
        func_called = self.__func_calls_stack.peek()
        origin_var = self.__operands_stack.pop()
        param_counter = self.__param_counter_stack.pop()
        self.__param_counter_stack.push(param_counter + 1)

        # More arguments given than requested
        if param_counter >= len(func_called.arguments):
            return (True, None
                    )  # Ignore, later will be caught on add_func_gosub

        # Check parameter type matching
        destination_var = func_called.arguments[param_counter]
        if origin_var.var_type != destination_var.var_type:
            return (False, destination_var.address, destination_var.var_type)

        if destination_var.is_param_by_ref:
            self.__quads.append((OperationCode.PARAMREF, origin_var.address,
                                 None, destination_var.address))
        else:
            self.__quads.append((OperationCode.PARAM, origin_var.address, None,
                                 destination_var.address))
        return (True, None)

    def add_func_gosub(self):
        func_called = self.__func_calls_stack.peek()

        if func_called:
            if func_called.name != "__default_constructor__":  # Ignore default constructor

                # If function has return value, save return in temporal
                if func_called.return_type != None:
                    if func_called.return_type == func_called.name:  # Constructor call
                        self.__quads.append(
                            (OperationCode.GOSUB, func_called.name, None,
                             self.__operands_stack.peek().address))
                    else:
                        result = self.__temporals.next(func_called.return_type)
                        self.__quads.append(
                            (OperationCode.GOSUB, func_called.name, None,
                             result.address))
                        self.__operands_stack.push(result)
                else:
                    self.__quads.append(
                        (OperationCode.GOSUB, func_called.name, None, None))

            # Check number of parameters
            param_counter = self.__param_counter_stack.pop()
            if param_counter != len(func_called.arguments):
                return (False, func_called.name, len(func_called.arguments),
                        param_counter)

        return (True, None)

    def check_not_void(self, check: bool):
        func_called = self.__func_calls_stack.pop()

        if check and func_called.return_type == None:
            return False

        return True

    def add_empty_return(self):
        self.__quads.append((OperationCode.ENDPROC, None, None, None))

    def add_func_return(self, expected_return_type: DataType) -> bool:
        return_val = None
        return_type = None

        if not self.__operands_stack.isEmpty():
            return_var = self.__operands_stack.pop()
            return_val = return_var.address
            return_type = return_var.var_type

        if expected_return_type != None:
            # Quad appended even on error to avoid also reporting 'missing return statement' on add_endproc
            self.__quads.append((OperationCode.RETURN, return_val, None, None))

        if return_type != expected_return_type:
            return False

        return True

    def add_endproc(self, current_func: Func) -> bool:
        self.__quads.append((OperationCode.ENDPROC, None, None, None))
        self.__temporals = Temporal()

        # Assumes last quad should be return statement
        if current_func.return_type != None:
            if current_func.return_type != current_func.name:  # Not a constructor
                try:
                    if self.__quads[-2][0] != OperationCode.RETURN:
                        return False
                except IndexError:  # Happens when method is blank
                    return False

        return True
示例#2
0
class IdleVirtualMachine():
    def __init__(self, const_quadruples, quadruples, debug=False):
        self.__const_quadruples = const_quadruples
        self.__quadruples = quadruples
        self.__memory_stack = Stack()
        self.__next_class_stack = Stack()
        self.__debug = debug

        curr_class = ClassMemory()
        curr_class.era_func(self.__quadruples[0][3])
        curr_class.goto_next_func()
        self.__memory_stack.push(curr_class)

        if self.__debug:
            for i in range(0, (len(self.__quadruples))):
                print(i, self.__quadruples[i])

    @property
    def current_memory(self):
        return self.__memory_stack.peek()

    def run(self):
        """ 
        Executes all of the quads generated during compilation.
        """
        instruction_set = {
            OperationCode.GOTO: self.run_goto,
            OperationCode.GOTOF: self.run_gotof,
            OperationCode.GOTOT: self.run_gotot,
            OperationCode.ASSIGN: self.run_assign,
            OperationCode.ERA: self.run_era,
            OperationCode.PARAM: self.run_param,
            OperationCode.PARAMREF: self.run_paramref,
            OperationCode.GOSUB: self.run_gosub,
            OperationCode.RETURN: self.run_return,
            OperationCode.ENDPROC: self.run_endproc,
            OperationCode.ADD: self.run_add,
            OperationCode.SUB: self.run_sub,
            OperationCode.MULT: self.run_mult,
            OperationCode.DIV: self.run_div,
            OperationCode.GT: self.run_gt,
            OperationCode.LT: self.run_lt,
            OperationCode.GE: self.run_ge,
            OperationCode.LE: self.run_le,
            OperationCode.EQUAL: self.run_equal,
            OperationCode.NOTEQUAL: self.run_not_equal,
            OperationCode.AND: self.run_and,
            OperationCode.OR: self.run_or,
            OperationCode.PRINT: self.run_print,
            OperationCode.READFLOAT: self.run_read_float,
            OperationCode.READINT: self.run_read_int,
            OperationCode.READSTRING: self.run_read_string,
            OperationCode.TOSTRING: self.run_to_string,
            OperationCode.ARRACCESS: self.run_arr_access,
            OperationCode.ARRINDEXCHECK: self.run_arr_index_check,
            OperationCode.ARRSORT: self.run_arr_sort,
            OperationCode.ARRFIND: self.run_arr_find
        }

        # Add variables for constants to memory
        self.init_consts()

        # Execute all quads
        next_quad = self.next_instruction()
        while next_quad != None:
            instruction = instruction_set[OperationCode(next_quad[0])]
            instruction(next_quad)

            if self.__debug:
                print("===== EXECUTED: " + str(next_quad) + " =========")
                print(self.current_memory)

            next_quad = self.next_instruction()

    def init_consts(self):
        temp = Memory()

        for quad in self.__const_quadruples:
            temp.set_value(quad[1], quad[3])

    def next_instruction(self):
        """
        Gets the next instruction from memory.
        """
        counter = self.current_memory.next_instruction()
        if counter != None:
            return self.__quadruples[counter]
        elif self.__memory_stack.size() > 1:
            self.__memory_stack.pop()
            return self.next_instruction()

        return None

    # INSTRUCTION SET FUNCTIONS

    def run_goto(self, quad):
        self.current_memory.goto(quad[3])

    def run_gotof(self, quad):
        op1 = self.current_memory.get_value(quad[1])
        if not op1:
            self.current_memory.goto(quad[3])

    def run_gotot(self, quad):
        op1 = self.current_memory.get_value(quad[1])
        if op1:
            self.current_memory.goto(quad[3])

    def run_assign(self, quad):
        if quad[2] == None:  # Regular access
            value = self.current_memory.get_value(quad[1])
            self.current_memory.set_value(value, quad[3])
        else:  # Instance var access
            obj_instance = self.current_memory.get_value(quad[1])
            ref = obj_instance.get_reference(quad[2])
            self.current_memory.set_reference(ref, quad[3])

    def run_era(self, quad):
        if quad[2] != None:
            obj = self.current_memory.get_value(quad[2])
            obj.era_func(quad[1])
            self.__next_class_stack.push(obj)
        else:
            self.current_memory.era_func(quad[1])

    def run_param(self, quad):
        value = self.current_memory.get_value(quad[1])

        if not self.__next_class_stack.isEmpty():
            self.__next_class_stack.peek().send_param(value, quad[3])
        else:
            self.current_memory.send_param(value, quad[3])

    def run_paramref(self, quad):
        reference = self.current_memory.get_reference(quad[1])

        if not self.__next_class_stack.isEmpty():
            self.__next_class_stack.peek().send_param_by_ref(
                reference, quad[3])
        else:
            self.current_memory.send_param_by_ref(reference, quad[3])

    def run_gosub(self, quad):
        if not self.__next_class_stack.isEmpty():
            self.__memory_stack.push(self.__next_class_stack.pop())
        self.current_memory.goto_next_func()

    def run_return(self, quad):
        value = self.current_memory.get_value(quad[1])

        # If there is another function in stack, it means it will return to that function within class
        if self.current_memory.can_return():
            counter = self.current_memory.prev_func_last_instruction()
            address = self.__quadruples[counter][3]
            self.current_memory.return_value(value, address)
        else:  # Return of object function call
            prev_class = self.__memory_stack.peek_next_to_last()
            counter = prev_class.curr_func_last_instruction()
            address = self.__quadruples[counter][3]
            prev_class.set_value(value, address)

        self.run_endproc()

    def run_endproc(self, quad=None):
        self.current_memory.end_func()

    def run_add(self, quad):
        op1 = self.current_memory.get_value(quad[1])
        op2 = self.current_memory.get_value(quad[2])
        self.current_memory.set_value(op1 + op2, quad[3])

    def run_sub(self, quad):
        op1 = self.current_memory.get_value(quad[1])
        op2 = self.current_memory.get_value(quad[2])
        self.current_memory.set_value(op1 - op2, quad[3])

    def run_mult(self, quad):
        op1 = self.current_memory.get_value(quad[1])
        op2 = self.current_memory.get_value(quad[2])
        self.current_memory.set_value(op1 * op2, quad[3])

    def run_div(self, quad):
        op1 = self.current_memory.get_value(quad[1])
        op2 = self.current_memory.get_value(quad[2])
        if op2 == 0:
            print("Runtime Error: Division by 0.")
            exit()
        if isinstance(op1, int) and isinstance(op2, int):
            self.current_memory.set_value(op1 // op2, quad[3])
        else:
            self.current_memory.set_value(op1 / op2, quad[3])

    def run_gt(self, quad):
        op1 = self.current_memory.get_value(quad[1])
        op2 = self.current_memory.get_value(quad[2])
        self.current_memory.set_value(op1 > op2, quad[3])

    def run_lt(self, quad):
        op1 = self.current_memory.get_value(quad[1])
        op2 = self.current_memory.get_value(quad[2])
        self.current_memory.set_value(op1 < op2, quad[3])

    def run_equal(self, quad):
        op1 = self.current_memory.get_value(quad[1])
        op2 = self.current_memory.get_value(quad[2])
        self.current_memory.set_value(op1 == op2, quad[3])

    def run_ge(self, quad):
        op1 = self.current_memory.get_value(quad[1])
        op2 = self.current_memory.get_value(quad[2])
        self.current_memory.set_value(op1 >= op2, quad[3])

    def run_le(self, quad):
        op1 = self.current_memory.get_value(quad[1])
        op2 = self.current_memory.get_value(quad[2])
        self.current_memory.set_value(op1 <= op2, quad[3])

    def run_not_equal(self, quad):
        op1 = self.current_memory.get_value(quad[1])
        op2 = self.current_memory.get_value(quad[2])
        self.current_memory.set_value(op1 != op2, quad[3])

    def run_and(self, quad):
        op1 = self.current_memory.get_value(quad[1])
        op2 = self.current_memory.get_value(quad[2])
        self.current_memory.set_value(op1 and op2, quad[3])

    def run_or(self, quad):
        op1 = self.current_memory.get_value(quad[1])
        op2 = self.current_memory.get_value(quad[2])
        self.current_memory.set_value(op1 or op2, quad[3])

    def run_print(self, quad):
        op1 = self.current_memory.get_value(quad[1])
        if isinstance(op1, bool):
            op1 = str(op1).lower()
        print(op1)

    def run_read_float(self, quad):
        op1 = input()
        try:
            op1 = float(op1)
        except ValueError:
            print("Runtime Error: Expected float.")
            exit()
        self.current_memory.set_value(op1, quad[3])

    def run_read_int(self, quad):
        op1 = input()
        try:
            op1 = int(op1)
        except ValueError:
            print("Runtime Error: Expected int.")
            exit()
        self.current_memory.set_value(op1, quad[3])

    def run_read_string(self, quad):
        op1 = str(input())
        self.current_memory.set_value(op1, quad[3])

    def run_to_string(self, quad):
        value = self.current_memory.get_value(quad[1])
        string_value = str(value)
        self.current_memory.set_value(string_value, quad[3])

    def run_arr_access(self, quad):
        base_address = quad[1]
        arr_index = self.current_memory.get_value(quad[2])
        address = base_address + arr_index * 100
        # RUNTIME ERROR index out of bounds

        self.current_memory.set_pointer_address(quad[3], address)

    def run_arr_index_check(self, quad):
        lower_limit = quad[1]
        upper_limit = quad[2]
        index = self.current_memory.get_value(quad[3])
        if index < lower_limit or index > upper_limit:
            print("Runtime Error: Array index out of bounds.")
            exit()

    def run_arr_sort(self, quad):
        base_address = quad[3]
        start_address = base_address + quad[1][0] * 100
        end_address = base_address + quad[1][1] * 100
        array = self.current_memory.get_memory_slice(start_address,
                                                     end_address)
        array.sort()
        if quad[2] == "desc":
            array.reverse()
        self.current_memory.set_memory_slice(array, start_address, end_address)

    def run_arr_find(self, quad):
        base_address = quad[2][1]
        start_address = base_address + quad[1][0] * 100
        end_address = base_address + quad[1][1] * 100

        value = self.current_memory.get_value(quad[2][0])
        array = self.current_memory.get_memory_slice(start_address,
                                                     end_address)
        value_index = -1
        for index, item in enumerate(array):
            if item == value:
                value_index = index
                break

        self.current_memory.set_value(value_index, quad[3])
示例#3
0
文件: nintVM.py 项目: hecerinc/nint
class nintVM:
    """docstring for nintVM"""
    def __init__(self, filename: str):
        super().__init__()
        self.ip = 0  # Instruction pointer
        self.quads = []
        self.ConstTable = dict()

        # Memcounts
        self._memcount_temp = 0
        self._memcount_global = 0

        self._returns_value = False
        self._newstack = None

        self.load_data(filename)
        self._total_quads = len(self.quads)

        assert self._memcount_global != 0 and self._memcount_temp != 0, "No data read for global or temp counts from bytecode"

        if debug_mode == 'debug':
            debug("========== QUADS ===========")
            for quad in self.quads:
                debug(quad)
            debug()
            debug()

        # TODO: Create memory sections here
        self.Temp = Memory(self._memcount_temp)
        self._GlobalMemory = Memory(self._memcount_global)
        self.CallStack = Stack()  # Local memory
        self.CallStack.push(self._GlobalMemory)

        # Instruction set
        self.nintIS = {

            # Arithmetic
            Operator.ASSIGN: self.assign,
            Operator.ADD: self.add,
            Operator.SUB: self.sub,
            Operator.MULT: self.mult,
            Operator.DIV: self.div,

            # Relops
            Operator.GT: self.gt,
            Operator.GTE: self.gte,
            Operator.LT: self.lt,
            Operator.LTE: self.lte,
            Operator.EQUAL: self.equals,
            Operator.NEQ: self.neq,

            # Boolean comparisons
            Operator.AND: self.bool_and,
            Operator.OR: self.bool_or,

            # GOTOs
            Operator.GOTO: self.goto,
            Operator.GOTOF: self.gotoF,
            Operator.GOTOV: self.gotoV,

            # Functions
            Operator.GOSUB: self.gosub,
            Operator.PARAM: self.param,
            Operator.ERA: self.expand_active_record,
            Operator.ENDPROC: self.endproc,
            Operator.RETURN: self.return_proc,
            Operator.PRINT: self._print
        }

    def load_data(self, filename: str):
        '''Read the data into memory'''
        with open(filename, 'rb') as f:
            data = pickle.load(f)
        self.quads = data[0]  # quads
        # TODO: I need to parse this table and get the actual values with their real types
        self.ConstTable = data[1]  # consttable
        self.FunDir = data[2]
        self._memcount_global = data[3]
        self._memcount_temp = data[4]

    def run(self):
        '''Run the actual code'''
        quad = self.quads[self.ip]
        while quad is not None:
            instruction = Operator(quad[0])
            method = self.nintIS[instruction]
            method(quad)
            quad = self.next()

    def next(self):
        self.ip += 1
        if self.ip < self._total_quads:
            return self.quads[self.ip]
        return None

    def set_value(self, address: str, value):
        if is_local(address):
            self.CallStack.peek().set_value(address, value)
        elif is_temp(address):
            self.Temp.set_value(address, value)
        else:
            self.CallStack.peek().set_value(address, value)

    def get_value(self, address):
        if is_constant(address):
            debug(address, "is_constant")
            return self.ConstTable[address]
        elif is_temp(address):
            return self.Temp.get_val(address)
        elif is_global(address):  # TODO: we could probably remove this now
            return self._GlobalMemory.get_val(address)
        return self.CallStack.peek().get_val(address)
        # return self.mem.get_val(address)

    # Operation functions
    # ---------------------------------------------------------------
    def assign(self, quad):
        debug("assign")
        debug(quad)
        if self._returns_value:
            # Second param is function
            self._returns_value = False
            func = self.FunDir[quad[1]]
            assert 'value' in func, "Function should have a value because it is non-void"
            value = func['value']
        else:
            value = self.get_value(quad[1])
        assert value is not None
        target_address = quad[3]
        self.set_value(target_address, value)
        debug()

    # Relational operators
    # -------------------------------------------------
    def equals(self, quad):
        debug("equals")
        left_operand = self.get_value(quad[1])
        right_operand = self.get_value(quad[2])
        result = left_operand == right_operand
        self.set_value(quad[3], result)
        debug()

    def neq(self, quad):
        left_operand = self.get_value(quad[1])
        right_operand = self.get_value(quad[2])
        result = left_operand != right_operand
        self.set_value(quad[3], result)

    def gt(self, quad):
        left_operand = self.get_value(quad[1])
        right_operand = self.get_value(quad[2])
        result = left_operand > right_operand
        self.set_value(quad[3], result)

    def gte(self, quad):
        left_operand = self.get_value(quad[1])
        right_operand = self.get_value(quad[2])
        result = left_operand >= right_operand
        self.set_value(quad[3], result)

    def lt(self, quad):
        left_operand = self.get_value(quad[1])
        right_operand = self.get_value(quad[2])
        result = left_operand < right_operand
        self.set_value(quad[3], result)

    def lte(self, quad):
        left_operand = self.get_value(quad[1])
        right_operand = self.get_value(quad[2])
        result = left_operand <= right_operand
        self.set_value(quad[3], result)

    # Boolean operators
    # -------------------------------------------------
    def bool_and(self, quad):
        left_operand = self.get_value(quad[1])
        right_operand = self.get_value(quad[2])
        result = left_operand and right_operand
        self.set_value(quad[3], result)

    def bool_or(self, quad):
        left_operand = self.get_value(quad[1])
        right_operand = self.get_value(quad[2])
        result = left_operand or right_operand
        self.set_value(quad[3], result)

    # Arithmetic
    # -------------------------------------------------

    def add(self, quad):
        debug("add")
        debug(quad)
        left_operand = self.get_value(quad[1])
        right_operand = self.get_value(quad[2])
        result = left_operand + right_operand
        self.set_value(quad[3], result)
        debug()

    def sub(self, quad):
        left_operand = self.get_value(quad[1])
        right_operand = self.get_value(quad[2])
        result = left_operand - right_operand
        self.set_value(quad[3], result)

    def mult(self, quad):
        left_operand = self.get_value(quad[1])
        right_operand = self.get_value(quad[2])
        result = left_operand * right_operand
        self.set_value(quad[3], result)

    def div(self, quad):
        left_operand = self.get_value(quad[1])
        right_operand = self.get_value(quad[2])
        if right_operand == 0:
            raise Exception("Runtime Exception: Division by 0.")
        result = left_operand / right_operand
        self.set_value(quad[3], result)

    # GOTOs
    # -------------------------------------------------

    def goto(self, quad):
        quad_addr = int(quad[3])
        self.ip = quad_addr - 1

    def gotoF(self, quad):
        expr_result = self.get_value(quad[1])
        if not expr_result:
            self.ip = int(quad[3]) - 1

    def gotoV(self, quad):
        raise Exception('Not implemented')

    # Functions
    # ---------------------------------------------------------------

    def expand_active_record(self, quad):
        debug("ERA")
        func_name = quad[1]

        assert func_name in self.FunDir, "No function"

        size_map = self.FunDir[func_name]

        # Create the AR
        sf = StackFrame(func_name, size_map)

        # Add it to the callstack
        # self.CallStack.push(sf)
        self._newstack = sf
        debug()

    def param(self, quad):
        debug('param')
        current_scope = self.CallStack.peek()
        assert current_scope is not None, "No callstack"
        param = self.get_value(quad[1])
        address = quad[3]
        assert self._newstack is not None
        self._newstack.set_value(address, param)
        debug()

    def gosub(self, quad):
        debug('gosub')
        sf = self._newstack
        self.CallStack.push(sf)
        sf.set_return_addr(self.ip)
        self.ip = int(quad[3]) - 1  # minus 1 because next() adds one
        debug()

    def endproc(self, quad):
        debug('endproc')
        current_scope = self.CallStack.pop()
        self.ip = current_scope.return_addr
        self._newstack = None
        del current_scope  # vacuous statement but makes me feel good
        debug()

    def return_proc(self, quad):
        is_empty_return = quad[1] is None
        if is_empty_return:
            return self.endproc(quad)
        current_scope = self.CallStack.peek()
        fname = current_scope.function_name
        retval = self.get_value(quad[1])
        self.FunDir[fname]['value'] = retval
        self._returns_value = True
        return self.endproc(None)

    # Special functions
    # ---------------------------------------------------------------
    def _print(self, quad):
        arg = self.get_value(quad[3])
        if isinstance(arg, bool):
            arg = str(arg).lower()
        print(arg)
示例#4
0
文件: Memory.py 项目: jgalvan/idle
class ClassMemory(Memory):
    def __init__(self):
        super().__init__()
        self.__func_memory_stack = Stack()
        self.__next_func = Stack()

    def set_reference(self, reference, address):
        if self.get_type(address) == DataType.POINTER:
            actual_address = self.__func_memory_stack.peek().get_value(address)
            self.set_reference(reference, actual_address)
        elif address % 10 == CompilationMemory.INSTANCE_ID:
            super().set_reference(reference, address)
        else:
            self.__func_memory_stack.peek().set_reference(reference, address)

    def get_reference(self, address):
        if self.get_type(address) == DataType.POINTER:
            actual_address = self.__func_memory_stack.peek().get_value(address)
            return self.get_reference(actual_address)
        elif address % 10 == CompilationMemory.INSTANCE_ID:
            return super().get_reference(address)
        else:
            return self.__func_memory_stack.peek().get_reference(address)

    def set_value(self, value, address):
        if self.get_type(address) == DataType.POINTER:
            actual_address = self.__func_memory_stack.peek().get_value(address)
            self.set_value(value, actual_address)
        elif address % 10 == CompilationMemory.INSTANCE_ID:
            super().set_value(value, address)
        else:
            self.__func_memory_stack.peek().set_value(value, address)

    def get_value(self, address):
        if self.get_type(address) == DataType.POINTER:
            actual_address = self.__func_memory_stack.peek().get_value(address)
            return self.get_value(actual_address)
        if address % 10 == CompilationMemory.INSTANCE_ID:
            return super().get_value(address)
        else:
            return self.__func_memory_stack.peek().get_value(address)

    def set_pointer_address(self, pointer_address, pointing_address):
        self.__func_memory_stack.peek().set_value(pointing_address, pointer_address)

    def era_func(self, func_start):
        self.__next_func.push(LocalMemory(func_start))

    def send_param(self, value, address):
        self.__next_func.peek().set_value(value, address)
    
    def send_param_by_ref(self, reference, address):
        self.__next_func.peek().set_reference(reference, address)

    def can_return(self):
        return self.__func_memory_stack.size() > 1

    def curr_func_last_instruction(self):
        return self.__func_memory_stack.peek().last_instruction

    def prev_func_last_instruction(self):
        return self.__func_memory_stack.peek_next_to_last().last_instruction

    def return_value(self, value, address):
        self.__func_memory_stack.peek_next_to_last().set_value(value, address)
    
    def goto_next_func(self):
        self.__func_memory_stack.push(self.__next_func.pop())

    def end_func(self):
        self.__func_memory_stack.pop()

    def next_instruction(self):
        if self.__func_memory_stack.peek() != None:
            return self.__func_memory_stack.peek().next_instruction()

        return None
    
    def goto(self, counter):
        self.__func_memory_stack.peek().goto(counter)
        
    def __str__(self):
        representation = "----------------------\n"
        representation += "CLASS MEMORY:\n"
        representation += super().__str__() + "\n"

        representation += "\nFUNCTION MEMORY:\n"
        for fnc in self.__func_memory_stack.items:
            representation += fnc.__str__() + "\n"

        representation += "\nNEXT FUNCTIONS:\n"
        for fnc in self.__next_func.items:
            representation += fnc.__str__() + "\n"

        representation += "CONSTANTS:\n"
        representation += self.CONSTANTS.__str__() + "\n"

        representation += "----------------------\n"
        
        return representation
示例#5
0
class nintCompiler:
    """docstring for nintCompiler"""
    def __init__(self):
        super().__init__()
        self.OperatorStack = Stack()
        self.OperandStack = Stack()
        self.TypeStack = Stack()
        self.JumpStack = Stack()
        self.GScope = Env(None, MemType.GLOBAL)  # Global env?

        # Function helpers
        self._found_main = False
        self._print = False
        self._current_func = None
        self._call_proc = None
        self._func_returned = None  # Check if the current function has returned something
        self._param_k = None
        # Generate the global scope and insert it into the functions directory
        # gscope = Function(GLOBAL, None, VOID)
        # self.FunDir.insert(gscope) # TODO: are objects passed by reference here?
        self.ScopeStack = Stack()  # Keep track of the current Scope
        self.ScopeStack.push(self.GScope)

        # Temporal memory
        self._Temporal = Temp()
        self._TempStack = Stack()
        self._TempStack.push(self._Temporal)

        self.quads = []

        # Add GOTO main
        self.quads.append([Operator.GOTO.value, None, None, None])

    def __getattribute__(self, attr):
        method = object.__getattribute__(self, attr)
        # if not method:
        # 	raise Exception("Method %s not implemented" % attr)
        if callable(method):
            name = method.__code__.co_name
            if name not in no_check and not name.startswith('procedure'):
                self.__check_main()
        return method

    def __check_main(self):
        if not self._found_main and self._current_func is None:
            self._found_main = True
            self.quads[0][3] = len(self.quads)

    def serialize(self, filename="out.nint.bytecode"):
        '''Serialize the quads and the quads into an intermediate
		obj file to be read by the VM.'''
        const_map = dict()
        for const_dict in Memory.CONST_TABLE.values():
            for const, var in const_dict.items():
                const_map[var.address] = utils.parseVar(var)

        temp_counters = self._Temporal._counters
        global_counters = self.GScope.memory._counters
        fun_dir = self.functionDirectory()

        data = [self.quads, const_map, fun_dir, global_counters, temp_counters]

        with open(filename, 'wb') as f:
            pickle.dump(data, f, pickle.HIGHEST_PROTOCOL)

    def functionDirectory(self):
        result = dict()
        for func in self.GScope.functions:
            result.update({func.name: func.size_map})
        return result

    def intercode(self):
        '''Print the quadruples'''
        for i, quad in enumerate(self.quads):
            if debug_mode == 'debug':
                print("{}) ".format(i), end='')
            print('\t'.join(map(printable, quad)))

    def add_var_declaration(self, type_str: str, identifier: str):
        '''Add variable to the varsTable of the current context'''
        debug("add_var_declaration")
        current_scope = self.ScopeStack.peek()
        # Check if it's not already been defined
        if current_scope.exists(identifier):
            raise Exception(
                'Double declaration. {} has already been declared in this context.'
                .format(identifier))

        dtype = mapType(type_str)
        address = current_scope.memory.next_address(dtype)

        var = Variable(identifier, dtype, address)
        current_scope.insert(var)

    def add_var(self, token: str):
        '''Add variable to the operand stack'''
        debug("add_var")

        current_scope = self.ScopeStack.peek()
        variable = current_scope.get(token)
        # Check that the variable has already been declared _somewhere_ (could be current, could be upper scopes)
        assert variable is not None, "Name {} has not been declared.".format(
            token)

        debug("add_var: OperandStack.push({})".format(variable.name))
        debug("add_var: TypeStack.push({})".format(variable.dtype))
        self.OperandStack.push(variable)
        self.TypeStack.push(variable.dtype)

        debug()

    def add_constant(self, token, dtype: DType):
        '''Adds a constant to the operand stack'''
        debug("add_constant")
        debug("Operand.push({})".format(token))
        debug("TypeStack.push({})".format(dtype))
        debug()
        self.OperandStack.push(Memory.constant(
            dtype, token))  # TODO: should we parse?
        self.TypeStack.push(dtype)
        debug()

    def add_operator(self, op: str):
        ''' Adds operator op to the OperatorStack'''
        debug("add_operator")
        debug("Operator.push({})".format(op))
        operator = Operator(op)
        self.OperatorStack.push(operator)  # TODO: change this to an enum
        debug()

    # TODO: refactor the check_* functions
    def check_relop(self):
        debug("check_relop")
        top = self.OperatorStack.peek()
        debug('top: {}'.format(top))
        if top not in RELOPS:
            return
        right_operand = self.OperandStack.pop()
        right_type = self.TypeStack.pop()
        left_operand = self.OperandStack.pop()
        left_type = self.TypeStack.pop()
        operator = self.OperatorStack.pop()
        debug((operator, left_type, right_type))
        result_type = SemanticCube.check(operator, left_type, right_type)
        # Relational operators *always* return a boolean
        assert result_type is DType.BOOL
        result = self._TempStack.peek().next(result_type)
        debug("Adds quad")
        self.quads.append((operator.value, left_operand.address,
                           right_operand.address, result.address))
        self.OperandStack.push(result)
        self.TypeStack.push(result_type)
        debug()

    def check_and(self):
        debug("check_and")
        top = self.OperatorStack.peek()
        debug('top: {}'.format(top))
        if top is not Operator.AND:
            return
        right_operand = self.OperandStack.pop()
        right_type = self.TypeStack.pop()
        left_operand = self.OperandStack.pop()
        left_type = self.TypeStack.pop()
        operator = self.OperatorStack.pop()
        debug((operator, left_type, right_type))
        result_type = SemanticCube.check(operator, left_type, right_type)
        # Relational operators *always* return a boolean
        assert result_type is DType.BOOL
        result = self._TempStack.peek().next(result_type)
        debug("Adds quad")
        self.quads.append((operator.value, left_operand.address,
                           right_operand.address, result.address))
        self.OperandStack.push(result)
        self.TypeStack.push(result_type)
        debug()

    def check_or(self):
        debug("check_or")
        top = self.OperatorStack.peek()
        debug('top: {}'.format(top))
        if top is not Operator.OR:
            return
        right_operand = self.OperandStack.pop()
        right_type = self.TypeStack.pop()
        left_operand = self.OperandStack.pop()
        left_type = self.TypeStack.pop()
        operator = self.OperatorStack.pop()
        debug((operator, left_type, right_type))
        result_type = SemanticCube.check(operator, left_type, right_type)
        # Relational operators *always* return a boolean
        assert result_type is DType.BOOL
        result = self._TempStack.peek().next(result_type)
        debug("Adds quad")
        self.quads.append((operator.value, left_operand.address,
                           right_operand.address, result.address))
        self.OperandStack.push(result)
        self.TypeStack.push(result_type)
        debug()

    def check_eqop(self):
        debug("check_eqop")
        # TODO: implement this
        top = self.OperatorStack.peek()
        if top is not Operator.EQUAL and top is not Operator.NEQ:
            return
        right_operand = self.OperandStack.pop()
        right_type = self.TypeStack.pop()
        left_operand = self.OperandStack.pop()
        left_type = self.TypeStack.pop()
        operator = self.OperatorStack.pop()
        debug((operator, left_type, right_type))
        result_type = DType.BOOL
        # result_type = SemanticCube.check(operator, left_type, right_type)
        # TODO: rn we allow comparison of everything vs everything, is this correct?
        # TODO: what if we compare a function name to a variable? (e.g. uno == 2)
        result = self._TempStack.peek().next(result_type)
        debug("Adds quad")
        self.quads.append((operator.value, left_operand.address,
                           right_operand.address, result.address))
        self.OperandStack.push(result)
        self.TypeStack.push(result_type)

        debug()

    def check_addsub(self):
        debug('check_addsub')
        top = self.OperatorStack.peek()

        if not (top == Operator.ADD or top == Operator.SUB):
            return

        right_operand = self.OperandStack.pop()
        right_type = self.TypeStack.pop()
        left_operand = self.OperandStack.pop()
        left_type = self.TypeStack.pop()
        operator = self.OperatorStack.pop()
        debug((operator, left_operand.name, right_operand.name))

        result_type = SemanticCube.check(operator, left_type, right_type)
        result = self._TempStack.peek().next(result_type)
        self.OperandStack.push(result)
        self.TypeStack.push(result_type)

        debug("Adds quad")
        self.quads.append((operator.value, left_operand.address,
                           right_operand.address, result.address))

        debug()

    def check_multdiv(self):
        debug('check_multdiv')
        top = self.OperatorStack.peek()

        if not (top == Operator.MULT or top == Operator.DIV):
            return

        right_operand = self.OperandStack.pop()
        right_type = self.TypeStack.pop()
        left_operand = self.OperandStack.pop()
        left_type = self.TypeStack.pop()
        operator = self.OperatorStack.pop()
        debug((operator, left_type, right_type))

        result_type = SemanticCube.check(operator, left_type, right_type)
        result = self._TempStack.peek().next(result_type)
        self.OperandStack.push(result)
        self.TypeStack.push(result_type)

        debug("Adds quad")
        self.quads.append((operator.value, left_operand.address,
                           right_operand.address, result.address))

        debug()

    # If-Else
    # --------------------------------------------
    def ifelse_start_jump(self):
        debug("ifelse_start_jump")
        expression_type = self.TypeStack.pop()
        if expression_type != DType.BOOL:
            raise Exception(
                "Type mismatch on line {}".format('SOMELINE TODO FIX THIS'))
            # TODO: maybe make a static function for this?
        result = self.OperandStack.pop()
        self.quads.append([Operator.GOTOF.value, result.address, None, None])
        self.JumpStack.push(
            len(self.quads) - 1
        )  # TODO: definitely change this. There has to be a better way to do this
        debug()

    def ifelse_end_jump(self):
        debug("ifelse_end_jump")
        counter = len(self.quads)  # TODO: change this
        debug('counter: {}'.format(counter))
        self.fill(self.JumpStack.pop(), counter)
        debug()

    def fill(self, pending_jump_pos, jump_location):
        debug("fill")
        self.quads[pending_jump_pos][
            3] = jump_location  # TODO: definitely make a class for this

    def ifelse_start_else(self):
        debug("ifelse_start_else")
        debug("ADD ELSE QUAD GOTO")
        self.quads.append([Operator.GOTO.value, None, None, None])

        # Fill the false condition jump of the `if`
        if_false_jump = self.JumpStack.pop()
        counter = len(self.quads)  # TODO: counter
        self.JumpStack.push(counter - 1)
        self.fill(if_false_jump, counter)

    def assignment_quad(self):
        debug("assignment_quad")
        operator = self.OperatorStack.pop()

        assert operator == Operator.ASSIGN

        right_operand = self.OperandStack.pop()
        right_type = self.TypeStack.pop()
        left_operand = self.OperandStack.pop()
        left_type = self.TypeStack.pop()

        debug("<{}> = <{}>".format(left_type, right_type))

        # TODO: probably change this
        # TODO: these don't need to be *exactly* the same, they just need to be compatible
        # example: float a = 10
        assert right_type == left_type, "Type mismatch: assignment does not match"

        self.quads.append((operator.value, right_operand.address, None,
                           left_operand.address))

        debug()

    # While
    # --------------------------------------------
    def while_condition_start(self):
        debug("while_condition_start")
        counter = len(self.quads)  # TODO: counter
        self.JumpStack.push(counter)

    def while_block_start(self):
        debug("while_block_start")
        expression_type = self.TypeStack.pop()
        if expression_type != DType.BOOL:
            raise Exception(
                "Type mismatch on line {}".format("SOMELINE FIX THIS TODO"))
        result = self.OperandStack.pop()
        debug("ADD QUAD: while block_start GOTOF")
        self.quads.append([Operator.GOTOF.value, result.address, None, None])
        counter = len(self.quads)
        self.JumpStack.push(counter - 1)  # TODO: counter

    def while_end(self):
        debug("while_end")

        pending_while_end_jump = self.JumpStack.pop()
        return_pos = self.JumpStack.pop()

        debug("ADD QUAD: while_end GOTO return")
        self.quads.append([Operator.GOTO.value, None, None, return_pos])

        counter = len(self.quads)  # TODO: change this
        self.fill(pending_while_end_jump, counter)

    # Function definitions
    # --------------------------------------------
    def procedure_start(self, name: str):
        '''Insert procedure name into dirfunc table, verify semantics'''
        debug('procedure_start')
        current_scope = self.ScopeStack.peek()
        # Check that it's not already defined in the **current** scope
        if current_scope.exists(name) or name in special_functions:
            raise Exception('The name {} is already defined'.format(name))
        func = Function(name, current_scope)
        current_scope.insert(func)
        self._current_func = func
        self._func_returned = False
        self.ScopeStack.push(func.varsTable)
        self._TempStack.push(func.varsTable.memory)

    def procedure_add_params(self, params):
        '''Add the total params to the current function'''
        debug('procedure_add_params')
        for param in params:
            self.procedure_add_param(param['type'], param['id'])

    def procedure_add_param(self, type_str: str, pname: str):
        '''Call function.add_param()'''
        debug('procedure_add_param')

        assert self._current_func is not None

        current_scope = self.ScopeStack.peek()

        if current_scope.exists(pname):
            raise Exception(
                'Redefinition of parameter {} in function signature'.format(
                    pname))

        data_type = mapType(type_str)
        address = current_scope.memory.next_address(data_type)
        var = Variable(pname, data_type, address)
        self._current_func.add_param(var)

    def procedure_mark_start(self):
        '''Mark the current quadruple counter as the start of this function'''
        debug('procedure_mark_start')
        self._current_func.start_pos = len(self.quads)

    def procedure_set_type(self, type_str: str):
        '''Set the return type of this function'''
        self._current_func.update_type(mapType(type_str))

    def procedure_update_size(self):
        '''Once we know the number of temps, and local variables defined, we can update the size'''
        # TODO: define this
        debug('procedure_update_size')

    def procedure_return(self, returns_expresion=False):
        '''Generate a RETURN command'''
        debug('procedure_return')
        retval = None
        if returns_expresion:
            retval = self.OperandStack.pop()
            retval_type = self.TypeStack.pop()
            assert self._current_func.dtype == retval_type, 'Type mismatch: value returned does not match function signature.'
            self._func_returned = True
            retval = retval.address
        else:
            assert self._current_func.is_void, 'Type mismatch: no value returned in non-void function.'
        self.quads.append((Operator.RETURN.value, retval, None, None))
        debug()

    def procedure_end(self):
        '''Generate an ENDPROC'''
        debug('procedure_end')
        # TODO: release the current vartable?
        # TODO: resolve any ERAs here
        # self.procedure_update_size()

        # if function is non-void, it returned something
        if not self._current_func.is_void and not self._func_returned:
            raise Exception("Non-void function must return a valid value.")

        self.quads.append((Operator.ENDPROC.value, None, None, None))
        debug(('FUNCTION TYPE:', self._current_func.dtype))

        self._current_func = None
        self.ScopeStack.pop()
        self._TempStack.pop()
        debug()

    # Function calls
    # --------------------------------------------
    def method_call_start(self, method_name):
        '''Verify that the procedure exists in DirFunc'''
        debug('method_call_start')

        if not self.GScope.exists(method_name):
            raise Exception(
                'Method {} has not been defined'.format(method_name))
        call_proc = self.GScope.find(
            method_name
        )  # Assumes all functions are defined in the global scope
        assert call_proc is not None
        self._call_proc = call_proc
        debug()

    def method_call_param_start(self):
        '''Start reading parameters for function call'''
        debug('method_call_param_start')
        debug("Start param k counter at 0")
        self._param_k = 0
        fname = self._call_proc.name
        # TODO: add stack/list to keep track of these
        self.quads.append([Operator.ERA.value, fname, None,
                           None])  # ActivationRecord expansion
        debug()

    def method_call_param(self):
        '''Get the kth parameter for the function call and perform semantic validations'''
        param = self.OperandStack.pop()
        param_type = self.TypeStack.pop()

        assert self._param_k is not None

        assert self._param_k < len(self._call_proc.param_list)

        kth_param = self._call_proc.param_list[self._param_k]
        kth_param_type = kth_param.dtype

        # Check param_type
        # TODO: estos no **tienen** que ser iguales, solo compatibles
        assert param_type == kth_param_type  # TODO: Aqui es donde entra el cubo semantico

        self.quads.append(
            (Operator.PARAM.value, param.address, None, kth_param.address))

    def method_call_param_end(self):
        '''Verify the last parameter points to null'''
        # i.e. we consumed all of the parameters
        # i.e. the parameter call matches the function definition
        # NOTE: when the argument list ends, the k pointer should be pointing at the last elem (len-1)
        arglength = len(self._call_proc.param_list)
        if arglength == 0:
            assert self._param_k == 0, "Parameter count mismatch."
        else:
            assert self._param_k == arglength - 1, "Parameter count mismatch."

    def method_call_end(self):
        '''Generate a GOSUB to take control flow the procedure'''
        debug('method_call_end')
        func = self._call_proc
        name = func.name
        init_address = func.start_pos
        is_void = func.is_void
        return_type = func.dtype

        self._call_proc = None
        self._param_k = None
        self.quads.append((
            Operator.GOSUB.value, name, None,
            init_address))  # TODO: migaja de pan pa saber donde voy a regresar

        # If the function returned something, we should assign it to a local temporary var
        if not is_void:
            result = self._TempStack.peek().next(return_type)
            self.OperandStack.push(result)
            self.TypeStack.push(return_type)
            self.quads.append(('=', name, None, result.address))
        else:  # TODO: test this thoroughly, not sure if this is going to work
            assert self.OperatorStack.peek(
            ) is not Operator.ASSIGN, 'Void function does not return anything. Cannot assign void value.'

        debug()

    def print_start(self):
        self._print = True

    def print_end(self):
        self._print = False

    def print_expression(self):
        debug("print_expression")
        assert self._print
        op = self.OperandStack.pop()
        self.TypeStack.pop()
        self.quads.append((Operator.PRINT.value, None, None, op.address))
        debug()

    def paren_open(self):
        self.OperatorStack.push(Operator.FAKE)

    def paren_close(self):
        self.OperatorStack.pop()