Provided by:
manpages-es-extra_0.8a-17_all 
NOMBRE
flex - generador de analizadores lexicos rapidos
SINOPSIS
flex [-bcdfhilnpstvwBFILTV78+? -C[aefFmr] -osalida -Pprefijo
-Sesqueleto] [--help --version] [nombrefichero ...]
INTRODUCCI'ON
Este manual describe flex, una herramienta para la generacion de
programas que realizan concordancia de patrones en texto. El manual
incluye a la vez secciones de tutorial y de referencia:
Descripcion
una breve introduccion a la herramienta
Algunos Ejemplos Simples
Formato del Fichero de Entrada
Patrones
las expresiones regulares extendidas que utiliza flex
Como se Empareja la Entrada
las reglas para determinar lo que ha concordado
Acciones
como especificar que hacer cuando concuerde un patron
El Escaner Generado
detalles respecto al escaner que produce flex;
como controlar la fuente de entrada
Condiciones de Arranque
la introducion de contexto en sus escaneres, y
conseguir "mini-escaneres"
Multiples Buffers de Entrada
como manipular varias fuentes de entrada; como
analizar cadenas en lugar de ficheros.
Reglas de Fin-de-Fichero
reglas especiales para reconocer el final de la entrada
Macros Miscelaneas
un sumario de macros disponibles para las acciones
Valores Disponibles para el Usuario
un sumario de valores disponibles para las acciones
Interfaz con Yacc
conectando escaneres de flex junto con analizadores de yacc
Opciones
opciones de linea de comando de flex, y la directiva
"%option"
Consideraciones de Rendimiento
como hacer que sus analizadores vayan tan rapido
como sea posible
Generando Escaneres en C++
la facilidad (experimental) para generar analizadores
lexicos como clases de C++
Incompatibilidades con Lex y POSIX
como flex difiere del lex de AT&T y del lex estandar
de POSIX
Diagnosticos
esos mensajes de error producidos por flex (o por
los escaneres que este genera) cuyo significado podria
no ser evidente
Ficheros
los ficheros usados por flex
Deficiencias / Errores
problemas de flex conocidos
Ver Tambien
otra documentacion, herramientas relacionadas
Autor
incluye informacion de contacto
DESCRIPCI'ON
flex es una herramienta para generar esc'aneres: programas que reconocen
patrones lexicos en un texto. flex lee los ficheros de entrada dados,
o la entrada estandar si no se le ha indicado ningun nombre de fichero,
con la descripcion de un escaner a generar. La descripcion se
encuentra en forma de parejas de expresiones regulares y codigo 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 libreria -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 codigo C correspondiente.
ALGUNOS EJEMPLOS SIMPLES
En primer lugar veremos algunos ejemplos simples para una toma de
contacto con el uso de flex. La siguiente entrada de flex especifica
un escaner que siempre que encuentre la cadena "username" la
reemplazara por el nombre de entrada al sistema del usuario:
%%
username printf( "%s", getlogin() );
Por defecto, cualquier texto que no reconozca el analizador lexico de
flex se copia a la salida, asi que el efecto neto de este escaner es
copiar su fichero de entrada a la salida con cada aparicion de
"username" expandida. En esta entrada, hay solamente una regla.
"username" es el patr'on y el "printf" es la acci'on. El "%%" marca el
comienzo de las reglas.
Aqui hay otro ejemplo simple:
int num_lineas = 0, num_caracteres = 0;
%%
\n ++num_lineas; ++num_caracteres;
. ++num_caracteres;
%%
main()
{
yylex();
printf( "# de lineas = %d, # de caracteres. = %d\n",
num_lineas, num_caracteres );
}
Este analizador cuenta el numero de caracteres y el numero de lineas en
su entrada (no produce otra salida que el informe final de la cuenta).
La primera linea declara dos variables globales, "num_lineas" y
"num_caracteres", que son visibles al mismo tiempo dentro de yylex() y
en la rutina main() declarada despues del segundo "%%". Hay dos
reglas, una que empareja una linea nueva ("\n") e incrementa la cuenta
de lineas y la cuenta de caracteres, y la que empareja cualquier
caracter que no sea una linea nueva (indicado por la expresion regular
".").
Un ejemplo algo mas complicado:
/* escaner para un lenguaje de juguete al estilo de Pascal */
%{
/* se necesita esto para la llamada a atof() mas 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 podria ser los comienzos de un escaner 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 explicaran en las secciones siguientes.
FORMATO DEL FICHERO DE ENTRADA
El fichero de entrada de flex esta compuesto de tres secciones,
separadas por una linea donde aparece unicamente un %% en esta:
definiciones
%%
reglas
%%
codigo de usuario
La seccion de definiciones contiene declaraciones de definiciones de
nombres sencillas para simplificar la especificacion del escaner, y
declaraciones de condiciones de arranque, que se explicaran en una
seccion posterior.
Las definiciones de nombre tienen la forma:
nombre definicion
El "nombre" es una palabra que comienza con una letra o un subrayado
('_') seguido por cero o mas letras, digitos, '_', o '-' (guion). La
definicion 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 linea. Posteriormente se puede hacer referencia a la definicion
utilizando "{nombre}", que se expandira a "(definicion)". Por ejemplo,
DIGITO [0-9]
ID [a-z][a-z0-9]*
define "DIGITO" como una expresion regular que empareja un digito
sencillo, e "ID" como una expresion regular que empareja una letra
seguida por cero o mas letras o digitos. Una referencia posterior a
{DIGITO}+"."{DIGITO}*
es identica a
([0-9])+"."([0-9])*
y empareja uno o mas digitos seguido por un '.' seguido por cero o mas
digitos.
La seccion de reglas en la entrada de flex contiene una serie de reglas
de la forma:
patron accion
donde el patron debe estar sin sangrar y la accion debe comenzar en la
misma linea.
Ver mas abajo para una descripcion mas amplia sobre patrones y
acciones.
Finalmente, la seccion de codigo de usuario simplemente se copia a
lex.yy.c literalmente. Esta seccion se utiliza para rutinas de
complemento que llaman al escaner o son llamadas por este. La
presencia de esta seccion es opcional; Si se omite, el segundo %% en el
fichero de entrada se podria omitir tambien.
En las secciones de definiciones y reglas, cualquier texto sangrado o
encerrado entre %{ y %} se copia integramente a la salida (sin los
%{}'s). Los %{}'s deben aparecer sin sangrar en lineas ocupadas
unicamente por estos.
En la seccion de reglas, cualquier texto o %{} sangrado que aparezca
antes de la primera regla podria utilizarse para declarar variables que
son locales a la rutina de analisis y (despues de las declaraciones) al
codigo que debe ejecutarse siempre que se entra a la rutina de
analisis. Cualquier otro texto sangrado o %{} en la seccion de reglas
sigue copiandose a la salida, pero su significado no esta bien definido
y bien podria causar errores en tiempo de compilacion (esta propiedad
se presenta para conformidad con POSIX ; ver mas abajo para otras
caracteristicas similares)
En la seccion de definiciones (pero no en la seccion de reglas), un
comentario sin sangria (es decir, una linea comenzando con "/*")
tambien se copia literalmente a la salida hasta el proximo "*/".
PATRONES
Los patrones en la entrada se escriben utilizando un conjunto extendido
de expresiones regulares. Estas son:
x empareja el caracter 'x'
. cualquier caracter (byte) excepto una linea nueva
[xyz] una "clase de caracteres"; en este caso, el patron
empareja una 'x', una 'y', o una 'z'
[abj-oZ] una "clase de caracteres" con un rango; empareja
una 'a', una 'b', cualquier letra desde la 'j'
hasta la 'o', o una 'Z'
[^A-Z] una "clase de caracteres negada", es decir, cualquier
caracter menos los que aparecen en la clase. En
este caso, cualquier caracter EXCEPTO una letra
mayuscula.
[^A-Z\n] cualquier caracter EXCEPTO una letra mayuscula o
una linea nueva
r* cero o mas r's, donde r es cualquier expresion regular
r+ una o mas r's
r? cero o una r (es decir, "una r opcional")
r{2,5} donde sea de dos a cinco r's
r{2,} dos o mas r's
r{4} exactamente 4 r's
{nombre} la expansion de la definicion de "nombre"
(ver mas abajo)
"[xyz]\"foo"
la cadena literal: [xyz]"foo
\X si X es una 'a', 'b', 'f', 'n', 'r', 't', o 'v',
entonces la interpretacion ANSI-C de \x.
En otro caso, un literal 'X' (usado para
indicar operadores tales como '*')
\0 un caracter NUL (codigo ASCII 0)
\123 el caracter con valor octal 123
\x2a el caracter con valor hexadecimal 2a
(r) empareja una r; los parentesis se utilizan para
anular la precedencia (ver mas abajo)
rs la expresion regular r seguida por la expresion
regular s; se denomina "concatenacion"
r|s bien una r o una s
r/s una r pero solo si va seguida por una s. El
texto emparejado por s se incluye cuando se
determina si esta regla es el "emparejamiento
mas largo", pero se devuelve entonces a la
entrada antes que se ejecute la accion. Asi
que la accion solo ve el texto emparejado
por r. Este tipo de patrones se llama
"de contexto posterior".
(Hay algunas combinaciones de r/s que flex
no puede emparejar correctamente; vea las notas
en la seccion Deficiencias / Errores mas abajo
respecto al "contexto posterior peligroso".)
^r una r, pero solo al comienzo de una linea (es
decir, justo al comienzo del analisis, o a la
derecha despues de que se haya analizado una
linea nueva).
r$ una r, pero solo al final de una linea (es decir,
justo antes de una linea nueva). Equivalente
a "r/\n".
Fijese que la nocion de flex de una "linea nueva"
es exactamente lo que el compilador de C utilizado
para compilar flex interprete como '\n'; en
particular, en algunos sistemas DOS debe filtrar
los \r's de la entrada used mismo, o explicitamente
usar r/\r\n para "r$".
<s>r una r, pero solo en la condicion de arranque s
(ver mas abajo para una discusion sobre las
condiciones de arranque)
<s1,s2,s3>r
lo mismo, pero en cualquiera de las condiciones
de arranque s1, s2, o s3
<*>r una r en cualquier condicion de arranque, incluso
una exclusiva.
<<EOF>> un fin-de-fichero
<s1,s2><<EOF>>
un fin-de-fichero en una condicion de arranque s1 o s2
Fijese 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, '-',
Las expresiones regulares en el listado anterior estan agrupadas de
acuerdo a la precedencia, desde la precedencia mas alta en la cabeza a
la mas 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 concatenacion, y
la concatenacion mas alta que el operador '|'. Este patron por lo
tanto empareja bien la cadena "foo" o la cadena "ba" seguida de cero o
mas r's. Para emparejar "foo" o, cero o mas "bar"'s, use:
foo|(bar)*
y para emparejar cero o mas "foo"'s o "bar"'s:
(foo|bar)*
Ademas de caracteres y rangos de caracteres, las clases de caracteres
pueden tambien contener expresiones de clases de caracteres. Son
expresiones encerradas entre los delimitadores [: y :] (que tambien
deben aparecer entre el '[' y el ']' de la clase de caracteres; ademas
pueden darse otros elementos dentro de la clase de caracteres). Las
expresiones validas 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 funcion estandar isXXX de C. Por ejemplo,
[:alnum:] designa aquellos caracteres para los cuales isalnum()
devuelve verdadero - esto es, cualquier caracter alfabetico o numerico.
Algunos sistemas no ofrecen isblank(), asi 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 escaner ignora la distincion entre mayusculas y minusculas (la
bandera -i ), entonces [:upper:] y [:lower:] son equivalentes a
[:alpha:].
Algunas notas sobre los patrones:
- Una clase de caracteres negada tal como el ejemplo "[^A-Z]"
anterior emparejar'a una l'inea nueva a menos que "\n" (o una
secuencia de escape equivalente) sea uno de los caracteres
presentes explicitamente en la clase de caracteres negada
(p.ej., "[^A-Z\n]"). Esto es diferente a como muchas de las
otras herramientas de expresiones regulares tratan las clases de
caracteres negadas, pero desafortunadamente la inconsistencia
esta fervientemente enrraizada historicamente. Emparejar lineas
nuevas significa que un patron como [^"]* puede emparejar la
entrada completa a menos que haya otra comilla en la entrada.
- Una regla puede tener lo mas una instancia del contexto
posterior (el operador '/' o el operador '$'). La condicion de
arranque, los patrones '^', y "<<EOF>>" pueden aparecer
solamente al principio de un patron, y, al igual que con '/' y
'$', no pueden agruparse dentro de parentesis. Un '^' que no
aparezca al principio de una regla o un '$' que no aparezca al
final de una regla pierde sus propiedades especiales y es
tratado como un caracter normal.
Lo siguiente no esta permitido:
foo/bar$
<sc1>foo<sc2>bar
Fijese 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|^bar
Si lo que se desea es un "foo" o un "bar" seguido de una linea
nueva, puede usarse lo siguiente (la accion especial '|' se
explica mas abajo):
foo |
bar$ /* la accion va aqui */
Un truco parecido funcionara para emparejar un "foo" o, un "bar"
al principio de una linea.
C'OMO SE EMPAREJA LA ENTRADA
Cuando el escaner generado esta funcionando, este analiza su entrada
buscando cadenas que concuerden con cualquiera de sus patrones. Si
encuentra mas de un emparejamiento, toma el que empareje mas 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
mas 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) esta disponible en el puntero a
caracter global yytext, y su longitud en la variable global entera
yyleng. Entonces la acci'on correspondiente al patron emparejado se
ejecuta (una descripcion mas detallada de las acciones viene a
continuacion), 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 estandar. Asi, la entrada valida mas simple de
flex es:
%%
que genera un escaner que simplemente copia su entrada (un caracter a
la vez) a la salida.
Fijese 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 definicion que usa flex incluyendo una de las directivas
especiales %pointer o %array en la primera seccion (definiciones) de su
entrada de flex. Por defecto es %pointer, a menos que use la opcion de
compatibilidad -l, en cuyo caso yytext sera un array. La ventaja de
usar %pointer es un analisis substancialmente mas rapido y la ausencia
de desbordamiento del buffer cuando se emparejen tokens muy grandes (a
menos que se agote la memoria dinamica). La desventaja es que se
encuentra restringido en como sus acciones pueden modificar yytext (vea
la siguiente seccion), y las llamadas a la funcion 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 mas
abajo). Ademas, los programas de lex existentes a veces acceden a
yytext externamente utilizando declaraciones de la forma:
extern char yytext[];
Esta definicion es erronea 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 tamano
simplemente definiendo con #define a YYLMAX con un valor diferente en
la primera seccion de su entrada de flex. Como se menciono antes, con
%pointer yytext crece dinamicamente para acomodar tokens grandes.
Aunque esto signifique que con %pointer su escaner puede acomodar
tokens muy grandes (tales como emparejar bloques enteros de
comentarios), tenga presente que cada vez que el escaner deba cambiar
el tamano de yytext tambien debe reiniciar el analisis del token entero
desde el principio, asi que emparejar tales tokens puede resultar
lento. Ahora yytext no crece dinamicamente si una llamada a unput()
hace que se deba devolver demasiado texto; en su lugar, se produce un
error en tiempo de ejecucion.
Tambien tenga en cuenta que no puede usar %array en los analizadores
generados como clases de C++ (la opcion c++; vea mas abajo).
ACCIONES
Cada patron en una regla tiene una accion asociada, que puede ser
cualquier sentencia en C. El patron finaliza en el primer caracter de
espacio en blanco que no sea una secuencia de escape; lo que queda de
la linea es su accion. Si la accion esta vacia, entonces cuando el
patron se empareje el token de entrada simplemente se descarta. Por
ejemplo, aqui esta la especificacion de un programa que borra todas las
apariciones de "zap me" en su entrada:
%%
"zap me"
(Este copiara el resto de caracteres de la entrada a la salida ya que
seran emparejados por la regla por defecto.)
Aqui 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 linea:
%%
[ \t]+ putchar( ' ' );
[ \t]+$ /* ignora este token */
Si la accion contiene un '{', entonces la accion abarca hasta que se
encuentre el correspondiente '}', y la accion podria entonces cruzar
varias lineas. flex es capaz de reconocer las cadenas y comentarios de
C y no se dejara enganar por las llaves que encuentre dentro de estos,
pero aun asi tambien permite que las acciones comiencen con %{ y
considerara que la accion es todo el texto hasta el siguiente %} (sin
tener en cuenta las llaves ordinarias dentro de la accion).
Una accion que consista solamente de una barra vertical ('|') significa
"lo mismo que la accion para la siguiente regla." Vea mas abajo para
una ilustracion.
Las acciones pueden incluir codigo C arbitrario, incuyendo sentencias
return para devolver un valor desde cualquier rutina llamada yylex().
Cada vez que se llama a yylex() esta continua procesando tokens desde
donde lo dejo la ultima vez hasta que o bien llegue al final del
fichero o ejecute un return.
Las acciones tienen libertad para modificar yytext excepto para
alargarla (anadiendo caracteres al final--esto sobreescribira mas tarde
caracteres en el flujo de entrada). Sin embargo esto no se aplica
cuando se utiliza %array (ver arriba); en ese caso, yytext podria
modificarse libremente de cualquier manera.
Las acciones tienen libertad para modificar yyleng excepto que estas no
deberian hacerlo si la accion tambien incluye el uso de yymore() (ver
mas abajo).
Hay un numero de directivas especiales que pueden incluirse dentro de
una accion:
- ECHO copia yytext a la salida del escaner.
- BEGIN seguido del nombre de la condicion de arranque pone al
escaner en la condicion de arranque correspondiente (ver mas
abajo).
- REJECT ordena al escaner 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 describio anteriormente en "Como se
Empareja la Entrada", y yytext e yyleng se ajustan de forma
apropiada. Podria ser una que empareje tanto texto como la
regla escogida originalmente pero que viene mas tarde en el
fichero de entrada de flex, o una que empareje menos texto. Por
ejemplo, lo que viene a continuacion contara las palabras en la
entrada y llamara a la rutina especial() siempre que vea "frob":
int contador_palabras = 0;
%%
frob especial(); REJECT;
[^ \t\n]+ ++contador_palabras;
Sin el REJECT, cualquier numero de "frob"'s en la entrada no
serian contados como palabras, ya que el escaner normalmente
ejecuta solo una accion por token. Se permite el uso de
multiples REJECT's, cada uno buscando la siguiente mejor
eleccion a la regla que actualmente este activa. Por ejemplo,
cuando el siguiente escaner analice el token "abcd", este
escribira "abcdabcaba" a la salida:
%%
a |
ab |
abc |
abcd ECHO; REJECT;
.|\n /* se come caracteres sin emparejar */
(Las primeras tres reglas comparten la accion de la cuarta ya
que estas usan la accion especial '|'.) REJECT es una propiedad
particularmente cara en terminos de rendimiento del escaner; si
se usa en cualquiera de las acciones del escaner esta
ralentizara todo el proceso de emparejamiento del escaner.
Ademas, REJECT no puede usarse con las opciones -Cf o -CF (ver
mas abajo).
Fijese tambien que a diferencia de las otras acciones
especiales, REJECT es una bifurcaci'on; el codigo que la siga
inmediatamente en la accion no sera ejecutado.
- yymore() dice al escaner que la proxima vez que empareje una
regla, el token correspondiente debe ser a~nadido tras el valor
actual de yytext en lugar de reemplazarlo. Por ejemplo, dada la
entrada "mega-klugde" lo que viene a continuacion escribira
"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 aun esta esperando
al inicio de yytext asi que el ECHO para la regla del "kludge"
realmente escribira "mega-kludge".
Dos notas respecto al uso de yymore(). Primero, yymore() depende de
que el valor de yyleng refleje correctamente el tamano del token
actual, asi que no debe modificar yyleng si esta utilizando yymore().
Segundo, la presencia de yymore() en la accion del escaner implica una
pequena penalizacion de rendimiento en la velocidad de emparejamiento
del escaner.
- yyless(n) devuelve todos excepto los primeros n caracteres del
token actual de nuevo al flujo de entrada, donde seran
reanalizados cuando el escaner busque el siguiente
emparejamiento. yytext e yyleng se ajustan de forma adecuada
(p.ej., yyleng no sera igual a n ). Por ejemplo, con la entrada
"foobar" lo que viene a continuacion escribira "foobarbar":
%%
foobar ECHO; yyless(3);
[a-z]+ ECHO;
Un argumento de 0 para yyless hara que la cadena de entrada
actual sea analizada por completo de nuevo. A menos que haya
cambiado la manera en la que el escaner procese de ahora en
adelante su entrada (utilizando BEGIN, por ejemplo), esto
producira un bucle sin fin.
Fijese que yyless es una macro y puede ser utilizada solamente en el
fichero de entrada de flex, no desde otros ficheros fuente.
- unput(c) pone el caracter c de nuevo en el flujo de entrada.
Este sera el proximo caracter analizado. La siguiente accion
tomara el token actual y hara que se vuelva a analizar pero
encerrado entre parentesis.
{
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 );
}
Fijese 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 atras hacia delante.
Un problema potencial importante cuando se utiliza unput() es que si
esta usando %pointer (por defecto), una llamada a unput() destruye el
contenido de yytext, comenzando con su caracter mas a la derecha y
devorando un caracter a la izquierda con cada llamada. Si necesita que
se preserve el valor de yytext despues de una llamada a unput() (como
en el ejemplo anterior), usted debe o bien copiarlo primero en
cualquier lugar, o construir su escaner usando %array
(ver Como se Empareja la Entrada).
Finalmente, note que no puede devolver EOF para intentar marcar el
flujo de entrada con un fin-de-fichero.
- input() lee el proximo caracter del flujo de entrada. Por
ejemplo, lo que viene a continuacion es una manera de comerse
los comentarios en C:
%%
"/*" {
register int c;
for ( ; ; )
{
while ( (c = input()) != '*' &&
c != EOF )
; /* se come el texto del comentario */
if ( c == '*' )
{
while ( (c = input()) == '*' )
;
if ( c == '/' )
break; /* encontro el final */
}
if ( c == EOF )
{
error( "EOF en comentario" );
break;
}
}
}
(Fijese que si el escaner se compila usando C++, entonces a
input() se le hace referencia con yyinput(), para evitar una
colision de nombre con el flujo de C++ por el nombre input.)
- YY_FLUSH_BUFFER vacia el buffer interno del escaner de manera
que la proxima vez que el escaner intente emparejar un token,
este primero rellenara el buffer usando YY_INPUT (ver El Escaner
Generado, mas abajo). Esta accion es un caso especial de la
funcion mas general yy_flush_buffer(), descrita mas abajo en la
seccion Multiples Buffers de Entrada.
- yyterminate() se puede utilizar en lugar de una sentencia de
retorno en una accion. Esta hace que finalice el escaner y
retorne un 0 a quien haya llamado al escaner, indicando que
"todo esta hecho". Por defecto, tambien se llama a
yyterminate() cuando se encuentra un fin-de-fichero. Esta es
una macro y podria ser redefinida.
El Esc'aner Generado
La salida de flex es el fichero lex.yy.c, que contiene la rutina de
analisis yylex(), un numero de tablas usadas por esta para emparejar
tokens, y un numero de rutinas auxiliares y macros. Por defecto,
yylex() se declara asi
int yylex()
{
... aqui van varias definiciones y las acciones ...
}
(Si su entorno acepta prototipos de funciones, entonces este sera "int
yylex( void )"). Esta definicion podria modificarse definiendo la
macro "YY_DECL". Por ejemplo, podria utilizar:
#define YY_DECL float lexscan( a, b ) float a, b;
para darle a la rutina de analisis el nombre lexscan, que devuelve un
real, y toma dos reales como argumentos. Fijese que si pone argumentos
a la rutina de analisis usando una declaracion de funcion no-
prototipada/tipo-K&R, debe hacer terminar la definicion 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 funcion
continua 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 escaner alcanza un fin-de-fichero, entonces el comportamiento en
las llamadas posteriores esta indefinido a menos que o bien yyin apunte
a un nuevo fichero de entrada (en cuyo caso el analisis continua 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 asignacion a yyin de un nuevo fichero de entrada o el uso de
yyrestart() para hacerlo; esto ultimo esta disponible por
compatibilidad con versiones anteriores de flex, y porque puede
utilizarse para conmutar ficheros de entrada en medio del analisis.
Tambien se puede utilizar para desechar el buffer de entrada actual,
invocandola con un argumento igual a yyin; pero mejor es usar
YY_FLUSH_BUFFER (ver mas arriba). Fijese que yyrestart() no
reinicializa la condicion de arranque a INITIAL (ver Condiciones de
Arranque, mas abajo).
Si yylex() para el analisis debido a la ejecucion de una sentencia
return en una de las acciones, el analizador podria ser llamado de
nuevo y este reanudaria el analisis donde lo dejo.
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 accion es poner hasta max_size
caracteres en el array de caracteres buf y devolver en la variable
entera result bien o el numero de caracteres leidos 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 definicion de ejemplo para YY_INPUT (en la seccion 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 definicion cambiara el procesamiento de la entrada para que suceda
un caracter a la vez.
Cuando el analizador reciba una indicacion de fin-de-fichero desde
YY_INPUT, entonces esta comprueba la funcion yywrap(). Si yywrap()
devuelve falso (cero), entonces se asume que la funcion ha ido mas alla
y ha preparado yyin para que apunte a otro fichero de entrada, y el
analisis continua. Si este retorna verdadero (no-cero), entonces el
analizador termina, devolviendo un 0 a su invocador. Fijese que en
cualquier caso, la condicion de arranque permanece sin cambios; esta no
vuelve a ser INITIAL.
Si no proporciona su propia version 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
version 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 seccion Multiples Buffers de
Entrada.
El analizador escribe su salida con ECHO a la variable global yyout
(por defecto, stdout), que el usuario podria redefinir asignandole
cualquier otro puntero a FILE.
CONDICIONES DE ARRANQUE
flex dispone de un mecanismo para activar reglas condicionalmente.
Cualquier regla cuyo patron se prefije con "<sc>" unicamente estara
activa cuando el analizador se encuentre en la condicion de arranque
llamada "sc". Por ejemplo,
<STRING>[^"]* { /* se come el cuerpo de la cadena ... */
...
}
estara activa solamente cuando el analizador este en la condicion de
arranque "STRING", y
<INITIAL,STRING,QUOTE>\. { /* trata una secuencia de escape ... */
...
}
estara activa solamente cuando la condicion de arranque actual sea o
bien "INITIAL", "STRING", o "QUOTE".
Las condiciones de arranque se declaran en la (primera) seccion de
definiciones de la entrada usando lineas sin sangrar comenzando con %s
o %x seguida por una lista de nombres. Lo primero declara condiciones
de arranque inclusivas, lo ultimo condiciones de arranque exclusivas.
Una condicion de arranque se activa utilizando la accion BEGIN. Hasta
que se ejecute la proxima accion BEGIN, las reglas con la condicion de
arranque dada estaran activas y las reglas con otras condiciones de
arranque estaran inactivas. Si la condicion de arranque es inclusiva,
entonces las reglas sin condiciones de arranque tambien estaran
activas. Si es exclusiva, entonces s'olamente las reglas calificadas con
la condicion de arranque estaran activas. Un conjunto de reglas
dependientes de la misma condicion 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 facil la especificacion de "mini-escaneres" que analizan
porciones de la entrada que son sintacticamente diferentes al resto
(p.ej., comentarios).
Si la distincion entre condiciones de arranque inclusivas o exclusivas
es aun un poco vaga, aqui hay un ejemplo simple que ilustra la conexion
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 patron bar en el segundo
ejemplo no estara activo (es decir, no puede emparejarse) cuando se
encuentre en la condicion de arranque example. Si hemos usado
<example> para calificar bar, aunque, entonces este unicamente estara
activo en example y no en INITIAL, mientras que en el primer ejemplo
esta activo en ambas, porque en el primer ejemplo la condicion de
arranque example es una condicion de arranque inclusiva (%s).
Fijese tambien que el especificador especial de la condicion de
arranque <*> empareja todas las condiciones de arranque. Asi, el
ejemplo anterior tambien 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 estan activas. Este estado tambien puede
referirse a la condicion de arranque "INITIAL", asi que BEGIN(INITIAL)
es equivalente a BEGIN(0). (No se requieren los parentesis alrededor
del nombre de la condicion de arranque pero se considera de buen
estilo.)
Las acciones BEGIN pueden darse tambien como codigo sangrado al
comienzo de la seccion de reglas. Por ejemplo, lo que viene a
continuacion hara que el analizador entre en la condicion 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
...mas reglas a continuacion...
Para ilustrar los usos de las condiciones de arranque, aqui hay un
analizador que ofrece dos interpretaciones diferentes para una cadena
como "123.456". Por defecto este la tratara como tres tokens, el
entero "123", un punto ('.'), y el entero "456". Pero si la cadena
viene precedida en la linea por la cadena "espera-reales" este la
tratara como un unico token, el numero en coma flotante 123.456:
%{
#include <math.h>
%}
%s espera
%%
espera-reales BEGIN(espera);
<espera>[0-9]+"."[0-9]+ {
printf( "encontro un real, = %f\n",
atof( yytext ) );
}
<espera>\n {
/* este es el final de la linea,
* asi que necesitamos otro
* "espera-numero" antes de
* que volvamos a reconocer mas
* numeros
*/
BEGIN(INITIAL);
}
[0-9]+ {
printf( "encontro un entero, = %d\n",
atoi( yytext ) );
}
"." printf( "encontro un punto\n" );
Aqui esta un analizador que reconoce (y descarta) comentarios de C
mientras mantiene una cuenta de la linea 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 mas que
pueda, ya que esto es un buen logro.
Fijese que los nombres de las condiciones de arranque son realmente
valores enteros y pueden ser almacenados como tales. Asi, lo anterior
podria 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 '/'s */
<comentario>\n ++num_linea;
<comentario>"*"+"/" BEGIN(invocador_comentario);
Ademas, puede acceder a la condicion de arranque actual usando la macro
de valor entero YY_START. Por ejemplo, las asignaciones anteriores a
invocador_comentario podrian 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).
Fijese 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, aqui hay un ejemplo de como emparejar cadenas entre
comillas al estilo de C usando condiciones de arranque exclusivas,
incluyendo secuencias de escape expandidas (pero sin incluir la
comprobacion 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 esta hecho */
BEGIN(INITIAL);
*string_buf_ptr = '\0';
/* devuelve un tipo de token de cadena constante y
* el valor para el analizador sintactico
*/
}
<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 erronea;
* 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 numero de reglas todas precedidas por la(s)
misma(s) condicion(es) de arranque. Flex hace esto un poco mas facil y
claro introduciendo la nocion de 'ambito de la condicion de arranque.
Un ambito de condicion de arranque comienza con:
<SCs>{
Donde SCs es una lista de una o mas condiciones de arranque. Dentro
del ambito de la condicion de arranque, cada regla automaticamente
tiene el prefijo <SCs> aplicado a esta, hasta un '}' que corresponda
con el '{' inicial. Asi, 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 ambitos de las condiciones de arranque pueden anidarse.
Estan disponibles tres rutinas para manipular pilas de condiciones de
arranque:
void yy_push_state(int new_state)
empuja la condicion de arranque actual al tope de la pila de las
condiciones de arranque y cambia a new_state como si hubiera
utilizado BEGIN new_state (recuerde que los nombres de las
condiciones de arranque tambien son enteros).
void yy_pop_state()
extrae el tope de la pila y cambia a este mediante un BEGIN.
int yy_top_state()
devuelve el tope de la pila sin alterar el contenido de la pila.
La pila de las condiciones de arranque crece dinamicamente y por ello
no tiene asociada ninguna limitacion de tamano. Si la memoria se
agota, se aborta la ejecucion del programa.
Para usar pilas de condiciones de arranque, su analizador debe incluir
una directiva %option stack (ver Opciones mas abajo).
M'ULTIPLES BUFFERS DE ENTRADA
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 donde sera leida la siguiente entrada escribiendo
simplemente un YY_INPUT que sea sensible al contexto del analisis. A
YY_INPUT solo se le llama cuando el analizador alcanza el final de su
buffer, que podria ser bastante tiempo despues 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 tamano "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 tamano). Este
devuelve un handle YY_BUFFER_STATE, que podria pasarse a otras rutinas
(ver mas abajo). El tipo de YY_BUFFER_STATE es un puntero a una
estructura opaca struct yy_buffer_state, de manera que podria
inicializar de forma segura variables YY_BUFFER_STATE a
((YY_BUFFER_STATE) 0) si lo desea, y tambien hacer referencia a la
estructura opaca para declarar correctamente buffers de entrada en
otros ficheros fuente ademas de los de su analizador. Fijese 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 mas 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. Fijese que
yy_switch_to_buffer() podria usarlo yywrap() para arreglar las cosas
para un analisis continuo, en lugar de abrir un nuevo fichero y que
yyin apunte a este. Fijese tambien que cambiar las fuentes de entrada
ya sea por medio de yy_switch_to_buffer() o de yywrap() no cambia la
condicion 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
tambien limpiar el contenido actual de un buffer usando:
void yy_flush_buffer( YY_BUFFER_STATE buffer )
Esta funcion descarta el contenido del buffer, de manera que la proxima
vez que el analizador intente emparejar un token desde el buffer, este
primero rellenara 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 dinamicos.
Finalmente, la macro YY_CURRENT_BUFFER retorna un handle
YY_BUFFER_STATE al buffer actual.
Aqui hay un ejemplo del uso de estas propiedades para escribir un
analizador que expande ficheros incluidos (la propiedad <<EOF>> se
comenta mas abajo):
/* 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
analisis 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 deberia borrar con
yy_delete_buffer() cuando termine con el). Estas tambien conmutan el
nuevo buffer usando yy_switch_to_buffer(), de manera que la proxima
llamada a yylex() comenzara analizando la cadena.
yy_scan_string(const char *str)
analiza una cadena terminada en nulo.
yy_scan_bytes(const char *bytes, int len)
analiza len bytes (incluyendo posibles NUL's) comenzando desde
el punto bytes.
Fijese que ambas de estas funciones crean y analizan una copia de la
cadena o bytes. (Esto podria ser deseable, ya que yylex() modifica el
contenido del buffer que esta analizado.) Usted puede evitar la copia
utilizando:
yy_scan_buffer(char *base, yy_size_t size)
que analiza in situ el buffer comenzando en base, que consiste
de size bytes, donde los dos ultimos bytes deben ser
YY_END_OF_BUFFER_CHAR (ASCII NUL). Estos dos ultimos bytes no
se analizan; asi, el analisis 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
conversion a una expresion entera para reflejar el tamano del
buffer.
REGLAS DE FIN-DE-FICHERO
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 accion debe finalizar haciendo una de estas cuatro
cosas:
- asignando a yyin un nuevo fichero de entrada (en versiones
anteriores de flex, despues de hacer la asignacion debia llamar
a la accion especial YY_NEW_FILE; esto ya no es necesario);
- ejecutando una sentencia return;
- ejecutando la accion especial yyterminate();
- o, conmutando a un nuevo buffer usando yy_switch_to_buffer()
como se mostro en el ejemplo anterior.
Las reglas <<EOF>> no deberian usarse con otros patrones; estas
deberian 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 condicion de arranque
inicial, use
<INITIAL><<EOF>>
Estas reglas son utiles 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();
}
MACROS MISCEL'ANEAS
La macro YY_USER_ACTION puede definirse para indicar una accion que
siempre se ejecuta antes de la accion de la regla emparejada. Por
ejemplo, podria declararse con #define para que llame a una rutina que
convierta yytext a minusculas. Cuando se invoca a YY_USER_ACTION, la
variable yy_act da el numero de la regla emparejada (las reglas estan
numeradas comenzando en 1). Suponga que quiere medir la frecuencia con
la que sus reglas son emparejadas. Lo que viene a continuacion podria
hacer este truco:
#define YY_USER_ACTION ++ctr[yy_act]
donde ctr en un vector que mantiene la cuenta para las diferentes
reglas. Fijese que la macro YY_NUM_RULES da el numero total de reglas
(incluyendo la regla por defecto, incluso si usted usa -s), asi que una
declaracion correcta para ctr es:
int ctr[YY_NUM_RULES];
La macro YY_USER_INIT podria definirse para indicar una accion que
siempre se ejecuta antes del primer analisis (y antes de que se haga la
inicializacion interna del analizador). Por ejemplo, este podria
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 mas 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 mas abajo). Un valor distinto de cero en
la invocacion de la macro marcara el buffer como interactivo, un valor
de cero como no-interactivo. Fijese que el uso de esta macro no tiene
en cuenta %option always-interactive o %option never-interactive (ver
Opciones mas abajo). yy_set_interactive() debe invocarse antes del
comienzo del analisis 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 analisis actual para el proximo emparejamiento de token
se hace como si se encontrara al principio de una linea. 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 proximo token analizado a
partir del buffer actual tendra activas las reglas '^', de otra manera
falso.
En el analizador generado, las acciones estan recogidas en una gran
sentencia switch y separadas usando YY_BREAK, que puede ser redefinida.
Por defecto, este es simplemente un "break", para separar la accion 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
(imientras 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 accion de la regla finaliza con un
"return", el YY_BREAK es inaccesible.
VALORES DISPONIBLES AL USUARIO
Esta seccion resume los diferentes valores disponibles al usuario en
las acciones de la regla.
- char *yytext apunta al texto del token actual. Este puede
modificarse pero no alargarse (no puede anadir caracteres al
final).
Si aparece la directiva especial %array en la primera seccion de
la descripcion del analizador, entonces yytext se declara en su
lugar como char yytext[YYLMAX], donde YYLMAX es la definicion de
una macro que puede redefinir en la primera seccion si no le
gusta el valor por defecto (generalmente 8KB). El uso de %array
produce analizadores algo mas 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 -+ ).
- int yyleng contiene la longitud del token actual.
- FILE *yyin es el fichero por el que flex lee por defecto. Este
podria redefinirse pero hacerlo solo tiene sentido antes de que
el analisis comience o despues de que se haya encontrado un EOF.
Cambiandolo en medio del analisis tendra resultados inesperados
ya que flex utiliza buffers en su entrada; use yyrestart() en su
lugar. Una vez que el analisis 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.
- void yyrestart( FILE *new_file ) podria ser llamada para que
yyin apunte al nuevo fichero de entrada. El cambio al nuevo
fichero es inmediato (cualquier entrada contenida en el buffer
previamente se pierde). Fijese que llamando a yyrestart() con
yyin como argumento de esta manera elimina el buffer de entradda
actual y continua analizando el mismo fichero de entrada.
- FILE *yyout es el fichero sobre el que se hacen las acciones
ECHO. Este puede ser reasignado por el usuario.
- YY_CURRENT_BUFFER devuelve un handle YY_BUFFER_STATE al buffer
actual.
- YY_START devuelve un valor entero correspondiente a la condicion
de arranque actual. Posteriormente puede usar este valor con
BEGIN para retornar a la condicion de arranque.
INTERFAZ CON YACC
Uno de los usos principales de flex es como companero del generador de
analizadores sintacticos yacc. Los analizadores de yacc esperan
invocar a una rutina llamada yylex() para encontrar el proximo token de
entrada. La rutina se supone que devuelve el tipo del proximo token
ademas de poner cualquier valor asociado en la variable global yylval.
Para usar flex con yacc, uno especifica la opcion -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 podria parecerse a:
%{
#include "y.tab.h"
%}
%%
[0-9]+ yylval = atoi( yytext ); return TOK_NUMERO;
OPCIONES
flex tiene las siguientes opciones:
-b Genera informacion de retroceso en lex.backup. Esta es una
lista de estados del analizador que requieren retroceso y los
caracteres de entrada con los que la hace. Anadiendo reglas uno
puede eliminar estados de retroceso. Si todos los estados de
retroceso se eliminan y se usa -Cf o -CF, el analizador generado
funcionara mas rapido (ver la bandera -p). Unicamente los
usuarios que desean exprimir hasta el ultimo ciclo de sus
analizadores necesitan preocuparse de esta opcion. (Ver la
seccion sobre Consideraciones de Rendimiento mas abajo.)
-c es una opcion que no hace nada, incluida para cumplir con POSIX.
-d hace que el analizador generado se ejecute en modo de
depuraci'on. Siempre que se reconoce un patron y la variable
global yy_flex_debug no es cero (que por defecto no lo es), el
analizador escribira en stderr una linea de la forma:
--accepting rule at line 53 ("el texto emparejado")
El numero de linea 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 tambien 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.
-f especifica un analizador r'apido. No se realiza una compresion
de tablas y se evita el uso de stdio. El resultado es grande
pero rapido. Esta opcion es equivalente a -Cfr (ver mas abajo).
-h genera un sumario de "ayuda" de las opciones de flex por stdout
y entonces finaliza. -? y --help son sinonimos de -h.
-i indica a flex que genere un analizador case-insensitive. Se
ignorara si las letras en los patrones de entrada de flex son en
mayusculas o en minusculas, y los tokens en la entrada seran
emparejados sin tenerlo en cuenta. El texto emparejado dado en
yytext tendra las mayusculas y minusculas preservadas (es decir,
no se convertiran).
-l activa el modo de maxima compatibilidad con la implementacion
original de lex de AT&T. Fijese que esto no significa una
compatibilidad completa. El uso de esta opcion cuesta una
cantidad considerable de rendimiento, y no puede usarse con las
opciones -+, -f, -F, -Cf, o -CF. Para los detalles a cerca de
la compatibilidad que se ofrece, vea la seccion
"Incompatibilidades con Lex y POSIX" mas abajo. Esta opcion
tambien hace que se defina el nombre YY_FLEX_LEX_COMPAT en el
analizador generado.
-n es otra opcion que no hace nada, incluida para cumplir con
POSIX.
-p genera un informe de rendimiento en stderr. El informe consta
de comentarios que tratan de las propiedades del fichero de
entrada de flex que provocaran perdidas serias de rendimiento en
el analizador resultante. Si indica esta bandera dos veces,
tambien obtendra comentarios que tratan de las propiedades que
producen perdidas menores de rendimiento.
Fijese que el uso de REJECT, %option yylineno, y el contexto
posterior variable (vea la seccion Deficiencias / Errores mas
abajo) supone una penalizacion substancial del rendimiento; el
uso de yymore(), el operador ^, y la bandera -I supone
penalizaciones del rendimiento menores.
-s hace que la regla por defecto (que la entrada sin emparejar del
analizador se repita por stdout) se suprima. Si el analizador
encuentra entrada que no es reconocida por ninguna de sus
reglas, este aborta con un error. Esta opcion es util para
encontrar agujeros en el conjunto de reglas del analizador.
-t indica a flex que escriba el analizador que genera a la salida
estandar en lugar de en lex.yy.c.
-v especifica que flex deberia escribir en stderr un sumario de
estadisticas respecto al analizador que genera. La mayoria de
las estadisticas no tienen significado para el usuario casual de
flex, pero la primera linea identifica la version de flex (la
misma que se informa con -V), y la proxima linea las banderas
utilizadas cuando se genera el analizador, incluyendo aquellas
que se encuentran activadas por defecto.
-w suprime los mensajes de aviso.
-B dice a flex que genere un analizador batch, que es lo opuesto al
analizador interactivo generador por -I (ver mas abajo). En
general, use -B cuando este seguro de que su analizador nunca se
usara de forma interactiva, y quiere con esto exprimir un poco
mas el rendimiento. Si por el contrario su objetivo es
exprimirlo mucho mas, deberia estar utilizando la opcion -Cf o
-CF (comentadas mas abajo), que activa -B automaticamente de
todas maneras.
-F especifica que se debe utilizar la representacion de la tabla
r'apida (y elimina referencias a stdio). Esta representacion es
aproximadamente tan rapida como la representacion completa de la
tabla (-f), y para algunos conjuntos de patrones sera
considerablemente mas pequena (y para otros, mayor). En
general, si el conjunto de patrones contiene "palabras clave" y
una regla "identificador" atrapalo-todo, como la del conjunto:
"case" return TOK_CASE;
"switch" return TOK_SWITCH;
...
"default" return TOK_DEFAULT;
[a-z]+ return TOK_ID;
entonces sera mejor que utilice la representacion de la tabla
completa. Si solo esta presente la regla "identificador" y
utiliza una tabla hash o algo parecido para detectar palabras
clave, mejor utilice -F.
Esta opcion es equivalente a -CFr (ver mas abajo). Esta opcion
no puede utilizarse con -+.
-I ordena a flex que genere un analizador interactivo Un analizador
interactivo es uno que solo mira hacia delante para decidir que
token ha sido reconocido unicamente si debe hacerlo. Resulta
que mirando siempre un caracter extra hacia delante, incluso si
el analizador ya ha visto suficiente texto para eliminar la
ambiguedad del token actual, se es un poco mas rapido que
mirando solamente cuando es necesario. Pero los analizadores
que siempre miran hacia delante producen un comportamiento
interactivo malisimo; por ejemplo, cuando un usuario teclea una
linea nueva, esta no se reconoce como un token de linea nueva
hasta que introduzca otro token, que a menudo significa
introducir otra linea completa.
Los analizadores de flex por defecto son interactivos a menos
que use la opcion -Cf o -CF de compresion de tablas (ver mas
abajo). Esto es debido a que si esta buscando un rendimiento
alto tendria que estar utilizando una de estas opciones, asi que
si no lo ha hecho flex asume que prefiere cambiar un poco de
rendimiento en tiempo de ejecucion en beneficio de un
comportamiento iteractivo intuitivo. Fijese tambien que no
puede utilizar -I conjuntamente con -Cf o -CF. Asi, esta opcion
no se necesita realmente; esta activa por defecto para todos
esos casos en los que se permite.
Usted puede forzar al analizador que no sea interactivo usando
-B (ver mas arriba).
-L ordena a flex que no genere directivas #line. Sin esta opcion,
flex acribilla al analizador generado con directivas #line para
que los mensajes de error en las acciones esten localizadas
correctamente respecto al fichero original de flex (si los
errores son debidos al codigo en el fichero de entrada), o a
lex.yy.c (si los errores son fallos de flex -- deberia informar
de este tipo de errores a la direccion de correo dada mas
abajo).
-T hace que flex se ejecute en modo de traza. Este generara un
monton de mensajes en stderr relativos a la forma de la entrada
y el automata finito no-determinista o determinista resultante.
Esta opcion generalmente es para usarla en el mantenimiento de
flex.
-V imprime el numero de la version en stdout y sale. --version es
un sinonimo de -V.
-7 ordena a flex que genere un analizador de 7-bits, es decir, uno
que solo 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 tamano de aquellas generadas usando la opcion
-8 (ver mas abajo). La desventaja es que tales analizadores a
menudo se cuelgan o revientan si su entrada contiene caracteres
de 8-bits.
Fijese, sin embargo, que a menos que genere su analizador
utilizando las opciones de compresion de tablas -Cf o -CF, el
uso de -7 ahorrara solamente una pequena cantidad de espacio en
la tabla, y hara su analizador considerablemente menos portable.
El comportamiento por defecto de flex es generar un analizador
de 8-bits a menos que use -Cf o -CF, en cuyo caso flex por
defecto genera analizadores de 7-bits a menos que su sistema
siempre este configurado para generar analizadores de 8-bits (a
menudo este sera el caso de los sistemas fuera de EEUU). Puede
decir si flex genero un analizador de 7 u 8 bits inspeccionando
el sumario de banderas en la salida de -v como se describio
anteriormente.
Fijese que si usa -Cfe o -CFe (esas opciones de compresion de
tablas, pero tambien el uso de clases de equivalencia como se
comentara mas abajo), flex genera aun por defecto un analizador
de 8-bits, ya que normalmente con estas opciones de compresion
las tablas de 8-bits completas no son mucho mas caras que las
tablas de 7-bits.
-8 ordena a flex que genere un analizador de 8-bits, es decir, uno
que puede reconocer caracteres de 8-bits. Esta bandera solo es
necesaria para analizadores generados usando -Cf o -CF, ya que
de otra manera flex por defecto genera un analizador de 8-bits
de todas formas.
Vea el comentario sobre -7 mas arriba a cerca del comportamiento
por defecto de flex y la discusion entre los analizadores de
7-bits y 8-bits.
-+ especifica que quiere que flex genere un analizador como una
clase de C++. Vea la seccion Generando Escaners en C++ mas
abajo para los detalles.
-C[aefFmr]
controla el grado de compresion de la tabla y, mas generalmente,
el compromiso entre analizadores pequenos y analizadores
rapidos.
-Ca ("alinea") ordena a flex que negocie tablas mas grandes en
el analizador generado para un comportamiento mas rapido porque
los elementos de las tablas estan mejor alineados para el acceso
a memoria y computacion. En algunas arquitecturas RISC, la
busqueda y manipulacion de palabras largas es mas eficiente que
con unidades mas pequenas tales como palabras cortas. Esta
opcion puede doblar el tamano de las tablas usadas en su
analizador.
-Ce ordena a flex que construya clases de equivalencia, es
decir, conjunto de caracteres que tienen identicas propiedades
lexicas (por ejemplo, si la unica aparicion de digitos en la
entrada de flex es en la clase de caracteres "[0-9]" entonces
los digitos '0', '1', ..., '9' se pondran todos en la misma
clase de equivalencia). Las clases de equivalencia normalmente
ofrecen notables reducciones en los tamanos de los ficheros
finales de tabla/objeto (tipicamente un factor de 2-5) y son
juiciosamente bastante baratos en cuanto al rendimiento (una
localizacion en un vector por caracter analizado).
-Cf especifica que se deben generar las tablas del analizador
completas - flex no deberia comprimir las tablas tomando ventaja
de las funciones de transicion similares para diferentes
estados.
-CF especifica que deberia usarse la representacion del
analizador rapido alternativo (descrito anteriormente en la
bandera -F ) Esta opcion no puede usarse con -+.
-Cm ordena a flex a que construya clases de meta-equivalencias,
que son conjuntos de clases de equivalencia (o caracteres, si
las clases de equivalencia no se estan 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 localizacion en un array por caracter analizado).
-Cr hace que el analizador generado elimine el uso de la
libreria de E/S estandar para la entrada. En lugar de llamar a
fread() o getc(), el analizador utilizara la llamada al sistema
read(), produciendo una ganancia en el rendimiento que varia de
sistema en sistema, pero en general probablemente es
insignificante a menos que tambien este usando -Cf o -CF. El
uso de -Cr puede producir un comportamiento extrano si, por
ejemplo, lee de yyin usando stdio antes de llamar al analizador
(porque el analizador perdera cualquier texto que sus lecturas
anteriores dejaron en el buffer de entrada de stdio).
-Cr no tiene efecto si usted define YY_INPUT (ver El Escaner
Generado mas arriba).
Con solamente -C se especifica que las tablas del analizador
deberian comprimirse pero no deberia utilizarse ni las clases de
equivalencia ni las clases de meta-equivalencias.
Las opciones -Cf o -CF y -Cm no tienen sentido juntas - no hay
oportunidad para las clases de meta-equivalencias si la tabla no
esta siendo comprimida. De otra forma las opciones podrian
mezclarse libremente, y son acumulativas.
La configuracion por defecto es -Cem, que especifica que flex
deberia generar clases de equivalencia y clases de meta-
equivalencias. Esta configuracion provee el mayor grado de
compresion. Puede llegarse a un compromiso entre analizadores
de ejecucion mas rapida con el coste de tablas mayores siendo
generalmente verdadero lo siguiente:
lo mas lento y pequeno
-Cem
-Cm
-Ce
-C
-C{f,F}e
-C{f,F}
-C{f,F}a
lo mas rapido y grande
Fijese que los analizadores con tablas mas pequenas normalmente
se generan y compilan de la forma mas rapida posible, asi que
durante el desarrollo usted normalmente querra usar como viene
por defecto, compresion maxima.
-Cfe a menudo es un buen compromiso entre velocidad y tamano
para la produccion de analizadores.
-osalida
ordena a flex que escriba el analizador al fichero salida en
lugar de a lex.yy.c. Si combina -o con la opcion -t, entonces
el analizador se escribe en stdout pero sus directivas #line
(vea la opcion -L mas arriba) hacen referencia al fichero
salida.
-Pprefijo
cambia el prefijo yy usado por defecto por 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 tambien cambia el nombre por defecto del fichero
de salida de lex.yy.c a lex.foo.c. Aqui estan 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 esta utilizando un analizador en C++, entonces
unicamente yywrap y yyFlexLexer se ven afectados.) Dentro de su
analizador, puede aun hacer referencia a las variables globales
y funciones usando cualquier version de su nombre; pero
externamente, estas tienen el nombre modificado.
Esta opcion le deja enlazar facilmente multiples programas flex
conjuntamente en el mismo ejecutable. Fijese, sin embargo, que
usando esta opcion tambien se renombra yywrap(), de manera que
ahora debe o bien proveer su propia version de la rutina (con el
nombre apropiado) para su analizador, o usar %option noyywrap,
ya que enlazar con -lfl no podra proveerle una por defecto.
-Sfichero_esqueleto
ignora el fichero de esqueleteo por defecto con el que flex
construye sus analizadores. Usted probablemente nunca
necesitara utilizar esta opcion a menos que este haciendo
mantenimiento o un desarrollo de flex.
flex tambien ofrece un mecanismo para controlar las opciones dentro de
la propia especificacion del analizador, en vez de a partir de la linea
de comando. Esto se hace incluyendo las directivas %option en la
primera seccion de la especificacion del analizador. Usted puede
especificar varias opciones con una sola directiva %option, y varias
directivas en la primera seccion de su fichero de entrada de flex.
La mayoria 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 negacion
son equivalentes a un numero:
7bit opcion -7
8bit opcion -8
align opcion -Ca
backup opcion -b
batch opcion -B
c++ opcion -+
caseful o
case-sensitive opuesto de -i (por defecto)
case-insensitive o
caseless opcion -i
debug opcion -d
default opuesto de la opcion -s
ecs opcion -Ce
fast opcion -F
full opcion -f
interactive opcion -I
lex-compat opcion -l
meta-ecs opcion -Cm
perf-report opcion -p
read opcion -Cr
stdout opcion -t
verbose opcion -v
warn opuesto de la opcion -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
estan disponibles:
always-interactive
ordena a flex que genere un analizador que siempre considere su
entrada como "interactiva". Normalmente, sobre cada fichero de
entrada nuevo el analizador llama a isatty() como intento para
determinar si la entrada del analizador es interactiva y por lo
tanto deberia leer un caracter a la vez. Cuando esta opcion se
utilice, sin embargo, entonces no se hace tal llamada.
main ordena a flex que facilite un programa main() por defecto para
el analizador, que simplemente llame a yylex(). Esta opcion
implica noyywrap (ver mas abajo).
never-interactive
ordena a flex que genere un analizador que nunca considere su
entrada como "interactiva" (de nuevo, no se hace ninguna llamada
a isatty()). Esta es la opuesta a always-interactive.
stack activa el uso de pilas de condiciones de arranque (ver
Condiciones de Arranque mas arriba).
stdinit
si se establece (es decir, %option stdinit) inicializa 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 compilacion.
yylineno
ordena a flex a generar un analizador que mantenga el numero de
la linea actual leida desde su entrada en la variable global
yylineno. Esta opcion viene implicita con %option lex-compat.
yywrap si no se establece (es decir, %option noyywrap), hace que el
analizador no llame a yywrap() hasta el fin-de-fichero, pero
simplemente asume que no hay mas ficheros que analizar (hasta
que el usuario haga apuntar 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 estan
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 desactivandolas 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"
solo se aplica cuando se genera un analizador en C++ (opcion -+). Este
informa a flex que ha derivado a foo como una subclase de yyFlexLexer,
asi que flex pondra sus acciones en la funcion miembro foo::yylex() en
lugar de yyFlexLexer::yylex(). Este tambien genera una funcion miembro
yyFlexLexer::yylex() que emite un error en tiempo de ejecucion
(invocando a yyFlexLexer::LexerError()) si es llamada. Ver Generando
Escaners en C++, mas abajo, para informacion adicional.
Estan disponibles un numero de opciones para los puristas de lint que
desean suprimir la aparicion 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 apareceran de todas manera a
menos que use %option stack).
CONSIDERACIONES DE RENDIMIENTO
El principal objetivo de diseno 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 compresion de tablas -C
anteriormente introducidas, hay un numero de opciones/acciones que
degradan el rendimiento. Estas son, desde la mas 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 linea
yymore()
siendo las tres primeras bastante costosas y las dos ultimas bastante
economicas. Fijese tambien que unput() se implementa como una llamada
de rutina que potencialmente hace bastante trabajo, mientras que
yyless() es una macro bastante economica; asi que si esta devolviendo
algun texto excedente que ha analizado, use yyless().
REJECT deberia evitarse a cualquier precio cuando el rendimiento es
importante. Esta es una opcion particularmente cara.
Es lioso deshacerse del retroceso y a menudo podria 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 -
numeros de linea 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 -
numeros de linea 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 -
numeros de linea 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 lineas nos dicen que hay un estado del analizador en el
que se puede hacer una transicion 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 lineas 2 y 3 en el fichero de
entrada. Si el analizador esta en ese estado y entoces lee cualquier
cosa que no sea una 'o', tendra que retroceder para encontrar una regla
que empareje. Con un poco de analisis uno puede ver que este debe ser
el estado en el que se esta cuando se ha visto "fo". Cuando haya
ocurrido, si se ve cualquier cosa que no sea una 'o', el analizador
tendra 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 tendra 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
o -CF, y que no hay ninguna mejora del rendimiento haciendolo con
analizadores comprimidos.
La manera de quitar los retrocesos es anadiendo 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 eliminacion de retroceso en una lista de palabras clave tambien
puede hacerse utilizando una regla "atrapalo-todo":
%%
foo return TOK_KEYWORD;
foobar return TOK_KEYWORD;
[a-z]+ return TOK_ID;
Normalmente esta es la mejor solucion cuando sea adecuada.
Los mensajes sobre retrocesos tienden a aparecer en cascada. Con un
conjunto complicado de reglas no es poco comun obtener cientos de
mensajes. Si uno puede descifrarlos, sin embargo, a menudo solo hay
que tomar una docena de reglas o algo asi para eliminar los retrocesos
(ya que es facil cometer una equivocacion y tener una regla de error
que reconozca un token valido. Una posible caracteristica futura de
flex sera anadir reglas automaticamente para eliminar el retroceso).
Es importante tener en cuenta que se obtienen los beneficios de
eliminar el retroceso solo 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 perdida de rendimiento
que REJECT (es decir, substanciales). Asi que cuando sea posible una
regla como esta:
%%
raton|rata/(gato|perro) correr();
es mejor escribirla asi:
%%
raton/gato|perro correr();
rata/gato|perro correr();
o asi
%%
raton|rata/gato correr();
raton|rata/perro correr();
Fijese que aqui la accion especial '|' no ofrece ningun ahorro, y puede
incluso hacer las cosas peor (ver Deficiencias / Errores mas abajo).
Otro area donde el usuario puede incrementar el rendimiento del
analizador (y una que es mas facil de implementar) surge del hecho que
cuanto mas tarde se empareje un token, mas rapido ira el analizador.
Esto es debido a que con tokens grandes el procesamiento de la mayoria
de los caracteres de entrada tiene lugar en el (corto) bucle de
analisis mas interno, y no tiene que ir tan a menudo a hacer el trabajo
de mas para constituir el entorno del analizador (p.ej., yytext) para
la accion. 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 podria acelerarse escribiendolo 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 linea nueva requiera el procesamiento de
otra regla, el reconocimiento de las lineas nuevas se "distribuye"
sobre las otras reglas para mantener el texto reconocido tan largo como
sea posible. iFijese que el a~nadir reglas no ralentiza el analizador!
La velocidad del analizador es independiente del numero de reglas o
(dadas las consideraciones dadas al inicio de esta seccion) cuan
complicadas sean las reglas respecto a operadores tales como '*' y '|'.
Un ejemplo final sobre la aceleracion de un analizador: suponga que
quiere analizar un fichero que contiene identificadores y palabras
clave, una por linea y sin ningun caracter extrano, y reconocer todas
las palabras clave. Una primera aproximacion 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 atrapalo-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 exactamente una palabra por linea,
entonces podemos reducir el numero total de emparejamientos por la
mitad mezclando el reconocimiento de lineas 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 aqui, ya que hemos reintroducido retroceso
en el analizador. En particular, aunque nosotros sepamos que ahi nunca
habran otros caracteres en el flujo de entrada que no sean letras o
lineas nuevas, flex no puede figurarse eso, y planeara la posible
necesidad de retroceder cuando haya analizado un token como "auto" y el
proximo caracter sea algo distinto a una linea nueva o una letra.
Previamente este podria 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, podriamos o bien
duplicar todas las reglas pero sin linea nueva al final, o, ya que
nunca esperamos encontrar tal entrada y por lo tanto ni como es
clasificada, podemos introducir una regla atrapalo-todo mas, esta que
no incluye una linea 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 rapido 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 multiples NUL's. Es mejor escribir reglas que
emparejen cortas cantidades de texto si se anticipa que el texto
incluira NUL's a menudo.
Otra nota final en relacion con el rendimiento: tal y como se menciono
en la seccion Como se Reconoce la Entrada, el reajuste dinamico 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, deberia intentar emparejar
"grandes" cantidades de texto pero no "inmensas" cantidades, donde el
punto medio esta en torno a los 8K caracteres/token.
GENERANDO ESC'ANERES EN C++
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 deberia encontrarse ante ningun error de compilacion (por favor
informe de cualquier error que encuentre a la direccion de correo
electronico dada en la seccion Autores mas abajo). Puede entonces usar
codigo C++ en sus acciones de las reglas en lugar de codigo C. Fijese
que la fuente de entrada por defecto para su analizador permanece como
yyin, y la repeticion por defecto se hace aun a yyout. Ambos
permanecen como variables FILE * y no como flujos de C++.
Tambien puede utilizar flex para generar un analizador como una clase
de C++, utilizando la opcion -+ (o, equivalentemente, %option c++), que
se especifica automaticamente si el nombre del ejecutable de flex
finaliza con un '+', tal como flex++. Cuando se usa esta opcion, flex
establece por defecto la generacion 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:
const char* YYText()
retorna el texto del token reconocido mas recientemente, el
equivalente a yytext.
int YYLeng()
retorna la longitud del token reconocido mas recientemente, el
equivalente a yyleng.
int lineno() const
retorna el numero de linea de entrada actual (ver %option
yylineno), o 1 si no se uso %option yylineno.
void set_debug( int flag )
activa la bandera de depuracion para el analizador, equivalente
a la asignacion de yy_flex_debug (ver la seccion Opciones mas
arriba). Fijese que debe construir el analizador utilizando
%option debug para incluir informacion de depuracion en este.
int debug() const
retorna el estado actual de la bandera de depuracion.
Tambien 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( istream* arg_yyin = 0, ostream* arg_yyout = 0 )
construye un objeto 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.
virtual int yylex()
hace el mismo papel que yylex() en los analizadores de flex
ordinarios: analiza el flujo de entrada, consumiendo tokens,
hasta que la accion de una regla retorne un valor. Si usted
deriva una subclase S a partir de 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 estara utilizando esa subclase en lugar de yyFlexLexer.
Es este caso, en vez de generar yyFlexLexer::yylex(), flex
genera S::yylex() (y tambien genera un substituto
yyFlexLexer::yylex() que llama a yyFlexLexer::LexerError() si se
invoca).
virtual void switch_streams(istream* new_in = 0,
ostream* new_out = 0) reasigna yyin a new_in (si no es nulo) e
yyout a new_out (idem), borrando el buffer de entrada anterior
si se reasigna yyin.
int yylex( istream* new_in, ostream* new_out = 0 )
primero conmuta el flujo de entrada via switch_streams( new_in,
new_out ) y entonces retorna el valor de yylex().
Ademas, yyFlexLexer define las siguientes funciones virtuales
protegidas que puede redefinir en clases derivadas para adaptar el
analizador:
virtual int LexerInput( char* buf, int max_size )
lee hasta max_size caracteres en buf y devuelve el numero de
caracteres leidos. Para indicar el fin-de-la-entrada, devuelve
0 caracteres. Fijese que los analizadores "interactivos" (ver
las banderas -B y -I ) definen la macro YY_INTERACTIVE. Si
usted redefine LexerInput() y necesita tomar acciones distintas
dependiendo de si el analizador esta analizando una fuente de
entrada interactivo o no, puede comprobar la presencia de este
nombre mediante #ifdef.
virtual void LexerOutput( const char* buf, int size )
escribe a la salida size caracteres desde el buffer buf, que,
mientras termine en NUL, puede contener tambien NUL's "internos"
si las reglas del analizador pueden emparejar texto con NUL's
dentro de este.
virtual void LexerError( const char* msg )
informa con un mensaje de error fatal. La version por defecto
de esta funcion escribe el mensaje al flujo cerr y finaliza.
Fijese que un objeto yyFlexLexer contiene su estado de analisis
completo. Asi 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 opcion -P comentada
anteriormente.
Finalmente, note que la caracteristica %array no esta disponible en
clases de analizadores en C++; debe utilizar %pointer (por defecto).
Aqui 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 << "numero " << 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 opcion prefix= ) para renombrar cada yyFlexLexer a algun otro
xxFlexLexer. Entonces puede incluir <FlexLexer.h> en los otros
ficheros fuente una vez por clase analizadora, primero renombrando
yyFlexLexer como se presenta a continuacion:
#undef yyFlexLexer
#define yyFlexLexer xxFlexLexer
#include <FlexLexer.h>
#undef yyFlexLexer
#define yyFlexLexer zzFlexLexer
#include <FlexLexer.h>
si, por ejemplo, usted utilizo %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
podria cambiar considerablemente entre versiones principales.
INCOMPATIBILIDADES CON LEX Y POSIX
flex es una reescritura de la herramienta lex del Unix de AT&T (aunque
las dos implementaciones no comparten ningun codigo), con algunas
extensiones e incompatibilidades, de las que ambas conciernen a
aquellos que desean escribir analizadores aceptables por cualquier
implementacion. Flex sigue completamente la especificacion 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
especificacion POSIX.
En esta seccion comentaremos todas las areas conocidas de
incompatibilidades entre flex, lex de AT&T, y la especificacion POSIX.
La opcion -l de flex activa la maxima compatibilidad con la
implementacion original de lex de AT&T, con el coste de una mayor
perdida de rendimiento en el analizador generado. Indicamos mas abajo
que incompatibilidades pueden superarse usando la opcion -l.
flex es totalmente compatible con lex con las siguientes excepciones:
- La variable interna del analizador de lex sin documentar
yylineno no se ofrece a menos que se use -l o %option yylineno.
yylineno deberia gestionarse por buffer, en lugar de por
analizador (simple variable global).
yylineno no es parte de la especificacion POSIX.
- La rutina input() no es redefinible, aunque podria invocarse
para leer los caracteres que siguen a continuacion de lo que
haya sido reconocido por una regla. Si input() se encuentra con
un fin-de-fichero se realiza el procesamiento de yywrap()
normal. input() retorna un fin-de-fichero ``real'' como EOF.
La entrada en su lugar se controla definiendo la macro YY_INPUT.
La restriccion de flex de que input() no puede redefinirse va de
acuerdo a la especificacion POSIX, que simplemente no especifica
ninguna manera de controlar la entrada del analizador que no sea
haciendo una asignacion inicial a yyin.
- La rutina unput() no es redefinible. Esta restriccion va de
acuerdo a POSIX.
- Los analizadores de flex no son tan reentrantes como los
analizadores de lex. En particular, si tiene un analizador
interactivo y un gestor de interrupcion con long-jumps fuera del
analizador, y el analizador a continuacion se invoca de nuevo,
podria obtener el siguiente mensaje:
fatal flex scanner internal error--end of buffer missed
Para volver al analizador, primero utilice
yyrestart( yyin );
Vea que esta llamada eliminara cualquier entrada en el buffer;
normalmente esto no es un problema con un analizador
interactivo.
Dese cuenta tambien de que las clases analizadoras en C++ son
reentrantes, asi que si usar C++ es una opcion para usted,
deberia utilizarla. Vea "Generando Escaners en C++" mas arriba
para los detalles.
- output() no se provee. La salida desde la macro ECHO se hace al
puntero de fichero yyout (por defecto a stdout).
output() no es parte de la especificacion POSIX.
- lex no acepta condiciones de arranque exclusivas (%x), aunque
estan en la especificacion POSIX.
- Cuando se expanden las definiciones, flex las encierra entre
parentesis. Con lex, lo siguiente:
NOMBRE [A-Z][A-Z0-9]*
%%
foo{NOMBRE}? printf( "Lo encontro\n" );
%%
no reconocera 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 expandira a "foo([A-Z][A-Z0-9]*)?" y asi la cadena "foo" se
reconocera.
Fijese que si la definicion comienza con ^ o finaliza con $
entonces no se expande con parentesis, 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 definicion de flex.
El uso de -l produce en el comportamiendo de lex el no poner
parentesis alrededor de la definicion.
La especificacion de POSIX dice que la definicion debe ser
encerrada entre parentesis.
- Algunas implementaciones de lex permiten que la accion de una
regla comience en una linea separada, si el patron de la regla
tiene espacios en blanco al final:
%%
foo|bar<espacio aqui>
{ foobar_action(); }
flex no dispone de esta propiedad.
- La opcion %r de lex (generar un analizador Ratfor) no se ofrece.
No es parte de la especificacion de POSIX.
- Despues de una llamada a unput(), el contenido de yytext esta
indefinido hasta que se reconozca el proximo token, a menos que
el analizador se haya construido usando %array. Este no es el
caso de lex o la especificacion de POSIX. La opcion -l elimina
esta incompatibilidad.
- La precedencia del operador {} (rango numerico) es diferente.
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 ultimo va de
acuerdo con la especificacion de POSIX.
- La precedencia del operador ^ es diferente. lex interpreta
"^foo|bar" como "empareja bien 'foo' al principio de una linea,
o 'bar' en cualquier lugar", mientras que flex lo interpreta
como "empareja 'foo' o 'bar' si vienen al principio de una
linea". Lo ultimo va de acuerdo con la especificacion de POSIX.
- Las declaraciones especiales del tamano de las tablas tal como
%a que reconoce lex no se requieren en los analizadores de flex;
flex los ignora.
- El identificador FLEX_SCANNER se #define de manera que los
analizadores podrian escribirse para ser procesados con flex o
con lex. Los analizadores tambien incluyen
YY_FLEX_MAJOR_VERSION y YY_FLEX_MINOR_VERSION indicando que
version de flex genero el analizador (por ejemplo, para la
version 2.5, estas definiciones seran 2 y 5 respectivamente).
Las siguientes propiedades de flex no se incluyen en lex o la
especificacion POSIX:
analizadores en C++
%option
ambitos 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 linea
mas casi todas las banderas de flex. La ultima propiedad en la lista
se refiere al hecho de que con flex puede poner varias acciones en la
misma linea, 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 accion. Las acciones que no se encierran en llaves
simplemente se terminan al final de la linea.
DIAGN'OSTICOS
aviso, la regla no se puede aplicar indica que la regla dada no puede
emparejarse porque sigue a otras reglas que siempre emparejaran el
mismo texto que el de esta. Por ejemplo, en el siguiente ejemplo "foo"
no puede emparejarse porque viene despues de una regla "atrapalo-todo"
para identificadores:
[a-z]+ obtuvo_identificador();
foo obtuvo_foo();
El uso de REJECT en un analizador suprime este aviso.
aviso, se ha especificado la opci'on -s pero se puede aplicar la regla
por defecto significa que es posible (tal vez unicamente en una
condicion de arranque en particular) que la regla por defecto
(emparejar cualquier caracter simple) sea la unica que emparejara una
entrada particular. Ya que se indico -s, presumiblemente esto no es lo
que se pretendia.
definici'on no definida {reject_used_but_not_detected} o definici'on no
definida {yymore_used_but_not_detected} - Estos errores pueden suceder
en tiempo de compilacion. Indican que el analizador usa REJECT o
yymore() pero que flex fallo en darse cuenta del hecho, queriendo decir
que flex analizo las dos primeras secciones buscando apariciones de
estas acciones y fallo en encontrar alguna, pero que de algun modo se
le han colado (por medio de un archivo #include, por ejemplo). Use
%option reject o %option yymore para indicar a flex que realmente usa
esta funcionalidad.
flex scanner jammed - un analizador compilado con -s ha encontrado una
cadena de entrada que no fue reconocida por niguna de sus reglas. Este
error puede suceder tambien debido a problemas internos.
token too large, exceeds YYLMAX - su analizador usa %array y una de sus
reglas reconocio una cadena mas grande que la constante YYLMAX (8K
bytes por defecto). Usted puede incrementar el valor haciendo un
#define YYLMAX en la seccion de definiciones de su entrada de flex.
el analizador requiere la opci'on -8 para poder usar el car'acter 'x' -
La especificacion de su analizador incluye el reconocimiento del
caracter de 8-bits 'x' y no ha especificado la bandera -8, y su
analizador por defecto esta a 7-bits porque ha usado las opciones -Cf o
-CF de compresion de tablas. Vea el comentario de la bandera -7 para
los detalles.
flex scanner push-back overflow - usted utilizo unput() para devolver
tanto texto que el buffer del analizador no pudo mantener el texto
devuelto y el token actual en yytext. Idealmente el analizador deberia
ajustar dinamicamente el buffer en este caso, pero actualmente no lo
hace.
input buffer overflow, can't enlarge buffer because scanner uses REJECT
- el analizador estaba intentando reconocer un token extremadamente
largo y necesito expandir el buffer de entrada. Esto no funciona con
analizadores que usan REJECT.
fatal flex scanner internal error--end of buffer missed - Esto puede
suceder en un analizador que se reintroduce despues de que un long-jump
haya saltado fuera (o sobre) el registro de activacion del analizador.
Antes de reintroducir el analizador, use:
yyrestart( yyin );
o, como se comento mas arriba, cambie y use el analizador como clase de
C++.
too many start conditions in <> construct! - ha listado mas condiciones
de arranque en una construccion <> que las que existen (asi que tuvo
que haber listado al menos una de ellas dos veces).
FICHEROS
-lfl libreria con la que los analizadores deben enlazarse.
lex.yy.c
analizador generado (llamado lexyy.c en algunos sistemas).
lex.yy.cc
clase generada en C++ con el analizador, cuando se utiliza -+.
<FlexLexer.h>
fichero de cabecera definiendo la clase base del analizador en
C++, FlexLexer, y su clase derivada, yyFlexLexer.
flex.skl
esqueleto del analizador. Este fichero se utiliza unicamente
cuando se construye flex, no cuando flex se ejecuta.
lex.backup
informacion de los retrocesos para la bandera -b (llamada
lex.bck en algunos sistemas).
DEFICIENCIAS / ERRORES
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.
(Fijese que el borrador de POSIX establece que el texto reconocido por
tales patrones no esta definido.)
Para algunas reglas de contexto posterior, partes que son de hecho de
longitud fija no se reconocen como tales, resultando en la perdida de
rendimiento mencionada anteriormente. En particular, las partes que
usan '|' o {n} (tales como "foo{3}") siempre se consideran de longitud
variable.
La combinacion de contexto posterior con la accion especial '|' puede
producir que el contexto posterior fijo se convierta en contexto
posterior variable que es mas caro. Por ejemplo, en lo que viene a
continuacion:
%%
abc |
xyz/def
El uso de unput() invalida yytext e yyleng, a menos que se use la
directiva %array o la opcion -l.
La concordancia de patrones de NUL's es substancialmente mas lento que
el reconocimiento de otros caracteres.
El ajuste dinamico del buffer de entrada es lento, ya que conlleva el
reanalisis de todo el texto reconocido hasta entonces por el
(generalmente enorme) token actual.
Debido al uso simultaneo 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 numero de entradas en la tabla necesarias para determinar
que regla ha sido emparejada. El numero de entradas es igual al numero
de estados del DFA si el analizador no usa REJECT, y algo mayor que el
numero de estados si se usa.
REJECT no puede usarse con las opciones -f o -F.
El algoritmo interno de flex necesita documentacion.
VER TAMBI'EN
lex(1), yacc(1), sed(1), awk(1).
John Levine, Tony Mason, and Doug Brown, Lex & Yacc, O'Reilly and
Associates. Este seguro de obtener la 2a edicion.
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) (Edicion en castellano:
Compiladores: Principios, T'ecnicas y Herramientas, Addison-Wesley
Iberoamericana, S.A. (1990)) Describe las tecnicas de concordancia de
patrones usadas por flex (automata finito determinista).
AUTOR
Vern Paxson, con la ayuda de muchas ideas e inspiracion de Van
Jacobson. Version original por Jef Poskanzer. La representacion de
tablas rapidas es una implementacion parcial de un diseno hecho por Van
Jacobson. La implementacion 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 caido 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 distribucion.
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 multiples 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 alli por el apoyo que recibi.
Enviar comentarios a vern@ee.lbl.gov.
Sobre esta traduccion enviar comentarios a Adrian Perez Jorge
(alu1415@csi.ull.es).
Version 2.5 Abril 1995 FLEX(1)