//*******************************************************************************
//sem.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/>.
//*******************************************************************************

/***********************************************************************/
/* Analisis semantico                                                  */
/***********************************************************************/

#include <stdio.h>
#include <string.h>
#include "error.h"
#include "sem.h"
#include "tds.h"
#include "ast.h"

/********************************************/
/* Emparejamiento if-else                   */
/********************************************/

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

static A_NODO **nodoref;	
		
/********************************************/
/* Objetos y métodos nativos                */
/********************************************/

	/**************************************/
	/* Lista de objetos y metodos nativos */
	/**************************************/

static struct{
	char *objeto;
	char *miembro;
	unsigned char parametros;
} 
_nativos[]=
{
	{"object",NULL,0},
	{NULL,"self",0},
	{NULL,"copy",0},
	{NULL,"deepcopy",0},
	{NULL,"lexicalparent",0},
	{NULL,"lexicalsons",0},
	{NULL,"operatorequal",1},
	{NULL,"operatornotequal",1},
	{NULL,"tostring",0},
	{"null",NULL,0},
	{NULL,"operatorequal",1},
	{NULL,"operatornotequal",1},
	{NULL,"tostring",0},
	{"boolean",NULL,0},
	{NULL,"operatorequal",1},
	{NULL,"operatornotequal",1},
	{NULL,"tostring",0},
	{"integer",NULL,0},
	{NULL,"operatorplus",0},
	{NULL,"operatorminus",0},
	{NULL,"operatorplus",1},
	{NULL,"operatorminus",1},	
	{NULL,"operatormultiplication",1},
	{NULL,"operatordivision",1},	
	{NULL,"operatormodulus",1},	
	{NULL,"operatorequal",1},
	{NULL,"operatornotequal",1},	
	{NULL,"operatorgreater",1},
	{NULL,"operatorless",1},	
	{NULL,"operatorgreaterorequal",1},
	{NULL,"operatorlessorequal",1},	
	{NULL,"tofloat",0},
	{NULL,"tostring",0},	
	{"float",NULL,0},
	{NULL,"operatorplus",0},
	{NULL,"operatorminus",0},
	{NULL,"operatorplus",1},
	{NULL,"operatorminus",1},	
	{NULL,"operatormultiplication",1},
	{NULL,"operatordivision",1},	
	{NULL,"operatormodulus",1},		
	{NULL,"operatorequal",1},
	{NULL,"operatornotequal",1},	
	{NULL,"operatorgreater",1},
	{NULL,"operatorless",1},	
	{NULL,"operatorgreaterorequal",1},
	{NULL,"operatorlessorequal",1},	
	{NULL,"tointeger",0},
	{NULL,"tostring",0},	
	{"string",NULL,0},
	{NULL,"length",0},
	{NULL,"operatorequal",1},
	{NULL,"operatornotequal",1},	
	{NULL,"operatorget",1},
	{NULL,"operatorconcat",1},
	{NULL,"tokens",0},
	{NULL,"tokens",1},
	{NULL,"tolower",0},
	{NULL,"toupper",0},	
	{NULL,"tostring",0},	
	{"array",NULL,0},
	{NULL,"length",0},
	{NULL,"operatorequal",1},
	{NULL,"operatornotequal",1},	
	{NULL,"operatorget",1},
	{NULL,"operatorset",2},
	{NULL,"operatorunion",1},
	{NULL,"tostring",0},	
	{"system",NULL,0},
	{NULL,"readln",0},
	{NULL,"print",1},
	{NULL,"println",1},
	{NULL,"exit",0},
	
	{NULL,NULL,0}	/* marca de finalización de secuencia*/
};

	/****************************************************************/
	/* Inicializar tabla de simbolos para metodos y objetos nativos */
	/****************************************************************/

static void *nativos; /* tabla de simbolos*/

#define NAT_VACIO (&_nativos)
static void inicializarNativos(void){
int i;
void *miembros;
char *id;

	id=NULL;
	miembros=NULL;
	nativos=t_crear();	
	for(i=0;_nativos[i].miembro!=NULL || _nativos[i].objeto!=NULL;i++){
		if (_nativos[i].objeto!=NULL){
			if (id!=NULL) nativos=t_insertar(nativos,
				t_simbolo(id),0,
				miembros==NULL?NAT_VACIO:miembros);
			id=_nativos[i].objeto;
			miembros=t_crear();
		}
		else
			miembros=t_insertar(miembros,
				t_simbolo(_nativos[i].miembro),_nativos[i].parametros,
				NAT_VACIO);
	}	
	t_insertar(nativos,t_simbolo(id),0,miembros==NULL?NAT_VACIO:miembros);
}

	/**********************************************/
	/* Comprobar si un objeto o miembro es nativo */
	/**********************************************/

static char esNativo(char *idObjeto,char *idMiembro,unsigned char parametros){
void *p;
	p=t_buscar(nativos,idObjeto,0);
	if (idMiembro!=NULL) p=p==NAT_VACIO?NULL:t_buscar(p,idMiembro,parametros);
	return p!=NULL;
}

/********************************************/
/* Resolucion de nombres                    */
/********************************************/
/* Utiliza: programa->tds		    */
/* Utiliza: objeto->tds  		    */
/* Utiliza: objeto->heredados		    */
/* Utiliza: objeto->nodoEspNombres          */
/* Utiliza: miembro->tds                    */
/* Utiliza: miembro->nodoEspNombres         */
/* Utiliza: let->tds                        */
/* Utiliza: let->nodoEspNombres             */
/********************************************/
	
static A_NODO *buscarId(A_NODO *nodoEspNombres, char *id, unsigned char parametros){
A_NODO *n;

	switch(nodoEspNombres->tipo){
		case A_PROGRAMA:
			n=t_buscar(((A_NODOPROGRAMA *)nodoEspNombres)->tds,id,parametros);
			if (n==NULL && id==t_simbolo("program") && parametros==0) n=nodoEspNombres;
			break;
		case A_OBJETO:
			n=t_buscar(((A_NODOOBJETO *)nodoEspNombres)->tds,id,parametros);
			if (n==NULL) n=t_buscar(((A_NODOOBJETO *)nodoEspNombres)->heredados,id,parametros);
			if (n==NULL) n=buscarId(((A_NODOOBJETO *)nodoEspNombres)->nodoEspNombres,id,parametros);
			break;
		case A_MIEMBRO:
			n=t_buscar(((A_NODOMIEMBRO *)nodoEspNombres)->tds,id,parametros);
			if (n==NULL) n=buscarId(((A_NODOMIEMBRO *)nodoEspNombres)->nodoEspNombres,id,parametros);
			break;
		case A_LET:
			n=t_buscar(((A_NODOLET *)nodoEspNombres)->tds,id,parametros);
			if (n==NULL) n=buscarId(((A_NODOLET *)nodoEspNombres)->nodoEspNombres,id,parametros);
			break;
		default:
			n=NULL;
			break;
	}
	return n;
}

