//*******************************************************************************
//opt.c
//*******************************************************************************
//This file is part of the Orb compiler,
//Copyright (C) 2007 Adrian Fraga Fernandez
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//*******************************************************************************

#include <stdio.h>
#include "ast.h"
#include "opt.h"
#include "tds.h"

/***********************************************************************/
/* Optimización de código intermedio                                   */
/***********************************************************************/

	/**********************************/
	/* Referencias hacia delante      */
	/**********************************/

static void visitar(A_NODO *nodo);

	/**********************************/
	/* Variables globales             */
	/**********************************/
	
		/* Variables inicializadas con los símbolos correspon-  */
		/* dientes del mismo nombre, para evitar repetir llama- */
		/* das a "t_simbolo"                                    */
		
static char *operatorMinus;
static char *operatorPlus;
static char *operatorEqual;
static char *operatorNotEqual;
static char *operatorGreater;		
static char *operatorLess;
static char *operatorGreaterOrEqual;
static char *operatorLessOrEqual;
static char *operatorUnion;		
static char *operatorMultiplication;
static char *operatorDivision;
static char *operatorModulus;		
static char *operatorEqual;
static char *operatorNotEqual;
static char *toString;
static char *operatorConcat;
static char *operatorSet;
static char *operatorGet;

		/* Variable global utilizada para contener un puntero al*/
		/* último enlace paterno recorrido, para modificarlo si */
		/* es necesario                                         */
	
static A_NODO **nodoref;	

	/****************************************************************/
	/* Metodos del patron visitante                                 */
	/****************************************************************/

		/* Nodos para los que no se realiza ninguna acción */
	
static void visitarPrograma(A_NODOPROGRAMA *nodo){
	if (nodo->h1!=NULL) {nodoref=&nodo->h1;visitar(nodo->h1);}
}
static visitarObjeto(A_NODOOBJETO *nodo){
	if (nodo->h1!=NULL) {nodoref=&nodo->h1;visitar(nodo->h1);}
	if (nodo->h2!=NULL) {nodoref=&nodo->h2;visitar(nodo->h2);}
	if (nodo->h3!=NULL) {nodoref=&nodo->h3;visitar(nodo->h3);}
	if (nodo->h4!=NULL) {nodoref=&nodo->h4;visitar(nodo->h4);}
}
static visitarMiembro(A_NODOMIEMBRO *nodo){
	if (nodo->h1!=NULL) {nodoref=&nodo->h1;visitar(nodo->h1);}
	if (nodo->h2!=NULL) {nodoref=&nodo->h2;visitar(nodo->h2);}
	if (nodo->h3!=NULL) {nodoref=&nodo->h3;visitar(nodo->h3);}
	if (nodo->h4!=NULL) {nodoref=&nodo->h4;visitar(nodo->h4);}
}
static visitarLet(A_NODOLET *nodo){
	if (nodo->h1!=NULL) {nodoref=&nodo->h1;visitar(nodo->h1);}
	if (nodo->h2!=NULL) {nodoref=&nodo->h2;visitar(nodo->h2);}
}
static visitarNodo1(A_NODO1 *nodo){
	if (nodo->h1!=NULL) {nodoref=&nodo->h1;visitar(nodo->h1);}
}
static visitarNodo2(A_NODO2 *nodo){
	if (nodo->h1!=NULL) {nodoref=&nodo->h1;visitar(nodo->h1);}
	if (nodo->h2!=NULL) {nodoref=&nodo->h2;visitar(nodo->h2);}
}
static visitarNodo3(A_NODO3 *nodo){
	if (nodo->h1!=NULL) {nodoref=&nodo->h1;visitar(nodo->h1);}
	if (nodo->h2!=NULL) {nodoref=&nodo->h2;visitar(nodo->h2);}
	if (nodo->h3!=NULL) {nodoref=&nodo->h3;visitar(nodo->h3);}
}

		/* Nodos para los que se realiza alguna acción */
	
static void aux1(A_NODO1 *nodo, char *miembro){	
A_NODO3 *nuevo;
	
	*nodoref=a_nodo3(A_ACCESOMIEMBRO,
		nodo->h1,
		a_id(miembro,&nodo->posicion),
		NULL,
		&nodo->posicion);
	a_free(nodo);
	visitar(*nodoref);
}
static void aux2(A_NODO2 *nodo, char *miembro){
A_NODO3 *nuevo;

	*nodoref=a_nodo3(A_ACCESOMIEMBRO,
		nodo->h1,
		a_id(miembro,&nodo->posicion),
		a_nodo2(A_PARAMETROSLISTA,
			nodo->h2,
			NULL,
			&nodo->posicion),
		&nodo->posicion);
	a_free(nodo);
	visitar(*nodoref);
}

