//*******************************************************************************
//tradJava.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 <string.h>
#include <stdarg.h>
#include "ast.h"
#include "tradJava.h"
#include "tds.h"

#define true  1
#define false 0

#define TEMPVAR		"temp"	/* Nombre de variable temporal basica */
#define TEMPVAR_PREFIJO "temp"	/* Prefijo para variables temporales de almacenamiento */
				/* intermedio */
				
/****************************************************************/
/* Debug                                                        */
/****************************************************************/

#if 0
#define fprintf zprintf
int zprintf(FILE *fp,const char *format,...){
va_list ap;
	va_start(ap,format);
	vfprintf(fp,format,ap);fflush(fp);
	va_end(ap);	
}
#endif

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

	/* Patron visitante */

static void *nodoEspNombres;	/* Ultimo espacio de nombres atravesado en la visita */
static A_NODOOBJETO *nodoObjeto;/* Ultimo objeto atravesado en la visita */
static A_NODOMIEMBRO *nodoMiembro;/* Ultimo miembro atravesado en la visita */

	/* Traduccion (general) */

static FILE *fp;		/* Fichero de salida */	
static int fptab;		/* Nivel de tabulación */

	/* Traduccion de expresiones */
	
static char expSimple;		/* Indica si la expresión puede traducirse directamente a */
				/* una expresión Java o si por el contrario es necesario */
				/* utilizar una o más sentencias en su traducción */

static int nTempVars; 		/* Numero de variables temporales de almacenamiento intermedio */

static char *expTexto;		/* Cadena de caracteres con la traducción de la expresión */
				/* visitada */

	/* Traduccion de expresiones compuestas de un conjunto arbitrario de subexpresiones */
		
typedef struct SUBEXP{			
	char expSimple;
	char *expTexto;
	struct SUBEXP *sig;
} SUBEXP;	

static SUBEXP *subExp;
			
/***********************************************************************/
/* Concatenación de cadenas                                            */
/***********************************************************************/

static char *charPuntero(void *a){
char temp[20];
	sprintf(temp,"%p",a);
	return strdup(temp);
}

static char *charEntero(int a){
char temp[20];
	sprintf(temp,"%d",a);
	return strdup(temp);
}

static char *charDouble(double a){
char temp[20];
	sprintf(temp,"%f",a);
	return strdup(temp);
}

static char *concatAux(char *a,char *b){
char *s;
	s=(char *)malloc((a==NULL?0:strlen(a))+(b==NULL?0:strlen(b))+1);
	if (s==NULL) 
		e_fatal(NULL,"Sin memoria durante la traduccion");
	s[0]=0;
	if (a!=NULL) strcat(s,a);
	if (b!=NULL) strcat(s,b);
	return s;
}

static char *concat(char *s,...){
char *p;
va_list ap;
	
	va_start(ap,s);
	
	p=va_arg(ap,char*);
	while(p!=NULL){
		s=concatAux(s,p);
		p=va_arg(ap,char*);
	}
	va_end(ap);
	return s;
}

/********************************************************/
/* Salida a fichero                                     */
/********************************************************/

#define IMPRIMIR_TAB "    " 	/*tabulador*/

	/* Tabulacion */
	
static void nuevaLinea(void){
int i;	
	for (i=0;i<fptab;i++) fprintf(fp,IMPRIMIR_TAB);
}	

	/* Imprimir cadena de caracteres */

static void imprimir(char *cadena){	
int i;
	for(i=0;cadena[i]!=0;i++)
		if (cadena[i]=='\t') fprintf(fp,IMPRIMIR_TAB);
		else fprintf(fp,"%c",cadena[i]);
}

static void imprimir2(char *cadena){	
int i;
	for(i=0;cadena[i]!=0;i++)
		if (cadena[i]=='\t') fprintf(fp,IMPRIMIR_TAB);
		else if (cadena[i]=='\n') {fprintf(fp,"\n");nuevaLinea();}
		else fprintf(fp,"%c",cadena[i]);
}
	/* Imprimir entero */

static void imprimirEntero(int i){
	fprintf(fp,"%d",i);
}
	
	/* Imprimir puntero */

static void imprimirPuntero(void *p){
	fprintf(fp,"%p",p);
}

	/* Imprimir tablas de simbolos */

static char primero;
static char *add[2];
static void visitarObjetoAux(char *simbolo,unsigned char parametros,void *datos){
A_NODOOBJETO *n;

	if (primero==true) primero=false;
	else imprimir(",");
	imprimir(add[0]);
	imprimir(simbolo);imprimir("_");
	if (parametros) {imprimirEntero(parametros);imprimir("_");}
	imprimir(add[1]);
}
static void imprimirTdsArray(void *tabla,char *prefijo,char *sufijo){
	primero=true;
	add[0]=prefijo;add[1]=sufijo;
	t_forAll(tabla,&visitarObjetoAux);
}

/********************************************/
/* Objetos y métodos nativos                */
/********************************************/

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

