Está en la página 1de 33

Conocer cabalmente cuestiones relativas a Lex y Yacc, conocer los mtodos de anlisis, as como entender las como lex

y Yacc sirven para generar tokenizers y parsers en C, como sirven para reconocer gramticas libres de contexto.

Conocer como Yacc sirve para generar parsers, y usa a lex para leer y reconocer sus tokens y basado en reglas sencillas en formato similar al !", es capaz de ir reduciendo una expresi#n con bastante eficiencia.

Lex y Yacc son las ms conocidas y usadas de todas las $erramientas %ue se emplean en la generaci#n automtica de &partes de' compiladores de los lengua(es de programaci#n. )on utilidades del sistema operativo *nix y los programas %ue producen estn escritos en lengua(e C. +ara entender cabalmente algunas cuestiones relativas a Yacc es preciso conocer los mtodos de anlisis sintctico ascendente debido a %ue el analizador generado pertenece a esa categora, Yacc tambin sirve de apoyo para la implementaci#n de las tareas de anlisis semntico y de generaci#n de c#digo. -stas cuestiones ms avanzadas no se tratan a%u. -n este traba(o se $ace una descripci#n de los aspectos bsicos de las dos $erramientas, con ello se pretende aportar los conocimientos necesarios para la realizaci#n de las prcticas de la asignatura Compiladores e .ntrpretes de esta -scuela *niversitaria, en concreto, se trata de su aplicaci#n para la obtenci#n de los analizadores lexicogrfico y sintctico de los traductores de lengua(es. Lex y Yacc son un par de especificaciones %ue sirven para generar tokenizers y parsers en C %ue reconozcan gramticas libres de contexto, como lengua(es de programaci#n o calculadoras entre otros. Lex es el encargado de leer de la entrada, tpicamente stdin y extraer de la misma los tokens reconocidos por el basado en un lengua(e de expresiones. /mbos, Lex y Yacc, fueron desarrollados en los 012s en los laboratorios ell de /343, y estuvieron disponibles desde la 0a -dici#n de *!.5, versiones antiguas derivadas de )6 seguan usando Lex y Yacc de /343, $asta la aparici#n de flex y bison &/nlogos a lex y yacc respectivamente' %ue cuentan con algunas caracteristicas extra adems de las tradicionales, as como un me(or soporte para reducciones o expresiones muy largas o comple(as.

