Flex es, según su definición una herramienta para ayudar al programador a generar un analizador léxico, palabra un tanto compleja que aleja a muchos de su compañía.
Sin embargo, flex es una potente herramienta para el programador, ya que permite la generación de rutinas, cuya escritura manual requiere pasar largo tiempo escribiendo, y largo tiempo depurando.
Veamos un ejemplo:
Supongamos que queremos construir una rutina, que lea los caracteres devueltos por una línea RS-232 a la que tenemos enchufado un modem, pero que filtre los códigos devueltos por el modem (por ejemplo CONNECT o NO CARRIER) entregando códigos especiales para estos casos.
La solución clara, sería tener una rutina que devuelva códigos enteros, reservando los 256 primeros códigos para cualquier posible caracter válido y del 257 en adelante, para los códigos de error entregados por el modem.
Lo primero que nos viene a la mente es que el diseño de un autómata reconocedor de cadenas 1 de un modo eficiente (que de una sola pasada por el texto, que tenga un número de estados mínimo, que ocupe poca memoria, que esté libre de errores, etc.) 2 es muy dificil de conseguir, pero muy facil si se sabe un poco de expresiones regulares 3
En el caso de las respuestas de un modem, tendremos las siguientes cadenas,
que serán tratadas especialmente (y de una sola pasada) por el autómata
generado por flex(1)
:
El programa siguiente nos mostrará el código de una rutina, que leyendo de la entrada estandar, permite reconocer las cadenas anteriores y devolverá códigos especiales, cuando estas cadenas se presenten a la entrada.
%% BUSY\n { /* La cadena literal BUSY\n */ return 257; } NO\ *CARRIER\n { /* La cadena NO, seguida de un número arbitrario de * caracteres espacio en blanco (incluido cero), * seguida de la palabra CARRIER\n */ return 258; } CONNECT(\ *[0-9]+)?\n { /* La palabra CONNECT, seguida, opcionalmente, de * una subexpresion regular formada por espacios (igual * que antes), seguidos de un número arbitrario de * dígitos. */ return 259; } ERROR\n return 260; . return yytext[0]; /* cualquier cadena de un caracter, no reconocida anteriormente */ %% |
Cuando ejecutemos flex sobre este código, se generará un fichero fuente C
con una rutina, llamada yylex()
, que leerá la entrada
estandar, y retornará códigos enteros con los valores de los caracteres
introducidos, o los códigos descritos en el programa, para los casos de
reconocimiento de las cadenas devueltas por el modem. Básicamente, la rutina
yylex()
tiene una tabla de doble entrada (estado
actual, caracter leido) en la que encontramos el siguiente estado, y la
ejecución del código C intercalado en las llaves, que se ejecuta cuando
se reconoce una de las cadenas programadas.
Ventajas:
Inconvenientes:
stdin
o cuando debemos analizar datos interactivos (como la entrada en modo
raw del terminal, por ejemplo en un programa calculadora) la
adaptación del fuente flex, para que la entrada se haga sin buffer, o para
poder cambiar el fichero de entrada se hace un tanto oscura o compleja.
:-)
.
regcomp(3)
, regexec(3)
, etc.)
NO\ +CARRIER\ndonde se aceptan uno o mas espacios en blanco.
http://SLUG.HispaLinux.ES/~luis/