Ejemplo n.º 1
0
class Compiler:
    def __init__(self):
        self.functions_table = FunctionsTable()
        self.current_function = Function("void",
                                         "global", [], {},
                                         function_memory=Memory(
                                             MemoryConstants.GLOBAL_INITIAL))
        self.semantic_cube = SemanticCube()
        self.quadruples = QuadruplesTable()
        self.operators_stack = []
        self.operands_stack = []
        self.jumps_stack = []
        self.types_stack = []
        self.temporal_memory = Memory(MemoryConstants.TEMPORAL_INITIAL)
        self.constant_memory = Memory(MemoryConstants.CONSTANT_INITIAL)
        self.constant_exec_memory = ExecMemory(
            MemoryConstants.CONSTANT_INITIAL)

    def add_function(self, function: Function):
        self.current_function.start_quadruple = self.quadruples.length()
        self.functions_table.add_function(function)
        if function.function_type != "void":
            self.functions_table.functions["global"].update_variables(
                function.function_type, function.function_name)

    def add_variable(self, variable_name):
        if variable_name in self.current_function.function_variables:
            variable = self.current_function.function_variables[variable_name]
            self.operands_stack.append(variable.variable_address)
            self.add_type(variable.variable_type)

        elif variable_name in self.functions_table.functions[
                "global"].function_variables:
            variable = self.functions_table.functions[
                "global"].function_variables[variable_name]
            self.operands_stack.append(variable.variable_address)
            self.add_type(variable.variable_type)

        elif variable_name in self.functions_table.functions:
            if self.functions_table.functions[
                    variable_name].function_type == 'void':
                print('ERROR: No se puede asignar una función void ' +
                      variable_name + ' a una variable')
            else:
                self.operands_stack.append(variable_name)
                self.add_type(self.functions_table.functions[variable_name].
                              function_type)
        else:
            print('ERROR: La variable ' + str(variable_name) +
                  ' no está declarada')

    def add_operator(self, operator):
        self.operators_stack.append(operator)
        # print('Operator: ', operator, self.operators_stack)

    def add_type(self, type):
        self.types_stack.append(type)
        # print('Type: ', type, self.types_stack)

    def add_constant_operand(self, operand, type):
        address = self.constant_memory.get_address(type)
        constant = Variable(type, operand, operand, address)
        self.constant_exec_memory.save_value(address, operand)
        self.operands_stack.append(constant.variable_address)
        self.types_stack.append(type)
        # print('Operand: ', operand, self.operators_stack)

    def left_parenthesis(self):
        self.operators_stack.append('(')
        # print('Parenthesis: (', self.operators_stack)

    def right_parenthesis(self):
        self.operators_stack.pop()
        # print('Parenthesis: )')

    def operation_quadruple(self):
        # print("Operation Quad Gen: ", self.operands_stack, self.operators_stack, self.types_stack)
        right_operand = self.operands_stack.pop()
        left_operand = self.operands_stack.pop()
        operator = self.operators_stack.pop()
        right_operand_type = self.types_stack.pop()
        left_operand_type = self.types_stack.pop()
        result_type = SemanticCube().check_operation(left_operand_type,
                                                     right_operand_type,
                                                     operator)
        if result_type == Types.ERROR:
            print(
                'ERROR: Los tipos de datos de la operación no son compatibles.'
            )
        else:
            # Create quadruple and add its type and and result to type and operand stacks
            result_address = self.temporal_memory.get_address(result_type)
            self.quadruples.append(operator, left_operand, right_operand,
                                   result_address)
            self.types_stack.append(result_type)
            self.operands_stack.append(result_address)

    def read_quadruple(self, operand):
        result_type = self.types_stack.pop()
        self.operands_stack.pop()
        if (operand in self.current_function.function_variables) or (
                operand in
                self.functions_table.functions["global"].function_variables):
            result_address = self.temporal_memory.get_address(result_type)
            self.quadruples.append("read", operand, None, result_address)
        else:
            print('ERROR: La variable ' + str(operand) + ' no está declarada')

    def write_quadruple(self):
        # print("Write Quad Gen: ", self.operands_stack[len(self.operands_stack)-1])
        if len(self.operands_stack) == 0:
            print(
                'ERROR: Se quiere hacer un print pero no hay operandos en el stack'
            )
        else:
            printed_operand = self.operands_stack.pop()
            self.types_stack.pop()
            self.quadruples.append("print", None, None, printed_operand)

    def assign_quadruple(self):
        # print("Assign Quad Gen: ", self.operators_stack[len(self.operators_stack)-1], self.operands_stack)
        if self.operators_stack[len(self.operators_stack) - 1] == '=':
            right_operand = self.operands_stack.pop()
            left_operand = self.operands_stack.pop()
            operator = self.operators_stack.pop()
            right_operand_type = self.types_stack.pop()
            left_operand_type = self.types_stack.pop()
            result_type = SemanticCube().check_operation(
                left_operand_type, right_operand_type, operator)
            if result_type == Types.ERROR:
                print(
                    'ERROR: Los tipos de datos de la asignación no son compatibles.'
                )
            self.quadruples.append(operator, right_operand, None, left_operand)
        else:
            print(
                'ERROR: Se entró a crear un quad de asignación pero no está el operando = en el stack.',
                self.operators_stack)

    def check_for_mult_or_div(self):
        # print("Mult/Div Check", len(self.operators_stack), self.operators_stack[-1:])
        if len(self.operators_stack) is not 0 and self.operators_stack[
                len(self.operators_stack) -
                1] in [Operations.MULTIPLICATION, Operations.DIVISION]:
            self.operation_quadruple()

    def check_for_add_or_subs(self):
        # print("Add/Sub Check", len(self.operators_stack), self.operators_stack[-1:])
        if len(self.operators_stack) > 0 and self.operators_stack[
                len(self.operators_stack) -
                1] in [Operations.ADDITION, Operations.SUBTRACTION]:
            self.operation_quadruple()

    def check_for_relational_operators(self):
        # print("Relational Check", len(self.operators_stack), self.operators_stack[-1:])
        if len(self.operators_stack) > 0 and self.operators_stack[
                len(self.operators_stack) - 1] in [
                    Operations.EQUAL, Operations.NOT_EQUAL,
                    Operations.GREATER_OR_EQUAL, Operations.LESS_OR_EQUAL,
                    Operations.GREATER, Operations.LESS
                ]:
            self.operation_quadruple()

    def check_for_logic_operators(self):
        # print("Logic Check", len(self.operators_stack), self.operators_stack[-1:])
        if len(self.operators_stack) > 0 and self.operators_stack[
                len(self.operators_stack) -
                1] in [Operations.AND, Operations.OR]:
            self.operation_quadruple()

    def check_for_assignment(self):
        # print("Asignment Check", len(self.operators_stack), self.operators_stack[-1:])
        if len(self.operators_stack) > 0 and self.operators_stack[
                len(self.operators_stack) - 1] == Operations.ASSIGN:
            self.operation_quadruple()

    def if_statement(self):
        if len(self.operands_stack) != 0:
            # Check if while statement is valid
            condition_var = self.operands_stack.pop()
            type_condition_var = self.types_stack.pop()
            # print(self.operands_stack, self.operators_stack, self.types_stack, self.jumps_stack)
            if type_condition_var == Types.BOOL:
                # Create GOTOF and save quad in jumps stack to fill when we know false jump location
                self.jumps_stack.append(self.quadruples.length())
                self.quadruples.append("GOTOF", condition_var, None, None)
            else:
                print('ERROR: ' + str(condition_var) +
                      ' no es un boolean, es ' + str(type_condition_var))

    def else_statement(self):
        self.quadruples.quads[
            self.jumps_stack.pop()].result = self.quadruples.length() + 1
        # Create GOTO and save quad in jumps stack to fill when we know jump location
        self.jumps_stack.append(self.quadruples.length())
        self.quadruples.append("GOTO", None, None, None)

    def end_if_else_function(self):
        # Fill if's GOTOF quad with current index
        self.quadruples.quads[
            self.jumps_stack.pop()].result = self.quadruples.length()

    def while_statement(self):
        # Save while statement quad in jumps stack to fill when we know jump location
        self.jumps_stack.append(self.quadruples.length())

    def while_statutes(self):
        if len(self.operands_stack) != 0:
            condition_var = self.operands_stack.pop()
            type_condition_var = self.types_stack.pop()
            if type_condition_var == Types.BOOL:
                # Save begin statutes quad in jumps stack to fill when we know jump location
                self.jumps_stack.append(self.quadruples.length())
                self.quadruples.append("GOTOF", condition_var, None, None)
            else:
                print('ERROR: ' + condition_var + ' es ' + type_condition_var +
                      'en vez de boolean.')

    def while_end(self):
        # Fill while statutes quad with ending of while
        while_statutes_index = self.jumps_stack.pop()
        self.quadruples.quads[
            while_statutes_index].result = self.quadruples.length() + 1
        # Get while statement index to generate GOTO quad to loop in while
        while_statement_index = self.jumps_stack.pop()
        self.quadruples.append("GOTO", None, None, while_statement_index)

    def from_initialize(self, operand):
        var_operand = None
        if operand in self.current_function.function_variables:
            var_operand = self.current_function.function_variables[operand]
        elif operand in self.functions_table.functions[
                "global"].function_variables:
            var_operand = self.functions_table.functions[
                "global"].function_variables[operand]
        # From variable was already declared locally or globally
        if var_operand:
            # Check if variable is INT to procede
            if (var_operand.variable_type == Types.INT):
                # Pop variable value and save
                from_variable_value = self.operands_stack.pop()
                self.types_stack.pop()
                self.quadruples.append("=", from_variable_value, None,
                                       var_operand.variable_address)
                self.current_function.function_variables[
                    'from_variable'] = Variable(Types.INT, 'from_variable',
                                                from_variable_value,
                                                var_operand.variable_address)
            else:
                print('ERROR: La variable ' + operand + 'no es un entero')
        else:
            print('ERROR: La variable ' + operand + ' no está declarada')

    def from_statutes(self):
        if len(self.operands_stack) != 0:

            # Get the initial and limit values of the from loop
            try:
                from_variable = self.current_function.function_variables[
                    'from_variable']
            except:
                print('ERROR: No se encontró la variable del loop desde-hasta')

            from_variable_limit_value = self.operands_stack.pop()
            from_variable_limit_type = self.types_stack.pop()

            if from_variable_limit_type == Types.INT:
                res_address = self.temporal_memory.get_address(Types.BOOL)
                self.jumps_stack.append(self.quadruples.length())
                self.quadruples.append("<", from_variable.variable_address,
                                       from_variable_limit_value, res_address)
                # Add point to jump stack and create GOTOF
                self.jumps_stack.append(self.quadruples.length())
                self.quadruples.append("GOTOF", res_address, None, None)
                self.add_constant_operand("1", Types.INT)
                self.types_stack.pop()
                self.quadruples.append("+", from_variable.variable_address,
                                       self.operands_stack.pop(),
                                       from_variable.variable_address)
            else:
                print(
                    'ERROR: La expresión asignada a la variable del from debe de ser un entero'
                )

    def end_from(self):
        # Fill from statutes quad with ending of while
        from_statutes_index = self.jumps_stack.pop()
        self.quadruples.quads[
            from_statutes_index].result = self.quadruples.length() + 1
        # Get from statutes index to generate GOTO quad to loop back to from
        from_statement_index = self.jumps_stack.pop()
        self.quadruples.append("GOTO", None, None, from_statement_index)

    def add_function_operand_type(self, operand):
        if self.functions_table.functions[operand].function_type == 'void':
            print('ERROR: No se le puede asignar a la función void ' +
                  operand + ' un valor.')
        else:
            self.operands_stack.append(operand)
            self.add_type(
                self.functions_table.functions[operand].function_type)

    def goto_main(self):
        self.jumps_stack.append(0)
        self.quadruples.append("GOTO", None, None, "_")

    def start_main(self):
        mainQuad = self.jumps_stack.pop()
        self.quadruples.quads[mainQuad].result = self.quadruples.length()

    def goto_function(self, id):
        self.quadruples.append(
            'GOSUB', None, None,
            self.functions_table.functions[id].start_quadruple)
        if self.functions_table.functions[id].function_type != "void":
            ret_type = self.functions_table.functions[id].function_type
            ret_address = self.temporal_memory.get_address(ret_type)
            self.quadruples.append(
                '=', self.functions_table.functions["global"].
                function_variables[id].variable_address, None, ret_address)
            self.operands_stack.append(ret_address)
            self.types_stack.append(ret_type)

    def create_era(self, function_name):
        self.quadruples.append("ERA", None, None, function_name)

    def add_parameters(self, function_name):
        for parameter in reversed(
                self.functions_table.functions[function_name].
                function_parameters):
            parameter_operand = self.operands_stack.pop()
            self.types_stack.pop()
            self.quadruples.append(
                "PARAM", parameter_operand, None,
                self.functions_table.functions[function_name].
                function_variables[parameter.variable_name].variable_address)

    def end_function(self):
        self.current_function.end_quadruple = self.quadruples.length()
        self.quadruples.append('ENDPROC', None, None, None)

    def return_end_function(self):
        if self.current_function.function_type != "void":
            self.current_function.end_quadruple = self.quadruples.length()
            return_address = self.functions_table.functions[
                "global"].function_variables[
                    self.current_function.function_name].variable_address
            self.types_stack.pop()
            self.quadruples.append('RETURN', self.operands_stack.pop(), None,
                                   return_address)
        else:
            print('ERROR: La función void ' +
                  self.current_function.function_name +
                  ' no puede tener un estatuto regresa.')

    def void_end_function(self):
        if self.current_function.function_type == "void":
            self.current_function.end_quadruple = self.quadruples.length()
            self.quadruples.append('GOTO', None, None, '_')
        else:
            print('ERROR: No se encontró valor de retorno en la función' +
                  self.current_function.name + 'de tipo ' +
                  self.current_function.function_type)

    def finish_program(self):
        self.quadruples.append("END", None, None, None)
        print("Quadruplos del Programa: ")
        self.quadruples.print()
        print('Stack de operandos: ', self.operands_stack)
        print('Stack de operadores: ', self.operators_stack)
        print('Stack de tipos', self.types_stack)
        print('Stack de saltos', self.jumps_stack)
        vm = VirtualMachine(self.quadruples, self.constant_exec_memory)
        vm.execute()
