%{
/*
 * Andrea Di Biagio
 * Politecnico di Milano, 2007
 * 
 * Acse.y
 * Formal Languages & Compilers Machine, 2007/2008
 * 
 */

/*************************************************************************

                   LanCE BNF with semantic actions

***************************************************************************/

#include <stdio.h>       
#include <stdlib.h>
#include <assert.h>
#include <malloc.h>
#include "axe_struct.h"
#include "axe_engine.h"
#include "symbol_table.h"
#include "axe_errors.h"
#include "collections.h"
#include "axe_expressions.h"
#include "axe_gencode.h"
#include "axe_utils.h"
#include "axe_array.h"
#include "axe_cflow_graph.h"
#include "cflow_constants.h"
#include "axe_transform.h"
#include "axe_reg_alloc.h"
#include "reg_alloc_constants.h"
#include "axe_io_manager.h"
#ifndef NDEBUG
#  include "axe_debug.h"
#endif



/* global variables */
int line_num;        /* this variable will keep trak of the
                      * source code line number. Every time that a newline
                      * is encountered while parsing the input file, this
                      * value is increased by 1. This value is then used
                      * for error tracking: if the parser returns an error
                      * or a warning, this value is used in order to notify
                      * in which line of code the error has been found */
int num_error;       /* the number of errors found in the code. This value
                      * is increased by 1 every time a new error is found
                      * in the code. */
int num_warning;     /* As for the `num_error' global variable, this one
                      * keeps track of all the warning messages displayed */

/* errorcode is defined inside "axe_engine.c" */
extern int errorcode;   /* this variable is used to test if an error is found
                         * while parsing the input file. It also is set
                         * to notify if the compiler internal state is invalid.
                         * When the parsing process is started, the value
                         * of `errorcode' is set to the value of the macro
                         * `AXE_OK' defined in "axe_constants.h".
                         * As long as everything (the parsed source code and
                         * the internal state of the compiler) is correct,
                         * the value of `errorcode' is set to `AXE_OK'.
                         * When an error occurs (because the input file contains
                         * one or more syntax errors or because something went
                         * wrong in the machine internal state), the errorcode
                         * is set to a value that is different from `AXE_OK'. */
                         

extern int cflow_errorcode;   /* As for `errorcode' this value is used to
                        * test if an error occurs during the creation process of
                        * a control flow graph. More informations can be found
                        * analyzing the file `axe_cflow_graph.h'. */
                     
/* program informations */
t_program_infos *program;  /* The singleton instance of `program'.
                            * An instance of `t_program_infos' holds in its
                            * internal structure, all the useful informations
                            * about a program. For example: the assembly
                            * (code and directives); the symbol table;
                            * the label manager (see axe_labels.h) etc. */
t_cflow_Graph *graph;      /* An instance of a control flow graph. This instance
                            * will be generated starting from `program' and will
                            * be used during the register allocation process */

t_reg_allocator *RA;       /* Register allocator. It implements the "Linear scan"
                            * algorythm */

t_io_infos *file_infos;    /* input and output files used by the compiler */

%}



/*=========================================================================
                          SEMANTIC RECORDS 
=========================================================================*/

%union {            /* The semantic records                          */
   int intval;
   char *svalue;
   t_axe_expression expr;
   t_axe_declaration *decl;
   t_list *list;
   t_axe_label *label;
   t_while_statement while_stmt;
   t_for_statement for_stmt;
} 
/*=========================================================================
                               TOKENS 
=========================================================================*/
%start program

%expect 1

%token LBRACE RBRACE LPAR RPAR LSQUARE RSQUARE
%token SEMI COLON PLUS MINUS MUL_OP DIV_OP MOD_OP
%token AND_OP OR_OP NOT_OP
%token ASSIGN LT GT SHL_OP SHR_OP EQ NOTEQ LTEQ GTEQ
%token ANDAND OROR
%token COMMA
%token ELSE IN
%token RETURN
%token READ
%token WRITE

