//*******************************************************************************
//lexer.l
//*******************************************************************************
//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 lexico                                                     */
/***********************************************************************/

%option noyywrap
%option stack

%{
#include <stdio.h>			
#include <string.h>
#include <unistd.h>
#include <libgen.h>
#include "error.h"
#include "ast.h"
#include "lexer.h"
#define  YYSTYPE L_TOKEN	/* (definir antes de incluir y.tab.h) */
#include "y.tab.h"		
#include "tds.h"

/*********************************************/
/* Variables globales y funciones auxiliares */
/*********************************************/
	
	/*****************************/
	/* Posicion del token actual */    
	/*****************************/

static E_POSICION 	posicion={-1,-1,NULL};	/* Posicion del token actual */

	/************************/
	/* Comentarios Anidados */
	/************************/
	
static int profundidad_comentario;

	/**********************/
	/* Directiva #Include */
	/**********************/

#define INCLUDESTACKMAXDEPTH 256	/* numero maximo de directivas #include anidadas */

static struct{				
	char *fichero;			/* Fichero actual */
	int lineaInicio;		/* Linea en la que se encontro la directiva include */
	int indent;			/* Sangrado en el que se encontro la directiva include */
	char *cwd;			/* Directorio de trabajo (current working directory)*/
	YY_BUFFER_STATE	estado;		/* Estado interno del analizador */
} 
includeStack[INCLUDESTACKMAXDEPTH];	/* Pila de estado */

static int includeStackPtr;		/* Puntero al estado actual */

	/**********************/
	/* Sangrado           */
	/**********************/

#define TABSIZE	8
	
#define INDENTMAX 1024			/* numero maximo de niveles de sangrado */

static int indentStack[INDENTMAX];	/* Pila de niveles de sangrado */

static int indentStackPtr;		/* Puntero al nivel actual */

static int indent;			/* Contador de sangrado */

static char unirLineas;			/* forzar union de líneas */

					/*    unirLineas=1 para descartar marcas de sangrado */
					/*    unirLineas=2 para descartar sólamente en caso */
					/* 		de NEWLINE (primera linea de archivo)*/ 

static int marcaUnionLineas;		/* indica si hubo marca reciente de union de linea */
					/* y guarda el número de línea (sólo permitir marca */
					/* de unión de líneas tras último token) */
						
	/*************************/
	/* Cadenas de caracteres */
	/*************************/
		
static	int 	maxTempCad=1024;
static	char 	*tempCad;
static 	int	tempCadPos=0;

static void newTempCad(){
	tempCadPos=0;
}

static void addTempCad(char *s){
	int i,k;
	char *newTempCad;
	
	i=0;
	while(s[i]!=0){
		while(tempCadPos<maxTempCad && s[i]!=0)
			tempCad[tempCadPos++]=s[i++];
			
		if (s[i]!=0){
			if ((newTempCad=(char *)malloc(maxTempCad*2))==NULL)
				e_fatal(NULL,"Sin memoria durante el analisis lexico");
			
			for(k=0;k<maxTempCad;k++)
				newTempCad[k]=tempCad[k];
			
			free(tempCad);
			
			tempCad=newTempCad;
			maxTempCad*=2;
		}
	}
}

static char *copiaTempCad(){
	int i;
	char *temp;
	
	if ((temp=(char *)malloc(tempCadPos+1))==NULL)
		e_fatal(NULL,"Sin memoria durante el analisis lexico");
	
	for(i=0;i<tempCadPos;i++)
		temp[i]=tempCad[i];
		
	temp[i]=0;
	return temp;
}

	/**********/
	/* Macros */
	/**********/
		
#define TOKEN(x) 	unirLineas=0;\
			yylval.posicion.lineaInicio=posicion.lineaInicio;\
			yylval.posicion.lineaFin=posicion.lineaFin;\
			yylval.posicion.fichero=posicion.fichero;\
			return x

/*************************/
/* Expresiones regulares */
/*************************/
%}

digito			[0-9]
digitoBinario		[0-1]
digitoDecimal		[0-9]
digitoOctal		[0-7]
digitoHexadecimal 	[0-9a-f]
letra			[a-z]
espacio			[ ]
tabulador		\t
formfeed		\f
cualquierEspacio	[ \t\f]
eol			\n

literalHexadecimal 	0x{digitoHexadecimal}+
literalOctal		0o{digitoOctal}+
literalBinario		0b{digitoBinario}+
literalDecimal		{digitoDecimal}+