static struct{
	char *objeto;			/* Nombre del objeto 		*/
	char *miembro;			/* Nombre del miembro 		*/
	unsigned char parametros;	/* Parametros del miembro 	*/
	char *codigo;			/* Codigo nativo		*/
					/* (secuencia de escape @<n> = parametro numero n) */
} 
_nativos[]=
{
	{"object",NULL,0,
		""},
	{NULL,"self",0,
		"return this;"},
	{NULL,"copy",0,
		"System.out.println(\":: El metodo 'object.copy' no ha sido implementado\");\n"
		"System.exit(1);\n"
		"return null_object_;"},
	{NULL,"deepcopy",0,
		"System.out.println(\":: El metodo 'object.deepCopy' no ha sido implementado\");\n"
		"System.exit(1);\n"
		"return null_object_;"},
	{NULL,"lexicalparent",0,
		"return lexicalparent__();"},
	{NULL,"lexicalsons",0,
		"return new array_class_(lexicalsons__());"},
	{NULL,"operatorequal",1,
		"return new boolean_class_(this==@1);"},
	{NULL,"operatornotequal",1,
		"return new boolean_class_(this!=@1);"},
	{NULL,"tostring",0,
		"return new string_class_(id__());"},
	{"null",NULL,0,
		""},
	{NULL,"operatorequal",1,
		"return new boolean_class_(@1 instanceof null_class_);"},
	{NULL,"operatornotequal",1,
		"return new boolean_class_(!(@1 instanceof null_class_));"},
	{NULL,"tostring",0,
		"return new string_class_(\"null\");"},
	{"boolean",NULL,0,
		"public boolean value__;\n"
		"public boolean_class_(){value__=false;}\n"
		"public boolean_class_(boolean x){value__=x;}\n"
		},
	{NULL,"operatorequal",1,
		"return new boolean_class_((@1 instanceof boolean_class_) && (((boolean_class_)@1).value__==value__));"},
	{NULL,"operatornotequal",1,
		"return new boolean_class_(!(@1 instanceof boolean_class_) || (((boolean_class_)@1).value__!=value__));"},
	{NULL,"tostring",0,
		"return new string_class_(this.value__==true?\"true\":\"false\");"
		},
	{"integer",NULL,0,
		"public int value__;\n"
		"public integer_class_(){value__=0;}\n"
		"public integer_class_(int x){value__=x;}\n"
		"\n"
		},
	{NULL,"operatorplus",0,
		"return new integer_class_(value__);"},
	{NULL,"operatorminus",0,
		"return new integer_class_(-value__);"},
	{NULL,"operatorplus",1,
		"if (@1 instanceof integer_class_) return new integer_class_(value__+((integer_class_)@1).value__);\n"
		"else if (@1 instanceof float_class_) return new float_class_(value__+((float_class_)@1).value__);\n"
		"else {System.err.println(\":: 'integer.operatorPlus' solo puede aplicarse a operandos de tipo 'native integer' o 'native float'\");return null;}\n"
		},
	{NULL,"operatorminus",1,
		"if (@1 instanceof integer_class_) return new integer_class_(value__-((integer_class_)@1).value__);\n"
		"else if (@1 instanceof float_class_) return new float_class_(value__-((float_class_)@1).value__);\n"
		"else {System.err.println(\":: 'integer.operatorMinus' solo puede aplicarse a operandos de tipo 'native integer' o 'native float'\");return null;}\n"
		},
	{NULL,"operatormultiplication",1,
		"if (@1 instanceof integer_class_) return new integer_class_(value__*((integer_class_)@1).value__);\n"
		"else if (@1 instanceof float_class_) return new float_class_(value__*((float_class_)@1).value__);\n"
		"else {System.err.println(\":: 'integer.operatorMultiplication' solo puede aplicarse a operandos de tipo 'native integer' o 'native float'\");return null;}\n"
		},
	{NULL,"operatordivision",1,
		"if (@1 instanceof integer_class_) return new integer_class_(value__/((integer_class_)@1).value__);\n"
		"else if (@1 instanceof float_class_) return new float_class_(value__/((float_class_)@1).value__);\n"
		"else {System.err.println(\":: 'integer.operatorDivision' solo puede aplicarse a operandos de tipo 'native integer' o 'native float'\");return null;}\n"
		},
	{NULL,"operatormodulus",1,
		"if (@1 instanceof integer_class_) return new integer_class_(value__%((integer_class_)@1).value__);\n"
		"else if (@1 instanceof float_class_) return new float_class_(value__%((float_class_)@1).value__);\n"
		"else {System.err.println(\":: 'integer.operatorModulus' solo puede aplicarse a operandos de tipo 'native integer' o 'native float'\");return null;}\n"
		},
	{NULL,"operatorequal",1,
		"if (@1 instanceof integer_class_) return new boolean_class_(value__==((integer_class_)@1).value__);\n"
		"else if (@1 instanceof float_class_) return new boolean_class_(value__==((float_class_)@1).value__);\n"
		"else return new boolean_class_(false);\n"
		},
	{NULL,"operatornotequal",1,
		"if (@1 instanceof integer_class_) return new boolean_class_(value__!=((integer_class_)@1).value__);\n"
		"else if (@1 instanceof float_class_) return new boolean_class_(value__!=((float_class_)@1).value__);\n"
		"else return new boolean_class_(true);\n"
		},
	{NULL,"operatorgreater",1,
		"if (@1 instanceof integer_class_) return new boolean_class_(value__>((integer_class_)@1).value__);\n"
		"else if (@1 instanceof float_class_) return new boolean_class_(value__>((float_class_)@1).value__);\n"
		"else {System.err.println(\":: 'integer.operatorGreater' solo puede aplicarse a operandos de tipo 'native integer' o 'native float'\");return null;}\n"
		},
	{NULL,"operatorless",1,
		"if (@1 instanceof integer_class_) return new boolean_class_(value__<((integer_class_)@1).value__);\n"
		"else if (@1 instanceof float_class_) return new boolean_class_(value__<((float_class_)@1).value__);\n"
		"else {System.err.println(\":: 'integer.operatorLess' solo puede aplicarse a operandos de tipo 'native integer' o 'native float'\");return null;}\n"
		},
	{NULL,"operatorgreaterorequal",1,
		"if (@1 instanceof integer_class_) return new boolean_class_(value__>=((integer_class_)@1).value__);\n"
		"else if (@1 instanceof float_class_) return new boolean_class_(value__>=((float_class_)@1).value__);\n"
		"else {System.err.println(\":: 'integer.operatorGreaterOrEqual' solo puede aplicarse a operandos de tipo 'native integer' o 'native float'\");return null;}\n"
		},
	{NULL,"operatorlessorequal",1,
		"if (@1 instanceof integer_class_) return new boolean_class_(value__<=((integer_class_)@1).value__);\n"
		"else if (@1 instanceof float_class_) return new boolean_class_(value__<=((float_class_)@1).value__);\n"
		"else {System.err.println(\":: 'integer.operatorLessOrEqual' solo puede aplicarse a operandos de tipo 'native integer' o 'native float'\");return null;}\n"
		},
	{NULL,"tofloat",0,
		"return new float_class_((double)value__);"},
	{NULL,"tostring",0,
		"return new string_class_(\"\"+value__);"},	
	{"float",NULL,0,
		"public double value__;\n"
		"public float_class_(){value__=0.0;}\n"
		"public float_class_(double x){value__=x;}\n"
		"\n"
		},
	{NULL,"operatorplus",0,
		"return new float_class_(value__);"},
	{NULL,"operatorminus",0,
		"return new float_class_(-value__);"},
	{NULL,"operatorplus",1,
		"if (@1 instanceof integer_class_) return new float_class_(value__+((integer_class_)@1).value__);\n"
		"else if (@1 instanceof float_class_) return new float_class_(value__+((float_class_)@1).value__);\n"
		"else {System.err.println(\":: 'float.operatorPlus' solo puede aplicarse a operandos de tipo 'native integer' o 'native float'\");return null;}\n"
		},
	{NULL,"operatorminus",1,
		"if (@1 instanceof integer_class_) return new float_class_(value__-((integer_class_)@1).value__);\n"
		"else if (@1 instanceof float_class_) return new float_class_(value__-((float_class_)@1).value__);\n"
		"else {System.err.println(\":: 'float.operatorMinus' solo puede aplicarse a operandos de tipo 'native integer' o 'native float'\");return null;}\n"
		},
	{NULL,"operatormultiplication",1,
		"if (@1 instanceof integer_class_) return new float_class_(value__*((integer_class_)@1).value__);\n"
		"else if (@1 instanceof float_class_) return new float_class_(value__*((float_class_)@1).value__);\n"
		"else {System.err.println(\":: 'float.operatorMultiplication' solo puede aplicarse a operandos de tipo 'native integer' o 'native float'\");return null;}\n"
		},
	{NULL,"operatordivision",1,
		"if (@1 instanceof integer_class_) return new float_class_(value__/((integer_class_)@1).value__);\n"
		"else if (@1 instanceof float_class_) return new float_class_(value__/((float_class_)@1).value__);\n"
		"else {System.err.println(\":: 'float.operatorDivision' solo puede aplicarse a operandos de tipo 'native integer' o 'native float'\");return null;}\n"
		},
	{NULL,"operatormodulus",1,
		"if (@1 instanceof integer_class_) return new float_class_(value__%((integer_class_)@1).value__);\n"
		"else if (@1 instanceof float_class_) return new float_class_(value__%((float_class_)@1).value__);\n"
		"else {System.err.println(\":: 'float.operatorModulus' solo puede aplicarse a operandos de tipo 'native integer' o 'native float'\");return null;}\n"
		},
	{NULL,"operatorequal",1,
		"if (@1 instanceof integer_class_) return new boolean_class_(value__==((integer_class_)@1).value__);\n"
		"else if (@1 instanceof float_class_) return new boolean_class_(value__==((float_class_)@1).value__);\n"
		"else return new boolean_class_(false);\n"
		},
	{NULL,"operatornotequal",1,
		"if (@1 instanceof integer_class_) return new boolean_class_(value__!=((integer_class_)@1).value__);\n"
		"else if (@1 instanceof float_class_) return new boolean_class_(value__!=((float_class_)@1).value__);\n"
		"else return new boolean_class_(true);\n"
		},
	{NULL,"operatorgreater",1,
		"if (@1 instanceof integer_class_) return new boolean_class_(value__>((integer_class_)@1).value__);\n"
		"else if (@1 instanceof float_class_) return new boolean_class_(value__>((float_class_)@1).value__);\n"
		"else {System.err.println(\":: 'float.operatorGreater' solo puede aplicarse a operandos de tipo 'native integer' o 'native float'\");return null;}\n"
		},
	{NULL,"operatorless",1,
		"if (@1 instanceof integer_class_) return new boolean_class_(value__<((integer_class_)@1).value__);\n"
		"else if (@1 instanceof float_class_) return new boolean_class_(value__<((float_class_)@1).value__);\n"
		"else {System.err.println(\":: 'float.operatorLess' solo puede aplicarse a operandos de tipo 'native integer' o 'native float'\");return null;}\n"
		},
	{NULL,"operatorgreaterorequal",1,
		"if (@1 instanceof integer_class_) return new boolean_class_(value__>=((integer_class_)@1).value__);\n"
		"else if (@1 instanceof float_class_) return new boolean_class_(value__>=((float_class_)@1).value__);\n"
		"else {System.err.println(\":: 'float.operatorGreaterOrEqual' solo puede aplicarse a operandos de tipo 'native integer' o 'native float'\");return null;}\n"
		},
	{NULL,"operatorlessorequal",1,
		"if (@1 instanceof integer_class_) return new boolean_class_(value__<=((integer_class_)@1).value__);\n"
		"else if (@1 instanceof float_class_) return new boolean_class_(value__<=((float_class_)@1).value__);\n"
		"else {System.err.println(\":: 'float.operatorLessOrEqual' solo puede aplicarse a operandos de tipo 'native integer' o 'native float'\");return null;}\n"
		},
	{NULL,"tointeger",0,
		"return new integer_class_((int)value__);"},
	{NULL,"tostring",0,
		"return new string_class_(\"\"+value__);"},	
	{"string",NULL,0,
		"public String value__;\n"
		"public string_class_(){value__=\"\";}\n"
		"public string_class_(String x){value__=x;}\n"
		"public String toString(){return value__;}\n"
		},
	{NULL,"length",0,
		"return new integer_class_(value__.length());"},
	{NULL,"operatorequal",1,
		"return new boolean_class_((@1 instanceof string_class_) && (((string_class_)@1).value__.equals(value__)));"},
	{NULL,"operatornotequal",1,
		"return new boolean_class_(!(@1 instanceof string_class_) || !(((string_class_)@1).value__.equals(value__)));"},
	{NULL,"operatorget",1,
		"if (@1 instanceof integer_class_){\n"
		"\nint i=((integer_class_)@1).value__;\n"
		"\nif (i>=value__.length()){System.err.println(\":: 'string.operatorGet': Intento de acceso fuera de los limites del array\");\nSystem.exit(1);return null;}\n"
		"\treturn new string_class_(\"\"+value__.charAt(i));\n"
		"}\n"
		"else {System.err.println(\":: 'string.operatorGet' solo puede aplicarse a parametros de tipo 'native integer'\");\nSystem.exit(1);return null;}\n"
		},
	{NULL,"operatorconcat",1,
		"if (@1 instanceof string_class_) return new string_class_(value__+((string_class_)@1).value__);\n"
		"else{System.err.println(\":: 'string.operatorConcat' solo puede aplicarse a parámetros de tipo 'native string'\");System.exit(1);return null;}\n"
		},
	{NULL,"tokens",0,
		"String[] s=value__.split(\" \");\n"
		"Object[] t=new Object[s.length];\n"
		"for(int i=0;i<s.length;i++) t[i]=new string_class_(s[i]);\n"
		"return new array_class_(t);"
		},
	{NULL,"tokens",1,
		"if (!(@1 instanceof string_class_)){System.err.println(\":: 'string.tokens' solo puede aplicarse a parámetros de tipo 'native string'\");System.exit(1);return null;}\n"
		"String[] s=value__.split(((string_class_)@1).value__);\n"
		"Object[] t=new Object[s.length];\n"
		"for(int i=0;i<s.length;i++) t[i]=new string_class_(s[i]);\n"
		"return new array_class_(t);"
		},
	{NULL,"tolower",0,
		"return new string_class_(value__.toLowerCase());"},	
	{NULL,"toupper",0,
		"return new string_class_(value__.toUpperCase());"},	
	{NULL,"tostring",0,
		"return new string_class_(value__);"},	
	{"array",NULL,0,
		"public Object[] value__;\n"
		"public array_class_(){value__=new Object[]{};}\n"
		"public array_class_(Object[] x){value__=x;}\n"
		},
	{NULL,"length",0,
		"return new integer_class_(value__.length);"},
	{NULL,"operatorequal",1,
		"if (!(@1 instanceof array_class_)) return new boolean_class_(false);\n"
		"Object[] d=((array_class_)@1).value__;\n"
		"if (d.length!=value__.length) return new boolean_class_(false);\n"
		"\tfor(int i=0;i<d.length;i++){\n"
		"\t\tObject o=eval(value__[i],\"operatorequal\",1,new Object[]{d[i]},\"array.operatorequal\",0,0);\n"
		"\t\tif (!(o instanceof boolean_class_) || ((boolean_class_)o).value__==false) return new boolean_class_(false);\n"
		"\t}\n"
		"return new boolean_class_(true);\n"
		},
	{NULL,"operatornotequal",1,
		"if (!(@1 instanceof array_class_)) return new boolean_class_(true);\n"
		"Object[] d=((array_class_)@1).value__;\n"
		"if (d.length!=value__.length) return new boolean_class_(true);\n"
		"\tfor(int i=0;i<d.length;i++){\n"
		"\t\tObject o=eval(value__[i],\"operatorequal\",1,new Object[]{d[i]},\"array.operatorequal\",0,0);\n"
		"\t\tif (!(o instanceof boolean_class_) || ((boolean_class_)o).value__==false) return new boolean_class_(true);\n"
		"\t}\n"
		"return new boolean_class_(false);\n"
		},
	{NULL,"operatorget",1,
		"if (@1 instanceof integer_class_){\n"
		"\nint i=((integer_class_)@1).value__;\n"
		"\nif (i>=value__.length){System.err.println(\":: 'array.operatorGet': Intento de acceso fuera de los limites del array\");\nSystem.exit(1);return null;}\n"
		"\treturn value__[i];\n"
		"}\n"
		"else {System.err.println(\":: 'array.operatorGet' solo puede aplicarse a parametros de tipo 'native integer'\");\nSystem.exit(1);return null;}\n"
		},
	{NULL,"operatorset",2,
		"if (@1 instanceof integer_class_) return value__[((integer_class_)@1).value__]=@2;\n"
		"else {System.err.println(\":: 'array.operatorSet' debe aplicarse sobre un primer parametro de tipo 'native integer'\");\nSystem.exit(1);return null;}\n"
		},
	{NULL,"operatorunion",1,
		"if (!(@1 instanceof array_class_)){System.err.println(\":: 'array.operatorUnion' solo puede aplicarse a parametros de tipo 'native array'\");\nSystem.exit(1);return null;}\n"
		"Object[] a=((array_class_)@1).value__;\n"
		"Object[] res=new Object[a.length+value__.length];\n"
		"int i=0;\n"
		"for(;i<value__.length;i++) res[i]=value__[i];\n"
		"for(;i<res.length;i++) res[i]=a[i-value__.length];\n"
		"return new array_class_(res);"},
	{NULL,"tostring",0,
		"String x=\"[\";"
		"for (int i=0;i<value__.length;i++)\n"
		"\tx+=(i!=0?\",\":\"\")+eval(value__[i],\"tostring\",0,new Object[]{},\"array.tostring\",0,0);\n"
		"return new string_class_(x+\"]\");"
		},
	{"system",NULL,0,
		""},
	{NULL,"readln",0,
		"try{\n"
		"\treturn new string_class_(new java.io.BufferedReader(new java.io.InputStreamReader(System.in)).readLine());"
		"}catch(java.io.IOException e){\n"
		"\tSystem.out.println(\":: 'system.readln': Error al leer la entrada estandar\");System.exit(1);return null;\n"
		"}"
		},
	{NULL,"print",1,
		"System.out.print(eval(@1,\"tostring\",0,new Object[]{},\"system.print\",0,0));\n"
		"return null_object_;"},
	{NULL,"println",1,
		"System.out.println(eval(@1,\"tostring\",0,new Object[]{},\"system.println\",0,0));\n"
		"return null_object_;"},
	{NULL,"exit",0,
		"System.exit(0);return null;"},
	
	{NULL,NULL,0}	/* marca de finalización de secuencia*/
};

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