%token <label> DO
%token <while_stmt> WHILE 
%token <for_stmt> FOR
%token <label> IF
%token <intval> TYPE
%token <svalue> IDENTIFIER
%token <intval> NUMBER

%type <expr> exp
%type <decl> declaration
%type <list> declaration_list num_list
%type <label> if_statements


/*=========================================================================
                          OPERATOR PRECEDENCE
 =========================================================================*/

%left COMMA
%left ASSIGN
%left OROR
%left ANDAND
%left OR_OP
%left AND_OP
%left EQ NOTEQ
%left LT GT LTEQ GTEQ
%left SHL_OP SHR_OP
%left MINUS PLUS
%left MUL_OP DIV_OP
%right NOT

/*=========================================================================
                         Syntactic Rules
=========================================================================*/
%% 

/* `program' is the starting non-terminal of the grammar.
 * A program is composed by:
      1. declarations (zero or more);
      2. A list of instructions. (at least one instruction!).
 * When the rule associated with the non-terminal `program' is executed,
 * the parser notify it to the `program' singleton instance. */
program  : var_declarations statements
         {
            /* Notify the end of the program. By calling this
             * function, if necessary, a `HALT' instruction
             * is inserted in the list of instructions. */
            set_end_Program(program);

            /* return from yyparse() */
            YYACCEPT;
         }
;

var_declarations : var_declarations var_declaration   { /* does nothing */ }
                 | /* empty */                        { /* does nothing */ }
;

var_declaration   : TYPE declaration_list SEMI
                  {
                     /* update the program infos by adding new variables */
                     set_new_variables(program, $1, $2);
                  }
;

declaration_list  : declaration_list COMMA declaration
                  {  /* add the new declaration to the list of declarations */
                     $$ = addElement($1, $3, -1);
                  }
                  | declaration
                  {
                        /* add the new declaration to the list of declarations */
                        $$ = addElement(NULL, $1, -1);
                  }
;

declaration : IDENTIFIER ASSIGN NUMBER
            {
               /* create a new instance of t_axe_declaration */
               $$ = alloc_declaration($1, 0, 0, (t_value_block)$3);

               /* test if an `out of memory' occurred */
               if ($$ == NULL)
                  notifyError(AXE_OUT_OF_MEMORY);
            }
            | IDENTIFIER LSQUARE NUMBER RSQUARE
            {
               /* create a new instance of t_axe_declaration */
               $$ = alloc_declaration($1, 1, $3, (t_value_block)0);

               /* test if an `out of memory' occurred */
               if ($$ == NULL)
                  notifyError(AXE_OUT_OF_MEMORY);
            }
            | IDENTIFIER
            {
               /* create a new instance of t_axe_declaration */
               $$ = alloc_declaration($1, 0, 0, (t_value_block)0);

               /* test if an `out of memory' occurred */
               if ($$ == NULL)
                  notifyError(AXE_OUT_OF_MEMORY);
            }
            | IDENTIFIER LSQUARE NUMBER RSQUARE ASSIGN LBRACE num_list RBRACE
            {
							 int i,v;
							 int *init=(int *)_AXE_ALLOC_FUNCTION(sizeof(int)*($3));
							 
							 for(i=0; i<getLength($7); i++){
							 	v=*((int *)(getElementAt($7,i)->data));
								init[i]=v;
							 }
							 freeList($7);
               /* create a new instance of t_axe_declaration */
               $$ = alloc_declaration($1, 1, $3, (t_value_block)init);

               /* test if an `out of memory' occurred */
               if ($$ == NULL)
                  notifyError(AXE_OUT_OF_MEMORY);
            }
;

num_list : num_list COMMA NUMBER 
         {
				 		int *i=(int *)_AXE_ALLOC_FUNCTION(sizeof(int));
						*i=$3;
				 		$$ = addElement($1, i, -1);
				 }
         | NUMBER 
				 {
				 		int *i=(int *)_AXE_ALLOC_FUNCTION(sizeof(int));
						*i=$1;
				 		$$ = addElement(NULL, i, -1);
				 }