static void visitarMenos(A_NODO1 *nodo){	
	aux1(nodo,operatorMinus);
}
static void visitarMas(A_NODO1 *nodo){
	aux1(nodo,operatorPlus);
}
static void visitarIgual(A_NODO2 *nodo){
	aux2(nodo,operatorEqual);
}
static void visitarNoIgual(A_NODO2 *nodo){
	aux2(nodo,operatorNotEqual);
}
static void visitarMayor(A_NODO2 *nodo){
	aux2(nodo,operatorGreater);
}
static void visitarMenor(A_NODO2 *nodo){
	aux2(nodo,operatorLess);
}
static void visitarMayorIgual(A_NODO2 *nodo){
	aux2(nodo,operatorGreaterOrEqual);
}
static void visitarMenorIgual(A_NODO2 *nodo){
	aux2(nodo,operatorLessOrEqual);
}
static void visitarUnion(A_NODOUNION *nodo){
	aux2(nodo,operatorUnion);
}
static void visitarSuma(A_NODO2 *nodo){
	aux2(nodo,operatorPlus);
}
static void visitarResta(A_NODO2 *nodo){
	aux2(nodo,operatorMinus);
}
static void visitarMultiplicacion(A_NODO2 *nodo){
	aux2(nodo,operatorMultiplication);
}
static void visitarDivision(A_NODO2 *nodo){
	aux2(nodo,operatorDivision);
}
static void visitarModulo(A_NODO2 *nodo){
	aux2(nodo,operatorModulus);
}
static void visitarAccesoArray(A_NODO2 *nodo){	
	aux2(nodo,operatorGet);	
}
static void visitarConcatenacion(A_NODOCONCATENACION *nodo){
A_NODO3 *nuevo;

	*nodoref=
		a_nodo3(A_ACCESOMIEMBRO,
			a_nodo3(A_ACCESOMIEMBRO,
				nodo->h1,
				a_id(toString,&nodo->posicion),
				NULL,
				&nodo->posicion),
			a_id(operatorConcat,&nodo->posicion),
			a_nodo2(A_PARAMETROSLISTA,
				a_nodo3(A_ACCESOMIEMBRO,
					nodo->h2,
					a_id(toString,&nodo->posicion),
					NULL,
					&nodo->posicion),
				NULL,
				&nodo->posicion),
			&nodo->posicion);
	a_free(nodo);
	visitar(*nodoref);
}
static void visitarAsignacion(A_NODOASIGNACION *nodo){
A_NODO2 *h1;
A_NODO3 *nuevo;

	if (nodo->h1->tipo==A_ACCESOARRAY){
		h1=(A_NODO2 *)nodo->h1;
		*nodoref=
			a_nodo3(A_ACCESOMIEMBRO,
				h1->h1,
				a_id(operatorSet,&h1->posicion),
				a_nodo2(A_PARAMETROSLISTA,
					h1->h2,
					a_nodo2(A_PARAMETROSLISTA,
						nodo->h2,
						NULL,
						&nodo->posicion
						),
					&nodo->posicion),
				&nodo->posicion);
		
		a_free(nodo);
		a_free(h1);
		visitar(*nodoref);
	}
	else{
		if (nodo->h1!=NULL) {nodoref=&nodo->h1;visitar(nodo->h1);}
		if (nodo->h2!=NULL) {nodoref=&nodo->h2;visitar(nodo->h2);}
	}
				
}

	/* Conversión For a Do-While */

static visitarFor(A_NODOFOR *nodo){
	*nodoref=a_nodosecuencia(nodo->h1,
			a_nodowhile(nodo->h2,
				a_nodosecuencia(nodo->h4,
				                nodo->h3,
						&nodo->posicion
						),
				&nodo->posicion
				),
			&nodo->posicion
			);
	a_free(nodo);
	visitar(*nodoref);
}

	/* Convertir secuencias tipo bloque a secuencias de operador secuencia */
	
static void visitarBloque(A_NODOBLOQUE *nodo){
	nodo->tipo=A_SECUENCIA;
	nodoref=&nodo->h1;visitar(nodo->h1);
	nodoref=&nodo->h2;visitar(nodo->h2);
}
	/****************************************************************/
	/* Patrón visitante                                             */
	/****************************************************************/

static void visitarError(A_NODO *nodo){
	e_fatal(NULL,"Error interno: Patron visitante (Optimizacion de Codigo Intermedio)");
}	
	
