Вы находитесь на странице: 1из 22

UNIVERSIDAD CENTRAL DEL ECUADOR

DISEO DE COMPILADORES

MANUEL ROSERO Trabajo sobre LEX y YACC


INTEGRANTES:

ING.

CYNTHIA ORTIZ WILLINGTON MAILA

Objetivo de Uso y generalidades


Fueron desarrollados en los 70's en los laboratorios Bell de AT&T. Estuvieron disponibles desde la 7a Edicin de UNIX. Aparecieron flex y bison (Anlogos a lex y yacc respectivamente). Cuentan con algunas caractersticas extra adems de las tradicionales. Mejor soporte para reduccin de expresiones muy largas o complejas. Lex genera el cdigo C para un analizador lxico. La extensin del archivo de las especificaciones para lex es .l La salida de lex es cdigo fuente C. Lex crea una rutina llamada yylex en un archivo llamado lex.yy.c.

Lex
Es el encargado de leer de la entrada, tpicamente stdin y extraer de la misma los tokens reconocidos por el basado en un lenguaje de expresiones regulares.

Ejemplos:

"==" Reconocera el token "==" [a-z] Reconocera una letra de la a "a" la "z" [0-9] Reconocera un numero del 0 al 9 [0-9]+ Reconocera un numero entero a partir del mayor que 0 -?[0-9]+ Reconocera cualquier numero entero

INTERACCIN ENTRE LAS RUTINAS LXICA Y DE PARSING


Una rutina main invoca a yyparse para evaluar si la entrada es vlida o no. yyparse invoca a una rutina llamada yylex cada vez que necesita un token. (yylex puede ser generado manualmente o generado por lex). Esta rutina lxica lee la entrada, y por cada token que reconoce, retorna el nmero de token al parser. El lxico puede tambin pasar el valor del token usando la variable externa yylval.

Estructura Lex

Operadores

