flex-es-2.5

Embed Size (px)

Citation preview

Flex, versin 2.5 oUn generador de analizadores lxicos rpidos. e a Edicin 2.5, Abril 1995 o

Vern Paxson

Copyright c 1990 The Regents of the University of California. All rights reserved. This code is derived from software contributed to Berkeley by Vern Paxson. The United States Government has rights in this work pursuant to contract no. DE-AC0376SF00098 between the United States Department of Energy and the University of California. Redistribution and use in source and binary forms with or without modication are permitted provided that: (1) source distributions retain this entire copyright notice and comment, and (2) distributions including binaries display the following acknowledgement: This product includes software developed by the University of California, Berkeley and its contributors in the documentation or other materials provided with the distribution and in all advertising materials mentioning features or use of this software. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specic prior written permission. THIS SOFTWARE IS PROVIDED AS IS AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

Cap tulo 1: Introduccin o

1

1 Introduccin oEste manual describe flex, una herramienta para la generacin de programas que realio zan concordancia de patrones en texto. El manual incluye a la vez secciones de tutorial y de referencia: Descripcin o una breve introduccin a la herramienta o Algunos Ejemplos Simples Formato del Fichero de Entrada Patrones las expresiones regulares extendidas que utiliza ex Cmo se Empareja la Entrada o las reglas para determinar lo que ha concordado Acciones cmo especicar qu hacer cuando concuerde un patrn o e o El Escner Generado a detalles respecto al escner que produce ex; cmo controlar la fuente de entrada a o Condiciones de Arranque la introducin de contexto en sus escneres, y conseguir "mini-escneres" o a a M ltiples Buers de Entrada u cmo manipular varias fuentes de entrada; cmo analizar cadenas en lugar de cheo o ros. Reglas de Fin-de-Fichero reglas especiales para reconocer el nal de la entrada Macros Miscelneas a 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 escneres de ex junto con analizadores de yacc a Opciones opciones de l nea de comando de ex, y la directiva "%option" Consideraciones de Rendimiento cmo hacer que sus analizadores vayan tan rpido como sea posible o a Generando Escneres en C++ a la facilidad (experimental) para generar analizadores lxicos como clases de C++ e Incompatibilidades con Lex y POSIX cmo ex diere del lex de AT&T y del lex estndar de POSIX o a Diagnsticos o esos mensajes de error producidos por ex (o por los escneres que este genera) cuyo a signicado podr no ser evidente a Ficheros los cheros usados por ex Deciencias / Errores problemas de ex conocidos Ver Tambin e otra documentacin, herramientas relacionadas o Autor incluye informacin de contacto o

Cap tulo 2: Descripcin o

2

2 Descripcin oflex es una herramienta para generar escneres: programas que reconocen patrones lxicos a e en un texto. flex lee los cheros de entrada dados, o la entrada estndar si no se le ha indicado a ning n nombre de chero, con la descripcin de un escner a generar. La descripcin se encuentra u o a o en forma de parejas de expresiones regulares y cdigo C, denominadas reglas. flex genera como o salida un chero fuente en C, lex.yy.c, que dene una rutina yylex(). Este chero se compila y se enlaza con la librer -lfl para producir un ejecutable. Cuando se arranca el a chero ejecutable, este analiza su entrada en busca de casos de las expresiones regulares. Siempre que encuentra uno, ejecuta el cdigo C correspondiente. o

Cap tulo 3: Algunos ejemplos simples

3

3 Algunos ejemplos simplesEn primer lugar veremos algunos ejemplos simples para una toma de contacto con el uso de flex. La siguiente entrada de flex especica un escner que siempre que encuentre la cadena a "username" la reemplazar por el nombre de entrada al sistema del usuario: a %% username printf( "%s", getlogin() ); Por defecto, cualquier texto que no reconozca el analizador lxico de flex se copia a la salida, e as que el efecto neto de este escner es copiar su chero de entrada a la salida con cada aparicin a o de "username" expandida. En esta entrada, hay solamente una regla. "username" es el patrn o y el "printf" es la accin. El "%%" marca el comienzo de las reglas. o Aqu hay otro ejemplo simple: int num_lineas = 0, num_caracteres = 0; %% \n . %% main() { yylex(); printf( "# de lneas = %d, # de caracteres. = %d\n", num_lineas, num_caracteres ); } Este analizador cuenta el n mero de caracteres y el n mero de l u u neas en su entrada (no produce otra salida que el informe nal 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 despus del segundo "%%". Hay dos reglas, una que empareja e 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 expresin regular "."). o Un ejemplo algo ms complicado: a /* escner para un lenguaje de juguete al estilo de Pascal */ a %{ /* se necesita esto para la llamada a atof() ms abajo */ a #include %} DIGITO ID %% {DIGITO}+ { printf( "Un entero: %s (%d)\n", yytext, atoi( yytext ) ); } [0-9] [a-z][a-z0-9]*

++num_lineas; ++num_caracteres; ++num_caracteres;

Cap tulo 3: Algunos ejemplos simples

4

{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 ); /* se come una linea de comentarios */ /* se come los espacios en blanco */ printf( "Caracter no reconocido: %s\n", yytext );

"+"|"-"|"*"|"/" "{"[^}\n]*"}" [ \t\n]+ . %%

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 ser los comienzos de un escner simple para un lenguaje como Pascal. Este a a identica diferentes tipos de tokens e informa a cerca de lo que ha visto. Los detalles de este ejemplo se explicarn en las secciones siguientes. a

Cap tulo 4: Formato del chero de entrada

5

4 Formato del chero de entradaEl chero de entrada de flex est compuesto de tres secciones, separadas por una l a nea donde aparece unicamente un %% en esta: definiciones %% reglas %% cdigo de usuario o La seccin de deniciones contiene declaraciones de deniciones de nombres sencillas para o simplicar la especicacin del escner, y declaraciones de condiciones de arranque, que se o a explicarn en una seccin posterior. Las deniciones de nombre tienen la forma: a o nombre definicin o El "nombre" es una palabra que comienza con una letra o un subrayado (_) seguido por cero o ms letras, d a gitos, _, o - (guin). La denicin se considera que comienza en el primer o o caracter que no sea un espacio en blanco siguiendo al nombre y continuando hasta el nal de la l nea. Posteriormente se puede hacer referencia a la denicin utilizando "{nombre}", que se o expandir a "(denicin)". Por ejemplo, a o DIGITO [0-9] ID [a-z][a-z0-9]* dene "DIGITO" como una expresin regular que empareja un d o gito sencillo, e "ID" como una expresin regular que empareja una letra seguida por cero o ms letras o d o a gitos. Una referencia posterior a {DIGITO}+"."{DIGITO}* es idntica a e ([0-9])+"."([0-9])* y empareja uno o ms d a gitos seguido por un . seguido por cero o ms d a gitos. La seccin de reglas en la entrada de flex contiene una serie de reglas de la forma: o patrn o accin o donde el patrn debe estar sin sangrar y la accin debe comenzar en la misma l o o nea. Ver Cap tulo 7 [Acciones], pgina 10, para una descripcin ms amplia sobre patrones y a o a acciones. Finalmente, la seccin de cdigo de usuario simplemente se copia a lex.yy.c literalmente. o o Esta seccin se utiliza para rutinas de complemento que llaman al escner o son llamadas por o a este. La presencia de esta seccin es opcional; Si se omite, el segundo %% en el chero de entrada o se podr omitir tambin. a e En las secciones de deniciones 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 unicamente por estos. En la seccin de reglas, cualquier texto o %{} sangrado que aparezca antes de la primera o regla podr utilizarse para declarar variables que son locales a la rutina de anlisis y (despus a a e de las declaraciones) al cdigo que debe ejecutarse siempre que se entra a la rutina de anlisis. o a Cualquier otro texto sangrado o %{} en la seccin de reglas sigue copindose a la salida, pero o a su signicado no est bien denido y bien podr causar errores en tiempo de compilacin (esta a a o propiedad se presenta para conformidad con POSIX; para otras caracter sticas similares) ver Cap tulo 18 [Incompatibilidades con lex y POSIX], pgina 44) a En la seccin de deniciones (pero no en la seccin de reglas), un comentario sin sangr (es o o a decir, una l nea comenzando con "/*") tambin se copia literalmente a la salida hasta el prximo e o "*/".

Cap tulo 5: Patrones

6

5 PatronesLos patrones en la entrada se escriben utilizando un conjunto extendido de expresiones regulares. Estas son: x . [xyz] [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] [^A-Z\n] cualquier caracter EXCEPTO una letra may scula o una l u nea nueva r* r+ r? r{2,5} r{2,} r{4} {nombre} la expansin de la denicin de "nombre" (ver ms abajo) o o a "[xyz]\"foo" la cadena literal: [xyz]"foo \x \0 \123 \x2a (r) si x es una a, b, f, n, r, t, o v, entonces la interpretacin ANSI-C de \x. o En otro caso, un literal x (usado para indicar operadores tales como *) un caracter NUL (cdigo ASCII 0) o el caracter con valor octal 123 el caracter con valor hexadecimal 2a empareja una R; los parntesis se utilizan para anular la precedencia (ver ms abajo) e a cero o ms rs, donde r es cualquier expresin regular a o una o ms rs a cero o una r (es decir, "una r opcional") donde sea de dos a cinco rs dos o ms rs a exactamente 4 rs 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. u empareja el caracter x cualquier caracter (byte) excepto una l nea nueva una "clase de caracteres"; en este caso, el patrn empareja una x, una y, o una o z