static void *objetosNativos; /* tabla de simbolos*/
static void *miembrosNativos; /* tabla de simbolos*/

static void inicializarNativos(void){
int i;
void *miembros;
char *id;

	id=NULL;
	miembros=NULL;
	miembrosNativos=t_crear();	
	objetosNativos=t_crear();
	for(i=0;_nativos[i].miembro!=NULL || _nativos[i].objeto!=NULL;i++){
		if (_nativos[i].objeto!=NULL){
				
				/* tabla de objetos nativos */
		
			objetosNativos=t_insertar(objetosNativos,
				t_simbolo(_nativos[i].objeto),0,
				_nativos[i].codigo);
		
				/* tabla de miembros nativos (a dos niveles) */
			
			if (id!=NULL) miembrosNativos=t_insertar(miembrosNativos,
				t_simbolo(id),0,
				miembros);
			
			id=_nativos[i].objeto;
			miembros=t_crear();
		}
		else
			miembros=t_insertar(miembros,
				t_simbolo(_nativos[i].miembro),_nativos[i].parametros,
				_nativos[i].codigo);
	}	
	miembrosNativos=t_insertar(miembrosNativos,t_simbolo(id),0,miembros);
}

	/***********************************************************/
	/* Obtener el codigo asociado a un objeto o miembro nativo */
	/***********************************************************/

static char *obtenerCodigoNativoObjeto(char *idObjeto){
	return t_buscar(objetosNativos,idObjeto,0);
}

static char *obtenerCodigoNativoMiembro(char *idObjeto,char *idMiembro,unsigned char parametros){
	return t_buscar(t_buscar(miembrosNativos,idObjeto,0),idMiembro,parametros);
}

static void imprimirCodigoNativoObjeto(A_NODOOBJETO *objeto){
int i;
char *codigo;
	
	codigo=obtenerCodigoNativoObjeto(objeto->id);
	if (codigo==NULL)
		e_fatal(NULL,"Error interno: '%s' no es un objeto nativo valido",objeto->id);
	
	nuevaLinea();
	for(i=0;codigo[i]!=0;i++){
		switch(codigo[i]){
			case '@':
				i++;
				switch(codigo[i]){
					case 's': imprimir("_");break;
					case '@': imprimir("@");break;
				}
				break;
			case '\n': 
				imprimir("\n");
				nuevaLinea();
				break;
			case '\t': 
				imprimir(IMPRIMIR_TAB);
				break;
			default: 
				fprintf(fp,"%c",codigo[i]);
				break;
		}
	}
	imprimir("\n");
}

static void imprimirCodigoNativoMiembro(A_NODOMIEMBRO *miembro){
int i,j,k;
char *codigo;
A_NODOARGUMENTOSLISTA *n;

	codigo=obtenerCodigoNativoMiembro(
		miembro->nodoEspNombres->tipo==A_OBJETO?((A_NODOOBJETO*)(miembro->nodoEspNombres))->id:NULL,
		miembro->id,miembro->parametros);
	if (codigo==NULL)
		e_fatal(NULL,"Error interno: '%s' no es un miembro nativo valido",miembro->id);
	
	nuevaLinea();
	for(i=0;codigo[i]!=0;i++){
		switch(codigo[i]){
			case '@':
				i++;
				switch(codigo[i]){
					case 's': imprimir("_");break;
					case '@': imprimir("@");break;
					case '1':case '2':case '3':case '4':case '5':
					case '6':case '7':case '8':case '9':
						j=codigo[i]-'1';
						n=(A_NODOARGUMENTOSLISTA*)miembro->h3;
						for(k=0;k<j && n!=NULL;k++)
							n=(A_NODOARGUMENTOSLISTA*)n->h2;
						if (n!=NULL && n->h1!=NULL){
							imprimir(((A_NODOID*)(n->h1))->id);
							imprimir("_");
						}
						break;
				}
				break;
			case '\n': 
				imprimir("\n");
				nuevaLinea();
				break;
			case '\t': 
				imprimir(IMPRIMIR_TAB);
				break;
			default: 
				fprintf(fp,"%c",codigo[i]);
				break;
		}
	}
	imprimir("\n");
}

/********************************************/
/* 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);
			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;
	x=0;
	while(nodo!=NULL){
		x++;
		nodo=(A_NODOPARAMETROSLISTA *)(nodo->h2);
	}
	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));
}

/***********************************************************************/
/* Traduccion                                                          */
/***********************************************************************/

	/********************************************************/
	/* Referencias hacia adelante                           */
	/********************************************************/

static void visitar(A_NODO *nodo);
	
	/****************************************************************/
	/* Metodos del patron visitante                   	 	*/
	/****************************************************************/

	/************/
	/* Programa */
	/************/

static void visitarPrograma(A_NODOPROGRAMA *nodo){

		/* Comentario con el nombre del objeto */

	nuevaLinea();imprimir("/*************/\n");
	nuevaLinea();imprimir("/* (program) */\n");
	nuevaLinea();imprimir("/*************/\n\n");
	
		/* Declaracion del objeto */
		
	nuevaLinea();		
	imprimir("static public class program{\n\n");
	fptab++;
	
		/* Traduccion del cuerpo del programa */
	
	nodoEspNombres=nodo;
	if (nodo->h1!=NULL) visitar(nodo->h1);
	
		/* Cerrar declaración de la clase principal */
		
	fptab--;
	nuevaLinea();imprimir("}\n\n");
	
}

	/**********/
	/* Objeto */
	/**********/

static A_NODOOBJETO *auxobjeto;
static A_NODOOBJETO *auxprimero;

static void auxHerenciaMultiple2(char *simbolo,unsigned char parametros, void *datos){
	if 	(	
		t_buscar(auxobjeto->tds,simbolo,parametros)==NULL &&
		t_buscar(auxobjeto->solapados,simbolo,parametros)==NULL &&
		t_buscar(auxprimero->tds,simbolo,parametros)==NULL &&
		t_buscar(auxprimero->heredados,simbolo,parametros)==NULL
		)
			visitar(datos);
	
}
static void auxHerenciaMultiple(char *simbolo,unsigned char parametros,void *datos){
A_NODOOBJETO *nodo;
	nodo=datos;
	if (nodo!=auxprimero){
		t_forAll(nodo->tds,&auxHerenciaMultiple2);
		t_forAll(nodo->heredados,&auxHerenciaMultiple2);
	}
}	
	
static void visitarObjeto(A_NODOOBJETO *nodo){
int i;
A_NODOOBJETO *n;
A_NODOCUERPOOBJETOLISTA *cl;
char *id;

		/* Anotar ultimo objeto recorrido */

	nodoObjeto=nodo;
		
		/* Comentario con el nombre del objeto */

	if (nodo->id==NULL){
		nuevaLinea();imprimir("/***************/\n");
		nuevaLinea();imprimir("/* (anonymous) */\n");
		nuevaLinea();imprimir("/***************/\n\n");
	}
	else{
		nuevaLinea();imprimir("/*");for(i=0;i<strlen(nodo->id)+2;i++) imprimir("*");imprimir("*/\n");
		nuevaLinea();imprimir("/* ");imprimir(nodo->id);imprimir(" */\n");
		nuevaLinea();imprimir("/*");for(i=0;i<strlen(nodo->id)+2;i++) imprimir("*");imprimir("*/\n");
		imprimir("\n");
	}
	
		/* Declaracion del objeto */
		
	nuevaLinea();
	if (nodo->mod&A_MODABSTRACT) imprimir("abstract ");
	imprimir("public class ");
	if (nodo->id==NULL){imprimir("anonymous");imprimirPuntero(nodo);}
	else imprimir(nodo->id);
	imprimir("_class_ ");
	if (!t_vacio(nodo->padres)){
		imprimir("extends ");
		n=(A_NODOOBJETO *)t_primero(nodo->padres);
		imprimir(n->id);imprimir("_class_");
	}
	imprimir("{\n");
	fptab++;
		
		/* Traducir cuerpo del objeto */
		
	if (nodo->mod&A_MODNATIVE) imprimirCodigoNativoObjeto(nodo);
	nodoEspNombres=nodo;
	if (nodo->h4!=NULL) visitar(nodo->h4);
	
		/* Herencia multiple: incluir miembros heredados no solapados y no */
		/*		      declarados en el objeto actual ni en la superclase */
		/*		      considerada */

	imprimir("\n");
	if (nodo->mod&A_MODHERENCIAMULTIPLE){
		auxobjeto=nodo;
		auxprimero=t_primero(nodo->padres);
		t_forAll(nodo->padres,&auxHerenciaMultiple);
	}
	
		/* Reflexion */
		
	nuevaLinea();
	imprimir("/* (reflection) */\n\n");		
	
		/* Nombre del objeto */
		
	nuevaLinea();
	imprimir("public String id__(){return \"");
	if (nodo->id==NULL) imprimir("(anonymous)");
	else imprimir(nodo->id);
	imprimir("\";}\n");
	
		/* Padres del objeto */
				
	nuevaLinea();
	imprimir("public Object[] parents__(){return new Object[]{");
	//if (!t_vacio(nodo->padres)) imprimirTdsArray(nodo->padres,"","object_");
	if (!t_vacio(nodo->padres)) imprimirTdsArray(nodo->padres,"","class_.class");
	imprimir("};}\n");
		
		/* Objetos contenidos */

	nuevaLinea();
	imprimir("public Object[] lexicalsons__(){return new Object[]{");
	for(i=0,cl=(A_NODOCUERPOOBJETOLISTA*)(nodo->h4);cl!=NULL;cl=(A_NODOCUERPOOBJETOLISTA*)(cl->h2)){
		if (cl->h1->tipo==A_OBJETO){
			n=(A_NODOOBJETO*)(cl->h1);
			if (n->mod&A_MODHIDDEN) continue;
			if (n->mod&A_MODABSTRACT) continue;
			
			if (i==0) i++;
			else imprimir(",");
				
			if (n->id==NULL){
				imprimir("anonymous");
				imprimirPuntero(n);
				imprimir("_object_");
			}
			else{
				imprimir(n->id);
				imprimir("_object_");
			}
		}
	}
	imprimir("};}\n");
	
		/* Objeto contenedor */
		
	nuevaLinea();
	imprimir("public Object lexicalparent__(){return ");
	if (nodo->nodoEspNombres==NULL || nodo->nodoEspNombres->tipo==A_PROGRAMA)
		imprimir("null_object_");
	else{
		id=((A_NODOOBJETO*)(nodo->nodoEspNombres))->id;
		if (id==NULL){imprimir("anonymous");imprimirPuntero(nodo->nodoEspNombres);}
		else imprimir(id);
		imprimir("_object_");
	}
	imprimir(";}\n");
	
		/* Separador */
		
	imprimir("\n");
		
		/* Cerrar declaración de objeto */
	
	fptab--;
	nuevaLinea();
	imprimir("}\n\n");
		
		/* Declaración del miembro implícito */

	if ((nodo->mod&A_MODABSTRACT)==0){
		if (nodo->id==NULL){
			nuevaLinea();fprintf(fp,"public final Object anonymous%p_=null;\n",nodo);
			nuevaLinea();fprintf(fp,"public Object anonymous%p_member_(){\n",nodo);
			fptab++;
			nuevaLinea();fprintf(fp,"return anonymous%p_object_;\n",nodo);
			fptab--;
			nuevaLinea();fprintf(fp,"}\n");
		}
		else{		
			nuevaLinea();fprintf(fp,"public final Object %s_=null;\n",nodo->id);
			nuevaLinea();fprintf(fp,"public Object %s_member_(){\n",nodo->id);
			fptab++;
			nuevaLinea();fprintf(fp,"return %s_object_;\n",nodo->id);
			fptab--;
			nuevaLinea();fprintf(fp,"}\n");
		}
	}
	
		/* Declaración de la instancia estática */

	if ((nodo->mod&A_MODABSTRACT)==0){
		nuevaLinea();
		if (nodo->id==NULL) fprintf(fp,"public final anonymous%p_class_ anonymous%p_object_=new anonymous%p_class_();\n",nodo,nodo,nodo);
		else fprintf(fp,"public final %s_class_ %s_object_=new %s_class_();\n",nodo->id,nodo->id,nodo->id);
	}
}

