/*
 * Andrea Di Biagio
 * Politecnico di Milano, 2007
 * 
 * axe_engine.c
 * Formal Languages & Compilers Machine, 2007/2008
 * 
 */

#include <assert.h>
#include <string.h>
#include <malloc.h>
#include "axe_engine.h"
#include "symbol_table.h"
#include "axe_errors.h"

/* global variable errorcode */
int errorcode;

/* Function used when a compare is needed between two labels */
static int compareVariables (void *Var_A, void *Var_B);

/* Finalize the memory associated with an instruction */
static void finalizeInstructions(t_list *instructions);

/* Finalize the data segment */
static void finalizeDataSegment(t_list *dataDirectives);

/* finalize the informations associated with all the variables */
static void finalizeVariables(t_list *variables);

/* Translate the assembler directives (definitions inside the data segment */
static void translateDataSegment(t_program_infos *program, FILE *fp);

/* Translate all the instructions within the code segment */
static void translateCodeSegment(t_program_infos *program, FILE *fp);

/* print out to the file `fp' an opcode */
static void printOpcode(int opcode, FILE *fp);

/* print out to the file `fp' a register information */
static void printRegister(t_axe_register *reg, FILE *fp);

/* add a variable to the program */
static void addVariable(t_program_infos *program, t_axe_variable *variable);

      
/* create a new variable */
void createVariable(t_program_infos *program
      , char *ID, int type, int isArray, int arraySize, t_value_block init_val)
{
   t_axe_variable *var;
         
   /* test the preconditions */
   if (program == NULL)
      notifyError(AXE_PROGRAM_NOT_INITIALIZED);

   /* initialize a new variable */
   var = alloc_variable(ID, type, isArray, arraySize, init_val);
   if (var == NULL)
      notifyError(AXE_OUT_OF_MEMORY);
   
   /* assign a new label to the newly created variable `var' */
   var->labelID = reserveLabel(program);

   /* add the new variable to program */
   addVariable(program, var);
}

