def zigzag_level(self, root): if root is None: return curLevel = Stack() nextLevel = Stack() curLevel.push(root) ltr = True # left to right for level 1 while not curLevel.isEmpty(): temp = curLevel.pop() print(temp.val, end=" ") if ltr: # left to right if temp.left: nextLevel.push(temp.left) if temp.right: nextLevel.push(temp.right) else: # right to left if temp.right: nextLevel.push(temp.right) if temp.left: nextLevel.push(temp.left) if curLevel.isEmpty(): ltr = not ltr curLevel, nextLevel = nextLevel, curLevel print()
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
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])