Para que se pueda comprender mejor la implementación de las reglas léxicas, vamos a mostrar un pequeño ejemplo. Se trata de reconocer los sustantivos que forman el género mediante G1 y el número mediante N1.
Lo primero que debemos hacer es definir las condiciones de arranque en la
sección inicial de declaraciones del programa Flex. Para nuestro
ejemplo, es suficiente con definir G1, N1 y E. esta última
constituye la condición de error a la cual se enviará el autómata
del reconocedor siempre que una palabra no puede ser reconocida. Esto
se traduce en código de la siguiente manera:
/* Error */ %x E /* gender */ %x G1 /* Number */ %x N1
Después definimos los dos patrones siguientes:
En las siguientes línea de código se definen estos dos
patrones:
/* Separator characters */ sep [" "\t,;:\.\n] /* Non-separator characters */ nosep [^" "\t,;:\.\n]]
Con esto ya podemos comenzar a definir reglas en la sección de reglas del programa Flex. Como ejemplo vamos a mostrar cómo se reconocen las palabras cuyos lemas son perro y gato.
Los lexemas de estas palabra son perr y gat, respectivamente. Como dijimos anteriormente, deberán ser utilizados como patrones en la parte izquierda de una regla sin condición de arranque, como se muestra a continuación:
gat | perr {yymore(); BEGIN G1; }
Mediante la llamada a la función yymore indicamos a Flex que
queremos seguir añadiendo en yytext los caracteres que sean
reconocidos a continuación.
Ahora es preciso definir la reglas mediante las cuales se reconoce el género de las palabras incluidas en el grupo G1. Estas palabras forman el masculino añadiendo una o al lexema y el femenino añadiendo una a. En el siguiente código se muestra la definición de tales reglas:
/* Terminations for gender: Case 1 */ <G1>o {/* Masculine */ printf(" masculine"); yymore(); BEGIN N1: } <G1>a {/* Feminine */ printf(" feminine"); yymore(); BEGIN N1; }
El prefijo <G1> antes de los patrones o y a indica que ambas reglas sólo se activarán cuando la condición de arranque G1 haya sido activada mediante BEGIN G1. Como G1 ha sido definida en modo exclusivo, después de BEGIN G1 únicamente estas dos reglas estarán activas.
Como todas las palabras que forman el género en G1 forman el número en N1, ambas reglas utilizan BEGIN N1 para activar la condición de arranque N1.
Las reglas para el reconocimiento del número son la siguientes:
/* Terminations for number: Case 1 */ <N1>s/{sep} {/* Plural */ printf(", plural --> %s",yytext); BEGIN(INITIAL) } <N1>""/{sep} {/* Singular */ printf(", singular --> %s", yytext); BEGIN(INITIAL); } <N1>. {/* Bad word. Error. */ yymore(); BEGIN E; }
La terminación /{sep} constituye un contexto de
cola. Se utiliza para indicar que
el patrón de esa regla deberá estar segido por un separador, pero
el carácter separador no es incluido en yytext y será
reconocido en patrones de reglas posteriores, pero no en la actual. Se
utiliza en estos patrones para evitar reconocer una palabra
incompleta.
La expresión "" se utiliza para indicar un carácter vacío, puesto que las palabras de N1 no añaden ningún carácter para formar el plural.
El patrón . empareja con cualquier carácter excepto con un avance de línea. Se utiliza para detectar palabras erróneas o aquellas que no han sido incluidas en el lexical. En las acciones de esta regla se utiliza yymore para que al activar el modo pánico, en yytext se almacene todo el texto no reconocido como válido. La regla del error es la siguiente:
<E>{nosep}*/{sep} {/* Panic mode. Recognise every character * until the next separator. */ printf(", ERROR --> %s", yytext); BEGIN(INITIAL); }
La acción BEGIN(INITIAL) hace que el reconocedor vuelva a las
reglas que no tienen condición de arranque.
Por último las siguientes reglas permiten tratar los separadores.
"" | \t | \n {/* separator non significatives */ ; } "," | ";" | ":" | "." {/* Puntuation marks */ printf(" Puntuation mark -> %s", yytext); BEGIN(INITIAL); }
Los espacios, tabuladores y caracteres de nueva línea se ignoran, pero las marcas de puntuación se reconocen como un componente léxico, ya que son significativas en el proceso de análisis sintáctico para detectar los límites de las oraciones.
En este ejemplo sólo se muestra por pantalla el género y el número. En el analizador real, se almacenan los datos en una estructura que se pasa al analizador sintáctico. El modo en que se enlazan los analizadores léxico y sintáctico se muestra en el capítulo 6.
Un aspecto importante que hay que tener en cuenta es el de la
definición de yytext. La variable yytext está definida por
defecto en Flex como un puntero a char. La ventaja de usar esta
definción es que no se producirán overflows en el buffer
cuando se reconozcan componentes léxicos muy largos, ya que Flex va asignando memoria a
yytext dinámicamente
. La desventaja es que no se puede
modificar el contenido de yytext, ya que los resultados que se
obtendrían serían impredecibles. Esto último implica que
no se pueden utilizar funciones como input y unput.
Se puede definir la variables yytext como un array mediante la
utilización de %array en la sección de declaraciones del
programa Flex. El tamaño viene determinado por el valor de
YYLMAX, originalmente 8 Kilobytes. Utilizando esta definicón se
puede modificar el valor de yytext sin problemas. El único
inconveniente que podría surgir sería el derivado del
límite de tamaño impuesto por el array. En nuestro caso no es
ningún problema, ya que no existen palabras de miles de
caracteres.