/* translate each instruction in his assembler symbolic representation */
void translateCodeSegment(t_program_infos *program, FILE *fp)
{
   t_list *current_element;
   t_axe_instruction *current_instruction;
   int _error;
   
   /* preconditions */
   if (fp == NULL)
      notifyError(AXE_INVALID_INPUT_FILE);

   if (program == NULL)
   {
      _error = fclose(fp);
      if (_error == EOF)
         notifyError(AXE_FCLOSE_ERROR);
      notifyError(AXE_PROGRAM_NOT_INITIALIZED);
   }

   /* initialize the current_element */
   current_element = program->instructions;

   /* write the .text directive */
   if (current_element != NULL)
   {
      if (fprintf(fp, "\t.text\n") < 0)
      {
         _error = fclose(fp);
         if (_error == EOF)
            notifyError(AXE_FCLOSE_ERROR);
         notifyError(AXE_FWRITE_ERROR);
      }
   }

   while (current_element != NULL)
   {
      /* retrieve the current instruction */
      current_instruction = (t_axe_instruction *) LDATA(current_element);
      assert(current_instruction != NULL);
      assert(current_instruction->opcode != INVALID_OPCODE);

      if (current_instruction->labelID != NULL)
      {
            /* create a string identifier for the label */
            if (  fprintf(fp, "L%d : \t"
                     , (current_instruction->labelID)->labelID ) < 0)
            {
              _error = fclose(fp);
               if (_error == EOF)
                  notifyError(AXE_FCLOSE_ERROR);
               notifyError(AXE_FWRITE_ERROR);
            }
      }
      else
      {
            /* create a string identifier for the label */
            if (fprintf(fp, "\t") < 0)
            {
              _error = fclose(fp);
               if (_error == EOF)
                  notifyError(AXE_FCLOSE_ERROR);
               notifyError(AXE_FWRITE_ERROR);
            }
      }

      /* print the opcode */
      printOpcode(current_instruction->opcode, fp);

      if (  (current_instruction->opcode == HALT)
            || (current_instruction->opcode == NOP) )
      {
         if (fprintf(fp, "\n") < 0)
         {
              _error = fclose(fp);
               if (_error == EOF)
                  notifyError(AXE_FCLOSE_ERROR);
               notifyError(AXE_FWRITE_ERROR);
         }

         /* update the current_element */
         current_element = LNEXT(current_element);
         continue;
      }
      
      if (fputc(' ', fp) == EOF)
      {
         _error = fclose(fp);
         if (_error == EOF)
            notifyError(AXE_FCLOSE_ERROR);
         notifyError(AXE_FWRITE_ERROR);
      }

      if (current_instruction->reg_1 != NULL)
      {
         printRegister(current_instruction->reg_1, fp);
         
         if (fputc(' ', fp) == EOF)
         {
            _error = fclose(fp);
            if (_error == EOF)
               notifyError(AXE_FCLOSE_ERROR);
            notifyError(AXE_FWRITE_ERROR);
         }
      }
      if (current_instruction->reg_2 != NULL)
      {
         printRegister(current_instruction->reg_2, fp);
         if (errorcode != AXE_OK)
            return;

         if (fputc(' ', fp) == EOF)
         {
            _error = fclose(fp);
            if (_error == EOF)
               notifyError(AXE_FCLOSE_ERROR);
            notifyError(AXE_FWRITE_ERROR);
         }
      }
      if (current_instruction->reg_3 != NULL)
      {
         printRegister(current_instruction->reg_3, fp);

         if (fprintf(fp, "\n") < 0) {
            _error = fclose(fp);
            if (_error == EOF)
               notifyError(AXE_FCLOSE_ERROR);
            notifyError(AXE_FWRITE_ERROR);
         }

         /* update the current_element */
         current_element = LNEXT(current_element);
         continue;
      }

      if (current_instruction->address != NULL)
      {
         if ((current_instruction->address)->type == ADDRESS_TYPE)
         {
            if (fprintf(fp, "%d", (current_instruction->address)->addr) < 0)
            {
               _error = fclose(fp);
               if (_error == EOF)
                  notifyError(AXE_FCLOSE_ERROR);
               notifyError(AXE_FWRITE_ERROR);
            }
         }
         else
         {
            assert((current_instruction->address)->type == LABEL_TYPE);
            if (  fprintf(fp, "L%d"
                     , ((current_instruction->address)->labelID)
                              ->labelID) < 0)
            {
               _error = fclose(fp);
               if (_error == EOF)
                  notifyError(AXE_FCLOSE_ERROR);
               notifyError(AXE_FWRITE_ERROR);
            }
         }
         
         if (fprintf(fp, "\n") < 0) {
            _error = fclose(fp);
            if (_error == EOF)
               notifyError(AXE_FCLOSE_ERROR);
            notifyError(AXE_FWRITE_ERROR);
         }

         /* update the current_element */
         current_element = LNEXT(current_element);
         continue;
      }

      if (fprintf(fp, "#%d", current_instruction->immediate) < 0)
      {
         _error = fclose(fp);
         if (_error == EOF)
            notifyError(AXE_FCLOSE_ERROR);
         notifyError(AXE_FWRITE_ERROR);
      }

      if (fprintf(fp, "\n") < 0) {
         _error = fclose(fp);
         if (_error == EOF)
            notifyError(AXE_FCLOSE_ERROR);
         notifyError(AXE_FWRITE_ERROR);
      }

      /* loop termination condition */
      current_element = LNEXT(current_element);
   }
}