;


/* A block of code can be either a single statement or
 * a set of statements enclosed between braces */
code_block  : statement                   { /* does nothing */ }
            | LBRACE statements RBRACE    { /* does nothing */ }
;

/* One or more code statements */
statements  : statements statement       { /* does nothing */ }
            | statement                  { /* does nothing */ }
;

statement   : assignment_statements SEMI { /* does nothing */ }
						| read_write_statements SEMI { /* does nothing */ }
            | control_statements { /* does nothing */ }
            | SEMI
            {
               /* insert a NOP instruction */
               gen_nop_instruction(program);
            }
            | RETURN SEMI
            {
               /* insert an HALT instruction */
               gen_halt_instruction(program);
            }       
;

control_statements : if_statements
            {
               /* fix the `label_else' */
               fixLabel(program, $1);
            }
            | if_statements ELSE
            {
               /* reserve a new label that points to the address where to jump if
                * `exp' is verified */
               $<label>$ = reserveLabel(program);

               /* exit from the if-else */
               gen_bt_instruction (program, $<label>$, 0);

               /* fix the `label_else' */
               fixLabel(program, $1);
            }
            code_block
            {
               /* fix the `label_else' */
               fixLabel(program, $<label>3);
            }
            | while_statements { /* does nothing */ }
            | for_statements { /* does nothing */ }
            | do_while_statements SEMI { /* does nothing */ }
;

assignment_statements : IDENTIFIER LSQUARE exp RSQUARE ASSIGN exp 
            {
               /* Notify to `program' that the value $6
                * have to be assigned to the location
                * addressed by $1[$3]. Where $1 is obviously
                * the array/pointer identifier, $3 is an expression
                * that holds an integer value. That value will be
                * used as an index for the array $1 */
               storeArrayElement(program, $1, $3, $6);

               /* free the memory associated with the IDENTIFIER.
                * The use of the free instruction is required
                * because of the value associated with IDENTIFIER.
                * The value of IDENTIFIER is a string created
                * by a call to the function `strdup' (see Acse.lex) */
               free($1);
            }
            | IDENTIFIER ASSIGN exp 
            {
               int location;
               t_axe_instruction *instr;

               /* in order to assign a value to a variable, we have to
                * know where the variable is located (i.e. in which register).
                * the function `get_symbol_location' must be used in order
                * to retrieve the register location assigned to an Identifier.
                * A symbol table keeps track of the location of every
                * declared variable.
                * `get_symbol_location' will query the symbol table in order
                * to discover the correct location of the variable with $1
                * as identifier */
               
               /* get the location of the symbol with the given ID. */
               location = get_symbol_location(program, $1, 0);

               /* update the value of location */
               if ($3.expression_type == IMMEDIATE)
               {
                  /* ADDI R[location], R0, #imm */ 
                  instr = gen_addi_instruction
                     (program, location, REG_0, $3.value);
               }
               else
               {
                  /* ADD R[location], R0, R[exp] */
                  instr = gen_addi_instruction
                        (program, location, $3.value, 0);
               }

               /* free the memory associated with the IDENTIFIER */
               free($1);
            }
;

read_write_statements : READ LPAR IDENTIFIER RPAR 
            {
               int location;
               
               /* read from standard input an integer value and assign
                * it to a variable associated with the given identifier */
               /* get the location of the symbol with the given ID */
               
               /* lookup the symbol table and fetch the register location
                * associated with the IDENTIFIER $3. */
               location = get_symbol_location(program, $3, 0);

               /* insert a read instruction */
               gen_read_instruction (program, location);

               /* free the memory associated with the IDENTIFIER */
               free($3);
            }
            | WRITE LPAR exp RPAR 
            {
   
               int location;

               if ($3.expression_type == IMMEDIATE)
               {
                  /* load `immediate' into a new register. Returns the new register
                   * identifier or REG_INVALID if an error occurs */
                  location = load_immediate(program, $3.value);
               }
               else
                  location = $3.value;

               /* write to standard output an integer value */
               gen_write_instruction (program, location);
            }
