def detect_extended_for_loops(self): for_indices = [] for_stack = Stack() with open("/opt/lampp/htdocs/Blog/post views/view_post_image_phpcode.php", "r") as source_file: code = source_file.read() source = code.replace("\n", " ") # for_start_indices = [(w.start(0), w.end(0)) for w in re.finditer("while\s*\((.*?)\s*:", source)] for_start_indices = [f.start(0) for f in re.finditer("for\s*\((.*?)\s*:", source)] end_start_indices = [e.start(0) for e in re.finditer("endfor;", source)] for f_index in for_start_indices: for_indices.append(f_index) for e_index in end_start_indices: for_indices.append(e_index) for_indices.sort(reverse=False) for idx in for_indices: if idx in for_start_indices: for_stack.push(idx) elif idx in end_start_indices: if for_stack.size() == 1: while_start = for_stack.pop() file = source[while_start:idx] print(file) else: for_stack.pop()
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()
def forward_singlePath(self, modulelist, input, name): stack = Stack() k = 0 temp = [] for layers in modulelist: # use the pop-pull logic, looks like a stack if k == 0: temp = layers(input) else: # met a pooling layer, take its input if isinstance(layers, nn.AvgPool2d) or isinstance( layers, nn.MaxPool2d): stack.push(temp) temp = layers(temp) # met a unpooling layer, take its output if isinstance(layers, nn.Upsample): if name == 'offset': temp = torch.cat( (temp, stack.pop()), dim=1 ) # short cut here, but optical flow should concat instead of add else: temp += stack.pop( ) # short cut here, but optical flow should concat instead of add k += 1 return temp
class HamiltonChecker: def __init__(self, graph): self.hamiltonian_flag = False self.graph = graph self.visited = [False] * (len(graph.graph_representation) + 1) self.stack = Stack() self.hamiltonianPath = [] def check_hamilton(self, vertex): if not self.hamiltonian_flag: self.stack.push(vertex) self.visited[vertex] = True if self.stack.size() < len(self.graph.graph_representation): for neighbour in self.graph.graph_representation[vertex]: if self.visited[neighbour] == False: self.check_hamilton(neighbour) if self.stack.size() < len(self.graph.graph_representation): self.visited[self.stack.pop()] = False else: if self.stack.bottom( ) in self.graph.graph_representation[vertex]: self.hamiltonian_flag = True self.stack.push(self.stack.bottom()) self.hamiltonianPath = self.stack.getListFromStack() else: self.visited[self.stack.pop()] = False def is_hamiltionian(self): return (self.hamiltonian_flag, self.hamiltonianPath) def reset(self): self.hamiltonian_flag = False self.visited = [False] * len(graph.graph_representation) self.stack = Stack() self.hamiltonianPath = []
def unixpath(self, path: str) -> str: pathlist = path.split("/") stack = Stack() for p in pathlist: if p != "." and p != "..": stack.push(p) if p == "..": stack.pop() return "/".join(stack.toList())
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 get_navigation_files_details(self, file_list): indices = [] ul_stack = Stack() navigation_files_details = [] for file in file_list: with open(file, "r") as source_file: original_src = source_file.read().replace("\n", " ") original_src = original_src.replace("\t", "") ul_start_indices = [ w.start(0) for w in re.finditer("<ul", original_src) ] ul_end_indices = [ e.end(0) for e in re.finditer("</ul>", original_src) ] for ul_s_index in ul_start_indices: indices.append(ul_s_index) for ul_e_index in ul_end_indices: indices.append(ul_e_index) ul_segments = [] if indices.__len__() > 0: indices.sort(reverse=False) for idx in indices: if idx in ul_start_indices: ul_stack.push(idx) elif idx in ul_end_indices: if ul_stack.size() == 1: ul_start = ul_stack.pop() ul_segments.append(original_src[ul_start:idx]) else: ul_stack.pop() # ul_lists = re.findall("<ul(.*?)<\/ul>", original_src) if ul_segments.__len__() > 0: navigation_details = [] for ul_segment in ul_segments: navigators = re.findall("<li>(.*?)</li>", ul_segment) for nav in navigators: navigation_details.append(nav) if navigation_details.__len__() > 0: navigator = NavigationDO() navigator.set_nav_file_name(file) navigator.set_navigations(navigation_details) navigation_files_details.append(navigator) return navigation_files_details
def detect_extended_while_loops(self, source, file_name): indices = [] while_stack = Stack() dir_maker = FilesDirectoryMaker() target_php_base = "/home/shan/Developments/Projects/research-devs/python-devs/filehandler/phpSnippets" # --below commented code is to demonstrate the program from a single file # with open("/opt/lampp/htdocs/Blog/post views/view_post_image_phpcode.php", "r") as source_file: # code = source_file.read() # source = code.replace("\n", " ") # -- # for_start_indices = [(w.start(0), w.end(0)) for w in re.finditer("while\s*\((.*?)\s*:", source)] while_start_indices = [w.start(0) for w in re.finditer("while\s*\((.*?)\s*:", source)] end_start_indices = [e.end(0) for e in re.finditer("endwhile;", source)] for w_index in while_start_indices: indices.append(w_index) for e_index in end_start_indices: indices.append(e_index) # --while loop iterator i = 0 if indices.__len__() > 0: indices.sort(reverse=False) for idx in indices: if idx in while_start_indices: while_stack.push(idx) elif idx in end_start_indices: if while_stack.size() == 1: i = i + 1 while_start = while_stack.pop() while_file = source[while_start:idx] edited_while_file = while_file.replace(">", ">\n") dir_path = target_php_base + "/" + file_name dir_maker.create_target_directory(dir_path) while_file_path = dir_path + "/while_loop_" + i.__str__() + ".php" with open(while_file_path, "w") as file: file.write("<?php\n" + edited_while_file + "\n?>") source = source.replace(while_file, "include \"" + while_file_path + "\";") else: while_stack.pop() return source
def isValid(self, s: str) -> bool: mapping = {')': '(', '}': '{', ']': '['} stack = Stack() for char in s: if char in mapping: top = stack.pop() if len(stack) != 0 else '#' if mapping[char] != top: return False else: stack.push(char) return len(stack) == 0 # cpp solution # class Solution { # public: # bool isValid(string s) { # stack<char> stack; # for(auto c : s) { # if(c == '(' || c == '{' || c == '[') { # stack.push(c); # } # else if(!stack.empty() && # ((c == ')' && stack.top() == '(') # || (c == '}' && stack.top() == '{') # || (c == ']' && stack.top() == '['))) { # stack.pop(); # } # else { # return false; # } # } # return stack.empty(); # } # };
def test_push(self): s = Stack() s.push(3) self.assertEqual(s.toList(), [3])
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
class CodeGenerator: def __init__(self): # self.index = 2 self.index = 1 self.ss = Stack() self.symbol_table = Stack() self.scope_stack = Stack() self.scope_stack.push((0, None)) self.pb = [0] * 10000 self.data_index = 1000 self.temp_index = 5000 self.jmp_position_index = 7000 self.arg_index = 8000 self.return_values_index = 9000 self.current_arg = 0 self.in_rhs = False self.semantic_errors = [] self.variables_to_be_declared_before_main = [] def get_address_by_token(self, label): for symcell in self.symbol_table.stack: if symcell['token'] == label: if self.in_rhs and symcell['type'] == 'void': raise Exception('using return value of void function: {}.'.format(label)) return symcell['addr'] raise Exception("\'{}\' is not defined.".format(label)) def get_arg_address_by_token_and_num(self, func, num): for symcell in self.symbol_table.stack: if symcell['token'] == func and symcell.get('is_func', False): if num < len(symcell['args']): return symcell['args'][num] else: raise Exception('Mismatch in numbers of arguments of \'{}\'.'.format(func)) raise Exception("\'{}\' is not defined.".format(func)) def get_temp(self): res = self.temp_index self.temp_index += 4 return res def execute(self, func, token): key = token[2] if (token[0] == 'id' or token[0] == 'num') else token[0] try: func(self, key) except Exception as e: self.semantic_errors += ['#{}:\t{}\n'.format(str(token[1]), str(e))] self.print_pb() def print_pb(self): for ind, x in enumerate(self.pb): if x != 0: print(ind, x, sep='\t') print() def get_dict_by_address(self, address): for x in self.symbol_table.stack: addr = x.get('addr', None) if not addr: continue if addr == address or addr == int(address): return x def get_dict_by_token(self, token): for x in self.symbol_table.stack: if x['token'] == token: return x raise Exception("\'{}\' is not defined.".format(token)) def output_pb(self): with open('in_out/' + 'output.txt', 'w+') as f: for ind, x in enumerate(self.pb): if x != 0: l = '{}\t{}\n'.format((str(ind)), x) f.write(l)
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()
class ControlFlowGraph(object): __slots__ = ('_breakStack', '_continueStack', '_switchStack',\ '_scopeEntryStack', '_scopeExitStack', '_bblist','_stateStack',\ '_procName', '_uniq_id') def __init__(self, t): if not isinstance(t, Procedure): raise ControlFlowGraphException( "Invalid type for ControlFlowGraph, init").with_traceback( type(t)) self._procName = t.getIdentifier() self._uniq_id = 0 self._initStateStacks() self._setBBFromProc(t) def _initStateStacks(self): self._breakStack = Stack("%s_breakStack" % repr(self._procName)) self._continueStack = Stack("%s_continueStack" % repr(self._procName)) self._switchStack = Stack("%s_switchStack" % repr(self._procName)) self._scopeEntryStack = Stack("%s_scopeEntryStack" % repr(self._procName)) self._scopeExitStack = Stack("%s_scopeExitStack" % repr(self._procName)) self._bblist = [] self._stateStack = [self._breakStack, \ self._continueStack, \ self._switchStack, \ self._scopeEntryStack, \ self._scopeExitStack] for k in self._stateStack: k.setTrackStackUpdate(True) def graphIt(self): dotfile = open("%s.%s" % (repr(self._procName), "dot"), "w") dotfile.write("digraph %s { \n" % self._procName) #node [shape=plaintext]\n" % self._procName) for k in self._bblist: dotfile.write(k.getName() + " ") # dotfile.write("[label=<\n\t<TABLE>\n") if k.getStatements(): #dotfile.write("\t\t<TR><TD>%s</TD></TR>\n"%"Empty") dotfile.write("[label=<") for r in k.getStatements(): dotfile.write( self._fixForGraphIt(self._singleLineString(r))) #\t\t<TR><TD>\"%s\"</TD></TR>\n" \ #% self._fixForGraphIt(self._singleLineString(r))) #dotfile.write("\t</TABLE>>];\n") dotfile.write(">];\n") else: dotfile.write("[label=<EMPTY>];\n") for k in self._bblist: for r in k.getSuccs(): dotfile.write("%s -> %s;\n" % (k.getName(), r.getName())) dotfile.write("}\n") dotfile.close() def _setBBFromProc(self, t): p_BB = ProcBasicBlock(repr(self._procName)) self._scopeEntryStack.push(p_BB.entryBB, "to add %s" % p_BB.entryBB.getName()) self._scopeExitStack.push(p_BB.exitBB, "to add %s" % p_BB.exitBB.getName()) # Nice if we can add Entry & RETURN Annontation here? self._extendBBWithList([p_BB.entryBB, p_BB.exitBB]) self._dispatcher(t.getBody()) connect_last = self._pop("_scopeEntryStack", \ "last_one connects to procedure exit BB") self._addSuccPred(connect_last, p_BB.exitBB) def _dispatcher(self, t): self._testAndApply(t) def _testAndApply(self, t): if hasattr(self, '_do_' + t.__class__.__name__): getattr(self, '_do_' + t.__class__.__name__)(t) else: raise DispatchFunctionNotFoundError( 'testAndApply fail: <%s> not found during CFG setup' % str(type(t))) def _catchNotABasicBlockError(self, u): if isinstance(u, list): for k in u: if not isinstance(k, BasicBlock): raise NotABasicBlockError(type(k)) elif not isinstance(u, BasicBlock): raise NotABasicBlockError(type(u)) def _addSuccPred(self, u, v): self._catchNotABasicBlockError([u, v]) u.addSuccessor(v) v.addPredecessor(u) def _addBasicBlock(self, tag=None): if tag: k = BasicBlock("%s_%s" % (repr(self._procName), tag)) else: k = BasicBlock("%s_%s_%d" % (repr(self._procName), "L", self._getNextCount())) self._bblist.append(k) print("addBB:<%s>" % k) return k def _extendBBWithList(self, t): self._catchNotABasicBlockError(t) if isinstance(t, list): self._bblist.extend(t) else: self._bblist.append(t) def _push(self, aList, ntry, debugNfo=""): self._catchNotABasicBlockError(ntry) if hasattr(self, aList): aList = getattr(self, aList) aList.push(ntry, debugNfo) else: raise StackNotFoundError("Unknown Stack Variable requested: %s" % aList) def _pop(self, aList, debugNfo=""): if hasattr(self, aList): real_list = getattr(self, aList) k = real_list.pop(debugNfo) return k else: raise StackNotFoundError("Unknown Stack Variable requested: %s" % aList) def _peek(self, aList): if hasattr(self, aList): real_list = getattr(self, aList) return real_list.peek() else: raise StackNotFoundError("Unknown Stack Variable requested: %s" % aList) def _getNextCount(self): self._uniq_id += 1 return self._uniq_id - 1 def _singleLineString(self, k): import re str_repr = k.__class__.__name__ + '_' b4 = re.sub('\n|;', ' ', repr(k)) str_repr = b4 return str_repr def _fixForGraphIt(self, b4): import re for k in "!~`@#$%^&*()_-+=|\[];'/.,><?\"\:(){}": r = '\\' + k b4 = re.sub('\\' + k, r, b4) return b4 def _createBasicBlock(self, aName): bb = BasicBlock("%s_%s_%d" \ % (repr(self._procName), aName, self._uniq_id)) return bb def _do_ForLoop(self, t): # do the initialization and test if init < limit uniq_num = self._getNextCount() ie = t.getInit() eie, cie = list( map(ExpressionStatement, (t.getInit(), t.getCondition()))) eie.setParent(t) cie.setParent(t) # lose the predecessor BB pred = self._pop("_scopeEntryStack", "Predecessor BB: %s before processing for loop") # for_pre_header for_pre_header = self._createBasicBlock("FOR_PRE_HEADER") self._bblist.append(for_pre_header) self._addSuccPred(pred, for_pre_header) self._push("_scopeEntryStack", for_pre_header, \ "_for_pre_header: %s before processing init expression" \ % for_pre_header.getName()) self._dispatcher(eie) for_pre_header = self._pop("_scopeEntryStack", \ "_for_pre_header: %s after processing init expression" \ % self._peek("_scopeEntryStack").getName()) # for_header for_header = self._createBasicBlock("FOR_HEADER") self._bblist.append(for_header) self._addSuccPred(for_pre_header, for_header) self._push("_scopeEntryStack", for_header, \ "_for_header: %s before processing conditional expression" \ % for_header.getName()) self._dispatcher(cie) for_header = self._pop("_scopeEntryStack", \ "_for_header: %s after processing conditional expression" \ % self._peek("_scopeEntryStack").getName()) # for_exit for_exit = self._createBasicBlock("FOR_EXIT") self._bblist.append(for_exit) self._push("_breakStack", for_exit, "breaks must connect to _for_exit: %s" % for_exit.getName()) # for_step for_step = self._createBasicBlock("FOR_STEP") self._bblist.append(for_step) self._push("_continueStack", for_step, \ "continues must connect to _for_step: %s" % for_step.getName()) # for_body for_body = self._createBasicBlock("FOR_BODY") self._bblist.append(for_body) self._addSuccPred(for_header, for_body) self._push("_scopeEntryStack", for_body, \ "_for_body: %s before processing for body" \ % for_body.getName()) self._dispatcher(t.getBody()) for_body = self._pop("_scopeEntryStack", \ "_for_header: %s after processing for_body" \ % self._peek("_scopeEntryStack").getName()) self._addSuccPred(for_body, for_step) self._addSuccPred(for_step, for_header) self._addSuccPred(for_header, for_exit) self._push("_scopeEntryStack", for_exit, \ "_for_exit: %s after processing for_loop" \ % for_exit.getName()) def _do_WhileLoop(self, t): print(self._singleLineString(t)) def _do_DoLoop(self, t): print(self._singleLineString(t)) def _do_SwitchStatement(self, t): print(self._singleLineString(t)) def _do_ReturnStatement(self, t): print(self._singleLineString(t)) def _process_IfThen(self, t, ifthen): # call _dispatcher with then block, but add then to scopeEntryStack first # add edge between ifentry and then self._push("_scopeEntryStack", ifthen, \ "Pushing _if_then:%s into scopeEntryStack before then dispatch" \ % ifthen.getName()) thenStatement = t.getThenStatement() self._dispatcher(thenStatement) def _process_IfElse(self, t, ifelse): self._push("_scopeEntryStack", ifelse, \ "Pushing ElseStatement block:%s before processing ElseStatement" \ % ifelse.getName()) elseStatement = t.getElseStatement() self._dispatcher(elseStatement) def _handleIfStatement(self, t): uniq_num = self._getNextCount() pred = self._pop("_scopeEntryStack", \ "predecessor: %s b4 processing new IfStatement" \ % self._peek("_scopeEntryStack")) # process control expression # extract the control expression into an expression statement ce = t.getControlExpression() ece = ExpressionStatement(ce) ece.setParent(t) ifentry = self._createBasicBlock("IFENTRY") self._bblist.append(ifentry) self._push("_scopeEntryStack", ifentry, \ "_if_entry:%s into _scopeEntryStack for processing control expression" \ % ifentry.getName()) self._dispatcher(ece) ifentry = self._pop("_scopeEntryStack", "_if_entry:%s from _scopeEntryStack after processing control expression" \ % self._peek("_scopeEntryStack").getName()) # set up a continuing set of edges # edge from pred and ifentry # taken edge from ifentry and ifthen self._addSuccPred(pred, ifentry) ifthen = self._createBasicBlock("IFTHEN") self._bblist.append(ifthen) self._addSuccPred(ifentry, ifthen) self._process_IfThen(t, ifthen) # edge from ifthen to ifexit ifexit = self._createBasicBlock("IFEXIT") self._bblist.append(ifexit) self._addSuccPred(ifthen, ifexit) ifthen = self._pop("_scopeEntryStack", "_if_then:%s from _scopeEntryStack after processing then statement" \ % self._peek("_scopeEntryStack").getName()) # process else statement if any if t.getElseStatement(): ifelse = self._createBasicBlock("IFELSE") self._bblist.append(ifelse) # not taken edge from ifentry to ifelse self._addSuccPred(ifentry, ifelse) self._process_IfElse(t, ifelse) # add edge between ifelse and ifexit after processing ElseStatement self._addSuccPred(ifelse, ifexit) ifelse = self._pop("_scopeEntryStack", "_if_else:%s from scopeEntryStack after else dispatch" \ % ifelse.getName()) self._addSuccPred(ifelse, ifexit) else: self._addSuccPred(ifentry, ifexit) self._push("_scopeEntryStack", ifexit, \ "_if_exit:%s for use as pred" \ % ifexit.getName()) def _do_IfStatement(self, t): self._handleIfStatement(t) print(self._singleLineString(t)) def _do_ExpressionStatement(self, t): pred_bb = self._peek("_scopeEntryStack") pred_bb.addStatement(t) print(self._singleLineString(t)) def _do_DeclarationStatement(self, t): print(self._singleLineString(t)) def _do_ContinueStatement(self, t): print(self._singleLineString(t)) def _do_BreakStatement(self, t): print(self._singleLineString(t)) def _do_CompoundStatement(self, t): for k in t.getChildren(): self._testAndApply(k) print(self._singleLineString(t))
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 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)