Sie sind auf Seite 1von 19

TEMA 9. ANALISIS LEXICOGRFICO.

CONTENIDOS 9.1.- Funcionamiento de un Analizador lexicogrfico. 9.2.- Razones por las que existe un Explorador. 9.3.- Tcnicas de Lectura. 9.4.- Implementacin de un Explorador. 9.5.- Optimizacin de un Analizador lexicogrfico. 9.6.- Acciones Semnticas. 9.7.- Recuperacin de Errores lexicogrficos. 9.8.- Generadores de Analizadores lexicogrficos.

9.1.- ANALIZADOR LEXICOGRAFICO El analizador lexicogrfico o explorador, es la parte del compilador que lee el programa fuente, carcter a carcter y construye a partir de ste unas entidades primarias llamadas "tokens". Es decir el analizador lexicogrfico transforma el programa fuente en tiras de tokens. Ejemplo: Sea la sentencia: IF alfa < 718 Then alfa := alfa + beta El analizador lexicogrfico dara como resultado la siguiente cadena de caracteres correspondiente a la misma (79) ; (12) ; (28) ; (13) ; (80) ; (12) ; (65) ; (12) ; (34) ; (12) Como puede verse el analizador lexicogrfico ha simplificado el texto de entrada consistente en una secuencia de smbolos en otra cadena de caracteres, representados cada uno de ellos por un nmero, y que corresponden a los siguientes significados 12 13 28 34 65 79 80 Variable Real. Constante Entera. Comparador < Signo + Asignador Palabra Reservada IF Palabra Reservada THEN

La informacin que da esta secuencia de caracteres es suficiente para el anlisis de la estructura de la sentencia, pero no basta para un anlisis de su significado, para lo cual hay que tener cierta informacin de cuales son las variables que entran en juego en esta sentencia. Esto se soluciona mediante un segundo elemento que compone el token, que por lo general consiste en un puntero a la tabla de smbolos en donde se hallan almacenadas las variables, quedando una secuencia de pares : (79,-); (12,32); (28,-); (13,7); (80,-); (12,32); (65,-); (12,32); (34,-); (12,33) En estos pares el primer elemento nos indica el tipo de objeto que estamos procesando segun una tabla que nosotros hemos diseado previamente ( tabla de tokens ). El segundo miembro de estos pares es, en caso necesario, un puntero a la tabla de smbolos o a la tabla de constantes. El analizador lexicogrfico adems realiza ciertas tareas adicionales como: 1.- Eliminar comentarios del programa fuente. 2.- Eliminar los blancos, saltos de pgina, tabuladores, retornos de carro, y dems caracteres propios del dispositivo de entrada. 3.- Reconocer las variables y asignarles una posicin en la tabla de smbolos. 4.- Relacionar los mensajes de error que produce el compilador en sus diversas fases con la parte correspondiente del programa fuente ( nmero de lnea en que aparecen ).

En ciertos casos el analizador lexicogrfico se encarga tambin de producir el listado del programa con los errores de compilacin. 5.- Si el programa fuente incorpora metanotaciones o macros, analizador lexicogrfico puede incorporar un preprocesador. 6.- Avisa de los errores lexicogrficos que detecta.

9.2.- JUSTIFICACION DEL EXPLORADOR Hay diversas razones por las que se separa la fase de anlisis de un compilador en anlisis lexicogrfico y anlisis sintctico. Son las siguientes : 1.- El diseo del analizador sintctico es ms fcil de esta forma, ya que ste no ha de preocuparse de leer el fichero de entrada, ni de saltar blancos , ni comentarios, ni de recibir caracteres inesperados, puesto que todo ello ha sido filtrado previamente por el analizador lexicogrfico. El diseo consiguientemente se hace ms claro y comprensible. 2.- Se mejora la eficiencia del compilador en su conjunto. La lectura del programa fuente suele requerir gran parte del tiempo de compilacin, que se ve reducido si el analizador lexicogrfico incorpora tcnicas especiales de lectura, o est realizado en ensamblador. 3.- Aumenta la portabilidad del compilador. Ya que todas las diferencias que se produzcan en el alfabeto de entrada, o en el dispositivo de almacenamiento, pueden ser reducidas al analizador lexicogrfico, dejando al analizador sintctico intacto. Supngase que el compilador lee un texto ASCII y nos interesa construir uno que sea capaz de leer texto escrito bajo estandar EBCDIC, bastara con modificar el analizador lexicogrfico.