LEX Y YACC 78ue son y para %ue sirven9 Lex y Yacc son un par de especificaciones %ue sirven para generar tokenizers y parsers en C %ue reconozcan gramticas libres de contexto, como lengua(es de programaci#n o calculadoras entre otros. Lex es el encargado de leer de la entrada, tpicamente stdin y extraer de la misma los tokens reconocidos por el basado en un lengua(e de expresiones regulares. Yacc sirve para generar parsers, usa a lex para leer y reconocer sus tokens y basado en reglas sencillas en formato similar al !", es capaz de ir reduciendo una expresi#n con bastante eficiencia.

Principales implementaciones /mbos, Lex y Yacc, fueron desarrollados en los 012s en los laboratorios ell de /343, y estuvieron disponibles desde la 0a -dici#n de *!.5, versiones antiguas derivadas de )6 seguan usando Lex y Yacc de /343, $asta la aparici#n de flex y bison &/nlogos a lex y yacc respectivamente' %ue cuentan con algunas caracteristicas extra adems de las tradicionales, as como un me(or soporte para reducciones o expresiones muy largas o comple(as. Generalidades *n generador de analizadores es un programa %ue acepta como entrada la especificaci#n de las ca: ractersticas de un lengua(e L y produce como salida un analizador para L. La especificaci#n de en: trada puede referirse a la lexicografa, la sintaxis o la semntica, el analizador resultante servir para analizar las caractersticas especificadas. - -specificaci#n de las caractersticas del lengua(e L

E
/ /nalizador para L

Generador

Los generadores Lex y Yacc sirven, respectivamente, para generar analizadores lexicogrficos y analizadores sintcticos para su aprovec$amiento como partes de los compiladores de los lengua(es de programaci#n, estos usos de Lex y Yacc no son los ;nicos, aun%ue s son los %ue a%u se consi : deran principalmente. +ara entender cabalmente el funcionamiento de los generadores de analizadores, $ay %ue conocer la teora de compiladores relacionada con las tareas de anlisis de lengua(es. Cuando se emplea el trmino Lex, se mencionan dos posibles significados< a' una notaci#n para especificar las caractersticas lexicogrficas de un lengua(e de programaci#n, b' un traductor de especificaciones lexicogrficas. -sta misma dualidad tambin es de aplicaci#n al trmino Yacc.

Lex es un generador de analizadores lxicos. Cada vez %ue Lex encuentra un lexema %ue viene definido por una expresi#n regular, se e(ecutan las acciones &escritas en C' %ue van al lado de la definici#n de dic$a expresi#n. Lex crea yylex, una variable %ue contendr un n;mero, el cual se corresponde con el token de cada expresi#n regular. 3ambin, instancia una variable global yytext, %ue contiene el lexema %ue acaba de reconocer. /s, por e(emplo, para el siguiente c#digo fuente de entrada< ==expresi#n> ?acci#n>@expresi#nA ?acci#nA@... ?acci#nn@== ...expresi#nn

Lex genera un programa en C &generalmente denominado lex.yy.c' %ue incluye, entre otras, la funci#n yylex()<

int yylex&'? B$ile&Ceof&'' ? case >< ?acci#n>@, break, ...

sBitc$&...' ? case :>< ... , break, case 1< ... , break, case n< ?acci#nn@, break, ... @ ... @...@

-sta funci#n recorre el texto de entrada. /l descubrir alg;n lexema %ue se corresponde con alguna expresi#n regular, construye el token, y realiza la acci#n correspondiente. Cuando se alcanza el fin de fic$ero, devolver -1. La funci#n yylex() podr ser invocada desde cual%uier lugar del programa.

Cmo escribir expresiones regulares en Lex. 6ebern cumplir los siguientes re%uisitos<

Las expresiones regulares &ER, de a%u en adelante' $an de aparecer en la primera columna. Alfabeto de entrada< Caracteres /)C.. 0 al 127. Concatenacin< )in carcter especial, se ponen los caracteres (untos. Caracteres normales< )e representan a ellos mismos.

Caracteres especiales< )e les pone la barra 2 \2 delante. -stos son< D E 9 F G H & ' I J . ? @ K L M N O Caracteres especiales dentro de los corc$etes & 2 [2 y 2]2 '< : J K

Las e%uivalencias entre -P normales y las %ue se usan en Lex se muestran con e(emplos en esta tabla<

Caracteres Concatenaci#n *ni#n Pepetici#n

-(emplo xy xFy xD

)ignificado -l patr#n consiste en x seguido de y. -l patr#n consiste en x o en y. -l patr#n consiste en x repetido cero a ms veces. /lternancia de caracteres en el rango indicado, en este caso 1F>FAF...FQ. Rs de un rango se puede especificar, como por e(emplo< G1:Q/:Sa:zH para caracteres alfanumricos. -l primer carcter en una clase de caracteres deber ser K para indicar el complemento del con(unto de caracteres especificado. /s, GK1:QH especifica cual%uier carcter %ue no sea un dgito. Con un ;nico carcter, excepto Jn. Cero o una ocurrencia de x. *na o ms ocurrencias de x. x repetido entre n y m veces. *nifica x s#lo al comienzo de una lnea

Clases de caracteres

G1:QH

Tperador negaci#n

GK1:QH

Carcter arbitrario Pepetici#n ;nica Pepetici#n no nula Pepetici#n especificada Comienzo de lnea

. x9 xE x?n,m@ Kx

"in de lnea

xL

*nifica x s#lo al final de una lnea *nifica ab, pero s#lo seguido de bc.

)ensibilidad al contexto abMcd &operador Ilook a$eadI' Cadenas de literales Caracteres literales IxI Jx

Cuando x tenga un significado especial. Cuando x es un operador %ue se representa a l mismo. 3ambin para el caso de Jn, Jt, etc. +ueden definirse subpatrones. -sto significa incluir el patr#n predefinido llamado nombrevar.

6efiniciones

?nombrevar@

Definiciones en Lex. La secci#n de definiciones permite predefinir cadenas %ue sern ;tiles en la secci#n de las reglas. +or e(emplo< comentario SHletramin IMMI.Dlimitador Ga:zHletra JJnFJJJIdigito &?letramin@F?digito@'Dentero JI?&carascii@F?caresc@'DJI G JtJnHespblanco ?limitador@Eletramay G/: ?letramay@F?letramin@carascii GKJIJnHcaresc G1:QHvariable ?letramin@ ?digito@Etexto

Cada regla est compuesta de un nombre %ue se define en la parte iz%uierda, y su definici#n se coloca en la derec$a. /s, podemos definir comentario como // &con la barra puesto %ue es un carcter especial', seguido por un n;mero arbitrario de caracteres excepto el de fin de lnea. *n limitador ser un espacio, tabulador o fin de lnea, y un espblanco ser uno o ms limitadores. !#tese %ue la definici#n de espblanco usa la definici#n anterior de limitador. Reglas para eliminar ambigedades. )e producen cuando varias -P2s son aplicables a un mismo lexema reconocido. Lex seleccionar siempre la ocurrencia ms larga posible. )i dos ocurrencias tienen la misma longitud, tomar la primera. Notas complementarias sobre Lex.

Cada vez %ue se realice una de las acciones, la variable c$ar Dyytext contendr el lexema reconocido. 7

La variable int yyleng contiene la longitud del lexema reconocido. Entrada y salida< ".L- Dyyin, Dyyout. +or defecto, se usan los predefinidos en C. Cuando Lex reconoce el carcter de fin de fic$ero, llama a la funci#n int yywrap(), %ue por defecto devuelve 1. )i devuelve 0, significar %ue est disponible una entrada anterior, con lo cul a;n no se $abr terminado la lectura. Contextos< +ermiten especificar cuando se usarn ciertas reglas. Ueremos mediante un e(emplo de eliminaci#n de comentarios c#mo se usan los contextos< =start CTR-!3/P.T==JMJD ? -V.! CTR-!3/P.T,@ MD activa CTR-!3/P.T DMNCTR-!3/P.TOJDJM ? -V.! 1,@

/ continuaci#n una lista de las expresiones regulares mas usadas en lex. ps E!emplo Explicacin GH Ga:zH *na clase de Caracteres, coincide con un caracter perteneciente a la clase, pueden usarse rangos, como en el e(emplo, cual%uier caracter, excepto a%uellos especiales o de control son tomados literalmente, en el caso de los %ue no, pueden usarse secuencias de escape como las de C, Jt, Jn etctera. )i su primer caracter es un IKI, entonces coincidir con cual%uier caracter fuera de la clase. D G JnJtHD 3odas las cadenas %ue se puedan formar, se puede decir %ue este operador indica %ue se va a coincidir con cadenas formadas por ninguna o varias apariciones del patr#n %ue lo antecede. -l e(emplo coincide con cual%uier combinaci#n de smbolos usados para separar, el espacio, retorno y tabulador. E G1:QHE 3odas las cadenas %ue se puedan formar, excepto cadenas vacas. -n el e(emplo se aceptan a todos los n;meros naturales y al cero. . .E -ste es una expresi#n regular %ue coincide con cual%uier entrada excepto el retorno de carro &IJnI'. -l e(emplo acepta cual%uier cadena no vaca. ?@ a?W,X@ .ndica un rango de repetici#n cuando contiene dos n;meros separados por 8

comas, como en el e(emplo, la cadena aceptada ser a%uella con longitud W, Y, Z o X formada por el carcter 2a2. .ndica una repetici#n fi(a cuando contiene un solo numero, por e(emplo, a?Z@, aceptara cual%uier cadena formada por Z a2s sucesivas. -n caso de contener un nombre, indica una sustituci#n por una declaraci#n en la secci#n de declaraciones &Pevisar el e(emplo>'. 9 :9G1:QHE .ndica %ue el patr#n %ue lo antecede es opcional, es decir, puede existir o no. -n el e(emplo, el patr#n coincide con todos los n;meros enteros, positivos o negativos por igual, ya %ue el signo es opcional. F &:FEF['9G1: -ste $ace coincidir, al patr#n %ue lo precede o lo antecede y puede usarse QHE consecutivamente. -n el e(emplo tenemos un patr#n %ue coincidir con un entero positivo, negativo o con signo de complemento. II IbyeI Las cadenas encerradas entre I y I son aceptadas literalmente, es decir tal como aparecen dentro de las comillas, para incluir caracteres de control o no imprimibles, pueden usarse dentro de ellas secuencias de escape de C. -n el e(emplo la ;nica cadena %ue coincide es 2bye2. J J. .ndica a lex %ue el caracter a continuaci#n ser tomado literalmente, como una secuencia de escape, este funciona para todos los caracteres reservados para lex y para C por igual. -n el e(emplo, el patr#n coincide solo con el caracter I.I &punto', en lugar de coincidir con cual%uier caracter, como seria el casi sin el uso de IJI. NN-T"OO Ga:zH )olo en flex, este patr#n coincide con el fin de arc$ivo. Agregar "uncionalidad -s posible $acer %ue el lexer se comporte un tanto diferente de los defaults en cuanto a la implementaci#n se refiere, redefiniendo las funciones %ue el lexer usa, algunas de las cosas %ue se pueden $acer es cambiar la entrada, modificar el mane(o de final de arc$ivo, etctera. +ero antes de poder $acer esto, es necesario repasar algunas variables y funciones, %ue se usan dentro de un programa generado por lex. #rototipo Descripcin Contiene el token %ue acaba de ser reconocido, su uso es principalmente dentro de las reglas, donde es com;n $acer modificaciones al token %ue acaba de ser ledo o c$ar Dyytext, usarlo con alg;n otro fin. -n el e(emplo > este token es usado para dar $ec$o en la pantalla. int yyleng, Contiene la longitud del token ledo, su valor es e%uivalente a yyleng \ strlen&yytext',. -s un apuntador del %ue se leen los datos, si este no se modifica, su valor por defecto ".L- Dyyin, es stdin. ".L- Dyyout, -s un apuntador a la salida por default del programa, su valor predefinido es stdout. -sta es en realidad una macro, cuya funci#n es alimentar al tokenizer cada vez %ue int input&void', se le llama, esta regresa el siguiente caracter de la entrada. -sta macro $ace lo contrario a input&', esta pone el caracter especificado como void unput&int', argumento de regreso a la entrada del flu(o. void -sta macro, escribe su argumento en yyout. output&int',

int -s una interfaz para la macro input&'. yyinput&void', void -s una interfaz para la macro unput&'. yyunput&int', void -s una interfaz para la macro output&'. yyoutput&int', -sta funci#n sirve para mane(ar las condiciones de fin de arc$ivo, cada vez %ue el int lexer llega a un fin de arc$ivo, llama a esta funci#n para saber %ue $acer, si regresa yyBrap&void', 1, entonces sigue leyendo de la entrada, si es > el lexer regresa un token nulo para indicar %ue se llego al fin de arc$ivo. -sta es la funci#n principal de un lexer, para a]adir c#digo a esta funci#n, es int yylex&void', necesario, incluirlo en la secci#n de reglas encerrado entre =? y =@. Reimplementaciones $ usos m%s comunes FILE *yyin -ste es un apuntador declarado globalmente %ue apunta al lugar de donde se van a leer los datos, por ser un file pointer, este solo puede leer de flu(os como arc$ivos, para leer de una cadena es necesario reimplementar el macro input&' como se vera mas adelante.

FILE *yyout -ste es el lugar al %ue se escriben por default todos los mensa(es, al igual %ue yyin esta declarado globalmente y es un apuntador. int input(void) -l ob(etivo de esta Racro es alimentar a yylex&' caracter por caracter, devuelve el siguiente caracter de la entrada, la intenci#n ms com;n para modificar esta funci#n, es cambiar el origen de la entrada de manera mas flexible %ue con yyin, ya %ue no solo es posible leer de otro arc$ivo, sino %ue tambin es posible leer el flu(o para parsear una cadena cual%uiera, o un grupo de cadenas como una lnea de comandos. +ara reimplementar esta macro, es necesario primero eliminarla del arc$ivo por lo %ue es necesario incluir un comando del preprocesador de C la secci#n de declaraciones < =? ^undef input =@ y en la parte de subrutinas, implementar nuestro nuevo input&' con el prototipo mostrado anteriormente. void unput(int) -l ob(etivo de esta macro, es regresar un caracter a la entrada de datos, es ;til para yylex&' tener una de estas, ya %ue para identificar un patr#n puede ser necesario saber %ue caracter es el %ue sigue. La intenci#n de reimplementar esta es complementar el uso de la reimplementacion de input&', ya %ue input&' y unput&' deben ser congruentes entre si.

10

/ntes de reimplementar esta, tambin es necesario eliminarla antes usando una instrucci#n del preprocesador de C< =? ^undef unput =@ int yywrap(void) -sta funci#n, es auxiliar en el mane(o de condiciones de final de arc$ivo, su misi#n es proporcionarle al programador la posibilidad de $acer algo con estas condiciones, como continuar leyendo pero desde otro arc$ivo etctera. int yylex(void) -sta funci#n, es casi totalmente implementada por el usuario en la secci#n de reglas, donde como ya vimos, puede agregarse c#digo encerrado entre =? y =@ as como en las reglas mismas. #rogramacin de anali&adores mediante LEX. -n este apartado y en el siguiente se describe una $erramienta particular, llamada Lex, %ue $a sido ampliamente usada para especificar analizadores lxicos para una variedad de lengua(es. )e $ar referencia a la $erramienta como el compilador Lex, y a su especificaci#n de entrada como el lengua!e Lex. La discusi#n de una $erramienta existente nos permitir mostrar como la especificaci#n de patrones usando expresiones regulares puede estar combinada con acciones, como por e(emplo, crear entradas en una tabla de smbolos, expandir macros, o incluso generar documentaci#n automticamente. -l programa Lex est dise]ado para ser utilizado (unto con el programa Yacc, %ue como se ver en el captulo .U es un generador de analizadores sintcticos. Lex suele ser usado seg;n la siguiente figura<

11

+rimero, se prepara una especificaci#n de un analizador lxico creando un programa contenido, por e(emplo en el fic$ero prog.l, en lengua(e Lex. -ntonces, prog.l se pasa a travs del compilador Lex para producir un programa en C, %ue por defecto se denomina lex.yy.c en el sistema operativo *!.5. _ste consiste en una representaci#n tabular de un diagrama de transici#n construido a partir de las expresiones regulares de prog.l, (unto con una rutina estndar %ue usa la tabla de reconocimiento de lexemas. Las acciones asociadas con expresiones regulares en prog.l son trozos de c#digo C, y son transcritas directamente a lex.yy.c. "inalmente, lex.yy.c se pasa a travs del compilador C para producir un programa ob(eto, %ue por defecto se llama a.out, el cual es el analizador lxico %ue transforma una entrada en una secuencia de tokens. *n programa Lex consta de tres secciones< 'declaraciones( == 'reglas == 'procedimientos auxiliares(

de

traduccin(

La seccin de declaraciones incluye declaraciones de variables, constantes y definiciones regulares. Las definiciones regulares son sentencias usadas como componentes de las expresiones regulares %ue aparecen en las reglas. Las reglas de traduccin de un programa Lex son sentencias de la forma< p> pA ... pn ? acci#nn @ ? ? acci#n> acci#nA @ @ ...

12

donde cada pi es una expresi#n regular y cada acci#ni es un fragmento de programa, describiendo %u acci#n debe realizar el analizador lxico cuando el patr#n pi se corresponde con un lexema. -n Lex, las acciones estn escritas en C. La tercera secci#n contiene cuales%uiera procedimientos auxiliares %ue sean re%ueridos por las acciones. /lternativamente, estos procedimientos pueden ser compilados separadamente y montados (unto con el analizador lxico. *n analizador lxico creado por Lex funciona en concierto con un analizador sintctico de la siguiente manera. Cuando es activado por el analizador sintctico, el analizador lxico comienza leyendo de su entrada un carcter a la vez, $asta %ue encuentre el prefi(o ms largo de la entrada %ue $a correspondido con una de las expresiones regulares pi. -ntonces, e(ecuta acci#ni, %ue tpicamente devolver el control al parser. +ero, si no lo $ace, entonces el analizador lxico procede a buscar ms lexemas, $asta %ue una acci#n contenga una sentencia return o se lea el fic$ero completo. La b;s%ueda repetida de lexemas $asta una devoluci#n explcita del control permite %ue el analizador lxico procese los espacios en blanco y comentarios convenientemente. -l analizador lxico devuelve un entero, %ue representa el token, al analizador sintctico. +ara pasar un valor de atributo con informaci#n sobre el lexema, se puede usar una variable global llamada yylval. -sto se $ace cuando se use Yacc como generador del analizador sintctico. Los analizadores lxicos, para ciertas construcciones de lengua(es de programaci#n, necesitan ver adelantadamente ms all del final de un lexema antes de %ue puedan determinar un token con certeza. -n Lex, se puede escribir un patr#n de la forma r1/r2, donde r1 y r2 son expresiones regulares, %ue significa %ue una cadena se corresponde con r1, pero s#lo si est seguida por una cadena %ue se corresponde con r2. La expresi#n regular r2, despus del operador loo a!ead IMI, indica el contexto derec$o para una correspondencia, se usa ;nicamente para restringir una correspondencia, no para ser parte de la correspondencia. Recuperacin de errores lexicogr%ficos) Los programas pueden contener diversos tipos de errores, %ue pueden ser<

Errores lexicogr%ficos) 8ue veremos a continuaci#n. Errores sintacticos) +or e(emplo, una expresi#n aritmtica con mayor numero de parntesis de apertura %ue de cierre. Errores sem%nticas) +or e(emplo, la aplicaci#n de un operador a un tipo de datos incompatible con el mismo. Errores lgicos) +or e(emplo, un bucle sin final.

Cuando se detecta un error, un compilador puede detenerse en ese punto e informar al usuario, o bien desec$ar una serie de caracteres del texto fuente y continuar con el anlisis, dando al final una lista completa de todos los errores detectados. -n ciertas ocasiones es incluso posible %ue el compilador corri(a el error, $aciendo una interpretaci#n co$erente de los caracteres ledos. -n estos casos, el compilador emite una advertencia, indicando la suposici#n %ue $a tomado, y contin;a el proceso sin afectar a las sucesivas fases de compilaci#n.

13

Los errores lexicogrficos se producen cuando el analizador no es capaz de generar un token tras leer una determinada secuencia de caracteres. -n general, puede decirse %ue los errores lexicogrficos son a los lengua(es de programaci#n lo %ue las faltas de ortografa a los lengua(es naturales. Las siguientes situaciones producen con frecuencia la aparici#n de errores lexicogrficos< >. Lectura de un carcter que no pertenece al vocabulario Terminal previsto para el autmata. Lo ms normal en este caso es %ue el aut#mata ignore estos caracteres extra]os y continu el proceso normalmente. +or e(emplo, pueden dar error en la fase de anlisis lexicogrfico la inclusi#n de caracteres de control de la impresora en el programa fuente para facilitar su listado. A. misin de un carcter. +or e(emplo, si se $a escrito "#$ en lugar de "#$". W. !e "a introducido un nuevo caracter. +or e(emplo, si escribimos "#$$" en lugar de "#$". Y. #an sido permutados dos caracteres en el to$en anali%ado. +or e(emplo, si escribiramos "$#" en lugar de "#$". 5. &n carcter "a sido cambiado. +or e(emplo, si se escribiera -LS- en vez de -L)-. Las tcnicas de recuperaci#n de errores lexicogrficos se basan, en general, en la obtenci#n de los distintos sin#nimos de una determinada cadena %ue $emos detectado como err#nea. +or otra parte, el analizador sintctico es capaz en muc$os casos de avisar al analizador lexicogrfico de cul es el token %ue espera %ue ste lea. +ara el e(emplo de borrado de un carcter, tenemos %ue los sin#nimos de -L)- son -L), -L-, -)-, y L)-. +or tanto, si incluimos en nuestro analizador una rutina de recuperaci#n de errores debidos a omisi#n de caracteres, cual%uiera de estos sin#nimos sera aceptado en lugar del lexema -L)-, se emitira la correspondiente advertencia, y el proceso continuara asumiendo %ue se $a ledo el token Npal`res`-L)-O. /nlogamente, podemos incluir rutinas para los dems casos. +or e(emplo, si el analizador lee el lexema -)L-, y no puede construir un token correcto para l mismo, procedera a generar los sin#nimos por intercambio de caracteres &es decir, )-L-, -L)- o -)-L' y comprobara si alguno de ellos es reconocible. -n caso afirmativo, genera el token correspondiente y advierte al usuario del posible error y de su interpretaci#n automtica, continuando con el proceso. 3odos los procedimientos para la recuperaci#n de errores lexicogrficos son en la prctica mtodos especficos, y muy dependientes del lengua(e %ue se pretende compilar.

Y/CC significa Iotro compilador de compiladores msI &del ingls 'et Anot"er Compilers(Compiler', lo %ue refle(a la popularidad de los generadores de analizadores sintcticos al principio de los a]os setenta, cuando ). C. ao$nson cre# la primera versi#n de Y/CC. -ste generador se encuentra

14

disponible como una orden del sistema *!.5, y se $a utilizado para facilitar la implantaci#n de cientos de compiladores. +ara construir un traductor utilizando Y/CC primero se prepara un arc$ivo, por e(emplo trad%ce.y, %ue contiene una especificaci#n en Y/CC del traductor. La orden del sistema *!.5 yacc traduce.y transforma al arc$ivo traduce.y en un programa escrito en C llamado y.tab.c, %ue implementa un analizador sintctico escrito en C, (unto con otras rutinas en C %ue el usuario pudo $aber preparado en el fic$ero traduce.y. +osteriormente, se compila el fic$ero y.tab.c y se obtiene el programa ob(eto deseado %ue realiza la traducci#n especificada por el programa original en Y/CC. )i se necesitan otros procedimientos, se pueden compilar o cargar con y.tab.c, igual %ue en cual%uier programa en C. *n programa fuente en Y/CC tiene tres secciones< declaraciones == reglas ==rutinas en C de apo$o

de

traduccin

La parte de declaraciones. bay dos secciones opcionales en la parte de declaraciones de un programa en Y/CC< o -n la primera secci#n, se ponen declaraciones ordinarias en C, delimitadas por & ' y &(. /%u se sit;an las declaraciones de todas las variables temporales usadas por las reglas de traducci#n o los procedimientos de la segunda y tercera secciones. +or e(emplo< =? ^include Nstring.$O =@

3ambin en la parte de declaraciones $ay declaraciones de los componentes lxicos de la gramtica. +or e(emplo< =token 6.V.3T declara %ue )*+*,- es un componente lxico o token. Los componentes lxicos %ue se declaran en esta secci#n se pueden utilizar despus en la segunda y tercera partes de la especificaci#n en Y/CC.

La parte de las reglas de traduccin . -n la parte de la especificaci#n en Y/CC despus del primer par == se escriben las reglas de traducci#n. Cada regla consta de una producci#n de la gramtica y la acci#n semntica asociada. *n con(unto de producciones como< N lado iz%uierdo O :O N alt> O F N altA O ... F N altn O

15

-n Y/CC se escribira< N lado iz%uierdo O < N alt> O ? acci#n semntica > @ F N altA O semntica A @ .... F N altn O ? acci#n semntica n @ ? acci#n ,

-n una producci#n en Y/CC, un carcter simple entrecomillado 2c2 se considera como el smbolo terminal c, y las cadenas sin comillas de letras y dgitos no declarados como componentes lxicos se consideran smbolos no terminales. Los lados derec$os alternativos de las reglas se pueden separar con una barra vertical, y un smbolo de punto y coma sigue a cada lado iz%uierdo con sus alternativas y sus acciones semnticas. -l primer lado iz%uierdo se considera, por defecto, como el smbolo inicial. *na acci#n semntica en Y/CC es una secuencia de sentencias en C. -n una acci#n semntica, el smbolo .. se refiere al valor del atributo asociado con el no terminal del lado iz%uierdo, mientras %ue .i se refiere al valor asociado con el i: simo smbolo gramatical &terminal o no terminal' del lado derec$o. La acci#n semntica se realiza siempre %ue se reduzca por la producci#n asociada, por lo %ue normalmente la acci#n semntica calcula un valor para .. en funci#n de los .i. )i la regla no especifica ninguna acci#n semntica, la acci#n semntica por omisi#n es '.. / .10(.

La parte de las rutinas de apo$o en C. La tercera parte de una especificaci#n en Y/CC consta de rutinas de apoyo escritas en C. +ara %ue el analizador sintctico funcione, se debe proporcionar un anlisis lxico de nombre yylex&'. -n caso necesario se pueden agregar otros procedimientos, como rutinas de recuperaci#n de errores. -l analizador lxico yylex() produce pares formados por un componente lxico y su valor de atributo asociado. )i se devuelve un componente lxico como )*+*,-, el componente lxico se debe declarar en la primera secci#n de la especificaci#n en Y/CC. -l valor del atributo asociado a un componente lxico se comunica al analizador sintctico mediante una variable especial %ue se denomina yyl1al.

*so de YACC con gram%ticas ambiguas . )i la gramtica de la especificaci#n en Y/CC es ambigua se producen conflictos en las acciones del analizador sintctico. Y/CC informar del n;mero de conflictos en las acciones del anlisis sintctico %ue se produzcan. )e puede obtener una descripci#n de los con(untos de elementos y de los conflictos en las acciones de anlisis sintctico invocando a Y/CC con la opci#n +,. -sta opci#n generar un arc$ivo adicional llamado y.output %ue contiene los n;cleos de los con(untos de elementos encontrados por el analizador sintctico, una descripci#n de los conflictos en las acciones del anlisis, y una representaci#n legible de la tabla de anlisis sintctico LR %ue muestra c#mo se resolvieron los conflictos de las acciones del anlisis sintctico. / menos %ue se ordene lo contrario, Y/CC resolver todos los conflictos en las acciones del anlisis sintctico utilizando las dos reglas siguientes<

16

>. *n conflicto de reduccin-reduccin se resuelve eligiendo la regla en conflicto %ue se $aya escrito primero en la especificaci#n. A. *n conflicto de despla&amiento-reduccin se resuelve en favor del desplazamiento. Como estas reglas %ue se siguen por omisi#n, no siempre refle(an lo %ue %uiere el escritor del compilador, Y/CC proporciona un mecanismo general para resolver los conflictos de despla%amiento)reduccin. -n la parte de declaraciones, se pueden asignar precedencias y asociatividades a los terminales. La declaraci#n =left 2E2 2:2 $ace %ue 232 y 2-2 tengan la misma precedencia y %ue sean asociativos por la iz%uierda. )e puede declarar %ue un operador es asociativo por la derec$a diciendo =rig$t 2K2 y se puede obligar a un operador a ser un operador binario no asociativo &por e(emplo, dos casos del operador no se pueden combinar en absoluto' diciendo< =nonassoc 2N2 / los componentes lxicos se les dan precedencias en el orden en %ue aparecen en la parte de declaraciones, siendo los primeros los de menor precedencia. Los componentes lxicos de una misma declaraci#n tienen la misma precedencia. /s, la declaraci#n =rig$t R-!T)* dara al componente lxico R-!T)* un nivel de precedencia mayor %ue a los terminales declarados anteriormente. Y/CC resuelve los conflictos de despla%amiento)reduccin asociando una precedencia y asociatividad a cada producci#n implicada en un conflicto, as como a cada terminal implicado en un conflicto. )i debe elegir entre despla%ar el smbolo de entrada a y reducir por la producci#n /:Oa, Y/CC reduce si la precedencia de la producci#n es mayor %ue la de a o si las precedencias son las mismas y la asociatividad de la regla es =left. 6e lo contrario se elige la acci#n de despla%ar. Veneralmente, la precedencia de una producci#n se considera igual a la del smbolo terminal situado ms a la derec$a. -n la mayora de los casos, esta es la decisi#n sensata. -n las situaciones en %ue el smbolo terminal situado ms a la derec$a no proporcione la precedencia adecuada a una producci#n, se puede forzar una precedencia a]adiendo al final a la regla la eti%ueta =prec N terminal O -ntonces, la precedencia y asociatividad de la producci#n sern las mismas %ue las de terminal, %ue se define seguramente en la secci#n de declaraciones. Y/CC no informa de los conflictos de

17

despla%amiento)reduccin %ue se resuelven utilizando este mecanismo de precedencia y asociatividad. -ste IterminalI puede ser simplemente un marcador. -s decir, el terminal no es devuelto por el analizador lxico, sino %ue se declara tan s#lo para definir una precedencia para una producci#n. +or e(emplo, expr < 2:2 expr =prec R-!T)* $ace %ue la regla anterior tenga la precedencia correspondiente al token R-!T)*, en lugar de la del token 2:2.

*sando Yacc Conceptos .%sicos -n la secci#n anterior de este documento, ya repasamos como generar un analizador lxico, un lexer o tokenizer, %ue puede reconocer patrones de expresiones regulares y en un momento dado determinar %ue es lo %ue se esta leyendo, pero para aplicaciones un poco ms comple(as, tambin puede ser necesario, analizar gramaticalmente la composici#n de la entrada para en un momento dado, determinar si la entrada coincide o no con una gramtica definida y resolverla, o darle alg;n significado un tanto mas comple(o. -s correcto decir %ue yacc es una $erramienta %ue sirve para generar un programa, capaz de analizar gramaticalmente una entrada dada por lex, a partir de una especificaci#n. -sta especificaci#n, debe contener los tokens reconocidos y los tipos de datos de los mismos si es %ue se ocupan para realizar operaciones sobre ellos, y una especificaci#n de gramtica en un formato similar a !" & ackus !aus "orm', %ue va desde el simbolo no terminal ms general a cada una de las opciones terminales. -(emplo < N-xpresionO !umero E N-xpresionO !umero : N-xpresionO !umero

-n el e(emplo podemos ver %ue N-xpresionO es un simbolo no terminal %ue esta compuesto por un I!umeroI terminal seguido de un simbolo 2E2 o 2:2 terminales seguido por un N-xpresionO no terminal, %ue a su vez puede ser otro numero u otra expresi#n mas comple(a. -s importante notar %ue esta especificaci#n es recursiva sobre N-xpresionO pero no es ambigua, es decir, siempre se llegara a un terminal. *na especificaci#n yacc se divide en tres secciones diferentes de manera similar a lex, la de definiciones, la de reglas, y la de subrutinas, %ue van igualmente separadas por un 2==2, mismas %ue pueden incluir c#digo de C encerrado entre un =? y un =@.

18

E!emplo /./ 01ini calculadora2 / continuaci#n, vamos a analizar un e(emplo sencillo de una verdadera especificaci#n de yacc, %ue es la gramtica para una calculadora sencilla %ue permite $acer operaciones como suma, resta, multiplicaci#n, divisi#n y exponente. 6oBnload e(em>.>.y 6oBnload e(em>.>.l =? ^include Nmat$.$O =@ =union? double dval, @ =token =token =token =token NdvalO !*R -P +L*) R.!*) 3.R-) 6.U.6- +Tc-P L-"3`+/P-!3b-).) P.Vb3`+/P-!3b-).) -!6

=left +L*) R.!*) =left 3.R-) 6.U.6=left !-V =rig$t +Tc-P =type NdvalO -xpression =start .nput == .nput< , Line< -!6 F -xpression -!6 , !*R -P ? printf&IPesult< =fJnI,L>', @ ? LL\L>, @ Line F .nput Line

-xpression<

F -xpression +L*) -xpression ? LL\L>ELW, @ F -xpression R.!*) -xpression ? LL\L>:LW, @ F -xpression 3.R-) -xpression ? LL\L>DLW, @ F -xpression 6.U.6- -xpression ? LL\L>MLW, @ F R.!*) -xpression =prec !-V ? LL\:LA, @