;

while_statements  : WHILE
                  {
                     /* initialize the value of the non-terminal */
                     $1 = create_while_statement();

                     /* reserve and fix a new label */
                     $1.label_condition
                           = assignNewLabel(program);
                  }
                  LPAR exp RPAR
                  {
                     /* reserve a new label. This new label will point
                      * to the first instruction after the while code
                      * block */
                     $1.label_end = reserveLabel(program);

                     /* if `exp' returns FALSE, jump to the label $$ */
                     gen_beq_instruction (program, $1.label_end, 0);
                  }
                  code_block
                  {
                     /* if `exp' returns FALSE, jump to the label $$ */
                     gen_bt_instruction
                           (program, $1.label_condition, 0);

                     /* fix the label `label_end' */
                     fixLabel(program, $1.label_end);
                  }
;

for_statements  : FOR LPAR assignment_statements SEMI
                  {
                     /* initialize the value of the non-terminal */
                     $1 = create_for_statement();

                     /* reserve and fix a new label */
                     $1.label_condition = assignNewLabel(program);
                  }
                  exp SEMI 
                  {
                     /* reserve a new label. This new label will point
                      * to the first instruction after the loop body */
                     $1.label_end = reserveLabel(program);
                     $1.label_body = reserveLabel(program);

                     /* if `exp' returns TRUE, jump to the body label */
                     gen_bne_instruction (program, $1.label_body, 0);
                     /* if `exp' returns FALSE, jump to the end label */
                     gen_beq_instruction (program, $1.label_end, 0);

                     /* reserve and fix a new label */
                     $1.label_step = assignNewLabel(program);
                  }
									assignment_statements RPAR
									{

                     /* return to condition checking */
                     gen_bt_instruction
                           (program, $1.label_condition, 0);

                     /* fix the label `label_body' */
										 fixLabel(program, $1.label_body);							
									}
                  code_block
                  {
                     /* after executing the loop body, go to the step */
                     gen_bt_instruction
                           (program, $1.label_step, 0);
                     /* fix the label `label_end' */
                     fixLabel(program, $1.label_end);
                  }
								 | FOR IDENTIFIER IN IDENTIFIER DO
                  {
										 int location, reg, index, size;
										 t_axe_variable *array;

                     /* initialize the value of the non-terminal */
                     $1 = create_for_statement();
										 /* Low-level index */
										 index = getNewRegister(program);

                     /* lookup the symbol table and fetch the register *
      		            * location associated with the IDENTIFIER $2.    */
			               location = get_symbol_location(program, $2, 0);
										 
										 /* get the array size */
										 array = getVariable(program,$4);
										 if (!array->isArray) {
										 		notifyError(AXE_INVALID_TYPE);
										 }
										 size = array->arraySize;
											
										 /* PRE: Initialize index at 0 */
                     gen_add_instruction(program, index, REG_0, REG_0, CG_DIRECT_ALL);
										 
										 /* Reserve labels */
										 $1.label_end = reserveLabel(program);
										 $1.label_condition = assignNewLabel(program);
										 
										 /* COND: check current value of index vs array size */
										 gen_subi_instruction (program, REG_0, index, size);
										 gen_bge_instruction (program, $1.label_end, 0);
										 
                     /* STEP: load the value ($4)[i] into a new register */
                     reg = loadArrayElement(program, $4, create_expression(index, REGISTER));
                     gen_add_instruction(program, location, REG_0, reg, CG_DIRECT_ALL);

										 /* STEP: index++ */
										 gen_addi_instruction(program, index, index, 1);
                  }
                  code_block
                  {
                     /* after executing the loop body, go to the step */
                     gen_bt_instruction
                           (program, $1.label_condition, 0);
                     /* fix the label `label_end' */
                     fixLabel(program, $1.label_end);
                  }