Para la implementacin de una analizador lexicogrfico existen en la actualidad tres vas. La principal ventaja de cad una de ellas respecto a las siguientes es la mayor comodidad y facilidad de programacin, por el contrario, la implementacin en un lenguaje ms cercano a la mquina hace posible una mejor gestin de las entradas/salidas con la consiguiente rapidez de ejecucin. 1.- Escribir el analizador lexicogrfico en lenguaje ensamblador. 2.- Escribir el analizador lexicogrfico en un lenguaje de alto nivel, usando las facilidades de entrada/salida que este tenga. 3.- Usar un generador de analizadores lexicogrficos, como el conocido compilador "Lex", capaz de producir un analizador lexicogrfico a partir de las correspondientes expresiones regulares que definen los distintos tokens. En este caso el generador incorpora sus propias rutinas de entrada/salida.

9.3.- TECNICAS DE LECTURA La lectura del programa fuente es una de las tareas que mayor tiempo requiere en comparacin con las restantes funciones del compilador, por tanto es conveniente emplear unas tcnicas de lectura adecuadas a fin de minimizarlo. Tngase en cuenta por ejemplo que la lectura de un slo carcter almacenado en un disco, emplea por lo general el mismo tiempo que la lectura de todo el sector en el que est almacenado. Por otra parte, el reconocimiento de los tokens, implica en muchos casos la lectura de una serie de caracteres adicionales, para interpretar correctamente el lexema ledo, o ms frecuentemente para detectar su final. Por ejemplo, en la sentencia Pascal: alfa := alfabeto; el analizador lexicogrfico al leer esta sentencia ha de leer una "a", una "l", una "f" y una "a". En este instante nuestro explorador no sabe si la variable que est leyendo finaliza aqu o no, ha de leer el siguiente carcter ":" para determinarlo. Se produce entonces una situacin conflictiva para el reconocimiento del siguiente token, ya que el puntero de lectura al final del reconocimiento del primer token se halla situado sobre el signo "=". La solucin obvia a este problema es devolver al fichero fuente el carcter leido al final de cada token con una funcin como la unget del lenguaje C. Una solucin general a los problemas de lectura planteados anteriormente es el empleo de un buffer dividido en dos mitades, cada una de ellas de longitud N, siendo N el nmero de caracteres de un sector de disco (256,512,1024,...). Se emplearn as mismo dos punteros, uno que seala el comienzo del token que llamaremos puntero de comienzo, y otro que iremos moviendo sobre la tira de entrada, que llamaremos puntero avanzado. Al principio cargamos la primera de las dos mitades con los N caracteres del primer sector correspondiente a nuestro fichero fuente, colocando el puntero de comienzo y el puntero avanzado sobre el primer carcter de la primera mitad. A partir de aqu, y hasta que detectemos el carcter de final de fichero eof repetimos el proceso que se describe a continuacin. Situamos el autmata en el estado inicial de reconocimiento y vamos leyendo los caracteres que est bajo el puntero avanzado y moviendo ste de izquierda a derecha hasta detectar un token. Si el puntero avanzado alcanza el final de una de las dos mitades leemos nuevamente N caracteres del fichero de entrada y los situamos sobre la otra mitad, moviendo el puntero al principio de sta. Un vez detectado el token, se forma ste a partir de la posicin que indica el puntero de comienzo, y se vuelven a colocar ambos punteros sobre el carcter siguiente al lexema que corresponde al token. Ejemplo: Sea la sentencia Pascal alfa:=alfas Comenzamos el proceso cargando la primera mitad y situando los punteros al comienzo de la misma. a|l|f|a|:|=|a | | | | | | p.c p.a