static void visitarMiembro(A_NODOMIEMBRO *nodo){
A_NODOARGUMENTOSLISTA *n;
int i;

		/* Los miembros abstractos no es necesario declararlos */
		
	if (nodo->mod&A_MODABSTRACT) return;

		/* Anotar ultimo miembro visitado */

	nodoMiembro=nodo;
	
		/* Comentario */

	nuevaLinea();fprintf(fp,"/* %s */\n\n",nodo->id);

		/* atributo */

	nuevaLinea();
	if (nodo->mod&A_MODSHARED) imprimir("static ");
	if (nodo->mod&A_MODABSTRACT) imprimir("abstract ");
	if (!(nodo->mod&A_MODVAR)) imprimir("final ");
	fprintf(fp,"public Object %s_",nodo->id);
	if (nodo->parametros) fprintf(fp,"%d_",nodo->parametros);
	fprintf(fp,"=null;\n");
	
		/* obtener traduccion del miembro */
	
	if ((nodo->mod&A_MODNATIVE)==0){
		nTempVars=0;
		expSimple=true;
		nodoEspNombres=nodo;
		if (nodo->h4!=NULL) visitar(nodo->h4);
	}
	
		/* miembro */
	
	nuevaLinea();
	fprintf(fp,"public Object %s_",nodo->id);
	if (nodo->parametros) fprintf(fp,"%d_",nodo->parametros);
	fprintf(fp,"member_(");
	if (nodo->h3!=NULL) visitar(nodo->h3);
	fprintf(fp,"){\n",nodo->id);
	fptab++;
	
	if (nodo->mod&A_MODNATIVE)
		imprimirCodigoNativoMiembro(nodo);
	else if (nodo->mod&A_MODEXTERN){
		nuevaLinea();imprimir("return ");imprimir(expTexto);imprimir("(");
		for(n=(A_NODOARGUMENTOSLISTA*)(nodo->h3);n!=NULL;n=(A_NODOARGUMENTOSLISTA*)(n->h2)){
			imprimir(((A_NODOID*)(n->h1))->id);imprimir("_");
			if (n->h2!=NULL) imprimir(",");
		}
		imprimir(");\n");
	}
	else{
		if (!expSimple){
			nuevaLinea();imprimir("Object " TEMPVAR ";\n");
		}
		if (nTempVars){
			nuevaLinea();imprimir("Object ");
			for (i=0;i<nTempVars;i++){
				if (i!=0) imprimir(",");
				fprintf(fp,TEMPVAR_PREFIJO "%d",i);
			}	
			imprimir(";\n");
		}		
		if ((!expSimple)||nTempVars){
			nuevaLinea();imprimir("\n");
		}
		
		if (expSimple) {
			nuevaLinea();imprimir("return ");imprimir2(expTexto);imprimir(";\n");
		}
		else {
			nuevaLinea();imprimir2(expTexto);imprimir(";\n");
			nuevaLinea();imprimir("return " TEMPVAR ";\n");
		}
	}

	fptab--;
	nuevaLinea();
	imprimir("}\n");
}
static void visitarArgumentosLista(A_NODOARGUMENTOSLISTA *nodo){
	if (nodo->h1!=NULL) {fprintf(fp,"Object %s_",((A_NODOID*)(nodo->h1))->id);}
	if (nodo->h2!=NULL) {imprimir(",");visitar(nodo->h2);}
}

static void visitarMiembroExterno(A_NODOMIEMBROEXTERNO *nodo){
A_NODOLITERALCADENA *paquete,*objeto,*miembro;

	paquete=((A_NODOLITERALCADENA*)(nodo->h1));
	objeto=((A_NODOLITERALCADENA*)(nodo->h2));
	miembro=((A_NODOLITERALCADENA*)(nodo->h3));
	expTexto=concat(paquete->s,strlen(paquete->s)?".":"",
			objeto->s,strlen(objeto->s)?".":"",
			miembro->s,NULL);
}

	/****************/
	/* CuerpoObjeto */
	/****************/

static void visitarCuerpoObjetoLista(A_NODOCUERPOOBJETOLISTA *nodo){
	if (nodo->h1!=NULL) visitar(nodo->h1);
	if (nodo->h2!=NULL) {imprimir("\n");visitar(nodo->h2);}
}
	
	/**************/
	/* Let        */
	/**************/
	
static void visitarLet(A_NODOLET *nodo){
char *a,*b,simb;

	nodoEspNombres=nodo;visitar(nodo->h1);a=expTexto;               /* declaracionLista */
	nodoEspNombres=nodo;visitar(nodo->h2);b=expTexto;simb=expSimple;/* expresion */
	
	if (simb) expTexto=concat(a,";\ntemp=",b,NULL);
	else expTexto=concat(a,";\n",b,NULL);
	expSimple=false;
}

static void visitarDeclaracionLista(A_NODODECLARACIONLISTA *nodo){
char *a,*b,*s;	
	visitar(nodo->h1);a=expTexto;
	
	if (nodo->h2==NULL)
		expTexto=concat("Object ",a,"_",charPuntero(nodoEspNombres),"_=null_object_",NULL);
	else{
		visitar(nodo->h2);b=expTexto;
		if (expSimple){
			expTexto=concat("Object ",a,"_",charPuntero(nodoEspNombres),"_","=",b,NULL);
		}
		else{
			expTexto=concat(b,";\n","Object ",a,"_",charPuntero(nodoEspNombres),"_","=" TEMPVAR "",NULL);
		}
	}
	
	s=expTexto;
	if (nodo->h3!=NULL){
		visitar(nodo->h3);
		expTexto=concat(s,";\n",expTexto,NULL);
		}
		
	expSimple=false;		
}	


	/*****************************************************/
	/* Subexpresiones (idAtomico, accesoMiembros,arrays) */
	/*****************************************************/
	
	/* Constructor */

static SUBEXP *nuevoSubExp(void){	
SUBEXP *p;
	p=(SUBEXP *)malloc(sizeof(SUBEXP));
	if (p==NULL)
		e_fatal(NULL,"Sin memoria durante la traducción");	
	return p;
}
	
	/* Destructor */

/*
static void freeSubExp(SUBEXP *p){
SUBEXP *s;

	while(p!=NULL){
		s=p->sig;
		free(p);
		p=s;
	}
}
*/

	/* asignación del resultado subexpresiones complejas a variables temporales */

	/* (iTempVar indica el índice de la primera variable temporal disponible) */
	
static char *subExpAsig(SUBEXP *p,int iTempVar){
char *s;

	s=strdup("");
	while(p!=NULL){
		if (!(p->expSimple)) 
			s=concat(s,p->expTexto,";\n" TEMPVAR_PREFIJO,charEntero(iTempVar++),"=" TEMPVAR ";\n",NULL);
		p=p->sig;
	}
	return s;
}

	/* subexpresiones como parametros*/
 
	/* (iTempVar indica el índice de la primera variable temporal disponible) */
	
static char *subExpPars(SUBEXP *p,int iTempVar){
char *s;
int i;
	
	s=strdup("");
	while(p!=NULL){
		if (p->expSimple) s=concat(s,p->expTexto,NULL);
		else              s=concat(s,TEMPVAR_PREFIJO,charEntero(iTempVar++),NULL);
		if (p->sig!=NULL) s=concat(s,",",NULL);
		p=p->sig;
	}
	return s;
}

	/* indica si todas las subexpresiones son simples */

char subExpSimple(SUBEXP *p){
char c=true;

	while(p!=NULL){
		if (!p->expSimple) c=false;
		p=p->sig;
	}
	return c;
}

	/* indica el número de subexpresiones */

unsigned char subExpN(SUBEXP *p){
unsigned char i;
	i=0;
	while (p!=NULL){
		i++;
		p=p->sig;
	}
	return i;
}

	/* indica el número de subexpresiones complejas */

unsigned char subExpNTempVars(SUBEXP *p){
unsigned char i;
	i=0;
	while (p!=NULL){
		if (!p->expSimple) i++;
		p=p->sig;
	}
	return i;
}


static void visitarParametrosLista(A_NODOPARAMETROSLISTA* nodo){
SUBEXP *p;

	p=nuevoSubExp();
	visitar(nodo->h1);
	p->expSimple=expSimple;
	p->expTexto=expTexto;
	
	if (nodo->h2!=NULL) {
		visitar(nodo->h2);p->sig=subExp;
	}
	else 
		p->sig=NULL;
		
	subExp=p;
}

static void visitarLiteralSecuenciaLista(A_NODOLITERALSECUENCIALISTA *nodo){
SUBEXP *p;

	p=nuevoSubExp();
	visitar(nodo->h1);
	p->expSimple=expSimple;
	p->expTexto=expTexto;
	
	if (nodo->h2!=NULL) {
		visitar(nodo->h2);p->sig=subExp;
	}
	else 
		p->sig=NULL;
		
	subExp=p;
}

	/**************/
	/* ID Atomico */
	/**************/
	
