Para facilitar la compresión de la incorporación del
comportamiento no determinista, vamos a utilizar un pequeño ejemplo.
Como en castellano hay un gran número de reglas léxicas, lo que nos
obligaría a definir numerosas condiciones de arranque para crear un
ejemplo mínimamente real, hemos sonsiderado más conveniente
utilizar como ejemplo un pequeño analizador léxico en el que se muestra
cómo reconocer todas las variantes de la palabra inglesa
bellow. Este ha sido el ejemplo sobre el que hemos trabajado con Vern
Paxon. La palabra bellow tienen cuatro entradas en un
diccionario:
El problema surge al tener que reconocer una palabra como
bellows,
que puede ser:
Para ser capaces de tratar con estas definiciones, debemos crear dos condiciones de arranque, que son las siguientes:
Para ello utilizamos la siguiente línea de código, en la que también incluimos la declaración de la condición E de error:
/* Start conditions */ %x E N V
A continiuación se definen los patrones de los separadores y de los no separadores:
/* Separators */ sep [" ",;:\.\t\n] /* Non separators */ nosep [^" ",;:\.\t\n]
Algo muy importante es declarar las variables enteras match_no y semaphore:
%{ unsigned int match_no, semaphore; %}
A continuación ya se pasa a escribir las reglas léxicas con el no determinismo incorporado, no sin antes haber escrito un pequeño trozo de código en el cual se establece a cero el valor de las dos variables anteriores. Esta porción de código situado después de %% y encerrada entre %{ y %} se ejecutrá cada vez que se llame a la función yylex.
%% %{ /* Code executed each time yylex is called */ match_no = 0; semaphore = 0; %} /* Error condition: Panic mode */ <E>{nosep}*/{sep} { printf("%s -> error \n\n", yytext); BEGIN(INITIAL);RETURN(1); } /* Verbs */ <V>s/{sep} printf("%s -> verb, 3rd \n",yytext);REJECT; <V>ed/{sep} printf("%s -> verb, past \n",yytext);REJECT; <V>ing/{sep} printf("%s -> verb, gerund \n",yytext);REJECT; <V>""/{sep} printf("%s -> verb, infinitive \n",yytext);REJECT; <V>""/. yyless(0); BEGIN(INITIAL); /* Nouns */ <N>s/{sep} printf("%s -> noun, plural \n",yytext);REJECT; <N>""/{sep} printf("%s -> noun, singular \n",yytext);REJECT; <N>""/. yyless(0);BEGIN(INITIAL); /* WORDS */ bellow { if ( semaphore!=0) { semaphore--; REJECT; }; match_no++; semaphore=match_no; yymore(); BEGIN(V); } bellow { if ( semaphore!=0) { semaphore--; REJECT; }; match_no++; semaphore=match_no; yymore(); BEGIN(N); } bellows { if ( semaphore!=0) { semaphore--; REJECT; }; match_no++; semaphore=match_no; yymore(); BEGIN(N); } /* Separators */ " " | \t | \n | "," | "," | ";" | ":" | "." printf("%s -> separator \n\n'', yytext);return(1); . yymore(); BEGIN (E); <<EOF>> yyterminate(); %%
Mediante la utilización de las dos líneas iniciales de código de todas las reglas con comportamiento no determinista
if ( semaphore!=0) { semaphore--; REJECT; } match_no++;semaphore=match_no;se comprueba si el valor del semáforo es correcto. Si no lo es, se realiza un REJECT, con lo cual el resto de las acciones de esa regla no se ejecutan. En caso contrario, se actualizan las variables convenientemente. Para facilitar la escritura de reglas en Flex, se puede definir una macro NDFLEX que contenga esas instrucciones, que van a ser iguales en todas las reglas que se quiere que participen del comportamiento no determinista.
La última regla permite evitar que el reconocedor genere errores cuando se intenta llamar a yylex y ya se ha alcanzado el fin de fichero. Puede ser eliminada y el programa seguirá funcionando, pero se recomienda que sea incluida.
En este ejemplo, por sencillez, no se ha utilizado la condición de arranque S, sino que yylex devuelve el control cuando encuentra un separador. En un programa real los espacios, tabuladores y retornos de carro se ignorarían y las marcas de puntuación se reconocerían como componentes léxicos.
Aunque el ejemplo se ha puesto en inglés por cuestiones de legibilidad, en castellano se dan muchos más casos de ambigüedades. Por ejemplo, la palabra para puede ser reconocida como:
Existen muchos casos como éste, pero para mostrarlos es necesario escibir muchas reglas debido a la complejidad de la conjugación verbal, por lo que son poco adecuados para un ejemplo didáctico.