rs

la expresin regular r seguida por la expresin regular s; se denomina "concateo o nacin" o

r|s

bien una r o una s

Cap tulo 5: Patrones

7

r/s

una r pero slo si va seguida por una s. El texto emparejado por s se incluye o cuando se determina si esta regla es el "emparejamiento ms largo", pero se devuelve a entonces a la entrada antes que se ejecute la accin. As que la accin slo ve el o o o texto emparejado por r. Este tipo de patrones se llama "de contexto posterior". (Hay algunas combinaciones de r/s que ex no puede emparejar correctamente. Ver Cap tulo 21 [Deciencias / Errores], pgina 50, las notas a cerca del "contexto a posterior peligroso".) una r, pero slo al comienzo de una l o nea (es decir, justo al comienzo del anlisis, o a a la derecha despus de que se haya analizado una l e nea nueva). una r, pero slo al nal de una l o nea (es decir, justo antes de una l nea nueva). Equivalente a "r/\n". F jese que la nocin de ex de una "l o nea nueva" es exctamente lo que el compilador a de C utilizado para compilar ex interprete como \n; en particular, en algunos sistemas DOS debe ltrar los \rs de la entrada used mismo, o expl citamente usar r/\r\n para "r$". una r, pero slo en la condicin de arranque s (Ver Cap o o tulo 9 [Condiciones de arranque], pgina 16, para una discusin sobre las condiciones de arranque) a o

^r r$

r

r lo mismo, pero en cualquiera de las condiciones de arranque s1, s2, o s3 r una r en cualquier condicin de arranque, incluso una exclusiva. o un n-de-chero

un n-de-chero en una condicin de arranque s1 o s2 o F jese que dentro de una clase de caracteres, todos los operadores de expresiones regulares pierden su signicado especial excepto el caracter de escape (\) y los operadores de clase de caracteres, -, ], y, al principio de la clase, ^. Las expresiones regulares en el listado anterior estn agrupadas de acuerdo a la precedena cia, desde la precedencia ms alta en la cabeza a la ms baja al nal. Aquellas agrupadas a a 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 concatenacin, y la concatenacin ms o o a alta que el operador |. Este patrn por lo tanto empareja bien la cadena "foo" o la cadena o "ba" seguida de cero o ms rs. Para emparejar "foo" o, cero o ms "bar"s, use: a a foo|(bar)* y para emparejar cero o ms "foo"s o "bar"s: a (foo|bar)* Adems de caracteres y rangos de caracteres, las clases de caracteres pueden tambin contener a e expresiones de clases de caracteres. Son expresiones encerradas entre los delimitadores [: y :] (que tambin deben aparecer entre el [ y el ] de la clase de caracteres; adems pueden e a darse otros elementos dentro de la clase de caracteres). Las expresiones vlidas son: a [:alnum:] [:alpha:] [:blank:] [:cntrl:] [:digit:] [:graph:] [:lower:] [:print:] [:punct:] [:space:] [:upper:] [:xdigit:]

Cap tulo 5: Patrones

8

