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 flObserva 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)
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; } . \nObserve 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 260Nos 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 */ }