static void visitarIdAtomico(A_NODOIDATOMICO *nodo){
char *id,*s,*tx;
int i;
A_NODO *n;
unsigned char parametros;

	visitar(nodo->h1);id=expTexto;
	
	n=buscarId(nodoEspNombres,obtenerId((A_NODOID*)(nodo->h1)),
		parametros=contarParametros(((A_NODOPARAMETROSLISTA *)(nodo->h2))));
	
	if (nodo->h2!=NULL) visitar(nodo->h2);	/* parametros */
	else subExp=NULL;
	
	switch(n->tipo){
		case A_LET:	/* v. locales */
			tx=concat(id,"_",charPuntero(n),"_",NULL);
			break;
		case A_ID:	/* parametros */
			tx=concat(id,"_",NULL);
			break;
		case A_OBJETO:
			tx=concat(id,"_object_",NULL);
			break;
		case A_MIEMBRO:
				/*(id_[<n>_]==null?id_[<n>_]member_:id_[<n>_])*/		
			tx=strdup("(");
			tx=concat(tx,id,"_",NULL);
			if (parametros) tx=concat(tx,charEntero(parametros),"_",NULL);
			tx=concat(tx,"!=null?",id,"_",NULL);
			if (parametros) tx=concat(tx,charEntero(parametros),"_",NULL);
			tx=concat(tx,":",id,"_",NULL);
			if (parametros) tx=concat(tx,charEntero(parametros),"_",NULL);
			tx=concat(tx,"member_",NULL);
			tx=concat(tx,"(",subExpPars(subExp,nTempVars),")",NULL);
			tx=concat(tx,")",NULL);
			break;		
		default:
			e_fatal(&nodo->posicion,"Error interno (visitarIdAtomico)");
			break;	
	}
	
	if (subExpSimple(subExp)){
		expTexto=tx;
		expSimple=true;
	}
	else{
		expTexto=concat(subExpAsig(subExp,nTempVars),TEMPVAR "=",tx,NULL);
		expSimple=false;
	}
	nTempVars+=subExpNTempVars(subExp);
		
	//freePar(subExp);
}	
	
	/*******************/
	/* Acceso miembros */
	/*******************/
	
static void visitarAccesoMiembro(A_NODOACCESOMIEMBRO *nodo){
char *a,*b,*s,sima;
int i;

	visitar(nodo->h1);a=expTexto;sima=expSimple;	/* expresion */
	visitar(nodo->h2);b=expTexto;			/* id */
	if (nodo->h3!=NULL) visitar(nodo->h3);		/* parametros */
	else subExp=NULL;
	
	if (sima)
		expTexto=concat(
			subExpAsig(subExp,nTempVars),
			"eval(",
				a,",",
				"\"",b,"\",",
				charEntero(contarParametros((A_NODOPARAMETROSLISTA*)(nodo->h3))),",",
				"new Object[]{",subExpPars(subExp,nTempVars),"},",
				"\"",nodo->posicion.fichero,"\",",
				charEntero(nodo->posicion.lineaInicio),",",
				charEntero(nodo->posicion.lineaFin),
			")",
			NULL);
	else
		expTexto=concat(
			subExpAsig(subExp,nTempVars),a,
			"eval(" TEMPVAR ",",
				"\"",b,"\",",
				charEntero(contarParametros((A_NODOPARAMETROSLISTA*)(nodo->h3))),",",
				"new Object[]{",subExpPars(subExp,nTempVars),"},",
				"\"",nodo->posicion.fichero,"\",",
				charEntero(nodo->posicion.lineaInicio),",",
				charEntero(nodo->posicion.lineaFin),
			")",
			NULL);
	
	if (subExpSimple(subExp) && sima){expSimple=true;} else{expSimple=false;}
	nTempVars+=subExpNTempVars(subExp);
	
	//freePar(subExp);
}	
	
	/*********************/
	/* Asignacion        */
	/*********************/

static void visitarAsignacion(A_NODOASIGNACION *nodo){
A_NODOID *nodoid;
A_NODOPARAMETROSLISTA *nodopar;
A_NODO *n;
A_NODOACCESOMIEMBRO *nodoacceso;
int parametros;
char *e2,es2,*ea,esa,*id;
	
	visitar(nodo->h2);
	es2=expSimple;e2=expTexto;
	
	/* Asignacion con identificador atomico */

	if (nodo->h1->tipo==A_IDATOMICO){
				
		nodoid=(A_NODOID*)( ((A_NODOIDATOMICO*)(nodo->h1)) ->h1);
		nodopar=(A_NODOPARAMETROSLISTA*)( ((A_NODOIDATOMICO*)(nodo->h1)) ->h2);
	
		id=obtenerId(nodoid);
		parametros=contarParametros(nodopar);
		n=buscarId(nodoEspNombres,id,parametros);

		id=concat(id,"_",NULL);
		if (parametros) id=concat(id,charEntero(parametros),"_",NULL);
		if (n->tipo==A_LET) id=concat(id,charPuntero(n),"_",NULL);
		
		if (es2){
			expTexto=concat("(",id,"=",e2,")",NULL);
			expSimple=true;
		}
		else{
			expTexto=concat(e2,";\n",id,"=" TEMPVAR "",NULL);
			expSimple=false;
		}
	}
	
	/* Asignacion con acceso a miembros */
	
	else if (nodo->h1->tipo==A_ACCESOMIEMBRO){
		nodoacceso=(A_NODOACCESOMIEMBRO*)(nodo->h1);	
	
		nodoid=(A_NODOID*)(nodoacceso->h2);
		nodopar=(A_NODOPARAMETROSLISTA*)(nodoacceso->h3);
		visitar(nodoacceso->h1);ea=expTexto;esa=expSimple;	/* expresion */
	
		id=obtenerId(nodoid);
		parametros=contarParametros(nodopar);
		
		if (esa && es2){
			expTexto=concat(
				"asig(",ea,",\"",id,"\",",charEntero(parametros),",",e2,",\"",nodo->posicion.fichero,"\",",
					charEntero(nodo->posicion.lineaInicio),",",
					charEntero(nodo->posicion.lineaFin),")",
					NULL);
			expSimple=true;
		}
		else if ((esa || es2) && !(esa && es2)){
			expTexto=concat(esa?ea:e2,";\n",
				TEMPVAR "=asig(",esa?ea:TEMPVAR "",",\"",id,"\",",charEntero(parametros),",",es2?e2:TEMPVAR "",",\"",nodo->posicion.fichero,"\",",
					charEntero(nodo->posicion.lineaInicio),",",
					charEntero(nodo->posicion.lineaFin),")",
					NULL);
			expSimple=false;
		}
		else{
			expTexto=concat(ea,";\n" TEMPVAR_PREFIJO,charEntero(nTempVars),"=" TEMPVAR ";\n",e2,";\n"
				TEMPVAR "=asig(" TEMPVAR_PREFIJO, charEntero(nTempVars),",",",\"",id,"\"," TEMPVAR ",\"",nodo->posicion.fichero,"\",",
					charEntero(nodo->posicion.lineaInicio),",",
					charEntero(nodo->posicion.lineaFin),")",
					NULL);
			expSimple=false;
			nTempVars++;
		}
	}
}
	
	/*************/
	/* Secuencia */
	/*************/

static void visitarSecuencia(A_NODOSECUENCIA *nodo){
char *a,*b,sa,sb;
	visitar(nodo->h1);a=expTexto;sa=expSimple;
	visitar(nodo->h2);b=expTexto;sb=expSimple;
	
	if (sa)
		a=concat(TEMPVAR "=",a,NULL);/* para convertir en sentencia la expresion simple */
	if (sb) 
		expTexto=concat(a,";\ntemp=",b,NULL);
	else 
		expTexto=concat(a,";\n",b,NULL);
	
	expSimple=false;
}

	/*************/
	/* Literales */
	/*************/

static void visitarLiteralSecuencia(A_NODOLITERALSECUENCIA *nodo){
char *a,*s;
int i;

	if (nodo->h1!=NULL) visitar(nodo->h1);	
	else subExp=NULL;
	
	expTexto=concat(
		subExpAsig(subExp,nTempVars),
		"new array_class_(new Object[]{",
		subExpPars(subExp,nTempVars),
		"})",
		NULL);

	if (subExpSimple(subExp)){expSimple=true;} else{expSimple=false;}
	nTempVars+=subExpNTempVars(subExp);
		
	//freePar(subExp);
}	
static void visitarLiteralCadena(A_NODOLITERALCADENA *nodo){
	expTexto=concat("new string_class_(\"",nodo->s,"\")",NULL);
	expSimple=true;
}
static void visitarLiteralEntero(A_NODOLITERALENTERO *nodo){
	expTexto=concat("new integer_class_(",charEntero(nodo->i),")",NULL);
	expSimple=true;
}
static void visitarLiteralReal(A_NODOLITERALREAL *nodo){
	expTexto=concat("new float_class_(",charDouble(nodo->f),")",NULL);
	expSimple=true;
}
static void visitarLiteralTrue(A_NODOLITERALTRUE *nodo){
	expTexto=strdup("new boolean_class_(true)");
	expSimple=true;
}
static void visitarLiteralFalse(A_NODOLITERALFALSE *nodo){
	expTexto=strdup("new boolean_class_(false)");
	expSimple=true;
}
static void visitarId(A_NODOID *nodo){
	expTexto=strdup(nodo->id);
	expSimple=true;
}

	/*****************/
	/* Identidad     */
	/*****************/
	
static void visitarIdentico(A_NODOIDENTICO *nodo){
char *exp1,exp1s,*exp2,exp2s;

	if (nodo->h1!=NULL) visitar(nodo->h1);exp1=expTexto;exp1s=expSimple;
	if (nodo->h2!=NULL) visitar(nodo->h2);exp2=expTexto;exp2s=expSimple;

	if (exp1s && exp2s){
		expTexto=concat("new boolean_class_(",exp1,"==",exp2,")",NULL);
		expSimple=true;
	}	
	else if (exp1s && !exp2s){
		expTexto=concat(exp2,";\ntemp=new boolean_class_(",exp1,"==" TEMPVAR ")",NULL);
		expSimple=false;
	}
	else if (!exp1s && exp2s){
		expTexto=concat(exp1,";\ntemp=new boolean_class_(",exp2,"==" TEMPVAR ")",NULL);
		expSimple=false;
	}
	else{
		expTexto=concat(exp1,";\n" TEMPVAR_PREFIJO, charEntero(nTempVars), "=" TEMPVAR ";\n",exp2,";\n" TEMPVAR_PREFIJO, charEntero(nTempVars+1), "=" TEMPVAR ";\ntemp=new boolean_class_(" TEMPVAR_PREFIJO, charEntero(nTempVars), "==" TEMPVAR_PREFIJO, charEntero(nTempVars+1), ")",NULL);
		expSimple=false;
		nTempVars+=2;
	}
}