/********************************************/
/* Obtener identificadores                  */
/********************************************/

static char *obtenerId(A_NODOID *nodo){
	return nodo==NULL?NULL:nodo->id;
}

static char *obtenerIdObjeto(A_NODOOBJETO *nodo){
	return obtenerId((A_NODOID*)(nodo->h2));
}

static char *obtenerIdMiembro(A_NODOMIEMBRO *nodo){
	return obtenerId((A_NODOID*)(nodo->h2));
}

/********************************************/
/* Conteo de parametros                     */
/********************************************/

static unsigned contarParametros(A_NODOPARAMETROSLISTA *nodo){
unsigned x;
	for(x=0;nodo!=NULL;nodo=(A_NODOPARAMETROSLISTA *)(nodo->h2),x++);
	if (x>255)
		e_error(&nodo->posicion,"No se permite la declaración de más de 255 parámetros");
	return x;
}

static unsigned contarParametrosMiembro(A_NODOMIEMBRO *nodo){
	return contarParametros((A_NODOPARAMETROSLISTA*)(nodo->h3));
}

/************************************************/
/* Construcción de máscaras de modificadores    */
/************************************************/

	/************************************************************/
	/* Modificadores de un miembro       			    */
	/************************************************************/

static A_MOD construirModMiembro(A_NODOMIEMBRO *miembro){
A_NODOMODMIEMBROLISTA *nmod;
A_NODO *ncuerpo;
A_MOD mod;

	mod=0;
	
	nmod=(A_NODOMODMIEMBROLISTA*)(miembro->h1);
	while(nmod!=NULL){
		switch(nmod->h1->tipo){
			case A_VAR:   	mod|=A_MODVAR;break;
			case A_SHARED:  mod|=A_MODSHARED;break;		
		}		
		nmod=(A_NODOMODMIEMBROLISTA*)(nmod->h2);
	}
		
	switch(miembro->h4->tipo){
		case A_ABSTRACT:	mod|=A_MODABSTRACT;break;
		case A_MIEMBROEXTERNO:	mod|=A_MODEXTERN;break;
		case A_NATIVE:		mod|=A_MODNATIVE;break;
	}
	return mod;
}
	
	/************************************************************/
	/* Modificadores de un objeto				    */	
	/************************************************************/
	/* Excepto: A_MODHERENCIAMULTIPLE			    */
	/* Excepto: A_SOLAPAMIENTO				    */
	/************************************************************/

static A_MOD construirModObjeto(A_NODOOBJETO *objeto){
A_MOD mod;
A_NODOMODOBJETOLISTA *nodo;

	nodo=(A_NODOMODOBJETOLISTA*)(objeto->h1);
	mod=0;
	while(nodo!=NULL){
		switch(nodo->h1->tipo){
			case A_ABSTRACT:mod|=A_MODABSTRACT|A_MODHIDDEN;break;
			case A_HIDDEN:	mod|=A_MODHIDDEN;break;
			case A_NATIVE:	mod|=A_MODNATIVE;break;
		}			
		
		nodo=(A_NODOMODOBJETOLISTA*)(nodo->h2);
	}
	return mod;
}

/**************************************/
/* Construcción de tablas de simbolos */
/**************************************/

	/************************************************************/
	/* Miembros declarados en el cuerpo de un programa u objeto */
	/************************************************************/
	/* Comprueba: Declaraciones repetidas                       */
	/************************************************************/

static void *construirTdsObjetoPrograma(A_NODOCUERPOOBJETOLISTA* nodo){
void *tabla;
char *id;
unsigned char parametros;
A_NODO *t;
A_NODOID *nodoid;
E_POSICION *posicion;

	tabla=t_crear();	
	while(nodo!=NULL){
		switch(nodo->h1->tipo){
			case A_OBJETO:
				id=obtenerIdObjeto((A_NODOOBJETO*)(nodo->h1));
				parametros=0;
				posicion=&((A_NODOID*) ((A_NODOOBJETO*)(nodo->h1)) ->h2)->posicion;
				break;
			case A_MIEMBRO:
				id=obtenerIdMiembro((A_NODOMIEMBRO*)(nodo->h1));
				parametros=contarParametrosMiembro((A_NODOMIEMBRO*)(nodo->h1));
				posicion=&((A_NODOID*) ((A_NODOMIEMBRO*)(nodo->h1)) ->h2)->posicion;
				break;
			default:
				e_fatal(&nodo->posicion,"Error interno: construirTdsObjeto");
				break;
		}

		if (id!=NULL){		
			t=t_buscar(tabla,id,parametros);	
			if (t!=NULL)
				e_error(posicion,"Ya se ha declarado un %s '%s'%s",t->tipo==A_OBJETO?"objeto":"miembro",id,parametros==0?"":" con el mismo numero de parametros");
			else
				tabla=t_insertar(tabla,id,parametros,nodo->h1);
				
		}
		nodo=(A_NODOCUERPOOBJETOLISTA*)(nodo->h2);
	}
	return tabla;
}

	/************************************************************/
	/* Miembros declarados en un programa 			    */
	/************************************************************/
	/* Comprueba: Declaraciones repetidas                       */
	/************************************************************/

static void *construirTdsPrograma(A_NODOPROGRAMA *programa){
	construirTdsObjetoPrograma((A_NODOCUERPOOBJETOLISTA*)(programa->h1));
}
	
	/************************************************************/
	/* Miembros declarados en un objeto  			    */
	/************************************************************/
	/* Comprueba: Declaraciones repetidas                       */
	/************************************************************/

static void *construirTdsObjeto(A_NODOOBJETO *objeto){
	construirTdsObjetoPrograma((A_NODOCUERPOOBJETOLISTA*)(objeto->h4));
}

	/************************************************************/
	/* Padres declarados para un objeto  			    */
	/************************************************************/
	/* Utiliza: objeto->nodoEspNombres ('buscarId')             */
	/* Utiliza: objeto->id					    */
	/************************************************************/
	/* Comprueba: Padres invalidos                              */
	/* Comprueba: Padres repetidos (aviso) 			    */
	/************************************************************/

