next up previous contents index PLPL moodlepserratamodulosperlmonksperldocapuntes LHPgoogleetsiiullpcgull
Sig: Declaraciones pointer y array Sup: Expresiones Regulares en Flex Ant: Análisis Léxico desde una Err: Si hallas una errata ...

Análisis de la Línea de Comandos y 2 Analizadores

El objetivo de este ejercicio es mostrar como realizar un análisis léxico de los argumentos pasados en la línea de comandos. Para ello diseñaremos una librería que proporcionará un función yylexarg(argc,argv) que hace el análisis de la línea de acuerdo con la especificación flex correspondiente. En el ejemplo, esta descripción del analizador léxico es proporcionada en el fichero fl.l. Para complicar un poco mas las cosas, supondremos que queremos hacer el análisis léxico de un fichero (especificado en la línea de comandos) según se especifica en un segundo analizador léxico trivial.l. El siguiente ejemplo de ejecución muestra la conducta del programa:
$ fl -v -V -f tokens.h
verbose mode is on
version 1.0
File name is: tokens.h
Analyzing tokens.h
#-id-blanks-id-blanks-int-blanks-#-id-blanks-id-blanks-int-blanks-#-id-blanks-id-blanks
-int-blanks-#-id-blanks-id-blanks-int-blanks-#-id-blanks-id-blanks-int-blanks-
Los contenidos del fichero Makefile definen las dependencias y la estructura de la aplicación:
$ cat Makefile
LIBS=-lflarg
CC=gcc -g
LIBPATH=-L. -L~/lib
INCLUDES=-I. -I~/include
 
fl: main.c lex.arg.c lex.yy.c libflarg.a tokens.h
        $(CC) $(LIBPATH) $(INCLUDES) main.c lex.arg.c lex.yy.c $(LIBS) -o fl
lex.arg.c: fl.l
        flex -Parg fl.l
lex.yy.c: trivial.l tokens.h
        flex trivial.l
libflarg.a: flarg.o
        ar r libflarg.a flarg.o
flarg.o: flarg.c
        $(CC) -c flarg.c
clean:                                 
$ make clean;make
rm lex.arg.c lex.yy.c *.o  fl
flex -Parg fl.l
flex trivial.l
gcc -g -c flarg.c
ar r libflarg.a flarg.o
gcc -g -L. -L~/lib -I. -I~/include main.c lex.arg.c lex.yy.c -lflarg -o fl
Observa el uso de la opción -Parg en la traducción del fichero fl.l. Así no solo el fichero generado por flex, sino todas las variables y rutinas accesibles estarán prefijadas por arg en vez de yy. La librería la denominamos libflarg.a. (flarg por flex arguments). El correspondiente fichero cabecera será flarg.h. Los fuentes de las rutinas que compondrán la librería se mantendrán en el fichero flarg.c.

Lo que haremos será redefinir YY_INPUT(buf, result, max) para que lea su entrada desde la línea de argumentos.

$ cat flarg.h
int yyarglex(int argc, char **argv);
int YY_input_from_argv(char *buf, int max);
int argwrap(void);
 
#undef YY_INPUT
#define YY_INPUT(buf,result,max) (result = YY_input_from_argv(buf,max))
La función int YY_input_from_argv(char *buf, int max) utiliza los punteros char **YY_targv y char **YY_arglim para moverse a través de la familia de argumentos. Mientras que el primero es utilizado para el recorrido, el segundo marca el límite final. Su inicialización ocurre en

yyarglex(int argc, char **argv)
con las asignaciones:
  YY_targv = argv+1;
  YY_arglim = argv+argc;

despues, de lo cual, se llama al analizador léxico generado, arglex .

$ cat flarg.c
char **YY_targv;
char **YY_arglim;
 
int YY_input_from_argv(char *buf, int max)
{
  static unsigned offset = 0;
 
  int len, copylen;
 
    if (YY_targv >= YY_arglim) return 0;        /* EOF */
    len = strlen(*YY_targv)-offset;     /* amount of current arg */
    if(len >= max) {copylen = max-1; offset += copylen; }
    else copylen = len;
    if(len > 0) memcpy(buf, YY_targv[0]+offset, copylen);
    if(YY_targv[0][offset+copylen] == '\0') {   /* end of arg */
      buf[copylen] = ' '; copylen++; offset = 0; YY_targv++;
    }
    return copylen;
}
 
int yyarglex(int argc, char **argv) {
  YY_targv = argv+1;
  YY_arglim = argv+argc;
  return arglex();
}
 
int argwrap(void) {
  return 1;
}
El fichero fl.l contiene el analizador léxico de la línea de comandos:
$ cat fl.l
%{
unsigned verbose;
unsigned thereisfile;
char *progName;
char fileName[256];
#include "flarg.h"
#include "tokens.h"
%}
 
%%
-h      |
"-?"    |
-help   { printf("usage is: %s [-help | -h | -? ] [-verbose | -v]"
         " [-Version | -V]"
         " [-f filename]\n", progName);
        }
 
-v      |
-verbose { printf("verbose mode is on\n"); verbose = 1; }
 
-V      |
-version { printf("version 1.0\n"); }
 
-f[[:blank:]]+[^ \t\n]+ {
              strcpy(fileName,argtext+3);
              printf("File name is: %s\n",fileName);
              thereisfile = 1;
            }
.
 
\n
Observe el uso de la clase [:blank:] para reconocer los blancos. Las clases son las mismas que las introducidas en gawk.

El análisis léxico del fichero que se lee después de procesar la línea de comandos es descrito en trivial.l. Partiendo de trivial.l, la ejecución del Makefile da lugar a la construcción por parte de flex del fichero lex.yy.c conteniendo la rutina yylex.

$ cat trivial.l
%{
#include "tokens.h"
%}
digit [0-9]
id [a-zA-Z][a-zA-Z0-9]+
blanks [ \t\n]+
operator [+*/-]
%%
{digit}+ {return INTTOKEN; }
{digit}+"."{digit}+ {return FLOATTOKEN; }
{id} {return IDTOKEN;}
{operator} {return OPERATORTOKEN;}
{blanks} {return BLANKTOKEN;}
. {return (int) yytext[0];}
%%
int yywrap() {
  return 1;
}
El fichero tokens.h contiene la definición de los tokens y es compartido con main.c.
$ cat tokens.h
#define INTTOKEN  256
#define FLOATTOKEN 257
#define IDTOKEN 258
#define OPERATORTOKEN 259
#define BLANKTOKEN 260
Nos queda por presentar el fichero main.c:
$ cat main.c
#include <stdio.h>
#include "flarg.h"
#include "tokens.h"
extern unsigned verbose;
extern unsigned thereisfile;
extern char *progName;
extern char fileName[256];
extern FILE * yyin;
 
main(int argc, char **argv) {
  unsigned lookahead;
  FILE * file;
 
  progName = *argv;
  yyarglex(argc,argv);
  if (thereisfile) {
    if (verbose) printf("Analyzing %s\n",fileName);
    file = (fopen(fileName,"r"));
    if (file == NULL) exit(1);
    yyin = file;
    while (lookahead = yylex()) {
      switch (lookahead) {
        case INTTOKEN:
            printf("int-");
            break;
          case FLOATTOKEN:
            printf("float-");
            break;
          case IDTOKEN:
            printf("id-");
            break;
          case OPERATORTOKEN:
            printf("operator-");
            break;
          case BLANKTOKEN:
            printf("blanks-");
            break;
          default: printf("%c-",lookahead);
       }
    } /* while */
    printf("\n");
  } /* if */
}


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