literalFlotante		({digitoDecimal}+"."{digitoDecimal}*{exponente}?)|("."{digitoDecimal}+{exponente}?)|({digitoDecimal}+{exponente}) 

exponente		e[+-]?{digitoDecimal}+

identificador		({letra}|_)+({letra}|{digito})*

%x comentario directiva include cadena caracter sangrado

%%

 /*********************/
 /* Analizador lexico */
 /*********************/
 
			/**********************************************************/
			/******************* espacios y fin de linea **************/
			/**********************************************************/
				
<INITIAL>{cualquierEspacio}+ 	;

<sangrado>{espacio}+ 	{
				indent+=yyleng;
			}
			
<sangrado>{tabulador}	{
				indent=((indent/TABSIZE)+1)*TABSIZE;
			}

<sangrado>{formfeed}+	;

<INITIAL,sangrado>\\	{
			unirLineas=1;marcaUnionLineas=posicion.lineaInicio;
			}

<INITIAL,sangrado>{eol}	{
				posicion.lineaInicio++;
				posicion.lineaFin=posicion.lineaInicio;
				if (includeStackPtr==0) indent=0;
				else indent=includeStack[includeStackPtr-1].indent;
				BEGIN(sangrado);
			}
								
			/**********************************************************/
			/******************* comentarios **************************/
			/**********************************************************/
			
<INITIAL,sangrado>"//".* ;

<INITIAL,sangrado>"/*"	{
				indent+=2;
				posicion.lineaFin=posicion.lineaInicio;
				profundidad_comentario=0;
				yy_push_state(comentario);	/* BEGIN(comentario)*/
			}
<INITIAL,sangrado>"*/"	{
				indent+=2;
				e_error(&posicion,"Se ha encontrado un cierre de comentario sin la apertura correspondiente");
			}
<comentario>{tabulador}	{
				indent=((indent/TABSIZE)+1)*TABSIZE;
			}
<comentario>{formfeed}	;
<comentario>[^*/\t\f\n]+ {
				indent+=yyleng;
			}
<comentario>"/*"	{
				indent+=yyleng;
				profundidad_comentario++;
			}
<comentario>"*/"	{
				indent+=yyleng;
				if (profundidad_comentario==0){
					posicion.lineaInicio=posicion.lineaFin;
					yy_pop_state();		/*BEGIN(INITIAL);*/
				}
				else
					profundidad_comentario--;
			}
<comentario>"*"		{
				indent++;
			}
<comentario>"/"		{
				indent++;
			}
<comentario>{eol}+	{
				posicion.lineaFin+=yyleng;
				if (includeStackPtr==0) indent=0;
				else indent=includeStack[includeStackPtr-1].indent;
			}
<comentario><<EOF>>	{
				e_error(&posicion,"Se ha encontrado fin de archivo procesando un comentario");
				posicion.lineaInicio=posicion.lineaFin;
				yy_pop_state();		/*BEGIN(INITIAL);*/
			}
			
			/**********************************************************/
			/******************* directiva de inclusion****************/
			/**********************************************************/
			
<INITIAL>"#include"{espacio}*"\""	{
					posicion.lineaFin=posicion.lineaInicio;
					newTempCad();
					BEGIN(include);
					}
			