static void *construirTdsPadres(A_NODOOBJETO *objeto,unsigned* numeroPadres){
A_NODOIDLISTA *nodo;
char *id;
A_NODO *n;
void *tabla;
unsigned contador;

	tabla=t_crear();
	contador=0;
	for(contador=0,nodo=(A_NODOIDLISTA*)(objeto->h3);
	    nodo!=NULL;
	    contador++,nodo=(A_NODOIDLISTA*)(nodo->h2))
	    {
		id=obtenerId((A_NODOID*)(nodo->h1));
		if (t_buscar(tabla,id,0)!=NULL)
			e_warning(&((A_NODOID *)(nodo->h1))->posicion,"Ya se ha declarado '%s' como padre del objeto",id);
		else{
			n=buscarId(objeto->nodoEspNombres,id,0);	
			if (n==NULL || n->tipo!=A_OBJETO)
				e_error(&nodo->posicion,"'%s' no es un objeto valido",id);
			else if (objeto->id!=t_simbolo("object") || id!=t_simbolo("object"))
				tabla=t_insertar(tabla,id,0,n);
		}
	}	
	if (numeroPadres!=NULL) *numeroPadres=contador;
	return tabla;	
}

	/************************************************************/
	/* Parametros declarados para un miembro		    */
	/************************************************************/
	/* Comprueba: declaraciones repetidas                       */
	/************************************************************/

static void *construirTdsMiembro(A_NODOMIEMBRO *miembro){
void *tabla;
A_NODOARGUMENTOSLISTA *nodo;
char *id;

	tabla=t_crear();
	nodo=(A_NODOARGUMENTOSLISTA*)(miembro->h3);
	while(nodo!=NULL){
		id=obtenerId((A_NODOID*)(nodo->h1));
		if (t_buscar(tabla,id,0)!=NULL)
				e_error(&((A_NODOID *)(nodo->h1))->posicion,"Ya se ha declarado un argumento '%s'",id);
			else
				tabla=t_insertar(tabla,id,0,nodo->h1);
		nodo=(A_NODOARGUMENTOSLISTA*)(nodo->h2);
	}
	return tabla;
}

	/************************************************************/
	/* Miembros heredados y miembros solapados para un objeto   */
	/************************************************************/
	/* Comprueba: Dependencias cíclicas de paternidad           */
	/************************************************************/
	/* Utiliza: objeto->id                                      */
	/* Utiliza: objeto->padres                                  */
	/* Utiliza: tablas de simbolos de objetos declarados para   */
	/*          cada objeto en objeto->padres                   */
	/************************************************************/

static A_NODOOBJETO *auxobjeto;		/* Objeto */					
static E_POSICION *auxerrpos;		/* Posicion de la declaración de paternidad  */
static void *auxtablaheredados;		/* Miembros heredados */
static void *auxtablasolapados;		/* Miembros solapados */

static void *auxtablapadresrama;	/* Nodos recorridos en rama del árbol de paternidad */
static void *auxtablaheredadosrama;	/* Miembros heredados en rama del arbol de paternidad*/
static char auxciclos;			/* Existencia de ciclos de paternidad en dicha rama */

	
static void auxmiembros(char *simbolo,unsigned char parametros,void *datos){
	if (t_buscar(auxtablaheredadosrama,simbolo,parametros)==NULL)
		auxtablaheredadosrama=t_insertar(auxtablaheredadosrama,simbolo,parametros,datos);
}

static void auxpadres(char *simbolo,unsigned char parametros,void *datos){
A_NODOOBJETO *nodo;
	nodo=(A_NODOOBJETO*)datos;
	
	if (t_buscar(auxtablapadresrama,simbolo,parametros))
		auxciclos=1;
	else{
		auxtablapadresrama=t_insertar(auxtablapadresrama,simbolo,parametros,datos);
		t_forAll(nodo->tds,&auxmiembros);
		t_forAll(nodo->padres,&auxpadres);
		auxtablapadresrama=t_borrar(auxtablapadresrama,simbolo,parametros,NULL);
	}
}


static void auxheredadosrama(char *simbolo,unsigned char parametros,void *datos){
void *t;
	t=t_buscar(auxtablaheredados,simbolo,parametros);
	if (t==NULL)
		auxtablaheredados=t_insertar(auxtablaheredados,simbolo,parametros,datos);
	else if (t!=datos){
		if (t_buscar(auxtablasolapados,simbolo,parametros)==NULL)
			auxtablasolapados=t_insertar(auxtablasolapados,simbolo,parametros,datos);
	}
}

static void auxpadresdirectos(char *simbolo,unsigned char parametros,void *datos){
	auxciclos=0;
	auxtablapadresrama=t_crear();
	auxtablapadresrama=t_insertar(auxtablapadresrama,auxobjeto->id,0,auxobjeto);
	auxtablaheredadosrama=t_crear();
	
		/* Construir tds de heredados para ese padre directo */
	
	auxpadres(simbolo,0,datos);
	
		/* Actualizar tabla de heredados y solapados para todos los padres */
	
	t_forAll(auxtablaheredadosrama,&auxheredadosrama);
	
	t_free(auxtablapadresrama,NULL);
	t_free(auxtablaheredadosrama,NULL);
			
	if (auxciclos){
		e_error(auxerrpos,"'%s' provoca una dependencia ciclica de paternidad",simbolo);
		//nodo->padres=t_borrar(nodo->padres,id,0,NULL); <- no es necesario
	}
}


static void construirTdsHeredadosSolapados(A_NODOOBJETO *nodo,void **her,void **sol){
A_NODOIDLISTA *l;
A_NODOOBJETO *padre;
char *id;
	
	auxobjeto=nodo;
	auxtablaheredados=t_crear();
	auxtablasolapados=t_crear();
	auxerrpos=&((A_NODOIDLISTA*)(nodo->h3))->posicion;
	t_forAll(nodo->padres,&auxpadresdirectos);
	*sol=auxtablasolapados;
	*her=auxtablaheredados;
}

/******************************************************************/
/* Anotar ramas superiores del arbol sintactico abstracto         */
/******************************************************************/

static void anotarPrograma1(A_NODOPROGRAMA *nodo);	/* primer recorrido */
static void anotarPrograma2(A_NODOPROGRAMA *nodo);	/* segundo recorrido */

static void anotarRamasSuperiores(A_NODO *ast){
	if (ast==NULL) return;
	anotarPrograma1((A_NODOPROGRAMA*)ast);
	anotarPrograma2((A_NODOPROGRAMA*)ast);
}

	/************************************************/
	/* Primer recorrido:				*/
	/*	-Programa:				*/
	/*		-TDS Miembros declarados 	*/
	/*	-Objeto:				*/
	/*		-Modificadores 			*/
	/*		-Identificador			*/
	/*		-TDS Miembros declarados	*/
	/*		-TDS Padres			*/
	/*		-Espacio de nombres superior	*/
	/*	-Miembro:				*/
	/*		-Modificadores			*/
	/*		-Identificador			*/
	/*		-Numero de parámetros		*/
	/*		-TDS parámetros declarados 	*/
	/*						*/
	/************************************************/

static void recorrerCuerpoObjeto1(A_NODO* nodoEspNombres,A_NODOCUERPOOBJETOLISTA *nodo);