En este momento comenzamos el reconocimiento del primer token desplazando para ello el puntero avanzado, hasta alacanzar el carcter ":", que nos indica que se trata de una variable. En este momento tenemos : a|l|f|a|:|=|a | | | | | | p.c p.a Formamos el token <variable> con el lexema "alfa" y situamos ambos punteros sobre el carcter inmediatamente siguiente: a|l|f|a|:|=|a | | | | | | p.c p.a Repitiendo el proceso llegamos a: a|l|f|a|:|=|a | | | | | | p.c p.a En este instante el puntero avanzado alcanza el final de la primera mitad, por lo que tenemos que leer otros N caracteres de programa fuente y situarlos sobre la segunda, situando el puntero avanzado al principio de esta. a|l|f|a|:|=|al|f|a|s|$| | p.c p.a El proceso continua hasta que el puntero avanzado se situa sobre el carcter de fin de fichero, en cuyo caso formamos el ltimo token y damos por finalizada la lectura. El proceso descrito anteriormente requiere realizar dos comprobaciones cada vez que movemos el puntero avanzado, la primera de ellas para ver si el carcter bajo el puntero es el carcter de fin de fichero y la segunda para determinar si se ha alacanzado el final de alguna de las dos mitades. Puede mejorarse la eficacia de este algoritmo mediante el uso de dos caracteres centinela al final de cada una de las dos mitades del buffe, con lo que la longitud de cada una de ellas sera de N+1, en lugar de N caracteres. El carcter ms adecuado para servir de centinela es sin duda el propio carcter de fin de fichero. Siguiedo con el ejemplo anterior tendremos en este caso : a|l|f|a|:|=|a|$l|f|a|s|$| | |$ p.c p.a El analizador lexicogrfico va escrutando los caracteres bajo el puntero avanzado, si se detecta el carcter de fin de fichero comprobamos si estamos situados al final de alguna de las dos mitades, en cuyo caso se trata de carcter centinela que nos indica que debemos recargar la otra mitad. En el supuesto de encontrar el carcter de fin de fichero, antes del carcter N+1 de

una mitad, interpretaremos que se trata del autntico fin de fichero. Ntese que de esta forma el nmero de comprobaciones que se realizan en cada movimiento del puntero avanzado es slo una, y slo al final de una mitad se realizan dos, con lo que el proceso gana en eficiencia.

9.4.- IMPLEMENTACION DEL EXPLORADOR Los analizadores lexicogrficos como ya hemos visto, se basan en los autmatas finitos, y por tanto la construccin de analizadores, o de programas generadores de analizadores, como el Lex implican la implementacin de autmatas finitos sobre ordenadores digitales. Mtodo: a) Implementacin de un autmata finito determinista. Un diagrama de transiciones o tabla de transiciones equivale a una sentencia CASE OF donde los distintos estados del autmata son los valores condicionales del CASE. Cada estado no final junto con sus transiciones de salida sern expresados en forma de IF THEN ELSE sucesivos. Cada condicin de un IF es un comparacin con la etiqueta de un arco de salida y la accin asociada es la asignacin del nuevo estado al que transita. El ltimo ELSE de esta secuencia de IF encadenados corresponder a un Error y por tanto se abortar el reconocimiento del este autmata. Cada estado final se expresa igual que un no final, salvo en el ltimo ELSE que indicar que la cadena ha sido aceptada. Cada autmata finito ser una funcin para nuestro explorador y devolver el valor TRUE si es aceptada la cadena o el valor FALSE si es rechazada. Cada funcin deber hacer llamadas sucesivas a una funcin de LECTURA para obtener el carcter de entrada. b) Implementacin de la funcin de Lectura. El BUFFER de lectura ser un array accesible por todas las funciones y llevar asociado dos punteros uno denominado base y otro denominado avance. La funcin de lectura ser la que vaya modificando el puntero de AVANCE y devuelva el carcter de entrada. Cuando una cadena sea rechazada por un autmata se coloca el puntero de AVANCE en la posicin del BASE. c) Implementacin del programa principal. El conjunto de autmatas ser implementado como una secuencia encadenada de IF THEN ELSE. Como condicin de cada IF habr una llamada a una funcin autmata y como accin asociada habr una llamada a una funcin de Aceptacin de cadenas a la cual se le pasar como parmetro el tipo de TOKEN reconocido , que no ser ms que un ndice a la TABLA DE TOKENS donde tomaremos su cdigo. Adems ser necesario actualizar el puntero BASE para extraer el lexema y dependiendo del tipo de token se desencadenarn una serie de acciones.

El ltimo ELSE, perteneciente a la secuencia de IF encadenados, corresponde a una rutina de tratamiento de ERRORES, que trata las cadenas no reconocidas por nuestros autmatas, es decir, son errores lexicogrficos. La ejecucin de este anidamiento contemplar adems los espacios en blanco, los controles de carro, los tabuladores,... y finalizar cuando lleguemos al final del fichero fuente.