static void *tablaVisitante[A_TIPO_SIZE];
static void inicializarTablaVisitante(void){
int i;
	for(i=0;i<A_TIPO_SIZE;i++) tablaVisitante[i]=&visitarError;
	tablaVisitante[A_PROGRAMA]=&visitarPrograma;	
	tablaVisitante[A_OBJETO]=&visitarObjeto;	
	tablaVisitante[A_MIEMBRO]=&visitarMiembro;
	tablaVisitante[A_LET]=&visitarLet;
	tablaVisitante[A_RETURN]=&visitarNodo1;
	tablaVisitante[A_STATIC]=&visitarNodo1;
	tablaVisitante[A_MAS]=&visitarMas;
	tablaVisitante[A_MENOS]=&visitarMenos;
	tablaVisitante[A_NOT]=&visitarNodo1;
	tablaVisitante[A_LITERALSECUENCIA]=&visitarNodo1;
	tablaVisitante[A_OR]=&visitarNodo2;
	tablaVisitante[A_AND]=&visitarNodo2;
	tablaVisitante[A_IGUAL]=&visitarIgual;
	tablaVisitante[A_NOIGUAL]=&visitarNoIgual;
	tablaVisitante[A_IDENTICO]=&visitarNodo2;
	tablaVisitante[A_NOIDENTICO]=&visitarNodo2;
	tablaVisitante[A_MAYOR]=&visitarMayor;
	tablaVisitante[A_MENOR]=&visitarMenor;
	tablaVisitante[A_MAYORIGUAL]=&visitarMayorIgual;
	tablaVisitante[A_MENORIGUAL]=&visitarMenorIgual;
	tablaVisitante[A_UNION]=&visitarUnion;
	tablaVisitante[A_CONCATENACION]=&visitarConcatenacion;
	tablaVisitante[A_SUMA]=&visitarSuma;
	tablaVisitante[A_RESTA]=&visitarResta;
	tablaVisitante[A_MULTIPLICACION]=&visitarMultiplicacion;
	tablaVisitante[A_DIVISION]=&visitarDivision;
	tablaVisitante[A_MODULO]=&visitarModulo;
	tablaVisitante[A_IS]=&visitarNodo2;
	tablaVisitante[A_ISNOT]=&visitarNodo2;
	tablaVisitante[A_HAS]=&visitarNodo2;
	tablaVisitante[A_HASNOT]=&visitarNodo2;
	tablaVisitante[A_ACCESOARRAY]=&visitarAccesoArray;
	tablaVisitante[A_LITERALSECUENCIALISTA]=&visitarNodo2;
	tablaVisitante[A_CUERPOOBJETOLISTA]=&visitarNodo2;
	tablaVisitante[A_DOWHILE]=&visitarNodo2;
	tablaVisitante[A_WHILE]=&visitarNodo2;
	tablaVisitante[A_ASIGNACION]=&visitarAsignacion;
	tablaVisitante[A_PARAMETROSLISTA]=&visitarNodo2;
	tablaVisitante[A_SECUENCIA]=&visitarNodo2;
	tablaVisitante[A_BLOQUE]=&visitarBloque;
	tablaVisitante[A_RESEND]=&visitarNodo3;
	tablaVisitante[A_DECLARACIONLISTA]=&visitarNodo3;
	tablaVisitante[A_IF]=&visitarNodo3;
	tablaVisitante[A_ACCESOMIEMBRO]=&visitarNodo3;
	tablaVisitante[A_FOR]=&visitarFor;	
	tablaVisitante[A_IDATOMICO]=&visitarNodo2;
	tablaVisitante[A_LITERALCADENA]=NULL;
	tablaVisitante[A_LITERALENTERO]=NULL;
	tablaVisitante[A_LITERALTRUE]=NULL;
	tablaVisitante[A_LITERALFALSE]=NULL;
	tablaVisitante[A_LITERALREAL]=NULL;
	tablaVisitante[A_ID]=NULL;
	tablaVisitante[A_ERROR]=NULL;
	tablaVisitante[A_IDLISTA]=NULL;
	tablaVisitante[A_MODOBJETOLISTA]=NULL;
	tablaVisitante[A_ABSTRACT]=NULL;
	tablaVisitante[A_HIDDEN]=NULL;
	tablaVisitante[A_MIEMBROEXTERNO]=NULL;
	tablaVisitante[A_MODMIEMBROLISTA]=NULL;
	tablaVisitante[A_SHARED]=NULL;
	tablaVisitante[A_VAR]=NULL;
	tablaVisitante[A_ARGUMENTOSLISTA]=NULL;
	tablaVisitante[A_BREAK]=NULL;
	tablaVisitante[A_CONTINUE]=NULL;
	tablaVisitante[A_NATIVE]=NULL;
}

static void visitar(A_NODO *nodo){
void (*ptr)(void *n);

	if (nodo!=NULL){
		ptr=tablaVisitante[nodo->tipo];
		if (ptr!=NULL) ptr(nodo);
	}
}

	/************/
	/* Interfaz */
	/************/

A_NODO *s_opt(A_NODO *ast){
	operatorMinus=t_simbolo("operatorminus");
	operatorPlus=t_simbolo("operatorplus");
	operatorEqual=t_simbolo("operatorequal");
	operatorNotEqual=t_simbolo("operatornotequal");
	operatorGreater=t_simbolo("operatorgreater");		
	operatorLess=t_simbolo("operatorless");
	operatorGreaterOrEqual=t_simbolo("operatorgreaterorequal");
	operatorLessOrEqual=t_simbolo("operatorlessorequal");
	operatorUnion=t_simbolo("operatorunion");
	operatorMultiplication=t_simbolo("operatormultiplication");
	operatorDivision=t_simbolo("operatordivision");
	operatorModulus=t_simbolo("operatormodulus");
	operatorEqual=t_simbolo("operatorequal");
	operatorNotEqual=t_simbolo("operatornotequal");
	toString=t_simbolo("tostring");
	operatorConcat=t_simbolo("operatorconcat");
	operatorSet=t_simbolo("operatorset");
	operatorGet=t_simbolo("operatorget");
	inicializarTablaVisitante();
	visitar(ast);
	return ast;
}