static void anotarPrograma1(A_NODOPROGRAMA *nodo){
	nodo->tds=construirTdsPrograma(nodo);	
	recorrerCuerpoObjeto1((A_NODO*)nodo,(A_NODOCUERPOOBJETOLISTA*)(nodo->h1));
}

static void anotarObjeto1(A_NODO *nodoEspNombres,A_NODOOBJETO *nodo){
unsigned contador;
	nodo->nodoEspNombres=nodoEspNombres;
	nodo->id=obtenerIdObjeto(nodo);
	nodo->mod=construirModObjeto(nodo);
	nodo->tds=construirTdsObjeto(nodo);
	nodo->padres=construirTdsPadres(nodo,&contador);
	if (contador>1) nodo->mod|=A_MODHERENCIAMULTIPLE;
	recorrerCuerpoObjeto1((A_NODO*)nodo,(A_NODOCUERPOOBJETOLISTA*)(nodo->h4));
}

static void anotarMiembro1(A_NODO *nodoEspNombres,A_NODOMIEMBRO *nodo){
	nodo->nodoEspNombres=nodoEspNombres;
	nodo->id=obtenerIdMiembro(nodo);
	nodo->tds=construirTdsMiembro(nodo);
	nodo->parametros=contarParametrosMiembro(nodo);
	nodo->mod=construirModMiembro(nodo);
}

static void recorrerCuerpoObjeto1(A_NODO* nodoEspNombres,A_NODOCUERPOOBJETOLISTA *nodo){
	while(nodo!=NULL){
		switch(nodo->h1->tipo){
			case A_OBJETO:
				anotarObjeto1(nodoEspNombres,(A_NODOOBJETO*)(nodo->h1));
				break;
			case A_MIEMBRO:
				anotarMiembro1(nodoEspNombres,(A_NODOMIEMBRO*)(nodo->h1));
				break;
		}
		nodo=(A_NODOCUERPOOBJETOLISTA*)(nodo->h2);
	}
}
	
	/************************************************/
	/* Segundo recorrido:				*/
	/*	-Objeto:				*/
	/*		-TDS Miembros heredados 	*/
	/*		-TDS Miembros solapados		*/
	/*		-Modificadores			*/
	/************************************************/

static void recorrerCuerpoObjeto2(A_NODO* nodoEspNombres,A_NODOCUERPOOBJETOLISTA *nodo);

static void anotarPrograma2(A_NODOPROGRAMA *nodo){
	recorrerCuerpoObjeto2((A_NODO*)nodo,(A_NODOCUERPOOBJETOLISTA*)(nodo->h1));
}

static void anotarObjeto2(A_NODOOBJETO *nodo){
	construirTdsHeredadosSolapados(nodo,&nodo->heredados,&nodo->solapados);
	if (!t_vacio(nodo->solapados)) nodo->mod|=A_MODSOLAPADOS;
	recorrerCuerpoObjeto2((A_NODO*)nodo,(A_NODOCUERPOOBJETOLISTA*)(nodo->h4));
}

static void recorrerCuerpoObjeto2(A_NODO* nodoEspNombres,A_NODOCUERPOOBJETOLISTA *nodo){
	while(nodo!=NULL){
		if (nodo->h1->tipo==A_OBJETO)
			anotarObjeto2((A_NODOOBJETO*)(nodo->h1));
		nodo=(A_NODOCUERPOOBJETOLISTA*)(nodo->h2);
	}
}

/***********************************************************************/
/* Analisis semántico (Segundo recorrido)                              */
/***********************************************************************/

	/**********************/
	/* Variables globales */
	/**********************/

static int contadorBucle;	/* Nivel de anidamiento en bucles */	
static A_NODO *acceso;		/* Analisis de idAtomico/AccesoMiembros/Asignacion*/
static void *nodoEspNombres;	/* Espacio de nombres en el que se declara el nodo actual */
static A_NODOOBJETO *nodoObjeto;/* Objeto actual (NULL si programa) */
static A_NODOMIEMBRO *nodoMiembro;/* Miembro actual */

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

static void visitar(A_NODO *nodo);
	

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

	/************************************************************/
	/* Programa                                                 */
	/************************************************************/
	/* Actualiza: variable "nodoObjeto"			    */
	/* Actualiza: variable "nodoEspNombres"			    */
	/************************************************************/

static void visitarPrograma(A_NODOPROGRAMA *nodo){
	nodoEspNombres=nodo;
	nodoObjeto=NULL;
	if (nodo->h1!=NULL) {nodoref=&nodo->h1;visitar(nodo->h1);}
	
	if (t_buscar(nodo->tds,t_simbolo("main"),0)==NULL)
		e_error(&nodo->posicion,"No se ha declarado un miembro 'main' en el cuerpo del programa");
}

	/************************************************************/
	/* Objeto                                                   */
	/************************************************************/
	/* Comprueba: objetos anonimos declarados ocultos/abstractos*/
	/* Comprueba: objetos anonimos declarados nativos           */
	/* Comprueba: validez de objetos nativos                    */
	/* Comprueba: redefinición de miembros solapados            */
	/* Comprueba: redefinición de miembros compartidos          */
	/* Comprueba: redefinición de miembros variables            */
	/************************************************************/
	/* Actualiza: Variable "nodoEspNombres"                     */
	/* Actualiza: Variable "nodoObjeto"                         */
	/************************************************************/

static char auxcomp;	/* El miembro se declara variable en alguno de los padres */
static char auxvar;	/* El miembro se declara compartido en alguno de los padres */
static char *auxsim;	/* simbolo del miembro */
static unsigned auxpar; /* parametros del miembro */

static void visitarObjetoAux2(char *simbolo,unsigned char parametros,void *datos){
A_NODOOBJETO *o;
A_NODOMIEMBRO *m;

	o=(A_NODOOBJETO*)datos;	
	m=t_buscar(o->tds,auxsim,auxpar);
	if (m==NULL) t_buscar(o->heredados,auxsim,auxpar);
	if (m!=NULL){
		if ((m->mod&A_MODSHARED)!=0) auxcomp=1;
		if ((m->mod&A_MODVAR)!=0) auxvar=1;
	}
	
}	

static void visitarObjetoAuxDeclarados(char *simbolo,unsigned char parametros,void *datos){
A_NODO *n;		
A_NODOOBJETO *o;
A_NODOMIEMBRO *m;

	auxcomp=0;
	auxvar=0;
	auxsim=simbolo;
	auxpar=parametros;
	t_forAll(nodoObjeto->padres,&visitarObjetoAux2);
	
	if (auxcomp || auxvar){
		if (((A_NODO*)datos)->tipo==A_OBJETO){
			o=(A_NODOOBJETO*)datos;
			m=NULL;
		}
		else {
			o=NULL;
			m=(A_NODOMIEMBRO*)datos;
		}
		if (auxvar && (m==NULL || (m->mod&A_MODVAR)==0)){
			e_error(m==NULL?&o->posicion:&m->posicion,"'%s' ha sido declarado variable por algun padre",simbolo);
		}
		if (auxcomp && (m==NULL || (m->mod&A_MODSHARED)==0)){
			e_error(m==NULL?&o->posicion:&m->posicion,"'%s' ha sido declarado compartido por algun padre",simbolo);			
		}
	}
}
	