9.5.- OPTIMIZACION DE UN EXPLORADOR Se puede construir un programa ms eficiente si se trabaja con un nico diagrama de transiciones en lugar de una coleccin de diagramas, puesto que no se retrocede para volver a explorar en otro diagrama. Se puede agrupar el reconocimiento de palabras reservadas e identificadores. No se construyen diagramas para palabras reservadas, as una palabra reservada tendr el mismo diagrama que de los identificadores. En una tabla interna del analizador lexicogrfico se recogen por orden alfabtico todas las palabras reservadas del lenguaje a reconocer. Cuando se ha reconocido un identificador antes de instalarlo en la tabla de smbolos, se compara en una bsqueda dicotmica con la tabla de palabras reservadas. Si la bsqueda tiene xito entonces el token reconocido es una palabra reservada, si no se encuentra entonces el token reconocido es un identificador. 9.6.- GENERADORES DE ANALIZADORES LXICOS Como hemos visto, los analizadores lexicogrficos se construyen mediante la conjuncin de autmatas finitos. Hemos estudiado como pueden construirse autmatas finitos denterministas a partir de expresiones regulares, por lo que estamos en condiciones de poder realizar programas cuya entrada sea la definicin de un determinado lenguaje, o mejor dicho, de los tokens que lo forman, y cuya salida sea el analizador lexicogrfico correspondiente. Un ejemplo de generador de analizadores lexicogrficos es el conocido programa "Lex", que pertenece al entorno de programas del sistema UNIX. El programa "Lex", admite como entrada la definicin de los tokens de un determinado lenguaje, mediante expresiones regulares, y produce como salida un programa en lenguaje C, que es capaz de realizar el anlisis lexicogrfico correspondiente. El programa "Lex" est diseado para se utilizado junto con el programa "Yacc", que como veremos es un generador de analizadores sintcticos. Para poder introducir las expresiones regulares, estas deben estar escritas con un formato especila entendible por el PCLEX a este formato se le puede considerar a su vez como un lenguaje denominado SDL (lenguaje de descrpcion de analizadores). Por lo tanto, la entrada al programa "Lex" puede considerarse a su vez como un programa escrito en un lenguaje de programacin al que llamaremos tambin "Lex". Un programa escrito en "Lex" consta de tres partes: Zona de declaraciones %% reglas de traduccin %% procedimientos auxiliares

Lex genera un autmata finito determinista a partir de las expresiones regulares del fichero fuente. El resultado es un programa escrito en C que simula el trabajo de los autmatas, este programa contiene una funcin llamada yylex() que es la que verdaderamente tiene implementados los autmatas y finaliza su trabajo slo cuando encuentra el final del fichero fuente. La funcin principal main() tan slo contiene una llamada a la funcin yylex(). Por defecto el programa C generado no nos proporciona ninguna salida especial, tan slo escribe en pantalla los caracteres que va leyendo del fichero de entrada. No obstante podemos variar este comportamiento por defecto ya que el SDL nos permite introducir cdigo C que representa lo que queremos que realice el programa final cada vez que detecte un determinado token.

9.6.2.- LA ZONA DE DEFINICIONES Recordemos el formato del fuente lex: {definiciones} %% {reglas} %% {rutinas de usuario}

Es una seccin opcional y en ella podemos incluir tres tipos de definiciones (componentes propios del C, nombres de macros y condiciones de inicio) y para cada tipo debemos expresarlo de forma diferente. En primer lugar podemos incluir un a zona de declaraciones de componentes proios del C que sern utilizados por otras partes del cdigo, como son definiciones de tipos, de estructuras, de variables globales, de cabeceras de funciones o directivas para el preprocesador como los #define y los #include. Esta zona deber estar delimitada por dos marcas una de inicio %{ y otra de fin %} El lex incluye los ficheros <stdio.h>, <conio.h>, <string.h> por defecto.