;

                  
do_while_statements  : DO
                     {
                        /* the label that points to the address where to jump if
                         * `exp' is not verified */
                        $1 = reserveLabel(program);
                        
                        /* fix the label */
                        fixLabel(program, $1);
                     }
                     code_block WHILE LPAR exp RPAR
                     {
                        /* if `exp' returns FALSE, jump to the label $$ */
                        gen_bne_instruction (program, $1, 0);
                     }
;

if_statements  :  IF
               {
                  /* the label that points to the address where to jump if
                   * `exp' is not verified */
                  $1 = reserveLabel(program);
               } LPAR exp RPAR
               {
                  /* if `exp' returns FALSE, jump to the label $$ */
                  gen_beq_instruction (program, $1, 0);
               }
               code_block { $$ = $1; }
;

exp: NUMBER      { $$ = create_expression ($1, IMMEDIATE); }
   | IDENTIFIER  {
                     int location;
   
                     /* get the location of the symbol with the given ID */
                     location = get_symbol_location(program, $1, 0);
                     
                     /* return the register location of IDENTIFIER as
                      * a value for `exp' */
                     $$ = create_expression (location, REGISTER);

                     /* free the memory associated with the IDENTIFIER */
                     free($1);
   }
   | IDENTIFIER LSQUARE exp RSQUARE {
                     int reg;
                     
                     /* load the value IDENTIFIER[exp]
                      * into `arrayElement' */
                     reg = loadArrayElement(program, $1, $3);

                     /* create a new expression */
                     $$ = create_expression (reg, REGISTER);

                     /* free the memory associated with the IDENTIFIER */
                     free($1);
   }
   | NOT_OP NUMBER      { if ($2 == 0)
                              $$ = create_expression (1, IMMEDIATE);
                          else
                              $$ = create_expression (0, IMMEDIATE);
   }
   | NOT_OP IDENTIFIER  {
                           int identifier_location;
                           int output_register;
   
                           /* get the location of the symbol with the given ID */
                           identifier_location =
                                 get_symbol_location(program, $2, 0);

                           /* generate a NOT instruction. In order to do this,
                            * at first we have to ask for a free register where
                            * to store the result of the NOT instruction. */
                           output_register = getNewRegister(program);

                           /* Now we are able to generate a NOT instruction */
                           gen_notl_instruction (program, output_register
                                 , identifier_location);

                           $$ = create_expression (output_register, REGISTER);

                           /* free the memory associated with the IDENTIFIER */
                           free($2);
   }
   | exp AND_OP exp     {
                           $$ = perform_bin_numeric_op(program, $1, $3, ANDB);
   }
   | exp OR_OP exp      {
                           $$ = perform_bin_numeric_op(program, $1, $3, ORB);
   }
   | exp PLUS exp       {
                           $$ = perform_bin_numeric_op(program, $1, $3, ADD);
   }
   | exp MINUS exp      {
                           $$ = perform_bin_numeric_op(program, $1, $3, SUB);
   }
   | exp MUL_OP exp     {
                           $$ = perform_bin_numeric_op(program, $1, $3, MUL);
   }
   | exp DIV_OP exp     {
                           $$ = perform_bin_numeric_op(program, $1, $3, DIV);
   }
   | exp LT exp      {
                        $$ = perform_binary_comparison (program, $1, $3, _LT_);
   }
   | exp GT exp      {
                        $$ = perform_binary_comparison (program, $1, $3, _GT_);
   }
   | exp EQ exp      {
                        $$ = perform_binary_comparison (program, $1, $3, _EQ_);
   }
   | exp NOTEQ exp   {
                        $$ = perform_binary_comparison (program, $1, $3, _NOTEQ_);
   }
   | exp LTEQ exp    {
                        $$ = perform_binary_comparison (program, $1, $3, _LTEQ_);
   }
   | exp GTEQ exp    {
                        $$ = perform_binary_comparison (program, $1, $3, _GTEQ_);
   }
   | exp SHL_OP exp  {  $$ = perform_bin_numeric_op(program, $1, $3, SHL); }
   | exp SHR_OP exp  {  $$ = perform_bin_numeric_op(program, $1, $3, SHR); }
   | exp ANDAND exp  {  $$ = perform_bin_numeric_op(program, $1, $3, ANDL);}
   | exp OROR exp    {  $$ = perform_bin_numeric_op(program, $1, $3, ORL); }
   | LPAR exp RPAR   { $$ = $2; }
   | MINUS exp       {
                        if ($2.expression_type == IMMEDIATE)
                        {
                           $$ = $2;
                           $$.value = - ($$.value);
                        }
                        else
                        {
                           t_axe_expression exp_r0;

                           /* create an expression for regisrer REG_0 */
                           exp_r0.value = REG_0;
                           exp_r0.expression_type = REGISTER;
                           
                           $$ = perform_bin_numeric_op
                                 (program, exp_r0, $2, SUB);
                        }
                     }