void translateDataSegment(t_program_infos *program, FILE *fp)
{
   t_list *current_element;
   t_axe_data *current_data;
   int _error;
   
   /* preconditions */
   if (fp == NULL)
      notifyError(AXE_INVALID_INPUT_FILE);
   
   if (program == NULL)
   {
      _error = fclose(fp);
      if (_error == EOF)
         notifyError(AXE_FCLOSE_ERROR);

      notifyError(AXE_PROGRAM_NOT_INITIALIZED);
   }

   /* initialize the value of `current_element' */
   current_element = program->data;

   /* write the .data directive */
   if (current_element != NULL)
   {
      if (fprintf(fp, "\t.data\n") < 0)
      {
         _error = fclose(fp);
         if (_error == EOF)
            notifyError(AXE_FCLOSE_ERROR);
         notifyError(AXE_FWRITE_ERROR);
      }
   }

   /* iterate all the elements inside the data segment */
   while (current_element != NULL)
   {
      /* retrieve the current data element */
      current_data = (t_axe_data *) LDATA(current_element);

      /* assertions */
      assert (current_data != NULL);
      /* Unfortunately, this assertion is not anymore acceptable, since .WORD 
			 * directives without labels are produced by the new array syntax!
			  assert ( (current_data->labelID != NULL)
               && ((current_data->labelID)->labelID != LABEL_UNSPECIFIED) );*/
      assert (current_data->directiveType != DIR_INVALID);

      /* create a string identifier for the label 
			 * A check has been added to replace the above assertion 
			 */
			if ( (current_data->labelID != NULL) && ((current_data->labelID)->labelID != LABEL_UNSPECIFIED) ) {
         if (fprintf(fp, "L%d : \t", (current_data->labelID)->labelID) < 0)
         {
           _error = fclose(fp);
           if (_error == EOF)
              notifyError(AXE_FCLOSE_ERROR);
           notifyError(AXE_FWRITE_ERROR);
         }
			} else {
         if (fprintf(fp, "\t") < 0)
         {
           _error = fclose(fp);
           if (_error == EOF)
              notifyError(AXE_FCLOSE_ERROR);
           notifyError(AXE_FWRITE_ERROR);
         }
			}
      
      /* print the directive identifier */
      if (current_data->directiveType == DIR_WORD)
      {
         if (fprintf(fp, ".WORD ") < 0)
         {
            _error = fclose(fp);
            if (_error == EOF)
               notifyError(AXE_FCLOSE_ERROR);
            notifyError(AXE_FWRITE_ERROR);
         }
      }
      
      else if (current_data->directiveType == DIR_SPACE)
      {
         if (fprintf(fp, ".SPACE ") < 0)
         {
            _error = fclose(fp);
            if (_error == EOF)
               notifyError(AXE_FCLOSE_ERROR);
            notifyError(AXE_FWRITE_ERROR);
         }
      }

      /* print the value associated with the directive */
      if (fprintf(fp, "%d\n", current_data->value) < 0)
      {
         _error = fclose(fp);
         if (_error == EOF)
            notifyError(AXE_FCLOSE_ERROR);
         notifyError(AXE_FWRITE_ERROR);
      }

      /* loop termination condition */
      current_element = LNEXT(current_element);
   }
}

void finalizeDataSegment(t_list *dataDirectives)
{
   t_list *current_element;
   t_axe_data *current_data;

   /* nothing to finalize */
   if (dataDirectives == NULL)
      return;

   current_element = dataDirectives;
   while(current_element != NULL)
   {
      /* retrieve the current instruction */
      current_data = (t_axe_data *) LDATA(current_element);
      if (current_data != NULL)
         free_Data(current_data);

      current_element = LNEXT(current_element);
   }

   /* free the list of instructions */
   freeList(dataDirectives);
}

void finalizeInstructions(t_list *instructions)
{
   t_list *current_element;
   t_axe_instruction *current_instr;

   /* nothing to finalize */
   if (instructions == NULL)
      return;

   current_element = instructions;
   while(current_element != NULL)
   {
      /* retrieve the current instruction */
      current_instr = (t_axe_instruction *) LDATA(current_element);
      if (current_instr != NULL)
         free_Instruction(current_instr);

      current_element = LNEXT(current_element);
   }

   /* free the list of instructions */
   freeList(instructions);
}

int compareVariables (void *Var_A, void *Var_B)
{
   t_axe_variable *va;
   t_axe_variable *vb;
   
   if (Var_A == NULL)
   {
      if (Var_B == NULL)
         return 1;
   }

   if (Var_B == NULL)
      return 0;

   va = (t_axe_variable *) Var_A;
   vb = (t_axe_variable *) Var_B;

   /* test if the name is the same */
   return (!strcmp(va->ID, vb->ID));
}

