definirla como una secuencia de caracteres, que pueda hallarse codificada segn
cualquier estndar: ASCII (American Standard Code for Information Interchange),
EBCDIC (Extended Binary Coded Decimal Interchange Code), Unicode, etc. El
analizador lxico divide esta secuencia en palabras con significado propio y despus
las convierte a una secuencia de terminales desde el punto de vista del analizador
sintctico. Dicha secuencia es el punto de partida para que el analizador sintctico
construya el rbol sintctico que reconoce la/s sentencia/s de entrada.
La principal tarea del analizador lxico es leer los caracteres de la entrada del programa
fuente, agruparlos en lexemas y producir como salida una secuencia de tokens para
Como el analizador lxico es la parte del compilador que lee el texto de origen, debe
realizar otras tareas aparte de identificar lexemas. Una de esas tareas es eliminar los
La principal tarea del analizador lxico es leer los caracteres de la entrada del programa
fuente, agruparlos en lexemas y producir como salida una secuencia de tokens para
En otras palabras, por qu no se delega todo el procesamiento del programa fuente slo
en el anlisis sintctico, cosa perfectamente posible, ya que el sintctico trabaja con
Separar el anlisis lxico del anlisis sintctico a menudo permite simplificar una, otra
o ambas fases.
Las expresiones regulares son una notacin importante para especificar patrones de
lexemas. Aunque no pueden expresar todos los patrones posibles, son muy efectivas
para especificar los tipos de patrones que en realidad necesitamos para los tokens.
Una cadena sobre un alfabeto es una secuencia finita de smbolos que se extraen de ese
alfabeto. En la teora del lenguaje, los trminos oracin y palabra a menudo se
utilizan como sinnimos de cadena. La longitud de una cadena s, que por lo general
se escribe como |s|, es el nmero de ocurrencias de smbolos en s.
Un lenguaje es cualquier conjunto contable de cadenas sobre algn alfabeto fijo. Esta
definicin es demasiado amplia. Los lenguajes abstractos como , el conjunto vaco, o
{}, el conjunto que contiene slo la cadena vaca, son lenguajes bajo esta definicin.
Tambin lo son el conjunto de todos los programas en C que estn bien formados en
sentido sintctico, y el conjunto de todas las oraciones en ingls gramaticalmente
correctas, aunque estos ltimos dos lenguajes son difciles de especificar con exactitud.
ltimo, la cerradura positiva, denotada como L+, es igual que la cerradura de Kleene,
pero sin el trmino L0. Es decir, no estar en L+ a menos que est en el mismo L.
.
Una forma es que L y D son, respectivamente, los alfabetos de las letras maysculas y
minsculas, y de los dgitos. La segunda forma es que L y D son lenguajes cuyas
cadenas tienen longitud de uno.
2. LD es el conjunto de 520 cadenas de longitud dos, cada una de las cuales consiste en
una letra, seguida de un dgito.
3. L4 es el conjunto de todas las cadenas de 4 letras.
2. LD es el conjunto de 520 cadenas de longitud dos, cada una de las cuales consiste en
una letra, seguida de un dgito.
3. L4 es el conjunto de todas las cadenas de 4 letras.
nmero o cdigo identificador nico. En algunos casos, cada nmero tiene asociada
informacin adicional necesaria para las fases posteriores de la etapa de anlisis. El
concepto de token coincide directamente con el concepto de terminal desde el punto
Lexema: Es cada secuencia de caracteres concreta que encaja con un patrn. P.ej: 8",
23" y 50" son algunos lexemas que encajan con el patrn (0'|1'|2'| ... |9') . El
nmero de lexemas que puede encajar con un patrn + puede ser finito o infinito, p.ej.
en el patrn WHILE slo encaja el lexema WHILE.
Una vez detectado que un grupo de caracteres coincide con un patrn, se considera que
expresiones regulares para describir patrones de los tokens. La notacin de entrada para la
herramienta Lex se conoce como el lenguaje Lex, y la herramienta en s es el compilador
Lex. El compilador Lex transforma los patrones de entrada en un diagrama de transicin y
genera cdigo, en un archivo llamado lex.yy.c, que simula este diagrama de transicin.
es como una
subrutina del analizador sintctico. Es una funcin en C que devuelve un entero, el cual
representa un cdigo para uno de los posibles nombres de cada token. El valor del
atributo, ya sea otro cdigo numrico, un apuntador a la tabla de smbolos, o nada, se
coloca en una variable global llamada yylval,2 la cual se comparte entre el analizador
lxico y el analizador sintctico, con lo cual se simplifica el proceso de devolver tanto
el nombre como un valor de atributo de un token.
La tercera seccin contiene las funciones adicionales que se utilizan en las acciones.
De manera alternativa, estas funciones pueden compilarse por separado y cargarse con
el analizador lxico.
El analizador lxico que crea Lex trabaja en conjunto con el analizador sintctico de la
siguiente manera. Cuando el analizador sintctico llama al analizador lxico, ste
empieza a leer el resto de su entrada, un carcter a la vez, hasta que encuentra el prefijo
ms largo de la entrada que coincide con uno de los patrones Pi. Despus ejecuta la
accin asociada Ai. Por lo general, Ai regresar al analizador sintctico, pero si no lo
hace (tal vez debido a que Pi describe espacio en blanco o comentarios), entonces el
analizador lxico procede a buscar lexemas adicionales, hasta que una de las acciones
correspondientes provoque un retorno al analizador sintctico. El analizador lxico
devuelve un solo valor, el nombre del token, al analizador sintctico, pero utiliza la
variable entera compartida yylval para pasarle informacin adicional sobre el lexema
encontrado, si es necesario.
El yylex() generado por PCLex sigue dos directrices fundamentales para reconocer lexemas en caso de
ambigedad. Estas directrices son, por orden de prioridad:
1. Entrar siempre por el patrn que reconoce el lexema ms largo posible.
2. En caso de conflicto usa el patrn que aparece en primera posicin.
Como consecuencia de la segunda premisa: los patrones que reconocen palabras reservadas se colocan
siempre antes que el patrn de identificador de usuario. P.ej. un analizador lxico para Pascal (en el que
TYPE y VAR son palabras reservadas), podra tener una apariencia como:
%%
TYPE
VAR
[A-Z][A-Z0-9]*
...
Cuando yylex() se encuentra con la cadena VAR se produce un conflicto. Cambiar el orden de ambos
patrones tiene consecuencias funestas:
%%
TYPE
[A-Z][A-Z0-9]*
VAR
...
ya que esta vez el lexema VAR entrara por el patrn de identificador de usuario, y jams se
reconocera como una palabra reservada: el patrn VAR es superfluo.
\noctal: representa el carcter cuyo valor ASCII es n octal. P.ej: \012 reconoce el
carcter decimal 10 que se corresponde con LF (Line Feed).
[ ]: permiten especificar listas de caracteres, o sea uno de los caracteres que encierra,
ej.: [abc] reconoce o la a, o la b, o la c, ( [abc] / (a|b|c) ).
Dentro de los corchetes los siguientes caracteres tambin tienen un sentido especial:
-: indica rango. Ej.: [A-Z0-9] reconoce cualquier carcter de la A a la Z o
del 0' a 9'.
^: indica complecin cuando aparece al comienzo, justo detrs de [.
Ej.: [^abc] reconoce cualquier carcter excepto la a, la b o la c.
Ej.: [^A-Z] reconoce cualquier carcter excepto los de la A a la Z.
?: aquello que le precede es opcional. Ej.: a? / ( a |
letra de la A a la Z o bien g. Ej.: a?b / ab | gb.
.: representa a cualquier carcter (pero slo a uno) excepto el retorno de carro (\n). Es
muy interesante porque nos permite recoger cualquier otro carcter que no sea
reconocido por los patrones anteriores.
Lex suministra ciertas capacidades para reconocer patrones que no se ajustan a una
expresin regular, sino ms bien a una gramtica de contexto libre. Esto es especialmente
til en determinadas circunstancias, como p.ej. para reconocer los comentarios de final de
lnea, admitidos por algunos lenguajes de programacin (los que suelen comenzar por // y se
extienden hasta el final de la lnea actual.
$: el patrn que le precede slo se reconoce si est al final de la lnea. Ej.: (a|b|cd)$. Que el
lexema se encuentre al final de la lnea quiere decir que viene seguido por un retorno de
carro, o bien por EOF. El carcter que identifica el final de la lnea no forma parte del
lexema.
^: fuera de los corchetes indica que el patrn que le sucede slo se reconoce si est al
comienzo de la lnea. Que el lexema se encuentre al principio de la lnea quiere decir que
viene precedido de un retorno de carro, o que se encuentra al principio del fichero. El
retorno de carro no pasa a formar parte del lexema. Ntese como las dos premisas de Lex
hacen que los patrones de sensibilidad al contexto deban ponerse en primer lugar en caso de
que existan ambigedades:
Programa 1
Programa 2
^casa
casa
casa
^casa
Los estados lxicos vienen a ser como variables lgicas excluyentes (una y slo una
puede estar activa cada vez) que sirven para indicar que un patrn slo puede aplicarse
si el estado lxico que lleva asociado se encuentra activado. En los ejemplos que hemos
visto hasta ahora hemos trabajado con el estado lxico por defecto que, tambin por
defecto, se encuentra activo al comenzar el trabajo de yylex(). Por ser un estado lxico
por defecto no hemos tenido que hacer ninguna referencia explcita a l.
Los distintos estados lxicos (tambin llamados condiciones start) se declaran en el
rea de definiciones Lex de la forma:
%START id1, id2, ...
Una accin asociada a un patrn puede activar un estado lxico ejecutando la macro
BEGIN, que es suministrada por el programa generado por PCLex. As:
BEGIN idi;
activara la condicin start idi, que ha debido ser previamente declarada. La activacin
de un estado lxico produce automticamente la desactivacin de todos los dems. Por
otro lado, el estado lxico por defecto puede activarse con:
BEGIN 0;
Para indicar que un patrn slo es candidato a aplicarse en caso de que se encuentre
activa la condicin start idi, se le antepone la cadena <idi>. Si un patrn no tiene
asociada condicin start explcita, se asume que tiene asociado el estado lxico por
defecto.
El siguiente ejemplo muestra cmo visualizar todos los nombres de los procedimientos
y funciones de un programa Modula-2:
%START PROC
%%
PROCEDURE {BEGIN PROC;}
<PROC> [a-zA-Z][a-zA-Z0-9]* { printf (%s\n, yytext);
BEGIN 0 ; }
En Lex todo debe comenzar en la primera columna. Si algo no comienza en dicha columna,
PCLex lo pasa directamente al programa C generado, sin procesarlo. De esta forma es
posible crear definiciones de variables, etc. Sin embargo, esto no se recomienda ya que Lex
posee un bloque especfico para esto en el que se pueden poner incluso directivas del
procesador, que deben comenzar obligatoriamente en la primera columna.
As, el rea de definiciones tambin permite colocar cdigo C puro que se trasladar tal cual
al comienzo del programa en C generado por PCLex. Para ello se usan los delimitadores
%{ y %}. Ej.:
%{
#include <stdlib.h>
typedef struct _Nodo{
int valor;
Nodo * siguiente;
} Nodo;
Nodo * tablaDeSimbolos;
%}
Es normal poner en esta zona las declaraciones de variables globales utilizadas en las
acciones del rea de reglas y un #include del fichero que implementa la tabla de
smbolos.
Es normal poner en esta zona las declaraciones de variables globales utilizadas en las acciones del rea de reglas
y un #include del fichero que implementa la tabla de smbolos.
PCLex no genera main() alguno, sino que ste debe ser especificado por el programador. Por regla general,
cuando se pretende ejecutar aisladamente un analizador lxico (sin un sintctico asociado), lo normal es que las
acciones asociadas a cada regla no contengan ningn return y que se incluya un main() que nicamente invoque
a yylex():
void main(){
yylex();
};
Para especificar el main() y cuantas funciones adicionales se estimen oportunas, se dispone del rea de
funciones. Dicha rea es ntegramente copiada por PCLex en el fichero C generado. Es por ello que, a efectos
prcticos, escribir cdigo entre %{ y %} en el rea de definiciones es equivalente a escribirlo en el rea de
funciones.
El siguiente ejemplo informa de cuntas veces aparece el literal resultado en la cadena de entrada.
%{
int cont=0;
%}
%%
resultado {cont ++;}
. | \n {;}
%%
void main(){
yylex();
printf(resultado aparece %d veces, cont);
}
El ncleo bsico del programa en C generado por PCLex es la funcin yylex(), que se encarga de buscar un
lexema y ejecutar su accin asociada. Este proceso lo realiza iterativamente hasta que en una de las acciones se
encuentre un return o se acabe la entrada. Adems, PCLex suministra otras funciones macros y variables de
apoyo; entre los que tenemos:
yylex(): implementa el analizador lexicogrfico.
yytext: contiene el lexema actual
yyleng: nmero de caracteres del lexema actual.
yylval: es una variable global que permite la comunicacin con el sintctico.
Realmente no la define PCLex sino la herramienta PCYacc
yyin: es de tipo *FILE, y apunta al fichero de entrada que se lee. Inicialmente apunta a stdin, por lo que el
fichero de entrada coincide con la entrada estndar.
yyout: de tipo *FILE, apunta al fichero de salida (inicialmente stdout).
yyerror() : es una funcin que se encarga de emitir y controlar errores (saca mensajes de error por pantalla).
Realmente es definida por PCYacc
yylineno: variable de tipo entero que, curiosamente, debe ser creada, inicializada y actualizada por el
programador. Sirve para mantener la cuenta del nmero de lnea que se est procesando. Normalmente se
inicializa a 1 y se incrementa cada vez que se encuentra un retorno de carro.
yywrap(): el algoritmo de yylex() llama automticamente a esta macro cada vez que se encuentra con un EOF.
La utilidad de esta macro reside en que puede ser redefinida (para ello hay que borrarla previamente con la
directiva #undef). Esta macro debe devolver false (que en C se representa por el entero 0) en caso de que la
cadena de entrada no haya finalizado realmente, lo que puede suceder en dos tipos de situaciones: a) se est
procesando un fichero binario que puede contener por en medio el carcter EOF como uno ms, en cuyo caso el
verdadero final del fichero hay que descubrirlo contrastando la longitud del fichero con la longitud de la cadena
consumida; y b) la cadena a reconocer se encuentra particionada en varios ficheros, en cuyo caso cuando se
llega al final de uno de ellos, hay que cargar en yyin el siguiente y continuar el procesamiento. yywrap() debe
devolver true (valor entero 1) en caso de que el EOF encontrado identifique realmente el final de la cadena a
Procesar.
yyless(int n): deja en yytext los n primeros caracteres del lexema actual. El resto los devuelve a la entrada, por lo que podemos decir que son desconsumidos. yyleng tambin se modifica convenientemente. Por ejemplo, el patrn abc*/\n es equivalente a:
abc*\n { yyless(yyleng-1); }
input(): consume el siguiente carcter de la entrada y lo aade al lexema actual. P,ej., el programa:
%%
abc { printf (%s, yytext);
input( );
printf(%s, yytext);}
ante la entrada abcde entrara por este patrn: el lexema antes del input() (en el primer printf) es abc, y despus del input (en el segundo printf) es
abcd.
output(char c): emite el carcter c por la salida estndar. PCLex para DOS no soporta esta funcin, pero s el Lex para Unix.
unput(char c): des-consume el carcter c y lo coloca al comienzo de la entrada. P.ej., supongamos que el programa:
%%
abc { printf (%s,yytext); unput(t); }
tal { printf (%s,yytext); }
recibe la entrada abcal. Los tres primeros caracteres del lexema coinciden con el primer patrn. Despus queda en la entrada la cadena al, pero como se
ha hecho un unput(t), lo que hay realmente a la entrada es tal, que coincide con el segundo patrn.
ECHO: macro que copia la entrada en la salida (es la accin que se aplica por defecto a los lexemas que no encajan por ningn patrn explcito).
yymore(): permite pasar a reconocer el siguiente lexema, pero sin borrar el contenido actual de yytext, por lo que el nuevo lexema ledo se concatena al ya
existente. PCLex para DOS no soporta esta funcin, pero s el Lex para Unix.
La utilidad principal de yymore() reside en que facilita el reconocer los literales entrecomillados. Supongamos que se desea construir un patrn para
reconocer literales entrecomillados. Una primera idea podra ser el patrn:
\[^\]*\ /* Lexemas que empiecen por comillas,
cualquier cosa, y termine en comillas. */
que reconocera cadenas como Hola, adis, e incluso Ho;*. Sin embargo una cadena como Hola, \camarada\ que, a su vez contiene comillas
dentro, dar problemas porque la primera comilla de camarada, es considerada como unas comillas de cierre. La solucin a este problema pasa por el
siguiente bloque de cdigo Lex:
\[^\]*/\ { if (yytext[yyleng-1]==\\)
\[^\]*/\ { if (yytext[yyleng-1]==\\)
yymore();
else
input();}
Este patrn reconoce cadenas entrecomilladas (excepto las ltimas comillas), de forma
que las cadenas que tienen dentro comillas se reconocen por partes. Siguiendo con el
ejemplo de antes, Hola, \camarada\ se reconocera como:
Este patrn fallara si los caracteres anteriores a las comillas de cierre fuesen precisamente \\. Para
solucionarlo habra que hacer uso de unput y de una variable global que actuase como flag. Cualquier sentencia
que haya detrs de yymore() nunca se ejecutar, ya que acta como una especie de goto.
REJECT: rechaza el lexema actual, lo devuelve a la entrada y busca otro patrn.
REJECT le dice a yylex() que el lexema encontrado no corresponde realmente a la expresin regular en curso, y
que busque la siguiente expresin regular a que corresponda. La macro REJECT debe ser lo ltimo que una
accin ejecute puesto que si hay algo detrs, no se ejecutar. P.ej. Para buscar cuntas veces aparecen las
palabras teclado y lado, se puede construir el siguiente programa Lex:
%{
int t=0, l=0;
%}
%%
teclado {t ++; REJECT;}
lado {l ++;}
Ante la entrada teclado este programa se comporta de la siguiente forma:
1.- Entra por el patrn que lee el lexema ms largo que sera teclado y ejecuta la accin asociada: se
incrementa la variable t y se rechaza el lexema actual, por lo que el texto entero se devuelve a la entrada.
2.- Saca por pantalla tec que es la accin por defecto cuando se encuentran caracteres que no encajan con
ningn patrn.
3.- El lexema lado coincide con el segundo patrn y ejecuta la accin asociada: se incrementa la variable l.
Para finalizar, resulta evidente que el programador no debe declarar ninguna funcin o variable cuyo nombre
coincida con las que acabamos de estudiar. De hecho PCLex tambin genera una serie de variables auxiliares
cuyo nombre consta de un solo carcter, por lo que tampoco es bueno declarar variables o funciones con
nombres de una sola letra ni que empiecen por yy.