static void visitarNoIdentico(A_NODONOIDENTICO *nodo){
char *exp1,exp1s,*exp2,exp2s;

	if (nodo->h1!=NULL) visitar(nodo->h1);exp1=expTexto;exp1s=expSimple;
	if (nodo->h2!=NULL) visitar(nodo->h2);exp2=expTexto;exp2s=expSimple;

	if (exp1s && exp2s){
		expTexto=concat("new boolean_class_(",exp1,"!=",exp2,")",NULL);
		expSimple=true;
	}	
	else if (exp1s && !exp2s){
		expTexto=concat(exp2,";\ntemp=new boolean_class_(",exp1,"!=" TEMPVAR ")",NULL);
		expSimple=false;
	}
	else if (!exp1s && exp2s){
		expTexto=concat(exp1,";\ntemp=new boolean_class_(",exp2,"!=" TEMPVAR ")",NULL);
		expSimple=false;
	}
	else{
		expTexto=concat(exp1,";\n" TEMPVAR_PREFIJO, charEntero(nTempVars), "=" TEMPVAR ";\n",exp2,";\n" TEMPVAR_PREFIJO, charEntero(nTempVars+1), "=" TEMPVAR ";\ntemp=new boolean_class_(" TEMPVAR_PREFIJO, charEntero(nTempVars), "!=" TEMPVAR_PREFIJO, charEntero(nTempVars+1), ")",NULL);
		expSimple=false;
		nTempVars+=2;
	}
}
	
	/*****************/
	/* Reflexion     */
	/*****************/

static void visitarHas(A_NODOHAS *nodo){
char *exp,exps,*id;

	if (nodo->h1!=NULL) visitar(nodo->h1);exp=expTexto;exps=expSimple;
	if (nodo->h2!=NULL) visitar(nodo->h2);id=expTexto;
	
	if (exps){
		expTexto=concat("new boolean_class_(hasmember(",exp,",\"",id,"_\"))",NULL);
		expSimple=true;
	}
	else{
		expTexto=concat(exp,";\ntemp=new boolean_class_(hasmember(" TEMPVAR ",\"",id,"_\"))",NULL);
		expSimple=false;
	}
}
	
static void visitarHasNot(A_NODOHASNOT *nodo){
char *exp,exps,*id;

	if (nodo->h1!=NULL) visitar(nodo->h1);exp=expTexto;exps=expSimple;
	if (nodo->h2!=NULL) visitar(nodo->h2);id=expTexto;
	
	if (exps){
		expTexto=concat("new boolean_class_(!hasmember(",exp,",\"",id,"_\"))",NULL);
		expSimple=true;
	}
	else{
		expTexto=concat(exp,";\ntemp=new boolean_class_(!hasmember(" TEMPVAR ",\"",id,"_\"))",NULL);
		expSimple=false;
	}
}
	
static void visitarIs(A_NODOIS *nodo){
char *exp,exps,*id;

	if (nodo->h1!=NULL) visitar(nodo->h1);exp=expTexto;exps=expSimple;
	if (nodo->h2!=NULL) visitar(nodo->h2);id=expTexto;
	
	if (exps){
		expTexto=concat("new boolean_class_(hasparent(",exp,",",id,"_class_.class))",NULL);
		expSimple=true;
	}
	else{
		expTexto=concat(exp,";\ntemp=new boolean_class_(hasparent(" TEMPVAR ",",id,"_class_.class))",NULL);
		expSimple=false;
	}
}
	
static void visitarIsNot(A_NODOISNOT *nodo){
char *exp,exps,*id;

	if (nodo->h1!=NULL) visitar(nodo->h1);exp=expTexto;exps=expSimple;
	if (nodo->h2!=NULL) visitar(nodo->h2);id=expTexto;
	
	if (exps){
		expTexto=concat("new boolean_class_(!hasparent(",exp,",",id,"_class_.class))",NULL);
		expSimple=true;
	}
	else{
		expTexto=concat(exp,";\ntemp=new boolean_class_(!hasparent(" TEMPVAR ",",id,"_class.class_))",NULL);
		expSimple=false;
	}
}
	
	/**********/
	/* return */
	/**********/

static void visitarReturn(A_NODORETURN *nodo){
	visitar(nodo->h1);
	
	if (expSimple)
		expTexto=concat("return ",expTexto,NULL);
	else
		expTexto=concat(expTexto,";\nreturn " TEMPVAR "",NULL);
		
	expSimple=false;

	e_warning(&nodo->posicion,"La traduccion de expresiones 'return' no esta completamente\n"
			"implementada; es posible que aparezcan errores de alcanzabilidad de\n"
			"sentencias en el programa objeto"
		);

}
	
	/*****************/
	/* If-Else       */
	/*****************/

static void visitarIf(A_NODOIF *nodo){
char *e1,*e2,*e3,es1,es2,es3;

	visitar(nodo->h1);e1=expTexto;es1=expSimple;
	visitar(nodo->h2);e2=expTexto;es2=expSimple;
	if (nodo->h3!=NULL) {visitar(nodo->h3);e3=expTexto;es3=expSimple;}
	else {e3=strdup("null_object_");es3=true;}

	if (es1 && es2 && es3){		
		expTexto=concat("checkboolean(",e1,",\"if\",\"",
			nodo->posicion.fichero,"\",",
			charEntero(nodo->posicion.lineaInicio),",",
			charEntero(nodo->posicion.lineaFin),
			")?(Object)(",e2,"):(Object)(",e3,")",
			NULL);
		expSimple=true;
	}
	else{
		if (es1) 
			expTexto=concat("if (checkboolean(",e1,",\"if\",\"",
				nodo->posicion.fichero,"\",",
				charEntero(nodo->posicion.lineaInicio),",",
				charEntero(nodo->posicion.lineaFin),
				")){\n",NULL);
		else
			expTexto=concat(e1,";\nif (checkboolean(" TEMPVAR ",\"if\",\"",
				nodo->posicion.fichero,"\",",
				charEntero(nodo->posicion.lineaInicio),",",
				charEntero(nodo->posicion.lineaFin),
				")){\n",NULL);
	
		expTexto=concat(expTexto,
				"\t",es2?TEMPVAR "=":"",e2,";\n}\n"
				"else{\n"
				"\t",es3?TEMPVAR "=":"",e3,";\n}\n",
				NULL);
	
		expSimple=false;
	}
	
}

	/**********/
	/* static */
	/**********/
	
static void visitarStatic(A_NODOSTATIC *nodo){	
char *s,*out;
		/* nombre de la variable estática */

	s=concat("static",charPuntero(nodo),NULL);

		/* declaración de la variable estática */
	
	nuevaLinea();fprintf(fp,"private static Object %s=null;\n",s);
	
		/* traducción */
	
	visitar(nodo->h1);
	if (expSimple) expTexto=concat(TEMPVAR "=",expTexto,";\n",NULL);
	else expTexto=concat(expTexto,";\n",NULL);
	
	expTexto=concat("if (",s,"==null){\n",expTexto,s,"=" TEMPVAR ";\n}\nelse " TEMPVAR "=",s,
		NULL);
	
	expSimple=false;
}
	/**********************/
	/* operadores logicos */
	/**********************/

static void visitarNot(A_NODONOT *nodo){
	visitar(nodo->h1);
	if (expSimple)
		expTexto=concat("new boolean_class_(!checkboolean(",expTexto,",\"not\",\"",
			nodo->posicion.fichero,"\",",
			charEntero(nodo->posicion.lineaInicio),",",
			charEntero(nodo->posicion.lineaFin),"))",
			NULL);
	else
		expTexto=concat(expTexto,";\ntemp=new boolean_class_(!checkboolean(" TEMPVAR ",\"not\",\"",
			nodo->posicion.fichero,"\",",
			charEntero(nodo->posicion.lineaInicio),",",
			charEntero(nodo->posicion.lineaFin),"))",
			NULL);
		
}
static void visitarAnd(A_NODOAND *nodo){
char *e1,es1,*e2,es2;	
	visitar(nodo->h1);e1=expTexto;es1=expSimple;
	visitar(nodo->h2);e2=expTexto;es2=expSimple;

	if (es1 && es2){
		expTexto=concat("new boolean_class_(checkboolean(",e1,",\"and\",\"",
			nodo->posicion.fichero,"\",",
			charEntero(nodo->posicion.lineaInicio),",",
			charEntero(nodo->posicion.lineaFin),") && checkboolean(",e2,",\"and\",\"",
			nodo->posicion.fichero,"\",",
			charEntero(nodo->posicion.lineaInicio),",",
			charEntero(nodo->posicion.lineaFin),"))",
			NULL);
		expSimple=true;
	}
	else{
		if (es1)
			expTexto=concat("if (checkboolean(",e1,",\"and\",\"",
			nodo->posicion.fichero,"\",",
			charEntero(nodo->posicion.lineaInicio),",",
			charEntero(nodo->posicion.lineaFin),")){\n",NULL);
		else
			expTexto=concat(e1,";\nif (checkboolean(" TEMPVAR ",\"and\",\"",
			nodo->posicion.fichero,"\",",
			charEntero(nodo->posicion.lineaInicio),",",
			charEntero(nodo->posicion.lineaFin),")){\n",NULL);

		if (es2)
			expTexto=concat(expTexto,"\tif (checkboolean(",e2,",\"and\",\"",
			nodo->posicion.fichero,"\",",
			charEntero(nodo->posicion.lineaInicio),",",
			charEntero(nodo->posicion.lineaFin),"))\n",NULL);
		else
			expTexto=concat(expTexto,"\t",e2,";\n\tif (checkboolean(" TEMPVAR ",\"and\",\"",
			nodo->posicion.fichero,"\",",
			charEntero(nodo->posicion.lineaInicio),",",
			charEntero(nodo->posicion.lineaFin),"))\n",NULL);
			
		expTexto=concat(expTexto,"\t\ttemp=new boolean_class_(true);\n\telse\n\t\ttemp=new boolean_class_(false);\n\t}\nelse\n\ttemp=new boolean_class_(false)",NULL);
		
		expSimple=false;			
	}
}

static void visitarOr(A_NODOOR *nodo){
char *e1,es1,*e2,es2;	
	visitar(nodo->h1);e1=expTexto;es1=expSimple;
	visitar(nodo->h2);e2=expTexto;es2=expSimple;

	if (es1 && es2){
		expTexto=concat("new boolean_class_(checkboolean(",e1,",\"or\",\"",
			nodo->posicion.fichero,"\",",
			charEntero(nodo->posicion.lineaInicio),",",
			charEntero(nodo->posicion.lineaFin),") || checkboolean(",e2,",\"or\",\"",
			nodo->posicion.fichero,"\",",
			charEntero(nodo->posicion.lineaInicio),",",
			charEntero(nodo->posicion.lineaFin),"))",
			NULL);
		expSimple=true;
	}
	else{
		if (es1)
			expTexto=concat("if (checkboolean(",e1,",\"or\",\"",
			nodo->posicion.fichero,"\",",
			charEntero(nodo->posicion.lineaInicio),",",
			charEntero(nodo->posicion.lineaFin),"))\n",NULL);
		else
			expTexto=concat(e1,";\nif (checkboolean(" TEMPVAR ",\"or\",\"",
			nodo->posicion.fichero,"\",",
			charEntero(nodo->posicion.lineaInicio),",",
			charEntero(nodo->posicion.lineaFin),"))\n",NULL);

		expTexto=concat(expTexto,"\ttemp=new boolean_class_(true);\nelse{\n",NULL);
			
		if (es2)
			expTexto=concat(expTexto,"\t\tif (checkboolean(",e2,",\"or\",\"",
			nodo->posicion.fichero,"\",",
			charEntero(nodo->posicion.lineaInicio),",",
			charEntero(nodo->posicion.lineaFin),"))\n",NULL);
		else
			expTexto=concat(expTexto,"\t\t",e2,";\n\t\tif (checkboolean(" TEMPVAR ",\"or\",\"",
			nodo->posicion.fichero,"\",",
			charEntero(nodo->posicion.lineaInicio),",",
			charEntero(nodo->posicion.lineaFin),"))\n",NULL);
			
		expTexto=concat(expTexto,"\t\t\ttemp=new boolean_class_(true);\n\t\telse\n\t\t\ttemp=new boolean_class_(false);\n\t}",NULL);
		
		expSimple=false;			
	}
}
	
	/******************/
	/* While/Do-While */
	/******************/

	/* Subpatrón visitante para la comprobación de ocurrencias de declaración de */
	/* variables locales dentro de la condición de un bucle while/do-while       */
	