t_axe_variable * getVariable
      (t_program_infos *program, char *ID)
{
   t_axe_variable search_pattern;
   t_list *elementFound;
   
   /* preconditions */
   if (program == NULL)
      notifyError(AXE_PROGRAM_NOT_INITIALIZED);

   if (ID == NULL)
      notifyError(AXE_VARIABLE_ID_UNSPECIFIED);

   /* initialize the pattern */
   search_pattern.ID = ID;
   
   /* search inside the list of variables */
   elementFound = CustomfindElement
         (program->variables, &search_pattern, compareVariables);

   /* if the element is found return it to the caller. Otherwise return NULL. */
   if (elementFound != NULL)
      return (t_axe_variable *) LDATA(elementFound);

   return NULL;
}

/* initialize an instance of `t_program_infos' */
t_program_infos * allocProgramInfos()
{
   t_program_infos *result;

   /* initialize the local variable `result' */
   result = (t_program_infos *)
         _AXE_ALLOC_FUNCTION(sizeof(t_program_infos));

   /* verify if an error occurred during the memory allocation
    * process */
   if (result == NULL)
      notifyError(AXE_OUT_OF_MEMORY);

   /* initialize the new instance of `result' */
   result->variables = NULL;
   result->instructions = NULL;
   result->data = NULL;
   result->current_register = 1; /* we are excluding the register R0 */
   result->lmanager = initialize_label_manager();

   if (result->lmanager == NULL)
   {
      finalizeProgramInfos(result);
      notifyError(AXE_OUT_OF_MEMORY);
   }

   result->sy_table = initialize_sy_table();
   
   /* test if the sy_table is a NULL pointer */
   if (result->sy_table == NULL)
   {
      finalizeProgramInfos(result);
      notifyError(AXE_OUT_OF_MEMORY);
   }
   
   /* postcondition: return an instance of `t_program_infos' */
   return result;
}

/* add an instruction at the tail of the list `program->instructions'.
 * Returns an error code. */
void addInstruction(t_program_infos *program, t_axe_instruction *instr)
{
   /* test the preconditions */
   if (program == NULL)
      notifyError(AXE_PROGRAM_NOT_INITIALIZED);
   
   if (instr == NULL)
      notifyError(AXE_INVALID_INSTRUCTION);

   if (program->lmanager == NULL)
      notifyError(AXE_INVALID_LABEL_MANAGER);

   instr->labelID = assign_label(program->lmanager);

   /* update the list of instructions */
   program->instructions = addElement(program->instructions, instr, -1);
}

/* reserve a new label identifier for future uses */
t_axe_label * reserveLabel(t_program_infos *program)
{
   /* test the preconditions */
   if (program == NULL)
      notifyError(AXE_PROGRAM_NOT_INITIALIZED);

   if (program->lmanager == NULL)
      notifyError(AXE_INVALID_LABEL_MANAGER);

   return reserveLabelID(program->lmanager);
}

/* assign a new label identifier to the next instruction */
t_axe_label * fixLabel(t_program_infos *program, t_axe_label *label)
{
   /* test the preconditions */
   if (program == NULL)
      notifyError(AXE_PROGRAM_NOT_INITIALIZED);

   if (program->lmanager == NULL)
      notifyError(AXE_INVALID_LABEL_MANAGER);

   /* fix the label */
   return fixLabelID(program->lmanager, label);
}

/* reserve a new label identifier */
t_axe_label * assignNewLabel(t_program_infos *program)
{
   t_axe_label * reserved_label;

   /* reserve a new label */
   reserved_label = reserveLabel(program);
   if (reserved_label == NULL)
      return NULL;

   /* fix the label */
   return fixLabel(program, reserved_label);
}