%{ #include <stchar.h> #define IMPRIMIR printf("%s",yytext); #define NUMERO 2 Void funcion(void); int i=0; %} %% ... %% ...

En segundo lugar podemos incluir una zona de definicin de nombres de macros propios del PCELEX para simplificar la construccin de las expresiones regulares. El formato es: nombre sustitucin

y el efecto es que la cadena dada en la sustitucin est asociada con el nombre. El nombre y la sustitucin deben estar en la definicin separados al menos por un blanco o tabulador, y el nombre debe empezar por una letra. La sustitucin puede ser llamada por {nombre} en una regla. Por ejemplo: D L %% {D}+ [0-9] [a-zA-Z]

printf("integer");

Existe una tercera zona de definiciones llamadas condiciones de inicio, no permite tener desactivados ciertos autmatas hasta que nosotros los activemos, para ello deberemos acompaar a las expresiones regulares de las marcas que aqu definamos. Podemos diferenciar dos tipos de condiciones, las normales que cuando son activadas se ejecuta el autmata finitn0o asociado conjuntamente con el resto de autmatas del analizador. Y las exclusivas que cuando se activan el resto de los autmatas dejan de funcionar. Para introducir condiciones de comienzo normales vamos a utilizar el siguiente formato: %Start name1 name2 ...

donde la condicin puede ser nombrada en cualquier orden. La palabra Start puede ser abreviada por s o S. La condicin puede ser referenciada en la cabecera de una regla con ngulos : <name1>expresin esta regla slo se reconocer cuando lex tenga de condicin de comienzo name1. Para colocar lex a una determinada condicin ejecutamos la accin BEGIN name1; Para retornar al estado inicial ejecutamos BEGIN 0; Una regla puede ser activa en varias condiciones de comienzo como por ejemplo : <name1, name2,name3>

Ejemplo: Copiar la entrada en la salida, cambiando la palabra mgico por : 1. la palabra primero si le precede una a. 2. la palabra segundo si le precede una b. 3. la palabra tercero si le precede una c. El problema del ejemplo quedara con condiciones de comienzo: %START AA BB CC

%% ^a {ECHO;BEGIN AA;} ^b {ECHO;BEGIN BB;} ^c {ECHO;BEGIN CC;} \n {ECHO;BEGIN 0;} <AA>magico printf("primero"); <BB>magico printf("segundo"); <CC>magico printf("tercero");

Para introducir condiciones de incio exclusivas el formato es similar pero usando %x.

9.6.3.- SECCIN DE REGLAS Contiene en cada lnea un patrn de bsqueda, descrito mediante una expresin regular, y la accin a ejecutar cuando el patron sea localizado, que ser descrita en cdigo C entre {} si ocupa ms de una instruccin, la accin mnima que podemos situar es el ; lo cual signinifica que no hacemos nada. Si se omite en esta seccin la parte de acciones la entrada se copiar en la pantalla, como accin por defecto. Entre la expresin regular y la secuencia de acciones C dber haber al menos un espacio en blanco o un tabulador como separacin. Las reglas de traduccin de un programa "Lex" son sentencias de la forma: P1 P2 . . Pn {Accin1} {Accin2} . . . . {Accinn}

en donde cada Pi es una expresin regular, que deber situarse en la primera columna de la fila, y cada Accini es una secuencia de instrucciones que especifican que es lo que el analizador lexicogrfico debe hacer tras reconocer que una cadena encaja con la expresin regular Pi. EXPRESIONES REGULARES LEX Una expresin regular especifica un conjunto de cadenas para ser igualadas. Esta contiene caracteres texto ( que sern los que vayan comparando con los correspondientes caracteres de las cadenas de entrada) y caracteres de operador ( que especifican repeticiones, alternativas, y otras carctersticas ). Las letras del alfabeto y los digitos sern siempre caracteres texto. As la expresin regular: integer casa o iguala la cadena "integer" dondequiera que aparezca y la expresin: a57D mira para las cadenas "a57D" Los caracteres de operador son:

"\[]^-?.*+|()$/{}%<> Si alguno de estos caracteres son usados literalmente, necesitarn ser sealados inidividualmente con el smbolo (\), o todo un grupo ser introducido entre las marcas ("). La marca de operador (") indica que cualquier cosa que est contenida entre un par de (") sern tomadas como caracteres de texto. As: xyz"++" iguala o casa con la cadena xyz++ cuando sta aparezca. Ntese que slo una parte de la cadena ha sido marcada; pero lo ms normal hubiera sido la expresin "xyz++" Un carcter de operador puede ser transformado a texto si es precedido por (\), como hemos dicho anteriormente, por lo que podemos obtener otra expresin equivalente a la anterior, aunque menos comprensible : xyz\+\+ Los mecanismos de marcado que hemos visto ( \ y " ) pueden ser usados para obtener los caracteres en blanco de una expresin. Normalmente blancos y tabuladores son una regla. \n \t \b \\ Nueva lnea. Tabulador. Espacio en Blanco.

Especificacin de clases de caracteres. Las clases de caracteres pueden ser especificadas usando corchetes: [ y ]. La construccin: [abc] iguala o casa un carcter simple, el cual puede ser "a", "b", "c". Se pueden utilizar tres caracteres especiales para la especificacin de conjuntos (\), (-), (^). El (-) indica rangos, por ejemplo: [a-z0-9<>_] indica que la clase de caracteres contiene todas las letras minsculas, los dgitos, los dos angulos y el sublinea. Los rangos pueden ser dados en cualquier orden. Si usamos este smbolo entre dos caracteres que no sean letras mayusculas o minusculas o digitos se emitir un mensaje de error. Si deseamos incluir el signo - como parte de la clase entonces deber aparecer el primero o el ltimo. As : [-+0-9] iguala o casa todos los digitos y los signos ms y menos.

El (^) en una clase de caracteres debe aparecer como el primer carcter despus del corchete izquierdo. Indica que quedan excludios todos los caracteres de esa clase en la casacin o igualacin de cadenas. As: [^abc] iguala o casa todos los caracteres, excepto a,b o c, incluyendo todos los caracteres especiales o de control. [^a-zA-Z] es cualquier carcter menos una letra. El (\) provee un mecanismo escape con las clases de caracteres, as: [\40-\176] iguala todos los caracteres en el cdigo ASCII desde el octal 40 hasta el octal 176. El (.) designa la clase de todos los caracteres excepto un carcter de nueva lnea. Especificacin de Expresiones Opcionales El carcter operador (?) indica un elemento (el que estar delante ) es opcional en una expresesin. As: ab?c iguala o casa indistintamente las cadenas "ac" o "abc". Podemos utilizarla para simular el smbolo . Por ejemplo una expresin como puede ser a(bc+)d que representa las cadenas ad y abad pueden expresarse como a(bc)?d Especificacin de Expresiones Repetitivas. Las repeticiones de clases son indicadas por los operadores asterisco (*) y ms (+). Por ejemplo: a* iguala o casa cualquier nmero de smbolos "a" consecutivos, incluyendo el cero; Mientras que a+ iguala una o ms instancias de "a". Por ejemplo: [a-z]+ iguala o casa todas las cadenas de letras minsculas, y: [A-Za-z][A-Za-z0-9]* iguala o casa todas las cadenas alfanumricas que comienzan por un carcter alfabtico. Ntese que esta es la tpica expresin para reconocer los identificadores en los lenguajes de programacin.

Especificacin de Alternacin y Agrupamiento. El operador barra vertical (|) indica alternacin. Por ejemplo: (ab|cd) iguala o casa indistintamente "ab" o "cd". Notar que los parntesis son usados para agrupar, aunque no son necesarios en niveles exteriores. Por ejemplo: ab|cd podra ser suficiente en el ejemplo precedente. Los parntesis podran ser usados para expresiones ms complejas, como: (ab|cd+)?(ef)* la cual iguala o casa con cadenas del tipo "abefef", "efefef", "cdef" , "cddd",.... ; pero nunca del tipo "abc", "abcd", "abcdef".

Especificacin Sensible al Contexto. Los dos operadores que se usan son (^) y el ($). Si el primer carcter de una expresin es (^), entonces la expresin es solamente igualada o casada al comienzo de una lnea ( es decir despus de un carcter de nueva lnea, o al comienzo de la cinta de entrada ). Este operador nunca entrar en conflicto con su operador gemelo visto en un apartado anterior. Si el ltimo smbolo es ($), la expresin es slo igualada o casada al final de una lnea ( es decir seguida inmediatamente de un smbolo de nueva lnea ). Hay un operador especial (/) que indica el contexto de remolque, es decir ab/cd iguala las cadenas ab, pero solamente si van seguidas de cd. As las expresiones ab$ y ab/\n son equivalentes. Si una regla solamente puede ser ejecutada cuando un autmata lex interprete que se cumple la condicin x, la regla podra ser encerrada entre ngulos: <x> Especificacin de Repeticin de Expresiones. Los simbolos ({ y }) especifican cualquier repeticin ( si engloban nmeros ) o expansin de definiciones ( si engloban un nombre ). Por ejemplo: {digit}

localiza una cadena predefinida llamada "digit" e inserta en ese punto la expresin.

Especificacin de Definiciones. Las definiciones son dadas en la primera parte de la entrada lex, antes de las reglas. a{1,5} definicin usada para una o ms ocurrencias de "a". ESPECIFICACION DE ACCIONES. Cuando una expresin es casada por una parte del texto de entrada, lex ejecuta las correspondientes acciones. Esta seccin describe algunas caractersticas de lex para ayudar a escribir las acciones. Ntese que hay una accin por defecto que es copiar la entrada en la salida. Esto es realizado con las pequeas cadenas que no has sido casadas. As el usuario de lex que desear absorber la entrada entera y no producir nada en la salida deber proveer de reglas para casar con cualquier cosa. Una de las cosas ms simples que puede hacerse es ignorar la entrada, especificando un ";" que es un estamento nulo en C, como una accin. Una regla frecuente es : [\t\n] ; indica que los (caracteres blanco, tabuladores, nuevas lneas) sean ignorados . Otra manera fcil de escribir acciones es la usada para indicar acciones repetidas, y esto se hace con el carcter (|), el cual indica que la accin para esta regla es la misma que para la siguiente. El ejemplo anterior se podra haber sido escrito : "" "\t" "\n" | | ;

da el mismo resultado pero tiene diferente estilo. Las comillas del \t y \n no eran necesarias. En acciones ms complejas, como por ejemplo saber que texto ha sido casado con alguna expresin como puede ser [a-z]+ lex deja ese texto en un vector de caracteres externo llamado yytext. As, para encontrar el lexema encontrado, sera : [a-z]+ printf("%s",yytext); imprime la cadena contenida en yytext. Esta accin es comn que sea escrita como ECHO. Por ejemplo: [a-z]+ ECHO; tiene el mismo efecto que el ejemplo anterior. Parece poca cosa el crear reglas para posteriormente imprimir las cadenas que son reconocidas, entonces qu otras cosas podemos hacer ?,

Las reglas suelen ser requeridas para evitar casamientos con alguna otra regla no deseada, o por ejemplo saber cual es el final de un lexema encontrado,... Lex proporciona un contador del nmero de caracteres que han sido casados en la variable yyleng. Si por ejemplo queremos contar en nmero de palabras y el nmero de caracteres de todas las palabras de la entrada, deberemos poner: [a-zA-Z]+ {words++;chars+=yyleng;}

Si queremos acceder al ltimo carcter del lexema reconocido podemos especificar: yytext[yyleng-1] Ocasionalmente, una accin lex puede decidir que una regla no haya reconocido el correcto tramo de caracteres. Dos rutinas son provistas para auxiliar con estas acciones:

La primera es yymore() puede ser llamada para indicar que la prxima entrada expresin de entrada reconocida es para ser enlazada sobre el final de la presente entrada. Normalmente, la prxima cadeana de entrada sobreescribira la entrada actual en yytext. La segunda es yyless(n) puede ser llamada para indicar que no todos los caracteres casados por la expresin exitosa actual son necesarios. El argumento n indica el nmero de caracteres en yytext para ser retenidos. Los caracteres posteriores a ese nmero son devueltos a la entrada.

Ejercicio Consideremos un lenguaje que define una cadena como un conjunto de caracteres entre ("), y prevee que para introducir un simbolo comillas en una cadena esta debe ser precedida por el smbolo (\). La expresin regular que necesitamos es algo confusa: \"[^"]* { if (yytext[yyleng-1]=='\\') yymore(); else ..... } si introducimos la cadena "abc\"def", en primer lugar casa los primeros cinco caracteres "abc\ y entonces llama a yymore la cual casa a la siguiente parte de la cadena "def para ser soldada sobre el final de la anterior.

Ejercicio La funcin yyless() debe ser usada para reprocesar texto en varias circunstancias. Consideremos el problema en sintaxis de C de distinguir la ambigedad de =-a. Supongamos que deseamos tomar esto como =- a y entonces imprimir un mensaje. Para ello la siguiente regla sera aplicada: =-[a-zA-Z] { printf("Operador (=-) ambiguo\n"); yyless(yyleng-1); ... accin para =-.....

} la cual imprime el mensaje, retorna la letra posterior al operador a la cadena de entrada y toma de operador =-. Alternativamente, deseamos tomar esto como = -a. Para ello, retornaremos el smbolo menos junto a la letra hacia la entrada. =-[a-zA-Z] { printf("Operador (=-) ambiguo\n"); yyless(yyleng-2); ... accin ... } Ntese que la expresin para los casos podra haberse escrito tambin como : =-/[A-Za-z] =/-[A-Za-z] Primer caso. Segundo caso.

Si no queramos haber tocado el identificador y detectar la ambigedad se podra haber utilizado: =-/[^\t\n]

En adicin a las rutinas vistas, lex tambin permite acceso a las rutinas de I/O. Estas incluyen: 1. input() retorna el proximo carcter de entrada; Un cero en la salida significa final de fichero. 2. output(c) escribe el carcter c sobre la salida; 3. unput(c) coloca el carcter c en la entrada para ser leido posteriormente por input(). Estas rutinas son generadas automticamente al final al igual que el programa yylex(). Por defecto esta rutinas son provistas como definiciones de macros, pero el usuario puede definir su versin particular. Estas rutinas definen la relacin entre fichero externos y caracteres internos. La entrada normalmente para lex vendr en un fichero denominado yyin ( por defecto teclado ) y la salida en un fichero denominado yyout (por defecto pantalla ). Ejemplo: Dado un fichero de entrada "entrada" deseamos imprimir en un fichero de salida "salida" una x cuando se encuentre una palabra y un * cuando se encuentre un nmero. %% [0-9]+ fprintf(yyout,"*"); [A-Za-z]+ fprintf(yyout,"x"); \n | . ; %% main() { yyin=fopen("entrada","r"); yyout=fopen("salida","w");

yylex(); fclose(yyin); fclose(yyout); }

Otra rutina lex es yywrap() llamada cuando se detecta el EOF del fichero de entrada. Si devuelve 1 el proceso se para y si es 0 al continua. Esta rutina es modificable por el usuario en la zona de funciones. Ejemplo: int cont=0; %% [A-Z]+ cont++; %% yywrap() { printf("%d",cont); return (1); } Cuando trabajemos con una analizado sintctico ser necesario introducir al final de cada regla ( al final de las acciones) return(token) por ejemplo: %{ #define TOKEN 1 %} %% [0-9]+ { printf("lexema=%s",yytext(); return (TOKEN); } %% #include "mainlex.c" Esta instruccin RETURN obligue a la funcin yylex() a parar, el valor devuelto deber ser distinto de 0 ya que es el usado para identifica el final de todo el proceso. MANEJO DE REGLAS FUENTE AMBIGAS Lex puede manejar especificaciones ambigas. Cuando ms de una expresin puede casarse con la entrada actual, lex realiza la eleccin segun las siguientes normas:

El mayor casamiento es preferido. En el caso de que las reglas casen el mismo nmero de caracteres, la regla situada en primer lugar es preferida.

Por ejemplo, supongamos las siguientes reglas:

interger [a-z]*

Keyword action ...; identifier action ...;

si la entrada es integers la expresin seleccionada es la segunda porque casa 8 caracteres, mientras que la primera casa 7. si la entrada es integer las dos casan con igual nmero de caracteres luego se tomar la primera expresin. Por otro lado Lex particiona normalmente la cadena de entrada y no busca los posibles casamientos de cada expresin. Esto significa que cada carcter est acotado a una y slo una. Por ejemplo, supongamos que deseamos contar las ocurrencias de las palabras she y he en un texto de entrada. Algunas reglas lex para este ejercicio seran : she he \n . s++; h++; | ;

las dos ltimas reglas ignoran cualquier cosa que no sea she y he. Vemos que la palabra she incluye la palabra he, lex normalmente no reconocer las instancias de he incluido en she, ya que habrn pasado a she. Algunas veces el usuario podra variar ese hecho. La accin REJECT significa ir a la siguiente alternativa. Despus de ejecutar la regla actual pasa a comprobar la siguiente. La posicin del puntero de entrada es ajustada adecuadamente. Supongamos que el usuario realmente quiere contar las instancias incluidas de he : she he \n . {s++;REJECT;} {h++;REJECT;} | ;

9.6.4.- SECCIN DE FUNCIONES DE USUARIO En esta seccin el usuario podr definir todas las funciones que desee o estime oportunas para el desarrollo de las acciones del analizador y que podrn ser llamadas desde cualquier parte donde haya cdigo C. Esto incluye a la propia funcin principal main(). Por ejemplo, la entrada por defecto de datos al programa final se hace por teclado o bien desde el fichero usando una tubera. Para que la entrada pueda ser por finchero y pasado como parmetro tenemos que cambiar la funcin main().

Void main( int Nang, char *arg[]) { If (narg>1) //nos ha introducido un parmetro que supuestamente es el nombre del fichero. { Yyin=fopen(arg[1],r); //yyin es el puntero de flujo de entrada para PCLEX

If (yyin==NULL) printf(Error el fichero no existe); } While (yylex()!=0) printf( %s , yytext); //suponemos que hemos puesto un return en cada regla. Printf(\nProceso concluido); }

Ejercicios: 1.- a) Eliminar todos los blancos y tabuladores que puedan figurar al final de una lnea. b) Sustituye todos las secuencias de tabuladores y las secuencias de varios espacios en blanco por un solo blanco. 2.- Poner en maysculas la primera letra de todas las palabras que vayan detrs de un punto. 3.- Hacer un contador de las palabras, letras y lneas que figuran en un texto. 4.- Realizar un contador de nmeros y de palabras en un texto.

Das könnte Ihnen auch gefallen