<include>\"		{
				char *s,*cwd,*t;
				FILE *fp;

					/* Ajustar el camino (path) del fichero para que este */
					/* sea relativo al directorio de trabajo actual */
				
				s=copiaTempCad();
				t=strdup(posicion.fichero);
				if (t==NULL)
					e_fatal(&posicion,"Sin memoria durante el analisis lexico");
				newTempCad();
				addTempCad(dirname(t));
				addTempCad("/");
				addTempCad(s);
				free(s);
				free(t);
				s=copiaTempCad();
				
					/* Comprobar que el fichero a incluir es valido */
							
				if ((fp=fopen(s,"r"))==NULL){
					
						/* Fichero invalido */

					e_error(&posicion,"Error al abrir \"%s\" para lectura\n",s);
					free(s);
					posicion.lineaInicio=posicion.lineaFin;			
					BEGIN(INITIAL);
				}
				else{
						/* Fichero valido */
				
					if (includeStackPtr>=INCLUDESTACKMAXDEPTH)
						e_fatal(&posicion,"Nivel de anidamiento de directiva #include excedido");
						
						/* Cambiar buffer */
						
					yyin=fp;
					includeStack[includeStackPtr].estado=YY_CURRENT_BUFFER;
											
						/* Guardar directorio de trabajo */
					
					includeStack[includeStackPtr].cwd=cwd;
						
						/* Guardar control de posiciones */
					
					includeStack[includeStackPtr].fichero=posicion.fichero;
					includeStack[includeStackPtr].lineaInicio=posicion.lineaInicio;
					posicion.lineaInicio=posicion.lineaFin=1;
					posicion.fichero=s;
					yy_switch_to_buffer(yy_create_buffer(yyin,YY_BUF_SIZE));
						
						/* Guardar sangrado */
					
					includeStack[includeStackPtr].indent=indent;
										
						/* Avanzar a siguiente buffer */
						
					includeStackPtr++;					

					/*indent=indent*/					
					unirLineas=2;marcaUnionLineas=-1;
					BEGIN(sangrado);
				}
			}
			
			/**********************************************************/
			/******************* fin de archivo ***********************/
			/**********************************************************/

<INITIAL,sangrado><<EOF>> {

				if(--includeStackPtr<0){			
					if (indentStackPtr!=0){
						indentStackPtr--;
						TOKEN(L_DEDENT);
					}
					
					yyterminate();
				}
				else{
					/* recuperar buffer anterior */
					
					yy_delete_buffer(YY_CURRENT_BUFFER);
					yy_switch_to_buffer(includeStack[includeStackPtr].estado);
					//free(posicion.fichero); <--- no se debe liberar memoria
					//  con cadena de fichero, ya que es la que se anota en
					//  los tokens y luego es utilizada para la salida de 
					//  errores
					
					/* recuperar control de posiciones anterior */
					
					posicion.fichero=includeStack[includeStackPtr].fichero;
					posicion.lineaInicio=
					posicion.lineaFin=
						includeStack[includeStackPtr].lineaInicio;
					
					indent=includeStack[includeStackPtr].indent;
				}
			}
			
			/**********************************************************/
			/******************* Sangrado *****************************/
			/**********************************************************/
			
<sangrado>.		{	
				E_POSICION pos;

				yyless(0);	/* descartar entrada */
				
						/* comprobar que la marca de union de lineas */
						/* era el ultimo token de la línea */
				
				if (unirLineas==0 && marcaUnionLineas!=-1){
					pos.lineaInicio=marcaUnionLineas;
					pos.lineaFin=marcaUnionLineas;
					pos.fichero=posicion.fichero;
					e_error(&pos,"'\\' sólo puede aparecer antes de fin de línea");
				}
				marcaUnionLineas=-1;
				
						/* forzar union de lineas */
				
				if (unirLineas==1){
					BEGIN(INITIAL);
				}
				
						/* (feedback auxiliar de marca indent) */
				
				else if (indent==-1){
					BEGIN(INITIAL);
					TOKEN(L_INDENT);
				}
				
						/* newline */
				
				else if (indent==indentStack[indentStackPtr]) {
					BEGIN(INITIAL);
					if (unirLineas==2){
						unirLineas=0;
					}
					else{
						TOKEN(L_NEWLINE);
					}
				}
						
						/* indent */
				
				else if (indent>indentStack[indentStackPtr]){
					indentStackPtr++;
					if (indentStackPtr>=INCLUDESTACKMAXDEPTH)
						e_fatal(&posicion,"Demasiados niveles de sangrado");
					
					indentStack[indentStackPtr]=indent;
					
					/*
					>descomentar para que no haya NEWLINE antes de INDENT<
					>y eliminar condicion if ...=-1 <
					BEGIN(INITIAL);
					TOKEN(L_INDENT);
					*/
				
					indent=-1;
					TOKEN(L_NEWLINE);
				}
				
						/* dedent */
				
				else{
					indentStackPtr--;
					if (indent>indentStack[indentStackPtr]){
						BEGIN(INITIAL);
						e_error(&posicion,"No hay ninguna linea previa con ese nivel de sangrado");
						indentStackPtr++;
					}
					/*
					>descomentar para que no haya NEWLINE tras los DEDENT<
					else if (indent==indentStack[indentStackPtr]){
						BEGIN(INITIAL);
						TOKEN(L_DEDENT);
					}
					*/
					else{
						TOKEN(L_DEDENT);
					}
				}
			}			

			/*********************************************************/
			/****************** literales de cadena ******************/
			/*********************************************************/
			