static void visitarObjetoAuxSolapados(char *simbolo,unsigned char parametros,void *datos){
A_NODO *n;		
	auxcomp=0;
	auxvar=0;
	auxsim=simbolo;
	auxpar=parametros;
	t_forAll(nodoObjeto->padres,&visitarObjetoAux2);
	
	if (auxcomp){
		e_error(&nodoObjeto->posicion,"No se permite herencia multiple sobre miembros compartidos: '%s'",simbolo);
	}
	else{
		n=t_buscar(nodoObjeto->tds,simbolo,parametros);
		if (n==NULL)
			e_error(&nodoObjeto->posicion,"Es necesario redefinir el miembro '%s' (declarado en mas de un padre)",simbolo);
	}
}

static void visitarObjeto(A_NODOOBJETO *nodo){

	if (nodo->mod&A_MODABSTRACT && nodo->id==NULL)
		e_error(&((A_NODOMODOBJETOLISTA*)(nodo->h1))->posicion,"Los objetos anonimos no pueden ser declarados abstractos");
		
	if (nodo->mod&A_MODHIDDEN && nodo->id==NULL)
			e_error(&((A_NODOMODOBJETOLISTA*)(nodo->h1))->posicion,"Los objetos anonimos no pueden ser declarados ocultos");	
	
	if (nodo->mod&A_MODNATIVE){
		if (nodo->id==NULL)
			e_error(&((A_NODOMODOBJETOLISTA*)(nodo->h1))->posicion,"Los objetos anonimos no pueden ser declarados nativos");
		else if (!esNativo(nodo->id,NULL,0)){
			e_error(&((A_NODOMODOBJETOLISTA*)(nodo->h1))->posicion,"'%s' no es un objeto nativo valido",nodo->id);
		}
	}
	
	nodoObjeto=nodo;
	t_forAll(nodo->solapados,&visitarObjetoAuxSolapados);
	t_forAll(nodo->tds,&visitarObjetoAuxDeclarados);
	nodoEspNombres=nodo;
	if (nodo->h4!=NULL) visitar(nodo->h4); /* cuerpo */
}

	/************************************************************/
	/* Miembro                                                  */
	/************************************************************/
	/* Comprueba: miembros abstractos en objetos no abstractos  */
	/* Comprueba: miembros nativos en objetos no nativos        */
	/* Comprueba: validez de miembros nativos                   */
	/************************************************************/
	/* Actualiza: variable "nodoEspNombres"                     */
	/* Actualiza: variable "nodoMiembro"                        */
	/************************************************************/

static void visitarMiembro(A_NODOMIEMBRO *nodo){

	if (nodo->mod&A_MODABSTRACT){
		if (!(nodo->nodoEspNombres->tipo==A_OBJETO 
			&& ((((A_NODOOBJETO*)(nodo->nodoEspNombres))->mod)&A_MODABSTRACT)!=0
			))
			e_error(&nodo->posicion,"No se puede declarar un miembro abstracto en un objeto no abstracto");
	}
	if (nodo->mod&A_MODNATIVE){
		if (!(nodo->nodoEspNombres->tipo==A_OBJETO 
			&& ((((A_NODOOBJETO*)(nodo->nodoEspNombres))->mod)&A_MODNATIVE)!=0
			))
			e_error(&nodo->posicion,"No se puede declarar un miembro nativo en un objeto no nativo");
		else if (!esNativo(((A_NODOOBJETO*)(nodo->nodoEspNombres))->id,nodo->id,nodo->parametros))
			e_error(&nodo->posicion,"'%s.%s' no es un miembro nativo valido",((A_NODOOBJETO*)(nodo->nodoEspNombres))->id,nodo->id);
	}
		
	contadorBucle=0;
	nodoEspNombres=nodo;
	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);}
}

	/************************************************************/
	/* Let                                                      */
	/************************************************************/
	/* Actualiza: Variable "nodoEspNombres"                     */
	/************************************************************/

static void visitarLet(A_NODOLET *nodo){
	nodo->nodoEspNombres=nodoEspNombres;
	nodo->tds=t_crear();
	
	nodoEspNombres=nodo;
	if (nodo->h1!=NULL) {nodoref=&nodo->h1;visitar(nodo->h1);}
	nodoEspNombres=nodo;
	if (nodo->h2!=NULL) {nodoref=&nodo->h2;visitar(nodo->h2);}
}

	/************************************************************/
	/* DeclaracionLista                                         */
	/************************************************************/
	/* Anota: Tabla de simbolos de declaraciones de variables   */
	/************************************************************/
	/* Comprueba: Declaraciones repetidas                       */
	/************************************************************/

static void visitarDeclaracionLista(A_NODODECLARACIONLISTA *nodo){
A_NODOLET *p;
char *id;

	p=(A_NODOLET *)nodoEspNombres;		
	id=((A_NODOID*)(nodo->h1))->id;
	
	if (t_buscar(p->tds,id,0)!=NULL)
			e_error(&((A_NODOID *)(nodo->h1))->posicion,"Ya se ha declarado una variable local '%s'",id);
		else
			p->tds=t_insertar(p->tds,id,0,p);

	
	if (nodo->h2!=NULL) {nodoEspNombres=p->nodoEspNombres;nodoref=&nodo->h2;visitar(nodo->h2);}
	if (nodo->h3!=NULL) {nodoEspNombres=p;nodoref=&nodo->h3;visitar(nodo->h3);}
}

	/************************************************************/
	/* Bloque                                                   */
	/************************************************************/
	/* Actualiza: Emparejamiento IF-Else                        */
	/************************************************************/
	