static char hayLet;	
	
static void visitarWhile_(A_NODO *nodo);
static void visitarWhile_Let(A_NODOLET *nodo){
	hayLet=1;
	if (nodo->h1!=NULL) visitarWhile_(nodo->h1);
	if (nodo->h2!=NULL) visitarWhile_(nodo->h2);
}
static void visitarWhile_Nodo0(A_NODO0 *nodo){
}
static void visitarWhile_Nodo1(A_NODO1 *nodo){
	if (nodo->h1!=NULL) visitarWhile_(nodo->h1);
}
static void visitarWhile_Nodo2(A_NODO2 *nodo){
	if (nodo->h1!=NULL) visitarWhile_(nodo->h1);
	if (nodo->h2!=NULL) visitarWhile_(nodo->h2);
}
static void visitarWhile_Nodo3(A_NODO3 *nodo){
	if (nodo->h1!=NULL) visitarWhile_(nodo->h1);
	if (nodo->h2!=NULL) visitarWhile_(nodo->h2);
	if (nodo->h3!=NULL) visitarWhile_(nodo->h3);
}
static void visitarWhile_Nodo4(A_NODO4 *nodo){
	if (nodo->h1!=NULL) visitarWhile_(nodo->h1);
	if (nodo->h2!=NULL) visitarWhile_(nodo->h2);
	if (nodo->h3!=NULL) visitarWhile_(nodo->h3);
	if (nodo->h4!=NULL) visitarWhile_(nodo->h4);
}

static char initTablaVisWhile_=true;
static void *tablaVisitanteWhile_[A_TIPO_SIZE];
static void inicializarTablaVisitanteWhile_(void){
int i;
	for(i=0;i<A_TIPO_SIZE;i++) tablaVisitanteWhile_[i]=NULL;
	tablaVisitanteWhile_[A_LET]=&visitarWhile_Let;
	tablaVisitanteWhile_[A_RETURN]=&visitarWhile_Nodo1;
	tablaVisitanteWhile_[A_STATIC]=&visitarWhile_Nodo1;
	tablaVisitanteWhile_[A_NOT]=&visitarWhile_Nodo1;
	tablaVisitanteWhile_[A_LITERALSECUENCIA]=&visitarWhile_Nodo1;
	tablaVisitanteWhile_[A_OR]=&visitarWhile_Nodo2;
	tablaVisitanteWhile_[A_AND]=&visitarWhile_Nodo2;
	tablaVisitanteWhile_[A_IDENTICO]=&visitarWhile_Nodo2;
	tablaVisitanteWhile_[A_NOIDENTICO]=&visitarWhile_Nodo2;
	tablaVisitanteWhile_[A_IS]=&visitarWhile_Nodo2;
	tablaVisitanteWhile_[A_ISNOT]=&visitarWhile_Nodo2;
	tablaVisitanteWhile_[A_HAS]=&visitarWhile_Nodo2;
	tablaVisitanteWhile_[A_HASNOT]=&visitarWhile_Nodo2;
	tablaVisitanteWhile_[A_IDATOMICO]=&visitarWhile_Nodo2;
	tablaVisitanteWhile_[A_LITERALSECUENCIALISTA]=&visitarWhile_Nodo2;
	tablaVisitanteWhile_[A_IDLISTA]=&visitarWhile_Nodo2;
	tablaVisitanteWhile_[A_DOWHILE]=&visitarWhile_Nodo2;
	tablaVisitanteWhile_[A_WHILE]=&visitarWhile_Nodo2;
	tablaVisitanteWhile_[A_ASIGNACION]=&visitarWhile_Nodo2;
	tablaVisitanteWhile_[A_PARAMETROSLISTA]=&visitarWhile_Nodo2;
	tablaVisitanteWhile_[A_SECUENCIA]=&visitarWhile_Nodo2;
	tablaVisitanteWhile_[A_RESEND]=&visitarWhile_Nodo3;
	tablaVisitanteWhile_[A_DECLARACIONLISTA]=&visitarWhile_Nodo3;
	tablaVisitanteWhile_[A_IF]=&visitarWhile_Nodo3;
	tablaVisitanteWhile_[A_ACCESOMIEMBRO]=&visitarWhile_Nodo3;
}

static void visitarWhile_(A_NODO *nodo){
void (*ptr)(void *n);
	if (nodo!=NULL){
		if (nodo->tipo<0 || nodo->tipo>=A_TIPO_SIZE)
			e_fatal(NULL,"Se ha encontrado un nodo desconocido recorriendo el arbol\n"
				"sintactico abstracto");
	
		ptr=tablaVisitanteWhile_[nodo->tipo];	
		if (ptr!=NULL) ptr(nodo);
	}
}

	/* while */

static void visitarWhile(A_NODOWHILE *nodo){
char *e1,es1,*e2,es2;	

	visitar(nodo->h1);e1=expTexto;es1=expSimple;
	visitar(nodo->h2);e2=expTexto;es2=expSimple;
	
	if (es1) 
		expTexto=concat("while (checkboolean(",e1,",\"while\",\"",
			nodo->posicion.fichero,"\",",
			charEntero(nodo->posicion.lineaInicio),",",
			charEntero(nodo->posicion.lineaFin),")){\n",
			NULL);
	else
		expTexto=concat(e1,";\nwhile (checkboolean(" TEMPVAR ",\"while\",\"",
			nodo->posicion.fichero,"\",",
			charEntero(nodo->posicion.lineaInicio),",",
			charEntero(nodo->posicion.lineaFin),")){\n",
			NULL);
	
	expTexto=concat(expTexto,
		"\t",
		(es2?TEMPVAR "=":""),e2,";\n",
		(es1?"":e1),";\n}\ntemp=null_object_",NULL);	
	expSimple=false;

	hayLet=0;
	if (initTablaVisWhile_){inicializarTablaVisitanteWhile_();initTablaVisWhile_=false;}
	visitarWhile_(nodo->h1);
	if (hayLet)
	e_warning(&nodo->posicion,"La traducción de bucles 'while', 'do-while' y 'for' no esta\n"
			"completamente implementada; la declaracion de variables locales en la\n" "condicion de iteracion de bucles 'while' puede provocar errores en el\n"
			"programa objeto");
}

static void visitarDoWhile(A_NODODOWHILE *nodo){
char *e1,es1,*e2,es2;	

	visitar(nodo->h1);e1=expTexto;es1=expSimple;	/* bucle */
	visitar(nodo->h2);e2=expTexto;es2=expSimple; 	/* condicion */
	
	expTexto=concat("do{\n\t",
		(es1?TEMPVAR "=":""),e1,";\n",
		(es2?"":e2),";\n}\n",
		"while (checkboolean(",es2?e2:TEMPVAR "",",\"while\",\"",
		nodo->posicion.fichero,"\",",
		charEntero(nodo->posicion.lineaInicio),",",
		charEntero(nodo->posicion.lineaFin),"));\ntemp=null_object_",
		NULL);
	expSimple=false;
	
	hayLet=0;
	if (initTablaVisWhile_){inicializarTablaVisitanteWhile_();initTablaVisWhile_=false;}
	visitarWhile_(nodo->h2);
	if (hayLet)
	e_warning(&nodo->posicion,"La traducción de bucles 'while', 'do-while' y 'for' no esta\n"
			"completamente implementada; la declaracion de variables locales en la\n" "condicion de iteracion de bucles 'while' puede provocar errores en el\n"
			"programa objeto");
}
	
	/*****************/
	/* Break/Continue*/
	/*****************/

static void visitarBreak(A_NODOBREAK *nodo){
	expTexto=strdup(TEMPVAR "=null_object_;break");
	expSimple=false;
}
	
static void visitarContinue(A_NODOCONTINUE *nodo){
	expTexto=strdup(TEMPVAR "=null_object_;continue");
	expSimple=false;
}
	
	/*****************/
	/* resend        */
	/*****************/

static void visitarResend(A_NODORESEND *nodo){
char *miembro,*padre,*s;
unsigned char parametros;
int i;
	
	if (nodo->h1!=NULL) {visitar(nodo->h1);miembro=expTexto;}
	else miembro=nodoMiembro->id;
	
	if (nodo->h2!=NULL) {visitar(nodo->h2);padre=expTexto;}
	else padre=NULL;
	
	if (nodo->h3!=NULL) {
		visitar(nodo->h3);
		parametros=contarParametros(((A_NODOPARAMETROSLISTA *)(nodo->h3)));
	}
	else {
		subExp=NULL;
		parametros=0;
	}
	
	if (padre==NULL || (nodoObjeto->mod&A_MODHERENCIAMULTIPLE)==0){
		s=concat("super.",miembro,"_member_",(parametros?charEntero(parametros):""),(parametros?"_":""),"(",subExpPars(subExp,nTempVars),")",NULL);
		if (subExpSimple(subExp))
			expTexto=s;
		else
			expTexto=concat(subExpAsig(subExp,nTempVars),TEMPVAR "=",s,NULL);
	}
	else {
		s=concat("super.",miembro,"_member_",(parametros?charEntero(parametros):""),(parametros?"_":""),"(",subExpPars(subExp,nTempVars),")",NULL);
		if (subExpSimple(subExp))
			expTexto=s;
		else
			expTexto=concat(subExpAsig(subExp,nTempVars),TEMPVAR "=",s,NULL);
			
		e_warning(&nodo->posicion,"La traduccion de expresiones 'resend' no esta completamente\n"
			"implementada en el caso de herencia múltiple; es posible que\n"
			"el comportamiento del lenguaje objeto no sea el esperado"
			);
	}
	
	expSimple=subExpSimple(subExp);
	
	nTempVars+=subExpNTempVars(subExp);	
	//freePar(subExp);
	
}

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