19

F -xpression +Tc-P -xpression ? LL\poB&L>,LW', @ F L-"3`+/P-!3b-).) -xpression P.Vb3`+/P-!3b-).) ? LL\LA, @ , == int yyerror&c$ar Ds' ? printf&I=sJnI,s', @ int main&void' ? yyparse&', @ Definiciones -n esta primera secci#n, al igual %ue en lex, incluimos las libreras %ue usaremos en el programa, definiciones de los tokens, tipos de datos y precedencia de la gramtica. %unin -sta definici#n, se traduce a una uni#n de C %ue a su vez dar el tipo de dato a una variable global de nombre yylval %ue ser de donde yacc tomara los datos a procesar, en la uni#n se definen miembros cuyos correspondientes tipos de datos sern usados para dar el tipo de dato a los tokens como se explicara en la siguiente secci#n. =uni#n se traduce de la siguiente forma < -n yacc < =uni#n? double dval, @ -n C < typedef uni#n ? double dval, @ YY)3Y+-, Con esta definici#n, yacc declara algunas uniones de este tipo, de las cuales la ms importante es < YY)3Y+- yylval, %ue ser usada en la especificaci#n de lex, del mismo programa para asignarle valor a los tokens %ue yacc usara para realizar operaciones. -sta estructura puede llegar a ser muy comple(a, y para saber de %ue tipo es cada token devuelto por yylex&', se usan las definiciones =token y =type. %token y %type =token sirve para definir los tokens %ue $ay, y si es necesario, el tipo de dato %ue usan, todos los tokens son tomados como smbolos terminales, lo cual veremos me(or refle(ado en la secci#n de reglas, estos tambin tienen el ob(etivo de servir como eti%uetas %ue yylex&' regresa a yacc para identificar el token %ue se $a ledo recientemente.

20

)u uso es como sigue < =token GNmiembro`de`unionOH -3.8*-3/> G-3.8*-3/A ... -3.8*-3/nH 6onde todo lo %ue esta entre G y H es opcional. Nmiembro`de`unionO < .ndica el miembro al %ue sern mapeados los tokens en la union yylval dentro de lex. -3.8*-3/) < -stos son los nombres con los %ue se identificaran los tokens mismos, %ue sern traducidos en C como n;meros en instrucciones ^define del preprocesador de C. =type es anlogo a =token, solo %ue este define el tipo de dato para smbolos no terminales de nuestra gramtica, la ;nica diferencia es %ue el tipo de dato a usar es obligatorio. -n nuestro e(emplo < =token NdvalO !*R -P =token +L*) R.!*) 3.R-) 6.U.6- +Tc-P =token L-"3`+/P-!3b-).) P.Vb3`+/P-!3b-).) =token -!6 . . . =type NdvalO -xpresi#n La primera lnea indica %ue el token !dR-PT ser del tipo de miembro de dval, es decir, un double. Las siguientes tres lneas, son para definir algunos tokens mas %ue sern usados en la gramtica, pero no necesitan un tipo de dato ni un miembro en yylval asociado. -n la ;ltima lnea definimos el tipo de dato %ue usara nuestro no terminal -xpresi#n. %le t y %ri!"t -l siguiente paso, es definir el tipo de precedencia de nuestros tokens operadores, en este punto tenemos dos factores, la precedencia por si misma, y la agrupaci#n de los operadores. #recedencia La precedencia es asignada en orden inverso al %ue aparecen, es decir, el ;ltimo operador declarado, tiene mayor precedencia %ue el anterior y as sucesivamente.

