lex
y POSIX
@catcode`�=@active@def�{@questiondown{}} @catcode`�=@active@def�{@exclamdown{}} @catcode`�=@active@def�{'a} @catcode`�=@active@def�{'e} @catcode`�=@active@def�{'{@char16{}}} @catcode`�=@active@def�{'o} @catcode`�=@active@def�{'u} @catcode`�=@active@def�{'A} @catcode`�=@active@def�{'E} @catcode`�=@active@def�{'I} @catcode`�=@active@def�{'O} @catcode`�=@active@def�{'U} @catcode`�=@active@def�{@"u} @catcode`�=@active@def�{@"U} @catcode`�=@active@def�{@~n} @catcode`�=@active@def�{@~N} @catcode`�=@active@def�{a.}
@gdef@putwordChapter{Cap�tulo} @def@putwordInfo{Info} @gdef@putwordSee{Ver} @gdef@putwordsee{ver} @gdef@putwordfile{archivo} @gdef@putwordpage{p�gina} @gdef@putwordsection{secci�n} @gdef@putwordSection{Secci�n} @gdef@putwordTableofContents{Tabla de Contenido} @gdef@putwordTOC{Tabla de Contenido} @gdef@putwordShortContents{Contenido Breve} @gdef@putwordAppendix{Ap�ndice}
@def@ingles{0} @def@espanol{1}
@language=@espanol
@language=@ingles Copyright (C) 1990 The Regents of the University of California. All rights reserved.
This code is derived from software contributed to Berkeley by Vern Paxson.
The United States Government has rights in this work pursuant to contract no. DE-AC03-76SF00098 between the United States Department of Energy and the University of California.
Redistribution and use in source and binary forms with or without modification are permitted provided that: (1) source distributions retain this entire copyright notice and comment, and (2) distributions including binaries display the following acknowledgement: "This product includes software developed by the University of California, Berkeley and its contributors" in the documentation or other materials provided with the distribution and in all advertising materials mentioning features or use of this software. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. @language=@espanol
Este manual describe flex
,
una herramienta para la generaci�n de programas que realizan
concordancia de patrones en texto. El manual incluye a la vez
secciones de tutorial y de referencia:
flex
es una herramienta para generar esc�neres: programas
que reconocen patrones l�xicos en un texto. flex
lee los ficheros
de entrada dados, o la entrada est�ndar si no se le ha indicado ning�n
nombre de fichero, con la descripci�n de un esc�ner a generar. La
descripci�n se encuentra en forma de parejas de expresiones regulares
y c�digo C, denominadas reglas. flex
genera como salida un
fichero fuente en C, `lex.yy.c', que define una rutina `yylex()'.
Este fichero se compila y se enlaza con la librer�a
`-lfl' para producir un ejecutable. Cuando se arranca el fichero
ejecutable, este analiza su entrada en busca de casos de las expresiones
regulares. Siempre que encuentra uno, ejecuta el c�digo C correspondiente.
En primer lugar veremos algunos ejemplos simples para una toma de
contacto con el uso de flex
. La siguiente entrada de flex
especifica un esc�ner que siempre que encuentre la cadena "username" la
reemplazar� por el nombre de entrada al sistema del usuario:
%% username printf( "%s", getlogin() );
Por defecto, cualquier texto que no reconozca el analizador l�xico de
flex
se copia a la salida, as� que el efecto neto de este esc�ner
es copiar su fichero de entrada a la salida con cada aparici�n de
"username" expandida. En esta entrada, hay solamente una regla.
"username" es el patr�n y el "printf" es la acci�n.
El "%%" marca el comienzo de las reglas.
Aqu� hay otro ejemplo simple:
int num_lineas = 0, num_caracteres = 0; %% \n ++num_lineas; ++num_caracteres; . ++num_caracteres; %% main() { yylex(); printf( "# de l�neas = %d, # de caracteres. = %d\n", num_lineas, num_caracteres ); }
Este analizador cuenta el n�mero de caracteres y el n�mero de l�neas en su entrada (no produce otra salida que el informe final de la cuenta). La primera l�nea declara dos variables globales, "num_lineas" y "num_caracteres", que son visibles al mismo tiempo dentro de `yylex()' y en la rutina `main()' declarada despu�s del segundo "%%". Hay dos reglas, una que empareja una l�nea nueva ("\n") e incrementa la cuenta de l�neas y la cuenta de caracteres, y la que empareja cualquier caracter que no sea una l�nea nueva (indicado por la expresi�n regular ".").
Un ejemplo algo m�s complicado:
/* esc�ner para un lenguaje de juguete al estilo de Pascal */ %{ /* se necesita esto para la llamada a atof() m�s abajo */ #include <math.h> %} DIGITO [0-9] ID [a-z][a-z0-9]* %% {DIGITO}+ { printf( "Un entero: %s (%d)\n", yytext, atoi( yytext ) ); } {DIGITO}+"."{DIGITO}* { printf( "Un real: %s (%g)\n", yytext, atof( yytext ) ); } if|then|begin|end|procedure|function { printf( "Una palabra clave: %s\n", yytext ); } {ID} printf( "Un identificador: %s\n", yytext ); "+"|"-"|"*"|"/" printf( "Un operador: %s\n", yytext ); "{"[^}\n]*"}" /* se come una linea de comentarios */ [ \t\n]+ /* se come los espacios en blanco */ . printf( "Caracter no reconocido: %s\n", yytext ); %% main( argc, argv ) int argc; char **argv; { ++argv, --argc; /* se salta el nombre del programa */ if ( argc > 0 ) yyin = fopen( argv[0], "r" ); else yyin = stdin; yylex(); }
Esto podr�a ser los comienzos de un esc�ner simple para un lenguaje como Pascal. Este identifica diferentes tipos de tokens e informa a cerca de lo que ha visto.
Los detalles de este ejemplo se explicar�n en las secciones siguientes.
El fichero de entrada de flex
est� compuesto de tres secciones,
separadas por una l�nea donde aparece �nicamente un `%%' en esta:
definiciones %% reglas %% c�digo de usuario
La secci�n de definiciones contiene declaraciones de definiciones de nombres sencillas para simplificar la especificaci�n del esc�ner, y declaraciones de condiciones de arranque, que se explicar�n en una secci�n posterior. Las definiciones de nombre tienen la forma:
nombre definici�n
El "nombre" es una palabra que comienza con una letra o un subrayado (`_') seguido por cero o m�s letras, d�gitos, `_', o `-' (gui�n). La definici�n se considera que comienza en el primer caracter que no sea un espacio en blanco siguiendo al nombre y continuando hasta el final de la l�nea. Posteriormente se puede hacer referencia a la definici�n utilizando "{nombre}", que se expandir� a "(definici�n)". Por ejemplo,
DIGITO [0-9] ID [a-z][a-z0-9]*
define "DIGITO" como una expresi�n regular que empareja un d�gito sencillo, e "ID" como una expresi�n regular que empareja una letra seguida por cero o m�s letras o d�gitos. Una referencia posterior a
{DIGITO}+"."{DIGITO}*
es id�ntica a
([0-9])+"."([0-9])*
y empareja uno o m�s d�gitos seguido por un `.' seguido por cero o m�s d�gitos.
La secci�n de reglas en la entrada de flex
contiene una
serie de reglas de la forma:
patr�n acci�n
donde el patr�n debe estar sin sangrar y la acci�n debe comenzar en la misma l�nea.
See section Acciones, para una descripci�n m�s amplia sobre patrones y acciones.
Finalmente, la secci�n de c�digo de usuario simplemente se copia a `lex.yy.c' literalmente. Esta secci�n se utiliza para rutinas de complemento que llaman al esc�ner o son llamadas por este. La presencia de esta secci�n es opcional; Si se omite, el segundo `%%' en el fichero de entrada se podr�a omitir tambi�n.
En las secciones de definiciones y reglas, cualquier texto sangrado o encerrado entre `%{' y `%}' se copia �ntegramente a la salida (sin los %{}'s). Los %{}'s deben aparecer sin sangrar en l�neas ocupadas �nicamente por estos.
En la secci�n de reglas, cualquier texto o %{} sangrado que aparezca
antes de la primera regla podr�a utilizarse para declarar variables que
son locales a la rutina de an�lisis y (despu�s de las declaraciones) al
c�digo que debe ejecutarse siempre que se entra a la rutina de an�lisis.
Cualquier otro texto sangrado o %{} en la secci�n de reglas sigue
copi�ndose a la salida, pero su significado no est� bien definido y
bien podr�a causar errores en tiempo de compilaci�n (esta propiedad se
presenta para conformidad con POSIX
; para otras caracter�sticas
similares) see section Incompatibilidades con lex
y POSIX)
En la secci�n de definiciones (pero no en la secci�n de reglas), un comentario sin sangr�a (es decir, una l�nea comenzando con "/*") tambi�n se copia literalmente a la salida hasta el pr�ximo "*/".
Los patrones en la entrada se escriben utilizando un conjunto extendido de expresiones regulares. Estas son:
2a
F�jese que dentro de una clase de caracteres, todos los operadores de expresiones regulares pierden su significado especial excepto el caracter de escape (`\') y los operadores de clase de caracteres, `-', `]', y, al principio de la clase, `^'.
Las expresiones regulares en el listado anterior est�n agrupadas de acuerdo a la precedencia, desde la precedencia m�s alta en la cabeza a la m�s baja al final. Aquellas agrupadas conjuntamente tienen la misma precedencia. Por ejemplo,
foo|bar*
es lo mismo que
(foo)|(ba(r*))
ya que el operador `*' tiene mayor precedencia que la concatenaci�n, y la concatenaci�n m�s alta que el operador `|'. Este patr�n por lo tanto empareja bien la cadena "foo" o la cadena "ba" seguida de cero o m�s r's. Para emparejar "foo" o, cero o m�s "bar"'s, use:
foo|(bar)*
y para emparejar cero o m�s "foo"'s o "bar"'s:
(foo|bar)*
Adem�s de caracteres y rangos de caracteres, las clases de caracteres pueden tambi�n contener expresiones de clases de caracteres. Son expresiones encerradas entre los delimitadores `[:' y `:]' (que tambi�n deben aparecer entre el `[' y el `]' de la clase de caracteres; adem�s pueden darse otros elementos dentro de la clase de caracteres). Las expresiones v�lidas son:
[:alnum:] [:alpha:] [:blank:] [:cntrl:] [:digit:] [:graph:] [:lower:] [:print:] [:punct:] [:space:] [:upper:] [:xdigit:]
Todas estas expresiones designan un conjunto de caracteres equivalentes a la correspondiente funci�n est�ndar `isXXX' de C. Por ejemplo, `[:alnum:]' designa aquellos caracteres para los cuales `isalnum()' devuelve verdadero --es decir, cualquier caracter alfab�tico o num�rico. Algunos sistemas no ofrecen `isblank()', as� que flex define `[:blank:]' como un espacio en blanco o un tabulador.
Por ejemplo, las siguientes clases de caracteres son todas equivalentes:
[[:alnum:]] [[:alpha:][:digit:]] [[:alpha:]0-9] [a-zA-Z0-9]
Si su esc�ner ignora la distinci�n entre may�sculas y min�sculas (la bandera `-i'), entonces `[:upper:]' y `[:lower:]' son equivalentes a `[:alpha:]'.
Algunas notas sobre los patrones:
foo/bar$ <sc1>foo<sc2>barF�jese que la primera regla se puede escribir como "foo/bar\n". En el siguiente ejemplo un `$' o un `^' es tratado como un caracter normal:
foo|(bar$) foo|^barSi lo que se desea es un "foo" o un "bar" seguido de una l�nea nueva, puede usarse lo siguiente (la acci�n especial `|' se explica en la section Acciones.):
foo | bar$ /* la acci�n va aqu� */Un truco parecido funcionar� para emparejar un "foo" o, un "bar" al principio de una l�nea.
Cuando el esc�ner generado est� funcionando, este analiza su entrada
buscando cadenas que concuerden con cualquiera de sus patrones. Si
encuentra m�s de un emparejamiento, toma el que empareje m�s
texto (para reglas de contexto posterior, se incluye la longitud de
la parte posterior, incluso si se devuelve a la entrada). Si encuentra
dos o m�s emparejamientos de la misma longitud, se escoge la
regla listada en primer lugar en el fichero de entrada de
flex
.
Una vez que se determina el emparejamiento, el texto correspondiente
al emparejamiento (denominado el token) est� disponible en el
puntero a caracter global yytext
, y su longitud en la variable
global entera yyleng
.
Entonces la acci�n correspondiente al patr�n emparejado se ejecuta
(See section Acciones, para una descripci�n m�s detallada de las acciones), y
entonces la entrada restante se analiza para otro emparejamiento.
Si no se encuentra un emparejamiento, entonces se ejecuta la regla
por defecto: el siguiente caracter en la entrada se considera
reconocido y se copia a la salida est�ndar. As�, la entrada v�lida m�s
simple de flex
es:
%%
que genera un esc�ner que simplemente copia su entrada (un caracter a la vez) a la salida.
F�jese que yytext
se puede definir de dos maneras diferentes:
bien como un puntero a caracter o como un array de
caracteres. Usted puede controlar la definici�n que usa flex
incluyendo una de las directivas especiales `%pointer' o
`%array' en la primera secci�n (definiciones) de su entrada de
flex. Por defecto es `%pointer', a menos que use la opci�n de
compatibilidad `-l', en cuyo caso yytext
ser� un array.
La ventaja de usar `%pointer' es un an�lisis substancialmente m�s
r�pido y la ausencia de desbordamiento del buffer cuando se emparejen
tokens muy grandes (a menos que se agote la memoria din�mica). La
desventaja es que se encuentra restringido en c�mo sus acciones pueden
modificar yytext
(see section Acciones), y las llamadas a la
funci�n `unput()' destruyen el contenido actual de yytext
,
que puede convertirse en un considerable quebradero de cabeza de
portabilidad al cambiar entre diferentes versiones de lex
.
La ventaja de `%array' es que entoces puede modificar yytext
todo lo que usted quiera, las llamadas a `unput()' no destruyen
yytext
(ver m�s abajo). Adem�s, los programas de lex
existentes a veces acceden a yytext
externamente utilizando
declaraciones de la forma:
extern char yytext[];
Esta definici�n es err�nea cuando se utiliza `%pointer', pero correcta para `%array'.
`%array' define a yytext
como un array de YYLMAX
caracteres, que por defecto es un valor bastante grande. Usted puede
cambiar el tama�o s�mplemente definiendo con #define a YYLMAX
con un
valor diferente en la primera secci�n de su entrada de flex
.
Como se mencion� antes, con `%pointer' yytext crece din�micamente
para acomodar tokens grandes. Aunque esto signifique que con
`%pointer' su esc�ner puede acomodar tokens muy grandes (tales como
emparejar bloques enteros de comentarios), tenga presente que cada vez
que el esc�ner deba cambiar el tama�o de yytext
tambi�n debe
reiniciar el an�lisis del token entero desde el principio, as� que
emparejar tales tokens puede resultar lento.
Ahora yytext
no crece din�micamente si una llamada a
`unput()' hace que se deba devolver demasiado texto; en su lugar,
se produce un error en tiempo de ejecuci�n.
Tambi�n tenga en cuenta que no puede usar `%array' en los analizadores generados como clases de C++ (see section Generando esc�neres en C++).
Cada patr�n en una regla tiene una acci�n asociada, que puede ser cualquier sentencia en C. El patr�n finaliza en el primer caracter de espacio en blanco que no sea una secuencia de escape; lo que queda de la l�nea es su acci�n. Si la acci�n est� vac�a, entonces cuando el patr�n se empareje el token de entrada simplemente se descarta. Por ejemplo, aqu� est� la especificaci�n de un programa que borra todas las apariciones de "zap me" en su entrada:
%% "zap me"
(Este copiar� el resto de caracteres de la entrada a la salida ya que ser�n emparejados por la regla por defecto.)
Aqu� hay un programa que comprime varios espacios en blanco y tabuladores a un solo espacio en blanco, y desecha los espacios que se encuentren al final de una l�nea:
%% [ \t]+ putchar( ' ' ); [ \t]+$ /* ignora este token */
Si la acci�n contiene un `{', entonces la acci�n abarca hasta que se
encuentre el correspondiente `}', y la acci�n podr�a entonces cruzar
varias l�neas. flex
es capaz de reconocer las cadenas y
comentarios de C y no se dejar� enga�ar por las llaves que encuentre
dentro de estos, pero aun as� tambi�n permite que las acciones comiencen
con `%{' y considerar� que la acci�n es todo el texto hasta el
siguiente `%}' (sin tener en cuenta las llaves ordinarias dentro
de la acci�n).
Una acci�n que consista s�lamente de una barra vertical (`|') significa "lo mismo que la acci�n para la siguiente regla." Vea m�s abajo para una ilustraci�n.
Las acciones pueden incluir c�digo C arbitrario, incuyendo sentencias
return
para devolver un valor desde cualquier rutina llamada
`yylex()'. Cada vez que se llama a `yylex()' esta contin�a
procesando tokens desde donde lo dej� la �ltima vez hasta que o bien
llegue al final del fichero o ejecute un return.
Las acciones tienen libertad para modificar yytext
excepto para
alargarla (a�adiendo caracteres al final--esto sobreescribir� m�s tarde
caracteres en el flujo de entrada). Sin embargo esto no se aplica
cuando se utiliza `%array' (see section C�mo se empareja la entrada); en ese caso, yytext
podr�a modificarse libremente de cualquier manera.
Las acciones tienen libertad para modificar yyleng
excepto que
estas no deber�an hacerlo si la acci�n tambi�n incluye el uso de
`yymore()' (ver m�s abajo).
Hay un n�mero de directivas especiales que pueden incluirse dentro de una acci�n:
ECHO
copia yytext a la salida del esc�ner.
BEGIN
seguido del nombre de la condici�n de arranque pone al esc�ner en la
condici�n de arranque correspondiente (see section Condiciones de arranque).
REJECT
ordena al esc�ner a que proceda con la "segunda mejor" regla que
concuerde con la entrada (o un prefijo de la entrada). La regla se escoge
como se describi� anteriormente en el section C�mo se empareja la entrada, y
yytext
e yyleng
se ajustan de forma apropiada.
Podr�a ser una que empareje tanto texto como la regla escogida
originalmente pero que viene m�s tarde en el fichero de entrada de
flex
, o una que empareje menos texto.
Por ejemplo, lo que viene a continuaci�n contar� las palabras en la
entrada y llamar� a la rutina especial()
siempre que vea "frob":
int contador_palabras = 0; %% frob especial(); REJECT; [^ \t\n]+ ++contador_palabras;Sin el
REJECT
, cualquier n�mero de "frob"'s en la entrada no
ser�an contados como palabras, ya que el esc�ner normalmente ejecuta
solo una acci�n por token. Se permite el uso de m�ltiples
REJECT's
, cada uno buscando la siguiente mejor elecci�n a la
regla que actualmente est� activa. Por ejemplo, cuando el siguiente
esc�ner analice el token "abcd", este escribir� "abcdabcaba" a la
salida:
%% a | ab | abc | abcd ECHO; REJECT; .|\n /* se come caracteres sin emparejar */(Las primeras tres reglas comparten la acci�n de la cuarta ya que estas usan la acci�n especial `|'.)
REJECT
es una propiedad
particularmente cara en t�rminos de rendimiento del esc�ner; si se usa
en cualquiera de las acciones del esc�ner esta ralentizar�
todo el proceso de emparejamiento del esc�ner. Adem�s,
REJECT
no puede usarse con las opciones `-Cf' � `-CF'
(see section Opciones y section Consideraciones de rendimiento.)
F�jese tambi�n que a diferencia de las otras acciones especiales,
REJECT
es una bifurcaci�n; el c�digo que la siga
inmediatamente en la acci�n no ser� ejecutado.
yytext
en lugar de reemplazarlo. Por ejemplo, dada la
entrada "mega-klugde" lo que viene a continuaci�n escribir�
"mega-mega-kludge" a la salida:
%% mega- ECHO; yymore(); kludge ECHO;El primer "mega-" se empareja y se repite a la salida. Entonces se empareja "kludge", pero el "mega-" previo a�n est� esperando al inicio de
yytext
asi que el `ECHO' para la regla del "kludge"
realmente escribir� "mega-kludge".
Dos notas respecto al uso de `yymore()'. Primero, `yymore()'
depende de que el valor de yyleng
refleje correctamente el tama�o
del token actual, as� que no debe modificar yyleng
si est�
utilizando `yymore()'. Segundo, la presencia de `yymore()' en
la acci�n del esc�ner implica una peque�a penalizaci�n de rendimiento en
la velocidad de emparejamiento del esc�ner.
yytext
e yyleng
se
ajustan de forma adecuada (p.ej., yyleng
no ser� igual a
n). Por ejemplo, con la entrada "foobar" lo que viene a
continuaci�n escribir� "foobarbar":
%% foobar ECHO; yyless(3); [a-z]+ ECHO;Un argumento de 0 para
yyless
har� que la cadena de entrada
actual sea analizada por completo de nuevo. A menos que haya cambiado
la manera en la que el esc�ner procese de ahora en adelante su entrada
(utilizando BEGIN
, por ejemplo), esto producir� un bucle sin
fin.
F�jese que yyless
es una macro y puede ser utilizada solamente en
el fichero de entrada de flex, no desde otros ficheros fuente.
c
de nuevo en el flujo de
entrada. Este ser� el pr�ximo caracter analizado. La siguiente acci�n
tomar� el token actual y har� que se vuelva a analizar pero encerrado
entre par�ntesis.
{ int i; /* Copia yytext porque unput() desecha yytext */ char *yycopia = strdup( yytext ); unput( ')' ); for ( i = yyleng - 1; i >= 0; --i ) unput( yycopia[i] ); unput( '(' ); free( yycopia ); }F�jese que ya que cada `unput()' pone el caracter dado de nuevo al principio del flujo de entrada, al devolver cadenas de caracteres se debe hacer de atr�s hacia delante. Un problema potencial importante cuando se utiliza `unput()' es que si est� usando `%pointer' (por defecto), una llamada a `unput()' destruye el contenido de
yytext
, comenzando
con su caracter m�s a la derecha y devorando un caracter a la izquierda
con cada llamada. Si necesita que se preserve el valor de yytext
despu�s de una llamada a `unput()' (como en el ejemplo anterior),
usted debe o bien copiarlo primero en cualquier lugar, o construir su
esc�ner usando `%array' (see section C�mo se empareja la entrada).
Finalmente, note que no puede devolver EOF
para intentar marcar
el flujo de entrada con un fin-de-fichero.
%% "/*" { register int c; for ( ; ; ) { while ( (c = input()) != '*' && c != EOF ) ; /* se come el texto del comentario */ if ( c == '*' ) { while ( (c = input()) == '*' ) ; if ( c == '/' ) break; /* encontr� el final */ } if ( c == EOF ) { error( "EOF en comentario" ); break; } } }(F�jese que si el esc�ner se compila usando `C++', entonces a `input()' se le hace referencia con `yyinput()', para evitar una colisi�n de nombre con el flujo de `C++' por el nombre
input
.)
YY_INPUT
(see section El esc�ner generado).
Esta acci�n es un caso especial de la funci�n m�s general
`yy_flush_buffer()', descrita m�s abajo en el section M�ltiples buffers de entrada.
La salida de flex
es el fichero `lex.yy.c', que contiene la
rutina de an�lisis `yylex()', un n�mero de tablas usadas por esta
para emparejar tokens, y un n�mero de rutinas auxiliares y macros. Por
defecto, `yylex()' se declara as�
int yylex() { ... aqu� van varias definiciones y las acciones ... }
(Si su entorno acepta prototipos de funciones, entonces este ser� "int yylex( void )"). Esta definici�n podr�a modificarse definiendo la macro "YY_DECL". Por ejemplo, podr�a utilizar:
#define YY_DECL float lexscan( a, b ) float a, b;
para darle a la rutina de an�lisis el nombre lexscan
, que
devuelve un real, y toma dos reales como argumentos. F�jese que si pone
argumentos a la rutina de an�lisis usando una declaraci�n de funci�n
no-prototipada/tipo-K&R, debe hacer terminar la definici�n con un punto
y coma (`;').
Siempre que se llame a `yylex()', este analiza tokens desde el
fichero de entrada global yyin
(que por defecto es igual a
stdin). La funci�n contin�a hasta que alcance el final del fichero
(punto en el que devuelve el valor 0) o una de sus acciones ejecute una
sentencia return
.
Si el esc�ner alcanza un fin-de-fichero, entonces el comportamiento en
las llamadas posteriores est� indefinido a menos que o bien yyin
apunte a un nuevo fichero de entrada (en cuyo caso el an�lisis contin�a
a partir de ese fichero), o se llame a `yyrestart()'.
`yyrestart()' toma un argumento, un puntero `FILE *' (que
puede ser nulo, si ha preparado a YY_INPUT
para que analice una
fuente distinta a yyin
), e inicializa yyin
para que
escanee ese fichero. Esencialmente no hay diferencia entre la
asignaci�n a yyin
de un nuevo fichero de entrada o el uso de
`yyrestart()' para hacerlo; esto �ltimo est� disponible por
compatibilidad con versiones anteriores de flex
, y porque puede
utilizarse para conmutar ficheros de entrada en medio del an�lisis.
Tambi�n se puede utilizar para desechar el buffer de entrada actual,
invoc�ndola con un argumento igual a yyin
; pero mejor es usar
YY_FLUSH_BUFFER
(see section Acciones). F�jese que `yyrestart()'
no reinicializa la condici�n de arranque a INITIAL
(see section Condiciones de arranque).
Si `yylex()' para el an�lisis debido a la ejecuci�n de una
sentencia return
en una de las acciones, el analizador podr�a ser
llamado de nuevo y este reanudar�a el an�lisis donde lo dej�.
Por defecto (y por razones de eficiencia), el analizador usa lecturas
por bloques en lugar de simples llamadas a `getc()' para leer
caracteres desde yyin
. La manera en la que toma su entrada se
puede controlar definienfo la macro YY_INPUT
. La secuencia de
llamada para YY_INPUT es "YY_INPUT(buf,result,max_size)". Su acci�n es
poner hasta max_size caracteres en el array de caracteres
buf y devolver en la variable entera result bien o el n�mero
de caracteres le�dos o la constante YY_NULL (0 en sistemas Unix) para
indicar EOF. Por defecto YY_INPUT lee desde la variable global puntero
a fichero "yyin".
Una definici�n de ejemplo para YY_INPUT (en la secci�n de definiciones del fichero de entrada) es:
%{ #define YY_INPUT(buf,result,max_size) \ { \ int c = getchar(); \ result = (c == EOF) ? YY_NULL : (buf[0] = c, 1); \ } %}
Esta definici�n cambiar� el procesamiento de la entrada para que suceda un caracter a la vez.
Cuando el analizador reciba una indicaci�n de fin-de-fichero desde
YY_INPUT, entonces esta comprueba la funci�n `yywrap()'. Si
`yywrap()' devuelve falso (cero), entonces se asume que la funci�n
ha ido m�s all� y ha preparado yyin
para que apunte a otro
fichero de entrada, y el an�lisis contin�a. Si este retorna verdadero
(no-cero), entonces el analizador termina, devolviendo un 0 a su
invocador. F�jese que en cualquier caso, la condici�n de arranque
permanece sin cambios; esta no vuelve a ser INITIAL
.
Si no proporciona su propia versi�n de `yywrap()', entonces debe bien o usar `%option noyywrap' (en cuyo caso el analizador se comporta como si `yywrap()' devolviera un 1), o debe enlazar con `-lfl' para obtener la versi�n por defecto de la rutina, que siempre devuelve un 1.
Hay disponibles tres rutinas para analizar desde buffers de memoria en
lugar de desde ficheros: `yy_scan_string()',
`yy_scan_bytes()', e `yy_scan_buffer()'. Las trataremos en la
section M�ltiples buffers de entrada.
El analizador escribe su salida con `ECHO' a la variable global
yyout
(por defecto, stdout), que el usuario podr�a redefinir
asign�ndole cualquier otro puntero a FILE
.
flex
dispone de un mecanismo para activar reglas
condicionalmente. Cualquier regla cuyo patr�n se prefije con "<sc>"
�nicamente estar� activa cuando el analizador se encuentre en la
condici�n de arranque llamada "sc". Por ejemplo,
<STRING>[^"]* { /* se come el cuerpo de la cadena ... */ ... }
estar� activa solamente cuando el analizador est� en la condici�n de arranque "STRING", y
<INITIAL,STRING,QUOTE>\. { /* trata una secuencia de escape ... */ ... }
estar� activa solamente cuando la condici�n de arranque actual sea o bien "INITIAL", "STRING", o "QUOTE".
Las condiciones de arranque se declaran en la (primera) secci�n de
definiciones de la entrada usando l�neas sin sangrar comenzando con
`%s' � `%x' seguida por una lista de nombres. Lo primero
declara condiciones de arranque inclusivas, lo �ltimo condiciones
de arranque exclusivas.
Una condici�n de arranque se activa utilizando la acci�n
BEGIN
. Hasta que se ejecute la pr�xima acci�n BEGIN
,
las reglas con la condici�n de arranque dada estar�n activas y las
reglas con otras condiciones de arranque estar�n inactivas.
Si la condici�n de arranque es inclusiva, entonces las reglas sin
condiciones de arranque tambi�n estar�n activas. Si es exclusiva,
entonces s�lamente las reglas calificadas con la condici�n de
arranque estar�n activas. Un conjunto de reglas dependientes de la misma
condici�n de arranque exclusiva describe un analizador que es
independiente de cualquiera de las otras reglas en la entrada de flex
.
Debido a esto, las condiciones de arranque exclusivas hacen f�cil la
especificaci�n de "mini-esc�neres" que analizan porciones de la entrada
que son sint�cticamente diferentes al resto (p.ej., comentarios).
Si la distinci�n entre condiciones de arranque inclusivas o exclusivas es a�n un poco vaga, aqu� hay un ejemplo simple que ilustra la conexi�n entre las dos. El conjunto de reglas:
%s ejemplo %% <ejemplo>foo hacer_algo(); bar algo_mas();
es equivalente a
%x ejemplo %% <ejemplo>foo hacer_algo(); <INITIAL,ejemplo>bar algo_mas();
Sin el calificador `<INITIAL,example>', el patr�n `bar' en el
segundo ejemplo no estar� activo (es decir, no puede emparejarse) cuando
se encuentre en la condici�n de arranque `example'. Si hemos usado
`<example>' para calificar `bar', aunque, entonces este
�nicamente estar� activo en `example' y no en INITIAL
,
mientras que en el primer ejemplo est� activo en ambas, porque en el
primer ejemplo la condici�n de arranque `example' es una condici�n
de arranque inclusiva (`%s').
F�jese tambi�n que el especificador especial de la condici�n de arranque `<*>' empareja todas las condiciones de arranque. As�, el ejemplo anterior tambi�n pudo haberse escrito;
%x ejemplo %% <ejemplo>foo hacer_algo(); <*>bar algo_mas();
La regla por defecto (hacer un `ECHO' con cualquier caracter sin emparejar) permanece activa en las condiciones de arranque. Esta es equivalente a:
<*>.|\n ECHO;
`BEGIN(0)' retorna al estado original donde solo las reglas sin condiciones de arranque est�n activas. Este estado tambi�n puede referirse a la condici�n de arranque "INITIAL", as� que `BEGIN(INITIAL)' es equivalente a `BEGIN(0)'. (No se requieren los par�ntesis alrededor del nombre de la condici�n de arranque pero se considera de buen estilo.)
Las acciones BEGIN
pueden darse tambi�n como c�digo sangrado al
comienzo de la secci�n de reglas. Por ejemplo, lo que viene a
continuaci�n har� que el analizador entre en la condici�n de arranque
"ESPECIAL" siempre que se llame a `yylex()' y la variable global
entra_en_especial
sea verdadera:
int entra_en_especial; %x ESPECIAL %% if ( entra_en_especial ) BEGIN(ESPECIAL); <ESPECIAL>blablabla ...m�s reglas a continuaci�n...
Para ilustrar los usos de las condiciones de arranque, aqu� hay un analizador que ofrece dos interpretaciones diferentes para una cadena como "123.456". Por defecto este la tratar� como tres tokens, el entero "123", un punto (`.'), y el entero "456". Pero si la cadena viene precedida en la l�nea por la cadena "espera-reales" este la tratar� como un �nico token, el n�mero en coma flotante 123.456:
%{ #include <math.h> %} %s espera %% espera-reales BEGIN(espera); <espera>[0-9]+"."[0-9]+ { printf( "encontr� un real, = %f\n", atof( yytext ) ); } <espera>\n { /* este es el final de la l�nea, * as� que necesitamos otro * "espera-numero" antes de * que volvamos a reconocer m�s * n�meros */ BEGIN(INITIAL); } [0-9]+ { printf( "encontr� un entero, = %d\n", atoi( yytext ) ); } "." printf( "encontr� un punto\n" );
Aqu� est� un analizador que reconoce (y descarta) comentarios de C mientras mantiene una cuenta de la l�nea actual de entrada.
%x comentario %% int num_linea = 1; "/*" BEGIN(comentario); <comentario>[^*\n]* /* come todo lo que no sea '*' */ <comentario>"*"+[^*/\n]* /* come '*'s no seguidos por '/' */ <comentario>\n ++num_linea; <comentario>"*"+"/" BEGIN(INITIAL);
Este analizador se complica un poco para emparejar tanto texto como le sea posible en cada regla. En general, cuando se intenta escribir un analizador de alta velocidad haga que cada regla empareje lo m�s que pueda, ya que esto es un buen logro.
F�jese que los nombres de las condiciones de arranque son realmente valores enteros y pueden ser almacenados como tales. As�, lo anterior podr�a extenderse de la siguiente manera:
%x comentario foo %% int num_linea = 1; int invocador_comentario; "/*" { invocador_comentario = INITIAL; BEGIN(comentario); } ... <foo>"/*" { invocador_comentario = foo; BEGIN(comentario); } <comentario>[^*\n]* /* se come cualquier cosa que no sea un '*' */ <comentario>"*"+[^*/\n]* /* se come '*'s que no continuen con '/' */ <comentario>\n ++num_linea; <comentario>"*"+"/" BEGIN(invocador_comentario);
Adem�s, puede acceder a la condici�n de arranque actual usando la macro
de valor entero YY_START
. Por ejemplo, las asignaciones
anteriores a invocador_comentario
podr�an escribirse en su lugar
como
invocador_comentario = YY_START;
Flex ofrece YYSTATE
como un alias para YY_START
(ya que es
lo que usa lex
de AT&T).
F�jese que las condiciones de arranque no tienen su propio espacio de nombres; los %s's y %x's declaran nombres de la misma manera que con #define's.
Finalmente, aqu� hay un ejemplo de c�mo emparejar cadenas entre comillas al estilo de C usando condiciones de arranque exclusivas, incluyendo secuencias de escape expandidas (pero sin incluir la comprobaci�n de cadenas que son demasiado largas):
%x str %% char string_buf[MAX_STR_CONST]; char *string_buf_ptr; \" string_buf_ptr = string_buf; BEGIN(str); <str>\" { /* se vio la comilla que cierra - todo est� hecho */ BEGIN(INITIAL); *string_buf_ptr = '\0'; /* devuelve un tipo de token de cadena constante y * el valor para el analizador sint�ctico */ } <str>\n { /* error - cadena constante sin finalizar */ /* genera un mensaje de error */ } <str>\\[0-7]{1,3} { /* secuencia de escape en octal */ int resultado; (void) sscanf( yytext + 1, "%o", &resultado ); if ( resultado > 0xff ) /* error, constante fuera de rango */ *string_buf_ptr++ = resultado; } <str>\\[0-9]+ { /* genera un error - secuencia de escape err�nea; * algo como '\48' o '\0777777' */ } <str>\\n *string_buf_ptr++ = '\n'; <str>\\t *string_buf_ptr++ = '\t'; <str>\\r *string_buf_ptr++ = '\r'; <str>\\b *string_buf_ptr++ = '\b'; <str>\\f *string_buf_ptr++ = '\f'; <str>\\(.|\n) *string_buf_ptr++ = yytext[1]; <str>[^\\\n\"]+ { char *yptr = yytext; while ( *yptr ) *string_buf_ptr++ = *yptr++; }
A menudo, como en alguno de los ejemplos anteriores, uno acaba escribiendo un buen n�mero de reglas todas precedidas por la(s) misma(s) condici�n(es) de arranque. Flex hace esto un poco m�s f�cil y claro introduciendo la noci�n de �mbito de la condici�n de arranque. Un �mbito de condici�n de arranque comienza con:
<SCs>{
Donde `SCs' es una lista de una o m�s condiciones de arranque. Dentro del �mbito de la condici�n de arranque, cada regla autom�ticamente tiene el prefijo `<SCs>' aplicado a esta, hasta un `}' que corresponda con el `{' inicial. As�, por ejemplo,
<ESC>{ "\\n" return '\n'; "\\r" return '\r'; "\\f" return '\f'; "\\0" return '\0'; }
es equivalente a:
<ESC>"\\n" return '\n'; <ESC>"\\r" return '\r'; <ESC>"\\f" return '\f'; <ESC>"\\0" return '\0';
Los �mbitos de las condiciones de arranque pueden anidarse.
Est�n disponibles tres rutinas para manipular pilas de condiciones de arranque:
BEGIN
.
La pila de las condiciones de arranque crece din�micamente y por ello no tiene asociada ninguna limitaci�n de tama�o. Si la memoria se agota, se aborta la ejecuci�n del programa.
Para usar pilas de condiciones de arranque, su analizador debe incluir una directiva `%option stack' (see section Opciones).
Algunos analizadores (tales como aquellos que aceptan ficheros
"incluidos") requieren la lectura de varios flujos de entrada. Ya que
los analizadores de flex
hacen mucho uso de buffers, uno no puede
controlar de d�nde ser� le�da la siguiente entrada escribiendo
s�mplemente un YY_INPUT
que sea sensible al contexto del
an�lisis. A YY_INPUT
s�lo se le llama cuando el analizador
alcanza el final de su buffer, que podr�a ser bastante tiempo despu�s de
haber analizado una sentencia como un "include" que requiere el cambio
de la fuente de entrada.
Para solventar este tipo de problemas, flex
provee un mecanismo
para crear y conmutar entre varios buffers de entrada. Un buffer de
entrada se crea usando:
YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )
que toma un puntero a FILE
y un tama�o "size" y crea un buffer
asociado con el fichero dado y lo suficientemente grande para mantener
size caracteres (cuando dude, use YY_BUF_SIZE
para el
tama�o). Este devuelve un handle YY_BUFFER_STATE
, que podr�a
pasarse a otras rutinas (ver m�s abajo). El tipo de
YY_BUFFER_STATE
es un puntero a una estructura opaca
struct
yy_buffer_state
, de manera que podr�a inicializar
de forma segura variables YY_BUFFER_STATE
a
`((YY_BUFFER_STATE) 0)' si lo desea, y tambi�n hacer referencia a
la estructura opaca para declarar correctamente buffers de entrada en
otros ficheros fuente adem�s de los de su analizador. F�jese que el
puntero a FILE
en la llamada a yy_create_buffer
se usa
solamente como el valor de yyin
visto por YY_INPUT
; si
usted redefine YY_INPUT
de manera que no use m�s a yyin
,
entonces puede pasar de forma segura un puntero FILE
nulo a
yy_create_buffer
. Se selecciona un buffer en particular a
analizar utilizando:
void yy_switch_to_buffer( YY_BUFFER_STATE nuevo_buffer )
conmuta el buffer de entrada del analizador de manera que los tokens
posteriores provienen de nuevo_buffer. F�jese que
`yy_switch_to_buffer()' podr�a usarlo yywrap() para arreglar las
cosas para un an�lisis continuo, en lugar de abrir un nuevo fichero y
que yyin
apunte a este. F�jese tambi�n que cambiar las fuentes
de entrada ya sea por medio de `yy_switch_to_buffer()' o de
`yywrap()' no cambia la condici�n de arranque.
void yy_delete_buffer( YY_BUFFER_STATE buffer )
se usa para recuperar el almacenamiento asociado a un buffer. (El
buffer
puede ser nulo, en cuyo caso la rutina no hace nada.)
Puede tambi�n limpiar el contenido actual de un buffer usando:
void yy_flush_buffer( YY_BUFFER_STATE buffer )
Esta funci�n descarta el contenido del buffer, de manera que la
pr�xima vez que el analizador intente emparejar un token desde el
buffer, este primero rellenar� el buffer utilizando YY_INPUT
.
`yy_new_buffer()' es un alias de `yy_create_buffer()', que se
ofrece por compatibilidad con el uso en C++ de new
y
delete
para crear y destruir objetos din�micos.
Finalmente, la macro YY_CURRENT_BUFFER
retorna un handle
YY_BUFFER_STATE
al buffer actual.
Aqu� hay un ejemplo del uso de estas propiedades para escribir un analizador que expande ficheros incluidos (la propiedad `<<EOF>>' se comenta en el section Reglas de fin-de-fichero):
/* el estado "incl" se utiliza para obtener el nombre * del fichero a incluir. */ %x incl %{ #define MAX_INCLUDE_DEPTH 10 YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH]; int include_stack_ptr = 0; %} %% include BEGIN(incl); [a-z]+ ECHO; [^a-z\n]*\n? ECHO; <incl>[ \t]* /* se come los espacios en blanco */ <incl>[^ \t\n]+ { /* obtiene el nombre de fichero a incluir */ if ( include_stack_ptr >= MAX_INCLUDE_DEPTH ) { fprintf( stderr, "Demasiados include anidados" ); exit( 1 ); } include_stack[include_stack_ptr++] = YY_CURRENT_BUFFER; yyin = fopen( yytext, "r" ); if ( ! yyin ) error( ... ); 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] ); } }
Se dispone de tres rutinas para preparar buffers de entrada para el
an�lisis de cadenas en memoria en lugar de archivos. Todas estas
crean un nuevo buffer de entrada para analizar la cadena, y devuelven
el correspondiente handle YY_BUFFER_STATE
(que usted deber�a
borrar con `yy_delete_buffer()' cuando termine con �l). Estas
tambi�n conmutan el nuevo buffer usando `yy_switch_to_buffer()', de
manera que la pr�xima llamada a `yylex()' comenzar� analizando la
cadena.
len
bytes (incluyendo posibles NUL's) comenzando desde el
punto bytes.
F�jese que ambas de estas funciones crean y analizan una copia de la cadena o bytes. (Esto podr�a ser deseable, ya que `yylex()' modifica el contenido del buffer que est� analizado.) Usted puede evitar la copia utilizando:
YY_END_OF_BUFFER_CHAR
(ASCII NUL). Estos dos �ltimos bytes no se
analizan; as�, el an�lisis consta de `base[0]' hasta
`base[size-2]', inclusive.
Si se equivoca al disponer base de esta manera (es decir, olvidar
los dos YY_END_OF_BUFFER_CHAR
bytes finales), entonces
`yy_scan_buffer()' devuelve un puntero nulo en lugar de crear un
nuevo buffer de entrada.
El tipo yy_size_t
es un tipo entero con el que puede hacer una
conversi�n a una expresi�n entera para reflejar el tama�o del buffer.
La regla especial "<<EOF>>" indica las acciones que deben tomarse cuando se encuentre un fin-de-fichero e yywrap() retorne un valor distinto de cero (es decir, indica que no quedan ficheros por procesar). La acci�n debe finalizar haciendo una de estas cuatro cosas:
yyin
un nuevo fichero de entrada (en versiones
anteriores de flex, despu�s de hacer la asignaci�n deb�a llamar a la
acci�n especial YY_NEW_FILE
; esto ya no es necesario);
return
;
Las reglas <<EOF>> no deber�an usarse con otros patrones; estas deber�an calificarse con una lista de condiciones de arranque. Si se da una regla <<EOF>> sin calificar, esta se aplica a todas las condiciones de arranque que no tengan ya acciones <<EOF>>. Para especificar una regla <<EOF>> solamente para la condici�n de arranque inicial, use
<INITIAL><<EOF>>
Estas reglas son �tiles para atrapar cosas tales como comentarios sin final. Un ejemplo:
%x comilla %% ...otras reglas que tengan que ver con comillas... <comilla><<EOF>> { error( "comilla sin cerrar" ); yyterminate(); } <<EOF>> { if ( *++filelist ) yyin = fopen( *filelist, "r" ); else yyterminate(); }
La macro YY_USER_ACTION
puede definirse para indicar una acci�n
que siempre se ejecuta antes de la acci�n de la regla emparejada. Por
ejemplo, podr�a declararse con #define para que llame a una rutina que
convierta yytext a min�sculas. Cuando se invoca a YY_USER_ACTION
,
la variable yy_act
da el n�mero de la regla emparejada (las
reglas est�n numeradas comenzando en 1). Suponga que quiere medir la
frecuencia con la que sus reglas son emparejadas. Lo que viene a
continuaci�n podr�a hacer este truco:
#define YY_USER_ACTION ++ctr[yy_act]
donde ctr
en un vector que mantiene la cuenta para las diferentes
reglas. F�jese que la macro YY_NUM_RULES
da el n�mero total de
reglas (incluyendo la regla por defecto, incluso si usted usa
`-s'), as� que una declaraci�n correcta para ctr
es:
int ctr[YY_NUM_RULES];
La macro YY_USER_INIT
podr�a definirse para indicar una acci�n
que siempre se ejecuta antes del primer an�lisis (y antes de que se haga
la inicializaci�n interna del analizador). Por ejemplo, este podr�a
usarse para llamar a una rutina que lea una tabla de datos o abrir un
fichero de registro.
La macro `yy_set_interactive(is_interactive)' se puede usar para controlar si el buffer actual se considera interactivo. Un buffer interactivo se procesa m�s lentamente, pero debe usarse cuando la fuente de entrada del analizador es realmente interactiva para evitar problemas debidos a la espera para el llenado de los buffers (ver el comentario de la bandera `-I' en la section Opciones). Un valor distinto de cero en la invocaci�n de la macro marcar� el buffer como interactivo, un valor de cero como no-interactivo. F�jese que el uso de esta macro no tiene en cuenta `%option always-interactive' o `%option never-interactive' (see section Opciones). `yy_set_interactive()' debe invocarse antes del comienzo del an�lisis del buffer que es considerado (o no) interactivo.
La macro `yy_set_bol(at_bol)' puede usarse para controlar si el contexto del buffer de an�lisis actual para el pr�ximo emparejamiento de token se hace como si se encontrara al principio de una l�nea. Un argumento de la macro distinto de cero hace activas a las reglas sujetas a `^', mientras que un argumento igual a cero hacer inactivas a las reglas con `^'.
La macro `YY_AT_BOL()' devuelve verdadero si el pr�ximo token analizado a partir del buffer actual tendr� activas las reglas `^', de otra manera falso.
En el analizador generado, las acciones est�n recogidas en una gran
sentencia switch y separadas usando YY_BREAK
, que puede ser
redefinida. Por defecto, este es s�mplemente un "break", para separar
la acci�n de cada regla de las reglas que le siguen. Redefiniendo
YY_BREAK
permite, por ejemplo, a los usuarios de C++ que #define
YY_BREAK no haga nada (�mientras tengan cuidado para que cada regla
finalice con un "break" o un "return"!) para evitar que sufran los
avisos de sentencias inalcanzables cuando debido a que la acci�n de la
regla finaliza con un "return", el YY_BREAK
es inaccesible.
Esta secci�n resume los diferentes valores disponibles al usuario en las acciones de la regla.
yytext
se declara en su
lugar como `char yytext[YYLMAX]', donde YYLMAX
es la
definicion de una macro que puede redefinir en la primera secci�n si no
le gusta el valor por defecto (generalmente 8KB). El uso de
`%array' produce analizadores algo m�s lentos, pero el valor de
yytext
se vuelve inmune a las llamadas a `input()' y
`unput()', que potencialmente destruyen su valor cuando
yytext
es un puntero a caracter. El opuesto de `%array' es
`%pointer', que se encuentra por defecto.
Usted no puede utilizar `%array' cuando genera analizadores como
clases de C++ (la bandera `-+').
flex
lee por
defecto. Este podr�a redefinirse pero hacerlo solo tiene sentido antes
de que el an�lisis comience o despu�s de que se haya encontrado un EOF.
Cambi�ndolo en medio del an�lisis tendr� resultados inesperados ya que
flex
utiliza buffers en su entrada; use `yyrestart()' en su
lugar. Una vez que el an�lisis termina debido a que se ha visto un
fin-de-fichero, puede asignarle a yyin
el nuevo fichero de
entrada y entonces llamar al analizador de nuevo para continuar analizando.
yyin
apunte al nuevo fichero de entrada. El cambio al nuevo
fichero es inmediato (cualquier entrada contenida en el buffer
previamente se pierde). F�jese que llamando a `yyrestart()' con
yyin
como argumento de esta manera elimina el buffer de entradda
actual y contin�a analizando el mismo fichero de entrada.
YY_CURRENT_BUFFER
devuelve un handle YY_BUFFER_STATE
al
buffer actual.
YY_START
devuelve un valor entero correspondiente a la condici�n
de arranque actual. Posteriormente puede usar este valor con
BEGIN
para retornar a la condici�n de arranque.
Uno de los usos principales de flex
es como compa�ero del
generador de analizadores sint�cticos yacc
. Los analizadores de
yacc
esperan invocar a una rutina llamada `yylex()' para
encontrar el pr�ximo token de entrada. La rutina se supone que devuelve
el tipo del pr�ximo token adem�s de poner cualquier valor asociado en la
variable global yylval
. Para usar flex
con yacc
,
uno especifica la opci�n `-d' de yacc
para intruirle a que
genere el fichero `y.tab.h' que contiene las definiciones de todos
los `%tokens' que aparecen en la entrada de yacc
. Entonces
este archivo se incluye en el analizador de flex
. Por ejemplo, si
uno de los tokens es "TOK_NUMERO", parte del analizador podr�a parecerse a:
%{ #include "y.tab.h" %} %% [0-9]+ yylval = atoi( yytext ); return TOK_NUMERO;
flex [-bcdfhilnpstvwBFILTV78+? -C[aefFmr] -osalida -Pprefijo -Sesqueleto] [--help --version] [nombrefichero ...]
flex
tiene las siguientes opciones:
stderr
una l�nea de la forma:
--accepting rule at line 53 ("el texto emparejado")El n�mero de l�nea hace referencia al lugar de la regla en el fichero que define al analizador (es decir, el fichero que se le introdujo a flex). Los mensajes tambi�n se generan cuando el analizador retrocede, acepta la regla por defecto, alcanza el final de su buffer de entrada (o encuentra un NUL; en este punto, los dos parecen lo mismo en lo que le concierne al analizador), o alcance el fin-de-fichero.
flex
por
stdout
y entonces finaliza. `-?' y `--help' son
sin�nimos de `-h'.
flex
que genere un analizador case-insensitive.
Se ignorar� si las letras en los patrones de entrada de flex
son
en may�sculas o en min�sculas, y los tokens en la entrada ser�n
emparejados sin tenerlo en cuenta. El texto emparejado dado en
yytext
tendr� las may�sculas y min�sculas preservadas (es decir,
no se convertir�n).
lex
de AT&T. F�jese que esto no significa una compatibilidad
completa. El uso de esta opci�n cuesta una cantidad considerable de
rendimiento, y no puede usarse con las opciones `-+', `-f',
`-F', `-Cf',
� `-CF'. Para los detalles a cerca de la compatibilidad que se
ofrece, vea la section Incompatibilidades con lex
y POSIX.
Esta opci�n tambi�n hace que se defina el nombre
YY_FLEX_LEX_COMPAT
en el analizador generado.
flex
que provocar�n p�rdidas serias de rendimiento en el
analizador resultante. Si indica esta bandera dos veces, tambi�n
obtendr� comentarios que tratan de las propiedades que producen p�rdidas
menores de rendimiento.
F�jese que el uso de REJECT
, `%option yylineno', y el
contexto posterior variable (see section Deficiencias / Errores) supone
una penalizaci�n substancial del rendimiento; el uso de
`yymore()', el operador `^', y la bandera `-I' supone
penalizaciones del rendimiento menores.
stdout
) se suprima. Si el analizador
encuentra entrada que no es reconocida por ninguna de sus reglas, este
aborta con un error. Esta opci�n es �til para encontrar agujeros en el
conjunto de reglas del analizador.
flex
que escriba el analizador que genera a la salida
est�ndar en lugar de en `lex.yy.c'.
flex
deber�a escribir en stderr
un sumario
de estad�sticas respecto al analizador que genera. La mayor�a de las
estad�sticas no tienen significado para el usuario casual de
flex
, pero la primera l�nea identifica la versi�n de flex
(la misma que se informa con `-V'), y la pr�xima l�nea las banderas
utilizadas cuando se genera el analizador, incluyendo aquellas que se
encuentran activadas por defecto.
flex
que genere un analizador batch, que es lo opuesto al
analizador interactivo generador por `-I' (ver m�s abajo).
En general, use `-B' cuando est� seguro de que su analizador
nunca se usar� de forma interactiva, y quiere con esto exprimir un
poco m�s el rendimiento. Si por el contrario su
objetivo es exprimirlo mucho m�s, deber�a estar
utilizando la opci�n `-Cf' � `-CF' (comentadas m�s abajo), que
activa `-B' autom�ticamente de todas maneras.
"case" return TOK_CASE; "switch" return TOK_SWITCH; ... "default" return TOK_DEFAULT; [a-z]+ return TOK_ID;entonces ser� mejor que utilice la representaci�n de la tabla completa. Si s�lo est� presente la regla "identificador" y utiliza una tabla hash o algo parecido para detectar palabras clave, mejor utilice `-F'. Esta opci�n es equivalente a `-CFr' (ver m�s abajo). Esta opci�n no puede utilizarse con `-+'.
flex
que genere un analizador interactivo. Un
analizador interactivo es uno que solo mira hacia delante para decidir
que token ha sido reconocido �nicamente si debe hacerlo. Resulta que
mirando siempre un caracter extra hacia delante, incluso si el
analizador ya ha visto suficiente texto para eliminar la ambig�edad del
token actual, se es un poco m�s r�pido que mirando solamente cuando es
necesario. Pero los analizadores que siempre miran hacia delante
producen un comportamiento interactivo mal�simo; por ejemplo, cuando un
usuario teclea una l�nea nueva, esta no se reconoce como un token de
l�nea nueva hasta que introduzca otro token, que a menudo
significa introducir otra l�nea completa.
Los analizadores de flex
por defecto son interactivos a
menos que use la opci�n `-Cf' � `-CF' de compresi�n de tablas
(ver m�s abajo). Esto es debido a que si est� buscando un rendimiento alto
tendr�a que estar utilizando una de estas opciones, as� que si no lo ha
hecho flex
asume que prefiere cambiar un poco de rendimiento en
tiempo de ejecuci�n en beneficio de un comportamiento iteractivo
intuitivo. F�jese tambi�n que no puede utilizar `-I'
conjuntamente con `-Cf' � `-CF'. As�, esta opci�n no se
necesita realmente; est� activa por defecto para todos esos casos en los
que se permite.
Usted puede forzar al analizador que no sea interactivo usando
`-B' (ver m�s arriba).
flex
que no genere directivas `#line'. Sin esta
opci�n, flex
acribilla al analizador generado con directivas
`#line' para que los mensajes de error en las acciones est�n localizadas
correctamente respecto al fichero original de flex
(si los
errores son debidos al c�digo en el fichero de entrada), o a
`lex.yy.c' (si los errores son fallos de flex
--deber�a
informar de este tipo de errores a la direcci�n de correo dada m�s abajo).
flex
se ejecute en modo de traza
. Este generar�
un mont�n de mensajes en stderr
relativos a la forma de la
entrada y el aut�mata finito no-determinista o determinista resultante.
Esta opci�n generalmente es para usarla en el mantenimiento de flex
.
stdout
y
sale. `--version' es un sin�nimo de `-V'.
flex
que genere un analizador de 7-bits, es decir, uno
que s�lo puede reconocer caracteres de 7-bits en su entrada. La ventaja
de usar `-7' es que las tablas del analizador pueden ser hasta la
mitad del tama�o de aquellas generadas usando la opci�n `-8' (ver
m�s abajo). La desventaja es que tales analizadores a menudo se cuelgan
o revientan si su entrada contiene caracteres de 8-bits.
F�jese, sin embargo, que a menos que genere su analizador utilizando
las opciones de compresi�n de tablas `-Cf' � `-CF', el uso de
`-7' ahorrar� solamente una peque�a cantidad de espacio en la
tabla, y har� su analizador considerablemente menos portable. El
comportamiento por defecto de flex
es generar un analizador de
8-bits a menos que use `-Cf' � `-CF', en cuyo caso flex
por defecto genera analizadores de 7-bits a menos que su sistema siempre
est� configurado para generar analizadores de 8-bits (a menudo este ser� el
caso de los sistemas fuera de EEUU). Puede decir si flex gener� un
analizador de 7 u 8 bits inspeccionando el sumario de banderas en la
salida de `-v' como se describi� anteriormente.
F�jese que si usa `-Cfe' � `-CFe' (esas opciones de compresi�n
de tablas, pero tambi�n el uso de clases de equivalencia como se
comentar� m�s abajo), flex genera a�n por defecto un analizador de
8-bits, ya que normalmente con estas opciones de compresi�n las tablas
de 8-bits completas no son mucho m�s caras que las tablas de 7-bits.
flex
que genere un analizador de 8-bits, es decir, uno
que puede reconocer caracteres de 8-bits. Esta bandera s�lo es
necesaria para analizadores generados usando `-Cf' � `-CF', ya
que de otra manera flex por defecto genera un analizador de 8-bits de
todas formas.
Vea el comentario sobre `-7' m�s arriba a cerca del comportamiento
por defecto de flex y la discusi�n entre los analizadores de 7-bits y
8-bits.
flex
que construya clases de equivalencia, es
decir, conjunto de caracteres que tienen identicas propiedades l�xicas
(por ejemplo, si la �nica aparici�n de d�gitos en la entrada de
flex
es en la clase de caracteres "[0-9]" entonces los d�gitos
`0', `1', ..., `9' se pondr�n todos en la misma clase de
equivalencia). Las clases de equivalencia normalmente ofrecen notables
reducciones en los tama�os de los ficheros finales de tabla/objeto
(t�picamente un factor de 2-5) y son juiciosamente bastante baratos en
cuanto al rendimiento (una localizaci�n en un vector por caracter
analizado).
`-Cf' especifica que se deben generar las tablas del analizador
completas ---flex
no deber�a comprimir las tablas tomando
ventaja de las funciones de transici�n similares para diferentes estados.
`-CF' especifica que deber�a usarse la representaci�n del
analizador r�pido alternativo (descrito anteriormente en la bandera `-F'
) Esta opci�n no puede usarse con `-+'.
flex
que construya clases de meta-equivalencias,
que son conjuntos de clases de equivalencia (o caracteres, si las clases
de equivalencia no se est�n usando) que comunmente se usan de forma
conjunta. Las clases de meta-equivalencias son a menudo un gran
ahorro cuando se usan tablas comprimidas, pero tienen un impacto
moderado en el rendimiento (uno o dos tests "if" y una localizaci�n en
un array por caracter analizado).
`-Cr' hace que el analizador generado elimine el uso de la
librer�a de E/S est�ndar para la entrada. En lugar de llamar a
`fread()' o a `getc()', el analizador utilizar� la llamada al
sistema `read()', produciendo una ganancia en el rendimiento que
var�a de sistema en sistema, pero en general probablemente es
insignificante a menos que tambi�n est� usando `-Cf' � `-CF'.
El uso de `-Cr' puede producir un comportamiento extra�o si, por
ejemplo, lee de yyin
usando stdio antes de llamar al analizador
(porque el analizador perder� cualquier texto que sus lecturas
anteriores dejaron en el buffer de entrada de stdio).
`-Cr' no tiene efecto si usted define YY_INPUT
(see section El esc�ner generado).
Con solamente `-C' se especifica que las tablas del analizador deber�an
comprimirse pero no deber�a utilizarse ni las clases de equivalencia ni
las clases de meta-equivalencias.
Las opciones `-Cf' � `-CF' y `-Cm'
no tienen sentido juntas --no hay oportunidad para las clases de
meta-equivalencias si la tabla no est� siendo comprimida. De otra
forma las opciones podr�an mezclarse l�bremente, y son acumulativas.
La configuraci�n por defecto es `-Cem', que especifica que flex
deber�a generar clases de equivalencia y clases de meta-equivalencias.
Esta configuraci�n provee el mayor grado de compresi�n. Puede
llegarse a un compromiso entre analizadores de ejecuci�n m�s r�pida
con el coste de tablas mayores siendo generalmente verdadero lo siguiente:
lo m�s lento y peque�o -Cem -Cm -Ce -C -C{f,F}e -C{f,F} -C{f,F}a lo m�s r�pido y grandeF�jese que los analizadores con tablas m�s peque�as normalmente se generan y compilan de la forma m�s r�pida posible, as� que durante el desarrollo usted normalmente querr� usar como viene por defecto, compresi�n m�xima. `-Cfe' a menudo es un buen compromiso entre velocidad y tama�o para la producci�n de analizadores.
stdout
pero sus
directivas `#line' (vea la opci�n `-L' m�s arriba) hacen
referencia al fichero `salida'.
flex
para todas
las variables visibles globalmente y nombres de funciones para que sea
prefijo. Por ejemplo, `-Pfoo' cambia el nombre de
yytext
a `footext'. Este tambi�n cambia el nombre por
defecto del fichero de salida de `lex.yy.c' a
`lex.foo.c'. Aqu� est�n todos los nombres afectados:
yy_create_buffer yy_delete_buffer yy_flex_debug yy_init_buffer yy_flush_buffer yy_load_buffer_state yy_switch_to_buffer yyin yyleng yylex yylineno yyout yyrestart yytext yywrap(Si usted est� utilizando un analizador en C++, entonces �nicamente
yywrap
y yyFlexLexer
se ven afectados.) Dentro de su
analizador, puede a�n hacer referencia a las variables globales y
funciones usando cualquier versi�n de su nombre; pero externamente,
estas tienen el nombre modificado.
Esta opci�n le deja enlazar f�cilmente m�ltiples programas flex
conjuntamente en el mismo ejecutable. F�jese, sin embargo, que usando
esta opci�n tambi�n se renombra `yywrap()', de manera que ahora
debe o bien proveer su propia versi�n de la rutina (con el nombre
apropiado) para su analizador, o usar `%option noyywrap',
ya que enlazar con `-lfl' no podr� proveerle una por defecto.
flex
construye sus analizadores. Usted probablemente nunca necesitar�
utilizar esta opci�n a menos que este haciendo mantenimiento o
un desarrollo de flex
.
flex
tambi�n ofrece un mecanismo para controlar las opciones dentro de la
propia especificaci�n del analizador, en vez de a partir de la l�nea
de comando. Esto se hace incluyendo las directivas `%option'
en la primera secci�n de la especificaci�n del analizador. Usted
puede especificar varias opciones con una sola directiva `%option',
y varias directivas en la primera secci�n de su fichero de entrada de
flex.
La mayor�a de las opciones vienen dadas simplemente como nombres, opcionalmente precedidos por la palabra "no" (sin intervenir un espacio) para negar su significado. Las banderas de flex o su negaci�n son equivalentes a un n�mero:
7bit opci�n -7 8bit opci�n -8 align opci�n -Ca backup opci�n -b batch opci�n -B c++ opci�n -+ caseful o case-sensitive opuesto de -i (por defecto) case-insensitive o caseless opci�n -i debug opci�n -d default opuesto de la opci�n -s ecs opci�n -Ce fast opci�n -F full opci�n -f interactive opci�n -I lex-compat opci�n -l meta-ecs opci�n -Cm perf-report opci�n -p read opci�n -Cr stdout opci�n -t verbose opci�n -v warn opuesto de la opci�n -w (use "%option nowarn" para -w) array equivalente a "%array" pointer equivalente a "%pointer" (por defecto)
Algunas directivas `%option' ofrecen propiedades que de otra manera no est�n disponibles:
noyywrap
(ver m�s abajo).
yyin
e yyout
a stdin
y stdout
, en lugar del
que viene por defecto que es nil
. Algunos pogramas de lex
existentes dependen de este comportamiento, incluso si no sigue el ANSI
C, que no requiere que stdin
y stdout
sean constantes en
tiempo de compilaci�n.
flex
a generar un analizador que mantenga el n�mero de
la l�nea actual le�da desde su entrada en la variable global
yylineno
. Esta opci�n viene impl�cita con `%option
lex-compat'.
yyin
a un nuevo fichero y llame a
`yylex()' otra vez).
flex
analiza las acciones de sus reglas para determinar si utiliza las
propiedades REJECT
o `yymore()'. Las opciones
reject
e yymore
est�n disponibles para ignorar sus
decisiones siempre que use las opciones, o bien estableciendolas (p.ej.,
`%option reject') para indicar que la propiedad se utiliza
realmente, o desactiv�ndolas para indicar que no es utilizada (p.ej.,
`%option noyymore').
Tres opciones toman valores delimitados por cadenas, separadas por `=':
%option outfile="ABC"
es equivalente a `-oABC', y
%option prefix="XYZ"
es equivalente a `-PXYZ'. Finalmente,
%option yyclass="foo"
s�lo se aplica cuando se genera un analizador en C++ (opci�n `-+').
Este informa a flex
que ha derivado a `foo' como una
subclase de yyFlexLexer
, as� que flex
pondr� sus acciones
en la funci�n miembro `foo::yylex()' en lugar de
`yyFlexLexer::yylex()'. Este tambi�n genera una funci�n miembro
`yyFlexLexer::yylex()' que emite un error en tiempo de ejecuci�n
(invocando a `yyFlexLexer::LexerError()') si es llamada.
See section Generando esc�neres en C++, para informaci�n adicional.
Est�n disponibles un n�mero de opciones para los puristas de lint que desean suprimir la aparici�n de rutinas no necesarias en el analizador generado. Cada una de la siguientes, si se desactivan (p.ej., `%option nounput'), hace que la rutina correspondiente no aparezca en el analizador generado:
input, unput yy_push_state, yy_pop_state, yy_top_state yy_scan_buffer, yy_scan_bytes, yy_scan_string
(aunque `yy_push_state()' y sus amigas no aparecer�n de todas manera a menos que use `%option stack').
El principal objetivo de dise�o de flex
es que genere
analizadores de alto rendimiento. Este ha sido optimizado para
comportarse bien con conjuntos grandes de reglas. Aparte de los efectos
sobre la velocidad del analizador con las opciones de compresi�n de
tablas `-C' anteriormente introducidas, hay un n�mero de
opciones/acciones que degradan el rendimiento. Estas son, desde la m�s
costosa a la menos:
REJECT %option yylineno contexto posterior arbitrario conjunto de patrones que requieren retroceso %array %option interactive %option always-interactive `^' operador de comienzo de l�nea yymore()
siendo las tres primeras bastante costosas y las dos �ltimas bastante econ�micas. F�jese tambi�n que `unput()' se implementa como una llamada de rutina que potencialmente hace bastante trabajo, mientras que `yyless()' es una macro bastante econ�mica; as� que si est� devolviendo alg�n texto excedente que ha analizado, use `yyless()'.
REJECT
deber�a evitarse a cualquier precio cuando el rendimiento
es importante. Esta es una opci�n particularmente cara.
Es lioso deshacerse del retroceso y a menudo podr�a ser una cantidad de trabajo enorme para un analizador complicado. En principio, uno comienza utilizando la bandera `-b' para generar un archivo `lex.backup'. Por ejemplo, sobre la entrada
%% foo return TOK_KEYWORD; foobar return TOK_KEYWORD;
el fichero tiene el siguiente aspecto:
El estado #6 es no-aceptar - n�meros de l�nea asociados a la regla: 2 3 fin de transiciones: [ o ] transiciones de bloqueo: fin de archivo (EOF) [ \001-n p-\177 ] El estado #8 es no-aceptar - n�meros de l�nea asociados a la regla: 3 fin de transiciones: [ a ] transiciones de bloqueo: fin de archivo (EOF) [ \001-` b-\177 ] El estado #9 es no-aceptar - n�meros de l�nea asociados a la regla: 3 fin de transiciones: [ r ] transiciones de bloqueo: fin de archivo (EOF) [ \001-q s-\177 ] Las tablas comprimidas siempre implican un retroceso.
Las primeras l�neas nos dicen que hay un estado del analizador en el que se puede hacer una transici�n con una `o' pero no sobre cualquier otro caracter, y que en ese estado el texto recientemente analizado no empareja con ninguna regla. El estado ocurre cuando se intenta emparejar las reglas encontradas en las l�neas 2 y 3 en el fichero de entrada. Si el analizador est� en ese estado y entoces lee cualquier cosa que no sea una `o', tendr� que retroceder para encontrar una regla que empareje. Con un poco de an�lisis uno puede ver que este debe ser el estado en el que se est� cuando se ha visto "fo". Cuando haya ocurrido, si se ve cualquier cosa que no sea una `o', el analizador tendr� que retroceder para simplemente emparejar la `f' (por la regla por defecto).
El comentario que tiene que ver con el Estado #8 indica que hay un problema cuando se analiza "foob". En efecto, con cualquier caracter que no sea una `a', el analizador tendr� que retroceder para aceptar "foo". De forma similar, el comentario para el Estado #9 tiene que ver cuando se ha analizado "fooba" y no le sigue una `r'.
El comentario final nos recuerda que no mecere la pena todo el trabajo para eliminar el retroceso de las reglas a menos que estemos usando `-Cf' � `-CF', y que no hay ninguna mejora del rendimiento haci�ndolo con analizadores comprimidos.
La manera de quitar los retrocesos es a�adiendo reglas de "error":
%% foo return TOK_KEYWORD; foobar return TOK_KEYWORD; fooba | foob | fo { /* falsa alarma, realmente no es una palabra clave */ return TOK_ID; }
La eliminaci�n de retroceso en una lista de palabras clave tambi�n puede hacerse utilizando una regla "atr�palo-todo":
%% foo return TOK_KEYWORD; foobar return TOK_KEYWORD; [a-z]+ return TOK_ID;
Normalmente esta es la mejor soluci�n cuando sea adecuada.
Los mensajes sobre retrocesos tienden a aparecer en cascada.
Con un conjunto complicado de reglas no es poco com�n obtener cientos
de mensajes. Si uno puede descifrarlos, sin embargo, a menudo s�lo
hay que tomar una docena de reglas o algo as� para eliminar los
retrocesos (ya que es f�cil cometer una equivocaci�n y tener una regla de
error que reconozca un token v�lido. Una posible caracter�stica
futura de flex
ser� a�adir reglas autom�ticamente para eliminar
el retroceso).
Es importante tener en cuenta que se obtienen los beneficios de eliminar el retroceso s�lo si elimina cada instancia del retroceso. Dejar solamente una significa que no ha ganado absolutamente nada.
El contexto posterior variable (donde la parte delantera y
posterior no tienen una longitud fija) supone casi la misma p�rdida de
rendimiento que REJECT
(es decir, substanciales). As� que cuando
sea posible una regla como esta:
%% raton|rata/(gato|perro) correr();
es mejor escribirla as�:
%% raton/gato|perro correr(); rata/gato|perro correr();
o as�
%% raton|rata/gato correr(); raton|rata/perro correr();
F�jese que aqu� la acci�n especial `|' no ofrece ning�n ahorro, y puede incluso hacer las cosas peor (see section Deficiencias / Errores).
Otro �rea donde el usuario puede incrementar el rendimiento del
analizador (y una que es m�s f�cil de implementar) surge del hecho que
cuanto m�s tarde se empareje un token, m�s r�pido ir� el analizador.
Esto es debido a que con tokens grandes el procesamiento de la mayor�a
de los caracteres de entrada tiene lugar en el (corto) bucle de
an�lisis m�s interno, y no tiene que ir tan a menudo a hacer el
trabajo de m�s para constituir el entorno del analizador (p.ej.,
yytext
) para la acci�n. Recuerde el analizador para los
comentarios en C:
%x comentario %% int num_linea = 1; "/*" BEGIN(comentario); <comentario>[^*\n]* <comentario>"*"+[^*/\n]* <comentario>\n ++num_linea; <comentario>"*"+"/" BEGIN(INITIAL);
Esto podr�a acelerarse escribi�ndolo como:
%x comentario %% int num_linea = 1; "/*" BEGIN(comentario); <comentario>[^*\n]* <comentario>[^*\n]*\n ++num_linea; <comentario>"*"+[^*/\n]* <comentario>"*"+[^*/\n]*\n ++num_linea; <comentario>"*"+"/" BEGIN(INITIAL);
Ahora en lugar de que cada l�nea nueva requiera el procesamiento de otra regla, el reconocimiento de las l�neas nuevas se "distribuye" sobre las otras reglas para mantener el texto reconocido tan largo como sea posible. �F�jese que el a�adir reglas no ralentiza el analizador! La velocidad del analizador es independiente del n�mero de reglas o (dadas las consideraciones dadas al inicio de esta secci�n) cu�n complicadas sean las reglas respecto a operadores tales como `*' y `|'.
Un ejemplo final sobre la aceleraci�n de un analizador: suponga que quiere analizar un fichero que contiene identificadores y palabras clave, una por l�nea y sin ning�n caracter extra�o, y reconocer todas las palabras clave. Una primera aproximaci�n natural es:
%% asm | auto | break | ... etc ... volatile | while /* es una palabra clave */ .|\n /* no es una palabra clave */
Para eliminar el retroceso, introduzca una regla atr�palo-todo:
%% asm | auto | break | ... etc ... volatile | while /* es una palabra clave */ [a-z]+ | .|\n /* no es una palabra clave */
Ahora, si se garantiza que hay ex�ctamente una palabra por l�nea, entonces podemos reducir el n�mero total de emparejamientos por la mitad mezclando el reconocimiento de l�neas nuevas con las de los otros tokens:
%% asm\n | auto\n | break\n | ... etc ... volatile\n | while\n /* es una palabra clave */ [a-z]+\n | .|\n /* no es una palabra clave */
Uno tiene que ser cuidadoso aqu�, ya que hemos reintroducido retroceso
en el analizador. En particular, aunque nosotros sepamos que ah�
nunca habr�n otros caracteres en el flujo de entrada que no sean letras
o l�neas nuevas, flex
no puede figurarse eso, y planear� la
posible necesidad de retroceder cuando haya analizado un token como
"auto" y el pr�ximo caracter sea algo distinto a una l�nea nueva o una
letra. Previamente este podr�a entonces emparejar la regla "auto" y
estar todo hecho, pero ahora este no tiene una regla "auto", solamente
una regla "auto\n". Para eliminar la posibilidad de retroceso,
podr�amos o bien duplicar todas las reglas pero sin l�nea nueva al
final, o, ya que nunca esperamos encontrar tal entrada y por lo tanto ni
c�mo es clasificada, podemos introducir una regla atr�palo-todo m�s,
esta que no incluye una l�nea nueva:
%% asm\n | auto\n | break\n | ... etc ... volatile\n | while\n /* es una palabra clave */ [a-z]+\n | [a-z]+ | .|\n /* no es una palabra clave */
Compilado con `-Cf', esto es casi tan r�pido como lo que uno puede
obtener de un analizador de flex
para este problema en particular.
Una nota final: flex
es lento cuando empareja NUL's,
particularmente cuando un token contiene m�ltiples NUL's. Es mejor
escribir reglas que emparejen cortas cantidades de texto si se
anticipa que el texto incluir� NUL's a menudo.
Otra nota final en relaci�n con el rendimiento: tal y como se mencion�
en el section C�mo se empareja la entrada, el reajuste din�mico de
yytext
para acomodar tokens enormes es un proceso lento porque
ahora requiere que el token (inmenso) sea reanalizado desde el
principio. De esta manera si el rendimiento es vital, deber�a intentar
emparejar "grandes" cantidades de texto pero no "inmensas" cantidades,
donde el punto medio est� en torno a los 8K caracteres/token.
flex
ofrece dos maneras distintas de generar analizadores para
usar con C++. La primera manera es simplemente compilar un analizador
generado por flex
usando un compilador de C++ en lugar de un
compilador de C. No deber�a encontrarse ante ning�n error de
compilaci�n (por favor informe de cualquier error que encuentre a la
direcci�n de correo electr�nico dada en el section Autor).
Puede entonces usar c�digo C++ en sus acciones de las reglas en lugar de
c�digo C. F�jese que la fuente de entrada por defecto para su analizador
permanece como yyin
, y la repetici�n por defecto se hace a�n a
yyout
. Ambos permanecen como variables `FILE *' y no como
flujos
de C++.
Tambi�n puede utilizar flex
para generar un analizador como una
clase de C++, utilizando la opci�n `-+' (o, equivalentemente,
`%option c++'), que se especifica autom�ticamente si el nombre del
ejecutable de flex finaliza con un `+', tal como flex++
. Cuando
se usa esta opci�x, flex establece por defecto la generaci�n del
analizador al fichero `lex.yy.cc' en vez de `lex.yy.c'.
El analizador generado incluye el fichero de cabecera
`FlexLexer.h', que define el interfaz con las dos clases de C++.
La primera clase, FlexLexer
, ofrece una clase base abstracta
definiendo la interfaz a la clase del analizador general. Este provee
las siguientes funciones miembro:
yytext
.
yyleng
.
yy_flex_debug
(see section Opciones).
F�jese que debe construir el analizador utilizando
`%option debug' para incluir informaci�n de depuraci�n en este.
Tambi�n se proveen funciones miembro equivalentes a `yy_switch_to_buffer()', `yy_create_buffer()' (aunque el primer argumento es un puntero a objeto `istream*' y no un `FILE*'), `yy_flush_buffer()', `yy_delete_buffer()', y `yyrestart()' (de nuevo, el primer argumento es un puntero a objeto `istream*').
La segunda clase definida en `FlexLexer.h' es yyFlexLexer
,
que se deriva de FlexLexer
. Esta define las siguientes funciones
miembro adicionales:
yyFlexLexer
usando los flujos dados para la
entrada y salida. Si no se especifica, los flujos se establecen por
defecto a cin
y cout
, respectivamente.
yyFlexLexer
y quiere acceder a las funciones
y variables miembro de S dentro de `yylex()', entonces
necesita utilizar `%option yyclass="S"' para informar a flex
que estar� utilizando esa subclase en lugar de yyFlexLexer
. Es
este caso, en vez de generar `yyFlexLexer::yylex()', flex
genera `S::yylex()' (y tambi�n genera un substituto
`yyFlexLexer::yylex()' que llama a `yyFlexLexer::LexerError()'
si se invoca).
yyin
a new_in
(si no es nulo) e yyout
a
new_out
(idem), borrando el buffer de entrada anterior si se
reasigna yyin
.
Adem�s, yyFlexLexer
define las siguientes funciones virtuales
protegidas que puede redefinir en clases derivadas para adaptar el
analizador:
YY_INTERACTIVE
.
Si usted redefine LexerInput()
y necesita tomar acciones
distintas dependiendo de si el analizador est� analizando una fuente de
entrada interactivo o no, puede comprobar la presencia de este nombre
mediante `#ifdef'.
cerr
y finaliza.
F�jese que un objeto yyFlexLexer
contiene su estado de an�lisis
completo. As� puede utilizar tales objetos para crear analizadore
reentrantes. Puede hacer varias instancias de la misma clase
yyFlexLexer
, y puede combinar varias clases de analizadores en
C++ conjuntamente en el mismo programa usando la opci�n `-P'
comentada anteriormente.
Finalmente, note que la caracter�stica `%array' no est� disponible en clases de analizadores en C++; debe utilizar `%pointer' (por defecto).
Aqu� hay un ejemplo de un analizador en C++ simple:
// Un ejemplo del uso de la clase analizador en C++ de flex. %{ int mylineno = 0; %} string \"[^\n"]+\" ws [ \t]+ alpha [A-Za-z] dig [0-9] name ({alpha}|{dig}|\$)({alpha}|{dig}|[_.\-/$])* num1 [-+]?{dig}+\.?([eE][-+]?{dig}+)? num2 [-+]?{dig}*\.{dig}+([eE][-+]?{dig}+)? number {num1}|{num2} %% {ws} /* evita los espacios en blanco y tabuladores */ "/*" { int c; while((c = yyinput()) != 0) { if(c == '\n') ++mylineno; else if(c == '*') { if((c = yyinput()) == '/') break; else unput(c); } } } {number} cout << "n�mero " << YYText() << '\n'; \n mylineno++; {name} cout << "nombre " << YYText() << '\n'; {string} cout << "cadena " << YYText() << '\n'; %% int main( int /* argc */, char** /* argv */ ) { FlexLexer* lexer = new yyFlexLexer; while(lexer->yylex() != 0) ; return 0; }
Si desea crear varias (diferentes) clases analizadoras, use la bandera
`-P' (o la opci�n `prefix=') para renombrar cada
yyFlexLexer
a alg�n otro xxFlexLexer
. Entonces puede
incluir `<FlexLexer.h>' en los otros ficheros fuente una vez por
clase analizadora, primero renombrando yyFlexLexer
como se
presenta a continuaci�n:
#undef yyFlexLexer #define yyFlexLexer xxFlexLexer #include <FlexLexer.h> #undef yyFlexLexer #define yyFlexLexer zzFlexLexer #include <FlexLexer.h>
si, por ejemplo, usted utiliz� `%option prefix="xx"' para uno de sus analizadores y `%option prefix="zz"' para el otro.
IMPORTANTE: la forma actual de la clase analizadora es experimental y podr�a cambiar considerablemente entre versiones principales.
lex
y POSIX
flex
es una reescritura de la herramienta lex
del Unix de
AT&T (aunque las dos implementaciones no comparten ning�n c�digo), con
algunas extensiones e incompatibilidades, de las que ambas conciernen a
aquellos que desean escribir analizadores aceptables por cualquier
implementaci�n. Flex sigue completamente la especificaci�n POSIX de
lex
, excepto que cuando se utiliza `%pointer' (por defecto),
una llamada a `unput()' destruye el contenido de yytext
, que
va en contra de la especificaci�n POSIX.
En esta secci�n comentaremos todas las �reas conocidas de incompatibilidades entre flex, lex de AT&T, y la especificaci�n POSIX.
La opci�n `-l' de flex
activa la m�xima compatibilidad con
la implementaci�n original de lex
de AT&T, con el coste de una
mayor p�rdida de rendimiento en el analizador generado. Indicamos m�s
abajo qu� incompatibilidades pueden superarse usando la opci�n `-l'.
flex
es totalmente compatible con lex
con las siguientes
excepciones:
lex
sin documentar
yylineno
no se ofrece a menos que se use `-l' �
`%option yylineno'. yylineno
deber�a gestionarse por
buffer, en lugar de por analizador (simple variable
global). yylineno
no es parte de la especificaci�n POSIX.
EOF
.
La entrada en su lugar se controla definiendo la macro YY_INPUT
.
La restricci�n de flex
de que `input()' no puede redefinirse
va de acuerdo a la especificaci�n POSIX, que simplemente no especifica
ninguna manera de controlar la entrada del analizador que no sea
haciendo una asignaci�n inicial a yyin
.
flex
no son tan reentrantes como los
analizadores de lex
. En particular, si tiene un analizador
interactivo y un gestor de interrupci�n con long-jumps fuera del
analizador, y el analizador a continuaci�n se invoca de nuevo, podr�a
obtener el siguiente mensaje:
fatal flex scanner internal error--end of buffer missedPara volver al analizador, primero utilice
yyrestart( yyin );Vea que esta llamada eliminar� cualquier entrada en el buffer; normalmente esto no es un problema con un analizador interactivo. Dese cuenta tambi�n de que las clases analizadoras en C++ son reentrantes, as� que si usar C++ es una opci�n para usted, deber�a utilizarla. See section Generando esc�neres en C++, para los detalles.
yyout
(por defecto a stdout
).
`output()' no es parte de la especificaci�n POSIX.
lex
no acepta condiciones de arranque exclusivas (%x), aunque
est�n en la especificaci�n POSIX.
flex
las encierra entre
par�ntesis. Con lex, lo siguiente:
NOMBRE [A-Z][A-Z0-9]* %% foo{NOMBRE}? printf( "Lo encontr�\n" ); %%no reconocer� la cadena "foo" porque cuando la macro se expanda la regla es equivalente a "foo[A-Z][A-Z0-9]*?" y la precedencia es tal que el `?' se asocia con "[A-Z0-9]*". Con
flex
, la regla se
expandir� a "foo([A-Z][A-Z0-9]*)?" y as� la cadena "foo" se reconocer�.
F�jese que si la definici�n comienza con `^' o finaliza con
`$' entonces no se expande con par�ntesis, para permitir que
estos operadores aparezcan en las definiciones sin perder su significado
especial. Pero los operadores `<s>, /', y `<<EOF>>' no pueden
utilizarse en una definici�n de flex
.
El uso de `-l' produce en el comportamiendo de lex
el no
poner par�ntesis alrededor de la definici�n.
La especificaci�n de POSIX dice que la definici�n debe ser encerrada
entre par�ntesis.
lex
permiten que la acci�n de una
regla comience en una l�nea separada, si el patr�n de la regla tiene
espacios en blanco al final:
%% foo|bar<espacio aqu�> { foobar_action(); }
flex
no dispone de esta propiedad.
lex
(generar un analizador Ratfor) no se
ofrece. No es parte de la especificaci�n de POSIX.
yytext
est� indefinido hasta que se reconozca el pr�ximo token, a menos que el
analizador se haya construido usando `%array'. Este no es el caso
de lex
o la especificaci�n de POSIX. La opci�n `-l'
elimina esta incompatibilidad.
lex
interpreta "abc{1,3}" como "empareja uno, dos, o tres
apariciones de 'abc'", mientras que flex
lo interpreta como
"empareja 'ab' seguida de una, dos o tres apariciones de 'c'". Lo
�ltimo va de acuerdo con la especificaci�n de POSIX.
lex
interpreta "^foo|bar" como "empareja bien 'foo' al principio de una
l�nea, o 'bar' en cualquier lugar", mientras que flex
lo
interpreta como "empareja 'foo' o 'bar' si vienen al principio de una
l�nea". Lo �ltimo va de acuerdo con la especificaci�n de POSIX.
lex
no se requieren en los analizadores de
flex
; flex
los ignora.
flex
o con lex
.
Los analizadores tambi�n incluyen YY_FLEX_MAJOR_VERSION
y
YY_FLEX_MINOR_VERSION
indicando qu� versi�n de flex
gener�
el analizador (por ejemplo, para la versi�n 2.5, estas definiciones
ser�n 2 y 5 respectivamente).
Las siguientes propiedades de flex
no se incluyen en lex
o la especificaci�n POSIX:
analizadores en C++ %option �mbitos de condiciones de arranque pilas de condiciones de arranque analizadores interactivos/no-interactivos yy_scan_string() y sus amigas yyterminate() yy_set_interactive() yy_set_bol() YY_AT_BOL() <<EOF>> <*> YY_DECL YY_START YY_USER_ACTION YY_USER_INIT directivas #line %{}'s alrededor de acciones varias acciones en una l�nea
m�s casi todas las banderas de flex. La �ltima propiedad en la lista se
refiere al hecho de que con flex
puede poner varias acciones en
la misma l�nea, sepradas con punto y coma, mientras que con lex
,
lo siguiente
foo handle_foo(); ++num_foos_seen;
se trunca (sorprendentemente) a
foo handle_foo();
flex
no trunca la acci�n. Las acciones que no se encierran en
llaves simplemente se terminan al final de la l�nea.
[a-z]+ obtuvo_identificador(); foo obtuvo_foo();El uso de
REJECT
en un analizador suprime este aviso.
REJECT
o `yymore()' pero que flex
fall� en darse cuenta del hecho, queriendo decir que flex
analiz� las dos primeras secciones buscando apariciones de estas
acciones y fall� en encontrar alguna, pero que de alg�n modo se le han
colado (por medio de un archivo #include, por ejemplo). Use
`%option reject' � `%option yymore' para indicar a flex que
realmente usa esta funcionalidad.
YYLMAX
(8K bytes por defecto). Usted
puede incrementar el valor haciendo un #define YYLMAX
en la
secci�n de definiciones de su entrada de flex
.
yytext
. Idealmente el analizador deber�a ajustar din�micamente
el buffer en este caso, pero actualmente no lo hace.
REJECT
.
yyrestart( yyin );o, como se coment� en el section Generando esc�neres en C++, cambie y use el analizador como clase de C++.
FlexLexer
, y su clase derivada, yyFlexLexer
.
Algunos patrones de contexto posterior no pueden reconocerse correctamente y generan mensajes de aviso ("contexto posterior peligroso"). Estos son patrones donde el final de la primera parte de la regla reconoce el comienzo de la segunda parte, tal como "zx*/xy*", donde el 'x*' reconoce la `x' al comienzo del contexto posterior. (F�jese que el borrador de POSIX establece que el texto reconocido por tales patrones no est� definido.)
Para algunas reglas de contexto posterior, partes que son de hecho de longitud fija no se reconocen como tales, resultando en la p�rdida de rendimiento mencionada anteriormente. En particular, las partes que usan `|' o {n} (tales como "foo{3}") siempre se consideran de longitud variable.
La combinaci�n de contexto posterior con la acci�n especial `|' puede producir que el contexto posterior fijo se convierta en contexto posterior variable que es m�s caro. Por ejemplo, en lo que viene a continuaci�n:
%% abc | xyz/def
El uso de `unput()' invalida yytext e yyleng, a menos que se use la directiva `%array' o la opci�n `-l'.
La concordancia de patrones de NUL's es substancialmente m�s lento que el reconocimiento de otros caracteres.
El ajuste din�mico del buffer de entrada es lento, ya que conlleva el rean�lisis de todo el texto reconocido hasta entonces por el (generalmente enorme) token actual.
Debido al uso simult�neo de buffers de entrada y lecturas por
adelantado, no puede entremezclar llamadas a rutinas de <stdio.h>,
tales como, por ejemplo, `getchar()', con reglas de flex
y
esperar que funcione. Llame a `input()' en su lugar.
La totalidad de las entradas de la tabla listada por la bandera
`-v' excluye el n�mero de entradas en la tabla necesarias para
determinar qu� regla ha sido emparejada. El n�mero de entradas es
igual al n�mero de estados del DFA si el analizador no usa REJECT
,
y algo mayor que el n�mero de estados si se usa.
REJECT
no puede usarse con las opciones `-f' � `-F'.
El algoritmo interno de flex
necesita documentaci�n.
lex
(1), yacc
(1), sed
(1), awk
(1).
John Levine, Tony Mason, and Doug Brown: Lex & Yacc, O'Reilly and Associates. Est� seguro de obtener la 2� edici�n.
M. E. Lesk and E. Schmidt, LEX - Lexical Analyzer Generator
Alfred Aho, Ravi Sethi and Jeffrey Ullman: Compilers: Principles,
Techniques and Tools; Addison-Wesley (1986) --Edici�n en castellano:
Compiladores: Principios, T�cnicas y Herramientas,
Addison-Wesley Iberoamericana, S.A. (1990). Describe las t�cnicas de
concordancia de patrones usadas por flex
(aut�mata finito determinista).
Vern Paxson, con la ayuda de muchas ideas e inspiraci�n de Van Jacobson. Versi�n original por Jef Poskanzer. La representaci�n de tablas r�pidas es una implementaci�n parcial de un dise�o hecho por Van Jacobson. La implementaci�n fue hecha por Kevin Gong y Vern Paxson.
Agradecimientos a los muchos flex
beta-testers, feedbackers, y
contribuidores, especialmente a Francois Pinard, Casey Leedom, Robert
Abramovitz, Stan Adermann, Terry Allen, David Barker-Plummer, John
Basrai, Neal Becker, Nelson H.F. Beebe, `benson@odi.com', Karl
Berry, Peter A. Bigot, Simon Blanchard, Keith Bostic, Frederic Brehm,
Ian Brockbank, Kin Cho, Nick Christopher, Brian Clapper, J.T. Conklin,
Jason Coughlin, Bill Cox, Nick Cropper, Dave Curtis, Scott David
Daniels, Chris G. Demetriou, Theo Deraadt,
Mike Donahue, Chuck Doucette, Tom Epperly, Leo Eskin,
Chris Faylor, Chris Flatters, Jon Forrest, Jeffrey Friedl,
Joe Gayda, Kaveh R. Ghazi, Wolfgang Glunz,
Eric Goldman, Christopher M. Gould, Ulrich Grepel, Peer Griebel,
Jan Hajic, Charles Hemphill, NORO Hideo,
Jarkko Hietaniemi, Scott Hofmann,
Jeff Honig, Dana Hudes, Eric Hughes, John Interrante,
Ceriel Jacobs, Michal Jaegermann, Sakari Jalovaara, Jeffrey R. Jones,
Henry Juengst, Klaus Kaempf, Jonathan I. Kamens, Terrence O Kane,
Amir Katz, `ken@ken.hilco.com', Kevin B. Kenny,
Steve Kirsch, Winfried Koenig, Marq Kole, Ronald Lamprecht,
Greg Lee, Rohan Lenard, Craig Leres, John Levine, Steve Liddle,
David Loffredo, Mike Long,
Mohamed el Lozy, Brian Madsen, Malte, Joe Marshall,
Bengt Martensson, Chris Metcalf,
Luke Mewburn, Jim Meyering, R. Alexander Milowski, Erik Naggum,
G.T. Nicol, Landon Noll, James Nordby, Marc Nozell,
Richard Ohnemus, Karsten Pahnke,
Sven Panne, Roland Pesch, Walter Pelissero, Gaumond
Pierre, Esmond Pitt, Jef Poskanzer, Joe Rahmeh, Jarmo Raiha,
Frederic Raimbault, Pat Rankin, Rick Richardson,
Kevin Rodgers, Kai Uwe Rommel, Jim Roskind, Alberto Santini,
Andreas Scherer, Darrell Schiebel, Raf Schietekat,
Doug Schmidt, Philippe Schnoebelen, Andreas Schwab,
Larry Schwimmer, Alex Siegel, Eckehard Stolz, Jan-Erik Strvmquist,
Mike Stump, Paul Stuart, Dave Tallman, Ian Lance Taylor,
Chris Thewalt, Richard M. Timoney, Jodi Tsai,
Paul Tuinenga, Gary Weik, Frank Whaley, Gerhard Wilhelms, Kent Williams, Ken
Yap, Ron Zellar, Nathan Zelle, David Zuhn,
y aquellos cuyos nombres han ca�do bajo mis escasas dotes de
archivador de correo pero cuyas contribuciones son apreciadas todas
por igual.
Agradecimientos a Keith Bostic, Jon Forrest, Noah Friedman, John Gilmore, Craig Leres, John Levine, Bob Mulcahy, G.T. Nicol, Francois Pinard, Rich Salz, y a Richard Stallman por la ayuda con diversos quebraderos de cabeza con la distribuci�n.
Agradecimientos a Esmond Pitt y Earle Horton por el soporte de caracteres de 8-bits; a Benson Margulies y a Fred Burke por el soporte de C++; a Kent Williams y a Tom Epperly por el soporte de la clase de C++; a Ove Ewerlid por el soporte de NUL's; y a Eric Hughes por el soporte de m�ltiples buffers.
Este trabajo fue hecho principalmente cuando yo estaba con el Grupo de Sistemas de Tiempo Real en el Lawrence Berkeley Laboratory en Berkeley, CA. Muchas gracias a todos all� por el apoyo que recib�.
Enviar comentarios a @email{vern@ee.lbl.gov, Vern Paxson}.
Sobre esta traducci�n enviar comentarios a @email{alu1415@csi.ull.es, Adri�n P�rez Jorge}.
This document was generated on 25 May 1999 using the texi2html translator version 1.51.