static void *tablaVisitante[A_TIPO_SIZE];

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

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_BREAK]=&visitarBreak;
	tablaVisitante[A_CONTINUE]=&visitarContinue;
	tablaVisitante[A_NOT]=&visitarNot;
	tablaVisitante[A_OR]=&visitarOr;
	tablaVisitante[A_AND]=&visitarAnd;
	tablaVisitante[A_IDENTICO]=&visitarIdentico;
	tablaVisitante[A_NOIDENTICO]=&visitarNoIdentico;
	tablaVisitante[A_IS]=&visitarIs;
	tablaVisitante[A_ISNOT]=&visitarIsNot;
	tablaVisitante[A_HAS]=&visitarHas;
	tablaVisitante[A_HASNOT]=&visitarHasNot;
	tablaVisitante[A_STATIC]=&visitarStatic;
	tablaVisitante[A_IDATOMICO]=&visitarIdAtomico;
	tablaVisitante[A_CUERPOOBJETOLISTA]=&visitarCuerpoObjetoLista;
	tablaVisitante[A_DOWHILE]=&visitarDoWhile;
	tablaVisitante[A_WHILE]=&visitarWhile;
	tablaVisitante[A_ARGUMENTOSLISTA]=&visitarArgumentosLista;
	tablaVisitante[A_ASIGNACION]=&visitarAsignacion;
	tablaVisitante[A_PARAMETROSLISTA]=&visitarParametrosLista;
	tablaVisitante[A_SECUENCIA]=&visitarSecuencia;
	tablaVisitante[A_RESEND]=&visitarResend;
	tablaVisitante[A_DECLARACIONLISTA]=&visitarDeclaracionLista;
	tablaVisitante[A_IF]=&visitarIf;
	tablaVisitante[A_RETURN]=&visitarReturn;
	tablaVisitante[A_ACCESOMIEMBRO]=&visitarAccesoMiembro;
	tablaVisitante[A_MIEMBROEXTERNO]=&visitarMiembroExterno;
	tablaVisitante[A_LITERALSECUENCIA]=&visitarLiteralSecuencia;
	tablaVisitante[A_LITERALSECUENCIALISTA]=&visitarLiteralSecuenciaLista;
	tablaVisitante[A_LITERALCADENA]=&visitarLiteralCadena;
	tablaVisitante[A_LITERALENTERO]=&visitarLiteralEntero;
	tablaVisitante[A_LITERALREAL]=&visitarLiteralReal;
	tablaVisitante[A_LITERALTRUE]=&visitarLiteralTrue;
	tablaVisitante[A_LITERALFALSE]=&visitarLiteralFalse;
	tablaVisitante[A_ID]=&visitarId;
}

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

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

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

void j_trad(A_NODO *ast,char *ficheroSalida){
char *tempNombrePrograma;
char *nombrePrograma;

		/* Inicializacion de tablas */

	inicializarNativos();
	inicializarTablaVisitante();

		/* Abrir fichero */
	
	fp=fopen(ficheroSalida,"wt");
	if (fp==NULL) e_fatal(NULL,"Error al abrir '%s' para escritura",ficheroSalida);
	fptab=0;		
	
		/* Obtener nombre de la clase Java (remover path y extension) */
	
	tempNombrePrograma=strdup(ficheroSalida);
	nombrePrograma=rindex(tempNombrePrograma,'/');
	if (nombrePrograma==NULL) nombrePrograma=tempNombrePrograma;
	else nombrePrograma++;
	if (rindex(nombrePrograma,'.')!=NULL) *rindex(nombrePrograma,'.')=0;		

		/* Declaracion de la clase java */
	
	nuevaLinea();
	imprimir("public class ");
	imprimir(nombrePrograma);	
	imprimir("{\n\n");
											
		/* Traduccion del programa */
	
	fptab++;
	visitar(ast);
	fptab--;
		
		/* Miembros auxiliares */

	nuevaLinea();
	imprimir2(
		"\t/**************/\n"
		"\t/* (internal) */\n"
		"\t/**************/\n"
		"\n"
		"\tstatic public boolean hasmember(Object x,String miembro){\n"
		"\t\ttry{\n"
		"\t\t\tjava.lang.reflect.Field f=x.getClass().getField(miembro);\n"
		"\t\t\t	return true;\n"
		"\t\t}\n"
		"\t\tcatch(Exception e){\n"
		"\t\t\treturn false;\n"
		"\t\t}\n"
		"\t}\n"
		"\n"
		"\tstatic public boolean hasparent(Object x,Class y){\n"
		"\t\ttry{\n"
		"\t\t\tif (x.getClass()==y) return true;\n"
            	"\t\t\tjava.lang.reflect.Method m=x.getClass().getMethod(\"parents__\",new Class[]{});\n"
            	"\t\t\tObject[] a=(Object[])m.invoke(x,new Object[]{});\n"
		"\t\t\tfor (int i=0;i<a.length;i++){\n"
		"\t\t\t\tif (a[i]==y)\n"
		"\t\t\t\t\treturn true;\n"
		"\t\t\t\t}\n"
		"\t\t\treturn false;\n"
		"\t\t}\n"
		"\t\tcatch(Exception e){\n"
		"\t\t\treturn false;\n"
		"\t\t}\n"
		"\t}\n"
		"\n"
		"\tstatic public String getname(Object x,String fich,int posInicio,int posFin){\n"
        	"\t\ttry{\n"
            	"\t\t\tjava.lang.reflect.Method m=x.getClass().getMethod(\"id__\",new Class[]{});\n"
            	"\t\t\tObject a=m.invoke(x,new Object[]{});\n"
		"\t\t\tif (!(a instanceof String)){System.err.println(fich+\":\"+posInicio+(posInicio==posFin?\"\":\"-\"+posFin)+\": \"+\"Se ha intentado acceder a un objeto generico Java de estructura desconocida\");System.exit(1);return null;}\n"
		"\t\t\treturn (String)a;\n"
        	"\t\t}\n"
		"\t\tcatch(Exception e){System.err.println(fich+\":\"+posInicio+(posInicio==posFin?\"\":\"-\"+posFin)+\": \"+\"Se ha intentado acceder a un objeto generico Java de estructura desconocida\");System.exit(1);return null;}\n"
		"\t}\n"
		"\n"
		"\tstatic public Object eval(Object objeto,String miembro,int npar,Object[] parametros,String fich,int posInicio,int posFin){\n"
		"\t\ttry{\n"
		"\t\t\tString par=npar==0?\"\":npar+\"_\";\n"
		"\t\t\t\n"
		"\t\t\t\t/* Atributo */\n"
		"\t\t\t\n"
		"\t\t\tjava.lang.reflect.Field f=objeto.getClass().getField(miembro+\"_\"+par);\n"
		"\t\t\tObject x=f.get(objeto);\n"
		"\t\t\tif (x!=null) return x;\n"
		"\t\t\t\n"
		"\t\t\t\t/* Metodo */\n"
		"\t\t\t\n"
		"\t\t\tClass[] c=new Class[npar];\n"
		"\t\t\tfor (int j=0;j<npar;j++) c[j]=Object.class;\n"
		"\t\t\tjava.lang.reflect.Method m=objeto.getClass().getMethod(miembro+\"_\"+par+\"member_\",c);\n"
		"\t\t\treturn m.invoke(objeto,parametros);\n"
		"\t\t}\n"
		"\t\tcatch(Exception e){\n"
		"\t\t\tSystem.err.println(\n"
		"\t\t\t\tfich+\":\"+posInicio+(posInicio==posFin?\"\":\"-\"+posFin)+\": \"+\"No se puede acceder al miembro '\"+getname(objeto,fich,posInicio,posFin)+\".\"+miembro+\"'\"\n"
		"\t\t\t\t);\n"
		"\t\t\tSystem.exit(1);\n"
		"\t\t\treturn null;\n"
		"\t\t}\n"
		"\t}\n"
		"\t\n"
		"\tstatic public Object asig(Object objeto,String miembro,int parametros,Object valor,String fich,int posInicio,int posFin){\n"
		"\t	java.lang.reflect.Field f;\n"
		"\t\ttry{\n"
		"\t\t\tf=objeto.getClass().getField(miembro+\"_\"+(parametros==0?\"\":parametros+\"_\"));\n"
		"\t\t}\n"
		"\t\tcatch(Exception e){\n"
		"\t\t\tSystem.err.println(\n"
		"\t\t\t\tfich+\":\"+posInicio+(posInicio==posFin?\"\":\"-\"+posFin)+\": \"+\"No se puede al acceder al miembro '\"+getname(objeto,fich,posInicio,posFin)+\".\"+miembro+\"'\"\n"
		"\t\t\t\t);\n"
		"\t\t\tSystem.exit(1);\n"
		"\t\t\treturn null;\n"
		"\t\t}\n"
		"\t\ttry{\n"
		"\t\t\tf.set(objeto,valor);\n"
		"\t\t}\n"
		"\t\tcatch(Exception ee){\n"
		"\t\t\tSystem.err.println(\n"
		"\t\t\t\tfich+\":\"+posInicio+(posInicio==posFin?\"\":\"-\"+posFin)+\": \"+\"Error al modificar el contenido del miembro '\"+getname(objeto,fich,posInicio,posFin)+\".\"+miembro+\"'\"\n"
		"\t\t\t\t);\n"
		"\t\t\tSystem.exit(1);\n"
		"\t\t\treturn null;\n"
		"\t\t}\n"
		"\t\treturn valor;\n"
		"\t}\n"
		"\n"
		"\tstatic public boolean checkboolean(Object objeto,String operador,String fich,int posInicio,int posFin){\n"
		"\t\ttry{\n"
		"\t\t\tjava.lang.reflect.Field f=objeto.getClass().getField(\"value__\");\n"
		"\t\t\treturn f.getBoolean(objeto);\n"
		"\t\t}\n"
		"\t\tcatch(Exception e){\n"
		"\t\t\tString temp=\"\";\n"
		"\t\t\tif (operador.equals(\"if\"))\n"
		"\t\t\t\ttemp=\"La condicion de un operador 'if' debe tener tipo 'native boolean'\";\n"
		"\t\t\telse if (operador.equals(\"while\"))\n"
		"\t\t\t\ttemp=\"La condicion de un bucle 'while' debe tener tipo 'native boolean'\";\n"
		"\t\t\telse if (operador.equals(\"dowhile\"))\n"
		"\t\t\t\ttemp=\"La condicion de un bucle 'do-while' debe tener tipo 'native boolean'\";\n"
		"\t\t\telse\n"
		"\t\t\t\ttemp=\"El operador '\"+operador+\"' solo puede aplicarse a operandos de tipo 'native boolean'\";\n"
		"\t\t\tSystem.err.println(\n"
		"\t\t\t\tfich+\":\"+posInicio+(posInicio==posFin?\"\":\"-\"+posFin)+\": \"+temp\n"
		"\t\t\t\t);\n"
		"\t\t\tSystem.exit(1);\n"
		"\t\t\treturn false;\n"
		"\t\t}\n"
		"\t}\n"
		);
	imprimir("\n");	
		
		/* Miembro principal Java */

	imprimir2(
		"\t/**********/\n"
		"\t/* (main) */\n"
		"\t/**********/\n"
		"\t\n"
		"\tstatic public void main(String args[]){\n"
		"\t\t(new program()).main_member_();\n"
		"\t}\n"
		);
	imprimir("\n");	
		
		/* Cerrar declaración de la clase Java */
		
	nuevaLinea();
	imprimir("}\n");
	
	fclose(fp);
	//free(tempNombrePrograma);
}
	
	
