Provided by: manpages-es-extra_0.8a-16_all bug

NOMBRE

       flex - generador de analizadores léxicos rápidos

SINOPSIS

       flex    [-bcdfhilnpstvwBFILTV78+?    -C[aefFmr]    -osalida   -Pprefijo
       -Sesqueleto] [--help --version] [nombrefichero ...]

INTRODUCCIÓN

       Este manual describe  flex,  una  herramienta  para  la  generación  de
       programas  que  realizan  concordancia de patrones en texto.  El manual
       incluye a la vez secciones de tutorial y de referencia:

           Descripción
               una breve introducción a la herramienta

           Algunos Ejemplos Simples

           Formato del Fichero de Entrada

           Patrones
               las expresiones regulares extendidas que utiliza flex

           Cómo se Empareja la Entrada
               las reglas para determinar lo que ha concordado

           Acciones
               cómo especificar qué hacer cuando concuerde un patrón

           El Escáner Generado
               detalles respecto al escáner que produce flex;
               cómo controlar la fuente de entrada

           Condiciones de Arranque
               la introdución de contexto en sus escáneres, y
               conseguir "mini-escáneres"

           Múltiples Buffers de Entrada
               cómo manipular varias fuentes de entrada; cómo
               analizar cadenas en lugar de ficheros.

           Reglas de Fin-de-Fichero
               reglas especiales para reconocer el final de la entrada

           Macros Misceláneas
               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 escáneres de flex junto con analizadores de yacc

           Opciones
               opciones de línea de comando de flex, y la directiva
               "%option"

           Consideraciones de Rendimiento
               cómo hacer que sus analizadores vayan tan rápido
               como sea posible

           Generando Escáneres en C++
               la facilidad (experimental) para generar analizadores
               léxicos como clases de C++

           Incompatibilidades con Lex y POSIX
               cómo flex difiere del lex de AT&T y del lex estándar
               de POSIX

           Diagnósticos
               esos mensajes de error producidos por flex (o por
               los escáneres que este genera) cuyo significado podría
               no ser evidente

           Ficheros
               los ficheros usados por flex

           Deficiencias / Errores
               problemas de flex conocidos

           Ver También
               otra documentación, herramientas relacionadas

           Autor
               incluye información de contacto

DESCRIPCIÓN

       flex es una herramienta para generar escneres: programas que reconocen
       patrones  léxicos en un texto.  flex lee los ficheros de entrada dados,
       o la entrada estándar si no se le ha indicado ningún nombre de fichero,
       con  la  descripción  de  un  escáner  a  generar.   La  descripción se
       encuentra en forma de parejas de  expresiones  regulares  y  código  C,
       denominadas  reglas.  flex  genera  como salida un fichero fuente en C,
       lex.yy.c, que define una rutina yylex().  Este fichero se compila y  se
       enlaza  con  la  librería  -lfl para producir un ejecutable.  Cuando se
       arranca el fichero ejecutable, este analiza  su  entrada  en  busca  de
       casos de las expresiones regulares.  Siempre que encuentra uno, ejecuta
       el código C correspondiente.

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  escáner  que  siempre  que  encuentre  la  cadena   "username"   la
       reemplazará por el nombre de entrada al sistema del usuario:

           %%
           username    printf( "%s", getlogin() );

       Por  defecto,  cualquier texto que no reconozca el analizador léxico de
       flex se copia a la salida, así que el efecto neto de  este  escáner  es
       copiar  su  fichero  de  entrada  a  la  salida  con  cada aparición de
       "username" expandida.   En  esta  entrada,  hay  solamente  una  regla.
       "username"  es  el patrn y el "printf" es la accin.  El "%%" marca el
       comienzo de las reglas.

       Aquí hay otro ejemplo simple:

                   int num_lineas = 0, num_caracteres = 0;

           %%
           \n      ++num_lineas; ++num_caracteres;
           .       ++num_caracteres;

           %%
           main()
                   {
                   yylex();
                   printf( "# de líneas = %d, # de caracteres. = %d\n",
                           num_lineas, num_caracteres );
                   }

       Este analizador cuenta el número de caracteres y el número de líneas en
       su  entrada (no produce otra salida que el informe final de la cuenta).
       La  primera  línea  declara  dos  variables  globales,  "num_lineas"  y
       "num_caracteres",  que son visibles al mismo tiempo dentro de yylex() y
       en la rutina main()  declarada  después  del  segundo  "%%".   Hay  dos
       reglas,  una que empareja una línea nueva ("\n") e incrementa la cuenta
       de líneas y la cuenta  de  caracteres,  y  la  que  empareja  cualquier
       caracter  que no sea una línea nueva (indicado por la expresión regular
       ".").

       Un ejemplo algo más complicado:

           /* escáner para un lenguaje de juguete al estilo de Pascal */

           %{
           /* se necesita esto para la llamada a atof() más abajo */
           #include <math.h>
           %}

           DIGITO   [0-9]
           ID       [a-z][a-z0-9]*

           %%

           {DIGITO}+   {
                       printf( "Un entero: %s (%d)\n", yytext,
                               atoi( yytext ) );
                       }

           {DIGITO}+"."{DIGITO}*      {
                       printf( "Un real: %s (%g)\n", yytext,
                               atof( yytext ) );
                       }

           if|then|begin|end|procedure|function        {
                       printf( "Una palabra clave: %s\n", yytext );
                       }

           {ID}        printf( "Un identificador: %s\n", yytext );

           "+"|"-"|"*"|"/"   printf( "Un operador: %s\n", yytext );

           "{"[^}\n]*"}"     /* se come una linea de comentarios */

           [ \t\n]+          /* se come los espacios en blanco */

           .           printf( "Caracter no reconocido: %s\n", yytext );

           %%

           main( argc, argv )
           int argc;
           char **argv;
               {
               ++argv, --argc;  /* se salta el nombre del programa */
               if ( argc > 0 )
                       yyin = fopen( argv[0], "r" );
               else
                       yyin = stdin;

               yylex();
               }

       Esto podría ser los comienzos de un escáner  simple  para  un  lenguaje
       como  Pascal.   Este  identifica diferentes tipos de tokens e informa a
       cerca de lo que ha visto.

       Los detalles de este ejemplo se explicarán en las secciones siguientes.