Ejemplo n.º 2
0
class StatementManager:
    in_local_scope = False

    def __init__(self):
        self.table = SymbolTable()
        self.oracle = SemanticCube()
        self.quads = QuadGenerator()
        self.memory = Memory()
        self.flow = FlowManager(self.quads, self.memory)
        # Creating the quad for the initial functions jump
        self.create_initial_jump()

    def get_symbol_from_name(self, id):
        '''
			Function that checks if an id exists in current scope or above
		'''
        return self.table.lookup(id)

    def create_initial_jump(self):
        '''
			Function that creates the initial jump where 
			the main functions are being executed
		'''
        # We create a new GOTO quad
        self.quads.store_jump()
        # Current index quad is at 2, we store the previous quad by adding -1
        self.flow.generate_goto()

    def start_class_scope(self, class_name, parent_name, params):
        '''
			Function that starts a new class scope in symbol table and enters it
		'''
        # We first have be sure that the class isn't already defined
        class_exists = self.get_symbol_from_name(class_name)
        # Cannot redeclare class
        if class_exists:
            raise Exception('Class name already defined')
        # Fetching the current number index of the quad
        quad_number = self.quads.current_index
        # Checks if it has a parent, if it does then it copies every attribute and function from it
        if parent_name:
            # Get parent address from symbol
            parent_symbol = self.table.get_class(parent_name)
            p_addr = parent_symbol.address
            # Storing the class in the symbol table and creating scope
            self.table.store_with_parent_symbols(
                parent_name, class_name,
                ClassSymbol(quad_number, class_name, parent_name, params),
                'class')
            # Generating the quad
            self.quads.generate(OpIds.inherit, 0, 0, p_addr)
        else:
            # Store the class symbol in the symbol table and creating scope
            self.table.store(
                class_name,
                ClassSymbol(quad_number, class_name, parent_name, params),
                'class')
        # Add params to new scope
        self.store_params(params, 'instance', OpIds.attr)
        # Creating a quad that indicates that its the end of the class' attributes
        self.quads.generate(OpIds.endattr, 0, 0, 0)

    def store_params(self, params, address_type, operation):
        '''
			Function that stores the params with a VariableSymbol that contains its 
			address type -instace, local-
		'''
        params.reverse()
        for param in params:
            # Assigns a new address for the current param
            new_address = self.memory.get_address(address_type, param[1])
            # Stores the variable symbol in the symbol table
            self.table.store(param[0], VariableSymbol(new_address, param[1]))
            # Generates a new quad with the given operation and the new address
            self.quads.generate(operation, 0, 0, new_address)

    def check_class_exists(self, class_name):
        '''
			Function that checks if a class, that is 
			being inherited from, exists, if it doesn't it throws
			and exception
		'''
        class_exists = self.table.get_class(class_name)
        if not class_exists:
            raise Exception('Class ' + class_name + ' doesn\'t exist')

    def start_function_scope(self, function_name, return_type, parameters):
        # Stores the function element
        self.memory.locals.expand()
        self.table.store(
            function_name,
            FunctionSymbol(self.quads.current_index, return_type, parameters),
            'function')
        self.in_local_scope = True
        self.store_params(parameters, 'local', OpIds.grab)

    def close_function_scope(self):
        '''
		Function that closes a function scope
		'''
        self.memory.locals.end_function()
        self.table.close_scope()
        self.in_local_scope = False
        self.quads.generate(OpIds.func_return, 0, 0, 0)

    def get_attrs_dict_for_class(self, class_name, args):
        # Gets symbol of the class
        class_symbol = self.table.get_class(class_name)
        # Make sure args are same type as params
        argument_types = list(map(lambda x: x[1], args))
        accepts = class_symbol.accepts_arguments(argument_types)
        if not accepts:
            raise Exception('Arguments do not match constructor for class \'' +
                            class_name + '\'.')
        attrs = {}
        # Matches class param with passed args
        temp_args = list(args)
        temp_args.reverse()
        class_attributes = zip(class_symbol.params, temp_args)
        for (param, arg) in class_attributes:
            attrs[param[0]] = arg
        return attrs

    def instantiate(self, class_name, args):
        class_symbol = self.table.get_class(class_name)
        attrs = self.get_attrs_dict_for_class(class_name, args)
        obj = self.new_var_of_type(class_name, 'temp', attrs)
        for arg in args:
            self.quads.generate(OpIds.param, 0, 0, arg[0])
        self.quads.generate(OpIds.instance, class_symbol.address, 0, obj[0])
        self.foo = obj
        return obj

    def end_class_scope(self):
        '''
			Function that closes a class scope, by default it returns
			a quad of type RETURN 
		'''
        self.table.close_scope()

    def assign(self, variable, value):
        # Make sure value can be assigned to variable
        self.oracle.can_assign(variable[1], value[1])

        if len(value) == 3:
            variable = self.assign_variable(variable, value)
        else:
            variable = self.assign_temp(variable, value)

        # If trying to copy existing variable
        # if len(value) == 3 and isinstance(value[2], VariableSymbol):
        # 	new_symbol = copy.deepcopy(value[2])
        # 	new_attrs = self.copy_attributes(new_symbol)
        # 	new_symbol.address = variable[0]
        # 	print(new_symbol)
        # 	for name,attr in new_symbol.attrs.items():
        # 		new_address = self.memory.get_address('local' if self.in_local_scope else 'global', attr.type)
        # 	self.table.replace_symbol(variable[2], new_symbol)

        # self.quads.generate(OpIds.assign, value[0], 0, variable[0])
        return variable

    def assign_variable(self, variable, value):
        if isinstance(value[2], StackSymbol):
            return self.assign_temp(variable, value)
        return self.replicate(variable, value)

    def assign_temp(self, variable, value):
        self.quads.generate(OpIds.assign, value[0], 0, variable[0])
        self.free_temp_memory(value[0])
        return variable

    def copy_attributes(self, symbol):
        # If it's an object instance
        for key, attr in symbol.attrs.items():
            if isinstance(attr, VariableSymbol):
                prev = attr.address
                new = attr.address = self.memory.get_address(
                    'local' if self.in_local_scope else 'global', attr.type)
                self.quads.generate(OpIds.assign, prev, 0, new)
                if attr.type not in ['int', 'float', 'bool', 'str']:
                    self.copy_attributes_aux(attr)

    def copy_attributes_aux(self, symbol):
        symbol.address = self.memory.get_address(
            'local' if self.in_local_scope else 'global', symbol.type)

        for key, attr in symbol.attrs.items():
            if isinstance(attr, VariableSymbol):
                prev = attr.address
                new = attr.address = self.memory.get_address(
                    'local' if self.in_local_scope else 'global', attr.type)
                self.quads.generate(OpIds.assign, prev, 0, new)
                if attr.type not in ['int', 'float', 'bool', 'str']:
                    self.copy_attributes_aux(attr)

    def this_property(self, prop_id):
        '''
		Function that handles the this.property functionallity
		'''
        # returns tuple with the address of the attribute and str of type
        # First we check if user is in a class scope
        in_class_scope = self.table.check_class_scope()
        # We are in a class scope
        if in_class_scope:
            class_has_property = self.table.check_class_property(prop_id)
            if class_has_property:
                if isinstance(class_has_property, StackSymbol):
                    raise Exception('Cannot use stack ' + prop_id +
                                    ' outside a stack call.')

                # Returns the symbol
                return class_has_property.to_tuple()
            else:
                raise Exception('Property ' + prop_id +
                                ' does not exist in class ' + in_class_scope +
                                '.')
        # If we are not inside a class scope, then the keyword this is not available
        else:
            raise Exception(
                'Keyword this is not available outside a class scope')

    def var_property(self, var_id, property_id):
        '''
		Function that handles the id.id functionallity
		'''
        variable_exists = self.id_property(var_id)[2]
        if isinstance(variable_exists, FunctionSymbol):
            raise Exception('Cannot access property of function ' + var_id +
                            '.')
        if variable_exists:
            self.quads.generate(OpIds.context, 0, 0, variable_exists.address)
            symbol = variable_exists.get_attribute(property_id)
            if not symbol:
                raise Exception('Variable ' + var_id +
                                ' does not have attribute ' + property_id +
                                '.')
            return symbol

    def id_property(self, property_id):
        class_property = self.table.check_property(property_id)
        if isinstance(class_property, StackSymbol):
            raise Exception('Cannot use stack ' + property_id +
                            ' outside a stack call.')
        if class_property:
            return class_property.to_tuple()
        else:
            raise Exception('Variable ' + property_id + ' is not defined.')

    def print_output(self, expression):
        self.quads.generate(OpIds.io_print, 0, 0, expression[0])
        self.free_temp_memory(expression[0])
        return expression

    def return_value(self, return_value):
        # Validates that the return object is the same type as the function return type
        has_correct_return = self.table.check_return(return_value[1])
        if has_correct_return:
            self.quads.generate(OpIds.func_return, 0, 0, return_value[0])
        else:
            raise Exception('Cannot return ' + return_value[1] +
                            ' in function of type ' +
                            self.table.scope().symbol.type)

    def return_void(self):
        has_correct_return = self.table.check_return('void')
        if has_correct_return:
            self.quads.generate(OpIds.func_return, 0, 0, 0)
        else:
            raise Exception('Cannot return void in function of type ' +
                            self.table.scope().symbol.type)

    def float_constant(self, value):
        address = self.memory.get_constant_address('float', value)
        return (address, 'float')

    def int_constant(self, value):
        address = self.memory.get_constant_address('int', value)
        return (address, 'int')

    def string_constant(self, value):
        address = self.memory.get_constant_address('str', value)
        return (address, 'str')

    def free_temp_memory(self, memory_address):
        self.memory.free_if_temp(memory_address)

    def read(self):
        #Return temporal
        temp_addr = self.memory.get_address('temp', 'str')
        self.quads.generate(OpIds.io_read, 0, 0, temp_addr)
        return (temp_addr, 'str')

    def operate(self, operator, left_op, right_op):
        # left_op & right_op = tuple(address, type)
        res_type = self.oracle.is_valid(operator, left_op, right_op)
        new_address = self.memory.get_address('temp', res_type)
        self.quads.generate(OpIds.get(operator), left_op[0], right_op[0],
                            new_address)
        # liberar memoria temporal de left_op y right_op
        self.memory.free_if_temp(left_op[0])
        self.memory.free_if_temp(right_op[0])
        return (new_address, res_type)

    def check_call_validity(self, prop, arguments, context=0):
        if isinstance(prop, tuple):
            prop = prop[2]
        symbol = prop
        argument_types = list(map(lambda x: x[1], arguments))
        if not symbol.is_callable:
            raise Exception('Cannot call a non callable object')
        if not symbol.accepts_arguments(argument_types):
            raise Exception('Arguments do not match function parameters.')
        new_address = self.memory.get_address('temp', symbol.type)
        # arguments.reverse()
        for param in arguments:
            self.quads.generate(OpIds.param, 0, 0, param[0])
        self.quads.generate(OpIds.call, symbol.address, 0, new_address)
        return (new_address, symbol.type)

    def call_stack(self, stack_symbol, call):
        if call[0] == 'push':
            return self.push_stack(stack_symbol, call[1])
        elif call == 'peek':
            return self.peek_stack(stack_symbol)
        elif call == 'size':
            return self.size_stack(stack_symbol)
        else:
            return self.pop_stack(stack_symbol)

    def push_stack(self, symbol, tuple_expr):
        if symbol.type != tuple_expr[1]:
            raise Exception('Cannot push element of type ' + tuple_expr[1] +
                            ' into a stack of type ' + symbol.type)
        self.quads.generate(OpIds.push, tuple_expr[0], 0, symbol.address)
        return tuple_expr

    def size_stack(self, symbol):
        new_address = self.memory.get_address('temp', 'int')
        self.quads.generate(OpIds.size, symbol.address, 0, new_address)
        return (new_address, 'int')

    def pop_stack(self, symbol):
        new_address = self.memory.get_address('temp', symbol.type)
        self.quads.generate(OpIds.pop, symbol.address, 0, new_address)
        return (new_address, symbol.type)

    def peek_stack(self, symbol):
        new_address = self.memory.get_address('temp', symbol.type)
        self.quads.generate(OpIds.peek, symbol.address, 0, new_address)
        return (new_address, symbol.type)

    def is_stack_type(self, variable):
        symbol = self.table.lookup(variable)
        if not isinstance(symbol, StackSymbol):
            raise Exception(
                'Cannot perform stack methods on non-stack property ' +
                variable + '.')
        return symbol

    def clear_instance_memory(self):
        self.memory.clear_instance_memory()

    def id_does_not_exist(self, identifier):
        id_exist = self.table.lookup(identifier)
        if id_exist:
            raise Exception('Identifier ' + identifier + ' already exists')

    def fill_goto(self):
        self.quads.fill_jump()

    # TODO : move to quad_generator
    def create_quads_txt(self, filename):
        file = open(filename, "w+")
        for addr, value in self.memory.consts.ints.items():
            if addr > 0:
                file.write(str(addr) + ',' + str(value) + '\n')
        for addr, value in self.memory.consts.floats.items():
            if addr > 0:
                file.write(str(addr) + ',' + str(value) + '\n')
        for addr, value in self.memory.consts.bools.items():
            if addr > 0:
                file.write(str(addr) + ',' + str(value) + '\n')
        for addr, value in self.memory.consts.strs.items():
            if addr > 0:
                file.write(str(addr) + ',' + str(value) + '\n')
        file.write(str(OpIds.endconst) + '\n')
        for index, quad in self.quads.quads.items():
            file.write(str(quad) + '\n')
        file.close()