void addVariable(t_program_infos *program, t_axe_variable *variable)
{
   t_axe_variable *variableFound;
   t_axe_data *new_data_info;
   int sy_error;
   
   /* test the preconditions */
   if (variable == NULL)
      notifyError(AXE_INVALID_VARIABLE);

   if (program == NULL)
   {
      free_variable(variable);
      notifyError(AXE_PROGRAM_NOT_INITIALIZED);
   }

   if (variable->ID == NULL)
   {
      free_variable(variable);
      notifyError(AXE_VARIABLE_ID_UNSPECIFIED);
   }

   if (variable->type == UNKNOWN_TYPE)
   {
      free_variable(variable);
      notifyError(AXE_INVALID_TYPE);
   }

   if (variable->isArray)
   {
      if (variable->arraySize <= 0)
      {
         free_variable(variable);
         notifyError(AXE_INVALID_ARRAY_SIZE);
      }
   }
   
   if (variable->labelID == NULL)
   {
      free_variable(variable);
      notifyError(AXE_INVALID_LABEL);
   }
   
   /* we have to test if already exists a variable with the same ID */
   variableFound = getVariable(program, variable->ID);

   if (variableFound != NULL)
   {
      free_variable(variable);
      notifyError(AXE_VARIABLE_ALREADY_DECLARED);
   }

   /* now we can add the new variable to the program */
   program->variables = addElement(program->variables, variable, -1);

   /* create an instance of `t_axe_data' */
   if (variable->type == INTEGER_TYPE)
   {
      if (variable->isArray && variable->init_val.int_arr_val==NULL)
      {
         new_data_info = alloc_data
               (DIR_SPACE, (variable->arraySize * 4), variable->labelID);
         
         if (new_data_info == NULL)
            notifyError(AXE_OUT_OF_MEMORY);

         /* update the list of directives */
         program->data = addElement(program->data, new_data_info, -1);
      }
      else if (variable->isArray && variable->init_val.int_arr_val!=NULL)
      {
			   int i;
         new_data_info = alloc_data
               (DIR_WORD, variable->init_val.int_arr_val[0], variable->labelID);
         
         if (new_data_info == NULL)
            notifyError(AXE_OUT_OF_MEMORY);

				 /* update the list of directives */
         program->data = addElement(program->data, new_data_info, -1);
         
				 for(i=1; i<variable->arraySize; i++){
            new_data_info = alloc_data
               (DIR_WORD, variable->init_val.int_arr_val[i], NULL);
         
            if (new_data_info == NULL)
               notifyError(AXE_OUT_OF_MEMORY);
    		    /* update the list of directives */
            program->data = addElement(program->data, new_data_info, -1);
				 }
      }
      else
      {
         new_data_info = alloc_data
            (DIR_WORD, variable->init_val.int_val, variable->labelID);
         
         if (new_data_info == NULL)
            notifyError(AXE_OUT_OF_MEMORY);

				 /* update the list of directives */
         program->data = addElement(program->data, new_data_info, -1);
      }
   }

   /* update the content of the symbol table */
   sy_error = putSym(program->sy_table, variable->ID, variable->type);
     
   if (sy_error != SY_TABLE_OK)
      notifyError(AXE_SY_TABLE_ERROR);
}

int getNewRegister(t_program_infos *program)
{
   int result;
   
   /* test the preconditions */
   if (program == NULL)
      notifyError(AXE_PROGRAM_NOT_INITIALIZED);

   result = program->current_register;
   program->current_register++;
   
   /* return the current label identifier */
   return result;
}

void finalizeProgramInfos(t_program_infos *program)
{
   if (program == NULL)
      return;
   if (program->variables != NULL)
      finalizeVariables(program->variables);
   if (program->instructions != NULL)
      finalizeInstructions(program->instructions);
   if (program->data != NULL)
      finalizeDataSegment(program->data);
   if (program->lmanager != NULL)
      finalize_label_manager(program->lmanager);
   if (program->sy_table != NULL)
      finalize_sy_table(program->sy_table);

   _AXE_FREE_FUNCTION(program);
}