FORMATO DEL FICHERO DE ENTRADA

       El  fichero  de  entrada  de  flex  está  compuesto  de tres secciones,
       separadas por una línea donde aparece únicamente un %% en esta:

           definiciones
           %%
           reglas
           %%
           código de usuario

       La sección de definiciones contiene declaraciones  de  definiciones  de
       nombres  sencillas  para  simplificar  la especificación del escáner, y
       declaraciones de condiciones de arranque,  que  se  explicarán  en  una
       sección posterior.

       Las definiciones de nombre tienen la forma:

           nombre definición

       El  "nombre"  es  una palabra que comienza con una letra o un subrayado
       (’_’) seguido por cero o más letras, dígitos, ’_’, o ’-’  (guión).   La
       definición  se  considera que comienza en el primer caracter que no sea
       un espacio en blanco siguiendo al nombre y continuando hasta  el  final
       de  la línea.  Posteriormente se puede hacer referencia a la definición
       utilizando "{nombre}", que se expandirá a "(definición)".  Por ejemplo,

           DIGITO   [0-9]
           ID       [a-z][a-z0-9]*

       define  "DIGITO"  como  una  expresión  regular  que empareja un dígito
       sencillo, e "ID" como una expresión  regular  que  empareja  una  letra
       seguida por cero o más letras o dígitos.  Una referencia posterior a

           {DIGITO}+"."{DIGITO}*

       es idéntica a

           ([0-9])+"."([0-9])*

       y  empareja uno o más dígitos seguido por un ’.’ seguido por cero o más
       dígitos.

       La sección de reglas en la entrada de flex contiene una serie de reglas
       de la forma:

           patrón   acción

       donde  el patrón debe estar sin sangrar y la acción debe comenzar en la
       misma línea.

       Ver más  abajo  para  una  descripción  más  amplia  sobre  patrones  y
       acciones.

       Finalmente,  la  sección  de  código  de usuario simplemente se copia a
       lex.yy.c  literalmente.   Esta  sección  se  utiliza  para  rutinas  de
       complemento  que  llaman  al  escáner  o  son  llamadas  por  este.  La
       presencia de esta sección es opcional; Si se omite, el segundo %% en el
       fichero de entrada se podría omitir también.

       En  las  secciones de definiciones y reglas, cualquier texto sangrado o
       encerrado entre %{ y %} se copia íntegramente  a  la  salida  (sin  los
       %{}’s).   Los  %{}’s  deben  aparecer  sin  sangrar  en líneas ocupadas
       únicamente por estos.

       En la sección de reglas, cualquier texto o %{}  sangrado  que  aparezca
       antes de la primera regla podría utilizarse para declarar variables que
       son locales a la rutina de análisis y (después de las declaraciones) al
       código  que  debe  ejecutarse  siempre  que  se  entra  a  la rutina de
       análisis.  Cualquier otro texto sangrado o %{} en la sección de  reglas
       sigue copiándose a la salida, pero su significado no está bien definido
       y bien podría causar errores en tiempo de compilación  (esta  propiedad
       se  presenta  para  conformidad  con  POSIX  ; ver más abajo para otras
       características similares)

       En la sección de definiciones (pero no en la  sección  de  reglas),  un
       comentario  sin  sangría  (es  decir,  una  línea  comenzando con "/*")
       también se copia literalmente a la salida hasta el próximo "*/".

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 línea nueva
           [xyz]      una "clase de caracteres"; en este caso, el patrón
                        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
                        mayúscula.
           [^A-Z\n]   cualquier caracter EXCEPTO una letra mayúscula o
                        una línea nueva
           r*         cero o más r’s, donde r es cualquier expresión regular
           r+         una o más 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 más r’s
           r{4}       exactamente 4 r’s
           {nombre}   la expansión de la definición de "nombre"
                      (ver más abajo)
           "[xyz]\"foo"
                      la cadena literal: [xyz]"foo
           \X         si X es una ’a’, ’b’, ’f’, ’n’, ’r’, ’t’, o ’v’,
                        entonces la interpretación ANSI-C de \x.
                        En otro caso, un literal ’X’ (usado para
                        indicar operadores tales como ’*’)
           \0         un caracter NUL (código ASCII 0)
           \123       el caracter con valor octal 123
           \x2a       el caracter con valor hexadecimal 2a
           (r)        empareja una r; los paréntesis se utilizan para
                        anular la precedencia (ver más abajo)

           rs         la expresión regular r seguida por la expresión
                        regular s; se denomina "concatenación"

           r|s        bien una r o una s

           r/s        una r pero sólo si va seguida por una s.  El
                        texto emparejado por s se incluye cuando se
                        determina si esta regla es el "emparejamiento
                        más largo", pero se devuelve entonces a la
                        entrada antes que se ejecute la acción.  Así
                        que la acción sólo 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 sección Deficiencias / Errores más abajo
                        respecto al "contexto posterior peligroso".)
           ^r         una r, pero sólo al comienzo de una línea (es
                        decir, justo al comienzo del análisis, o a la
                        derecha después de que se haya analizado una
                        línea nueva).
           r$         una r, pero sólo al final de una línea (es decir,
                        justo antes de una línea nueva).  Equivalente
                        a "r/\n".

                      Fíjese que la noción de flex de una "línea nueva"
                      es exáctamente 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 explícitamente
                      usar r/\r\n para "r$".

           <s>r       una r, pero sólo en la condición de arranque s
                        (ver más abajo para una discusión 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 condición de arranque, incluso
                        una exclusiva.

           <<EOF>>    un fin-de-fichero
           <s1,s2><<EOF>>
                      un fin-de-fichero en una condición de arranque s1 o s2

       Fíjese  que  dentro de una clase de caracteres, todos los operadores de
       expresiones  regulares  pierden  su  significado  especial  excepto  el
       caracter de escape (’\’) y los operadores de clase de caracteres, ’-’,

       Las  expresiones  regulares  en  el listado anterior están agrupadas de
       acuerdo a la precedencia, desde la precedencia más alta en la cabeza  a
       la más baja al final.  Aquellas agrupadas conjuntamente tienen la misma
       precedencia.  Por ejemplo,

           foo|bar*

       es lo mismo que

           (foo)|(ba(r*))

       ya que el operador ’*’ tiene mayor precedencia que la concatenación,  y
       la  concatenación  más  alta  que  el operador ’|’.  Este patrón por lo
       tanto empareja bien la cadena "foo" o la cadena "ba" seguida de cero  o
       más r’s.  Para emparejar "foo" o, cero o más "bar"’s, use:

           foo|(bar)*

       y para emparejar cero o más "foo"’s o "bar"’s:

           (foo|bar)*

       Además  de  caracteres y rangos de caracteres, las clases de caracteres
       pueden también contener  expresiones  de  clases  de  caracteres.   Son
       expresiones  encerradas  entre  los  delimitadores [: y :] (que también
       deben aparecer entre el ’[’ y el ’]’ de la clase de caracteres;  además
       pueden  darse  otros  elementos dentro de la clase de caracteres).  Las
       expresiones válidas son:

           [:alnum:] [:alpha:] [:blank:]
           [:cntrl:] [:digit:] [:graph:]
           [:lower:] [:print:] [:punct:]
           [:space:] [:upper:] [:xdigit:]

       Todas estas expresiones designan un conjunto de caracteres equivalentes
       a  la  correspondiente  función  estándar  isXXX  de  C.   Por ejemplo,
       [:alnum:]  designa  aquellos  caracteres  para  los  cuales   isalnum()
       devuelve verdadero - esto es, cualquier caracter alfabético o numérico.
       Algunos sistemas no ofrecen isblank(), así que  flex  define  [:blank:]
       como un espacio en blanco o un tabulador.

       Por   ejemplo,   las   siguientes   clases   de  caracteres  son  todas
       equivalentes:

           [[:alnum:]]
           [[:alpha:][:digit:]]
           [[:alpha:]0-9]
           [a-zA-Z0-9]

       Si su escáner ignora la distinción entre mayúsculas  y  minúsculas  (la
       bandera  -i  ),  entonces  [:upper:]  y  [:lower:]  son  equivalentes a
       [:alpha:].

       Algunas notas sobre los patrones:

       -      Una clase de caracteres negada  tal  como  el  ejemplo  "[^A-Z]"
              anterior  emparejar  una  lnea  nueva  a menos que "\n" (o una
              secuencia de escape  equivalente)  sea  uno  de  los  caracteres
              presentes  explícitamente  en  la  clase  de  caracteres  negada
              (p.ej., "[^A-Z\n]").  Esto es diferente a  cómo  muchas  de  las
              otras herramientas de expresiones regulares tratan las clases de
              caracteres negadas, pero  desafortunadamente  la  inconsistencia
              está fervientemente enrraizada históricamente.  Emparejar líneas
              nuevas significa que un patrón como  [^"]*  puede  emparejar  la
              entrada completa a menos que haya otra comilla en la entrada.

       -      Una  regla  puede  tener  lo  más  una  instancia  del  contexto
              posterior (el operador ’/’ o el operador ’$’).  La condición  de
              arranque,   los   patrones  ’^’,  y  "<<EOF>>"  pueden  aparecer
              solamente al principio de un patrón, y, al igual que con  ’/’  y
              ’$’,  no  pueden  agruparse dentro de paréntesis.  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 está permitido:

                  foo/bar$
                  <sc1>foo<sc2>bar

              Fíjese que la primera regla se puede escribir como  "foo/bar\n".

              En  el  siguiente  ejemplo  un  ’$’  o un ’^’ es tratado como un
              caracter normal:

                  foo|(bar$)
                  foo|^bar

              Si lo que se desea es un "foo" o un "bar" seguido de  una  línea
              nueva,  puede  usarse  lo  siguiente  (la acción especial ’|’ se
              explica más abajo):

                  foo      |
                  bar$     /* la acción va aquí */

              Un truco parecido funcionará para emparejar un "foo" o, un "bar"
              al principio de una línea.

CÓMO SE EMPAREJA LA ENTRADA

       Cuando  el  escáner  generado está funcionando, este analiza su entrada
       buscando cadenas que concuerden con cualquiera  de  sus  patrones.   Si
       encuentra  más  de  un  emparejamiento,  toma el que empareje más texto
       (para reglas de contexto posterior, se incluye la longitud de la  parte
       posterior,  incluso  si  se devuelve a la entrada).  Si encuentra dos o
       más emparejamientos de la misma longitud, se escoge la regla listada en
       primer lugar en el fichero de entrada de flex.

       Una vez que se determina el emparejamiento, el texto correspondiente al
       emparejamiento (denominado el token) está disponible en  el  puntero  a
       caracter  global  yytext,  y  su  longitud en la variable global entera
       yyleng.  Entonces la accin correspondiente  al  patrón  emparejado  se
       ejecuta  (una  descripción  más  detallada  de  las  acciones  viene  a
       continuación), y entonces la entrada  restante  se  analiza  para  otro
       emparejamiento.

       Si  no se encuentra un emparejamiento, entonces se ejecuta la regla por
       defecto: el siguiente caracter en la entrada se considera reconocido  y
       se  copia  a  la salida estándar.  Así, la entrada válida más simple de
       flex es:

           %%

       que genera un escáner que simplemente copia su entrada (un  caracter  a
       la vez) a la salida.

       Fíjese que yytext se puede definir de dos maneras diferentes: bien como
       un puntero a caracter o como  un  array  de  caracteres.   Usted  puede
       controlar  la  definición que usa flex incluyendo una de las directivas
       especiales %pointer o %array en la primera sección (definiciones) de su
       entrada de flex.  Por defecto es %pointer, a menos que use la opción de
       compatibilidad -l, en cuyo caso yytext será un array.   La  ventaja  de
       usar  %pointer es un análisis substancialmente más rápido y la ausencia
       de desbordamiento del buffer cuando se emparejen tokens muy grandes  (a
       menos  que  se  agote  la  memoria  dinámica).  La desventaja es que se
       encuentra restringido en cómo sus acciones pueden modificar yytext (vea
       la siguiente sección), y las llamadas a la función unput() destruyen el
       contenido actual de yytext, que puede convertirse  en  un  considerable
       quebradero  de  cabeza  de  portabilidad  al  cambiar  entre diferentes
       versiones de lex.

       La ventaja de %array es que entoces puede modificar yytext todo lo  que
       usted  quiera,  las  llamadas  a  unput()  no destruyen yytext (ver más
       abajo).  Además, los programas de lex  existentes  a  veces  acceden  a
       yytext externamente utilizando declaraciones de la forma:
           extern char yytext[];
       Esta  definición  es  errónea cuando se utiliza %pointer, pero correcta
       para %array.

       %array define a yytext como un array  de  YYLMAX  caracteres,  que  por
       defecto  es  un  valor  bastante grande.  Usted puede cambiar el tamaño
       símplemente definiendo con #define a YYLMAX con un valor  diferente  en
       la  primera sección de su entrada de flex.  Como se mencionó antes, con
       %pointer yytext  crece  dinámicamente  para  acomodar  tokens  grandes.
       Aunque  esto  signifique  que  con  %pointer  su escáner puede acomodar
       tokens  muy  grandes  (tales  como   emparejar   bloques   enteros   de
       comentarios),  tenga  presente que cada vez que el escáner deba cambiar
       el tamaño de yytext también debe reiniciar el análisis del token entero
       desde  el  principio,  así  que  emparejar  tales tokens puede resultar
       lento.  Ahora yytext no crece dinámicamente si una  llamada  a  unput()
       hace  que  se deba devolver demasiado texto; en su lugar, se produce un
       error en tiempo de ejecución.

       También tenga en cuenta que no puede usar %array  en  los  analizadores
       generados como clases de C++ (la opción c++; vea más abajo).

ACCIONES

       Cada  patrón  en  una  regla  tiene  una acción asociada, que puede ser
       cualquier sentencia en C.  El patrón finaliza en el primer caracter  de
       espacio  en  blanco que no sea una secuencia de escape; lo que queda de
       la línea es su acción.  Si la acción está  vacía,  entonces  cuando  el
       patrón  se  empareje  el token de entrada simplemente se descarta.  Por
       ejemplo, aquí está la especificación de un programa que borra todas las
       apariciones de "zap me" en su entrada:

           %%
           "zap me"

       (Este  copiará  el resto de caracteres de la entrada a la salida ya que
       serán emparejados por la regla por defecto.)

       Aquí  hay  un  programa  que  comprime  varios  espacios  en  blanco  y
       tabuladores  a un solo espacio en blanco, y desecha los espacios que se
       encuentren al final de una línea:

           %%
           [ \t]+        putchar( ’ ’ );
           [ \t]+$       /* ignora este token */

       Si la acción contiene un ’{’, entonces la acción abarca  hasta  que  se
       encuentre  el  correspondiente  ’}’, y la acción podría entonces cruzar
       varias líneas.  flex es capaz de reconocer las cadenas y comentarios de
       C  y no se dejará engañar por las llaves que encuentre dentro de estos,
       pero aun así también permite  que  las  acciones  comiencen  con  %{  y
       considerará  que  la acción es todo el texto hasta el siguiente %} (sin
       tener en cuenta las llaves ordinarias dentro de la acción).

       Una acción que consista sólamente de una barra vertical (’|’) significa
       "lo  mismo  que la acción para la siguiente regla."  Vea más abajo para
       una ilustración.

       Las acciones pueden incluir código C arbitrario,  incuyendo  sentencias
       return  para  devolver un valor desde cualquier rutina llamada yylex().
       Cada vez que se llama a yylex() esta continúa procesando  tokens  desde
       donde  lo  dejó  la  última  vez  hasta  que o bien llegue al final del
       fichero o ejecute un return.

       Las  acciones  tienen  libertad  para  modificar  yytext  excepto  para
       alargarla (añadiendo caracteres al final--esto sobreescribirá más tarde
       caracteres en el flujo de entrada).  Sin  embargo  esto  no  se  aplica
       cuando  se  utiliza  %array  (ver  arriba);  en ese caso, yytext podría
       modificarse libremente de cualquier manera.

       Las acciones tienen libertad para modificar yyleng excepto que estas no
       deberían  hacerlo  si la acción también incluye el uso de yymore() (ver
       más abajo).

       Hay un número de directivas especiales que pueden incluirse  dentro  de
       una acción:

       -      ECHO copia yytext a la salida del escáner.

       -      BEGIN  seguido  del  nombre  de la condición de arranque pone al
              escáner en la condición de  arranque  correspondiente  (ver  más
              abajo).

       -      REJECT  ordena  al  escáner a que proceda con la "segunda mejor"
              regla que concuerde con la entrada (o un prefijo de la entrada).
              La  regla  se escoge como se describió anteriormente en "Cómo se
              Empareja la Entrada", y yytext e  yyleng  se  ajustan  de  forma
              apropiada.   Podría  ser  una  que  empareje tanto texto como la
              regla escogida originalmente pero que  viene  más  tarde  en  el
              fichero de entrada de flex, o una que empareje menos texto.  Por
              ejemplo, lo que viene a continuación contará las palabras en  la
              entrada y llamará a la rutina especial() siempre que vea "frob":

                          int contador_palabras = 0;
                  %%

                  frob        especial(); REJECT;
                  [^ \t\n]+   ++contador_palabras;

              Sin el REJECT, cualquier número de "frob"’s  en  la  entrada  no
              serían  contados  como  palabras,  ya que el escáner normalmente
              ejecuta solo una  acción  por  token.   Se  permite  el  uso  de
              múltiples   REJECTs,  cada  uno  buscando  la  siguiente  mejor
              elección a la regla que actualmente esté activa.   Por  ejemplo,
              cuando  el  siguiente  escáner  analice  el  token  "abcd", este
              escribirá "abcdabcaba" a la salida:

                  %%
                  a        |
                  ab       |
                  abc      |
                  abcd     ECHO; REJECT;
                  .|\n     /* se come caracteres sin emparejar */

              (Las primeras tres reglas comparten la acción de  la  cuarta  ya
              que estas usan la acción especial ’|’.)  REJECT es una propiedad
              particularmente cara en términos de rendimiento del escáner;  si
              se   usa   en  cualquiera  de  las  acciones  del  escáner  esta
              ralentizará todo  el  proceso  de  emparejamiento  del  escáner.
              Además,  REJECT  no puede usarse con las opciones -Cf o -CF (ver
              más abajo).

              Fíjese  también  que  a  diferencia  de   las   otras   acciones
              especiales,  REJECT  es  una  bifurcacin; el código que la siga
              inmediatamente en la acción no será ejecutado.

       -      yymore() dice al escáner que la próxima  vez  que  empareje  una
              regla,  el  token correspondiente debe ser aadido tras el valor
              actual de yytext en lugar de reemplazarlo.  Por ejemplo, dada la
              entrada  "mega-klugde"  lo  que  viene  a continuación escribirá
              "mega-mega-kludge" a la salida:

                  %%
                  mega-    ECHO; yymore();
                  kludge   ECHO;

              El primer "mega-" se empareja y se repite a la salida.  Entonces
              se  empareja "kludge", pero el "mega-" previo aún está esperando
              al inicio de yytext asi que el ECHO para la regla  del  "kludge"
              realmente escribirá "mega-kludge".

       Dos  notas  respecto  al uso de yymore().  Primero, yymore() depende de
       que el valor de  yyleng  refleje  correctamente  el  tamaño  del  token
       actual,  así  que no debe modificar yyleng si está utilizando yymore().
       Segundo, la presencia de yymore() en la acción del escáner implica  una
       pequeña  penalización  de rendimiento en la velocidad de emparejamiento
       del escáner.

       -      yyless(n) devuelve todos excepto los primeros n  caracteres  del
              token   actual  de  nuevo  al  flujo  de  entrada,  donde  serán
              reanalizados   cuando   el   escáner   busque    el    siguiente
              emparejamiento.   yytext  e  yyleng se ajustan de forma adecuada
              (p.ej., yyleng no será igual a n ).  Por ejemplo, con la entrada
              "foobar" lo que viene a continuación escribirá "foobarbar":

                  %%
                  foobar    ECHO; yyless(3);
                  [a-z]+    ECHO;

              Un  argumento  de  0  para  yyless hará que la cadena de entrada
              actual sea analizada por completo de nuevo.  A  menos  que  haya
              cambiado  la  manera  en  la  que el escáner procese de ahora en
              adelante  su  entrada  (utilizando  BEGIN,  por  ejemplo),  esto
              producirá un bucle sin fin.

       Fíjese  que  yyless  es una macro y puede ser utilizada solamente en el
       fichero de entrada de flex, no desde otros ficheros fuente.

       -      unput(c) pone el caracter c de nuevo en  el  flujo  de  entrada.
              Este  será  el  próximo caracter analizado.  La siguiente acción
              tomará el token actual y hará que  se  vuelva  a  analizar  pero
              encerrado entre paréntesis.

                  {
                  int i;
                  /* Copia yytext porque unput() desecha yytext */
                  char *yycopia = strdup( yytext );
                  unput( ’)’ );
                  for ( i = yyleng - 1; i >= 0; --i )
                      unput( yycopia[i] );
                  unput( ’(’ );
                  free( yycopia );
                  }

              Fíjese que ya que cada unput() pone el caracter dado de nuevo al
              principio  del  flujo  de  entrada,  al  devolver   cadenas   de
              caracteres se debe hacer de atrás hacia delante.

       Un  problema  potencial  importante cuando se utiliza unput() es que si
       está usando %pointer (por defecto), una llamada a unput()  destruye  el
       contenido  de  yytext,  comenzando  con  su caracter más a la derecha y
       devorando un caracter a la izquierda con cada llamada.  Si necesita que
       se  preserve  el valor de yytext después de una llamada a unput() (como
       en el  ejemplo  anterior),  usted  debe  o  bien  copiarlo  primero  en
       cualquier lugar, o construir su escáner usando %array
        (ver Cómo se Empareja la Entrada).

       Finalmente,  note  que  no  puede  devolver EOF para intentar marcar el
       flujo de entrada con un fin-de-fichero.

       -      input() lee el próximo  caracter  del  flujo  de  entrada.   Por
              ejemplo,  lo  que  viene a continuación 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;    /* encontró el final */
                                      }

                                  if ( c == EOF )
                                      {
                                      error( "EOF en comentario" );
                                      break;
                                      }
                                  }
                              }

              (Fíjese que si el escáner se  compila  usando  C++,  entonces  a
              input()  se  le  hace  referencia con yyinput(), para evitar una
              colisión de nombre con el flujo de C++ por el nombre input.)

       -      YY_FLUSH_BUFFER vacía el buffer interno del  escáner  de  manera
              que  la  próxima  vez que el escáner intente emparejar un token,
              este primero rellenará el buffer usando YY_INPUT (ver El Escáner
              Generado,  más  abajo).   Esta  acción es un caso especial de la
              función más general yy_flush_buffer(), descrita más abajo en  la
              sección Múltiples Buffers de Entrada.

       -      yyterminate()  se  puede  utilizar  en lugar de una sentencia de
              retorno en una acción.  Esta hace  que  finalice  el  escáner  y
              retorne  un  0  a  quien  haya llamado al escáner, indicando que
              "todo  está  hecho".   Por   defecto,   también   se   llama   a
              yyterminate()  cuando  se  encuentra un fin-de-fichero.  Esta es
              una macro y podría ser redefinida.