#
# Var instantiation
#

    def var_type_is_obj(self, type_name):
        return self.mem_type_from_var_type(type_name) == 'obj'

    def mem_type_from_var_type(self, type_name):
        return type_name if type_name in ['int', 'float', 'bool', 'str'
                                          ] else 'obj'

    def new_var_of_type(self, type_name, scope, args={}):
        is_stack = isinstance(type_name, tuple) and type_name[0] == 'stack'
        if is_stack:
            type_name = type_name[1]

        mem_type = self.mem_type_from_var_type(type_name)
        new_address = self.memory.get_address(scope, mem_type)
        self.quads.generate(OpIds.declare, 0, 0, new_address)

        if is_stack:
            return StackSymbol(new_address, type_name).to_tuple()

        new_attributes = {}
        if self.var_type_is_obj(type_name):
            new_attributes = self.get_attributes_for_class(
                type_name, scope, args)
        new_symbol = VariableSymbol(new_address, type_name, new_attributes)
        return new_symbol.to_tuple()

    def get_attributes_for_class(self, type_name, scope, args={}):
        class_scope = self.table.get_class_scope(type_name)
        attrs = {}
        for name, symbol in class_scope.symbols.items():
            if isinstance(symbol, VariableSymbol):
                arg = args.get(name, None)
                symbol = self.new_var_of_type(symbol.type, scope)
                self.quads.generate(OpIds.relate, symbol[0], 0,
                                    class_scope.symbols[name].address)
            attrs[name] = symbol
        return attrs

    def replicate(self, target, origin):
        if isinstance(target, FunctionSymbol):
            return target
        self.quads.generate(OpIds.assign, origin[0], 0, target[0])
        self.free_temp_memory(origin[0])
        if len(origin) == 3:
            for name, target_attr in target[2].attrs.items():
                self.replicate(target_attr, origin[2].attrs[name])
        return target

    def declare(self, var_tuple):
        scope = 'local' if self.in_local_scope else 'global'
        var_name = var_tuple[0]
        # Checks it doesnt exist
        self.table.local_neg_lookup(var_name)
        var_type = var_tuple[1]
        var = self.new_var_of_type(var_type, scope)
        self.table.store(var_name, var[2])
        return var