class VirtualMachine(): '''Clase virtual machine para ejecutar cuadruplos Recibe functions directory(fd) como apoyo''' def __init__(self, fd): #Lista de cuadruplos self.quadruples = [] # Program counter self.pc = 0 # Directorio de funciones self.fd = fd # Diccionario auxiliar self.temporal = {} # Instancia de memoria self.memory = Memory() # Scope del programa self.scope = 'main' # Ultima funcion llamada self.last_func = None # Stack de contadores pendientes self.PCS = Stack() self.pibool = False # Bool para saber si se llamaron metodos graficos self.called_graphics = False # Instancia de graficas self.window = GraphicsConstructor() def orderParams(self): '''Parsea cuadruplos para mejor ejecucion de llamadas a funciones''' aux = Stack() pc = 0 while self.quadruples[pc].op != 'END': quad = self.quadruples[pc] if quad.op == 'Param': aux.top.append(quad) del self.quadruples[pc] elif quad.op == 'Gosub': self.quadruples = self.quadruples[: pc] + aux.top + self.quadruples[ pc:] pc = pc + 1 + len(aux.top) aux.pop() elif quad.op == 'ERA': aux.push([]) pc += 1 else: pc += 1 def readFiles(self): '''Lee output y actualiza lista de cuadruplos''' cont = 0 f = open('output.cob', 'r') for line in f: # print(line), q = line[1:-2].split(',') quad = Quadruple(cont, striptLR(q[0]), striptLR(q[1]), striptLR(q[2]), striptLR(q[3])) cont += 1 self.quadruples.append(quad) f.close() def run(self): ''' Function that start reading all the quadruples and executing them''' # start reading the file self.readFiles() self.orderParams() #cycle until read the last quadruple 'END' while self.quadruples[self.pc].op != 'END': # get the current quad according to Program Counter(pc) quad = self.quadruples[self.pc] operation = quad.op # check all posible operations and do something according to if operation == 'ERA': # saves the name of the last function to use in Params and Gosub self.last_func = quad.left_operand elif operation == 'EndProc': # release the memory used in the function self.memory.endproc() # stablish pc with the value that called the function self.pc = self.PCS.pop() self.scope = 'main' elif operation == 'Return': self.set_memory_val(quad, 'Return') elif operation == 'Param': if not self.pibool: # allocate memory for the function that is going to be called self.memory.era() self.pibool = True self.set_memory_val(quad, 'Param') elif operation == 'Verify': valor = self.get_memory_val(quad.left_operand) if valor < int(quad.right_operand) or valor >= int(quad.res): raise IndexError('Error 7002: Index out of range') elif operation == 'Print': val = self.get_memory_val(quad.left_operand) # obtiene como quiere terminar el print endprint = quad.right_operand if endprint == '\\n': print(val) else: print(val, end=endprint[1:-1]) elif operation == 'Read': var_type = None # se realiza el input en formato raw temp = raw_input(">>> ") # verifica si va a guardar en una variable dimensionada if '.' in quad.res: # parsea para variable dimensionada if quad.res.count('.') > 1: components = quad.res.split('.') while len(components) > 2: index = components.pop() index = self.get_memory_val(index) var = components.pop() aux = var + '.' + str(index) val = self.memory.get_val(aux) components.append(str(val)) index = self.get_memory_val(components[1]) helper = components[0] + '.' + str(index) # se obtiene el size y type de la variable a la que se va a guardar ya sea # en el scope global o alguno local if self.fd.functions[self.scope].variables_dict.get( components[0][2:], None) is None: variable = self.fd.functions[ 'global'].variables_dict.get(components[0][2:]) var_size = variable.size var_type = variable.type else: variable = self.fd.functions[ self.scope].variablest_dict.get( components[0][2:]) var_size = variable.size var_type = variable.type else: # cuando estan varias variables dimensionadas anidadas res = quad.res.split('.') var = res[0] index = self.get_memory_val(res[1]) helper = var + '.' + str(index) # se obtiene el size y type de la variable a la que se va a guardar ya sea # en el scope global o alguno local if self.fd.functions[self.scope].variables_dict.get( var[2:], None) is None: variable = self.fd.functions[ 'global'].variables_dict.get(var[2:]) var_size = variable.size var_type = variable.type else: variable = self.fd.functions[ self.scope].variables_dict.get(var[2:]) var_size = variable.size var_type = variable.type # si la variable a la que se va a guardar es de tipo booleano se manda llamar un error if var_type == 'bool': raise TypeError( "Error 6005: Can't read to bool variable") try: # se intenta castear el valor del usuario al tipo de la variable en donde se va a guardar if var_type == 'int': temp = int(temp) elif var_type == 'float': temp = float(temp) except Exception: raise TypeError("Error 6004: Invalid Input Type") self.memory.set_val(helper, temp, var_size) else: # si no es un arreglo se obtiene el var type if self.fd.functions[self.scope].variables_dict.get( quad.res[2:], None) is None: variable = self.fd.functions[ 'global'].variables_dict.get(quad.res[2:]) var_type = variable.type else: variable = self.fd.functions[ self.scope].variables_dict.get(quad.res[2:]) var_type = variable.type # si la variable a la que se va a guardar es de tipo booleano se manda llamar un error if var_type == 'bool': raise TypeError( "Error 6005: Can't read to bool variable") try: # se intenta castear el valor del usuario al tipo de la variable en donde se va a guardar if var_type == 'int': temp = int(temp) elif var_type == 'float': temp = float(temp) except Exception: raise TypeError("Error 6004: Invalid Input Type") self.memory.set_val(quad.res, temp) #basic operations +, - , *, / elif operation == '+': self.execute(quad, '+') elif operation == '-': self.execute(quad, '-') elif operation == '*': self.execute(quad, '*') elif operation == '/': # muestra un error cuando se intenta hacer una division entre 0 if quad.right_operand == "0" or quad.right_operand == "0.0": raise ZeroDivisionError("Error 6003: Division by zero") self.execute(quad, '/') elif operation == '%' or operation == 'mod': # muestra error cuando se intenta hacer un modulo 0 if quad.right_operand == "0" or quad.right_operand == "0.0": raise ZeroDivisionError("Error 6006: Module by zero") self.execute(quad, '%') #assignment elif operation == '=': self.set_memory_val(quad, '=') #logic operations elif operation == 'and': self.execute(quad, 'and') elif operation == 'or': self.execute(quad, 'or') elif operation == '>=': self.execute(quad, '>=') elif operation == '<=': self.execute(quad, '<=') elif operation == '==': self.execute(quad, '==') elif operation == '>': self.execute(quad, '>') elif operation == '<': self.execute(quad, '<') elif operation == '!=': self.execute(quad, '!=') # gotos elif operation == 'Goto': self.pc = int(self.quadruples[self.pc].res) continue elif operation == 'GotoF': dir = quad.left_operand.lstrip().rstrip() if not self.get_memory_val(dir): self.pc = int(quad.res) continue # if memory.getVal(dir) == 'false': # self.pc = quad.res elif operation == 'GotoV': dir = quad.left_operand.lstrip().rstrip() if self.get_memory_val(dir): self.pc = int(quad.res) continue elif operation == 'Gosub': self.pibool = False # si el gosub es a una funcion especial if quad.left_operand in custom_functions: self.called_graphics = True params_dict = self.memory.doubles.temporal.top.copy() params_dict.update(self.memory.strings.temporal.top.copy()) params_dict.update( self.memory.integers.temporal.top.copy()) self.window.construct(quad.left_operand, params_dict) self.memory.endproc() else: self.scope = quad.left_operand[2:] self.PCS.push(self.pc) self.pc = int(self.quadruples[self.pc].res) continue # print self.quadruples[self.pc].printeame() self.pc += 1 if self.called_graphics: self.window.display() def set_memory_val(self, quad, op): left = quad.left_operand if op == 'Param': valor = self.get_memory_val(left, True) else: valor = self.get_memory_val(left) res = quad.res # se intenta castear a float o a int dependiendo de la variable donde se va a guardar if res[0] == "d": valor = float(valor) if res[0] == "i": valor = int(valor) if op == 'Return': self.memory.set_val(self.last_func, valor) elif op == 'Param' or op == '=' or op == 'Read': is_param = False if op == 'Param': is_param = True # parsea si es una variable dimensionada if '.' in quad.res: if quad.res.count('.') > 1: components = quad.res.split('.') while len(components) > 2: index = components.pop() index = self.get_memory_val(index, is_param) var = components.pop() aux = var + '.' + str(index) val = self.memory.get_val(aux, is_param) components.append(str(val)) index = self.get_memory_val(components[1], is_param) helper = components[0] + '.' + str(index) # obtiene el size de la variable if self.fd.functions[self.scope].variables_dict.get( components[0][2:], None) is None: var_size = self.fd.functions[ 'global'].variables_dict.get( components[0][2:]).size else: var_size = self.fd.functions[ self.scope].variablest_dict.get( components[0][2:]).size else: res = quad.res.split('.') var = res[0] index = self.get_memory_val(res[1], is_param) helper = var + '.' + str(index) # obtiene el size de la variable if self.fd.functions[self.scope].variables_dict.get( var[2:], None) is None: var_size = self.fd.functions[ 'global'].variables_dict.get(var[2:]).size else: var_size = self.fd.functions[ self.scope].variables_dict.get(var[2:]).size self.memory.set_val(helper, valor, var_size) else: self.memory.set_val(quad.res, valor) def get_memory_val(self, base, param=False): # intenta obtener valores de constantes if RepresentsInt(base): if self.memory.integers.constants.get(base, None) is None: self.memory.integers.constants[base] = int(base) valor = self.memory.integers.constants[base] elif RepresentsDouble(base): if self.memory.doubles.constants.get(base, None) is None: self.memory.doubles.constants[base] = float(base) valor = self.memory.doubles.constants[base] elif base[0] == "\"" or base[0] == "\'": if self.memory.strings.constants.get(base[1:-1], None) is None: self.memory.strings.constants[base[1:-1]] = base[1:-1] valor = self.memory.strings.constants[base[1:-1]] elif base == 'true': if self.memory.booleans.constants.get(base, None) is None: self.memory.booleans.constants[base] = 'true' valor = self.memory.booleans.constants[base] elif base == 'false': if self.memory.booleans.constants.get(base, None) is None: self.memory.booleans.constants[base] = 'false' valor = self.memory.booleans.constants[base] # si no es constante se busca el valor en la memoria else: # parsea cuando una variable es dimensionada if '.' in base: if base.count('.') > 1: components = base.split('.') while len(components) > 2: index = components.pop() index = self.get_memory_val(index, param) var = components.pop() aux = var + '.' + str(index) val = self.memory.get_val(aux, param) components.append(str(val)) index = self.get_memory_val(components[1], param) base = components[0] + '.' + str(index) else: res = base.split('.') var = res[0] index = self.get_memory_val(res[1], param) base = var + '.' + str(index) # trae el valor desde memoria valor = self.memory.get_val(base, param) return valor def execute(self, quad, op): left = quad.left_operand right = quad.right_operand left_val = self.get_memory_val(left) right_val = self.get_memory_val(right) if left_val == 'ERROR get_val: 458': raise MemoryError('Error 5001: Error getting Value') elif right_val == 'ERROR get_val: 458': raise MemoryError('Error 5001: Error getting Value') else: if op == '+': res = left_val + right_val elif op == '-': res = left_val - right_val elif op == '*': res = left_val * right_val elif op == '/': if right_val == 0 or right_val == 0.0: raise ZeroDivisionError("Error 6003: Division by zero") res = left_val / right_val elif op == '%': res = left_val % right_val elif op == '<': res = left_val < right_val elif op == '>': res = left_val > right_val elif op == '==': res = left_val == right_val elif op == '!=': res = left_val != right_val elif op == '<=': res = left_val <= right_val elif op == '>=': res = left_val >= right_val elif op == 'and': res = left_val and right_val elif op == 'or': res = left_val or right_val self.memory.set_val(quad.res, res)