Asociati,idad =left y =rig$t indican si el operador se agrupa a la derec$a o a la iz%uierda, por e(emplo, en el caso de +Tc-P &-xponente' debe asociarse a la derec$a, por %ue buscamos %ue se resuelva de ese modo, de derec$a a iz%uierda, por e(emplo < uscamos %ue YKWKZKAKQ sea evaluado as < YK&WK&ZK&AKQ'''

21

+or lo contrario, las sumas y restas %ueremos resolverlas de iz%uierda a derec$a< uscamos %ue Y:WEZEA:Q sea evaluado as < &&&Y:W'EZ'EA':Q *sar este tipo de declaraciones es importante para disminuir la posibilidad de ambigeedades en el lengua(e generado. %start -n algunos casos es conveniente indicarle a yacc cual es el simbolo &no terminal' inicial a la $ora de $acer el parseo, es decir, el simbolo %ue se trata de reducir, si esta opci#n no es especificada, yacc toma al primer simbolo de la secci#n de reglas como simbolo inicial. -n nuestro e(emplo, se presentan ambos casos, nuestro simbolo inicial I.nputI se encuentra al inicio del arc$ivo y tambin esta declarado como simbolo inicial. =start .nput Reglas -n esta parte finalmente llegamos a la definici#n de la gramtica, aca podemos observar %ue cada simbolo se define con su nombre, seguido de dos puntos I<I seguidos de varios smbolos %ue conformaran su composici#n gramatical %ue en caso de tener varias opciones, son separados por IFI &or' indicando %ue se tienen varias opciones para reducir ese simbolo y para terminar cada regla, un I,I. -(emplo < )i tomamos la gramtica %ue definimos al principio de esta secci#n < N-xpresionO !umero E N-xpresionO !umero : N-xpresionO !umero Y la transformamos a una regla de yacc, se vera como esto< -xpresion< !*R-PT 2E2 -xpresion F !*R-PT 2:2 -xpresion F !*R-PT , ?LL \ L> E LW,@ ?LL \ L> : LW,@ ?LL \ L>,@

en el e(emplo ya transformado a una regla gramatical de yacc, podemos ver %ue ya se especifica %ue $acer con los smbolos de la gramtica una vez %ue son resueltos en la parte de c#digo de C. -n este e(emplo, -xpresion es el simbolo no terminal %ue se esta definiendo de manera recursiva, el valor %ue tomara una vez resuelto es el valor asignado a la variable LL, %ue traducida a C es una variable mapeada al simbolo no terminal, L>, LA y LW son variables %ue son mapeadas al valor de los smbolos de cada lnea de cada regla, estas son numeradas de iz%uierda a derec$a. -(emplo < -n este segmento de regla < -xpresion< !*R-PT 2E2 -xpresion

22

-xpresion e%uivale a LL !*R-PT e%uivale a L> 2E2 e%uivale a LA y -xpresion &la parte recursiva' e%uivale a LW todo esto claro, en la parte de acciones en C para cada lnea de la regla en yacc. -n el e(emplo tambin podemos encontrar el uso de =prec %ue sirve para cambiar la precedencia de un operador dependiendo del contexto en el e(emplo, le estamos dando una ms alta precedencia a la operaci#n ImenosI cuando esta en un contexto unario, %ue a la mayora de los operadores excepto el +Tc-P &exponente' < F R.!*) -xpresi#n =prec !-V ? LL\:LA, @ .Peducci#n Yacc reduce sus reglas generando un parse tree &no literalmente', y va resolviendo cada regla completa tan pronto como puede, lo cual nos trae un detalle de dise]o de gramticas en yacc, y es la diferencia entre especificar la recursividad por la derec$a o por la iz%uierda, para expresiones muy sencillas %ue generen un parse tree pe%ue]o no $ay ning;n problema pero para casos donde la reducci#n es comple(a, puede desbordar la pila ya %ue cuando la recursion es derec$a, para resolverla, tiene %ue guardar los datos de la iz%uierda, y si estos son demasiados, no puede mane(arlos. +or lo contrario, cuando la recursion es iz%uierda, no tiene %ue guardar datos %ue no va a utilizar por %ue recorre el rbol de iz%uierda a derec$a y resuelve las reglas tan pronto como puede. -n el e(emplo anterior tenemos la recursion por la derec$a, un anlogo recurrente por la iz%uierda seria como este < -xpresion< -xpresion 2E2 !*R-PT F -xpresion 2:2 !*R-PT F !*R-PT , ?LL \ L> E LW,@ ?LL \ L> : LW,@ ?LL \ L>,@

especi icacin de Lex para el e#emplo$%$ +ara %ue el -(emplo>.> pueda funcionar, al igual %ue cual%uier otro programa en yacc, necesita un tokenizer, y a continuaci#n tenemos su tokenizer correspondiente escrito en lex. =? ^include Iy.tab.$I ^include Nstdlib.$O ^include Nstdio.$O =@ B$ite G JtHE 23

digit integer ==

G1:QH ?digit@E

?B$ite@ ? MD .gnoramos espacios en blanco DM @ IexitIFI%uitIFIbyeI ?printf&I3erminando programaJnI',exit&1',@ ?integer@ ? yylval.dval\atof&yytext', return&!*R -P', @ IEI return&+L*)', I:I return&R.!*)', IDI return&3.R-)', IMI return&6.U.6-', IKI return&+Tc-P', I&I return&L-"3`+/P-!3b-).)', I'I return&P.Vb3`+/P-!3b-).)', IJnI return&-!6', == /cerca de la secci#n de definiciones de este lexer, lo ;nico relevante %ue podemos mencionar es la lnea de C %ue dice < ^include Iy.tab.$I