static void visitarBloque(A_NODOBLOQUE *nodo){
A_NODOIF *a,*aa,*b;
A_NODOBLOQUE *x;

	if (nodo->h1->tipo==A_IF){
		a=(A_NODOIF *)nodo->h1;			/* a=primer if */
		
		for(aa=a;aa!=NULL;)			/* aa=primer if sin else */
			if (aa->h3==NULL) break;
			else if (aa->h3->tipo!=A_IF) {aa=NULL;break;}
			else aa=(A_NODOIF*)(aa->h3);
		
		if (aa!=NULL){				/* si hay if sin else... */
		
			if (nodo->h2->tipo==A_IF){ 	/* caso: en fin de bloque */
				b=(A_NODOIF *)(nodo->h2);
				if (b->h1==NULL){	/* else sin if */
				
					aa->h3=b->h3;	/* emparejar */
					
					*nodoref=(A_NODO*)a;/* cambiar enlace a primer if */
					a_free(nodo);	/* borrar nodo secuencia */
					a_free(b);	/* borrar else */
				
					visitar(*nodoref);
					return;
				}
			}
			
			else if (nodo->h2->tipo==A_BLOQUE){	/* caso: en medio de un bloque */
				
				x=(A_NODOBLOQUE *)(nodo->h2);
				if (x->h1->tipo==A_IF){
					b=(A_NODOIF *)(x->h1);
					if (b->h1==NULL){	/* else sin if */
					
						aa->h3=b->h3;	/* emparejar */
						
						nodo->h2=x->h2; /* cambiar enlace por el siguiente */
								/* en la secuencia */
						a_free(x);	/* borrar siguiente elemento de */
								/* la secuencia (que contiene */
								/* el else)*/
						a_free(b);	/* borrar else */
						
								/* visitar el nodo actualizado */
						visitar(*nodoref);			
						return;
					}
				}
			}
		}
	}
	nodoref=&nodo->h1;visitar(nodo->h1);
	nodoref=&nodo->h2;visitar(nodo->h2);
}
	
	/************************************************************/
	/* If                                                       */
	/************************************************************/
	/* Comprueba: No hay ELSE sin IF                            */
	/************************************************************/

static void visitarIf(A_NODOIF *nodo){
	if (nodo->h1==NULL){
		e_error(&nodo->posicion,"ELSE no asociado a ningun IF");
	}
	else{
		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);}
	}
	
}

	/************************************************************/
	/* DoWhile                                                  */
	/************************************************************/
	/* Actualiza: Contador de anidamiento en bucles             */
	/************************************************************/

static void visitarDoWhile(A_NODOWHILE *nodo){	
	contadorBucle++;
	if (nodo->h1!=NULL) {nodoref=&nodo->h1;visitar(nodo->h1);}
	contadorBucle--;
	if (nodo->h2!=NULL) {nodoref=&nodo->h2;visitar(nodo->h2);}
}
	
	/************************************************************/
	/* While                                                    */
	/************************************************************/
	/* Actualiza: Contador de anidamiento en bucles             */
	/************************************************************/
	
static void visitarWhile(A_NODOWHILE *nodo){	
	if (nodo->h1!=NULL) {nodoref=&nodo->h1;visitar(nodo->h1);}
	contadorBucle++;
	if (nodo->h2!=NULL) {nodoref=&nodo->h2;visitar(nodo->h2);}
	contadorBucle--;
}

	/************************************************************/
	/* For                                                      */
	/************************************************************/
	/* Actualiza: Contador de anidamiento en bucles             */
	/************************************************************/

static void visitarFor(A_NODOFOR *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);}
	contadorBucle++;
	if (nodo->h4!=NULL) {nodoref=&nodo->h4;visitar(nodo->h4);}
	contadorBucle--;
}

	/************************************************************/
	/* Break                                                    */
	/************************************************************/
	/* Comprueba: Contención dentro del cuerpo de un bucle      */
	/************************************************************/

static void visitarBreak(A_NODOBREAK *nodo){
	if (contadorBucle<=0)
		e_error(&nodo->posicion,"El operador 'break' debe estar contenido en el cuerpo de un bucle");
}
	
	/************************************************************/
	/* Continue                                                 */
	/************************************************************/
	/* Comprueba: Contención dentro del cuerpo de un bucle      */
	/************************************************************/

static void visitarContinue(A_NODOCONTINUE *nodo){
	if (contadorBucle<=0)
		e_error(&nodo->posicion,"El operador 'continue' debe estar contenido en el cuerpo de un bucle");
}

	/************************************************************/
	/* Is                                                       */
	/************************************************************/
	/* Comprueba: Aplicación a un objeto válido                 */
	/************************************************************/

static void visitarIs(A_NODOIS *nodo){
char *id;
A_NODO *n;

	id=((A_NODOID*)(nodo->h2))->id;
	n=buscarId(nodoEspNombres,id,0);
	if (n==NULL || n->tipo!=A_OBJETO) 
		e_error(&nodo->posicion,"'%s' no es un objeto valido",id);
	if (nodo->h1!=NULL) {nodoref=&nodo->h1;visitar(nodo->h1);}
}
	
	/************************************************************/
	/* IsNot                                                    */
	/************************************************************/
	/* Comprueba: Aplicación a un objeto válido                 */
	/************************************************************/

static void visitarIsNot(A_NODOISNOT *nodo){
char *id;
A_NODO *n;

	id=((A_NODOID*)(nodo->h2))->id;
	n=buscarId(nodoEspNombres,id,0);
	if (n==NULL || n->tipo!=A_OBJETO) 
		e_error(&nodo->posicion,"'%s' no es un objeto valido",id);
	if (nodo->h1!=NULL) {nodoref=&nodo->h1;visitar(nodo->h1);}
}

	/************************************************************/
	/* IdAtomico                                                */
	/************************************************************/
	/* Comprueba: Resolucion en un objeto, miembro o variable   */
	/* Actualiza: Variable "acceso"                             */
	/************************************************************/
	
static void visitarIdAtomico(A_NODOIDATOMICO *nodo){
char *id;
unsigned parametros;
A_NODO *n;

	id=((A_NODOID*)(nodo->h1))->id;
	parametros=contarParametros((A_NODOPARAMETROSLISTA *)(nodo->h2));
	n=buscarId(nodoEspNombres,id,parametros);
	
	if (n==NULL)
		if (parametros==0) e_error(&nodo->posicion,"'%s' no es un objeto, miembro o variable valido",id);
		else e_error(&nodo->posicion,"'%s' no es un miembro valido o no acepta ese numero de parámetros",id);
		
	if (nodo->h2!=NULL) {nodoref=&nodo->h2;visitar(nodo->h2);}

	acceso=n;
}

	/************************************************************/
	/* AccesoMiembro                                            */
	/************************************************************/
	/* Comprueba: Expresion corresponde a objeto conocido       */
	/* Utiliza: Variable "acceso"				    */
	/* Actualiza: Variable "acceso"                             */
	/************************************************************/

