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