<INITIAL>\"			{
					posicion.lineaFin=posicion.lineaInicio;
					newTempCad();
					BEGIN(cadena);
				}

<cadena,include>[^\\\n\" \t\f]+	{
					addTempCad(yytext);
				}
<cadena,include>{cualquierEspacio}*\n{cualquierEspacio}*	{
					addTempCad(" ");
					posicion.lineaFin++;
				}
<cadena,include>{cualquierEspacio}+	{
					addTempCad(yytext);
				}
<cadena>\"			{
					yylval.s=copiaTempCad();
					BEGIN(INITIAL);
	
					yylval.posicion.lineaInicio=posicion.lineaInicio;
			 		yylval.posicion.lineaFin=posicion.lineaFin;
			 		yylval.posicion.fichero=posicion.fichero;
					posicion.lineaInicio=posicion.lineaFin;
			 		return L_LITERALCADENA;			
				}
<cadena>\\\"			{
					addTempCad("\\\"");
				}
<cadena>\\n			{
					addTempCad("\\n");
				}
<cadena>\\\\			{
					addTempCad("\\\\");
				}
<include>\\\"			{
					addTempCad("\"");
				}
<include>\\n			{
					addTempCad("\n");
				}
<include>\\\\			{
					addTempCad("\\");
				}
<cadena,include>\\{eol}		{
				posicion.lineaFin++;
				}
<cadena,include>\\.		{
					e_error(&posicion,"Secuencia de escape desconocida (\\%c)",yytext[1]);
				}
<cadena,include><<EOF>>		{
					e_error(&posicion,"Se ha encontrado fin de archivo procesando un literal de cadena");
					posicion.lineaInicio=posicion.lineaFin;
					BEGIN(INITIAL);
				}
			
			/**********************************************************/
			/******************* otras directivas      ****************/
			/**********************************************************/

<INITIAL>"#"{identificador}*	{
				e_error(&posicion,"Directiva desconocida o comentario sin emparejar",l_texto());
			}
			
			/**********************************************************/
			/******************* palabras reservadas ******************/
			/**********************************************************/

<INITIAL>abstract	{TOKEN(L_ABSTRACT);}
<INITIAL>and		{TOKEN(L_AND);}				
<INITIAL>break		{TOKEN(L_BREAK);}
<INITIAL>continue	{TOKEN(L_CONTINUE);}
<INITIAL>do		{TOKEN(L_DO);}
<INITIAL>else		{TOKEN(L_ELSE);}
<INITIAL>extern		{TOKEN(L_EXTERN);}
<INITIAL>false		{TOKEN(L_FALSE);}
<INITIAL>for		{TOKEN(L_FOR);}
<INITIAL>has		{TOKEN(L_HAS);}
<INITIAL>hidden		{TOKEN(L_HIDDEN);}
<INITIAL>if		{TOKEN(L_IF);}
<INITIAL>in		{TOKEN(L_IN);}
<INITIAL>is		{TOKEN(L_IS);}
<INITIAL>let		{TOKEN(L_LET);}
<INITIAL>native		{TOKEN(L_NATIVE);}
<INITIAL>not		{TOKEN(L_NOT);}
<INITIAL>or		{TOKEN(L_OR);}
<INITIAL>resend		{TOKEN(L_RESEND);}
<INITIAL>return		{TOKEN(L_RETURN);}
<INITIAL>shared		{TOKEN(L_SHARED);}
<INITIAL>static		{TOKEN(L_STATIC);}
<INITIAL>then		{TOKEN(L_THEN);}
<INITIAL>true		{TOKEN(L_TRUE);}
<INITIAL>var  		{TOKEN(L_VAR);}
<INITIAL>while		{TOKEN(L_WHILE);}
		
			/**********************************************************/
			/******************* identificadores **********************/
			/**********************************************************/
			
<INITIAL>{identificador} 	{
				char *s;
				int i;
				
				#if 1
					s=strdup(yytext);
					if (s==NULL){
						e_fatal(&posicion,"Sin memoria durante el analisis lexico");
					}
					for (i=0;i<yyleng;i++)
						s[i]=tolower(s[i]);
					yylval.id=t_simbolo(s);
					free(s);
				#else
					for (i=0;i<yyleng;i++)
						yytext[i]=tolower(yytext[i]);
					yylval.id=t_simbolo(yytext);
				#endif
				
				TOKEN(L_ID);
			}
	
			/**********************************************************/
			/******************* literales numericos ******************/
			/**********************************************************/
			