esta lnea incluye al arc$ivo y.tab.$ %ue contiene algunas de las definiciones de yacc %ue lex necesita para poder interactuar con el, entre las mas importantes se encuentran definidas todas las eti%uetas de los tokens, como +L*), R.!*), !*R -P, etctera. -stas constantes son los valores %ue yylex&' regresara a yyparse&' &la funci#n del parser de yacc' para identificar el tipo de token %ue recin se $a ledo. -n la secci#n de reglas, en la parte del c#digo, podemos ver como al final de cada regla, se $ace un return especificando la eti%ueta %ue fue declarada como =token o como =leftM=rigt$ en la especificaci#n yacc. +ara compilar y correr este e(emplo en sistemas *!.5 o similares < L lex e(em>.>.l L yacc :d e(em>.>.y L cc :o e(em>.> lex.yy.c y.tab.c :ly :ll :lm L e(em>.> AZDZ:Z Pesult< >A1.111111 ZKADA Pesult< Z1.111111 24

ZK&ADA' Pesult< XAZ.111111 bye 3erminando programa L 3ubrutinas -n esta ;ltima secci#n, es posible reimplementar, siguiendo la misma idea de lex, algunas funciones %ue pueden ser ;tiles en alg;n momento dado o declarar nuevas funciones para usar dentro de nuestro c#digo o nuestras reglas, no $ay muc$o %ue reimplementar a este nivel &yacc' a menos %ue sea con prop#sitos realmente especficos. Las funciones mas com;nmente implementadas son main&' e yyerror&', la primera se usa para personalizar el programa con mensa(es antes o despus del parser, o para llamarlo varias veces en el c#digo y la segunda la ocupa yyparse&' cada vez ue encuentra un error de sintaxis, en este e(emplo, se incluyen ambas con su contenido mnimo.