Todas estas expresiones designan un conjunto de caracteres equivalentes a la correspondiente funcin estndar isXXX de C. Por ejemplo, [:alnum:] designa aquellos caracteres para los o a cuales isalnum() devuelve verdadero es decir, cualquier caracter alfabtico o numrico. e e Algunos sistemas no ofrecen isblank(), as que ex dene [: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 escner ignora la distincin entre may sculas y min sculas (la bandera -i), entonces a o u u [: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 a 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 cmo muchas de las otras herramientas de expresiones regulares tratan las clases o de caracteres negadas, pero desafortunadamente la inconsistencia est fervientemente ena rraizada histricamente. Emparejar l o neas nuevas signica que un patrn como [^"]* puede o emparejar la entrada completa a menos que haya otra comilla en la entrada. - Una regla puede tener lo ms una instancia del contexto posterior (el operador / o el a operador $). La condicin de arranque, los patrones ^, y "" pueden aparecer o solamente al principio de un patrn, y, al igual que con / y $, no pueden agruparse dentro o de parntesis. Un ^ que no aparezca al principio de una regla o un $ que no aparezca al e nal de una regla pierde sus propiedades especiales y es tratado como un caracter normal. Lo siguiente no est permitido: a foo/bar$ foobar 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 accin especial | se explica en la Cap o tulo 7 [Acciones], pgina 10.): a foo | bar$ /* la accin va aqu */ o Un truco parecido funcionar para emparejar un "foo" o, un "bar" al principio de una l a nea.

Cap tulo 6: Cmo se empareja la entrada o

9

6 Cmo se empareja la entrada oCuando el escner generado est funcionando, este analiza su entrada buscando cadenas que a a concuerden con cualquiera de sus patrones. Si encuentra ms de un emparejamiento, toma el a que empareje ms texto (para reglas de contexto posterior, se incluye la longitud de la parte a posterior, incluso si se devuelve a la entrada). Si encuentra dos o ms emparejamientos de la a misma longitud, se escoge la regla listada en primer lugar en el chero 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 a en la variable global entera yyleng. Entonces la accin correspondiente al patrn emparejado o o se ejecuta (Ver Cap tulo 7 [Acciones], pgina 10, para una descripcin ms detallada de las a o a acciones), y entonces la entrada restante se analiza para otro emparejamiento. Si no se encuentra un emparejamiento, entonces se ejecuta la regla por defecto: el siguiente caracter en la entrada se considera reconocido y se copia a la salida estndar. As la entrada a , vlida ms simple de flex es: a a %% que genera un escner que simplemente copia su entrada (un caracter a la vez) a la salida. a F jese que yytext se puede denir de dos maneras diferentes: bien como un puntero a caracter o como un array de caracteres. Usted puede controlar la denicin que usa flex incluyendo o una de las directivas especiales %pointer o %array en la primera seccin (deniciones) de su o entrada de ex. Por defecto es %pointer, a menos que use la opcin de compatibilidad -l, o en cuyo caso yytext ser un array. a La ventaja de usar %pointer es un anlisis substancialmente ms rpido y la ausencia de a a a desbordamiento del buer cuando se emparejen tokens muy grandes (a menos que se agote la memoria dinmica). La desventaja es que se encuentra restringido en cmo sus acciones pueden a o modicar yytext (ver Cap tulo 7 [Acciones], pgina 10), y las llamadas a la funcin unput() a o 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 modicar yytext todo lo que usted quiera, las llamadas a unput() no destruyen yytext (ver ms abajo). Adems, los programas de lex a a existentes a veces acceden a yytext externamente utilizando declaraciones de la forma: extern char yytext[]; Esta denicin es errnea cuando se utiliza %pointer, pero correcta para %array. o o %array dene a yytext como un array de YYLMAX caracteres, que por defecto es un valor bastante grande. Usted puede cambiar el tama o s n mplemente deniendo con #dene a YYLMAX con un valor diferente en la primera seccin de su entrada de flex. Como se mencion antes, con o o %pointer yytext crece dinmicamente para acomodar tokens grandes. Aunque esto signique a que con %pointer su escner puede acomodar tokens muy grandes (tales como emparejar a bloques enteros de comentarios), tenga presente que cada vez que el escner deba cambiar el a tama o de yytext tambin debe reiniciar el anlisis del token entero desde el principio, as n e a que emparejar tales tokens puede resultar lento. Ahora yytext no crece dinmicamente si una a llamada a unput() hace que se deba devolver demasiado texto; en su lugar, se produce un error en tiempo de ejecucin. o Tambin tenga en cuenta que no puede usar %array en los analizadores generados como e clases de C++ (ver Cap tulo 17 [Generando Escneres en C++], pgina 40). a a

Cap tulo 7: Acciones

10

7 AccionesCada patrn en una regla tiene una accin asociada, que puede ser cualquier sentencia en C. o o El patrn naliza en el primer caracter de espacio en blanco que no sea una secuencia de escape; o lo que queda de la l nea es su accin. Si la accin est vac entonces cuando el patrn se o o a a, o empareje el token de entrada simplemente se descarta. Por ejemplo, aqu est la especicacin a o 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 sern emparejados por a a 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 nal de una l nea: %% [ \t]+ putchar( ); [ \t]+$ /* ignora este token */ Si la accin contiene un {, entonces la accin abarca hasta que se encuentre el correspono o diente }, y la accin podr entonces cruzar varias l o a 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, a n pero aun as tambin permite que las acciones comiencen con %{ y considerar que la accin e a o es todo el texto hasta el siguiente %} (sin tener en cuenta las llaves ordinarias dentro de la accin). o Una accin que consista slamente de una barra vertical (|) signica "lo mismo que la o o accin para la siguiente regla." Vea ms abajo para una ilustracin. o a o Las acciones pueden incluir cdigo C arbitrario, incuyendo sentencias return para devolver o un valor desde cualquier rutina llamada yylex(). Cada vez que se llama a yylex() esta contin a procesando tokens desde donde lo dej la ultima vez hasta que o bien llegue al nal u o del chero o ejecute un return. Las acciones tienen libertad para modicar yytext excepto para alargarla (a adiendo caracn teres al nalesto sobreescribir ms tarde caracteres en el ujo de entrada). Sin embargo esto a a no se aplica cuando se utiliza %array (ver Cap tulo 6 [Cmo se empareja la entrada], pgina 9); o a en ese caso, yytext podr modicarse libremente de cualquier manera. a Las acciones tienen libertad para modicar yyleng excepto que estas no deber hacerlo si an la accin tambin incluye el uso de yymore() (ver ms abajo). o e a Hay un n mero de directivas especiales que pueden incluirse dentro de una accin: u o - ECHO copia yytext a la salida del escner. a - BEGIN seguido del nombre de la condicin de arranque pone al escner en la condicin de o a o arranque correspondiente (ver Cap tulo 9 [Condiciones de arranque], pgina 16). a - REJECT ordena al escner a que proceda con la "segunda mejor" regla que concuerde con la a entrada (o un prejo de la entrada). La regla se escoge como se describi anteriormente en el o Cap tulo 6 [Cmo se Empareja la Entrada], pgina 9, y yytext e yyleng se ajustan de forma o a apropiada. Podr ser una que empareje tanto texto como la regla escogida originalmente a pero que viene ms tarde en el chero de entrada de flex, o una que empareje menos texto. a Por ejemplo, lo que viene a continuacin contar las palabras en la entrada y llamar a la o a a rutina especial() siempre que vea "frob": int contador_palabras = 0; %% frob [^ \t\n]+ especial(); REJECT; ++contador_palabras;

Cap tulo 7: Acciones

11

Sin el REJECT, cualquier n mero de "frob"s en la entrada no ser contados como pau an labras, ya que el escner normalmente ejecuta solo una accin por token. Se permite el a o uso de m ltiples REJECTs, cada uno buscando la siguiente mejor eleccin a la regla que u o actualmente est activa. Por ejemplo, cuando el siguiente escner analice el token "abcd", e a este escribir "abcdabcaba" a la salida: a %% a | ab | abc | abcd ECHO; REJECT; .|\n /* se come caracteres sin emparejar */ (Las primeras tres reglas comparten la accin de la cuarta ya que estas usan la accin o o especial |.) REJECT es una propiedad particularmente cara en trminos de rendimiento e del escner; si se usa en cualquiera de las acciones del escner esta ralentizar todo el a a a proceso de emparejamiento del escner. Adems, REJECT no puede usarse con las opciones a a -Cf o -CF (ver Seccin 15.2 [Opciones], pgina 28 y Cap o a tulo 16 [Consideraciones de rendimiento], pgina 35.) a F jese tambin que a diferencia de las otras acciones especiales, REJECT es una bifurcacin; e o el cdigo que la siga inmediatamente en la accin no ser ejecutado. o o a - yymore() dice al escner que la prxima vez que empareje una regla, el token correspona o diente debe ser aadido tras el valor actual de yytext en lugar de reemplazarlo. Por ejemplo, n dada la entrada "mega-klugde" lo que viene a continuacin escribir "mega-mega-kludge" o a a la salida: %% megaECHO; 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 u a "kludge" realmente escribir "mega-kludge". a Dos notas respecto al uso de yymore(). Primero, yymore() depende de que el valor de yyleng reeje correctamente el tama o del token actual, as que no debe modicar yyleng si n est utilizando yymore(). Segundo, la presencia de yymore() en la accin del escner implica a o a una peque a penalizacin de rendimiento en la velocidad de emparejamiento del escner. n o a - yyless(n) devuelve todos excepto los primeros n caracteres del token actual de nuevo al ujo de entrada, donde sern reanalizados cuando el escner busque el siguiente emparejaa a miento. yytext e yyleng se ajustan de forma adecuada (p.ej., yyleng no ser igual a n). a Por ejemplo, con la entrada "foobar" lo que viene a continuacin escribir "foobarbar": o a %% foobar ECHO; yyless(3); [a-z]+ ECHO; Un argumento de 0 para yyless har que la cadena de entrada actual sea analizada por a completo de nuevo. A menos que haya cambiado la manera en la que el escner procese de a ahora en adelante su entrada (utilizando BEGIN, por ejemplo), esto producir un bucle sin a n. F jese que yyless es una macro y puede ser utilizada solamente en el chero de entrada de ex, no desde otros cheros fuente. - unput(c) pone el caracter c de nuevo en el ujo de entrada. Este ser el prximo caracter a o analizado. La siguiente accin tomar el token actual y har que se vuelva a analizar pero o a a encerrado entre parntesis. e {

Cap tulo 7: Acciones

12

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 ujo de entrada, al devolver cadenas de caracteres se debe hacer de atrs hacia delante. a Un problema potencial importante cuando se utiliza unput() es que si est usando a %pointer (por defecto), una llamada a unput() destruye el contenido de yytext, comenzando con su caracter ms a la derecha y devorando un caracter a la izquierda con cada a llamada. Si necesita que se preserve el valor de yytext despus de una llamada a unput() e (como en el ejemplo anterior), usted debe o bien copiarlo primero en cualquier lugar, o construir su escner usando %array (ver Cap a tulo 6 [Cmo se Empareja la Entrada], pgina 9). o a Finalmente, note que no puede devolver EOF para intentar marcar el ujo de entrada con un n-de-chero. - input() lee el prximo caracter del ujo de entrada. Por ejemplo, lo que viene a contio nuacin es una manera de comerse los comentarios en C: o %% "/*" { 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 */ o } if ( c == EOF ) { error( "EOF en comentario" ); break; } } } (F jese que si el escner se compila usando C++, entonces a input() se le hace referencia a con yyinput(), para evitar una colisin de nombre con el ujo de C++ por el nombre o input.)

Cap tulo 7: Acciones

13

a a o - YY FLUSH BUFFER vac el buer interno del escner de manera que la prxima vez que el escner intente emparejar un token, este primero rellenar el buer usando YY_INPUT a a (ver Cap tulo 8 [El Escner Generado], pgina 14). Esta accin es un caso especial de la a a o funcin ms general yy_flush_buffer(), descrita ms abajo en el Cap o a a tulo 10 [M ltiples u Buers de Entrada], pgina 21. a - yyterminate() se puede utilizar en lugar de una sentencia de retorno en una accin. Esta o hace que nalice el escner y retorne un 0 a quien haya llamado al escner, indicando que a a "todo est hecho". Por defecto, tambin se llama a yyterminate() cuando se encuentra a e un n-de-chero. Esta es una macro y podr ser redenida. a

Cap tulo 8: El escner generado a

14

8 El escner generado aLa salida de flex es el chero lex.yy.c, que contiene la rutina de anlisis yylex(), un a n mero de tablas usadas por esta para emparejar tokens, y un n mero de rutinas auxiliares y u u 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 a a denicin podr modicarse deniendo la macro "YY DECL". Por ejemplo, podr utilizar: o a #define YY_DECL float lexscan( a, b ) float a, b; para darle a la rutina de anlisis el nombre lexscan, que devuelve un real, y toma dos reales a como argumentos. F jese que si pone argumentos a la rutina de anlisis usando una declaracin a o de funcin no-prototipada/tipo-K&R, debe hacer terminar la denicin con un punto y coma o o (;). Siempre que se llame a yylex(), este analiza tokens desde el chero de entrada global yyin (que por defecto es igual a stdin). La funcin contin a hasta que alcance el nal del chero o u (punto en el que devuelve el valor 0) o una de sus acciones ejecute una sentencia return. Si el escner alcanza un n-de-chero, entonces el comportamiento en las llamadas posteriores a est indenido a menos que o bien yyin apunte a un nuevo chero de entrada (en cuyo caso el a anlisis contin a a partir de ese chero), o se llame a yyrestart(). yyrestart() toma un a u 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 chero. Esencialmente no hay diferencia entre la asignacin a yyin de un nuevo chero de entrada o el uso de yyrestart() o para hacerlo; esto ultimo est disponible por compatibilidad con versiones anteriores de flex, a y porque puede utilizarse para conmutar cheros de entrada en medio del anlisis. Tambin se a e puede utilizar para desechar el buer de entrada actual, invocndola con un argumento igual a a yyin; pero mejor es usar YY_FLUSH_BUFFER (ver Cap tulo 7 [Acciones], pgina 10). F a jese que yyrestart() no reinicializa la condicin de arranque a INITIAL (ver Cap o tulo 9 [Condiciones de arranque], pgina 16). a Si yylex() para el anlisis debido a la ejecucin de una sentencia return en una de las a o acciones, el analizador podr ser llamado de nuevo y este reanudar el anlisis donde lo dej. a a a o Por defecto (y por razones de eciencia), 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 denienfo la macro YY_INPUT. La secuencia de llamada para YY INPUT es "YY INPUT(buf,result,max size)". Su accin es poner hasta max size caracteres o en el array de caracteres buf y devolver en la variable entera result bien o el n mero de caracteres u 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 chero "yyin". Una denicin de ejemplo para YY INPUT (en la seccin de deniciones del chero de o o entrada) es: %{ #define YY_INPUT(buf,result,max_size) \ { \ int c = getchar(); \ result = (c == EOF) ? YY_NULL : (buf[0] = c, 1); \ } %} Esta denicin cambiar el procesamiento de la entrada para que suceda un caracter a la o a vez.

Cap tulo 8: El escner generado a

15

Cuando el analizador reciba una indicacin de n-de-chero desde YY INPUT, entonces esta o comprueba la funcin yywrap(). Si yywrap() devuelve falso (cero), entonces se asume que o la funcin ha ido ms all y ha preparado yyin para que apunte a otro chero de entrada, y o a a el anlisis contin a. Si este retorna verdadero (no-cero), entonces el analizador termina, devola u viendo un 0 a su invocador. F jese que en cualquier caso, la condicin de arranque permanece o sin cambios; esta no vuelve a ser INITIAL. Si no proporciona su propia versin de yywrap(), entonces debe bien o usar %option o noyywrap (en cuyo caso el analizador se comporta como si yywrap() devolviera un 1), o debe enlazar con -lfl para obtener la versin por defecto de la rutina, que siempre devuelve un 1. o Hay disponibles tres rutinas para analizar desde buers de memoria en lugar de desde cheros: yy_scan_string(), yy_scan_bytes(), e yy_scan_buffer(). Las trataremos en la Cap tulo 10 [M ltiples Buers de Entrada], pgina 21. El analizador escribe su salida con u a ECHO a la variable global yyout (por defecto, stdout), que el usuario podr redenir asignndole a a cualquier otro puntero a FILE.

Cap tulo 9: Condiciones de arranque

16

9 Condiciones de arranqueflex dispone de un mecanismo para activar reglas condicionalmente. Cualquier regla cuyo patrn se preje con "" unicamente estar activa cuando el analizador se encuentre en la o a condicin de arranque llamada "sc". Por ejemplo, o [^"]* { /* se come el cuerpo de la cadena ... */ ... } estar activa solamente cuando el analizador est en la condicin de arranque "STRING", y a e o \. { /* trata una secuencia de escape ... */ ... } estar activa solamente cuando la condicin de arranque actual sea o bien "INITIAL", a o "STRING", o "QUOTE". Las condiciones de arranque se declaran en la (primera) seccin de deniciones de la entrada o usando l neas sin sangrar comenzando con %s o %x seguida por una lista de nombres. Lo primero declara condiciones de arranque inclusivas, lo ultimo condiciones de arranque exclusi vas. Una condicin de arranque se activa utilizando la accin BEGIN. Hasta que se ejecute la o o prxima accin BEGIN, las reglas con la condicin de arranque dada estarn activas y las reglas o o o a con otras condiciones de arranque estarn inactivas. Si la condicin de arranque es inclusiva, a o entonces las reglas sin condiciones de arranque tambin estarn activas. Si es exclusiva, entonces e a slamente las reglas calicadas con la condicin de arranque estarn activas. Un conjunto de o o a reglas dependientes de la misma condicin de arranque exclusiva describe un analizador que es o independiente de cualquiera de las otras reglas en la entrada de flex. Debido a esto, las condiciones de arranque exclusivas hacen fcil la especicacin de "mini-escneres" que analizan a o a porciones de la entrada que son sintcticamente diferentes al resto (p.ej., comentarios). a Si la distincin entre condiciones de arranque inclusivas o exclusivas es a n un poco vaga, o u aqu hay un ejemplo simple que ilustra la conexin entre las dos. El conjunto de reglas: o %s ejemplo %% foo bar es equivalente a %x ejemplo %% foo hacer_algo(); hacer_algo(); algo_mas();

bar algo_mas(); Sin el calicador , el patrn bar en el segundo ejemplo no estar activo o a (es decir, no puede emparejarse) cuando se encuentre en la condicin de arranque example. Si o hemos usado para calicar bar, aunque, entonces este unicamente estar activo a en example y no en INITIAL, mientras que en el primer ejemplo est activo en ambas, porque a en el primer ejemplo la condicin de arranque example es una condicin de arranque inclusiva o o (%s). F jese tambin que el especicador especial de la condicin de arranque empareja todas e o las condiciones de arranque. As el ejemplo anterior tambin pudo haberse escrito; , e

Cap tulo 9: Condiciones de arranque

17

%x 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 estn a activas. Este estado tambin puede referirse a la condicin de arranque "INITIAL", as que e o BEGIN(INITIAL) es equivalente a BEGIN(0). (No se requieren los parntesis alrededor del e nombre de la condicin de arranque pero se considera de buen estilo.) o Las acciones BEGIN pueden darse tambin como cdigo sangrado al comienzo de la seccin e o o de reglas. Por ejemplo, lo que viene a continuacin har que el analizador entre en la condicin o a o 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); blablabla ...ms reglas a continuacin... a o 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 a 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 unico token, el n mero en coma a u otante 123.456: %{ #include %} %s espera %% espera-reales BEGIN(espera);

[0-9]+"."[0-9]+ { printf( "encontr un real, = %f\n", o atof( yytext ) ); } \n { /* este es el final de la lnea, * as que necesitamos otro * "espera-numero" antes de * que volvamos a reconocer ms a * nmeros u */ BEGIN(INITIAL);

Cap tulo 9: Condiciones de arranque

18

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

"." printf( "encontr un punto\n" ); o Aqu est un analizador que reconoce (y descarta) comentarios de C mientras mantiene una a cuenta de la l nea actual de entrada. %x comentario %% int num_linea = 1; "/*" BEGIN(comentario);

[^*\n]* /* come todo lo que no sea * */ "*"+[^*/\n]* /* come *s no seguidos por / */ \n ++num_linea; "*"+"/" 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 ms que pueda, ya que esto es un buen logro. a F jese que los nombres de las condiciones de arranque son realmente valores enteros y pueden ser almacenados como tales. As lo anterior podr extenderse de la siguiente manera: , a %x comentario foo %% int num_linea = 1; int invocador_comentario; "/*" { invocador_comentario = INITIAL; BEGIN(comentario); }

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

[^*\n]* /* se come cualquier cosa que no sea un * */ "*"+[^*/\n]* /* se come *s que no continuen con / */ \n ++num_linea; "*"+"/" BEGIN(invocador_comentario); Adems, puede acceder a la condicin de arranque actual usando la macro de valor entero a o YY_START. Por ejemplo, las asignaciones anteriores a invocador_comentario podr escribirse an en su lugar como invocador_comentario = YY_START;

Cap tulo 9: Condiciones de arranque

19

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 %ss y %xs declaran nombres de la misma manera que con #denes. Finalmente, aqu hay un ejemplo de cmo emparejar cadenas entre comillas al estilo de C o usando condiciones de arranque exclusivas, incluyendo secuencias de escape expandidas (pero sin incluir la comprobacin de cadenas que son demasiado largas): o %x str %% char string_buf[MAX_STR_CONST]; char *string_buf_ptr;

\" \"

string_buf_ptr = string_buf; BEGIN(str); { /* se vio la comilla que cierra - todo est hecho */ a BEGIN(INITIAL); *string_buf_ptr = \0; /* devuelve un tipo de token de cadena constante y * el valor para el analizador sintctico a */ } { /* error - cadena constante sin finalizar */ /* genera un mensaje de error */ }

\n

\\[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; } \\[0-9]+ { /* genera un error - secuencia de escape errnea; o * algo como \48 o \0777777 */ } \\n \\t \\r \\b \\f *string_buf_ptr++ *string_buf_ptr++ *string_buf_ptr++ *string_buf_ptr++ *string_buf_ptr++ = = = = = \n; \t; \r; \b; \f;

Cap tulo 9: Condiciones de arranque

20

\\(.|\n)

*string_buf_ptr++ = yytext[1];

[^\\\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 u de reglas todas precedidas por la(s) misma(s) condicin(es) de arranque. Flex hace esto un poco o ms fcil y claro introduciendo la nocin de ambito de la condicin de arranque. Un ambito de a a o o condicin de arranque comienza con: o { Donde SCs es una lista de una o ms condiciones de arranque. Dentro del ambito de la condicin a o de arranque, cada regla automticamente tiene el prejo aplicado a esta, hasta un } a que corresponda con el { inicial. As por ejemplo, , { "\\n" return \n; "\\r" return \r; "\\f" return \f; "\\0" return \0; } es equivalente a: "\\n" return \n; "\\r" return \r; "\\f" return \f; "\\0" return \0; Los ambitos de las condiciones de arranque pueden anidarse. Estn disponibles tres rutinas para manipular pilas de condiciones de arranque: a void yy_push_state(int new_state) empuja la condicin de arranque actual al tope de la pila de las condiciones de arrano que y cambia a new state como si hubiera utilizado BEGIN new_state (recuerde que los nombres de las condiciones de arranque tambin son enteros). e 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 dinmicamente y por ello no tiene asociada a ninguna limitacin de tama o. Si la memoria se agota, se aborta la ejecucin del programa. o n o Para usar pilas de condiciones de arranque, su analizador debe incluir una directiva %option stack (ver Seccin 15.2 [Opciones], pgina 28). o a

Cap tulo 10: M ltiples buers de entrada u

21

10 M ltiples buers de entrada uAlgunos analizadores (tales como aquellos que aceptan cheros "incluidos") requieren la lectura de varios ujos de entrada. Ya que los analizadores de flex hacen mucho uso de buers, uno no puede controlar de dnde ser le la siguiente entrada escribiendo s o a da mplemente un YY_ INPUT que sea sensible al contexto del anlisis. A YY_INPUT slo se le llama cuando el analizador a o alcanza el nal de su buer, que podr ser bastante tiempo despus de haber analizado una a e 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 buers de entrada. Un buer 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 buer asociado con el chero dado n y lo sucientemente grande para mantener size caracteres (cuando dude, use YY_BUF_SIZE para el tama o). Este devuelve un handle YY_BUFFER_STATE, que podr pasarse a otras rutinas (ver n a ms abajo). El tipo de YY_BUFFER_STATE es un puntero a una estructura opaca struct yy_ a buffer_state, de manera que podr inicializar de forma segura variables YY_BUFFER_STATE a a ((YY_BUFFER_STATE) 0) si lo desea, y tambin hacer referencia a la estructura opaca para e declarar correctamente buers de entrada en otros cheros fuente adems de los de su analizador. a 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 redene YY_INPUT de manera que no use ms a yyin, a entonces puede pasar de forma segura un puntero FILE nulo a yy_create_buffer. Se selecciona un buer en particular a analizar utilizando: void yy_switch_to_buffer( YY_BUFFER_STATE nuevo_buffer ) conmuta el buer de entrada del analizador de manera que los tokens posteriores provienen jese que yy_switch_to_buffer() podr usarlo yywrap() para arreglar las a de nuevo buer. F cosas para un anlisis continuo, en lugar de abrir un nuevo chero y que yyin apunte a este. a F jese tambin que cambiar las fuentes de entrada ya sea por medio de yy_switch_to_buffer() e o de yywrap() no cambia la condicin de arranque. o void yy_delete_buffer( YY_BUFFER_STATE buffer ) se usa para recuperar el almacenamiento asociado a un buer. (El buffer puede ser nulo, en cuyo caso la rutina no hace nada.) Puede tambin limpiar el contenido actual de un buer e usando: void yy_flush_buffer( YY_BUFFER_STATE buffer ) Esta funcin descarta el contenido del buer, de manera que la prxima vez que el analizador o o intente emparejar un token desde el buer, este primero rellenar el buer utilizando YY_INPUT. a 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 dinmicos. a Finalmente, la macro YY_CURRENT_BUFFER retorna un handle YY_BUFFER_STATE al buer actual. Aqu hay un ejemplo del uso de estas propiedades para escribir un analizador que expande cheros incluidos (la propiedad se comenta en el Cap tulo 11 [Reglas de n-de-chero], pgina 24): a /* 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];

Cap tulo 10: M ltiples buers de entrada u

22

int include_stack_ptr = 0; %} %% include [a-z]+ [^a-z\n]*\n? BEGIN(incl); ECHO; ECHO;

[ \t]* /* se come los espacios en blanco */ [^ \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); } { 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 buers de entrada para el anlisis de cadenas en a memoria en lugar de archivos. Todas estas crean un nuevo buer de entrada para analizar la cadena, y devuelven el correspondiente handle YY_BUFFER_STATE (que usted deber borrar a con yy_delete_buffer() cuando termine con l). Estas tambin conmutan el nuevo buer e e usando yy_switch_to_buffer(), de manera que la prxima llamada a yylex() comenzar o a analizando la cadena.

Cap tulo 10: M ltiples buers de entrada u

23

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 NULs) comenzando desde el punto bytes. F jese que ambas de estas funciones crean y analizan una copia de la cadena o bytes. (Esto podr ser deseable, ya que yylex() modica el contenido del buer que est analizado.) Usted a a puede evitar la copia utilizando: yy_scan_buffer(char *base, yy_size_t size) que analiza in situ el buer comenzando en base, que consiste de size bytes, donde los dos ultimos bytes deben ser YY_END_OF_BUFFER_CHAR (ASCII NUL). Es tos dos ultimos bytes no se analizan; as el anlisis consta de base[0] hasta , a base[size-2], inclusive. Si se equivoca al disponer base de esta manera (es decir, olvidar los dos YY_END_ OF_BUFFER_CHAR bytes nales), entonces yy_scan_buffer() devuelve un puntero nulo en lugar de crear un nuevo buer de entrada. El tipo yy_size_t es un tipo entero con el que puede hacer una conversin a una o expresin entera para reejar el tama o del buer. o n

Cap tulo 11: Reglas de n-de-chero

24

11 Reglas de n-de-cheroLa regla especial "" indica las acciones que deben tomarse cuando se encuentre un n-de-chero e yywrap() retorne un valor distinto de cero (es decir, indica que no quedan cheros por procesar). La accin debe nalizar haciendo una de estas cuatro cosas: o - asignando a yyin un nuevo chero de entrada (en versiones anteriores de ex, despus de e hacer la asignacin deb llamar a la accin especial YY_NEW_FILE; esto ya no es necesario); o a o - ejecutando una sentencia return; - ejecutando la accin especial yyterminate(); o - o, conmutando a un nuevo buer usando yy_switch_to_buffer() como se mostr en el o ejemplo anterior. Las reglas no deber usarse con otros patrones; estas deber calicarse con an an una lista de condiciones de arranque. Si se da una regla sin calicar, esta se aplica a todas las condiciones de arranque que no tengan ya acciones . Para especicar una regla solamente para la condicin de arranque inicial, use o Estas reglas son utiles para atrapar cosas tales como comentarios sin nal. Un ejemplo: %x comilla %% ...otras reglas que tengan que ver con comillas... { error( "comilla sin cerrar" ); yyterminate(); } { if ( *++filelist ) yyin = fopen( *filelist, "r" ); else yyterminate(); }

Cap tulo 12: Macros miscelneas a

25

12 Macros miscelneas aLa macro YY_USER_ACTION puede denirse para indicar una accin que siempre se ejecuta o antes de la accin de la regla emparejada. Por ejemplo, podr declararse con #dene para que o a llame a una rutina que convierta yytext a min sculas. Cuando se invoca a YY_USER_ACTION, la u variable yy_act da el n mero de la regla emparejada (las reglas estn numeradas comenzando u a en 1). Suponga que quiere medir la frecuencia con la que sus reglas son emparejadas. Lo que viene a continuacin podr hacer este truco: o a #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 u -s), as que una declaracin correcta para ctr es: o int ctr[YY_NUM_RULES]; La macro YY_USER_INIT podr denirse para indicar una accin que siempre se ejecuta antes a o del primer anlisis (y antes de que se haga la inicializacin interna del analizador). Por ejemplo, a o este podr usarse para llamar a una rutina que lea una tabla de datos o abrir un chero de a registro. La macro yy_set_interactive(is_interactive) se puede usar para controlar si el buer actual se considera interactivo. Un buer interactivo se procesa ms lentamente, pero debe a usarse cuando la fuente de entrada del analizador es realmente interactiva para evitar problemas debidos a la espera para el llenado de los buers (ver el comentario de la bandera -I en la Seccin 15.2 [Opciones], pgina 28). Un valor distinto de cero en la invocacin de la macro o a o marcar el buer como interactivo, un valor de cero como no-interactivo. F a jese que el uso de esta macro no tiene en cuenta %option always-interactive o %option never-interactive (ver Seccin 15.2 [Opciones], pgina 28). yy_set_interactive() debe invocarse antes del o a comienzo del anlisis del buer que es considerado (o no) interactivo. a La macro yy_set_bol(at_bol) puede usarse para controlar si el contexto del buer de anlisis actual para el prximo emparejamiento de token se hace como si se encontrara al prina o cipio 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 prximo token analizado a partir del buer o actual tendr activas las reglas ^, de otra manera falso. a En el analizador generado, las acciones estn recogidas en una gran sentencia switch y sepaa radas usando YY_BREAK, que puede ser redenida. Por defecto, este es s mplemente un "break", para separar la accin de cada regla de las reglas que le siguen. Redeniendo YY_BREAK permite, o por ejemplo, a los usuarios de C++ que #dene YY BREAK no haga nada (mientras tengan cuidado para que cada regla nalice con un "break" o un "return"!) para evitar que sufran los avisos de sentencias inalcanzables cuando debido a que la accin de la regla naliza con un o "return", el YY_BREAK es inaccesible.

Cap tulo 13: Valores disponibles al usuario

26

13 Valores disponibles al usuarioEsta seccin resume los diferentes valores disponibles al usuario en las acciones de la regla. o - char *yytext apunta al texto del token actual. Este puede modicarse pero no alargarse (no puede a adir caracteres al nal). n Si aparece la directiva especial %array en la primera seccin de la descripcin del analizao o dor, entonces yytext se declara en su lugar como char yytext[YYLMAX], donde YYLMAX es la denicion de una macro que puede redenir en la primera seccin si no le gusta el o valor por defecto (generalmente 8KB). El uso de %array produce analizadores algo ms a 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 chero por el que flex lee por defecto. Este podr redenirse pero a hacerlo solo tiene sentido antes de que el anlisis comience o despus de que se haya ena e contrado un EOF. Cambindolo en medio del anlisis tendr resultados inesperados ya que a a a flex utiliza buers en su entrada; use yyrestart() en su lugar. Una vez que el anlisis a termina debido a que se ha visto un n-de-chero, puede asignarle a yyin el nuevo chero de entrada y entonces llamar al analizador de nuevo para continuar analizando. - void yyrestart( FILE *new_file ) podr ser llamada para que yyin apunte al nuevo a chero de entrada. El cambio al nuevo chero es inmediato (cualquier entrada contenida en el buer previamente se pierde). F jese que llamando a yyrestart() con yyin como argumento de esta manera elimina el buer de entradda actual y contin a analizando el u mismo chero de entrada. - FILE *yyout es el chero 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 buer actual. - YY_START devuelve un valor entero correspondiente a la condicin de arranque actual. Poso teriormente puede usar este valor con BEGIN para retornar a la condicin de arranque. o

Cap tulo 14: Interfaz con YACC

27

14 Interfaz con YACCUno de los usos principales de flex es como compa ero del generador de analizadores n sintcticos yacc. Los analizadores de yacc esperan invocar a una rutina llamada yylex() a para encontrar el prximo token de entrada. La rutina se supone que devuelve el tipo del o prximo token adems de poner cualquier valor asociado en la variable global yylval. Para o a usar flex con yacc, uno especica la opcin -d de yacc para intruirle a que genere el chero o y.tab.h que contiene las deniciones 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 parecerse a: a %{ #include "y.tab.h" %} %% [0-9]+ yylval = atoi( yytext ); return TOK_NUMERO;

Cap tulo 15: Invocando a Flex

28

15 Invocando a Flex15.1 Sinopsisflex [-bcdfhilnpstvwBFILTV78+? -C[aefFmr] -osalida -Pprefijo -Sesqueleto] [--help --version] [nombrechero ...]

15.2 Opcionesflex tiene las siguientes opciones: -b Genera informacin de retroceso en lex.backup. Esta es una lista de estados del o 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 n de retroceso se eliminan y se usa -Cf o -CF, el analizador generado funcionar a ms rpido (ver la bandera -p). Unicamente los usuarios que desean exprimir a a hasta el ultimo ciclo de sus analizadores necesitan preocuparse de esta opcin. (ver o Cap tulo 16 [Consideraciones de Rendimiento], pgina 35) a es una opcin que no hace nada, inclu para cumplir con POSIX. o da hace que el analizador generado se ejecute en modo de depuracin. Siempre que se o reconoce un patrn y la variable global yy_flex_debug no es cero (que por defecto o no lo es), el analizador escribir en stderr una l a nea de la forma: --accepting rule at line 53 ("el texto emparejado") El n mero de l u nea hace referencia al lugar de la regla en el chero que dene al analizador (es decir, el chero que se le introdujo a ex). Los mensajes tambin e se generan cuando el analizador retrocede, acepta la regla por defecto, alcanza el nal de su buer de entrada (o encuentra un NUL; en este punto, los dos parecen lo mismo en lo que le concierne al analizador), o alcance el n-de-chero. especica un analizador rpido. No se realiza una compresin de tablas y se evita a o el uso de stdio. El resultado es grande pero rpido. Esta opcin es equivalente a a o -Cfr (ver ms abajo). a genera un sumario de "ayuda" de las opciones de flex por stdout y entonces naliza. -? y --help son sinnimos de -h. o indica a flex que genere un analizador case-insensitive. Se ignorar si las letras en a los patrones de entrada de flex son en may sculas o en min sculas, y los tokens en u u la entrada sern emparejados sin tenerlo en cuenta. El texto emparejado dado en a yytext tendr las may sculas y min sculas preservadas (es decir, no se convertirn). a u u a activa el modo de mxima compatibilidad con la implementacin original de lex a o de AT&T. F jese que esto no signica una compatibilidad completa. El uso de esta opcin cuesta una cantidad considerable de rendimiento, y no puede usarse o con las opciones -+, -f, -F, -Cf, o -CF. Para los detalles a cerca de la compatibilidad que se ofrece, vea la Cap tulo 18 [Incompatibilidades con lex y POSIX], pgina 44. Esta opcin tambin hace que se dena el nombre YY_FLEX_ a o e LEX_COMPAT en el analizador generado. es otra opcin que no hace nada, inclu para cumplir con POSIX. o da genera un informe de rendimiento en stderr. El informe consta de comentarios que tratan de las propiedades del chero de entrada de flex que provocarn prdidas a e serias de rendimiento en el analizador resultante. Si indica esta bandera dos veces,

-c -d

-f

-h -i

-l

-n -p

Cap tulo 15: Invocando a Flex

29

tambin obtendr comentarios que tratan de las propiedades que producen prdidas e a e menores de rendimiento. F jese que el uso de REJECT, %option yylineno, y el contexto posterior variable (ver Cap tulo 21 [Deciencias / Errores], pgina 50) supone una penalizacin subsa o tancial 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 opcin es util para encontrar o agujeros en el conjunto de reglas del analizador. indica a flex que escriba el analizador que genera a la salida estndar en lugar de a en lex.yy.c. especica que flex deber escribir en stderr un sumario de estad a sticas respecto al analizador que genera. La mayor de las estad a sticas no tienen signicado para el usuario casual de flex, pero la primera l nea identica la versin de flex (la misma o que se informa con -V), y la prxima l o nea las banderas utilizadas cuando se genera el analizador, incluyendo aquellas que se encuentran activadas por defecto. suprime los mensajes de aviso. dice a flex que genere un analizador batch, que es lo opuesto al analizador interactivo generador por -I (ver ms abajo). En general, use -B cuando est seguro de que a e su analizador nunca se usar de forma interactiva, y quiere con esto exprimir un a poco ms el rendimiento. Si por el contrario su objetivo es exprimirlo mucho ms, a a deber estar utilizando la opcin -Cf o -CF (comentadas ms abajo), que activa a o a -B automticamente de todas maneras. a especica que se debe utilizar la representacin de la tabla rpida (y elimina refeo a rencias a stdio). Esta representacin es aproximadamente tan rpida como la reo a presentacin completa de la tabla (-f), y para algunos conjuntos de patrones ser o a considerablemente ms peque a (y para otros, mayor). En general, si el conjunto de a n patrones contiene "palabras clave" y una regla "identicador" atrpalo-todo, como a 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 representacin de la tabla completa. Si slo a o o est presente la regla "identicador" y utiliza una tabla hash o algo parecido para a detectar palabras clave, mejor utilice -F. Esta opcin es equivalente a -CFr (ver ms abajo). Esta opcin no puede utilizarse o a o con -+. ordena a flex que genere un analizador interactivo. Un analizador interactivo es uno que solo mira hacia delante para decidir que token ha sido reconocido unicamente si debe hacerlo. Resulta que mirando siempre un caracter extra hacia delante, incluso si el analizador ya ha visto suciente texto para eliminar la ambig edad del u token actual, se es un poco ms rpido que mirando solamente cuando es necesario. a a 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 signica introducir otra l nea completa.

-t -v

-w -B

-F

-I

Cap tulo 15: Invocando a Flex

30

Los analizadores de flex por defecto son interactivos a menos que use la opcin o -Cf o -CF de compresin de tablas (ver ms abajo). Esto es debido a que si est o a a buscando un rendimiento alto tendr que estar utilizando una de estas opciones, as a que si no lo ha hecho flex asume que preere cambiar un poco de rendimiento en tiempo de ejecucin en benecio de un comportamiento iteractivo intuitivo. F o jese tambin que no puede utilizar -I conjuntamente con -Cf o -CF. As esta opcin e , o no se necesita realmente; est activa por defecto para todos esos casos en los que se a permite. Usted puede forzar al analizador que no sea interactivo usando -B (ver ms arriba). a -L ordena a flex que no genere directivas #line. Sin esta opcin, flex acribilla al o analizador generado con directivas #line para que los mensajes de error en las acciones estn localizadas correctamente respecto al chero original de flex (si los e errores son debidos al cdigo en el chero de entrada), o a lex.yy.c (si los errores o son fallos de flex deber informar de este tipo de errores a la direccin de correo a o dada ms abajo). a -T hace que flex se ejecute en modo de traza. Este generar un montn de mensajes a o en stderr relativos a la forma de la entrada y el autmata nito no-determinista o o determinista resultante. Esta opcin generalmente es para usarla en el mantenio miento de flex. -V imprime el n mero de la versin en stdout y sale. --version es un sinnimo de u o o -V. -7 ordena a flex que genere un analizador de 7-bits, es decir, uno que slo puede o 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 n usando la opcin -8 (ver ms abajo). La desventaja es que tales analizadores a o 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 compresin de tablas -Cf o -CF, el uso de -7 ahorrar solamente una peque a o a n cantidad de espacio en la tabla, y har su analizador considerablemente menos a 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 congurado para generar analizadores e de 8-bits (a menudo este ser el caso de los sistemas fuera de EEUU). Puede decir a si ex gener un analizador de 7 u 8 bits inspeccionando el sumario de banderas en o la salida de -v como se describi anteriormente. o F jese que si usa -Cfe o -CFe (esas opciones de compresin de tablas, pero tambin o e el uso de clases de equivalencia como se comentar ms abajo), ex genera a n a a u por defecto un analizador de 8-bits, ya que normalmente con estas opciones de compresin las tablas de 8-bits completas no son mucho ms caras que las tablas de o a 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 slo es necesaria para analizadores generados o usando -Cf o -CF, ya que de otra manera ex por defecto genera un analizador de 8-bits de todas formas. Vea el comentario sobre -7 ms arriba a cerca del comportamiento por defecto de a ex y la discusin entre los analizadores de 7-bits y 8-bits. o -+ especica que quiere que ex genere un analizador como una clase de C++. Vea la Cap tulo 17 [Generando Escneres en C++], pgina 40, para los detalles. a a -C[aefFmr] controla el grado de compresin de la tabla y, ms generalmente, el compromiso o a entre analizadores peque os y analizadores rpidos. n a

Cap tulo 15: Invocando a Flex

31

-Ca

("alinea") ordena a ex que negocie tablas ms grandes en el analizador generado a para un comportamiento ms rpido porque los elementos de las tablas estn mejor a a a alineados para el acceso a memoria y computacin. En algunas arquitecturas RISC, o la b squeda y manipulacin de palabras largas es ms eciente que con unidades u o a ms peque as tales como palabras cortas. Esta opcin puede doblar el tama o de a n o n las tablas usadas en su analizador. ordena a flex que construya clases de equivalencia, es decir, conjunto de caracteres que tienen identicas propiedades lxicas (por ejemplo, si la unica aparicin de d e o gitos en la entrada de flex es en la clase de caracteres "[0-9]" entonces los d gitos 0, 1, . . . , 9 se pondrn todos en la misma clase de equivalencia). Las clases de equivalena cia normalmente ofrecen notables reducciones en los tama os de los cheros nales n de tabla/objeto (t picamente un factor de 2-5) y son juiciosamente bastante baratos en cuanto al rendimiento (una localizacin en un vector por caracter analizado). o -Cf especica que se deben generar las tablas del analizador completas flex no deber comprimir las tablas tomando ventaja de las funciones de transicin a o similares para diferentes estados. -CF especica que deber usarse la representacin del analizador rpido alternativo a o a (descrito anteriormente en la bandera -F ) Esta opcin no puede usarse con -+. o ordena a flex que construya clases de meta-equivalencias, que son conjuntos de clases de equivalencia (o caracteres, si las clases de equivalencia no se estn usando) a 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 localizacin en un array por o caracter analizado). -Cr hace que el analizador generado elimine el uso de la librer de E/S estndar a a para la entrada. En lugar de llamar a fread() o a getc(), el analizador utilizar la llamada al sistema read(), produciendo una ganancia en el rendimiento a que var de sistema en sistema, pero en general probablemente es insignicante a a menos que tambin est usando -Cf o -CF. El uso de -Cr puede producir un e e comportamiento extra o si, por ejemplo, lee de yyin usando stdio antes de llamar al n analizador (porque el analizador perder cualquier texto que sus lecturas anteriores a dejaron en el buer de entrada de stdio). -Cr no tiene efecto si usted dene YY_INPUT (ver Cap tulo 8 [El Escner Generado], a pgina 14). a Con solamente -C se especica que las tablas del analizador deber comprian mirse pero no deber utilizarse ni las clases de equivalencia ni las clases de metaa 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 a forma las opciones podr mezclarse l an bremente, y son acumulativas. La conguracin por defecto es -Cem, que especica que flex deber generar o a clases de equivalencia y clases de meta-equivalencias. Esta conguracin provee el o mayor grado de compresin. Puede llegarse a un compromiso entre analizadores de o ejecucin ms rpida con el coste de tablas mayores siendo generalmente verdadero o a a lo siguiente: lo ms lento y peque~o a n -Cem -Cm -Ce -C -C{f,F}e

-Ce

-Cm

Cap tulo 15: Invocando a Flex

32

-C{f,F} -C{f,F}a lo ms rpido y grande a a F jese que los analizadores con tablas ms peque as normalmente se generan y coma n pilan de la forma ms rpida posible, as que durante el desarrollo usted normalmente a a querr usar como viene por defecto, compresin mxima. a o a -Cfe a menudo es un buen compromiso entre velocidad y tama o para la pron duccin de analizadores. o -osalida ordena a ex que escriba el analizador al chero salida en lugar de a lex.yy.c. Si combina -o con la opcin -t, entonces el analizador se escribe en stdout pero o sus directivas #line (vea la opcin -L ms arriba) hacen referencia al chero o a salida. -Pprefijo cambia el prejo yy usado por defecto por flex para todas las variables visibles globalmente y nombres de funciones para que sea prejo. Por ejemplo, -Pfoo cambia el nombre de yytext a footext. Este tambin cambia el nombre por e defecto del chero de salida de lex.yy.c a lex.foo.c. Aqu estn todos los a 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 unicamente yywrap y a yyFlexLexer se ven afectados.) Dentro de su analizador, puede a n hacer refeu rencia a las variables globales y funciones usando cualquier versin de su nombre; o pero externamente, estas tienen el nombre modicado. Esta opcin le deja enlazar fcilmente m ltiples programas flex conjuntamente o a u en el mismo ejecutable. F jese, sin embargo, que usando esta opcin tambin se o e renombra yywrap(), de manera que ahora debe o bien proveer su propia versin de o la rutina (con el nombre apropiado) para su analizador, o usar %option noyywrap, ya que enlazar con -lfl no podr proveerle una por defecto. a -Sfichero_esqueleto ignora el chero de esqueleteo por defecto con el que flex construye sus analizadores. Usted probablemente nunca necesitar utilizar esta opcin a menos que este haciendo a o mantenimiento o un desarrollo de flex. flex tambin ofrece un mecanismo para controlar las opciones dentro de la propia especie cacin del analizador, en vez de a partir de la l o nea de comando. Esto se hace incluyendo las directivas %option en la primera seccin de la especicacin del analizador. Usted puede o o

Cap tulo 15: Invocando a Flex

33

especicar varias opciones con una sola directiva %option, y varias directivas en la primera seccin de su chero de entrada de ex. o La mayor de las opciones vienen dadas simplemente como nombres, opcionalmente precea didos por la palabra "no" (sin intervenir un espacio) para negar su signicado. Las banderas de ex o su negacin son equivalentes a un n mero: o u 7bit opcin -7 o 8bit opcin -8 o align opcin -Ca o backup opcin -b o batch opcin -B o c++ opcin -+ o caseful o case-sensitive

opuesto de -i (por defecto)

case-insensitive o caseless opcin -i o debug default ecs fast full interactive lex-compat meta-ecs perf-report read stdout verbose warn opcin -d o opuesto de la opcin -s o opcin -Ce o opcin -F o opcin -f o opcin -I o opcin -l o opcin -Cm o opcin -p o opcin -Cr o opcin -t o opcin -v o opuesto de la opcin -w o (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 estn disponibles: a always-interactive ordena a ex que genere un analizador que siempre considere su entrada como "interactiva". Normalmente, sobre cada chero de entrada nuevo el analizador llama a isatty() como intento para determinar si la entrada del analizador es interactiva y por lo tanto deber leer un caracter a la vez. Cuando esta opcin se a o utilice, sin embargo, entonces no se hace tal llamada. main ordena a ex que facilite un programa main() por defecto para el analizador, que simplemente llame a yylex(). Esta opcin implica noyywrap (ver ms abajo). o a

never-interactive ordena a ex 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 Cap tulo 9 [Condiciones de arranque], pgina 16). a

Cap tulo 15: Invocando a Flex

34

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 compilacin. o ordena a flex a generar un analizador que mantenga el n mero de la l u nea actual le desde su entrada en la variable global yylineno. Esta opcin viene impl da o cita con %option lex-compat.

yylineno

yywrap

si no se establece (es decir, %option noyywrap), hace que el analizador no llame a yywrap() hasta el n-de-chero, pero simplemente asume que no hay ms cheros a que analizar (hasta que el usuario haga apuntar yyin a un nuevo chero 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 estn disponibles para ignorar sus decisiones a siempre que use las opciones, o bien estableciendolas (p.ej., %option reject) para indicar que la propiedad se utiliza realmente, o desactivndolas para indicar que no es utilizada (p.ej., a %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" slo se aplica cuando se genera un analizador en C++ (opcin -+). Este informa a flex que o o ha derivado a foo como una subclase de yyFlexLexer, as que flex pondr sus acciones en a la funcin miembro foo::yylex() en lugar de yyFlexLexer::yylex(). Este tambin genera o e una funcin miembro yyFlexLexer::yylex() que emite un error en tiempo de ejecucin (invoo o cando a yyFlexLexer::LexerError()) si es llamada. Ver Cap tulo 17 [Generando Escneres a en C++], pgina 40, para informacin adicional. a o Estn disponibles un n mero de opciones para los puristas de lint que desean suprimir la a u aparicin de rutinas no necesarias en el analizador generado. Cada una de la siguientes, si o 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 aparecern de todas manera a menos que use a %option stack).

Cap tulo 16: Consideraciones de rendimiento

35

16 Consideraciones de rendimientoEl principal objetivo de dise o de flex es que genere analizadores de alto rendimiento. Este n ha sido optimizado para comportarse bien con conjuntos grandes de reglas. Aparte de los efectos sobre la velocidad del analizador con las opciones de compresin de tablas -C anteriormente o introducidas, hay un n mero de opciones/acciones que degradan el rendimiento. Estas son, u desde la ms costosa a la menos: a REJECT %option yylineno contexto posterior arbitrario conjunto de patrones que requieren retroceso %array %option interactive %option always-interactive ^ operador de comienzo de lnea yymore() siendo las tres primeras bastante costosas y las dos ultimas bastante econmicas. F o jese tambin que unput() se implementa como una llamada de rutina que potencialmente hace e bastante trabajo, mientras que yyless() es una macro bastante econmica; as que si est o a devolviendo alg n texto excedente que ha analizado, use yyless(). u REJECT deber evitarse a cualquier precio cuando el rendimiento es importante. Esta es una a opcin particularmente cara. o Es lioso deshacerse del retroceso y a menudo podr ser una cantidad de trabajo enorme para a 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 chero tiene el siguiente aspecto: El estado #6 es no-aceptar nmeros de lnea asociados a la regla: u 2 3 fin de transiciones: [ o ] transiciones de bloqueo: fin de archivo (EOF) [ \001-n p-\177 ] El estado #8 es no-aceptar nmeros de lnea asociados a la regla: u 3 fin de transiciones: [ a ] transiciones de bloqueo: fin de archivo (EOF) [ \001- El estado #9 es no-aceptar nmeros de lnea asociados a la regla: u 3 fin de transiciones: [ r ] transiciones de bloqueo: fin de archivo (EOF) [ \001-q Las tablas comprimidas siempre implican un retroceso.

b-\177 ]

s-\177 ]

Cap tulo 16: Consideraciones de rendimiento

36

Las primeras l neas nos dicen que hay un estado del analizador en el que se puede hacer una transicin con una o pero no sobre cualquier otro caracter, y que en ese estado el texto o 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 chero de entrada. Si el analizador est en ese estado y entoces lee cualquier cosa que no sea una o, tendr que retroceder para a a encontrar una regla que empareje. Con un poco de anlisis uno puede ver que este debe ser el a estado en el que se est cuando se ha visto "fo". Cuando haya ocurrido, si se ve cualquier cosa a que no sea una o, el analizador tendr que retroceder para simplemente emparejar la f (por a 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 a 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 nal 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 hacindolo con analizadores comprimidos. e La manera de quitar los retrocesos es a adiendo reglas de "error": n %% foo return TOK_KEYWORD; foobar return TOK_KEYWORD; | | { /* falsa alarma, realmente no es una palabra clave */ return TOK_ID; } La eliminacin de retroceso en una lista de palabras clave tambin puede hacerse utilizando o e una regla "atrpalo-todo": a %% foo return TOK_KEYWORD; foobar return TOK_KEYWORD; [a-z]+ return TOK_ID; Normalmente esta es la mejor solucin cuando sea adecuada. o 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, u a menudo slo hay que tomar una docena de reglas o algo as para eliminar los retrocesos (ya o que es fcil cometer una equivocacin y tener una regla de error que reconozca un token vlido. a o a Una posible caracter stica futura de flex ser a adir reglas automticamente para eliminar el a n a retroceso). Es importante tener en cuenta que se obtienen los benecios de eliminar el retroceso slo si o elimina cada instancia del retroceso. Dejar solamente una signica que no ha ganado absolutamente nada. El contexto posterior variable (donde la parte delantera y posterior no tienen una longitud ja) supone casi la misma prdida de rendimiento que REJECT (es decir, substanciales). As que e cuando sea posible una regla como esta: %% raton|rata/(gato|perro) correr(); es mejor escribirla as : fooba foob fo

Cap tulo 16: Consideraciones de rendimiento

37

%% raton/gato|perro rata/gato|perro o as

correr(); correr();

%% raton|rata/gato correr(); raton|rata/perro correr(); F jese que aqu la accin especial | no ofrece ning n ahorro, y puede incluso hacer las cosas o u peor (ver Cap tulo 21 [Deciencias / Errores], pgina 50). a Otro area donde el usuario puede incrementar el rendimiento del analizador (y una que es ms a fcil de implementar) surge del hecho que cuanto ms tarde se empareje un token, ms rpido a a a a ir el analizador. Esto es debido a que con tokens grandes el procesamiento de la mayor de los a a caracteres de entrada tiene lugar en el (corto) bucle de anlisis ms interno, y no tiene que ir a a tan a menudo a hacer el trabajo de ms para constituir el entorno del analizador (p.ej., yytext) a para la accin. Recuerde el analizador para los comentarios en C: o %x comentario %% int num_linea = 1; "/*" BEGIN(comentario);

[^*\n]* "*"+[^*/\n]* \n ++num_linea; "*"+"/" BEGIN(INITIAL); Esto podr acelerarse escribindolo como: a e %x comentario %% int num_linea = 1; "/*" BEGIN(comentario);

[^*\n]* [^*\n]*\n ++num_linea; "*"+[^*/\n]* "*"+[^*/\n]*\n ++num_linea; "*"+"/" 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! n La velocidad del analizador es independiente del n mero de reglas o (dadas las consideraciones u dadas al inicio de esta seccin) cun complicadas sean las reglas respecto a operadores tales o a como * y |. Un ejemplo nal sobre la aceleracin de un analizador: suponga que quiere analizar un chero o que contiene identicadores y palabras clave, una por l nea y sin ning n caracter extra o, y u n reconocer todas las palabras clave. Una primera aproximacin natural es: o %% asm | auto | break |

Cap tulo 16: Consideraciones de rendimiento

38

... etc ... volatile | while /* es una palabra clave */ .|\n /* no es una palabra clave */

Para eliminar el retroceso, introduzca una regla atrpalo-todo: a %% asm | auto | break | ... etc ... volatile | while /* es una palabra clave */ [a-z]+ .|\n | /* no es una palabra clave */

Ahora, si se garantiza que hay exctamente una palabra por l a nea, entonces podemos reducir el n mero total de emparejamientos por la mitad mezclando el reconocimiento de l u 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 habrn otros caracteres en el ujo de a entrada que no sean letras o l neas nuevas, flex no puede gurarse eso, y planear la posible a necesidad de retroceder cuando haya analizado un token como "auto" y el prximo caracter o sea algo distinto a una l nea nueva o una letra. Previamente este podr entonces emparejar la a 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 nal, o, ya que nunca esperamos encontrar tal entrada y por lo tanto ni cmo es clasicada, podemos introducir una regla atrpalo-todo ms, esta que no incluye una o a a 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 */

Cap tulo 16: Consideraciones de rendimiento

39

Compilado con -Cf, esto es casi tan rpido como lo que uno puede obtener de un analizador a de flex para este problema en particular. Una nota nal: flex es lento cuando empareja NULs, particularmente cuando un token contiene m ltiples NULs. Es mejor escribir reglas que emparejen cortas cantidades de texto si u se anticipa que el texto incluir NULs a menudo. a Otra nota nal en relacin con el rendimiento: tal y como se mencion en el Cap o o tulo 6 [Cmo se Empareja la Entrada], pgina 9, el reajuste dinmico de yytext para acomodar tokens o a a 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 intentar emparejar "grandes" a cantidades de texto pero no "inmensas" cantidades, donde el punto medio est en torno a los a 8K caracteres/token.

Cap tulo 17: Generando escneres en C++ a

40

17 Generando escneres en C++ aflex 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 encontrarse ante ning n error de compilacin (por a u o favor informe de cualquier error que encuentre a la direccin de correo electrnico dada en el o o Cap tulo 23 [Autor], pgina 52). Puede entonces usar cdigo C++ en sus acciones de las reglas a o en lugar de cdigo C. F o jese que la fuente de entrada por defecto para su analizador permanece como yyin, y la repeticin por defecto se hace a n a yyout. Ambos permanecen como variables o u FILE * y no como flujos de C++. Tambin puede utilizar flex para generar un analizador como una clase de C++, utilizando la e opcin -+ (o, equivalentemente, %option c++), que se especica automticamente si el nombre o a del ejecutable de ex naliza con un +, tal como flex++. Cuando se usa esta opcix, ex o establece por defecto la generacin del analizador al chero lex.yy.cc en vez de lex.yy.c. o El analizador generado incluye el chero de cabecera FlexLexer.h, que dene el interfaz con las dos clases de C++. La primera clase, FlexLexer, ofrece una clase base abstracta deniendo la interfaz a la clase del analizador general. Este provee las siguientes funciones miembro: const char* YYText() retorna el texto del token reconocido ms recientemente, el equivalente a yytext. a int YYLeng() retorna la longitud del token reconocido ms recientemente, el equivalente a yyleng. a int lineno() const retorna el n mero de l u nea de entrada actual (ver %option yylineno), o 1 si no se us %option yylineno. o void set_debug( int flag ) activa la bandera de depuracin para el analizador, equivalente a la asignacin de o o yy_flex_debug (ver Seccin 15.2 [Opciones], pgina 28). F o a jese que debe construir el analizador utilizando %option debug para incluir informacin de depuracin en o o este. int debug() const retorna el estado actual de la bandera de depuracin. o Tambin se proveen funciones miembro equivalentes a yy_switch_to_buffer(), yy_create_buffer() e (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 denida en FlexLexer.h es yyFlexLexer, que se deriva de FlexLexer. Esta dene las siguientes funciones miembro adicionales: yyFlexLexer( istream* arg_yyin = 0, ostream* arg_yyout = 0 ) construye un objeto yyFlexLexer usando los ujos dados para la entrada y salida. Si no se especica, los ujos se establecen por defecto a cin y cout, respectivamente. virtual int yylex() hace el mismo papel que yylex() en los analizadores de ex ordinarios: analiza el ujo de entrada, consumiendo tokens, hasta que la accin de una regla reo torne 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 utilia zando esa subclase en lugar de yyFlexLexer. Es este caso, en vez de generar

Cap tulo 17: Generando escneres en C++ a

41

yyFlexLexer::yylex(), flex genera S::yylex() (y tambin genera un subse tituto 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 buer de entrada anterior si se reasigna yyin. int yylex( istream* new_in, ostream* new_out = 0 ) primero conmuta el ujo de entrada via switch_streams( new_in, new_out ) y entonces retorna el valor de yylex(). Adems, yyFlexLexer dene las siguientes funciones virtuales protegidas que puede redenir a 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 u dos. Para indicar el n-de-la-entrada, devuelve 0 caracteres. F jese que los analizadores "interactivos" (ver las banderas -B y -I) denen la macro YY_INTERACTIVE. Si usted redene LexerInput() y necesita tomar acciones distintas dependiendo de si el analizador est analizando una fuente de entrada interactivo o no, puede comprobar a la presencia de este nombre mediante #ifdef. virtual void LexerOutput( const char* buf, int size ) escribe a la salida size caracteres desde el buer buf, que, mientras termine en NUL, puede contener tambin NULs "internos" si las reglas del analizador pueden e emparejar texto con NU