El Escáner Generado

       La salida de flex es el fichero lex.yy.c, que  contiene  la  rutina  de
       análisis  yylex(),  un  número de tablas usadas por esta para emparejar
       tokens, y un número de  rutinas  auxiliares  y  macros.   Por  defecto,
       yylex() se declara así

           int yylex()
               {
               ... aquí van varias definiciones y las acciones ...
               }

       (Si  su entorno acepta prototipos de funciones, entonces este será "int
       yylex( void )").  Esta  definición  podría  modificarse  definiendo  la
       macro "YY_DECL".  Por ejemplo, podría utilizar:

           #define YY_DECL float lexscan( a, b ) float a, b;

       para  darle  a la rutina de análisis el nombre lexscan, que devuelve un
       real, y toma dos reales como argumentos.  Fíjese que si pone argumentos
       a  la  rutina  de  análisis  usando  una  declaración  de  función  no-
       prototipada/tipo-K&R, debe hacer terminar la definición con un punto  y
       coma (;).

       Siempre que se llame a yylex(), este analiza tokens desde el fichero de
       entrada global yyin (que por defecto es igual  a  stdin).   La  función
       continúa  hasta  que  alcance  el  final  del  fichero (punto en el que
       devuelve el valor 0) o  una  de  sus  acciones  ejecute  una  sentencia
       return.

       Si  el escáner alcanza un fin-de-fichero, entonces el comportamiento en
       las llamadas posteriores está indefinido a menos que o bien yyin apunte
       a  un  nuevo  fichero  de  entrada (en cuyo caso el análisis continúa a
       partir de ese fichero), o se llame a yyrestart().  yyrestart() toma  un
       argumento,  un  puntero  FILE  * (que puede ser nulo, si ha preparado a
       YY_INPUT para que analice una fuente distinta  a  yyin),  e  inicializa
       yyin  para  que  escanee  ese fichero.  Esencialmente no hay diferencia
       entre la asignación a yyin de un nuevo fichero de entrada o el  uso  de
       yyrestart()   para   hacerlo;   esto   último   está   disponible   por
       compatibilidad  con  versiones  anteriores  de  flex,  y  porque  puede
       utilizarse  para  conmutar  ficheros  de entrada en medio del análisis.
       También se puede utilizar para desechar el buffer  de  entrada  actual,
       invocándola  con  un  argumento  igual  a  yyin;  pero  mejor  es  usar
       YY_FLUSH_BUFFER  (ver  más  arriba).    Fíjese   que   yyrestart()   no
       reinicializa  la  condición  de  arranque a INITIAL (ver Condiciones de
       Arranque, más abajo).

       Si yylex() para el análisis debido a  la  ejecución  de  una  sentencia
       return  en  una  de  las  acciones, el analizador podría ser llamado de
       nuevo y este reanudaría el análisis donde lo dejó.

       Por defecto (y por razones de eficiencia), el analizador  usa  lecturas
       por  bloques en lugar de simples llamadas a getc() para leer caracteres
       desde yyin.  La manera en la que toma su  entrada  se  puede  controlar
       definienfo la macro YY_INPUT.  La secuencia de llamada para YY_INPUT es
       "YY_INPUT(buf,result,max_size)".  Su acción  es  poner  hasta  max_size
       caracteres  en  el  array  de  caracteres buf y devolver en la variable
       entera result bien o el número de  caracteres  leídos  o  la  constante
       YY_NULL  (0  en  sistemas Unix) para indicar EOF.  Por defecto YY_INPUT
       lee desde la variable global puntero a fichero "yyin".

       Una definición de ejemplo para YY_INPUT (en la sección de  definiciones
       del fichero de entrada) es:

           %{
           #define YY_INPUT(buf,result,max_size) \
               { \
               int c = getchar(); \
               result = (c == EOF) ? YY_NULL : (buf[0] = c, 1); \
               }
           %}

       Esta definición cambiará el procesamiento de la entrada para que suceda
       un caracter a la vez.

       Cuando el analizador reciba  una  indicación  de  fin-de-fichero  desde
       YY_INPUT,  entonces  esta  comprueba  la función yywrap().  Si yywrap()
       devuelve falso (cero), entonces se asume que la función ha ido más allá
       y  ha  preparado  yyin  para que apunte a otro fichero de entrada, y el
       análisis continúa.  Si este retorna verdadero  (no-cero),  entonces  el
       analizador  termina,  devolviendo  un  0 a su invocador.  Fíjese que en
       cualquier caso, la condición de arranque permanece sin cambios; esta no
       vuelve a ser INITIAL.

       Si  no  proporciona su propia versión de yywrap(), entonces debe bien o
       usar %option noyywrap (en cuyo caso el analizador se comporta  como  si
       yywrap()  devolviera  un  1),  o  debe enlazar con -lfl para obtener la
       versión por defecto de la rutina, que siempre devuelve un 1.

       Hay disponibles tres rutinas para analizar desde buffers de memoria  en
       lugar   de   desde   ficheros:   yy_scan_string(),  yy_scan_bytes(),  e
       yy_scan_buffer().  Las trataremos en la sección  Múltiples  Buffers  de
       Entrada.

       El  analizador  escribe  su  salida con ECHO a la variable global yyout
       (por defecto, stdout), que  el  usuario  podría  redefinir  asignándole
       cualquier otro puntero a FILE.

CONDICIONES DE ARRANQUE

       flex  dispone  de  un  mecanismo  para activar reglas condicionalmente.
       Cualquier regla cuyo patrón se prefije  con  "<sc>"  únicamente  estará
       activa  cuando  el  analizador se encuentre en la condición de arranque
       llamada "sc".  Por ejemplo,

           <STRING>[^"]*        { /* se come el cuerpo de la cadena ... */
                       ...
                       }

       estará activa solamente cuando el analizador esté en  la  condición  de
       arranque "STRING", y

           <INITIAL,STRING,QUOTE>\. { /* trata una secuencia de escape ... */
                       ...
                       }

       estará  activa  solamente  cuando la condición de arranque actual sea o
       bien "INITIAL", "STRING", o "QUOTE".

       Las condiciones de arranque se declaran  en  la  (primera)  sección  de
       definiciones  de la entrada usando líneas sin sangrar comenzando con %s
       o %x seguida por una lista de nombres.  Lo primero declara  condiciones
       de  arranque  inclusivas, lo último condiciones de arranque exclusivas.
       Una condición de arranque se activa utilizando la acción BEGIN.   Hasta
       que  se ejecute la próxima acción BEGIN, las reglas con la condición de
       arranque dada estarán activas y las reglas  con  otras  condiciones  de
       arranque  estarán inactivas.  Si la condición de arranque es inclusiva,
       entonces  las  reglas  sin  condiciones  de  arranque  también  estarán
       activas. Si es exclusiva, entonces slamente las reglas calificadas con
       la condición  de  arranque  estarán  activas.  Un  conjunto  de  reglas
       dependientes  de  la  misma condición de arranque exclusiva describe un
       analizador que es independiente de cualquiera de las otras reglas en la
       entrada de flex.  Debido a esto, las condiciones de arranque exclusivas
       hacen  fácil  la  especificación  de  "mini-escáneres"   que   analizan
       porciones  de  la  entrada  que son sintácticamente diferentes al resto
       (p.ej., comentarios).

       Si la distinción entre condiciones de arranque inclusivas o  exclusivas
       es aún un poco vaga, aquí hay un ejemplo simple que ilustra la conexión
       entre las dos.  El conjunto de reglas:

           %s ejemplo
           %%

           <ejemplo>foo   hacer_algo();

           bar            algo_mas();

       es equivalente a

           %x ejemplo
           %%

           <ejemplo>foo   hacer_algo();

           <INITIAL,ejemplo>bar    algo_mas();

       Sin el calificador <INITIAL,example>,  el  patrón  bar  en  el  segundo
       ejemplo  no  estará  activo  (es decir, no puede emparejarse) cuando se
       encuentre  en  la  condición  de  arranque  example.   Si  hemos  usado
       <example>  para  calificar bar, aunque, entonces este únicamente estará
       activo en example y no en INITIAL, mientras que en  el  primer  ejemplo
       está  activo  en  ambas,  porque  en  el primer ejemplo la condición de
       arranque example es una condición de arranque inclusiva (%s).

       Fíjese también  que  el  especificador  especial  de  la  condición  de
       arranque  <*>  empareja  todas  las  condiciones  de arranque.  Así, el
       ejemplo anterior también pudo haberse escrito;

           %x ejemplo
           %%

           <ejemplo>foo   hacer_algo();

           <*>bar    algo_mas();

       La regla  por  defecto  (hacer  un  ECHO  con  cualquier  caracter  sin
       emparejar)  permanece  activa  en las condiciones de arranque.  Esta es
       equivalente a:

           <*>.|\n     ECHO;

       BEGIN(0)  retorna  al  estado  original  donde  solo  las  reglas   sin
       condiciones  de  arranque  están  activas.   Este  estado también puede
       referirse a la condición de arranque "INITIAL", así que  BEGIN(INITIAL)
       es  equivalente  a BEGIN(0).  (No se requieren los paréntesis alrededor
       del nombre de la condición  de  arranque  pero  se  considera  de  buen
       estilo.)

       Las  acciones  BEGIN  pueden  darse  también  como  código  sangrado al
       comienzo de la  sección  de  reglas.   Por  ejemplo,  lo  que  viene  a
       continuación  hará  que el analizador entre en la condición de arranque
       "ESPECIAL" siempre  que  se  llame  a  yylex()  y  la  variable  global
       entra_en_especial sea verdadera:

                   int entra_en_especial;

           %x ESPECIAL
           %%
                   if ( entra_en_especial )
                       BEGIN(ESPECIAL);

           <ESPECIAL>blablabla
           ...más reglas a continuación...

       Para  ilustrar  los  usos  de  las condiciones de arranque, aquí hay un
       analizador que ofrece dos interpretaciones diferentes para  una  cadena
       como  "123.456".   Por  defecto  este  la  tratará como tres tokens, el
       entero "123", un punto (’.’), y el entero "456".   Pero  si  la  cadena
       viene  precedida  en  la  línea  por  la cadena "espera-reales" este la
       tratará como un único token, el número en coma flotante 123.456:

           %{
           #include <math.h>
           %}
           %s espera

           %%
           espera-reales        BEGIN(espera);

           <espera>[0-9]+"."[0-9]+      {
                       printf( "encontró un real, = %f\n",
                               atof( yytext ) );
                       }
           <espera>\n           {
                       /* este es el final de la línea,
                        * así que necesitamos otro
                        * "espera-numero" antes de
                        * que volvamos a reconocer más
                        * números
                        */
                       BEGIN(INITIAL);
                       }

           [0-9]+      {
                       printf( "encontró un entero, = %d\n",
                               atoi( yytext ) );
                       }

           "."         printf( "encontró un punto\n" );

       Aquí está un analizador que reconoce  (y  descarta)  comentarios  de  C
       mientras mantiene una cuenta de la línea actual de entrada.

           %x comentario
           %%
                   int num_linea = 1;

           "/*"         BEGIN(comentario);

           <comentario>[^*\n]*       /* come todo lo que no sea ’*’ */
           <comentario>"*"+[^*/\n]*  /* come ’*’s no seguidos por ’/’ */
           <comentario>\n            ++num_linea;
           <comentario>"*"+"/"       BEGIN(INITIAL);

       Este  analizador se complica un poco para emparejar tanto texto como le
       sea posible en cada regla.  En general, cuando se intenta  escribir  un
       analizador  de  alta  velocidad haga que cada regla empareje lo más que
       pueda, ya que esto es un buen logro.

       Fíjese que los nombres de las condiciones  de  arranque  son  realmente
       valores  enteros y pueden ser almacenados como tales.  Así, lo anterior
       podría extenderse de la siguiente manera:

           %x comentario foo
           %%
                   int num_linea = 1;
                   int invocador_comentario;

           "/*"         {
                        invocador_comentario = INITIAL;
                        BEGIN(comentario);
                        }

           ...

           <foo>"/*"    {
                        invocador_comentario = foo;
                        BEGIN(comentario);
                        }

           <comentario>[^*\n]*        /* se come cualquier cosa que no sea un ’*’ */
           <comentario>"*"+[^*/\n]*   /* se come ’*’s que no continuen con ’/’s */
           <comentario>\n             ++num_linea;
           <comentario>"*"+"/"        BEGIN(invocador_comentario);

       Además, puede acceder a la condición de arranque actual usando la macro
       de  valor  entero YY_START.  Por ejemplo, las asignaciones anteriores a
       invocador_comentario podrían escribirse en su lugar como

           invocador_comentario = YY_START;

       Flex ofrece YYSTATE como un alias para YY_START (ya que es lo  que  usa
       lex de AT&T).

       Fíjese  que  las condiciones de arranque no tienen su propio espacio de
       nombres; los %s’s y %x’s declaran nombres de la misma  manera  que  con
       #define’s.

       Finalmente,  aquí  hay  un  ejemplo  de  cómo  emparejar  cadenas entre
       comillas al estilo de C  usando  condiciones  de  arranque  exclusivas,
       incluyendo  secuencias  de  escape  expandidas  (pero  sin  incluir  la
       comprobación de cadenas que son demasiado largas):

           %x str

           %%
                   char string_buf[MAX_STR_CONST];
                   char *string_buf_ptr;

           \"      string_buf_ptr = string_buf; BEGIN(str);

           <str>\"        { /* se vio la comilla que cierra - todo está hecho */
                   BEGIN(INITIAL);
                   *string_buf_ptr = ’\0’;
                   /* devuelve un tipo de token de cadena constante y
                    * el valor para el analizador sintáctico
                    */
                   }

           <str>\n        {
                   /* error - cadena constante sin finalizar */
                   /* genera un mensaje de error */
                   }

           <str>\\[0-7]{1,3} {
                   /* secuencia de escape en octal */
                   int resultado;

                   (void) sscanf( yytext + 1, "%o", &resultado );

                   if ( resultado > 0xff )
                           /* error, constante fuera de rango */

                   *string_buf_ptr++ = resultado;
                   }

           <str>\\[0-9]+ {
                   /* genera un error - secuencia de escape errónea;
                    * algo como ’\48’ o ’\0777777’
                    */
                   }

           <str>\\n  *string_buf_ptr++ = ’\n’;
           <str>\\t  *string_buf_ptr++ = ’\t’;
           <str>\\r  *string_buf_ptr++ = ’\r’;
           <str>\\b  *string_buf_ptr++ = ’\b’;
           <str>\\f  *string_buf_ptr++ = ’\f’;

           <str>\\(.|\n)  *string_buf_ptr++ = yytext[1];

           <str>[^\\\n\"]+        {
                   char *yptr = yytext;

                   while ( *yptr )
                           *string_buf_ptr++ = *yptr++;
                   }

       A menudo,  como  en  alguno  de  los  ejemplos  anteriores,  uno  acaba
       escribiendo  un  buen  número  de  reglas  todas  precedidas  por la(s)
       misma(s) condición(es) de arranque.  Flex hace esto un poco más fácil y
       claro  introduciendo  la  noción de mbito de la condición de arranque.
       Un ámbito de condición de arranque comienza con:

           <SCs>{

       Donde SCs es una lista de una o más condiciones  de  arranque.   Dentro
       del  ámbito  de  la  condición  de arranque, cada regla automáticamente
       tiene el prefijo <SCs> aplicado a esta, hasta un  }  que  corresponda
       con el { inicial.  Así, por ejemplo,

           <ESC>{
               "\\n"   return ’\n’;
               "\\r"   return ’\r’;
               "\\f"   return ’\f’;
               "\\0"   return ’\0’;
           }

       es equivalente a:

           <ESC>"\\n"  return ’\n’;
           <ESC>"\\r"  return ’\r’;
           <ESC>"\\f"  return ’\f’;
           <ESC>"\\0"  return ’\0’;

       Los ámbitos de las condiciones de arranque pueden anidarse.

       Están  disponibles  tres rutinas para manipular pilas de condiciones de
       arranque:

       void yy_push_state(int new_state)
              empuja la condición 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 también 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 dinámicamente y  por  ello
       no  tiene  asociada  ninguna  limitación  de  tamaño.  Si la memoria se
       agota, se aborta la ejecución del programa.

       Para usar pilas de condiciones de arranque, su analizador debe  incluir
       una directiva %option stack (ver Opciones más abajo).

MÚLTIPLES 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  dónde  será  leída  la  siguiente  entrada   escribiendo
       símplemente  un  YY_INPUT que sea sensible al contexto del análisis.  A
       YY_INPUT sólo se le llama cuando el analizador alcanza el final  de  su
       buffer,  que  podría ser bastante tiempo después de haber analizado una
       sentencia como un "include" que requiere el  cambio  de  la  fuente  de
       entrada.

       Para  solventar  este  tipo de problemas, flex provee un mecanismo para
       crear y conmutar entre varios buffers de entrada.  Un buffer de entrada
       se crea usando:

           YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )

       que toma un puntero a FILE y un tamaño "size" y crea un buffer asociado
       con el fichero dado y lo  suficientemente  grande  para  mantener  size
       caracteres  (cuando  dude,  use  YY_BUF_SIZE  para  el  tamaño).   Este
       devuelve un handle YY_BUFFER_STATE, que podría pasarse a otras  rutinas
       (ver  más  abajo).   El  tipo  de  YY_BUFFER_STATE  es un puntero a una
       estructura  opaca  struct  yy_buffer_state,  de   manera   que   podría
       inicializar    de    forma    segura    variables   YY_BUFFER_STATE   a
       ((YY_BUFFER_STATE) 0) si lo desea, y  también  hacer  referencia  a  la
       estructura  opaca  para  declarar  correctamente  buffers de entrada en
       otros ficheros fuente además de los de su analizador.   Fíjese  que  el
       puntero  a  FILE en la llamada a yy_create_buffer se usa solamente como
       el valor de yyin visto por YY_INPUT;  si  usted  redefine  YY_INPUT  de
       manera  que  no use más a yyin, entonces puede pasar de forma segura un
       puntero FILE nulo a  yy_create_buffer.   Se  selecciona  un  buffer  en
       particular a analizar utilizando:

           void yy_switch_to_buffer( YY_BUFFER_STATE nuevo_buffer )

       conmuta  el  buffer  de entrada del analizador de manera que los tokens
       posteriores     provienen     de     nuevo_buffer.      Fíjese      que
       yy_switch_to_buffer()  podría  usarlo  yywrap() para arreglar las cosas
       para un análisis continuo, en lugar de abrir un  nuevo  fichero  y  que
       yyin  apunte a este.  Fíjese también que cambiar las fuentes de entrada
       ya sea por medio de yy_switch_to_buffer() o de yywrap()  no  cambia  la
       condición de arranque.

           void yy_delete_buffer( YY_BUFFER_STATE buffer )

       se  usa  para  recuperar  el  almacenamiento asociado a un buffer.  (El
       buffer puede ser nulo, en cuyo caso la rutina  no  hace  nada.)   Puede
       también limpiar el contenido actual de un buffer usando:

           void yy_flush_buffer( YY_BUFFER_STATE buffer )

       Esta función descarta el contenido del buffer, de manera que la próxima
       vez que el analizador intente emparejar un token desde el buffer,  este
       primero rellenará el buffer utilizando YY_INPUT.

       yy_new_buffer()  es  un  alias de yy_create_buffer(), que se ofrece por
       compatibilidad con el uso en C++ de new y delete para crear y  destruir
       objetos dinámicos.

       Finalmente,    la    macro    YY_CURRENT_BUFFER   retorna   un   handle
       YY_BUFFER_STATE al buffer actual.

       Aquí hay un ejemplo del uso  de  estas  propiedades  para  escribir  un
       analizador  que  expande  ficheros  incluidos  (la propiedad <<EOF>> se
       comenta más 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
       análisis de cadenas en memoria en lugar de archivos.  Todas estas crean
       un nuevo buffer de entrada para analizar  la  cadena,  y  devuelven  el
       correspondiente  handle  YY_BUFFER_STATE  (que usted debería borrar con
       yy_delete_buffer() cuando termine con él).  Estas también  conmutan  el
       nuevo  buffer  usando  yy_switch_to_buffer(),  de manera que la próxima
       llamada a yylex() comenzará analizando la cadena.

       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.

       Fíjese  que  ambas  de estas funciones crean y analizan una copia de la
       cadena o bytes.  (Esto podría ser deseable, ya que yylex() modifica  el
       contenido  del buffer que está analizado.)  Usted puede evitar la copia
       utilizando:

       yy_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  últimos  bytes  deben  ser
              YY_END_OF_BUFFER_CHAR (ASCII NUL).  Estos dos últimos  bytes  no
              se   analizan;   así,   el  análisis  consta  de  base[0]  hasta
              base[size-2], inclusive.

              Si se equivoca al  disponer  base  de  esta  manera  (es  decir,
              olvidar  los  dos YY_END_OF_BUFFER_CHAR bytes finales), entonces
              yy_scan_buffer() devuelve un puntero nulo en lugar de  crear  un
              nuevo buffer de entrada.

              El  tipo  yy_size_t es un tipo entero con el que puede hacer una
              conversión a una expresión entera para reflejar  el  tamaño  del
              buffer.

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 acción debe  finalizar  haciendo  una  de  estas  cuatro
       cosas:

       -      asignando  a  yyin  un  nuevo  fichero  de entrada (en versiones
              anteriores de flex, después de hacer la asignación debía  llamar
              a la acción especial YY_NEW_FILE; esto ya no es necesario);

       -      ejecutando una sentencia return;

       -      ejecutando la acción especial yyterminate();

       -      o,  conmutando  a  un  nuevo buffer usando yy_switch_to_buffer()
              como se mostró en el ejemplo anterior.

       Las reglas  <<EOF>>  no  deberían  usarse  con  otros  patrones;  estas
       deberían  calificarse  con una lista de condiciones de arranque.  Si se
       da una regla  <<EOF>>  sin  calificar,  esta  se  aplica  a  todas  las
       condiciones  de  arranque  que  no  tengan  ya  acciones <<EOF>>.  Para
       especificar una regla <<EOF>> solamente para la condición  de  arranque
       inicial, use

           <INITIAL><<EOF>>

       Estas  reglas  son útiles para atrapar cosas tales como comentarios sin
       final.  Un ejemplo:

           %x comilla
           %%

           ...otras reglas que tengan que ver con comillas...

           <comilla><<EOF>>   {
                    error( "comilla sin cerrar" );
                    yyterminate();
                    }
           <<EOF>>  {
                    if ( *++filelist )
                        yyin = fopen( *filelist, "r" );
                    else
                       yyterminate();
                    }

MACROS MISCELÁNEAS

       La macro YY_USER_ACTION puede definirse para  indicar  una  acción  que
       siempre  se  ejecuta  antes  de  la acción de la regla emparejada.  Por
       ejemplo, podría declararse con #define para que llame a una rutina  que
       convierta  yytext  a minúsculas.  Cuando se invoca a YY_USER_ACTION, la
       variable yy_act da el número de la regla emparejada (las  reglas  están
       numeradas comenzando en 1).  Suponga que quiere medir la frecuencia con
       la que sus reglas son emparejadas.  Lo que viene a continuación  podría
       hacer este truco:

           #define YY_USER_ACTION ++ctr[yy_act]

       donde  ctr  en  un  vector  que  mantiene la cuenta para las diferentes
       reglas.  Fíjese que la macro YY_NUM_RULES da el número total de  reglas
       (incluyendo la regla por defecto, incluso si usted usa -s), así que una
       declaración correcta para ctr es:

           int ctr[YY_NUM_RULES];

       La macro YY_USER_INIT podría definirse  para  indicar  una  acción  que
       siempre se ejecuta antes del primer análisis (y antes de que se haga la
       inicialización interna  del  analizador).   Por  ejemplo,  este  podría
       usarse  para  llamar a una rutina que lea una tabla de datos o abrir un
       fichero de registro.

       La  macro  yy_set_interactive(is_interactive)  se   puede   usar   para
       controlar  si  el  buffer  actual  se considera interactivo.  Un buffer
       interactivo se procesa más  lentamente,  pero  debe  usarse  cuando  la
       fuente  de  entrada del analizador es realmente interactiva para evitar
       problemas debidos a la espera para el llenado de los  buffers  (ver  el
       comentario  de  la bandera -I más abajo).  Un valor distinto de cero en
       la invocación de la macro marcará el buffer como interactivo, un  valor
       de  cero como no-interactivo.  Fíjese que el uso de esta macro no tiene
       en cuenta %option always-interactive o %option  never-interactive  (ver
       Opciones  más  abajo).   yy_set_interactive()  debe invocarse antes del
       comienzo del análisis del buffer que es considerado (o no) interactivo.

       La  macro yy_set_bol(at_bol) puede usarse para controlar si el contexto
       del buffer de análisis actual para el próximo emparejamiento  de  token
       se  hace como si se encontrara al principio de una línea.  Un argumento
       de la macro distinto de cero hace activas a las reglas sujetas  a  ’^’,
       mientras que un argumento igual a cero hacer inactivas a las reglas con
       ’^’.

       La macro YY_AT_BOL() devuelve verdadero si el próximo token analizado a
       partir  del buffer actual tendrá activas las reglas ’^’, de otra manera
       falso.

       En el analizador generado, las acciones están  recogidas  en  una  gran
       sentencia switch y separadas usando YY_BREAK, que puede ser redefinida.
       Por defecto, este es símplemente un "break", para separar la acción  de
       cada regla de las reglas que le siguen.  Redefiniendo YY_BREAK permite,
       por ejemplo, a los usuarios de C++ que #define YY_BREAK  no  haga  nada
       (¡mientras tengan cuidado para que cada regla finalice con un "break" o
       un  "return"!)  para  evitar  que  sufran  los  avisos  de   sentencias
       inalcanzables cuando debido a que la acción de la regla finaliza con un
       "return", el YY_BREAK es inaccesible.

VALORES DISPONIBLES AL USUARIO

       Esta sección 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  añadir  caracteres  al
              final).

              Si aparece la directiva especial %array en la primera sección de
              la descripción 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  sección  si  no  le
              gusta el valor por defecto (generalmente 8KB).  El uso de %array
              produce analizadores algo más lentos, pero el valor de yytext se
              vuelve   inmune   a  las  llamadas  a  input()  y  unput(),  que
              potencialmente destruyen su valor cuando yytext es un puntero  a
              caracter.   El  opuesto  de %array es %pointer, que se encuentra
              por defecto.

              Usted no puede utilizar %array cuando genera  analizadores  como
              clases de C++ (la bandera -+ ).

       -      int yyleng contiene la longitud del token actual.

       -      FILE  *yyin es el fichero por el que flex lee por defecto.  Este
              podría redefinirse pero hacerlo solo tiene sentido antes de  que
              el análisis comience o después de que se haya encontrado un EOF.
              Cambiándolo en medio del análisis tendrá resultados  inesperados
              ya que flex utiliza buffers en su entrada; use yyrestart() en su
              lugar.  Una vez que el análisis termina debido a que se ha visto
              un  fin-de-fichero,  puede  asignarle a yyin el nuevo fichero de
              entrada y entonces llamar al analizador de nuevo para  continuar
              analizando.

       -      void  yyrestart(  FILE  *new_file  ) podría 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).  Fíjese que llamando a  yyrestart()  con
              yyin como argumento de esta manera elimina el buffer de entradda
              actual y continúa analizando el mismo fichero de entrada.

       -      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 condición
              de  arranque  actual.   Posteriormente puede usar este valor con
              BEGIN para retornar a la condición de arranque.