<INITIAL>{literalBinario}	{
				int i,k;
				for (i=2,k=0;i<yyleng;i++){
					k=k*2;
					k=k+(yytext[i]-'0');
				}
				yylval.i=k;
				TOKEN(L_LITERALENTERO);
			}
<INITIAL>{literalOctal}		{
				int i,k;
				for (i=2,k=0;i<yyleng;i++){
					k=k*8;
					k=k+(yytext[i]-'0');
				}
				yylval.i=k;
				TOKEN(L_LITERALENTERO);
			}
<INITIAL>{literalHexadecimal}	{
				int i,k;
				for (i=2,k=0;i<yyleng;i++){
					k=k*16;
					if (yytext[i]>='0' && yytext[i]<='9') 
						k=k+(yytext[i]-'0');
					else if (yytext[i]>='a' && yytext[i]<='f') 
						k=k+10+(yytext[i]-'a');
					else 
						k=k+10+(yytext[i]-'A');
				}
				yylval.i=k;				
				TOKEN(L_LITERALENTERO);
			}
<INITIAL>{literalDecimal}	{
				int i,k;
				for (i=k=0;i<yyleng;i++){
					k=k*10;
					k=k+(yytext[i]-'0');
				}
				yylval.i=k;
				TOKEN(L_LITERALENTERO);
			}
<INITIAL>{literalFlotante}	{
				yylval.f=atof(yytext);
				TOKEN(L_LITERALREAL);
			}


			/**********************************************************/
			/******************* operadores ***************************/
			/**********************************************************/
			

<INITIAL>"==="		{TOKEN(L_IDENTICO);}
<INITIAL>"!=="		{TOKEN(L_NOIDENTICO);}
<INITIAL>"=="		{TOKEN(L_IGUAL);}
<INITIAL>"!="		{TOKEN(L_NOIGUAL);}
<INITIAL>"<="		{TOKEN(L_MENORIGUAL);}
<INITIAL>">="		{TOKEN(L_MAYORIGUAL);}
<INITIAL>"::"		{TOKEN(L_UNION);}

<INITIAL>"["|"("|","|")"|"."|";"|"{"|"}"|"="|"+"|"-"|"*"|"/"|"%"|":"|">"|"<"|"["|"]"|"@" {
				TOKEN(yytext[0]);	/* mas eficiente que tener que */ 
			}				/* traducir los caracteres unarios*/
							

<INITIAL>.		{
				e_error(&posicion,"Caracter desconocido (ASCII 0x%X)",(unsigned char)yytext[0]);
			}

%%

/***********************************************************************/
/* Obtener informacion del estado del analisis                         */
/***********************************************************************/

char* l_texto(void){
	return yyleng==0?"":yytext;
}

E_POSICION *l_posicion(void){
	return &posicion;
}

L_TOKEN	*l_token(void) {
	return &yylval;
}

/***********************************************************************/
/* Inicializacion                                                      */
/***********************************************************************/

void l_init(char *ficheroEntrada){
char *s;

	includeStackPtr=0;

	if ((yyin=fopen(ficheroEntrada,"rt"))==NULL)
		e_fatal(NULL,"Error al abrir \"%s\" para lectura",ficheroEntrada);

				/* Inicializar seguimiento de posiciones */
				
	posicion.fichero=strdup(ficheroEntrada);
	posicion.lineaInicio=1;
	posicion.lineaFin=posicion.lineaInicio;	

				/* Inicializar buffer temporal para literales de cadena */
				
	if ((tempCad=(char *)malloc(maxTempCad))==NULL)	
		e_fatal(NULL,"Sin memoria durante la inicializacion del analisis lexico",ficheroEntrada);

				/* Inicializar control de sangrado */
		
	indentStackPtr=0;indentStack[0]=0;indent=0;
	marcaUnionLineas=-1;unirLineas=2; /* descartar primer newline */
	BEGIN(sangrado);
}

int l_lex(void){
	return yylex();
}

void l_close(void){
	free(tempCad);		/* Liberar buffer temporal para literales de cadena */
	fclose(yyin);		/* Cerrar fichero de entrada */
}