Yacc &Yet Anot4er Compiler Compiler' es un programa %ue permite construir analizadores gramaticales en C a partir de una gramtica libre al contexto. aunto con Lex permite construir rpidamente las primeras etapas de un traductor. 3anto Lex como Yacc son $erramientas %ue se fueron desarrolladas (unto con el sistema *!.5 y es posible encontrar implementaciones de ellas en

25

otras plataformas como +C y Racintos$, al la vez %ue existen versiones de dominio p;blico ba(o los nombres de "lex y ison. *n analizador gramatical construido en Yacc genera una funci#n %ue realizar el anlisis gramatical con el nombre de yyparse&', esta funci#n solicita un token al analizador de lxico por medio de la funci#n yylex&'. La comunicaci#n %ue se da entre ambas funciones se muestra en el siguiente diagrama.

*n programa en Yacc tiene tres secciones. -stas secciones estn delimitadas por == como en Lex. La estructura del programa en Yacc es< 6efinici#n del ambiente del analizador gramatical == Vramtica libre al contexto == +rocedimientos auxiliares

La secci#n de definici#n del ambiente del analizador gramatical posee una secci#n donde se $acen definiciones sobre las variables %ue el analizador emplear. -sta secci#n esta delimitada por =? y =@ , en ella se $acen definiciones en C y se toma literalmente &Yacc no la revisa'. -n esta primera secci#n encontramos una serie de directivas de yacc %ue permiten definir< Los smbolos terminales %ue la =token gramtica emplear. -l axioma o smbolo inicial de la =start gramtica.

26

=union ? tipo> Los atributos %ue los smbolos de tipoA la gramtica pueden poseer. ... tipon @ -l atributo %ue cada smbolo =type NatributoO posee.

atributo>, atributoA, atributon,

La segunda secci#n est constituida por la gramtica libre al contexto %ue guiar al analizador gramatical. La notaci#n empleada es< noterminal F F ... F , < reglaW regla! regla> reglaA

donde regla> a regla! representan las ! producciones %ue tiene el noterminal en la gramtica. La tercer secci#n de un programa en Yacc tiene los procedimientos auxiliares %ue $acen operativo al analizador de lxico. -n lo ms simple, posee las funciones main y yyerror. -sta ;ltima sirve para especificar la reacci#n del analizador gramatical al encontrar un error. main simplemente invoca al analizador gramatical. *n caso pr%ctico )upongamos %ue se %uiere construir un analizador gramatical para revisar expresiones aritmticas constituidas por identificadores, n;meros enteros y los operadores de suma, resta, multiplicaci#n y divisi#n. -l programa en Yacc para determinar si una expresi#n est bien o mal escrita es el siguiente< =? =@ =token .6 !*R +/ +C T+)P T+R6 =start !.U-LA == !.U-LA < !.U-LA T+)P ).V`!.U-L F ).V`!.U-L , ).V`!.U-L < ).V`!.U-L T+R6 T+-P/!6T

27

F T+-P/!6T , T+-P/!6T < .6 F !*R F +/ !.U-LA +C , == main&' ? yyparse&', @ yyerror&c$ar D s' ? printf&I=s JnI,s', @ /l procesar este arc$ivo &llammoslo anagram.y' en Yacc a travs de la siguiente orden< yacc :d anagram.y se generar el programa anagram.c %ue contendr la funci#n yyparse&' y el arc$ivo yytab.$ &a consecuencia de la opci#n :d' %ue contiene la designaci#n de los valores a los tokens empleados en la gramtica &especificados por la directiva =token'. Yacc asigna los valores a los tokens a partir del AZ0, as %ue =token .6 !*R +/ +C T+)P T+R6 genera en yytab.$ las siguientes definiciones< ^define ^define ^define ^define ^define ^define T+R6 AXA .6 !*R +/ +C T+)P AZ0 AZf AZQ AX1 AX>

La funci#n yyerror&c$ar D s' espera una secuencia de caracteres %ue representa el mensa(e de error %ue el analizador gramatical encuentra, el mecanismo ms simple de mane(o es desplegar ese error &%ue es lo %ue $ace en este caso la funci#n '. -l programa en Lex %ue realizar el anlisis de lxico &analex.l' para devolver el token para cada unidad de lxico ser< =?

28

^include Iyytab.$I =@ L G/:SH 6 G1:QH G JtJnH == ?L@E ?6@E GJEJ:H GJDJMH GJ&H GJ'H ? @ . == -n el programa en Lex incluye al arc$ivo yytab.$ para %ue al reconocer a cada patr#n, devuelva el token como est definido en el programa anagram.y. Los dos programas generados, anagram.c y analex.c, se unen para formar un solo e(ecutable %ue realizar el anlisis gramatical. Como ya se mencion#, yyparse&' ya tiene invocaciones a yylex&'. )upongamos a$ora %ue lo %ue %uiere obtener no es solo saber si la expresi#n aritmtica est bien o mal escrita, sino evaluarla si es correcta gramaticalmente. )e espera tener un valor numrico entero para cada operando en la expresi#n y un caracter para saber %u operador aplicar &ya %ue se tienen en las categoras T+)P cuando se es suma o resta y T+R6 cuando es multiplicaci#n o divisi#n'. -n Yacc esto se especifica con =union de la siguiente manera< =union ? int valor, c$ar operador, @ return .6, return !*R, return T+)P, return T+R6, return +/, return +C,

