next up previous contents index PLPL moodlepserratamodulosperlmonksperldocapuntes LHPgoogleetsiiullpcgull
Sig: Análisis Léxico desde una Sup: Expresiones Regulares en Flex Ant: El operador de ``trailing Err: Si hallas una errata ...


Manejo de directivas include

El analisis léxico de algunos lenguajes requiere que, durante la ejecución, se realice la lectura desde diferentes ficheros de entrada. El ejemplo típico es el manejo de las directivas include file existentes en la mayoría de los lenguajes de programación.
¿Donde está el problema? La dificultad reside en que los analizadores generados por flex proveen almacenamiento intermedio (buffers) para aumentar el rendimiento. No basta con reescribir nuestro propio YY_INPUT de manera que tenga en cuenta con que fichero se esta trabajando. El analizador sólo llama a YY_INPUT cuando alcanza el final de su buffer, lo cual puede ocurrir bastante después de haber encontrado la sentencia include que requiere el cambio de fichero de entrada.
$ cat include.l
%x incl
%{
#define yywrap() 1
#define MAX_INCLUDE_DEPTH 10
YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
int include_stack_ptr = 0;
%}
%%
include          BEGIN(incl);
.                ECHO;
<incl>[ \t]*
<incl>[^ \t\n]+  { /* got the file name */
                   if (include_stack_ptr >= MAX_INCLUDE_DEPTH) {
                     fprintf(stderr,"Includes nested too deeply\n");
                     exit(1);
                   }
                   include_stack[include_stack_ptr++] = YY_CURRENT_BUFFER;
                   yyin = fopen(yytext,"r");
                   if (!yyin) {
                     fprintf(stderr,"File %s not found\n",yytext);
                     exit(1);
                   }
                   yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
                   BEGIN(INITIAL);
                 }
<<EOF>> {
          if ( --include_stack_ptr < 0) {
            yyterminate();
          } else {
            yy_delete_buffer(YY_CURRENT_BUFFER);
            yy_switch_to_buffer(include_stack[include_stack_ptr]);
          }
        }
%%
main(int argc, char ** argv) {
 
  yyin = fopen(argv[1],"r");
  yylex();
}
La función yy_create_buffer(yyin, YY_BUF_SIZE)); crea un buffer lo suficientemente grande para mantener YY_BUF_SIZE caracteres. Devuelve un YY_BUFFER_STATE, que puede ser pasado a otras rutinas. YY_BUFFER_STATE es un puntero a una estructura de datos opaca (struct yy_buffer_state) que contiene la información para la manipulación del buffer. Es posible por tanto inicializar un puntero YY_BUFFER_STATE usando la expresión ((YY_BUFFER_STATE) 0).

La función yy_switch_to_buffer(YY_BUFFER_STATE new_buffer); conmuta la entrada del analizador léxico. La función void yy_delete_buffer( YY_BUFFER_STATE buffer ) se usa para recuperar la memoria consumida por un buffer. También se pueden limpiar los contenidos actuales de un buffer llamando a: void yy_flush_buffer( YY_BUFFER_STATE buffer )

La regla especial <<EOF>> indica la acción a ejecutar cuando se ha encontrado un final de fichero e yywrap() retorna un valor distinto de cero. Cualquiera que sea la acción asociada, esta debe terminar con uno de estos cuatro supuestos:

  1. Asignar yyin a un nuevo fichero de entrada.
  2. Ejecutar return.
  3. Ejecutar yyterminate().
  4. Cambiar a un nuevo buffer usando yy_switch_to_buffer().

    La regla <<EOF>> no se puede mezclar con otros patrones.

Este es el resultado de una ejecución del programa:

$ cat hello.c
#include hello2.c
main() <%
int a<:1:>; /* a comment */
  a<:0:> = 4; /* a comment in
                 two lines */
  printf("\thell\157\nworld! a(0) is %d\n",a<:0:>);
%>
$ cat hello2.c
#include hello3.c
/* file hello2.c  */
$ cat hello3.c
/*
third file
*/
$ flex include.l ; gcc lex.yy.c ; a.out hello.c
##/*
third file
*/
 
/* file hello2.c  */
 
main() <%
int a<:1:>; /* a comment */
  a<:0:> = 4; /* a comment in
                 two lines */
  printf("\thell\157\nworld! a(0) is %d\n",a<:0:>);
%>                                                            
Una alternativa a usar el patrón <<EOF>> es dejar la responsabilidad de recuperar el buffer anterior a yywrap(). En tal caso suprimiríamos esta parajea patrón-acción y reescribiríamos yywrap():
%x incl
%{
#define MAX_INCLUDE_DEPTH 10
YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
int include_stack_ptr = 0;
%}
%%
include          BEGIN(incl);
.                ECHO;
<incl>[ \t]*
<incl>[^ \t\n]+  { /* got the file name */
                   if (include_stack_ptr >= MAX_INCLUDE_DEPTH) {
		     fprintf(stderr,"Includes nested too deeply\n");
		     exit(1);
		   }
		   include_stack[include_stack_ptr++] = YY_CURRENT_BUFFER;
		   yyin = fopen(yytext,"r");
		   if (!yyin) {
		     fprintf(stderr,"File %s not found\n",yytext);
		     exit(1);
		   }
		   yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
		   BEGIN(INITIAL);
		 }
%%
main(int argc, char ** argv) {

  yyin = fopen(argv[1],"r");
  yylex();
}

int yywrap() {
  if ( --include_stack_ptr < 0) { 
    return 1;
  } else {
    yy_delete_buffer(YY_CURRENT_BUFFER);
    yy_switch_to_buffer(include_stack[include_stack_ptr]);
    return 0;
  }
}


next up previous contents index PLPL moodlepserratamodulosperlmonksperldocapuntes LHPgoogleetsiiullpcgull
Sig: Análisis Léxico desde una Sup: Expresiones Regulares en Flex Ant: El operador de ``trailing Err: Si hallas una errata ...
Casiano Rodríguez León
2012-05-22