void writeAssembly(t_program_infos *program, char *output_file)
{
   FILE *fp;
   int _error;

   /* test the preconditions */
   if (program == NULL)
      notifyError(AXE_PROGRAM_NOT_INITIALIZED);

   /* If necessary, set the value of `output_file' to "output.asm" */
   if (output_file == NULL)
   {
      /* set "output.o" as output file name */
      output_file = "output.asm";
   }

#ifndef NDEBUG
   fprintf(stdout, "\n\n*******************************************\n");
   fprintf(stdout, "INITIALIZING OUTPUT FILE: %s. \n", output_file);
   fprintf(stdout, "CODE SEGMENT has a size of %d instructions \n"
         , getLength(program->instructions));
   fprintf(stdout, "DATA SEGMENT has a size of %d elements \n"
         , getLength(program->data));
   fprintf(stdout, "NUMBER OF LABELS : %d. \n"
         , get_number_of_labels(program->lmanager));
   fprintf(stdout, "*******************************************\n\n");
#endif
   
   /* open a new file */
   fp = fopen(output_file, "w");
   if (fp == NULL)
      notifyError(AXE_FOPEN_ERROR);

   /* print the data segment */
   translateDataSegment(program, fp);

   /* print the code segment */
   translateCodeSegment(program, fp);

   /* close the file and return */
   _error = fclose(fp);
   if (_error == EOF)
      notifyError(AXE_FCLOSE_ERROR);
}

void printOpcode(int opcode, FILE *fp)
{
   char *opcode_to_string;
   int _error;
   
   /* preconditions: fp must be different from NULL */
   if (fp == NULL)
      notifyError(AXE_INVALID_INPUT_FILE);

   switch(opcode)
   {
      case ADD : opcode_to_string = "ADD"; break;
      case SUB : opcode_to_string = "SUB"; break;
      case ANDL : opcode_to_string = "ANDL"; break;
      case ORL : opcode_to_string = "ORL"; break;
      case EORL : opcode_to_string = "EORL"; break;
      case ANDB : opcode_to_string = "ANDB"; break;
      case ORB : opcode_to_string = "ORB"; break;
      case EORB : opcode_to_string = "EORB"; break;
      case MUL : opcode_to_string = "MUL"; break;
      case DIV : opcode_to_string = "DIV"; break;
      case SHL : opcode_to_string = "SHL"; break;
      case SHR : opcode_to_string = "SHR"; break;
      case ROTL : opcode_to_string = "ROTL"; break;
      case ROTR : opcode_to_string = "ROTR"; break;
      case NEG : opcode_to_string = "NEG"; break;
      case SPCL : opcode_to_string = "SPCL"; break;
      case ADDI : opcode_to_string = "ADDI"; break;
      case SUBI : opcode_to_string = "SUBI"; break;
      case ANDLI : opcode_to_string = "ANDLI"; break;
      case ORLI : opcode_to_string = "ORLI"; break;
      case EORLI : opcode_to_string = "EORLI"; break;
      case ANDBI : opcode_to_string = "ANDBI"; break;
      case ORBI : opcode_to_string = "ORBI"; break;
      case EORBI : opcode_to_string = "EORBI"; break;
      case MULI : opcode_to_string = "MULI"; break;
      case DIVI : opcode_to_string = "DIVI"; break;
      case SHLI : opcode_to_string = "SHLI"; break;
      case SHRI : opcode_to_string = "SHRI"; break;
      case ROTLI : opcode_to_string = "ROTLI"; break;
      case ROTRI : opcode_to_string = "ROTRI"; break;
      case NOTL : opcode_to_string = "NOTL"; break;
      case NOTB : opcode_to_string = "NOTB"; break;
      case NOP : opcode_to_string = "NOP"; break;
      case MOVA : opcode_to_string = "MOVA"; break;
      case JSR : opcode_to_string = "JSR"; break;
      case RET : opcode_to_string = "RET"; break;
      case HALT : opcode_to_string = "HALT"; break;
      case BT : opcode_to_string = "BT"; break;
      case BF : opcode_to_string = "BF"; break;
      case BHI : opcode_to_string = "BHI"; break;
      case BLS : opcode_to_string = "BLS"; break;
      case BCC : opcode_to_string = "BCC"; break;
      case BCS : opcode_to_string = "BCS"; break;
      case BNE : opcode_to_string = "BNE"; break;
      case BEQ : opcode_to_string = "BEQ"; break;
      case BVC : opcode_to_string = "BVC"; break;
      case BVS : opcode_to_string = "BVS"; break;
      case BPL : opcode_to_string = "BPL"; break;
      case BMI : opcode_to_string = "BMI"; break;
      case BGE : opcode_to_string = "BGE"; break;
      case BLT : opcode_to_string = "BLT"; break;
      case BGT : opcode_to_string = "BGT"; break;
      case BLE : opcode_to_string = "BLE"; break;
      case LOAD : opcode_to_string = "LOAD"; break;
      case STORE : opcode_to_string = "STORE"; break;
      case SEQ : opcode_to_string = "SEQ"; break;
      case SGE : opcode_to_string = "SGE"; break;
      case SGT : opcode_to_string = "SGT"; break;
      case SLE : opcode_to_string = "SLE"; break;
      case SLT : opcode_to_string = "SLT"; break;
      case SNE : opcode_to_string = "SNE"; break;
      case AXE_READ : opcode_to_string = "READ"; break;
      case AXE_WRITE : opcode_to_string = "WRITE"; break;
      default :
         /* close the file and return */
         _error = fclose(fp);
         if (_error == EOF)
            notifyError(AXE_FCLOSE_ERROR);
         notifyError(AXE_INVALID_OPCODE);
   }
      
   /* postconditions */
   if (fprintf(fp, "%s", opcode_to_string) < 0)
   {
      _error = fclose(fp);
      if (_error == EOF)
         notifyError(AXE_FCLOSE_ERROR);
      notifyError(AXE_FWRITE_ERROR);
   }
}