/$ora $ay %ue designar %ue tipo de atributo se aplicar a cada smbolo en la gramtica, un operando puede ser id, num, T+6T, )! y !A, por lo stos tendrn el atributo valor, mientras %ue los operadores son T+)P y T+R6, por lo %ue el atributo %ue los califica es operador. -n Yacc est asociaci#n se da con la directiva =type NatributoO =type NvalorO .6 !*R !.U-LA ).V`!.U-L T+-P/!6T =type NoperadorO T+)P T+R6

29

Como los parntesis no tienen ning;n uso al evaluar una expresi#n, ni +/ ni +C tienen asociado atributo alguno. -n Yacc puede asociarse a cada producci#n una acci#n codificada en C, si en esa acci#n se %uiere referirse a los atributos %ue un smbolo de la gramtica posee, se $ace referencia a l por medio de LL si es el noterminal %ue est definiendo la producci#n y L>, LA ... Ln para los smbolos gramaticales %ue se encuentran en el lado derec$o de la producci#n en el orden >, A ... n. +or e(emplo, si %ueremos %ue al encontrar dos elementos %ue deben multiplicarse o dividirse al cumplirse la regla< ).V`!.U-L < ).V`!.U-L T+R6 T+-P/!6T podemos referir la acci#n como LL\opera&LA,L>,LW', suponiendo %ue la funci#n opera espera en primer lugar un caracter %ue representa la operaci#n a realizar y los dos siguientes parmetros sean los valores numricos de los operandos, el resultado de la operaci#n se asigna a LL por%ue ste ser el nuevo valor de ).V`!.U-L despus de cumplirse la regla. La acci#n por omisi#n %ue tiene definida Yacc es LL\L>, por l %ue el programa completo para evaluar una expresi#n aritmtica en Yacc es< =? =@ =token .6 !*R +/ +C T+)P T+R6 =union ? int valor, c$ar operador, @ =type NvalorO .6 !*R !.U-LA ).V`!.U-L T+-P/!6T =type NoperadorO T+)P T+R6 =start !.U-LA == !.U-LA < !.U-LA T+)P ).V`!.U-L ? LL\opera&LA,L>,LW', printf&I=d =c =d \ =dJnI,L>,LA,LW,LL',@ F ).V`!.U-L , ).V`!.U-L < ).V`!.U-L T+R6 T+-P/!6T ? LL\opera&LA,L>,LW', printf&I=d =c =d \ =dJnI,L>,LA,LW,LL',@ F T+-P/!6T , 30