INTERFAZ CON YACC

       Uno de los usos principales de flex es como compañero del generador  de
       analizadores  sintácticos  yacc.   Los  analizadores  de  yacc  esperan
       invocar a una rutina llamada yylex() para encontrar el próximo token de
       entrada.   La  rutina  se supone que devuelve el tipo del próximo token
       además de poner cualquier valor asociado en la variable global  yylval.
       Para  usar  flex  con  yacc,  uno  especifica la opción -d de yacc para
       intruirle a que genere el fichero y.tab.h que contiene las definiciones
       de todos los %tokens que aparecen en la entrada de yacc.  Entonces este
       archivo se incluye en el analizador de flex Por ejemplo, si uno de  los
       tokens es "TOK_NUMERO", parte del analizador podría parecerse a:

           %{
           #include "y.tab.h"
           %}

           %%

           [0-9]+        yylval = atoi( yytext ); return TOK_NUMERO;

OPCIONES

       flex tiene las siguientes opciones:

       -b     Genera  información  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.  Añadiendo reglas uno
              puede eliminar estados de retroceso.  Si todos  los  estados  de
              retroceso se eliminan y se usa -Cf o -CF, el analizador generado
              funcionará más rápido  (ver  la  bandera  -p).   Únicamente  los
              usuarios  que  desean  exprimir  hasta  el  último  ciclo de sus
              analizadores necesitan preocuparse  de  esta  opción.   (Ver  la
              sección sobre Consideraciones de Rendimiento más abajo.)

       -c     es una opción que no hace nada, incluída para cumplir con POSIX.

       -d     hace  que  el  analizador  generado  se  ejecute  en   modo   de
              depuracin.   Siempre  que  se  reconoce un patrón y la variable
              global yy_flex_debug no es cero (que por defecto no lo  es),  el
              analizador escribirá en stderr una línea de la forma:

                  --accepting rule at line 53 ("el texto emparejado")

              El  número  de  línea hace referencia al lugar de la regla en el
              fichero que define al analizador (es decir, el fichero que se le
              introdujo  a  flex).   Los mensajes también se generan cuando el
              analizador retrocede, acepta la regla por  defecto,  alcanza  el
              final  de  su  buffer  de  entrada  (o encuentra un NUL; en este
              punto, los dos parecen lo  mismo  en  lo  que  le  concierne  al
              analizador), o alcance el fin-de-fichero.

       -f     especifica  un  analizador rpido.  No se realiza una compresión
              de tablas y se evita el uso de stdio.  El  resultado  es  grande
              pero rápido.  Esta opción es equivalente a -Cfr (ver más abajo).

       -h     genera un sumario de "ayuda" de las opciones de flex por  stdout
              y entonces finaliza.  -?  y --help son sinónimos de -h.

       -i     indica  a  flex  que  genere un analizador case-insensitive.  Se
              ignorará si las letras en los patrones de entrada de flex son en
              mayúsculas  o  en  minúsculas,  y los tokens en la entrada serán
              emparejados sin tenerlo en cuenta.  El texto emparejado dado  en
              yytext tendrá las mayúsculas y minúsculas preservadas (es decir,
              no se convertirán).

       -l     activa el modo de máxima compatibilidad  con  la  implementación
              original  de  lex  de  AT&T.   Fíjese  que esto no significa una
              compatibilidad completa.  El  uso  de  esta  opción  cuesta  una
              cantidad  considerable de rendimiento, y no puede usarse con las
              opciones -+, -f, -F, -Cf, o -CF.  Para los detalles a  cerca  de
              la    compatibilidad    que    se   ofrece,   vea   la   sección
              "Incompatibilidades con Lex y POSIX"  más  abajo.   Esta  opción
              también  hace  que  se defina el nombre YY_FLEX_LEX_COMPAT en el
              analizador generado.

       -n     es otra opción que no  hace  nada,  incluída  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 provocarán pérdidas serias de rendimiento en
              el analizador resultante.  Si indica  esta  bandera  dos  veces,
              también  obtendrá  comentarios que tratan de las propiedades que
              producen pérdidas menores de rendimiento.

              Fíjese que el uso de REJECT, %option  yylineno,  y  el  contexto
              posterior  variable  (vea  la sección Deficiencias / Errores más
              abajo) supone una penalización 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 opción es útil para
              encontrar agujeros en el conjunto de reglas del analizador.

       -t     indica a flex que escriba el analizador que genera a  la  salida
              estándar en lugar de en lex.yy.c.

       -v     especifica  que  flex  debería  escribir en stderr un sumario de
              estadísticas respecto al analizador que genera.  La  mayoría  de
              las estadísticas no tienen significado para el usuario casual de
              flex, pero la primera línea identifica la versión  de  flex  (la
              misma  que  se  informa con -V), y la próxima línea las banderas
              utilizadas cuando se genera el analizador,  incluyendo  aquellas
              que se encuentran activadas por defecto.

       -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  más  abajo).   En
              general, use -B cuando esté seguro de que su analizador nunca se
              usará de forma interactiva, y quiere con esto exprimir  un  poco
              más  el  rendimiento.   Si  por  el  contrario  su  objetivo  es
              exprimirlo mucho más, debería estar utilizando la opción  -Cf  o
              -CF  (comentadas  más  abajo),  que activa -B automáticamente de
              todas maneras.

       -F     especifica que se debe utilizar la representación  de  la  tabla
              rpida  (y elimina referencias a stdio).  Esta representación es
              aproximadamente tan rápida como la representación completa de la
              tabla   (-f),   y   para  algunos  conjuntos  de  patrones  será
              considerablemente  más  pequeña  (y  para  otros,  mayor).    En
              general,  si el conjunto de patrones contiene "palabras clave" y
              una regla "identificador" atrápalo-todo, como la del conjunto:

                  "case"    return TOK_CASE;
                  "switch"  return TOK_SWITCH;
                  ...
                  "default" return TOK_DEFAULT;
                  [a-z]+    return TOK_ID;

              entonces será mejor que utilice la representación  de  la  tabla
              completa.   Si  sólo  está  presente  la regla "identificador" y
              utiliza una tabla hash o algo parecido  para  detectar  palabras
              clave, mejor utilice -F.

              Esta  opción es equivalente a -CFr (ver más abajo).  Esta opción
              no puede utilizarse con -+.

       -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 únicamente si  debe  hacerlo.   Resulta
              que  mirando siempre un caracter extra hacia delante, incluso si
              el analizador ya ha visto  suficiente  texto  para  eliminar  la
              ambigüedad  del  token  actual,  se  es  un  poco más rápido que
              mirando solamente cuando es necesario.   Pero  los  analizadores
              que  siempre  miran  hacia  delante  producen  un comportamiento
              interactivo malísimo; por ejemplo, cuando un usuario teclea  una
              línea  nueva,  esta  no se reconoce como un token de línea nueva
              hasta  que  introduzca  otro  token,  que  a  menudo   significa
              introducir otra línea completa.

              Los  analizadores  de  flex por defecto son interactivos a menos
              que use la opción -Cf o -CF de compresión  de  tablas  (ver  más
              abajo).   Esto  es  debido a que si está buscando un rendimiento
              alto tendría que estar utilizando una de estas opciones, así que
              si  no  lo  ha  hecho flex asume que prefiere cambiar un poco de
              rendimiento  en  tiempo  de  ejecución  en   beneficio   de   un
              comportamiento  iteractivo  intuitivo.   Fíjese  también  que no
              puede utilizar -I conjuntamente con -Cf o -CF.  Así, esta opción
              no  se  necesita  realmente;  está activa por defecto para todos
              esos casos en los que se permite.

              Usted puede forzar al analizador que no sea  interactivo  usando
              -B (ver más arriba).

       -L     ordena  a flex que no genere directivas #line.  Sin esta opción,
              flex acribilla al analizador generado con directivas #line  para
              que  los  mensajes  de  error  en las acciones estén localizadas
              correctamente respecto al  fichero  original  de  flex  (si  los
              errores  son  debidos  al  código en el fichero de entrada), o a
              lex.yy.c (si los errores son fallos de flex -- debería  informar
              de  este  tipo  de  errores  a  la  dirección de correo dada más
              abajo).

       -T     hace que flex se ejecute en modo de  traza.   Este  generará  un
              montón  de mensajes en stderr relativos a la forma de la entrada
              y el autómata finito no-determinista o determinista  resultante.
              Esta  opción  generalmente es para usarla en el mantenimiento de
              flex.

       -V     imprime el número de la versión en stdout y sale.  --version  es
              un sinónimo de -V.

       -7     ordena  a flex que genere un analizador de 7-bits, es decir, uno
              que sólo puede reconocer caracteres de 7-bits en su entrada.  La
              ventaja  de  usar -7 es que las tablas del analizador pueden ser
              hasta la mitad del tamaño de aquellas generadas usando la opción
              -8  (ver  más abajo).  La desventaja es que tales analizadores a
              menudo se cuelgan o revientan si su entrada contiene  caracteres
              de 8-bits.

              Fíjese,  sin  embargo,  que  a  menos  que  genere su analizador
              utilizando las opciones de compresión de tablas -Cf  o  -CF,  el
              uso  de -7 ahorrará solamente una pequeña cantidad de espacio en
              la tabla, y hará su analizador considerablemente menos portable.
              El  comportamiento  por defecto de flex es generar un analizador
              de 8-bits a menos que use -Cf o  -CF,  en  cuyo  caso  flex  por
              defecto  genera  analizadores  de  7-bits a menos que su sistema
              siempre esté configurado para generar analizadores de 8-bits  (a
              menudo  este será el caso de los sistemas fuera de EEUU).  Puede
              decir si flex generó un analizador de 7 u 8 bits  inspeccionando
              el  sumario  de  banderas  en  la salida de -v como se describió
              anteriormente.

              Fíjese que si usa -Cfe o -CFe (esas opciones  de  compresión  de
              tablas,  pero  también  el uso de clases de equivalencia como se
              comentará más abajo), flex genera aún por defecto un  analizador
              de  8-bits,  ya que normalmente con estas opciones de compresión
              las tablas de 8-bits completas no son mucho más  caras  que  las
              tablas de 7-bits.

       -8     ordena  a flex que genere un analizador de 8-bits, es decir, uno
              que puede reconocer caracteres de 8-bits.  Esta bandera sólo  es
              necesaria  para  analizadores generados usando -Cf o -CF, ya que
              de otra manera flex por defecto genera un analizador  de  8-bits
              de todas formas.

              Vea el comentario sobre -7 más arriba a cerca del comportamiento
              por defecto de flex y la discusión  entre  los  analizadores  de
              7-bits y 8-bits.

       -+     especifica  que  quiere  que  flex genere un analizador como una
              clase de C++.  Vea la sección  Generando  Escáners  en  C++  más
              abajo para los detalles.

       -C[aefFmr]
              controla el grado de compresión de la tabla y, más generalmente,
              el  compromiso  entre  analizadores  pequeños   y   analizadores
              rápidos.

              -Ca  ("alinea")  ordena a flex que negocie tablas más grandes en
              el analizador generado para un comportamiento más rápido  porque
              los elementos de las tablas están mejor alineados para el acceso
              a memoria y computación.   En  algunas  arquitecturas  RISC,  la
              búsqueda  y manipulación de palabras largas es más eficiente que
              con unidades más pequeñas  tales  como  palabras  cortas.   Esta
              opción  puede  doblar  el  tamaño  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
              léxicas (por ejemplo, si la única aparición  de  dígitos  en  la
              entrada  de  flex  es en la clase de caracteres "[0-9]" entonces
              los dígitos ’0’, ’1’, ..., ’9’ se  pondrán  todos  en  la  misma
              clase  de equivalencia).  Las clases de equivalencia normalmente
              ofrecen notables reducciones en  los  tamaños  de  los  ficheros
              finales  de  tabla/objeto  (típicamente  un factor de 2-5) y son
              juiciosamente bastante baratos en  cuanto  al  rendimiento  (una
              localización en un vector por caracter analizado).

              -Cf  especifica  que  se deben generar las tablas del analizador
              completas - flex no debería comprimir las tablas tomando ventaja
              de   las  funciones  de  transición  similares  para  diferentes
              estados.

              -CF  especifica  que  debería  usarse  la   representación   del
              analizador  rápido  alternativo  (descrito  anteriormente  en la
              bandera -F ) Esta opción no puede usarse con -+.

              -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 están usando) que comunmente se
              usan  de forma conjunta.  Las clases de meta-equivalencias son a
              menudo un gran ahorro cuando se usan  tablas  comprimidas,  pero
              tienen  un  impacto  moderado en el rendimiento (uno o dos tests
              "if" y una localización en un array por caracter analizado).

              -Cr hace que  el  analizador  generado  elimine  el  uso  de  la
              librería  de E/S estándar para la entrada.  En lugar de llamar a
              fread() o getc(), el analizador utilizará la llamada al  sistema
              read(),  produciendo una ganancia en el rendimiento que varía de
              sistema  en  sistema,   pero   en   general   probablemente   es
              insignificante  a  menos  que también esté usando -Cf o -CF.  El
              uso de -Cr puede producir  un  comportamiento  extraño  si,  por
              ejemplo,  lee de yyin usando stdio antes de llamar al analizador
              (porque el analizador perderá cualquier texto que  sus  lecturas
              anteriores dejaron en el buffer de entrada de stdio).

              -Cr  no  tiene  efecto  si usted define YY_INPUT (ver El Escáner
              Generado más arriba).

              Con solamente -C se especifica que  las  tablas  del  analizador
              deberían comprimirse pero no debería utilizarse ni las clases de
              equivalencia ni las clases de meta-equivalencias.

              Las opciones -Cf o -CF y -Cm no tienen sentido juntas -  no  hay
              oportunidad para las clases de meta-equivalencias si la tabla no
              está siendo comprimida.  De  otra  forma  las  opciones  podrían
              mezclarse líbremente, y son acumulativas.

              La  configuración  por  defecto es -Cem, que especifica que flex
              debería  generar  clases  de  equivalencia  y  clases  de  meta-
              equivalencias.   Esta  configuración  provee  el  mayor grado de
              compresión.  Puede llegarse a un compromiso  entre  analizadores
              de  ejecución  más  rápida con el coste de tablas mayores siendo
              generalmente verdadero lo siguiente:

                  lo más lento y pequeño
                        -Cem
                        -Cm
                        -Ce
                        -C
                        -C{f,F}e
                        -C{f,F}
                        -C{f,F}a
                  lo más rápido y grande

              Fíjese que los analizadores con tablas más pequeñas  normalmente
              se  generan  y  compilan de la forma más rápida posible, así que
              durante el desarrollo usted normalmente querrá usar  como  viene
              por defecto, compresión máxima.

              -Cfe  a  menudo  es  un buen compromiso entre velocidad y tamaño
              para la producción de analizadores.

       -osalida
              ordena a flex que escriba el analizador  al  fichero  salida  en
              lugar  de  a lex.yy.c.  Si combina -o con la opción -t, entonces
              el analizador se escribe en stdout  pero  sus  directivas  #line
              (vea  la  opción  -L  más  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 también cambia el nombre por defecto del fichero
              de salida de lex.yy.c a lex.foo.c.  Aquí están todos los nombres
              afectados:

                  yy_create_buffer
                  yy_delete_buffer
                  yy_flex_debug
                  yy_init_buffer
                  yy_flush_buffer
                  yy_load_buffer_state
                  yy_switch_to_buffer
                  yyin
                  yyleng
                  yylex
                  yylineno
                  yyout
                  yyrestart
                  yytext
                  yywrap

              (Si  usted  está  utilizando  un  analizador  en  C++,  entonces
              únicamente yywrap y yyFlexLexer se ven afectados.)  Dentro de su
              analizador,  puede aún hacer referencia a las variables globales
              y  funciones  usando  cualquier  versión  de  su  nombre;   pero
              externamente, estas tienen el nombre modificado.

              Esta  opción le deja enlazar fácilmente múltiples programas flex
              conjuntamente en el mismo ejecutable.  Fíjese, sin embargo,  que
              usando  esta  opción también se renombra yywrap(), de manera que
              ahora debe o bien proveer su propia versión de la rutina (con el
              nombre  apropiado)  para su analizador, o usar %option noyywrap,
              ya que enlazar con -lfl no podrá proveerle una por defecto.

       -Sfichero_esqueleto
              ignora el fichero de esqueleteo por  defecto  con  el  que  flex
              construye   sus   analizadores.    Usted   probablemente   nunca
              necesitará utilizar  esta  opción  a  menos  que  este  haciendo
              mantenimiento o un desarrollo de flex.

       flex  también ofrece un mecanismo para controlar las opciones dentro de
       la propia especificación del analizador, en vez de a partir de la línea
       de  comando.   Esto  se  hace  incluyendo  las directivas %option en la
       primera sección de  la  especificación  del  analizador.   Usted  puede
       especificar  varias  opciones  con una sola directiva %option, y varias
       directivas en la primera sección de su fichero de entrada de flex.

       La mayoría de las  opciones  vienen  dadas  simplemente  como  nombres,
       opcionalmente  precedidos  por  la  palabra  "no"  (sin  intervenir  un
       espacio) para negar su significado.  Las banderas de flex o su negación
       son equivalentes a un número:

           7bit            opción -7
           8bit            opción -8
           align           opción -Ca
           backup          opción -b
           batch           opción -B
           c++             opción -+

           caseful o
           case-sensitive  opuesto de -i (por defecto)

           case-insensitive o
           caseless        opción -i

           debug           opción -d
           default         opuesto de la opción -s
           ecs             opción -Ce
           fast            opción -F
           full            opción -f
           interactive     opción -I
           lex-compat      opción -l
           meta-ecs        opción -Cm
           perf-report     opción -p
           read            opción -Cr
           stdout          opción -t
           verbose         opción -v
           warn            opuesto de la opción -w
                           (use "%option nowarn" para -w)

           array           equivalente a "%array"
           pointer         equivalente a "%pointer" (por defecto)

       Algunas  directivas  %option  ofrecen propiedades que de otra manera no
       están disponibles:

       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 debería leer un caracter a la vez.  Cuando esta opción  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  opción
              implica noyywrap (ver más 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 más 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 compilación.

       yylineno
              ordena a flex a generar un analizador que mantenga el número  de
              la  línea  actual  leída  desde su entrada en la variable global
              yylineno.  Esta opción viene implícita con %option lex-compat.

       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 más 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 están
       disponibles para ignorar sus decisiones siempre que use las opciones, o
       bien  estableciendolas  (p.ej.,  %option  reject)  para  indicar que la
       propiedad se utiliza realmente, o desactivándolas para indicar  que  no
       es utilizada (p.ej., %option noyymore).

       Tres opciones toman valores delimitados por cadenas, separadas por ’=’:

           %option outfile="ABC"

       es equivalente a -oABC, y

           %option prefix="XYZ"

       es equivalente a -PXYZ.  Finalmente,

           %option yyclass="foo"

       sólo se aplica cuando se genera un analizador en C++ (opción -+).  Este
       informa  a flex que ha derivado a foo como una subclase de yyFlexLexer,
       así que flex pondrá sus acciones en la función miembro foo::yylex()  en
       lugar de yyFlexLexer::yylex().  Este también genera una función miembro
       yyFlexLexer::yylex()  que  emite  un  error  en  tiempo  de   ejecución
       (invocando  a  yyFlexLexer::LexerError()) si es llamada.  Ver Generando
       Escáners en C++, más abajo, para información adicional.

       Están disponibles un número de opciones para los puristas de  lint  que
       desean  suprimir la aparición de rutinas no necesarias en el analizador
       generado.  Cada una de la siguientes, si se desactivan (p.ej.,  %option
       nounput  ),  hace  que  la  rutina  correspondiente  no  aparezca en el
       analizador generado:

           input, unput
           yy_push_state, yy_pop_state, yy_top_state
           yy_scan_buffer, yy_scan_bytes, yy_scan_string

       (aunque yy_push_state() y sus amigas no aparecerán de  todas  manera  a
       menos que use %option stack).

CONSIDERACIONES DE RENDIMIENTO

       El  principal  objetivo de diseño de flex es que genere analizadores de
       alto rendimiento.  Este ha sido optimizado para  comportarse  bien  con
       conjuntos  grandes de reglas.  Aparte de los efectos sobre la velocidad
       del  analizador  con  las  opciones  de   compresión   de   tablas   -C
       anteriormente  introducidas,  hay  un  número  de opciones/acciones que
       degradan el rendimiento.  Estas son, desde la más costosa a la menos:

           REJECT
           %option yylineno
           contexto posterior arbitrario

           conjunto de patrones que requieren retroceso
           %array
           %option interactive
           %option always-interactive

           ’^’ operador de comienzo de línea
           yymore()

       siendo las tres primeras bastante costosas y las dos  últimas  bastante
       económicas.   Fíjese también que unput() se implementa como una llamada
       de rutina  que  potencialmente  hace  bastante  trabajo,  mientras  que
       yyless()  es  una macro bastante económica; así que si está devolviendo
       algún texto excedente que ha analizado, use yyless().

       REJECT debería evitarse a cualquier precio  cuando  el  rendimiento  es
       importante.  Esta es una opción particularmente cara.

       Es lioso deshacerse del retroceso y a menudo podría ser una cantidad de
       trabajo enorme  para  un  analizador  complicado.   En  principio,  uno
       comienza  utilizando  la bandera -b para generar un archivo lex.backup.
       Por ejemplo, sobre la entrada

           %%
           foo        return TOK_KEYWORD;
           foobar     return TOK_KEYWORD;

       el fichero tiene el siguiente aspecto:

           El estado #6 es no-aceptar -
            números de línea asociados a la regla:
                  2       3
            fin de transiciones: [ o ]
            transiciones de bloqueo: fin de archivo (EOF) [ \001-n  p-\177 ]

           El estado #8 es no-aceptar -
            números de línea asociados a la regla:
                  3
            fin de transiciones: [ a ]
            transiciones de bloqueo: fin de archivo (EOF) [ \001-‘  b-\177 ]

           El estado #9 es no-aceptar -
            números de línea asociados a la regla:
                  3
            fin de transiciones: [ r ]
            transiciones de bloqueo: fin de archivo (EOF) [ \001-q  s-\177 ]

           Las tablas comprimidas siempre implican un retroceso.

       Las primeras líneas nos dicen que hay un estado del  analizador  en  el
       que  se  puede hacer una transición con una ’o’ pero no sobre cualquier
       otro caracter, y que en ese estado el texto recientemente analizado  no
       empareja  con  ninguna  regla.   El  estado  ocurre  cuando  se intenta
       emparejar las reglas encontradas en las líneas 2 y 3 en el  fichero  de
       entrada.   Si  el analizador está en ese estado y entoces lee cualquier
       cosa que no sea una ’o’, tendrá que retroceder para encontrar una regla
       que  empareje.  Con un poco de análisis uno puede ver que este debe ser
       el estado en el que se está cuando  se  ha  visto  "fo".   Cuando  haya
       ocurrido,  si  se  ve  cualquier cosa que no sea una ’o’, el analizador
       tendrá que retroceder para simplemente emparejar la ’f’ (por  la  regla
       por defecto).

       El  comentario  que  tiene  que  ver con el Estado #8 indica que hay un
       problema cuando se analiza "foob".  En efecto, con  cualquier  caracter
       que  no  sea  una ’a’, el analizador tendrá que retroceder para aceptar
       "foo".  De forma similar, el comentario para el Estado #9 tiene que ver
       cuando se ha analizado "fooba" y no le sigue una ’r’.

       El  comentario final nos recuerda que no mecere la pena todo el trabajo
       para eliminar el retroceso de las reglas a menos que estemos usando -Cf
       o  -CF,  y  que  no  hay  ninguna mejora del rendimiento haciéndolo con
       analizadores comprimidos.

       La manera de quitar los retrocesos es añadiendo reglas de "error":

           %%
           foo         return TOK_KEYWORD;
           foobar      return TOK_KEYWORD;

           fooba       |
           foob        |
           fo          {
                       /* falsa alarma, realmente no es una palabra clave */
                       return TOK_ID;
                       }

       La eliminación de retroceso en una  lista  de  palabras  clave  también
       puede hacerse utilizando una regla "atrápalo-todo":

           %%
           foo         return TOK_KEYWORD;
           foobar      return TOK_KEYWORD;

           [a-z]+      return TOK_ID;

       Normalmente esta es la mejor solución cuando sea adecuada.

       Los  mensajes  sobre  retrocesos tienden a aparecer en cascada.  Con un
       conjunto complicado de reglas no  es  poco  común  obtener  cientos  de
       mensajes.   Si  uno  puede descifrarlos, sin embargo, a menudo sólo hay
       que tomar una docena de reglas o algo así para eliminar los  retrocesos
       (ya  que  es  fácil cometer una equivocación y tener una regla de error
       que reconozca un token válido.  Una posible  característica  futura  de
       flex será añadir reglas automáticamente para eliminar el retroceso).

       Es  importante  tener  en  cuenta  que  se  obtienen  los beneficios de
       eliminar el retroceso sólo si elimina  cada  instancia  del  retroceso.
       Dejar solamente una significa que no ha ganado absolutamente nada.

       El contexto posterior variable (donde la parte delantera y posterior no
       tienen una longitud fija) supone casi la misma pérdida  de  rendimiento
       que  REJECT  (es decir, substanciales).  Así que cuando sea posible una
       regla como esta:

           %%
           raton|rata/(gato|perro)   correr();

       es mejor escribirla así:

           %%
           raton/gato|perro          correr();
           rata/gato|perro           correr();

       o así

           %%
           raton|rata/gato           correr();
           raton|rata/perro          correr();

       Fíjese que aquí la acción especial ’|’ no ofrece ningún ahorro, y puede
       incluso hacer las cosas peor (ver Deficiencias / Errores más abajo).

       Otro  área  donde  el  usuario  puede  incrementar  el  rendimiento del
       analizador (y una que es más fácil de implementar) surge del hecho  que
       cuanto  más  tarde  se empareje un token, más rápido irá el analizador.
       Esto es debido a que con tokens grandes el procesamiento de la  mayoría
       de  los  caracteres  de  entrada  tiene  lugar  en  el (corto) bucle de
       análisis más interno, y no tiene que ir tan a menudo a hacer el trabajo
       de  más  para constituir el entorno del analizador (p.ej., yytext) para
       la acción.  Recuerde el analizador para los comentarios en C:

           %x comentario
           %%
                   int num_linea = 1;

           "/*"         BEGIN(comentario);

           <comentario>[^*\n]*
           <comentario>"*"+[^*/\n]*
           <comentario>\n             ++num_linea;
           <comentario>"*"+"/"        BEGIN(INITIAL);

       Esto podría acelerarse escribiéndolo como:

           %x comentario
           %%
                   int num_linea = 1;

           "/*"         BEGIN(comentario);

           <comentario>[^*\n]*
           <comentario>[^*\n]*\n      ++num_linea;
           <comentario>"*"+[^*/\n]*
           <comentario>"*"+[^*/\n]*\n ++num_linea;
           <comentario>"*"+"/"        BEGIN(INITIAL);

       Ahora en lugar de que cada línea nueva  requiera  el  procesamiento  de
       otra  regla,  el  reconocimiento  de  las líneas nuevas se "distribuye"
       sobre las otras reglas para mantener el texto reconocido tan largo como
       sea  posible.  ¡Fíjese que el aadir reglas no ralentiza el analizador!
       La velocidad del analizador es independiente del  número  de  reglas  o
       (dadas  las  consideraciones  dadas  al  inicio  de  esta sección) cuán
       complicadas sean las reglas respecto a operadores tales como ’*’ y ’|’.

       Un  ejemplo  final  sobre  la aceleración de un analizador: suponga que
       quiere analizar un fichero  que  contiene  identificadores  y  palabras
       clave,  una  por línea y sin ningún caracter extraño, y reconocer todas
       las palabras clave.  Una primera aproximación natural es:

           %%
           asm      |
           auto     |
           break    |
           ... etc ...
           volatile |
           while    /* es una palabra clave */

           .|\n     /* no es una palabra clave */

       Para eliminar el retroceso, introduzca una regla atrápalo-todo:

           %%
           asm      |
           auto     |
           break    |
           ... etc ...
           volatile |
           while    /* es una palabra clave */

           [a-z]+   |
           .|\n     /* no es una palabra clave */

       Ahora, si se garantiza que  hay  exáctamente  una  palabra  por  línea,
       entonces  podemos  reducir  el  número  total de emparejamientos por la
       mitad mezclando el reconocimiento de líneas nuevas con las de los otros
       tokens:

           %%
           asm\n    |
           auto\n   |
           break\n  |
           ... etc ...
           volatile\n |
           while\n  /* es una palabra clave */

           [a-z]+\n |
           .|\n     /* no es una palabra clave */

       Uno  tiene que ser cuidadoso aquí, ya que hemos reintroducido retroceso
       en el analizador.  En particular, aunque nosotros sepamos que ahí nunca
       habrán  otros  caracteres  en  el flujo de entrada que no sean letras o
       líneas nuevas, flex no puede  figurarse  eso,  y  planeará  la  posible
       necesidad de retroceder cuando haya analizado un token como "auto" y el
       próximo caracter sea algo distinto a  una  línea  nueva  o  una  letra.
       Previamente este podría entonces emparejar la regla "auto" y estar todo
       hecho, pero ahora este no tiene una regla "auto", solamente  una  regla
       "auto\n".   Para eliminar la posibilidad de retroceso, podríamos o bien
       duplicar todas las reglas pero sin línea nueva  al  final,  o,  ya  que
       nunca  esperamos  encontrar  tal  entrada  y  por  lo  tanto ni cómo es
       clasificada, podemos introducir una regla atrápalo-todo más,  esta  que
       no incluye una línea nueva:

           %%
           asm\n    |
           auto\n   |
           break\n  |
           ... etc ...
           volatile\n |
           while\n  /* es una palabra clave */

           [a-z]+\n |
           [a-z]+   |
           .|\n     /* no es una palabra clave */

       Compilado  con  -Cf,  esto  es  casi  tan  rápido como lo que uno puede
       obtener de un analizador de flex para este problema en particular.

       Una nota final: flex es lento cuando  empareja  NUL’s,  particularmente
       cuando un token contiene múltiples NUL’s.  Es mejor escribir reglas que
       emparejen cortas cantidades de  texto  si  se  anticipa  que  el  texto
       incluirá NUL’s a menudo.

       Otra  nota final en relación con el rendimiento: tal y como se mencionó
       en la sección Cómo se Reconoce la  Entrada,  el  reajuste  dinámico  de
       yytext  para  acomodar  tokens enormes es un proceso lento porque ahora
       requiere que el token (inmenso) sea reanalizado desde el principio.  De
       esta  manera  si  el  rendimiento  es vital, debería intentar emparejar
       "grandes" cantidades de texto pero no "inmensas" cantidades,  donde  el
       punto medio está en torno a los 8K caracteres/token.

GENERANDO ESCÁNERES 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 debería encontrarse ante ningún  error  de  compilación  (por  favor
       informe  de  cualquier  error  que  encuentre  a la dirección de correo
       electrónico dada en la sección Autores más abajo).  Puede entonces usar
       código  C++ en sus acciones de las reglas en lugar de código C.  Fíjese
       que la fuente de entrada por defecto para su analizador permanece  como
       yyin,  y  la  repetición  por  defecto  se  hace  aún  a  yyout.  Ambos
       permanecen como variables FILE * y no como flujos de C++.

       También puede utilizar flex para generar un analizador como  una  clase
       de C++, utilizando la opción -+ (o, equivalentemente, %option c++), que
       se especifica automáticamente si  el  nombre  del  ejecutable  de  flex
       finaliza  con un ’+’, tal como flex++.  Cuando se usa esta opción, flex
       establece por defecto la generación del analizador al fichero lex.yy.cc
       en  vez  de  lex.yy.c.   El  analizador  generado incluye el fichero de
       cabecera FlexLexer.h, que define el interfaz con las dos clases de C++.

       La primera clase, FlexLexer, ofrece una clase base abstracta definiendo
       la interfaz a  la  clase  del  analizador  general.   Este  provee  las
       siguientes funciones miembro:

       const char* YYText()
              retorna  el  texto  del  token  reconocido más recientemente, el
              equivalente a yytext.

       int YYLeng()
              retorna la longitud del token reconocido más  recientemente,  el
              equivalente a yyleng.

       int lineno() const
              retorna  el  número  de  línea  de  entrada  actual (ver %option
              yylineno), o 1 si no se usó %option yylineno.

       void set_debug( int flag )
              activa la bandera de depuración para el analizador,  equivalente
              a  la  asignación  de yy_flex_debug (ver la sección Opciones más
              arriba).  Fíjese que debe  construir  el  analizador  utilizando
              %option debug para incluir información de depuración en este.

       int debug() const
              retorna el estado actual de la bandera de depuración.

       También     se     proveen    funciones    miembro    equivalentes    a
       yy_switch_to_buffer(), yy_create_buffer() (aunque el  primer  argumento
       es  un  puntero  a  objeto  istream* y no un FILE*), yy_flush_buffer(),
       yy_delete_buffer(), y yyrestart() (de nuevo, el primer argumento es  un
       puntero a objeto istream* ).

       La  segunda clase definida en FlexLexer.h es yyFlexLexer, que se deriva
       de  FlexLexer.   Esta   define   las   siguientes   funciones   miembro
       adicionales:

       yyFlexLexer( 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  acción  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 estará utilizando esa subclase en lugar de yyFlexLexer.
              Es este caso,  en  vez  de  generar  yyFlexLexer::yylex(),  flex
              genera    S::yylex()    (y    también   genera   un   substituto
              yyFlexLexer::yylex() que llama a yyFlexLexer::LexerError() si se
              invoca).

       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().

       Además,  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  número  de
              caracteres  leídos.  Para indicar el fin-de-la-entrada, devuelve
              0 caracteres.  Fíjese 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 está 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 también 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 versión  por  defecto
              de esta función escribe el mensaje al flujo cerr y finaliza.

       Fíjese  que  un  objeto  yyFlexLexer  contiene  su  estado  de análisis
       completo.  Así puede utilizar  tales  objetos  para  crear  analizadore
       reentrantes.    Puede   hacer  varias  instancias  de  la  misma  clase
       yyFlexLexer, y puede combinar varias  clases  de  analizadores  en  C++
       conjuntamente  en  el  mismo  programa  usando  la  opción -P comentada
       anteriormente.

       Finalmente, note que la característica %array  no  está  disponible  en
       clases de analizadores en C++; debe utilizar %pointer (por defecto).

       Aquí hay un ejemplo de un analizador en C++ simple:

           // Un ejemplo del uso de la clase analizador en C++ de flex.

           %{
           int mylineno = 0;
           %}

           string  \"[^\n"]+\"

           ws      [ \t]+

           alpha   [A-Za-z]
           dig     [0-9]
           name    ({alpha}|{dig}|\$)({alpha}|{dig}|[_.\-/$])*
           num1    [-+]?{dig}+\.?([eE][-+]?{dig}+)?
           num2    [-+]?{dig}*\.{dig}+([eE][-+]?{dig}+)?
           number  {num1}|{num2}

           %%

           {ws}    /* evita los espacios en blanco y tabuladores */

           "/*"    {
                   int c;

                   while((c = yyinput()) != 0)
                       {
                       if(c == ’\n’)
                           ++mylineno;

                       else if(c == ’*’)
                           {
                           if((c = yyinput()) == ’/’)
                               break;
                           else
                               unput(c);
                           }
                       }
                   }

           {number}  cout << "número " << YYText() << ’\n’;

           \n        mylineno++;

           {name}    cout << "nombre " << YYText() << ’\n’;

           {string}  cout << "cadena " << YYText() << ’\n’;

           %%

           int main( int /* argc */, char** /* argv */ )
               {
               FlexLexer* lexer = new yyFlexLexer;
               while(lexer->yylex() != 0)
                   ;
               return 0;
               }
       Si  desea crear varias (diferentes) clases analizadoras, use la bandera
       -P (o la opción prefix= ) para renombrar cada yyFlexLexer a algún  otro
       xxFlexLexer.    Entonces  puede  incluir  <FlexLexer.h>  en  los  otros
       ficheros fuente una vez  por  clase  analizadora,  primero  renombrando
       yyFlexLexer como se presenta a continuación:

           #undef yyFlexLexer
           #define yyFlexLexer xxFlexLexer
           #include <FlexLexer.h>

           #undef yyFlexLexer
           #define yyFlexLexer zzFlexLexer
           #include <FlexLexer.h>

       si,  por  ejemplo,  usted  utilizó  %option prefix="xx" para uno de sus
       analizadores y %option prefix="zz" para el otro.

       IMPORTANTE: la forma actual de la clase analizadora es  experimental  y
       podría cambiar considerablemente entre versiones principales.

INCOMPATIBILIDADES CON LEX Y POSIX

       flex  es una reescritura de la herramienta lex del Unix de AT&T (aunque
       las dos implementaciones  no  comparten  ningún  código),  con  algunas
       extensiones  e  incompatibilidades,  de  las  que  ambas  conciernen  a
       aquellos que desean  escribir  analizadores  aceptables  por  cualquier
       implementación.   Flex  sigue  completamente la especificación POSIX de
       lex, excepto que cuando se utiliza %pointer (por defecto), una  llamada
       a  unput()  destruye  el  contenido  de  yytext, que va en contra de la
       especificación POSIX.

       En  esta  sección   comentaremos   todas   las   áreas   conocidas   de
       incompatibilidades  entre flex, lex de AT&T, y la especificación POSIX.

       La  opción  -l  de  flex  activa  la  máxima  compatibilidad   con   la
       implementación  original  de  lex  de  AT&T,  con el coste de una mayor
       pérdida de rendimiento en el analizador generado.  Indicamos más  abajo
       qué incompatibilidades pueden superarse usando la opción -l.

       flex es totalmente compatible con lex con las siguientes excepciones:

       -      La  variable  interna  del  analizador  de  lex  sin  documentar
              yylineno no se ofrece a menos que se use -l o %option  yylineno.

              yylineno  debería  gestionarse  por  buffer,  en  lugar  de  por
              analizador (simple variable global).

              yylineno no es parte de la especificación POSIX.

       -      La rutina input() no es  redefinible,  aunque  podría  invocarse
              para  leer  los  caracteres  que siguen a continuación 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 restricción de flex de que input() no puede redefinirse va de
              acuerdo a la especificación POSIX, que simplemente no especifica
              ninguna manera de controlar la entrada del analizador que no sea
              haciendo una asignación inicial a yyin.

       -      La rutina unput() no es redefinible.   Esta  restricción  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 interrupción con long-jumps fuera del
              analizador, y el analizador a continuación se invoca  de  nuevo,
              podría obtener el siguiente mensaje:

                  fatal flex scanner internal error--end of buffer missed

              Para volver al analizador, primero utilice

                  yyrestart( yyin );

              Vea  que  esta llamada eliminará cualquier entrada en el buffer;
              normalmente  esto  no  es  un   problema   con   un   analizador
              interactivo.

              Dese  cuenta  también  de que las clases analizadoras en C++ son
              reentrantes, así que si usar  C++  es  una  opción  para  usted,
              debería  utilizarla.  Vea "Generando Escáners en C++" más 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 especificación POSIX.

       -      lex  no  acepta  condiciones de arranque exclusivas (%x), aunque
              están en la especificación POSIX.

       -      Cuando se expanden las definiciones,  flex  las  encierra  entre
              paréntesis.  Con lex, lo siguiente:

                  NOMBRE    [A-Z][A-Z0-9]*
                  %%
                  foo{NOMBRE}?      printf( "Lo encontró\n" );
                  %%

              no  reconocerá la cadena "foo" porque cuando la macro se expanda
              la regla es equivalente a "foo[A-Z][A-Z0-9]*?" y la  precedencia
              es tal que el ’?’ se asocia con "[A-Z0-9]*".  Con flex, la regla
              se expandirá a "foo([A-Z][A-Z0-9]*)?" y así la cadena  "foo"  se
              reconocerá.

              Fíjese  que  si  la  definición  comienza con ^ o finaliza con $
              entonces no se expande con paréntesis, para permitir  que  estos
              operadores   aparezcan   en   las  definiciones  sin  perder  su
              significado especial.  Pero los operadores <s>, /, y <<EOF>>  no
              pueden utilizarse en una definición de flex.

              El  uso  de  -l  produce en el comportamiendo de lex el no poner
              paréntesis alrededor de la definición.

              La especificación de POSIX  dice  que  la  definición  debe  ser
              encerrada entre paréntesis.

       -      Algunas  implementaciones  de  lex permiten que la acción de una
              regla comience en una línea separada, si el patrón de  la  regla
              tiene espacios en blanco al final:

                  %%
                  foo|bar<espacio aquí>
                    { foobar_action(); }

              flex no dispone de esta propiedad.

       -      La opción %r de lex (generar un analizador Ratfor) no se ofrece.
              No es parte de la especificación de POSIX.

       -      Después de una llamada a unput(), el contenido  de  yytext  está
              indefinido  hasta que se reconozca el próximo token, a menos que
              el analizador se haya construido usando %array.  Este no  es  el
              caso  de lex o la especificación de POSIX.  La opción -l elimina
              esta incompatibilidad.

       -      La precedencia del operador {} (rango  numérico)  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 último va de
              acuerdo con la especificación de POSIX.

       -      La precedencia del operador  ^  es  diferente.   lex  interpreta
              "^foo|bar"  como "empareja bien ’foo’ al principio de una línea,
              o ’bar’ en cualquier lugar", mientras  que  flex  lo  interpreta
              como  "empareja  ’foo’  o  ’bar’  si  vienen al principio de una
              línea".  Lo último va de acuerdo con la especificación de POSIX.

       -      Las  declaraciones  especiales del tamaño 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 podrían escribirse para ser procesados con  flex  o
              con      lex.      Los     analizadores     también     incluyen
              YY_FLEX_MAJOR_VERSION  y  YY_FLEX_MINOR_VERSION  indicando   qué
              versión  de  flex  generó  el  analizador  (por ejemplo, para la
              versión 2.5, estas definiciones serán 2 y 5 respectivamente).

       Las siguientes  propiedades  de  flex  no  se  incluyen  en  lex  o  la
       especificación POSIX:

           analizadores en C++
           %option
           ámbitos de condiciones de arranque
           pilas de condiciones de arranque
           analizadores interactivos/no-interactivos
           yy_scan_string() y sus amigas
           yyterminate()
           yy_set_interactive()
           yy_set_bol()
           YY_AT_BOL()
           <<EOF>>
           <*>
           YY_DECL
           YY_START
           YY_USER_ACTION
           YY_USER_INIT
           directivas #line
           %{}’s alrededor de acciones
           varias acciones en una línea

       más  casi  todas las banderas de flex.  La última propiedad en la lista
       se refiere al hecho de que con flex puede poner varias acciones  en  la
       misma  línea,  sepradas  con  punto  y  coma,  mientras que con lex, lo
       siguiente

           foo    handle_foo(); ++num_foos_seen;

       se trunca (sorprendentemente) a

           foo    handle_foo();

       flex no trunca la acción.  Las acciones que no se encierran  en  llaves
       simplemente se terminan al final de la línea.

DIAGNÓSTICOS

       aviso,  la  regla no se puede aplicar indica que la regla dada no puede
       emparejarse porque sigue a otras  reglas  que  siempre  emparejarán  el
       mismo texto que el de esta.  Por ejemplo, en el siguiente ejemplo "foo"
       no puede emparejarse porque viene después de una regla  "atrápalo-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 opcin -s pero se puede aplicar la regla
       por defecto significa  que  es  posible  (tal  vez  únicamente  en  una
       condición   de  arranque  en  particular)  que  la  regla  por  defecto
       (emparejar cualquier caracter simple) sea la única que  emparejará  una
       entrada particular.  Ya que se indicó -s, presumiblemente esto no es lo
       que se pretendía.

       definicin no definida {reject_used_but_not_detected} o  definicin  no
       definida  {yymore_used_but_not_detected} - Estos errores pueden suceder
       en tiempo de compilación.  Indican  que  el  analizador  usa  REJECT  o
       yymore() pero que flex falló en darse cuenta del hecho, queriendo decir
       que flex analizó las dos primeras  secciones  buscando  apariciones  de
       estas  acciones  y falló en encontrar alguna, pero que de algún modo se
       le han colado (por medio de un archivo  #include,  por  ejemplo).   Use
       %option  reject  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 también debido a problemas internos.

       token too large, exceeds YYLMAX - su analizador usa %array y una de sus
       reglas  reconoció  una  cadena  más  grande que la constante YYLMAX (8K
       bytes por defecto).  Usted  puede  incrementar  el  valor  haciendo  un
       #define YYLMAX en la sección de definiciones de su entrada de flex.

       el  analizador  requiere la opcin -8 para poder usar el carcter x -
       La especificación  de  su  analizador  incluye  el  reconocimiento  del
       caracter  de  8-bits  x  y  no  ha  especificado  la bandera -8, y su
       analizador por defecto está a 7-bits porque ha usado las opciones -Cf o
       -CF  de  compresión de tablas.  Vea el comentario de la bandera -7 para
       los detalles.

       flex scanner push-back overflow - usted utilizó 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 debería
       ajustar  dinámicamente  el  buffer en este caso, pero actualmente no lo
       hace.

       input buffer overflow, cant enlarge buffer because scanner uses REJECT
       -  el  analizador  estaba  intentando reconocer un token extremadamente
       largo y necesitó 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 después de que un long-jump
       haya  saltado fuera (o sobre) el registro de activación del analizador.
       Antes de reintroducir el analizador, use:

           yyrestart( yyin );

       o, como se comentó más arriba, cambie y use el analizador como clase de
       C++.

       too many start conditions in <> construct! - ha listado más condiciones
       de arranque en una construcción <> que las que existen  (así  que  tuvo
       que haber listado al menos una de ellas dos veces).

FICHEROS

       -lfl   librería 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  únicamente
              cuando se construye flex, no cuando flex se ejecuta.

       lex.backup
              información  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.
       (Fíjese  que el borrador de POSIX establece que el texto reconocido por
       tales patrones no está definido.)

       Para algunas reglas de contexto posterior, partes que son de  hecho  de
       longitud  fija  no se reconocen como tales, resultando en la pérdida de
       rendimiento mencionada anteriormente.  En particular,  las  partes  que
       usan  ’|’ o {n} (tales como "foo{3}") siempre se consideran de longitud
       variable.

       La combinación de contexto posterior con la acción especial  ’|’  puede
       producir  que  el  contexto  posterior  fijo  se  convierta en contexto
       posterior variable que es más caro.  Por ejemplo, en  lo  que  viene  a
       continuación:

           %%
           abc      |
           xyz/def

       El  uso  de  unput()  invalida  yytext  e yyleng, a menos que se use la
       directiva %array o la opción -l.

       La concordancia de patrones de NUL’s es substancialmente más lento  que
       el reconocimiento de otros caracteres.

       El  ajuste  dinámico del buffer de entrada es lento, ya que conlleva el
       reanálisis  de  todo  el  texto  reconocido  hasta  entonces   por   el
       (generalmente enorme) token actual.

       Debido  al  uso  simultáneo  de  buffers  de  entrada  y  lecturas  por
       adelantado, no puede entremezclar  llamadas  a  rutinas  de  <stdio.h>,
       tales  como,  por  ejemplo, getchar(), con reglas de flex y esperar que
       funcione.  Llame a input() en su lugar.

       La totalidad de las entradas de la tabla  listada  por  la  bandera  -v
       excluye  el  número  de entradas en la tabla necesarias para determinar
       qué regla ha sido emparejada.  El número de entradas es igual al número
       de  estados del DFA si el analizador no usa REJECT, y algo mayor que el
       número de estados si se usa.

       REJECT no puede usarse con las opciones -f o -F.

       El algoritmo interno de flex necesita documentación.

VER TAMBIÉN

       lex(1), yacc(1), sed(1), awk(1).

       John Levine, Tony Mason, and Doug  Brown,  Lex  &  Yacc,  O’Reilly  and
       Associates.  Esté seguro de obtener la 2ª edición.

       M. E. Lesk and E. Schmidt, LEX - Lexical Analyzer Generator

       Alfred  Aho,  Ravi  Sethi  and  Jeffrey  Ullman, Compilers: Principles,
       Techniques and Tools, Addison-Wesley  (1986)  (Edición  en  castellano:
       Compiladores:   Principios,  Tcnicas  y  Herramientas,  Addison-Wesley
       Iberoamericana, S.A. (1990))  Describe las técnicas de concordancia  de
       patrones usadas por flex (autómata finito determinista).

AUTOR

       Vern  Paxson,  con  la  ayuda  de  muchas  ideas  e  inspiración de Van
       Jacobson.  Versión original por Jef Poskanzer.   La  representación  de
       tablas rápidas es una implementación parcial de un diseño hecho por Van
       Jacobson.  La implementación fue hecha por Kevin Gong y Vern Paxson.

       Agradecimientos  a  los  muchos  flex  beta-testers,   feedbackers,   y
       contribuidores,  especialmente  a Francois Pinard, Casey Leedom, Robert
       Abramovitz, Stan Adermann,  Terry  Allen,  David  Barker-Plummer,  John
       Basrai,  Neal  Becker,  Nelson  H.F. Beebe, benson@odi.com, Karl Berry,
       Peter A. Bigot, Simon Blanchard,  Keith  Bostic,  Frederic  Brehm,  Ian
       Brockbank,  Kin  Cho,  Nick  Christopher,  Brian Clapper, J.T. Conklin,
       Jason Coughlin, Bill  Cox,  Nick  Cropper,  Dave  Curtis,  Scott  David
       Daniels,   Chris  G.  Demetriou,  Theo  Deraadt,  Mike  Donahue,  Chuck
       Doucette, Tom Epperly, Leo Eskin, Chris  Faylor,  Chris  Flatters,  Jon
       Forrest,  Jeffrey  Friedl,  Joe  Gayda, Kaveh R. Ghazi, Wolfgang Glunz,
       Eric Goldman, Christopher M. Gould, Ulrich Grepel,  Peer  Griebel,  Jan
       Hajic,  Charles Hemphill, NORO Hideo, Jarkko Hietaniemi, Scott Hofmann,
       Jeff Honig, Dana Hudes, Eric Hughes, John  Interrante,  Ceriel  Jacobs,
       Michal  Jaegermann,  Sakari Jalovaara, Jeffrey R. Jones, Henry Juengst,
       Klaus  Kaempf,  Jonathan  I.  Kamens,  Terrence  O  Kane,  Amir   Katz,
       ken@ken.hilco.com,  Kevin B. Kenny, Steve Kirsch, Winfried Koenig, Marq
       Kole, Ronald Lamprecht, Greg  Lee,  Rohan  Lenard,  Craig  Leres,  John
       Levine, Steve Liddle, David Loffredo, Mike Long, Mohamed el Lozy, Brian
       Madsen, Malte, Joe Marshall,  Bengt  Martensson,  Chris  Metcalf,  Luke
       Mewburn,  Jim Meyering, R. Alexander Milowski, Erik Naggum, G.T. Nicol,
       Landon Noll,  James  Nordby,  Marc  Nozell,  Richard  Ohnemus,  Karsten
       Pahnke,  Sven  Panne,  Roland  Pesch, Walter Pelissero, Gaumond Pierre,
       Esmond  Pitt,  Jef  Poskanzer,  Joe  Rahmeh,  Jarmo   Raiha,   Frederic
       Raimbault,  Pat Rankin, Rick Richardson, Kevin Rodgers, Kai Uwe Rommel,
       Jim Roskind, Alberto Santini, Andreas Scherer,  Darrell  Schiebel,  Raf
       Schietekat,  Doug  Schmidt, Philippe Schnoebelen, Andreas Schwab, Larry
       Schwimmer, Alex  Siegel,  Eckehard  Stolz,  Jan-Erik  Strvmquist,  Mike
       Stump,  Paul  Stuart,  Dave  Tallman,  Ian Lance Taylor, Chris Thewalt,
       Richard M. Timoney, Jodi Tsai, Paul Tuinenga, Gary Weik, Frank  Whaley,
       Gerhard  Wilhelms,  Kent  Williams,  Ken Yap, Ron Zellar, Nathan Zelle,
       David Zuhn, y aquellos cuyos nombres han caído bajo mis  escasas  dotes
       de  archivador de correo pero cuyas contribuciones son apreciadas todas
       por igual.

       Agradecimientos a  Keith  Bostic,  Jon  Forrest,  Noah  Friedman,  John
       Gilmore,  Craig  Leres, John Levine, Bob Mulcahy, G.T.  Nicol, Francois
       Pinard, Rich Salz, y a Richard  Stallman  por  la  ayuda  con  diversos
       quebraderos de cabeza con la distribución.

       Agradecimientos  a  Esmond  Pitt  y  Earle  Horton  por  el  soporte de
       caracteres de 8-bits; a Benson Margulies y a Fred Burke por el  soporte
       de  C++;  a Kent Williams y a Tom Epperly por el soporte de la clase de
       C++; a Ove Ewerlid por el soporte de NUL’s; y  a  Eric  Hughes  por  el
       soporte de múltiples buffers.

       Este  trabajo fue hecho principalmente cuando yo estaba con el Grupo de
       Sistemas de Tiempo Real en el Lawrence Berkeley Laboratory en Berkeley,
       CA.  Muchas gracias a todos allí por el apoyo que recibí.

       Enviar comentarios a vern@ee.lbl.gov.

       Sobre   esta   traducción  enviar  comentarios  a  Adrián  Pérez  Jorge
       (alu1415@csi.ull.es).