void printRegister(t_axe_register *reg, FILE *fp)
{
   int _error;
   
   /* preconditions: fp must be different from NULL */
   if (fp == NULL)
      notifyError(AXE_INVALID_INPUT_FILE);
   if (reg == NULL)
   {
      _error = fclose(fp);
      if (_error == EOF)
         notifyError(AXE_FCLOSE_ERROR);
      notifyError(AXE_INVALID_REGISTER_INFO);
   }
   if (reg->ID == REG_INVALID)
   {
      _error = fclose(fp);
      if (_error == EOF)
         notifyError(AXE_FCLOSE_ERROR);
      notifyError(AXE_INVALID_REGISTER_INFO);
   }

   if (reg->indirect)
   {
      if (fprintf(fp, "(R%d)", reg->ID) < 0)
      {
         _error = fclose(fp);
         if (_error == EOF)
            notifyError(AXE_FCLOSE_ERROR);
         notifyError(AXE_FWRITE_ERROR);
      }
   }
   else
   {
      if (fprintf(fp, "R%d", reg->ID) < 0)
      {
         _error = fclose(fp);
         if (_error == EOF)
            notifyError(AXE_FCLOSE_ERROR);
         notifyError(AXE_FWRITE_ERROR);
      }
   }
}

t_axe_label * getLabelFromVariableID(t_program_infos *program, char *ID)
{
   t_axe_variable *var;
   
   var = getVariable(program, ID);
   if (var == NULL)
      return NULL;

   /* test the postconditions */
   assert(var->labelID != NULL);
   
   return var->labelID;
}

void finalizeVariables(t_list *variables)
{
   t_list *current_element;
   t_axe_variable *current_var;

   if (variables == NULL)
      return;

   /* initialize the `current_element' */
   current_element = variables;
   while(current_element != NULL)
   {
      current_var = (t_axe_variable *) LDATA(current_element);
      if (current_var != NULL)
      {
         if (current_var->ID != NULL)
            free(current_var->ID);
         
         _AXE_FREE_FUNCTION(current_var);
      }
      
      current_element = LNEXT(current_element);
   }

   /* free the list of variables */
   freeList(variables);
}