static void visitarAccesoMiembro(A_NODOACCESOMIEMBRO *nodo){
A_NODO *n;
char *id;
unsigned char parametros;
				
		/* visitar expresion */

	acceso=NULL;
	if (nodo->h1!=NULL) {nodoref=&nodo->h1;visitar(nodo->h1);}

		/* Analisis semantico */
		
	n=NULL;
	if (acceso!=NULL && acceso->tipo==A_OBJETO){
		id=((A_NODOID*)(nodo->h2))->id;
		parametros=contarParametros((A_NODOPARAMETROSLISTA *)(nodo->h3));
		n=buscarId(acceso,id,parametros);
		
		if (n==NULL){
			if (parametros==0) e_error(&nodo->posicion,"'%s' no es un miembro valido",id);
			else e_error(&nodo->posicion,"'%s' no es un miembro valido o no acepta ese numero de parámetros",id);		
		}
		else if ((n->tipo==A_OBJETO && (((A_NODOOBJETO *)n)->mod&A_MODABSTRACT)!=0) ||
			 (n->tipo==A_MIEMBRO && (((A_NODOMIEMBRO *)n)->mod&A_MODABSTRACT)!=0)){
			e_error(&nodo->posicion,"'%s' ha sido declarado abstracto y no es directamente accesible",id);
			n=NULL;
		}
	}
		
		/* Visitar parámetros */
	
	if (nodo->h3!=NULL) {nodoref=&nodo->h3;visitar(nodo->h3);}
	
	acceso=n;
}

	/************************************************************/
	/* Asignacion                                               */
	/************************************************************/
	/* Comprueba: Asignacion valida                             */
	/* Utiliza: Variable "acceso"                               */
	/* Actualiza: Variable "acceso"                             */
	/************************************************************/

static void visitarAsignacion(A_NODOASIGNACION *nodo){
	acceso=NULL;	
	if (nodo->h1!=NULL) {nodoref=&nodo->h1;visitar(nodo->h1);}
	
	switch(nodo->h1->tipo){
		case A_IDATOMICO:
		case A_ACCESOMIEMBRO:
			if (acceso!=NULL) switch(acceso->tipo){
				case A_LET:
					break;
				case A_MIEMBRO:
					if ((((A_NODOMIEMBRO*)acceso)->mod&A_MODVAR)==0)
						e_error(&nodo->posicion,"'%s' no es un miembro variable",((A_NODOMIEMBRO*)acceso)->id);
					break;
				case A_OBJETO:
					e_error(&nodo->posicion,"'%s' no es un miembro variable",((A_NODOOBJETO*)acceso)->id);
					break;
			}
			break;
		case A_ACCESOARRAY:
			break;
		default:
			e_error(&nodo->posicion,"Utilizacion invalida del operador de asignacion");
	}
	
	acceso=NULL;
	
	if (nodo->h2!=NULL) {nodoref=&nodo->h1;visitar(nodo->h2);}
}

	/************************************************************/
	/* Resend                                                   */
	/************************************************************/
	/* Comprueba: Acceso a padre no declarado                   */
	/* Comprueba: Acceso a miembro presente en mas de un padre  */
	/*            sin indicar el padre                          */
	/* Comprueba: Acceso a miembro no presente en ninguno de los*/
	/*            padres                                        */
	/* Comprueba: Acceso a miembro no presente en el padre al   */
	/*            que se dirige el acceso                       */
	/************************************************************/

static void visitarResend(A_NODORESEND *nodo){
char *id,*idPadre;
unsigned char parametros;
A_NODOOBJETO *o;
A_NODOMIEMBRO *m;
	
	id=obtenerId((A_NODOID*)(nodo->h1));
	idPadre=obtenerId((A_NODOID*)(nodo->h2));
	parametros=contarParametros((A_NODOPARAMETROSLISTA*)(nodo->h3));
	if (id==NULL) id=nodoMiembro->id;

	if (nodoObjeto==NULL)
		e_error(&nodo->posicion,"No hay ningun padre al que reenviar");		
	else if (idPadre!=NULL){
		o=t_buscar(nodoObjeto->padres,idPadre,0);
		if (o==NULL)
			e_error(&nodo->posicion,"'%s' no es un padre valido",idPadre);
		else{
			m=t_buscar(o->tds,id,parametros);
			if (m==NULL) m=t_buscar(o->heredados,id,parametros);
			if (m==NULL)
				e_error(&nodo->posicion,"'%s.%s' no es un miembro valido",idPadre,id);
		}
	}
	else{
		if (t_buscar(nodoObjeto->solapados,id,parametros))
			e_error(&nodo->posicion,"Debe indicarse el padre concreto para '%s'",id);
		else if (t_buscar(nodoObjeto->heredados,id,parametros)==NULL)
			e_error(&nodo->posicion,"'%s' no es un miembro heredado valido",id);		
	}
	
	if (nodo->h3!=NULL) {nodoref=&nodo->h1;visitar(nodo->h3);}
}
	
	/************************************************************/
	/* LiteralSecuencia                                         */
	/************************************************************/
	/* Comprueba: Se ha declarado el tipo nativo 'array'        */
	/************************************************************/

static void visitarLiteralSecuencia(A_NODOLITERALSECUENCIA *nodo){
A_NODO *t;	
	t=buscarId(nodoEspNombres,t_simbolo("array"),0);
	if (t==NULL || t->tipo!=A_OBJETO || (((A_NODOOBJETO*)t)->mod&A_MODNATIVE)==0)
		e_error(&nodo->posicion,"El tipo de dato nativo 'array' no ha sido declarado\no está oculto por otra declaración");

	if (nodo->h1!=NULL) {nodoref=&nodo->h1;visitar(nodo->h1);}
}
	
	/************************************************************/
	/* LiteralCadena                                            */
	/************************************************************/
	/* Comprueba: Se ha declarado el tipo nativo 'string'       */
	/************************************************************/

static void visitarLiteralCadena(A_NODOLITERALCADENA *nodo){
A_NODO *t;	
	t=buscarId(nodoEspNombres,t_simbolo("string"),0);
	if (t==NULL || t->tipo!=A_OBJETO || (((A_NODOOBJETO*)t)->mod&A_MODNATIVE)==0)
		e_error(&nodo->posicion,"El tipo de dato nativo 'string' no ha sido declarado\no está oculto por otra declaración");
}
	
	/************************************************************/
	/* LiteralEntero                                            */
	/************************************************************/
	/* Comprueba: Se ha declarado el tipo nativo 'integer'      */
	/************************************************************/
	
static void visitarLiteralEntero(A_NODOLITERALENTERO *nodo){
A_NODO *t;	
	t=buscarId(nodoEspNombres,t_simbolo("integer"),0);
	if (t==NULL || t->tipo!=A_OBJETO || (((A_NODOOBJETO*)t)->mod&A_MODNATIVE)==0)
		e_error(&nodo->posicion,"El tipo de dato nativo 'integer' no ha sido declarado\no está oculto por otra declaración");
}
	
	/************************************************************/
	/* LiteralReal                                              */
	/************************************************************/
	/* Comprueba: Se ha declarado el tipo nativo 'float'        */
	/************************************************************/
	