;

%%
/*=========================================================================
                                  MAIN
=========================================================================*/
int main (int argc, char **argv)
{
   /* initialize all the compiler data structures and global variables */
   init_compiler(argc, argv);
   
   /* start the parsing procedure */
   yyparse();
   
#ifndef NDEBUG
   fprintf(stdout, "Parsing process completed. \n");
#endif

   /* test if the parsing process completed succesfully */
   checkConsistency();
   
#ifndef NDEBUG
   fprintf(stdout, "Creating a control flow graph. \n");
#endif

   /* create the control flow graph */
   graph = createFlowGraph(program->instructions);
   checkConsistency();

#ifndef NDEBUG
   assert(program != NULL);
   assert(program->sy_table != NULL);
   assert(file_infos != NULL);
   assert(file_infos->syTable_output != NULL);
   printSymbolTable(program->sy_table, file_infos->syTable_output);
   printGraphInfos(graph, file_infos->cfg_1, 0);
      
   fprintf(stdout, "Updating the basic blocks. \n");
#endif
      
   /* update the control flow graph by inserting load and stores inside
   * every basic block */
   graph = insertLoadAndStoreInstr(program, graph);

#ifndef NDEBUG
   fprintf(stdout, "Executing a liveness analysis on the intermediate code \n");
#endif
   performLivenessAnalysis(graph);
   checkConsistency();

#ifndef NDEBUG
   printGraphInfos(graph, file_infos->cfg_2, 1);
#endif
      
#ifndef NDEBUG
   fprintf(stdout, "Starting the register allocation process. \n");
#endif
   /* initialize the register allocator by using the control flow
    * informations stored into the control flow graph */
   RA = initializeRegAlloc(graph);
      
   /* execute the linear scan algorythm */
   execute_linear_scan(RA);
      
#ifndef NDEBUG
   printRegAllocInfos(RA, file_infos->reg_alloc_output);
#endif

#ifndef NDEBUG
   fprintf(stdout, "Updating the control flow informations. \n");
#endif
   /* apply changes to the program informations by using the informations
   * of the register allocation process */
   updateProgramInfos(program, graph, RA);

#ifndef NDEBUG
   fprintf(stdout, "Writing the assembly file... \n");
#endif
   writeAssembly(program, file_infos->output_file_name);
      
#ifndef NDEBUG
   fprintf(stderr, "Assembly written on file \"%s\".\n", file_infos->output_file_name);
#endif
   
   /* shutdown the compiler */
   shutdownCompiler(0);

   return 0;
}

/*=========================================================================
                                 YYERROR
=========================================================================*/
int yyerror(const char* errmsg)
{
   errorcode = AXE_SYNTAX_ERROR;
   
   return 0;
}