T+-P/!6T < .6 F !*R F +/ !.U-LA +C ?LL\LA,@ , == main&' ? yyparse&', @ opera&c$ar a, int b, int c' ? sBitc$&a' ? case 2E2< return &bEc', break, case 2:2< return &b:c', break, case 2D2< return &bDc', break, case 2M2< return &bMc', break, @ @ yyerror&c$ar Ds' ? printf &I=s JnI,s', @ -ste programa supone %ue .6 y !*R son capaces de obtener un valor al momento %ue se reconocen por el analizador de lxico. -sto obliga a modificar el programa en Lex para %ue esto ocurra. -l analizador gramatical en Yacc crea un espacio para poder asociar un valor a cada smbolo por medio de la variable yylval, %ue es del tipo definido por =union. /s %ue al asociar un valor desde Lex a un terminal, es necesario $acer referencia a la entidad %ue representa al atributo. +or e(emplo,. si !*R debe asociar un valor entero como atributo, la orden yylval.valor\atoi&yytext', $ace esta asignaci#n al convertir el lexema de !*R en un entero y asociarlo a yylval.valor. -l programa completo en Lex %uedar como sigue<

=? ^include Iyytab.$I =@ L G/:SH 6 G1:QH G JtJnH

31

== ?L@E ?6@E GJEJ:H GJDJMH GJ&H GJ'H ? @ . == Como no se tiene una tabla de smbolos para guarda un valor a cada identificador, se asume %ue su valor es el valor ascii del primer caracter de su lexema. 3anto para +/ como para +C no $ay aignaci#n de yylval ya %ue stos no tienen definido atributo por =type. ?yylval.valor\D&yytext', return .6,@ ?yylval.valor\atoi&yytext', return !*R,@ ?yylval.operador\D&yytext', return T+)P,@ ?yylval.operador\D&yytext', return T+R6,@ return +/, return +C,

32

"ue muy importante conocer como Lex y Yacc sirven para generar tokenizers y parsers en C %ue reconozcan gramticas libres de contexto, como lengua(es de programaci#n o calculadoras entre otros. 3ambin se aprendi# %ue los generadores Lex y Yacc sirven, respectivamente, para generar analizadores lexicogrficos y analizadores sintcticos para su aprovec$amiento como partes de los compiladores de los lengua(es de programaci#n. /s mismo se aprendi# y es%uematizo como se realiza la obtenci#n y e(ecuci#n de un analizador y su es%uema de uso.

33

También podría gustarte