static void visitarLiteralReal(A_NODOLITERALREAL *nodo){
A_NODO *t;	
	t=buscarId(nodoEspNombres,t_simbolo("float"),0);
	if (t==NULL || t->tipo!=A_OBJETO || (((A_NODOOBJETO*)t)->mod&A_MODNATIVE)==0)
		e_error(&nodo->posicion,"El tipo de dato nativo 'float' no ha sido declarado\no está oculto por otra declaración");
}
	/************************************************************/
	/* LiteralTrue                                              */
	/************************************************************/
	/* Comprueba: Se ha declarado el tipo nativo 'boolean'      */
	/************************************************************/
	
static void visitarLiteralTrue(A_NODOLITERALTRUE *nodo){
A_NODO *t;	
	t=buscarId(nodoEspNombres,t_simbolo("boolean"),0);
	if (t==NULL || t->tipo!=A_OBJETO || (((A_NODOOBJETO*)t)->mod&A_MODNATIVE)==0)
		e_error(&nodo->posicion,"El tipo de dato nativo 'boolean' no ha sido declarado\no está oculto por otra declaración");
}
	
	/************************************************************/
	/* LiteralFalse                                             */
	/************************************************************/
	/* Comprueba: Se ha declarado el tipo nativo 'boolean'      */
	/************************************************************/
	
static void visitarLiteralFalse(A_NODOLITERALFALSE *nodo){
A_NODO *t;	
	t=buscarId(nodoEspNombres,t_simbolo("boolean"),0);
	if (t==NULL || t->tipo!=A_OBJETO || (((A_NODOOBJETO*)t)->mod&A_MODNATIVE)==0)
		e_error(&nodo->posicion,"El tipo de dato nativo 'boolean' no ha sido declarado\no está oculto por otra declaración");
}
		
	/************************************************************/
	/* Nodos sin accion semantica concreta                      */
	/************************************************************/

static void visitarNodo0(A_NODO0 *nodo){
}
static void visitarNodo1(A_NODO1 *nodo){
	if (nodo->h1!=NULL) {nodoref=&nodo->h1;visitar(nodo->h1);}
}
static void 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 void 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);}
}

/***********************************************************************/
/* Patrón Visitante                             	 	       */
/***********************************************************************/

static void *tablaVisitante[A_TIPO_SIZE];

static void visitarError(A_NODO *nodo){
	e_fatal(NULL,"Error interno: Patron visitante (Analisis Semantico)");
}	

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_ERROR]=&visitarNodo0;
	tablaVisitante[A_ABSTRACT]=&visitarNodo0;
	tablaVisitante[A_SHARED]=&visitarNodo0;
	tablaVisitante[A_NATIVE]=&visitarNodo0;	
	tablaVisitante[A_VAR]=&visitarNodo0;
	tablaVisitante[A_BREAK]=&visitarBreak;
	tablaVisitante[A_CONTINUE]=&visitarContinue;
	tablaVisitante[A_HIDDEN]=&visitarNodo0;
	tablaVisitante[A_RETURN]=&visitarNodo1;
	tablaVisitante[A_STATIC]=&visitarNodo1;
	tablaVisitante[A_MAS]=&visitarNodo1;
	tablaVisitante[A_MENOS]=&visitarNodo1;
	tablaVisitante[A_NOT]=&visitarNodo1;
	tablaVisitante[A_OR]=&visitarNodo2;
	tablaVisitante[A_AND]=&visitarNodo2;
	tablaVisitante[A_IGUAL]=&visitarNodo2;
	tablaVisitante[A_NOIGUAL]=&visitarNodo2;
	tablaVisitante[A_IDENTICO]=&visitarNodo2;
	tablaVisitante[A_NOIDENTICO]=&visitarNodo2;
	tablaVisitante[A_MAYOR]=&visitarNodo2;
	tablaVisitante[A_MENOR]=&visitarNodo2;
	tablaVisitante[A_MAYORIGUAL]=&visitarNodo2;
	tablaVisitante[A_MENORIGUAL]=&visitarNodo2;
	tablaVisitante[A_UNION]=&visitarNodo2;
	tablaVisitante[A_CONCATENACION]=&visitarNodo2;
	tablaVisitante[A_SUMA]=&visitarNodo2;
	tablaVisitante[A_RESTA]=&visitarNodo2;
	tablaVisitante[A_MULTIPLICACION]=&visitarNodo2;
	tablaVisitante[A_DIVISION]=&visitarNodo2;
	tablaVisitante[A_MODULO]=&visitarNodo2;
	tablaVisitante[A_IS]=&visitarIs;
	tablaVisitante[A_ISNOT]=&visitarIsNot;
	tablaVisitante[A_HAS]=&visitarNodo2;
	tablaVisitante[A_HASNOT]=&visitarNodo2;
	tablaVisitante[A_ACCESOARRAY]=&visitarNodo2;
	tablaVisitante[A_IDATOMICO]=&visitarIdAtomico;
	tablaVisitante[A_LITERALSECUENCIALISTA]=&visitarNodo2;
	tablaVisitante[A_MODOBJETOLISTA]=&visitarNodo2;
	tablaVisitante[A_IDLISTA]=&visitarNodo2;
	tablaVisitante[A_CUERPOOBJETOLISTA]=&visitarNodo2;
	tablaVisitante[A_DOWHILE]=&visitarDoWhile;
	tablaVisitante[A_MODMIEMBROLISTA]=&visitarNodo2;
	tablaVisitante[A_WHILE]=&visitarWhile;
	tablaVisitante[A_ARGUMENTOSLISTA]=&visitarNodo2;
	tablaVisitante[A_ASIGNACION]=&visitarAsignacion;
	tablaVisitante[A_PARAMETROSLISTA]=&visitarNodo2;
	tablaVisitante[A_SECUENCIA]=&visitarNodo2;
	tablaVisitante[A_BLOQUE]=&visitarBloque;
	tablaVisitante[A_RESEND]=&visitarResend;
	tablaVisitante[A_DECLARACIONLISTA]=&visitarDeclaracionLista;
	tablaVisitante[A_IF]=&visitarIf;
	tablaVisitante[A_ACCESOMIEMBRO]=&visitarAccesoMiembro;
	tablaVisitante[A_MIEMBROEXTERNO]=&visitarNodo3;
	tablaVisitante[A_FOR]=&visitarFor;	
	tablaVisitante[A_LITERALSECUENCIA]=&visitarLiteralSecuencia;
	tablaVisitante[A_LITERALCADENA]=&visitarLiteralCadena;
	tablaVisitante[A_LITERALENTERO]=&visitarLiteralEntero;
	tablaVisitante[A_LITERALREAL]=&visitarLiteralReal;
	tablaVisitante[A_LITERALTRUE]=&visitarLiteralTrue;
	tablaVisitante[A_LITERALFALSE]=&visitarLiteralFalse;
	tablaVisitante[A_ID]=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_sem(A_NODO *ast){
	inicializarNativos();
	inicializarTablaVisitante();
	anotarRamasSuperiores(ast);
	nodoref=&ast;visitar(ast);			
	return ast;	
}