\. Secuencia de Escape. Permite indicar nmeros en octal, los cuales son considerados como caracteres, as como la indicacin de considerar operadores como caracteres. \n \{ \% . Comillas dobles. Se coloca el carcter entre comillas y es tratado como tal y no como un operador, en caso de serlo. "\t" "[" "a Operaciones entre parntesis Operadores +, * y ? Operacin de concatenacin (ausencia de operador) Operacin de repeticin limitada { } Operador |

Expresiones regulares

Para poder crear expresiones regulares y patrones para las reglas, es necesario saber que la concatenacin de expresiones se logra simplemente juntando dos expresiones, sin dejar espacio entre ellas y que es bueno declarar una expresin muy compleja por partes como definiciones, y as evitar tener errores difciles de encontrar y corregir.

Ejemplo: <<EOF>> [a-z]

Funciones y variables LEX


yylex(): funcin que inicializa el anlisis lxico. yymore(): Modifica la variable yytext. Esta concatena la ltima cadena que ha concordado con un patrn determinado con la cadena entrante que concuerda con patrn determinado.

yylen: variable que almacena la dimensin de la cadena guardada en yytext. yywrap(): esta funcin maneja mltiples ficheros de entrada. Devuelve 0 si encuentra otro fichero para ser analizado o 1 si encuentra un fin de ficheros. Si se necesita trabajar con mltiples archivos, se debe definir yywrap. Si no se necesita trabajar con mltiples archivos de entrada se debe agregar la directiva %option noyywrap en la seccin de definiciones del archivo lex o compilar el archivo lex.yy.c con la opcin ll (-lfl en Flex).

yylval: variable nexo entre Lex y YACC. En esta variable lex colocar el token (o componente lxico) de un lexema que concuerda a una determinada expresin regular para que YACC pueda continuar realizando el anlisis sintctico. yyin: Permite manipular los procesos de entrada, por ejemplo se almacena en yyin el archivo de entrada el cual lex manejar automticamente. yyout: Permite manipular los procesos de salida. Es una variable de tipo FILE donde se define un archivo de salida el cual se copiarn todas las salidas. Desde Lex los tokens cualificados se devolvern haciendo, por ejemplo: [A-Z]+ { yylval.identificador = yytext; return ID; } [0-9]+ { yylval.numero = atoi(yytext); return NUM; } Desde Lex este tipo de tokens se devolvern haciendo, por ejemplo: + {return +; } ( {return (; } ; {return ;; } El nmero 0 est reservado para el fin de fichero. Lex har lo siguiente: <<EOF>> {return 0; }

%type Se utiliza cuando se ha hecho la declaracin %union para especificar mltiples tipos de valores. Permite declarar el tipo de los smbolos no terminales. Los no terminales a los que no se les asigna ningn valor a travs de $$ no es necesario declararlos

La declaracin es de la forma %type <campo_union> nombre_no_terminal %start Declara cual es el axioma de la gramtica. Si se omite la declaracin, se asume que el axioma de la gramtica es el primer no terminal de la seccin de reglas de la gramtica. La declaracin es de la forma: %start axioma %left y %right Permiten declarar la asociatividad de los operadores de la gramtica. La declaracin %left especifica asociativiadad por la izquierda. La declaracin %right especifica asociativiadad por la derecha. yyerror: Esta funcin invocada desde yyparse cuando ocurre un error sintctico. Se debe especificar la funcin yyerror() en el cdigo de usuario. Por ejemplo, la forma mas sencilla de definirlo es: %% %% void yyerror(char * msg) { printf(stderr,%s\n,msg); } El mensaje genrico que se mostrar es syntax error Si se desea personalizar esta funcin y tener una buena poltica de errores, se debern capturar los errores y ser enviados a la rutina. El analizador lxico puede ayudar a realizar este trabajo.

Ejemplo: Decimal a romano


%{ int NUM=0; %} finlinea \n %% I NUM++; V NUM=NUM+5; X NUM=NUM+10; L NUM=NUM+50; C NUM=NUM+100; D NUM=NUM+500; M NUM=NUM+1000; H NUM=NUM+5000; Q NUM=NUM+10000; I{4,100} NUM=0; V{2,100} NUM=0; X{4,100} NUM=0; L{4,100} NUM=0; D{4,100} NUM=0; M{4,100} NUM=0; I/[VXLCMDHQ] NUM-- ; V/[XLCMDHQ] NUM=NUM-5; X/[LCMDHQ] NUM=NUM-10; L/[CMDHQ] NUM=NUM-50; C/[MDHQ] NUM=NUM-100; D/[MHQ] NUM=NUM-500; M/[HQ] NUM=NUM-1000; H/Q NUM=NUM-5000;

{finlinea} { if(NUM!=0) printf(" _________________> EL ENTERO ES:%d\n\n\r",NUM); NUM=0; printf("\nIngreseel numero en romano : "); } %% int yywrap(void) {return 1;} int main(void) { printf(" \t ________________________________________\n"); printf(" \t ________________________________________\n"); printf(" \t|| TRANSFORMACION DE ROMANOS A DECIMAL ||\n"); printf(" \t _______________________________________\n"); printf(" \t _______________________________________\n"); printf("Ingrese el numero en romano: "); while(yylex()); return 0; }

Yacc

El programa generado por Yacc analiza sintcticamente un texto; para ello, recibe como entrada la secuencia de piezas sintcticas proporcionada por un analizador lexicogrfico encargado de la lectura del texto de entrada. Se trata, pues, de un anlisis jerrquico que complementa el anlisis lineal realizado por un programa generado por Lex.

Definiciones %union En la union se definen miembros cuyos correspondientes tipos de datos sern usados para dar el tipo de dato a los tokens. %union se traduce de la siguiente forma: En yacc : %union{ double dval; } La union mas importante es: YYSTYPE yylval;

que sera usada en la especificacion de lex, del mismo programa para asignarle valor a los tokens que yacc usara para realizar operaciones. Esta estructura puede llegar a ser muy compleja, y para saber de que tipo es cada token devuelto por yylex(), se usan las definiciones %token y %type.

%token y %type
%token sirve para definir los tokens que hay, y si es necesario, el tipo de dato que usan, todos los tokens son tomados como simbolos terminales, estos tambien tienen el objetivo de servir como etiquetas que yylex() regresa a yacc para identificar el token que se ha leido recientemente.

Su uso es como sigue: %token [<miembro_de_union>] ETIQUETA1 [ETIQUETA2 ... ETIQUETAn] Donde todo lo que esta entre [ y ] es opcional. <miembro_de_union> : Indica el miembro al que seran mapeados los tokens en la union yylval dentro de lex.

ETIQUETAS: Estos son los nombres con los que se identificaran los tokens mismos, que sern traducidos en C como numberos en instrucciones #define del

%type es analogo a %token, solo que este define el tipo de dato para simbolos no terminales de nuestra gramatica, la unica diferencia es que el tipo de dato a usar es obligatorio. Ejemplo: %token <dval> NUMBER %token PLUS MINUS TIMES DIVIDE POWER %token LEFT_PARENTHESIS RIGHT_PARENTHESIS %token END %type <dval> Expression

La primera linea indica que el token NUMERO sera del tipo de miembro de dval, es decir, un double. Las siguientes tres lineas, son para definir algunos tokens mas que seran usados en la gramatica, pero no necesitan un tipo de dato ni un miembro en yylval asociado. En la ultima linea definimos el tipo de dato que usara nuestro no terminal Expression.

Precedencia La precedencia es asignada en orden inverso al que aparecen, es decir, el ultimo operador declarado, tiene mayor precedencia que el anterior y asi sucesivamente. Asociatividad %left y %right indican si el operador se agrupa a la derecha o a la izquierda, por ejemplo, en el caso de POWER (Exponente) debe asociarse a la derecha, por que buscamos que se resuelva de ese modo, de derecha a izquierda %start En algunos casos es conveniente indicarle a yacc cual es el simbolo (no terminal) inicial a la hora de hacer el parseo, es decir, el simbolo que se trata de reducir, si esta opcion no es especificada, yacc toma al primer simbolo de la seccion de reglas como simbolo inicial. En nuestro ejemplo, se presentan ambos casos, nuestro simbolo inicial "Input" se encuentra al inicio del archivo y tambien esta declarado como simbolo inicial. %start Input

Reglas Cada smbolo se define con su nombre, seguido de dos puntos ":" seguidos de varios smbolos que conformaran su composicin gramatical que en caso de tener varias opciones, son separados por "|" (or) indicando que se tienen varias opciones para reducir ese smbolo y para terminar cada regla, un ";". Ejemplo: Si tomamos la gramtica que definimos al principio de esta seccin: <Expresin> Numero + <Expresin> Numero - <Expresin> Numero

Reduccin
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 diseo de gramticas en yacc, y es la diferencia entre especificar la recursividad por la derecha o por la izquierda, para expresiones muy sencillas que generen un parse tree pequeo no hay ningn problema pero para casos donde la reduccin es compleja, puede desbordar la pila ya que cuando la recursin es derecha, para resolverla, tiene que guardar los datos de la izquierda, y si estos son demasiados, no puede manejarlos. Por lo contrario, cuando la recursin es izquierda, no tiene que guardar datos que no va a utilizar por que recorre el rbol de izquierda a derecha y resuelve las reglas tan pronto como puede.

Subrutinas
En esta ultima seccin, es posible reimplementar, siguiendo la misma idea de lex, algunas funciones que pueden ser tiles en algn momento dado o declarar nuevas funciones para usar dentro de nuestro cdigo o nuestras reglas, no hay mucho que reimplementar a este nivel (yacc) a menos que sea con propsitos realmente especficos. Las funciones mas comnmente implementadas son main() e yyerror(), la primera se usa para personalizar el programa con mensajes antes o despus del parser, o para llamarlo varias veces en el cdigo y la segunda la ocupa yyparse() cada vez que encuentra un error de sintaxis.

Ejemplo: Decimal a romano


%{ #include <ctype.h> #include <stdio.h> static int lineto=0; int ultimo=0; %} %token NUM blanco %start program %% program : program expr '\n'{ printf(" _________________> EL ENTERO ES:%d\n\n\r",$2); $ $=$2; ultimo=0;} |program '\n' | |programerror '\n' = { yyerror("Ha existido un error, vuelva a ingresar\n"); }; expr :NUM {ultimo=$$=$1;} |NUM expr {if ($1 >= ultimo) $$= $2 + (ultimo=$1); else $$ =$2 - (ultimo=$1); printf("%d",$$); }; %% yylex(){ int c; c=getchar(); if(isalpha(c)){ switch(c){

case 'I': yylval=1; break; case 'i': yylval=1; break; case 'V': yylval=5; break; case 'X': yylval=10; break; case 'L': yylval=50; break; case 'C': yylval=100; break; case 'D': yylval=500; break; case 'M': yylval=1000; break; default: return c; break;

} return NUM;

if(c=='\n'){ lineto++; printf("linea %d :",lineto); } if(c==' '){ yylval=0; return blanco; } return c; } int yyerror(char *s){ fprintf(stderr, "%s\n",s); return 0; } int main(void){ printf(" \t ________________________________________\n"); printf(" \t ________________________________________\n"); printf(" \t|| DE ROMANOS A DECIMAL ||\n"); printf(" \t _______________________________________\n"); printf(" \t _______________________________________\n"); printf("Ingrese el numero romano : "); yyparse(); return 0; }

Вам также может понравиться